Reformatting using scalariform

This commit is contained in:
mecampbellsoup 2018-03-23 13:31:49 -07:00
parent 9c69ddc1a8
commit 74ceaeb0d4
281 changed files with 8457 additions and 8296 deletions

View file

@ -7,3 +7,5 @@ addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.0.3")
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.3.5")
addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.1.0")
addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.8.2")

View file

@ -1,24 +1,23 @@
package org.bitcoins.core.bloom
import org.bitcoins.core.crypto.{DoubleSha256Digest, HashDigest, Sha256Hash160Digest}
import org.bitcoins.core.number.{UInt32, UInt64}
import org.bitcoins.core.protocol.script.{MultiSignatureScriptPubKey, P2PKScriptPubKey, ScriptPubKey}
import org.bitcoins.core.protocol.transaction.{Transaction, TransactionOutPoint}
import org.bitcoins.core.protocol.{CompactSizeUInt, NetworkElement}
import org.bitcoins.core.script.constant.{ScriptConstant, ScriptToken}
import org.bitcoins.core.crypto.{ DoubleSha256Digest, HashDigest, Sha256Hash160Digest }
import org.bitcoins.core.number.{ UInt32, UInt64 }
import org.bitcoins.core.protocol.script.{ MultiSignatureScriptPubKey, P2PKScriptPubKey, ScriptPubKey }
import org.bitcoins.core.protocol.transaction.{ Transaction, TransactionOutPoint }
import org.bitcoins.core.protocol.{ CompactSizeUInt, NetworkElement }
import org.bitcoins.core.script.constant.{ ScriptConstant, ScriptToken }
import org.bitcoins.core.serializers.bloom.RawBloomFilterSerializer
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil, Factory}
import org.bitcoins.core.util.{ BitcoinSLogger, BitcoinSUtil, Factory }
import scala.annotation.tailrec
import scala.util.hashing.MurmurHash3
/**
* Created by chris on 8/2/16.
* Implements a bloom fitler that abides by the semantics of BIP37
* [[https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki]]
* [[https://github.com/bitcoin/bitcoin/blob/master/src/bloom.h]]
*/
* Created by chris on 8/2/16.
* Implements a bloom fitler that abides by the semantics of BIP37
* [[https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki]]
* [[https://github.com/bitcoin/bitcoin/blob/master/src/bloom.h]]
*/
sealed abstract class BloomFilter extends NetworkElement {
/** How large the bloom filter is, in Bytes */
@ -33,16 +32,17 @@ sealed abstract class BloomFilter extends NetworkElement {
/** An arbitrary value to add to the seed value in the hash function used by the bloom filter. */
def tweak: UInt32
/** A set of flags that control how outpoints corresponding to a matched pubkey script are added to the filter.
* See the 'Comparing Transaction Elements to a Bloom Filter' section in this link
* [[https://bitcoin.org/en/developer-reference#filterload]]
*/
/**
* A set of flags that control how outpoints corresponding to a matched pubkey script are added to the filter.
* See the 'Comparing Transaction Elements to a Bloom Filter' section in this link
* [[https://bitcoin.org/en/developer-reference#filterload]]
*/
def flags: BloomFlag
/** Inserts a sequence of bytes into the [[BloomFilter]] */
def insert(bytes: Seq[Byte]): BloomFilter = {
//these are the bit indexes that need to be set inside of data
val bitIndexes = (0 until hashFuncs.toInt).map(i => murmurHash(i,bytes))
val bitIndexes = (0 until hashFuncs.toInt).map(i => murmurHash(i, bytes))
@tailrec
def loop(remainingBitIndexes: Seq[Int], accum: Seq[Byte]): Seq[Byte] = {
if (remainingBitIndexes.isEmpty) accum
@ -54,14 +54,14 @@ sealed abstract class BloomFilter extends NetworkElement {
//we need to calculate the bitIndex we need to set inside of our byte
val bitIndex = (1 << (7 & currentIndex)).toByte
val byte = accum(byteIndex)
val setBitByte: Byte = (byte | bitIndex ).toByte
val setBitByte: Byte = (byte | bitIndex).toByte
//replace old byte with new byte with bit set
val newAccum: Seq[Byte] = accum.updated(byteIndex,setBitByte)
loop(remainingBitIndexes.tail,newAccum)
val newAccum: Seq[Byte] = accum.updated(byteIndex, setBitByte)
loop(remainingBitIndexes.tail, newAccum)
}
}
val newData = loop(bitIndexes,data)
BloomFilter(filterSize,newData,hashFuncs,tweak,flags)
val newData = loop(bitIndexes, data)
BloomFilter(filterSize, newData, hashFuncs, tweak, flags)
}
/** Inserts a [[HashDigest]] into [[data]] */
@ -78,7 +78,7 @@ sealed abstract class BloomFilter extends NetworkElement {
/** Checks if [[data]] contains the given sequence of bytes */
def contains(bytes: Seq[Byte]): Boolean = {
val bitIndexes = (0 until hashFuncs.toInt).map(i => murmurHash(i,bytes))
val bitIndexes = (0 until hashFuncs.toInt).map(i => murmurHash(i, bytes))
@tailrec
def loop(remainingBitIndexes: Seq[Int], accum: Seq[Boolean]): Boolean = {
if (remainingBitIndexes.isEmpty) !accum.exists(_ == false)
@ -91,7 +91,7 @@ sealed abstract class BloomFilter extends NetworkElement {
loop(remainingBitIndexes.tail, isBitSet +: accum)
}
}
loop(bitIndexes,Nil)
loop(bitIndexes, Nil)
}
/** Checks if [[data]] contains a [[DoubleSha256Digest]] */
@ -103,54 +103,58 @@ sealed abstract class BloomFilter extends NetworkElement {
/** Checks if [[data]] contains a [[Sha256Hash160Digest]] */
def contains(hash: Sha256Hash160Digest): Boolean = contains(hash.bytes)
/** Checks if the transaction's txid, or any of the constants in it's scriptPubKeys/scriptSigs match our BloomFilter
* See BIP37 for exact details on what is relevant to a bloom filter and what is not relevant
* [[https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki#filter-matching-algorithm]]
* */
/**
* Checks if the transaction's txid, or any of the constants in it's scriptPubKeys/scriptSigs match our BloomFilter
* See BIP37 for exact details on what is relevant to a bloom filter and what is not relevant
* [[https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki#filter-matching-algorithm]]
*/
def isRelevant(transaction: Transaction): Boolean = {
val scriptPubKeys = transaction.outputs.map(_.scriptPubKey)
//pull out all of the constants in the scriptPubKey's
val constantsWithOutputIndex = scriptPubKeys.zipWithIndex.flatMap { case (scriptPubKey, index) =>
val constants = scriptPubKey.asm.filter(_.isInstanceOf[ScriptConstant])
constants.map(c => (c,index))
val constantsWithOutputIndex = scriptPubKeys.zipWithIndex.flatMap {
case (scriptPubKey, index) =>
val constants = scriptPubKey.asm.filter(_.isInstanceOf[ScriptConstant])
constants.map(c => (c, index))
}
//check if the bloom filter contains any of the script constants in our outputs
val constantsOutput = constantsWithOutputIndex.filter {
case (c,_) => contains(c.bytes)
case (c, _) => contains(c.bytes)
}
val scriptSigs = transaction.inputs.map(_.scriptSignature)
val constantsWithInputIndex = scriptSigs.zipWithIndex.flatMap { case (scriptSig, index) =>
val constants = scriptSig.asm.filter(_.isInstanceOf[ScriptConstant])
constants.map(c => (c,index))
val constantsWithInputIndex = scriptSigs.zipWithIndex.flatMap {
case (scriptSig, index) =>
val constants = scriptSig.asm.filter(_.isInstanceOf[ScriptConstant])
constants.map(c => (c, index))
}
//check if the filter contains any of the prevouts in this tx
val containsOutPoint = transaction.inputs.filter(i => contains(i.previousOutput))
//check if the bloom filter contains any of the script constants in our inputs
val constantsInput = constantsWithInputIndex.filter {
case (c,_) => contains(c.bytes)
case (c, _) => contains(c.bytes)
}
constantsOutput.nonEmpty || constantsInput.nonEmpty ||
containsOutPoint.nonEmpty || contains(transaction.txId)
}
/** Updates this bloom filter to contain the relevant information for the given Transaction
* See BIP37 for the exact details on what parts of a transaction is added to the bloom filter
* [[https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki#filter-matching-algorithm]]
* */
/**
* Updates this bloom filter to contain the relevant information for the given Transaction
* See BIP37 for the exact details on what parts of a transaction is added to the bloom filter
* [[https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki#filter-matching-algorithm]]
*/
def update(transaction: Transaction): BloomFilter = flags match {
case BloomUpdateAll =>
val scriptPubKeys = transaction.outputs.map(_.scriptPubKey)
//a sequence of outPoints that need to be inserted into the filter
val outPoints: Seq[TransactionOutPoint] = scriptPubKeys.zipWithIndex.flatMap {
case (scriptPubKey,index) =>
case (scriptPubKey, index) =>
//constants that matched inside of our current filter
val constants = scriptPubKey.asm.filter(c => c.isInstanceOf[ScriptConstant] && contains(c.bytes))
//we need to create a new outpoint in the filter if a constant in the scriptPubKey matched
constants.map(c => TransactionOutPoint(transaction.txId,UInt32(index)))
constants.map(c => TransactionOutPoint(transaction.txId, UInt32(index)))
}
logger.debug("Inserting outPoints: " + outPoints)
@ -167,45 +171,46 @@ sealed abstract class BloomFilter extends NetworkElement {
//update the filter with the outpoint if the filter matches any of the constants in a p2pkh or multisig script pubkey
val scriptPubKeysWithIndex = transaction.outputs.map(_.scriptPubKey).zipWithIndex
updateP2PKOnly(scriptPubKeysWithIndex,transaction.txId)
updateP2PKOnly(scriptPubKeysWithIndex, transaction.txId)
}
/** Updates a bloom filter according to the rules specified by the [[BloomUpdateP2PKOnly]] flag
* See BIP37 for the exact rules on updating a bloom filter with this flag set
* [[https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki#filter-matching-algorithm]]
* */
def updateP2PKOnly(scriptPubKeysWithIndex: Seq[(ScriptPubKey,Int)],txId: DoubleSha256Digest): BloomFilter = {
/**
* Updates a bloom filter according to the rules specified by the [[BloomUpdateP2PKOnly]] flag
* See BIP37 for the exact rules on updating a bloom filter with this flag set
* [[https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki#filter-matching-algorithm]]
*/
def updateP2PKOnly(scriptPubKeysWithIndex: Seq[(ScriptPubKey, Int)], txId: DoubleSha256Digest): BloomFilter = {
@tailrec
def loop(constantsWithIndex: Seq[(ScriptToken,Int)], accumFilter: BloomFilter): BloomFilter = constantsWithIndex match {
def loop(constantsWithIndex: Seq[(ScriptToken, Int)], accumFilter: BloomFilter): BloomFilter = constantsWithIndex match {
case h :: t if (accumFilter.contains(h._1.bytes)) =>
logger.debug("Found constant in bloom filter: " + h._1.hex)
val filter = accumFilter.insert(TransactionOutPoint(txId,UInt32(h._2)))
val filter = accumFilter.insert(TransactionOutPoint(txId, UInt32(h._2)))
loop(t, filter)
case h :: t => loop(t,accumFilter)
case Nil => accumFilter
case h :: t => loop(t, accumFilter)
case Nil => accumFilter
}
val p2pkOrMultiSigScriptPubKeys: Seq[(ScriptPubKey,Int)] = scriptPubKeysWithIndex.filter {
case (s,index) => s.isInstanceOf[P2PKScriptPubKey] ||
val p2pkOrMultiSigScriptPubKeys: Seq[(ScriptPubKey, Int)] = scriptPubKeysWithIndex.filter {
case (s, index) => s.isInstanceOf[P2PKScriptPubKey] ||
s.isInstanceOf[MultiSignatureScriptPubKey]
}
//gets rid of all asm operations in the scriptPubKey except for the constants
val scriptConstantsWithOutputIndex: Seq[(ScriptToken,Int)] = p2pkOrMultiSigScriptPubKeys.flatMap { case (scriptPubKey,index) =>
(scriptPubKey.asm.map(token => (token,index))).filter {
case (token,index) => token.isInstanceOf[ScriptConstant]
}
val scriptConstantsWithOutputIndex: Seq[(ScriptToken, Int)] = p2pkOrMultiSigScriptPubKeys.flatMap {
case (scriptPubKey, index) =>
(scriptPubKey.asm.map(token => (token, index))).filter {
case (token, index) => token.isInstanceOf[ScriptConstant]
}
}
loop(scriptConstantsWithOutputIndex,this)
loop(scriptConstantsWithOutputIndex, this)
}
/**
* Performs the [[MurmurHash3]] on the given hash
*
* @param hashNum the nth hash function we are using
* @param bytes the bytes of the data that needs to be inserted into the [[BloomFilter]]
* @return the index of the bit inside of [[data]] that needs to be set to 1
*/
* Performs the [[MurmurHash3]] on the given hash
*
* @param hashNum the nth hash function we are using
* @param bytes the bytes of the data that needs to be inserted into the [[BloomFilter]]
* @return the index of the bit inside of [[data]] that needs to be set to 1
*/
private def murmurHash(hashNum: Int, bytes: Seq[Byte]): Int = {
//TODO: The call of .toInt is probably the source of a bug here, need to come back and look at this
//since this isn't consensus critical though I'm leaving this for now
@ -216,8 +221,10 @@ sealed abstract class BloomFilter extends NetworkElement {
modded.toInt
}
/** See BIP37 to see where this number comes from
* [[https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki#bloom-filter-format]] */
/**
* See BIP37 to see where this number comes from
* [[https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki#bloom-filter-format]]
*/
private def murmurConstant = UInt32("fba4c795")
/** Adds a sequence of byte vectors to our bloom filter then returns that new filter*/
@ -225,18 +232,17 @@ sealed abstract class BloomFilter extends NetworkElement {
@tailrec
def loop(remainingByteVectors: Seq[Seq[Byte]], accumBloomFilter: BloomFilter): BloomFilter = {
if (remainingByteVectors.isEmpty) accumBloomFilter
else loop(remainingByteVectors.tail,accumBloomFilter.insert(remainingByteVectors.head))
else loop(remainingByteVectors.tail, accumBloomFilter.insert(remainingByteVectors.head))
}
loop(bytes,this)
loop(bytes, this)
}
override def bytes = RawBloomFilterSerializer.write(this)
}
object BloomFilter extends Factory[BloomFilter] {
private case class BloomFilterImpl(filterSize: CompactSizeUInt, data: Seq[Byte], hashFuncs : UInt32,
private case class BloomFilterImpl(filterSize: CompactSizeUInt, data: Seq[Byte], hashFuncs: UInt32,
tweak: UInt32, flags: BloomFlag) extends BloomFilter
/** Max bloom filter size as per [[https://bitcoin.org/en/developer-reference#filterload]] */
val maxSize = UInt32(36000)
@ -245,23 +251,23 @@ object BloomFilter extends Factory[BloomFilter] {
val maxHashFuncs = UInt32(50)
/**
* Creates a bloom filter based on the number of elements to be inserted into the filter
* and the desired false positive rate
* [[https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki#bloom-filter-format]]
*/
* Creates a bloom filter based on the number of elements to be inserted into the filter
* and the desired false positive rate
* [[https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki#bloom-filter-format]]
*/
def apply(numElements: Int, falsePositiveRate: Double, tweak: UInt32, flags: BloomFlag): BloomFilter = {
import scala.math._
//m = number of bits in the array
//n = number of elements in the array
//from https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki#bloom-filter-format
val optimalFilterSize : Double = (-1 / pow(log(2),2) * numElements * log(falsePositiveRate)) / 8
val optimalFilterSize: Double = (-1 / pow(log(2), 2) * numElements * log(falsePositiveRate)) / 8
logger.debug("optimalFilterSize " + optimalFilterSize)
//BIP37 places limitations on the filter size, namely it cannot be > 36,000 bytes
val actualFilterSize: Int = max(1,min(optimalFilterSize, maxSize.toInt * 8)).toInt
val actualFilterSize: Int = max(1, min(optimalFilterSize, maxSize.toInt * 8)).toInt
logger.debug("actualFilterSize: " + actualFilterSize)
val optimalHashFuncs: Double = (actualFilterSize * 8 / numElements * log(2))
//BIP37 places a limit on the amount of hashFuncs we can use, which is 50
val actualHashFuncs: Int = max(1,min(optimalHashFuncs, maxHashFuncs.toInt)).toInt
val actualHashFuncs: Int = max(1, min(optimalHashFuncs, maxHashFuncs.toInt)).toInt
val emptyByteArray = Seq.fill(actualFilterSize)(0.toByte)
BloomFilter(CompactSizeUInt(UInt64(actualFilterSize)), emptyByteArray, UInt32(actualHashFuncs), tweak, flags)
@ -271,7 +277,6 @@ object BloomFilter extends Factory[BloomFilter] {
BloomFilterImpl(filterSize, data, hashFuncs, tweak, flags)
}
override def fromBytes(bytes: Seq[Byte]): BloomFilter = RawBloomFilterSerializer.read(bytes)
}

View file

@ -3,10 +3,10 @@ package org.bitcoins.core.bloom
import org.bitcoins.core.util.Factory
/**
* Created by chris on 8/3/16.
* Specifies how to update a bloom filter
* [[https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki#filter-matching-algorithm]]
*/
* Created by chris on 8/3/16.
* Specifies how to update a bloom filter
* [[https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki#filter-matching-algorithm]]
*/
sealed trait BloomFlag {
def byte: Byte
}
@ -17,23 +17,22 @@ case object BloomUpdateNone extends BloomFlag {
}
/**
* If the filter matches any data element in a pubkey script,
* the corresponding outpoint is added to the filter.
*/
* If the filter matches any data element in a pubkey script,
* the corresponding outpoint is added to the filter.
*/
case object BloomUpdateAll extends BloomFlag {
def byte = 1.toByte
}
/**
* If the filter matches any data element in a pubkey script and that
* scriptPubKey is either a P2PKH or non-P2SH pay-to-multisig script,
* the outpoint for this transaction is added to the filter.
*/
* If the filter matches any data element in a pubkey script and that
* scriptPubKey is either a P2PKH or non-P2SH pay-to-multisig script,
* the outpoint for this transaction is added to the filter.
*/
case object BloomUpdateP2PKOnly extends BloomFlag {
def byte = 2.toByte
}
object BloomFlag extends Factory[BloomFlag] {
private def flags = Seq(BloomUpdateNone, BloomUpdateAll, BloomUpdateP2PKOnly)
def apply(byte: Byte): BloomFlag = {

View file

@ -1,7 +1,7 @@
package org.bitcoins.core.channels
import org.bitcoins.core.crypto._
import org.bitcoins.core.currency.{CurrencyUnit, CurrencyUnits}
import org.bitcoins.core.currency.{ CurrencyUnit, CurrencyUnits }
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.policy.Policy
import org.bitcoins.core.protocol.script._
@ -12,11 +12,11 @@ import org.bitcoins.core.wallet.EscrowTimeoutHelper
import org.bitcoins.core.wallet.builder.TxBuilderError
import scala.annotation.tailrec
import scala.util.{Failure, Success, Try}
import scala.util.{ Failure, Success, Try }
/**
* Created by tom on 2/9/17.
*/
* Created by tom on 2/9/17.
*/
sealed abstract class Channel {
val logger = BitcoinSLogger.logger
/** Commitment transaction initializing the payment channel depositing funds into it. */
@ -25,14 +25,15 @@ sealed abstract class Channel {
/** The index of the output that is the [[EscrowTimeoutScriptPubKey]] in the [[anchorTx]] */
def outputIndex: Int = {
val expectedLock = witSPK
val outputOpt = anchorTx.outputs.zipWithIndex.find { case (o, _) =>
o.scriptPubKey == expectedLock
val outputOpt = anchorTx.outputs.zipWithIndex.find {
case (o, _) =>
o.scriptPubKey == expectedLock
}
require(outputOpt.isDefined, "We do not have the correct locking output on our anchor transasction")
outputOpt.get._2
}
def outPoint = TransactionOutPoint(anchorTx.txId,UInt32(outputIndex))
def outPoint = TransactionOutPoint(anchorTx.txId, UInt32(outputIndex))
/** The [[EscrowTimeoutScriptPubKey]] that needs to be satisfied to spend from the [[anchorTx]] */
def lock: EscrowTimeoutScriptPubKey
@ -54,49 +55,52 @@ sealed trait ChannelAwaitingAnchorTx extends Channel {
/** The number of confirmations on the anchor transaction */
def confirmations: Long
/** Creates a [[ChannelInProgress]] from this ChannelAwaitingAnchorTx,
* starts with [[Policy.minChannelAmount]] being paid to the server
*
* HashType is hard coded as SIGHASH_SINGLE|ANYONECANPAY -- this allows the tx to commit to the client's refund
* output. When the payment is actually redeemed the server will add one output that pays the server
* and possibly add another input & output that pays a network fee.
* */
/**
* Creates a [[ChannelInProgress]] from this ChannelAwaitingAnchorTx,
* starts with [[Policy.minChannelAmount]] being paid to the server
*
* HashType is hard coded as SIGHASH_SINGLE|ANYONECANPAY -- this allows the tx to commit to the client's refund
* output. When the payment is actually redeemed the server will add one output that pays the server
* and possibly add another input & output that pays a network fee.
*/
def clientSign(clientChangeSPK: ScriptPubKey, amount: CurrencyUnit,
privKey: ECPrivateKey): Either[ChannelInProgressClientSigned,TxBuilderError] = {
privKey: ECPrivateKey): Either[ChannelInProgressClientSigned, TxBuilderError] = {
require(confirmations >= Policy.confirmations, "Need " + Policy.confirmations + " confirmations on the anchor tx before " +
"we can create a payment channel in progress, got " + confirmations + " confirmations")
require(amount >= Policy.minChannelAmount, "First payment channel payment amount must be: " + Policy.minChannelAmount + " got: " + amount)
val o1 = TransactionOutput(lockedAmount - amount, clientChangeSPK)
val outputs = Seq(o1)
val p2shScriptSig = P2SHScriptSignature(witSPK)
val i1 = TransactionInput(outPoint,p2shScriptSig,TransactionConstants.sequence)
val partiallySigned = EscrowTimeoutHelper.clientSign(outPoint, anchorTx, outputs,(privKey.sign(_: Seq[Byte]),None),
val i1 = TransactionInput(outPoint, p2shScriptSig, TransactionConstants.sequence)
val partiallySigned = EscrowTimeoutHelper.clientSign(outPoint, anchorTx, outputs, (privKey.sign(_: Seq[Byte]), None),
lock, HashType.sigHashSingleAnyoneCanPay)
val inProgress = partiallySigned.left.map(tx => ChannelInProgressClientSigned(anchorTx,lock,clientChangeSPK, tx, Nil))
val inProgress = partiallySigned.left.map(tx => ChannelInProgressClientSigned(anchorTx, lock, clientChangeSPK, tx, Nil))
inProgress
}
/** Useful for the server to create a [[org.bitcoins.core.channels.ChannelInProgressClientSigned]]
* after we receive a partially signed transaction from the client.
* If None is returned, that means we did not find an output that has the clientSPK
*/
/**
* Useful for the server to create a [[org.bitcoins.core.channels.ChannelInProgressClientSigned]]
* after we receive a partially signed transaction from the client.
* If None is returned, that means we did not find an output that has the clientSPK
*/
def createClientSigned(partiallySigned: WitnessTransaction, clientChangeSPK: ScriptPubKey): Option[ChannelInProgressClientSigned] = {
val inputOpt = partiallySigned.inputs.zipWithIndex.find(_._1.previousOutput.txId == anchorTx.txId)
val inputIndex = inputOpt.map(i => UInt32(i._2))
val txSigComponent = inputIndex.map { i =>
WitnessTxSigComponent(partiallySigned, i, scriptPubKey, Policy.standardScriptVerifyFlags, lockedAmount)
}
txSigComponent.map(t => ChannelInProgressClientSigned(anchorTx,lock,clientChangeSPK,t,Nil))
txSigComponent.map(t => ChannelInProgressClientSigned(anchorTx, lock, clientChangeSPK, t, Nil))
}
/** Attempts to close the [[Channel]] because the [[org.bitcoins.core.protocol.script.EscrowTimeoutScriptPubKey]]
* has timed out.
* Note that this does not require any confirmations on the anchor tx,
* this is because the Client is essentially refunding himself the money
*/
/**
* Attempts to close the [[Channel]] because the [[org.bitcoins.core.protocol.script.EscrowTimeoutScriptPubKey]]
* has timed out.
* Note that this does not require any confirmations on the anchor tx,
* this is because the Client is essentially refunding himself the money
*/
def closeWithTimeout(clientChangeSPK: ScriptPubKey, clientKey: ECPrivateKey,
fee: CurrencyUnit): Either[ChannelClosedWithTimeout,TxBuilderError] = {
ChannelClosedWithTimeout.closeWithTimeout(this,clientChangeSPK,clientKey,fee)
fee: CurrencyUnit): Either[ChannelClosedWithTimeout, TxBuilderError] = {
ChannelClosedWithTimeout.closeWithTimeout(this, clientChangeSPK, clientKey, fee)
}
}
@ -107,9 +111,10 @@ sealed trait BaseInProgress { this: Channel =>
/** The most recent [[TxSigComponent]] in the payment channel */
def current: WitnessTxSigComponent
/** The previous states of the payment channel.
* The first item in the Seq is the most recent [[TxSigComponent]] in the [[Channel]]
*/
/**
* The previous states of the payment channel.
* The first item in the Seq is the most recent [[TxSigComponent]] in the [[Channel]]
*/
def old: Seq[WitnessTxSigComponent]
/** The [[ScriptPubKey]] that pays the client it's change */
@ -129,16 +134,18 @@ sealed trait BaseInProgress { this: Channel =>
/** The amount that will be refunded to the client */
def clientAmount: Option[CurrencyUnit] = clientOutput.map(_.value)
/** The amount the server will be paid, note this amount will not be the exact amount paid
* to the server because the network's transaction fee is deducted from the server output
*/
/**
* The amount the server will be paid, note this amount will not be the exact amount paid
* to the server because the network's transaction fee is deducted from the server output
*/
def serverAmount: Option[CurrencyUnit] = clientAmount.map(a => lockedAmount - a)
/** Attempts to close the [[Channel]] because the [[EscrowTimeoutScriptPubKey]]
* has timed out
*/
def closeWithTimeout(clientKey: ECPrivateKey, fee: CurrencyUnit): Either[ChannelClosedWithTimeout,TxBuilderError] = {
ChannelClosedWithTimeout.closeWithTimeout(this,clientChangeSPK,clientKey,fee)
/**
* Attempts to close the [[Channel]] because the [[EscrowTimeoutScriptPubKey]]
* has timed out
*/
def closeWithTimeout(clientKey: ECPrivateKey, fee: CurrencyUnit): Either[ChannelClosedWithTimeout, TxBuilderError] = {
ChannelClosedWithTimeout.closeWithTimeout(this, clientChangeSPK, clientKey, fee)
}
}
@ -156,7 +163,7 @@ sealed trait ChannelInProgress extends Channel with BaseInProgress {
val newClientAmount = c.value - amount
if (newClientAmount < CurrencyUnits.zero) Failure(new IllegalArgumentException("Client is spending more money than was locked in the output"))
else if (newClientAmount <= Policy.dustThreshold) Success(None)
else Success(Some(TransactionOutput(newClientAmount,c.scriptPubKey)))
else Success(Some(TransactionOutput(newClientAmount, c.scriptPubKey)))
case None =>
Failure(new IllegalArgumentException("Client has already spent all of it's money to the server"))
}
@ -174,12 +181,12 @@ sealed trait ChannelInProgress extends Channel with BaseInProgress {
val check = checkAmounts(os)
check match {
case Some(err) => Right(err)
case None => updateChannel(outPoint, anchorTx,os,clientKey)
case None => updateChannel(outPoint, anchorTx, os, clientKey)
}
}
result match {
case Success(err) => err
case Failure(_) => Right(TxBuilderError.MintsMoney)
case Failure(_) => Right(TxBuilderError.MintsMoney)
}
}
@ -202,23 +209,24 @@ sealed trait ChannelInProgress extends Channel with BaseInProgress {
/** Updates the payment channel with the given parameters */
private def updateChannel(outPoint: TransactionOutPoint, creditingTx: Transaction,
outputs: Seq[TransactionOutput],
clientKey: ECPrivateKey): Either[ChannelInProgressClientSigned,TxBuilderError] = {
val sign = (clientKey.sign(_: Seq[Byte]),None)
val partiallySigned = EscrowTimeoutHelper.clientSign(outPoint, creditingTx,outputs,
sign,lock, HashType.sigHashSingleAnyoneCanPay)
outputs: Seq[TransactionOutput],
clientKey: ECPrivateKey): Either[ChannelInProgressClientSigned, TxBuilderError] = {
val sign = (clientKey.sign(_: Seq[Byte]), None)
val partiallySigned = EscrowTimeoutHelper.clientSign(outPoint, creditingTx, outputs,
sign, lock, HashType.sigHashSingleAnyoneCanPay)
val inProgress = partiallySigned.left.map(tx =>
ChannelInProgressClientSigned(anchorTx,lock,clientChangeSPK, tx, current +: old))
ChannelInProgressClientSigned(anchorTx, lock, clientChangeSPK, tx, current +: old))
inProgress
}
/** Useful for the server to create a [[org.bitcoins.core.channels.ChannelInProgressClientSigned]]
* after we receive a partially signed transaction from the client
*/
/**
* Useful for the server to create a [[org.bitcoins.core.channels.ChannelInProgressClientSigned]]
* after we receive a partially signed transaction from the client
*/
def createClientSigned(partiallySigned: WitnessTransaction): ChannelInProgressClientSigned = {
val txSigComponent = WitnessTxSigComponent(partiallySigned,current.inputIndex, scriptPubKey,
current.flags,lockedAmount)
ChannelInProgressClientSigned(anchorTx,lock, clientChangeSPK,txSigComponent, current +: old)
val txSigComponent = WitnessTxSigComponent(partiallySigned, current.inputIndex, scriptPubKey,
current.flags, lockedAmount)
ChannelInProgressClientSigned(anchorTx, lock, clientChangeSPK, txSigComponent, current +: old)
}
}
@ -229,42 +237,46 @@ sealed trait ChannelInProgressClientSigned extends Channel with BaseInProgress {
private def partiallySignedTx: WitnessTransaction = current.transaction
/** Signs the payment channel transaction with the server's [[ECPrivateKey]] */
def serverSign(serverKey: ECPrivateKey): Either[ChannelInProgress,TxBuilderError] = {
val unsignedTxSigComponent = WitnessTxSigComponentRaw(partiallySignedTx,
current.inputIndex, scriptPubKey, Policy.standardScriptVerifyFlags,lockedAmount)
def serverSign(serverKey: ECPrivateKey): Either[ChannelInProgress, TxBuilderError] = {
val unsignedTxSigComponent = WitnessTxSigComponentRaw(
partiallySignedTx,
current.inputIndex, scriptPubKey, Policy.standardScriptVerifyFlags, lockedAmount
)
val signedTxSigComponent: Either[WitnessTxSigComponentRaw, TxBuilderError] = EscrowTimeoutHelper.serverSign(serverKey,
unsignedTxSigComponent, HashType.sigHashAllAnyoneCanPay)
val signedTxSigComponent: Either[WitnessTxSigComponentRaw, TxBuilderError] = EscrowTimeoutHelper.serverSign(
serverKey,
unsignedTxSigComponent, HashType.sigHashAllAnyoneCanPay
)
signedTxSigComponent.left.map { s =>
ChannelInProgress(anchorTx,lock, clientChangeSPK, s, old)
ChannelInProgress(anchorTx, lock, clientChangeSPK, s, old)
}
}
/** Closes this payment channel, paying the server's amount to the given [[ScriptPubKey]] */
def close(serverSPK: ScriptPubKey, serverKey: ECPrivateKey, fee: CurrencyUnit): Either[ChannelClosedWithEscrow,TxBuilderError] = {
def close(serverSPK: ScriptPubKey, serverKey: ECPrivateKey, fee: CurrencyUnit): Either[ChannelClosedWithEscrow, TxBuilderError] = {
val c = clientOutput
val clientAmount = c.map(_.value).getOrElse(CurrencyUnits.zero)
val serverAmount = lockedAmount - clientAmount - fee
val serverOutput = TransactionOutput(serverAmount,serverSPK)
val serverOutput = TransactionOutput(serverAmount, serverSPK)
//if the client is refunding itself less than the dust threshold we should just remove the output
val outputs: Seq[TransactionOutput] = if (clientAmount <= Policy.dustThreshold || c.isEmpty) {
Seq(serverOutput)
} else {
Seq(c.get,serverOutput)
Seq(c.get, serverOutput)
}
val invariant = checkCloseOutputs(outputs,fee, serverSPK)
val invariant = checkCloseOutputs(outputs, fee, serverSPK)
if (invariant.isDefined) {
Right(invariant.get)
} else {
val oldTx = partiallySignedTx
val updatedTx = WitnessTransaction(oldTx.version,oldTx.inputs,outputs,oldTx.lockTime,oldTx.witness)
val txSigComponent = WitnessTxSigComponent(updatedTx,current.inputIndex,
scriptPubKey,current.flags, current.amount)
val updatedInProgressClientSigned = ChannelInProgressClientSigned(anchorTx,lock,clientChangeSPK,txSigComponent,old)
val updatedTx = WitnessTransaction(oldTx.version, oldTx.inputs, outputs, oldTx.lockTime, oldTx.witness)
val txSigComponent = WitnessTxSigComponent(updatedTx, current.inputIndex,
scriptPubKey, current.flags, current.amount)
val updatedInProgressClientSigned = ChannelInProgressClientSigned(anchorTx, lock, clientChangeSPK, txSigComponent, old)
val serverSigned = updatedInProgressClientSigned.serverSign(serverKey)
serverSigned.left.map(s => ChannelClosedWithEscrow(s,serverSPK))
serverSigned.left.map(s => ChannelClosedWithEscrow(s, serverSPK))
}
}
@ -316,29 +328,32 @@ sealed trait ChannelClosedWithEscrow extends ChannelClosed {
object ChannelAwaitingAnchorTx {
private case class ChannelAwaitAnchorTxImpl(anchorTx: Transaction, lock: EscrowTimeoutScriptPubKey,
confirmations: Long) extends ChannelAwaitingAnchorTx {
confirmations: Long) extends ChannelAwaitingAnchorTx {
private val expectedScriptPubKey = P2WSHWitnessSPKV0(lock)
require(anchorTx.outputs.exists(_.scriptPubKey == expectedScriptPubKey),
"One output on the Anchor Transaction has to have a P2SH(P2WSH(EscrowTimeoutScriptPubKey))")
require(
anchorTx.outputs.exists(_.scriptPubKey == expectedScriptPubKey),
"One output on the Anchor Transaction has to have a P2SH(P2WSH(EscrowTimeoutScriptPubKey))"
)
require(lockedAmount >= Policy.minChannelAmount, "We need to lock at least " + Policy.minChannelAmount +
" in the payment channel, got: " + lockedAmount)
}
/** Initializes a payment channel with the given anchor transaction and [[EscrowTimeoutScriptPubKey]]
* Assumes that the anchor transaction has zero confirmations
*/
def apply(anchorTx: Transaction, lock: EscrowTimeoutScriptPubKey): Either[ChannelAwaitingAnchorTx,TxBuilderError] = {
ChannelAwaitingAnchorTx(anchorTx,lock,0)
/**
* Initializes a payment channel with the given anchor transaction and [[EscrowTimeoutScriptPubKey]]
* Assumes that the anchor transaction has zero confirmations
*/
def apply(anchorTx: Transaction, lock: EscrowTimeoutScriptPubKey): Either[ChannelAwaitingAnchorTx, TxBuilderError] = {
ChannelAwaitingAnchorTx(anchorTx, lock, 0)
}
def apply(anchorTx: Transaction, lock: EscrowTimeoutScriptPubKey, confirmations: Long): Either[ChannelAwaitingAnchorTx,TxBuilderError] = {
def apply(anchorTx: Transaction, lock: EscrowTimeoutScriptPubKey, confirmations: Long): Either[ChannelAwaitingAnchorTx, TxBuilderError] = {
val expectedScriptPubKey = P2WSHWitnessSPKV0(lock)
val output = anchorTx.outputs.find(_.scriptPubKey == expectedScriptPubKey)
if (output.isEmpty) {
Right(TxBuilderError.WrongWitness)
} else if (output.get.value < Policy.minChannelAmount) {
Right(TxBuilderError.OutputBelowDustThreshold)
} else Left(ChannelAwaitAnchorTxImpl(anchorTx,lock,confirmations))
} else Left(ChannelAwaitAnchorTxImpl(anchorTx, lock, confirmations))
}
}
@ -347,15 +362,14 @@ object ChannelInProgress {
clientChangeSPK: ScriptPubKey, current: WitnessTxSigComponent,
old: Seq[WitnessTxSigComponent]) extends ChannelInProgress
def apply(anchorTx: Transaction, lock: EscrowTimeoutScriptPubKey, clientSPK: ScriptPubKey,
current: WitnessTxSigComponent): ChannelInProgress = {
ChannelInProgress(anchorTx,lock,clientSPK, current,Nil)
ChannelInProgress(anchorTx, lock, clientSPK, current, Nil)
}
def apply(anchorTx: Transaction, lock: EscrowTimeoutScriptPubKey, clientSPK: ScriptPubKey,
current: WitnessTxSigComponent, old: Seq[WitnessTxSigComponent]): ChannelInProgress = {
ChannelInProgressImpl(anchorTx,lock,clientSPK,current,old)
current: WitnessTxSigComponent, old: Seq[WitnessTxSigComponent]): ChannelInProgress = {
ChannelInProgressImpl(anchorTx, lock, clientSPK, current, old)
}
}
@ -366,12 +380,12 @@ object ChannelInProgressClientSigned {
def apply(anchorTx: Transaction, lock: EscrowTimeoutScriptPubKey, clientSPK: ScriptPubKey,
current: WitnessTxSigComponent): ChannelInProgressClientSigned = {
ChannelInProgressClientSigned(anchorTx,lock, clientSPK, current,Nil)
ChannelInProgressClientSigned(anchorTx, lock, clientSPK, current, Nil)
}
def apply(anchorTx: Transaction, lock: EscrowTimeoutScriptPubKey, clientChangeSPK: ScriptPubKey,
current: WitnessTxSigComponent, old: Seq[WitnessTxSigComponent]): ChannelInProgressClientSigned = {
ChannelInProgressClientSignedImpl(anchorTx,lock, clientChangeSPK, current,old)
ChannelInProgressClientSignedImpl(anchorTx, lock, clientChangeSPK, current, old)
}
}
@ -385,11 +399,11 @@ object ChannelClosedWithEscrow {
def apply(anchorTx: Transaction, lock: EscrowTimeoutScriptPubKey, current: WitnessTxSigComponent,
old: Seq[WitnessTxSigComponent], clientSPK: ScriptPubKey, serverSPK: ScriptPubKey): ChannelClosedWithEscrow = {
ChannelClosedWithEscrowImpl(anchorTx,lock,current,old,clientSPK, serverSPK)
ChannelClosedWithEscrowImpl(anchorTx, lock, current, old, clientSPK, serverSPK)
}
def apply(i: ChannelInProgress, serverSPK: ScriptPubKey): ChannelClosedWithEscrow = {
ChannelClosedWithEscrowImpl(i.anchorTx,i.lock,i.current,i.old,i.clientChangeSPK,serverSPK)
ChannelClosedWithEscrowImpl(i.anchorTx, i.lock, i.current, i.old, i.clientChangeSPK, serverSPK)
}
}
@ -397,33 +411,36 @@ object ChannelClosedWithTimeout {
private case class ChannelClosedWithTimeoutImpl(anchorTx: Transaction, lock: EscrowTimeoutScriptPubKey,
current: WitnessTxSigComponent, old: Seq[WitnessTxSigComponent],
clientChangeSPK: ScriptPubKey) extends ChannelClosedWithTimeout {
require(current.transaction.outputs.exists(_.scriptPubKey == clientChangeSPK),
"Client SPK was not defined on a output. This is SPK that is suppose to refund the client it's money")
require(
current.transaction.outputs.exists(_.scriptPubKey == clientChangeSPK),
"Client SPK was not defined on a output. This is SPK that is suppose to refund the client it's money"
)
}
def apply(anchorTx: Transaction, lock: EscrowTimeoutScriptPubKey,
current: WitnessTxSigComponent, old: Seq[WitnessTxSigComponent],
clientSPK: ScriptPubKey): ChannelClosedWithTimeout = {
ChannelClosedWithTimeoutImpl(anchorTx,lock,current,old,clientSPK)
ChannelClosedWithTimeoutImpl(anchorTx, lock, current, old, clientSPK)
}
/** Attempts to close the [[Channel]] because the [[org.bitcoins.core.protocol.script.EscrowTimeoutScriptPubKey]]
* has timed out.
* Note that this does not require any confirmations on the anchor tx,
* this is because the Client is essentially refunding himself the money
*/
/**
* Attempts to close the [[Channel]] because the [[org.bitcoins.core.protocol.script.EscrowTimeoutScriptPubKey]]
* has timed out.
* Note that this does not require any confirmations on the anchor tx,
* this is because the Client is essentially refunding himself the money
*/
def closeWithTimeout(chan: Channel, clientChangeSPK: ScriptPubKey,
clientKey: ECPrivateKey, fee: CurrencyUnit): Either[ChannelClosedWithTimeout,TxBuilderError] = {
clientKey: ECPrivateKey, fee: CurrencyUnit): Either[ChannelClosedWithTimeout, TxBuilderError] = {
val timeout = chan.lock.timeout
val scriptNum = timeout.locktime
val sequence = UInt32(scriptNum.toLong)
val outputs = Seq(TransactionOutput(chan.lockedAmount - fee, clientChangeSPK))
val outPoint = TransactionOutPoint(chan.anchorTx.txId,UInt32(chan.outputIndex))
val input = TransactionInput(outPoint,EmptyScriptSignature,sequence)
val outPoint = TransactionOutPoint(chan.anchorTx.txId, UInt32(chan.outputIndex))
val input = TransactionInput(outPoint, EmptyScriptSignature, sequence)
val inputs = Seq(input)
val inputIndex = UInt32(inputs.indexOf(input))
val signed: Either[WitnessTxSigComponent,TxBuilderError] = EscrowTimeoutHelper.closeWithTimeout(inputs,outputs, inputIndex, clientKey,
chan.lock,chan.lockingOutput,HashType.sigHashSingleAnyoneCanPay)
signed.left.map(t => ChannelClosedWithTimeout(chan.anchorTx,chan.lock,t,Nil,clientChangeSPK))
val signed: Either[WitnessTxSigComponent, TxBuilderError] = EscrowTimeoutHelper.closeWithTimeout(inputs, outputs, inputIndex, clientKey,
chan.lock, chan.lockingOutput, HashType.sigHashSingleAnyoneCanPay)
signed.left.map(t => ChannelClosedWithTimeout(chan.anchorTx, chan.lock, t, Nil, clientChangeSPK))
}
}

View file

@ -2,10 +2,9 @@ package org.bitcoins.core.config
import org.bitcoins.core.protocol.blockchain._
/**
* Created by chris on 7/27/15.
*/
*/
sealed abstract class NetworkParameters {
/** The parameters of the blockchain we are connecting to */
def chainParams: ChainParams
@ -14,19 +13,19 @@ sealed abstract class NetworkParameters {
def p2shNetworkByte: Seq[Byte] = chainParams.base58Prefix(Base58Type.ScriptAddress)
def privateKey: Seq[Byte] = chainParams.base58Prefix(Base58Type.SecretKey)
def port : Int
def port: Int
def rpcPort: Int
def name: String = chainParams.networkId
/** The seeds used to bootstrap the network */
def dnsSeeds : Seq[String]
def dnsSeeds: Seq[String]
/**
* The message start string is designed to be unlikely to occur in normal data.
* The characters are rarely used upper ASCII, not valid as UTF-8, and produce
* a large 32-bit integer with any alignment.
* https://github.com/bitcoin/bitcoin/blob/master/src/chainparams.cpp#L108
*/
def magicBytes : Seq[Byte]
* The message start string is designed to be unlikely to occur in normal data.
* The characters are rarely used upper ASCII, not valid as UTF-8, and produce
* a large 32-bit integer with any alignment.
* https://github.com/bitcoin/bitcoin/blob/master/src/chainparams.cpp#L108
*/
def magicBytes: Seq[Byte]
/** In bitcoin, the network recaculates the difficulty for the network every 2016 blocks */
def difficultyChangeThreshold: Int
@ -44,8 +43,8 @@ sealed abstract class MainNet extends BitcoinNetwork {
override def rpcPort = 8332
//mainnet doesn't need to be specified like testnet or regtest
override def name = ""
override def dnsSeeds = Seq("seed.bitcoin.sipa.be","dnsseed.bluematt.me","dnsseed.bitcoin.dashjr.org",
"seed.bitcoinstats.com","bitseed.xf2.org","seed.bitcoin.jonasschnelli.ch")
override def dnsSeeds = Seq("seed.bitcoin.sipa.be", "dnsseed.bluematt.me", "dnsseed.bitcoin.dashjr.org",
"seed.bitcoinstats.com", "bitseed.xf2.org", "seed.bitcoin.jonasschnelli.ch")
override def magicBytes = Seq(0xf9.toByte, 0xbe.toByte, 0xb4.toByte, 0xd9.toByte)
@ -58,8 +57,10 @@ sealed abstract class TestNet3 extends BitcoinNetwork {
override def chainParams = TestNetChainParams
override def port = 18333
override def rpcPort = 18332
override def dnsSeeds = Seq("testnet-seed.bitcoin.petertodd.org",
"testnet-seed.bluematt.me","testnet-seed.bitcoin.schildbach.de")
override def dnsSeeds = Seq(
"testnet-seed.bitcoin.petertodd.org",
"testnet-seed.bluematt.me", "testnet-seed.bitcoin.schildbach.de"
)
override def magicBytes = Seq(0x0b.toByte, 0x11.toByte, 0x09.toByte, 0x07.toByte)
override def difficultyChangeThreshold: Int = 2016
@ -78,7 +79,6 @@ sealed abstract class RegTest extends BitcoinNetwork {
object RegTest extends RegTest
object Networks {
val knownNetworks: Seq[NetworkParameters] = Seq(MainNet, TestNet3, RegTest)
val secretKeyBytes = knownNetworks.map(_.privateKey)
@ -94,6 +94,6 @@ object Networks {
TestNet3.p2shNetworkByte -> TestNet3,
TestNet3.privateKey -> TestNet3
//ommitting regtest as it has the same network bytes as testnet3
//ommitting regtest as it has the same network bytes as testnet3
)
}

View file

@ -1,14 +1,14 @@
package org.bitcoins.core.consensus
import org.bitcoins.core.currency.{CurrencyUnit, Satoshis}
import org.bitcoins.core.currency.{ CurrencyUnit, Satoshis }
import org.bitcoins.core.number.Int64
import org.bitcoins.core.protocol.blockchain.Block
import org.bitcoins.core.protocol.transaction.{BaseTransaction, WitnessTransaction}
import org.bitcoins.core.protocol.transaction.{ BaseTransaction, WitnessTransaction }
import org.bitcoins.core.serializers.transaction.RawBaseTransactionParser
/**
* Created by chris on 5/13/16.
*/
* Created by chris on 5/13/16.
*/
sealed abstract class Consensus {
def maxBlockSize: Long = 1000000
@ -18,12 +18,12 @@ sealed abstract class Consensus {
def maxBlockWeight: Long = maxBlockSize * weightScalar
/**
* BIP141 changes this from 20,000 -> 80,000, to see how sigops are counted please see BIP 141
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#sigops]]
*/
* BIP141 changes this from 20,000 -> 80,000, to see how sigops are counted please see BIP 141
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#sigops]]
*/
def maxSigOps = 80000
def maxMoney : CurrencyUnit = Satoshis(Int64(2100000000000000L))
def maxMoney: CurrencyUnit = Satoshis(Int64(2100000000000000L))
}
object Consensus extends Consensus

View file

@ -2,47 +2,48 @@ package org.bitcoins.core.consensus
import org.bitcoins.core.crypto.DoubleSha256Digest
import org.bitcoins.core.protocol.blockchain.Block
import org.bitcoins.core.protocol.transaction.{BaseTransaction, Transaction, WitnessTransaction}
import org.bitcoins.core.protocol.transaction.{ BaseTransaction, Transaction, WitnessTransaction }
import org.bitcoins.core.util._
import scala.annotation.tailrec
/**
* Created by chris on 5/24/16.
* This trait contains all functionality related to computing merkle trees
* Mimics this functionality inside of bitcoin core
* [[https://github.com/bitcoin/bitcoin/blob/master/src/consensus/merkle.cpp]]
*/
* Created by chris on 5/24/16.
* This trait contains all functionality related to computing merkle trees
* Mimics this functionality inside of bitcoin core
* [[https://github.com/bitcoin/bitcoin/blob/master/src/consensus/merkle.cpp]]
*/
trait Merkle extends BitcoinSLogger {
type MerkleTree = BinaryTree[DoubleSha256Digest]
/**
* Computes the merkle root for the given block
* @param block the given block that needs the merkle root computed
* @return the hash representing the merkle root for this block
*/
def computeBlockMerkleRoot(block : Block) : DoubleSha256Digest = computeMerkleRoot(block.transactions)
* Computes the merkle root for the given block
* @param block the given block that needs the merkle root computed
* @return the hash representing the merkle root for this block
*/
def computeBlockMerkleRoot(block: Block): DoubleSha256Digest = computeMerkleRoot(block.transactions)
/**
* Computes the merkle root for the given sequence of transactions
* @param transactions the list of transactions whose merkle root needs to be computed
* @return the merkle root for the sequence of transactions
*/
def computeMerkleRoot(transactions : Seq[Transaction]) : DoubleSha256Digest = transactions match {
case Nil => throw new IllegalArgumentException("We cannot have zero transactions in the block. There always should be ATLEAST one - the coinbase tx")
* Computes the merkle root for the given sequence of transactions
* @param transactions the list of transactions whose merkle root needs to be computed
* @return the merkle root for the sequence of transactions
*/
def computeMerkleRoot(transactions: Seq[Transaction]): DoubleSha256Digest = transactions match {
case Nil => throw new IllegalArgumentException("We cannot have zero transactions in the block. There always should be ATLEAST one - the coinbase tx")
case h :: Nil => h.txId
case h :: t =>
val leafs = transactions.map(tx => Leaf(tx.txId))
val merkleTree = build(leafs,Nil)
val merkleTree = build(leafs, Nil)
merkleTree.value.get
}
/** Builds a [[MerkleTree]] from sequence of sub merkle trees.
* This subTrees can be individual txids (leafs) or full blown subtrees
* @param subTrees the trees that need to be hashed
* @param accum the accumulated merkle trees, waiting to be hashed next round
* @return the entire Merkle tree computed from the given merkle trees
*/
/**
* Builds a [[MerkleTree]] from sequence of sub merkle trees.
* This subTrees can be individual txids (leafs) or full blown subtrees
* @param subTrees the trees that need to be hashed
* @param accum the accumulated merkle trees, waiting to be hashed next round
* @return the entire Merkle tree computed from the given merkle trees
*/
@tailrec
final def build(subTrees: Seq[MerkleTree], accum: Seq[MerkleTree]): MerkleTree = subTrees match {
case Nil =>
@ -51,46 +52,48 @@ trait Merkle extends BitcoinSLogger {
else build(accum.reverse, Nil)
case h :: h1 :: t =>
logger.debug("Subtrees: " + subTrees)
val newTree = computeTree(h,h1)
val newTree = computeTree(h, h1)
build(t, newTree +: accum)
case h :: t =>
logger.debug("Subtrees: " + subTrees)
//means that we have an odd amount of txids, this means we duplicate the last hash in the tree
val newTree = computeTree(h,h)
val newTree = computeTree(h, h)
build(t, newTree +: accum)
}
/** Builds a merkle tree from a sequence of hashes */
def build(hashes: Seq[DoubleSha256Digest]): MerkleTree = {
val leafs = hashes.map(Leaf(_))
build(leafs,Nil)
build(leafs, Nil)
}
/** Computes the merkle tree of two sub merkle trees */
def computeTree(tree1: MerkleTree, tree2: MerkleTree): MerkleTree = {
val bytes = tree1.value.get.bytes ++ tree2.value.get.bytes
val hash = CryptoUtil.doubleSHA256(bytes)
Node(hash,tree1,tree2)
Node(hash, tree1, tree2)
}
/** Computes the commitment of the block to the wtxids
* See the definition of a block commitment in BIP141
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#commitment-structure]]
* [[https://github.com/bitcoin/bitcoin/blob/7490ae8b699d2955b665cf849d86ff5bb5245c28/src/consensus/merkle.cpp#L168]]
*/
/**
* Computes the commitment of the block to the wtxids
* See the definition of a block commitment in BIP141
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#commitment-structure]]
* [[https://github.com/bitcoin/bitcoin/blob/7490ae8b699d2955b665cf849d86ff5bb5245c28/src/consensus/merkle.cpp#L168]]
*/
def computeBlockWitnessMerkleTree(block: Block): MerkleTree = {
val coinbaseWTxId = CryptoUtil.emptyDoubleSha256Hash
val hashes = block.transactions.tail.map {
case wtx: WitnessTransaction => wtx.wTxId
case btx: BaseTransaction => btx.txId
case btx: BaseTransaction => btx.txId
}
build(coinbaseWTxId +: hashes)
}
/** Computes the merkle root for the committment inside of a coinbase txs scriptPubKey
* See BIP141
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#commitment-structure]]
* */
/**
* Computes the merkle root for the committment inside of a coinbase txs scriptPubKey
* See BIP141
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#commitment-structure]]
*/
def computeBlockWitnessMerkleRoot(block: Block): DoubleSha256Digest = {
computeBlockWitnessMerkleTree(block).value.get
}

View file

@ -17,7 +17,7 @@ sealed abstract class DERSignatureUtil {
* NOTE: This will fail if this signature contains the hash type appended to the end of it
* @return boolean representing if the signature is a valid
*/
def isDEREncoded(signature : ECDigitalSignature) : Boolean = isDEREncoded(signature.bytes)
def isDEREncoded(signature: ECDigitalSignature): Boolean = isDEREncoded(signature.bytes)
/**
* Checks if the bytes are encoded to DER correctly
@ -25,7 +25,7 @@ sealed abstract class DERSignatureUtil {
* This will fail if this signature contains the hash type appended to the end of it
* @return boolean representing if the signature is a valid
*/
def isDEREncoded(bytes : Seq[Byte]) : Boolean = {
def isDEREncoded(bytes: Seq[Byte]): Boolean = {
//signature is trivially valid if the signature is empty
if (bytes.nonEmpty && bytes.size < 9) false
else if (bytes.nonEmpty) {
@ -34,13 +34,13 @@ sealed abstract class DERSignatureUtil {
//second byte must indicate the length of the remaining byte array
val signatureSize = bytes(1).toLong
//checks to see if the signature length is the same as the signatureSize val
val signatureLengthIsCorrect = signatureSize == bytes.slice(2,bytes.size).size
val signatureLengthIsCorrect = signatureSize == bytes.slice(2, bytes.size).size
//third byte must be 0x02
val thirdByteIs0x02 = bytes(2) == 0x02
//this is the size of the r value in the signature
val rSize = bytes(3)
//r value in the signature
val r = bytes.slice(3,rSize+3)
val r = bytes.slice(3, rSize + 3)
//this 0x02 separates the r and s value )in the signature
val second0x02Exists = bytes(rSize + 4) == 0x02
//this is the size of the s value in the signature
@ -52,13 +52,12 @@ sealed abstract class DERSignatureUtil {
} else true
}
/**
* Decodes the given digital signature into it's r and s points
* @param signature
* @return
*/
def decodeSignature(signature : ECDigitalSignature) : (BigInt,BigInt) = decodeSignature(signature.bytes)
def decodeSignature(signature: ECDigitalSignature): (BigInt, BigInt) = decodeSignature(signature.bytes)
/**
* Decodes the given sequence of bytes into it's r and s points
@ -66,30 +65,30 @@ sealed abstract class DERSignatureUtil {
* @param bytes
* @return
*/
def decodeSignature(bytes : Seq[Byte]) : (BigInt,BigInt) = {
def decodeSignature(bytes: Seq[Byte]): (BigInt, BigInt) = {
logger.debug("Signature to decode: " + BitcoinSUtil.encodeHex(bytes))
val asn1InputStream = new ASN1InputStream(bytes.toArray)
//TODO: this is nasty, is there any way to get rid of all this casting???
//TODO: Not 100% this is completely right for signatures that are incorrectly DER encoded
//the behavior right now is to return the defaults in the case the signature is not DER encoded
//https://stackoverflow.com/questions/2409618/how-do-i-decode-a-der-encoded-string-in-java
val seq : DLSequence = Try(asn1InputStream.readObject.asInstanceOf[DLSequence]) match {
val seq: DLSequence = Try(asn1InputStream.readObject.asInstanceOf[DLSequence]) match {
case Success(seq) => seq
case Failure(err) => new DLSequence()
}
val default = new ASN1Integer(0)
val r : ASN1Integer = Try(seq.getObjectAt(0).asInstanceOf[ASN1Integer]) match {
val r: ASN1Integer = Try(seq.getObjectAt(0).asInstanceOf[ASN1Integer]) match {
case Success(r) =>
//this is needed for a bug inside of bouncy castle where zero length values throw an exception
//we need to treat these like zero
Try(r.getValue) match {
case Success(_) => r
case Failure(_) => default
}
}
case Failure(_) => default
}
logger.debug("r: " + r)
val s : ASN1Integer = Try(seq.getObjectAt(1).asInstanceOf[ASN1Integer]) match {
val s: ASN1Integer = Try(seq.getObjectAt(1).asInstanceOf[ASN1Integer]) match {
case Success(s) =>
//this is needed for a bug inside of bouncy castle where zero length values throw an exception
//we need to treat these like zero
@ -111,14 +110,13 @@ sealed abstract class DERSignatureUtil {
* @param signature the signature to check if they are strictly der encoded
* @return boolean indicating whether the signature was der encoded or not
*/
def isValidSignatureEncoding(signature : ECDigitalSignature) : Boolean = {
def isValidSignatureEncoding(signature: ECDigitalSignature): Boolean = {
signature match {
case EmptyDigitalSignature => true
case signature : ECDigitalSignature => isValidSignatureEncoding(signature.bytes)
case EmptyDigitalSignature => true
case signature: ECDigitalSignature => isValidSignatureEncoding(signature.bytes)
}
}
/**
* This functions implements the strict der encoding rules that were created in BIP66
* https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki
@ -126,7 +124,7 @@ sealed abstract class DERSignatureUtil {
* @param bytes the bytes to check if they are strictly der encoded
* @return boolean indicating whether the bytes were der encoded or not
*/
def isValidSignatureEncoding(bytes : Seq[Byte]) : Boolean = {
def isValidSignatureEncoding(bytes: Seq[Byte]): Boolean = {
// Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] [sighash]
// * total-length: 1-byte length descriptor of everything that follows,
// excluding the sighash byte.
@ -186,7 +184,7 @@ sealed abstract class DERSignatureUtil {
// Null bytes at the start of R are not allowed, unless R would
// otherwise be interpreted as a negative number.
if (rSize > 1 && (bytes(4) == 0x00) && !((bytes(5) & 0x80) != 0 )) return false
if (rSize > 1 && (bytes(4) == 0x00) && !((bytes(5) & 0x80) != 0)) return false
//logger.debug("There were not any null bytes at the start of R")
// Check whether the S element is an integer.
if (bytes(rSize + 4) != 0x02) return false
@ -207,6 +205,13 @@ sealed abstract class DERSignatureUtil {
true
}
/**
* Requires the S value in signatures to be the low version of the S value
* https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#low-s-values-in-signatures
* @param signature
* @return if the S value is the low version
*/
def isLowS(signature: ECDigitalSignature): Boolean = isLowS(signature.bytes)
/**
* Requires the S value in signatures to be the low version of the S value
@ -214,29 +219,21 @@ sealed abstract class DERSignatureUtil {
* @param signature
* @return if the S value is the low version
*/
def isLowS(signature : ECDigitalSignature) : Boolean = isLowS(signature.bytes)
/**
* Requires the S value in signatures to be the low version of the S value
* https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#low-s-values-in-signatures
* @param signature
* @return if the S value is the low version
*/
def isLowS(signature : Seq[Byte]) : Boolean = {
def isLowS(signature: Seq[Byte]): Boolean = {
val result = Try {
val (r,s) = decodeSignature(signature)
val (r, s) = decodeSignature(signature)
s.bigInteger.compareTo(CryptoParams.halfCurveOrder) <= 0
}
result match {
case Success(bool) => bool
case Failure(_) => false
case Failure(_) => false
}
}
/** Checks if the given digital signature uses a low s value, if it does not it converts it to a low s value and returns it */
def lowS(signature: ECDigitalSignature): ECDigitalSignature = {
val sigLowS = if (isLowS(signature)) signature
else ECDigitalSignature(signature.r,CryptoParams.curve.getN().subtract(signature.s.bigInteger))
else ECDigitalSignature(signature.r, CryptoParams.curve.getN().subtract(signature.s.bigInteger))
require(DERSignatureUtil.isLowS(sigLowS))
sigLowS
}

View file

@ -1,15 +1,15 @@
package org.bitcoins.core.crypto
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil, Factory}
import org.bitcoins.core.util.{ BitcoinSLogger, BitcoinSUtil, Factory }
/**
* Created by chris on 2/26/16.
*/
sealed abstract class ECDigitalSignature {
private val logger = BitcoinSLogger.logger
def hex : String = BitcoinSUtil.encodeHex(bytes)
def hex: String = BitcoinSUtil.encodeHex(bytes)
def bytes : Seq[Byte]
def bytes: Seq[Byte]
def isEmpty = bytes.isEmpty
@ -20,12 +20,12 @@ sealed abstract class ECDigitalSignature {
* https://crypto.stackexchange.com/questions/1795/how-can-i-convert-a-der-ecdsa-signature-to-asn-1
* @return boolean representing if the signature is a valid
*/
def isDEREncoded : Boolean = DERSignatureUtil.isDEREncoded(this)
def isDEREncoded: Boolean = DERSignatureUtil.isDEREncoded(this)
/** Checks if the signature is strictly der encoded as per BIP66
* [[https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki]]
* */
/**
* Checks if the signature is strictly der encoded as per BIP66
* [[https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki]]
*/
def isStrictEncoded: Boolean = DERSignatureUtil.isValidSignatureEncoding(this)
/**
@ -33,13 +33,11 @@ sealed abstract class ECDigitalSignature {
* throws an exception if the given sequence of bytes is not a DER encoded signature
* @return the (r,s) values for the elliptic curve digital signature
*/
def decodeSignature : (BigInt,BigInt) = DERSignatureUtil.decodeSignature(this)
def decodeSignature: (BigInt, BigInt) = DERSignatureUtil.decodeSignature(this)
/** Represents the r value found in a elliptic curve digital signature */
def r = decodeSignature._1
/** Represents the s value found in a elliptic curve digital signature */
def s = decodeSignature._2
@ -51,11 +49,10 @@ case object EmptyDigitalSignature extends ECDigitalSignature {
override def s = r
}
object ECDigitalSignature extends Factory[ECDigitalSignature] {
private case class ECDigitalSignatureImpl(bytes : Seq[Byte]) extends ECDigitalSignature
private case class ECDigitalSignatureImpl(bytes: Seq[Byte]) extends ECDigitalSignature
override def fromBytes(bytes : Seq[Byte]) : ECDigitalSignature = {
override def fromBytes(bytes: Seq[Byte]): ECDigitalSignature = {
//this represents the empty signature
if (bytes.size == 1 && bytes.head == 0x0) EmptyDigitalSignature
else if (bytes.size == 0) EmptyDigitalSignature
@ -68,20 +65,20 @@ object ECDigitalSignature extends Factory[ECDigitalSignature] {
}
}
def apply(r : BigInt, s : BigInt) = fromRS(r,s)
def apply(r: BigInt, s: BigInt) = fromRS(r, s)
/**
* Takes in the r and s component of a digital signature and gives back a ECDigitalSignature object
* The ECDigitalSignature object complies with strict der encoding as per BIP62
* note: That the hash type for the signature CANNOT be added to the digital signature
*
* @param r the r component of the digital signature
* @param s the s component of the digital signature
* @return
*/
def fromRS(r : BigInt, s : BigInt) : ECDigitalSignature = {
* Takes in the r and s component of a digital signature and gives back a ECDigitalSignature object
* The ECDigitalSignature object complies with strict der encoding as per BIP62
* note: That the hash type for the signature CANNOT be added to the digital signature
*
* @param r the r component of the digital signature
* @param s the s component of the digital signature
* @return
*/
def fromRS(r: BigInt, s: BigInt): ECDigitalSignature = {
val rsSize = r.toByteArray.size + s.toByteArray.size
val totalSize = 4 + rsSize
val bytes : Seq[Byte] = Seq(0x30.toByte, totalSize.toByte, 0x2.toByte, r.toByteArray.size.toByte) ++
val bytes: Seq[Byte] = Seq(0x30.toByte, totalSize.toByte, 0x2.toByte, r.toByteArray.size.toByte) ++
r.toByteArray.toSeq ++ Seq(0x2.toByte, s.toByteArray.size.toByte) ++ s.toByteArray.toSeq
fromBytes(bytes)
}

View file

@ -17,36 +17,37 @@ import scala.annotation.tailrec
import scala.util.{Failure, Success, Try}
/**
* Created by chris on 2/16/16.
*/
* Created by chris on 2/16/16.
*/
sealed abstract class BaseECKey extends NetworkElement {
/**
* Signs a given sequence of bytes with the signingKey
* @param dataToSign the bytes to be signed
* @param signingKey the key to sign the bytes with
* @return the digital signature
*/
private def sign(dataToSign : Seq[Byte], signingKey : BaseECKey): ECDigitalSignature = {
* Signs a given sequence of bytes with the signingKey
* @param dataToSign the bytes to be signed
* @param signingKey the key to sign the bytes with
* @return the digital signature
*/
private def sign(dataToSign: Seq[Byte], signingKey: BaseECKey): ECDigitalSignature = {
require(dataToSign.length == 32 && signingKey.bytes.length <= 32)
val signature = NativeSecp256k1.sign(dataToSign.toArray, signingKey.bytes.toArray)
ECDigitalSignature(signature)
}
def sign(dataToSign: Seq[Byte]): ECDigitalSignature = sign(dataToSign,this)
def sign(dataToSign: Seq[Byte]): ECDigitalSignature = sign(dataToSign, this)
def sign(hash: HashDigest, signingKey: BaseECKey): ECDigitalSignature = sign(hash.bytes,signingKey)
def sign(hash: HashDigest, signingKey: BaseECKey): ECDigitalSignature = sign(hash.bytes, signingKey)
def sign(hash: HashDigest): ECDigitalSignature = sign(hash,this)
def sign(hash: HashDigest): ECDigitalSignature = sign(hash, this)
@deprecated("Deprecated in favor of signing algorithm inside of secp256k1", "2/20/2017")
private def oldSign(dataToSign: Seq[Byte], signingKey: BaseECKey): ECDigitalSignature = {
val signer: ECDSASigner = new ECDSASigner(new HMacDSAKCalculator(new SHA256Digest()))
val privKey: ECPrivateKeyParameters = new ECPrivateKeyParameters(
new BigInteger(1,signingKey.bytes.toArray), CryptoParams.curve)
new BigInteger(1, signingKey.bytes.toArray), CryptoParams.curve
)
signer.init(true, privKey)
val components : Array[BigInteger] = signer.generateSignature(dataToSign.toArray)
val (r,s) = (components(0),components(1))
val signature = ECDigitalSignature(r,s)
val components: Array[BigInteger] = signer.generateSignature(dataToSign.toArray)
val (r, s) = (components(0), components(1))
val signature = ECDigitalSignature(r, s)
//make sure the signature follows BIP62's low-s value
//https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#Low_S_values_in_signatures
//bitcoinj implementation
@ -58,24 +59,24 @@ sealed abstract class BaseECKey extends NetworkElement {
}
/**
* Created by chris on 2/16/16.
*/
* Created by chris on 2/16/16.
*/
sealed abstract class ECPrivateKey extends BaseECKey {
/** Signifies if the this private key corresponds to a compressed public key */
def isCompressed : Boolean
def isCompressed: Boolean
/** Derives the public for a the private key */
def publicKey : ECPublicKey = {
val pubKeyBytes : Seq[Byte] = NativeSecp256k1.computePubkey(bytes.toArray, isCompressed)
def publicKey: ECPublicKey = {
val pubKeyBytes: Seq[Byte] = NativeSecp256k1.computePubkey(bytes.toArray, isCompressed)
require(NativeSecp256k1.isValidPubKey(pubKeyBytes.toArray), "secp256k1 failed to generate a valid public key, got: " + BitcoinSUtil.encodeHex(pubKeyBytes))
ECPublicKey(pubKeyBytes)
}
/**
* Converts a [[ECPrivateKey]] to WIF
* https://en.bitcoin.it/wiki/Wallet_import_format
* @return
*/
* Converts a [[ECPrivateKey]] to WIF
* https://en.bitcoin.it/wiki/Wallet_import_format
* @return
*/
def toWIF(network: NetworkParameters): String = {
val networkByte = network.privateKey
//append 1 byte to the end of the priv key byte representation if we need a compressed pub key
@ -86,28 +87,27 @@ sealed abstract class ECPrivateKey extends BaseECKey {
Base58.encode(encodedPrivKey)
}
override def toString = "ECPrivateKey(" + hex + "," + isCompressed +")"
override def toString = "ECPrivateKey(" + hex + "," + isCompressed + ")"
}
object ECPrivateKey extends Factory[ECPrivateKey] {
private case class ECPrivateKeyImpl(override val bytes : Seq[Byte], isCompressed: Boolean) extends ECPrivateKey {
private case class ECPrivateKeyImpl(override val bytes: Seq[Byte], isCompressed: Boolean) extends ECPrivateKey {
require(NativeSecp256k1.secKeyVerify(bytes.toArray), "Invalid key according to secp256k1, hex: " + BitcoinSUtil.encodeHex(bytes))
}
override def fromBytes(bytes : Seq[Byte]) : ECPrivateKey = fromBytes(bytes,true)
override def fromBytes(bytes: Seq[Byte]): ECPrivateKey = fromBytes(bytes, true)
@tailrec
def fromBytes(bytes: Seq[Byte], isCompressed: Boolean): ECPrivateKey = {
if (bytes.size == 32) ECPrivateKeyImpl(bytes,isCompressed)
if (bytes.size == 32) ECPrivateKeyImpl(bytes, isCompressed)
else if (bytes.size < 32) {
//means we need to pad the private key with 0 bytes so we have 32 bytes
val paddingNeeded = 32 - bytes.size
val padding = for { _ <- 0 until paddingNeeded} yield 0.toByte
val padding = for { _ <- 0 until paddingNeeded } yield 0.toByte
ECPrivateKey.fromBytes(padding ++ bytes, isCompressed)
}
//this is for the case when java serialies a BigInteger to 33 bytes to hold the signed num representation
else if (bytes.size == 33) ECPrivateKey.fromBytes(bytes.slice(1,33), isCompressed)
} //this is for the case when java serialies a BigInteger to 33 bytes to hold the signed num representation
else if (bytes.size == 33) ECPrivateKey.fromBytes(bytes.slice(1, 33), isCompressed)
else throw new IllegalArgumentException("Private keys cannot be greater than 33 bytes in size, got: " +
BitcoinSUtil.encodeHex(bytes) + " which is of size: " + bytes.size)
}
@ -115,47 +115,47 @@ object ECPrivateKey extends Factory[ECPrivateKey] {
def fromHex(hex: String, isCompressed: Boolean): ECPrivateKey = fromBytes(BitcoinSUtil.decodeHex(hex), isCompressed)
/** Generates a fresh [[ECPrivateKey]] that has not been used before. */
def apply() : ECPrivateKey = ECPrivateKey(true)
def apply(): ECPrivateKey = ECPrivateKey(true)
def apply(isCompressed: Boolean) = freshPrivateKey(isCompressed)
/** Generates a fresh [[ECPrivateKey]] that has not been used before. */
def freshPrivateKey : ECPrivateKey = freshPrivateKey(true)
def freshPrivateKey: ECPrivateKey = freshPrivateKey(true)
def freshPrivateKey(isCompressed: Boolean): ECPrivateKey = {
val secureRandom = new SecureRandom
val generator : ECKeyPairGenerator = new ECKeyPairGenerator
val keyGenParams : ECKeyGenerationParameters = new ECKeyGenerationParameters(CryptoParams.curve, secureRandom)
val generator: ECKeyPairGenerator = new ECKeyPairGenerator
val keyGenParams: ECKeyGenerationParameters = new ECKeyGenerationParameters(CryptoParams.curve, secureRandom)
generator.init(keyGenParams)
val keypair : AsymmetricCipherKeyPair = generator.generateKeyPair
val keypair: AsymmetricCipherKeyPair = generator.generateKeyPair
val privParams: ECPrivateKeyParameters = keypair.getPrivate.asInstanceOf[ECPrivateKeyParameters]
val priv : BigInteger = privParams.getD
val priv: BigInteger = privParams.getD
val bytes = priv.toByteArray
ECPrivateKey.fromBytes(bytes,isCompressed)
ECPrivateKey.fromBytes(bytes, isCompressed)
}
/**
* Takes in a base58 string and converts it into a private key.
* Private keys starting with 'K', 'L', or 'c' correspond to compressed public keys.
* https://en.bitcoin.it/wiki/Wallet_import_format
*
* @param WIF Wallet Import Format. Encoded in Base58
* @return
*/
def fromWIFToPrivateKey(WIF : String): ECPrivateKey = {
* Takes in a base58 string and converts it into a private key.
* Private keys starting with 'K', 'L', or 'c' correspond to compressed public keys.
* https://en.bitcoin.it/wiki/Wallet_import_format
*
* @param WIF Wallet Import Format. Encoded in Base58
* @return
*/
def fromWIFToPrivateKey(WIF: String): ECPrivateKey = {
val isCompressed = ECPrivateKey.isCompressed(WIF)
val privateKeyBytes = trimFunction(WIF)
ECPrivateKey.fromBytes(privateKeyBytes,isCompressed)
ECPrivateKey.fromBytes(privateKeyBytes, isCompressed)
}
/**
* Takes in WIF private key as a sequence of bytes and determines if it corresponds to a compressed public key.
* If the private key corresponds to a compressed public key, the last byte should be 0x01, and
* the WIF string will have started with K or L instead of 5 (or c instead of 9 on testnet).
*
* @param bytes private key in bytes
* @return
*/
def isCompressed(bytes : Seq[Byte]): Boolean = {
* Takes in WIF private key as a sequence of bytes and determines if it corresponds to a compressed public key.
* If the private key corresponds to a compressed public key, the last byte should be 0x01, and
* the WIF string will have started with K or L instead of 5 (or c instead of 9 on testnet).
*
* @param bytes private key in bytes
* @return
*/
def isCompressed(bytes: Seq[Byte]): Boolean = {
val validCompressedBytes: Seq[Seq[Byte]] = Networks.secretKeyBytes
val validCompressedBytesInHex: Seq[String] = validCompressedBytes.map(b => BitcoinSUtil.encodeHex(b))
val firstByteHex = BitcoinSUtil.encodeHex(bytes.head)
@ -163,19 +163,19 @@ object ECPrivateKey extends Factory[ECPrivateKey] {
else false
}
def isCompressed(WIF : String) : Boolean = {
def isCompressed(WIF: String): Boolean = {
val bytes = Base58.decode(WIF)
isCompressed(bytes)
}
/**
* When decoding a WIF private key, we drop the first byte (network byte), and the last 4 bytes (checksum).
* If the private key corresponds to a compressed public key, we drop the last byte again.
* https://en.bitcoin.it/wiki/Wallet_import_format
* @param WIF Wallet Import Format. Encoded in Base58
* @return
*/
private def trimFunction(WIF : String): Seq[Byte] = {
* When decoding a WIF private key, we drop the first byte (network byte), and the last 4 bytes (checksum).
* If the private key corresponds to a compressed public key, we drop the last byte again.
* https://en.bitcoin.it/wiki/Wallet_import_format
* @param WIF Wallet Import Format. Encoded in Base58
* @return
*/
private def trimFunction(WIF: String): Seq[Byte] = {
val bytesChecked = Base58.decodeCheck(WIF)
//see https://en.bitcoin.it/wiki/List_of_address_prefixes
@ -192,7 +192,7 @@ object ECPrivateKey extends Factory[ECPrivateKey] {
def compressedKeyPrefixes = Seq(Some('K'), Some('L'), Some('c'))
/** The Base58 prefixes that represent uncompressed private keys */
def uncompressedKeyPrefixes = Seq(Some('5'),Some('9'))
def uncompressedKeyPrefixes = Seq(Some('5'), Some('9'))
/** Returns the [[NetworkParameters]] from a serialized WIF key */
def parseNetworkFromWIF(wif: String): Try[NetworkParameters] = {
@ -211,15 +211,14 @@ object ECPrivateKey extends Factory[ECPrivateKey] {
}
/**
* Created by chris on 2/16/16.
*/
* Created by chris on 2/16/16.
*/
sealed abstract class ECPublicKey extends BaseECKey {
def verify(hash : HashDigest, signature : ECDigitalSignature) : Boolean = verify(hash.bytes, signature)
def verify(hash: HashDigest, signature: ECDigitalSignature): Boolean = verify(hash.bytes, signature)
/** Verifies if a given piece of data is signed by the [[ECPrivateKey]]'s corresponding [[ECPublicKey]]. */
def verify(data : Seq[Byte], signature : ECDigitalSignature): Boolean = {
def verify(data: Seq[Byte], signature: ECDigitalSignature): Boolean = {
logger.debug("PubKey for verifying: " + BitcoinSUtil.encodeHex(bytes))
logger.debug("Data to verify: " + BitcoinSUtil.encodeHex(data))
logger.debug("Signature to check against data: " + signature.hex)
@ -231,11 +230,11 @@ sealed abstract class ECPublicKey extends BaseECKey {
//bitcoin core implements this functionality here:
//https://github.com/bitcoin/bitcoin/blob/master/src/pubkey.cpp#L16-L165
//TODO: Implement functionality in Bitcoin Core linked above
oldVerify(data,signature)
oldVerify(data, signature)
} else result
}
def verify(hex : String, signature : ECDigitalSignature) : Boolean = verify(BitcoinSUtil.decodeHex(hex),signature)
def verify(hex: String, signature: ECDigitalSignature): Boolean = verify(BitcoinSUtil.decodeHex(hex), signature)
override def toString = "ECPublicKey(" + hex + ")"
@ -270,7 +269,7 @@ sealed abstract class ECPublicKey extends BaseECKey {
object ECPublicKey extends Factory[ECPublicKey] {
private case class ECPublicKeyImpl(override val bytes : Seq[Byte]) extends ECPublicKey {
private case class ECPublicKeyImpl(override val bytes: Seq[Byte]) extends ECPublicKey {
//unfortunately we cannot place ANY invariants here
//because of old transactions on the blockchain that have weirdly formatted public keys. Look at example in script_tests.json
//https://github.com/bitcoin/bitcoin/blob/master/src/test/data/script_tests.json#L457
@ -280,23 +279,23 @@ object ECPublicKey extends Factory[ECPublicKey] {
//we cannot do this. If there ever is a hard fork this would be a good thing to add.
}
override def fromBytes(bytes : Seq[Byte]) : ECPublicKey = ECPublicKeyImpl(bytes)
override def fromBytes(bytes: Seq[Byte]): ECPublicKey = ECPublicKeyImpl(bytes)
def apply() = freshPublicKey
/** Generates a fresh [[ECPublicKey]] that has not been used before. */
def freshPublicKey = ECPrivateKey.freshPrivateKey.publicKey
/** Checks if the public key is valid according to secp256k1
* Mimics this function in bitcoin core
* [[https://github.com/bitcoin/bitcoin/blob/27765b6403cece54320374b37afb01a0cfe571c3/src/pubkey.cpp#L207-L212]]
*/
/**
* Checks if the public key is valid according to secp256k1
* Mimics this function in bitcoin core
* [[https://github.com/bitcoin/bitcoin/blob/27765b6403cece54320374b37afb01a0cfe571c3/src/pubkey.cpp#L207-L212]]
*/
def isFullyValid(bytes: Seq[Byte]): Boolean = Try(NativeSecp256k1.isValidPubKey(bytes.toArray)).isSuccess && isValid(bytes)
/**
* Mimics the CPubKey::IsValid function in Bitcoin core, this is a consensus rule
* [[https://github.com/bitcoin/bitcoin/blob/27765b6403cece54320374b37afb01a0cfe571c3/src/pubkey.h#L158]]
*/
* Mimics the CPubKey::IsValid function in Bitcoin core, this is a consensus rule
* [[https://github.com/bitcoin/bitcoin/blob/27765b6403cece54320374b37afb01a0cfe571c3/src/pubkey.h#L158]]
*/
def isValid(bytes: Seq[Byte]): Boolean = bytes.nonEmpty
}
}

View file

@ -3,15 +3,16 @@ package org.bitcoins.core.crypto
import java.math.BigInteger
import org.bitcoin.NativeSecp256k1
import org.bitcoins.core.number.{UInt32, UInt8}
import org.bitcoins.core.number.{ UInt32, UInt8 }
import org.bitcoins.core.protocol.NetworkElement
import org.bitcoins.core.util._
import scala.util.{Failure, Success, Try}
import scala.util.{ Failure, Success, Try }
/** Represents an extended key as defined by BIP32
* [[https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki]]
*/
/**
* Represents an extended key as defined by BIP32
* [[https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki]]
*/
sealed abstract class ExtKey extends NetworkElement {
require(bytes.size == 78, "ExtKey must be 78 bytes in size, got: " + bytes.size)
/** The network and private/public key identifier for this key */
@ -20,14 +21,18 @@ sealed abstract class ExtKey extends NetworkElement {
def depth: UInt8
/** The fingerprint of the parent key */
def fingerprint: Seq[Byte]
/** Child number. This is ser32(i) for i in xi = xpar/i, with xi the key being serialized.
* (0x00000000 if master key) */
/**
* Child number. This is ser32(i) for i in xi = xpar/i, with xi the key being serialized.
* (0x00000000 if master key)
*/
def childNum: UInt32
/** In order to prevent these from depending solely on the key itself,
* we extend both private and public keys first with an extra 256 bits of entropy.
* This extension, called the chain code,
* is identical for corresponding private and public keys, and consists of 32 bytes. */
/**
* In order to prevent these from depending solely on the key itself,
* we extend both private and public keys first with an extra 256 bits of entropy.
* This extension, called the chain code,
* is identical for corresponding private and public keys, and consists of 32 bytes.
*/
def chainCode: ChainCode
/** The key at this path */
@ -70,20 +75,20 @@ object ExtKey extends Factory[ExtKey] {
require(bytes.size == 78, "Not 78 bytes")
val version: Try[ExtKeyVersion] = ExtKeyVersion(bytes.take(4)) match {
case Some(v) => Success(v)
case None => Failure(new IllegalArgumentException("Invalid version for ExtKey"))
case None => Failure(new IllegalArgumentException("Invalid version for ExtKey"))
}
val depth = UInt8(bytes.slice(4,5))
val fp = bytes.slice(5,9)
val childNum = UInt32(bytes.slice(9,13))
val chainCode = ChainCode(bytes.slice(13,45))
val depth = UInt8(bytes.slice(4, 5))
val fp = bytes.slice(5, 9)
val childNum = UInt32(bytes.slice(9, 13))
val chainCode = ChainCode(bytes.slice(13, 45))
val key: Try[ExtKey] = version.map {
case x @ (MainNetPub | TestNet3Pub) =>
val pub = ECPublicKey(bytes.slice(45,78))
ExtPublicKey(x,depth,fp,childNum,chainCode,pub)
val pub = ECPublicKey(bytes.slice(45, 78))
ExtPublicKey(x, depth, fp, childNum, chainCode, pub)
case x @ (MainNetPriv | TestNet3Priv) =>
require(bytes(45) == 0, "Byte at index 46 must be zero for a ExtPrivateKey, got: " + BitcoinSUtil.encodeHex(bytes(45)))
val priv = ECPrivateKey(bytes.slice(46,78))
ExtPrivateKey(x,depth,fp,childNum,chainCode,priv)
val priv = ECPrivateKey(bytes.slice(46, 78))
ExtPrivateKey(x, depth, fp, childNum, chainCode, priv)
}
key
}
@ -99,7 +104,6 @@ object ExtKey extends Factory[ExtKey] {
}
}
sealed abstract class ExtPrivateKey extends ExtKey {
override def key: ECPrivateKey
@ -111,19 +115,19 @@ sealed abstract class ExtPrivateKey extends ExtKey {
//derive non hardened key
key.publicKey.bytes ++ idx.bytes
}
val hmac = CryptoUtil.hmac512(chainCode.bytes,data)
val (il,ir) = hmac.splitAt(32)
val hmac = CryptoUtil.hmac512(chainCode.bytes, data)
val (il, ir) = hmac.splitAt(32)
//should be ECGroup addition
//parse256(IL) + kpar (mod n)
val childKey = ECPrivateKey(NativeSecp256k1.privKeyTweakAdd(il.toArray, key.bytes.toArray))
val fp = CryptoUtil.sha256Hash160(key.publicKey.bytes).bytes.take(4)
ExtPrivateKey(version, depth + UInt8.one, fp, idx,
ChainCode(ir),childKey)
ChainCode(ir), childKey)
}
def extPublicKey: ExtPublicKey = version match {
case MainNetPriv => ExtPublicKey(MainNetPub,depth,fingerprint,childNum,chainCode,key.publicKey)
case TestNet3Priv => ExtPublicKey(TestNet3Pub,depth,fingerprint,childNum,chainCode,key.publicKey)
case MainNetPriv => ExtPublicKey(MainNetPub, depth, fingerprint, childNum, chainCode, key.publicKey)
case TestNet3Priv => ExtPublicKey(TestNet3Pub, depth, fingerprint, childNum, chainCode, key.publicKey)
case MainNetPub | TestNet3Pub => throw new IllegalArgumentException("Cannot have pubkey version in ExtPrivateKey, got: " + version)
}
@ -143,8 +147,8 @@ object ExtPrivateKey extends Factory[ExtPrivateKey] {
val base58 = Base58.encode(bytes ++ CryptoUtil.doubleSHA256(bytes).bytes.take(4))
ExtKey.fromString(base58) match {
case Success(priv: ExtPrivateKey) => priv
case Success(_: ExtPublicKey) => throw new IllegalArgumentException("Cannot create ext public in ExtPrivateKey")
case f: Failure[_] => throw f.exception
case Success(_: ExtPublicKey) => throw new IllegalArgumentException("Cannot create ext public in ExtPrivateKey")
case f: Failure[_] => throw f.exception
}
}
def apply(version: ExtKeyVersion, depth: UInt8,
@ -153,24 +157,24 @@ object ExtPrivateKey extends Factory[ExtPrivateKey] {
ExtPrivateKeyImpl(version, depth, fingerprint, child, chainCode, privateKey)
}
/** Generates a master private key
* https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#master-key-generation
* */
/**
* Generates a master private key
* https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#master-key-generation
*/
def apply(version: ExtKeyVersion, seedOpt: Option[Seq[Byte]] = None): ExtPrivateKey = {
val seed = seedOpt match {
case Some(bytes) => bytes
case None => ECPrivateKey().bytes
case None => ECPrivateKey().bytes
}
val i = CryptoUtil.hmac512("Bitcoin seed".map(_.toByte), seed)
val (il,ir) = i.splitAt(32)
val (il, ir) = i.splitAt(32)
val masterPrivKey = ECPrivateKey(il)
val fp = UInt32.zero.bytes
ExtPrivateKey(version, UInt8.zero, fp, UInt32.zero,
ChainCode(ir),masterPrivKey)
ChainCode(ir), masterPrivKey)
}
}
sealed abstract class ExtPublicKey extends ExtKey {
override def key: ECPublicKey
@ -180,20 +184,22 @@ sealed abstract class ExtPublicKey extends ExtKey {
} else {
val data = key.bytes ++ idx.bytes
val hmac = CryptoUtil.hmac512(chainCode.bytes, data)
val (il,ir) = hmac.splitAt(32)
val (il, ir) = hmac.splitAt(32)
val priv = ECPrivateKey(il)
val tweaked = NativeSecp256k1.pubKeyTweakAdd(key.bytes.toArray,
val tweaked = NativeSecp256k1.pubKeyTweakAdd(
key.bytes.toArray,
hmac.toArray,
priv.isCompressed)
priv.isCompressed
)
val childPubKey = ECPublicKey(tweaked)
val bi = BigInt(new BigInteger(1,priv.bytes.toArray))
val bi = BigInt(new BigInteger(1, priv.bytes.toArray))
//we do not handle this case since it is impossible
//In case parse256(IL) n or Ki is the point at infinity, the resulting key is invalid,
//and one should proceed with the next value for i.
//https://botbot.me/freenode/bitcoin-wizards/2017-11-20/?tz=America/Chicago
val cc = ChainCode(ir)
val fp = CryptoUtil.sha256Hash160(key.bytes).bytes.take(4)
Success(ExtPublicKey(version,depth + UInt8.one, fp,idx,cc,childPubKey))
Success(ExtPublicKey(version, depth + UInt8.one, fp, idx, cc, childPubKey))
}
}
}
@ -204,7 +210,7 @@ object ExtPublicKey extends Factory[ExtPublicKey] {
chainCode: ChainCode, key: ECPublicKey) extends ExtPublicKey
def apply(version: ExtKeyVersion, depth: UInt8,
fingerprint: Seq[Byte], child: UInt32, chainCode: ChainCode, publicKey: ECPublicKey): ExtPublicKey = {
fingerprint: Seq[Byte], child: UInt32, chainCode: ChainCode, publicKey: ECPublicKey): ExtPublicKey = {
ExtPublicKeyImpl(version, depth, fingerprint, child, chainCode, publicKey)
}
@ -215,11 +221,8 @@ object ExtPublicKey extends Factory[ExtPublicKey] {
case Success(_: ExtPrivateKey) =>
throw new IllegalArgumentException("Cannot create ext privatkey in ExtPublicKey")
case Success(pub: ExtPublicKey) => pub
case f: Failure[_] => throw f.exception
case f: Failure[_] => throw f.exception
}
}
}

View file

@ -13,19 +13,19 @@ case object MainNetPub extends ExtKeyVersion {
}
case object MainNetPriv extends ExtKeyVersion {
override def bytes = Seq(0x04,0x88,0xAD,0xE4).map(_.toByte)
override def bytes = Seq(0x04, 0x88, 0xAD, 0xE4).map(_.toByte)
}
case object TestNet3Pub extends ExtKeyVersion {
override def bytes = Seq(0x04,0x35,0x87,0xCF).map(_.toByte)
override def bytes = Seq(0x04, 0x35, 0x87, 0xCF).map(_.toByte)
}
case object TestNet3Priv extends ExtKeyVersion {
override def bytes = Seq(0x04,0x35,0x83,0x94).map(_.toByte)
override def bytes = Seq(0x04, 0x35, 0x83, 0x94).map(_.toByte)
}
object ExtKeyVersion {
private val all: Seq[ExtKeyVersion] = Seq(MainNetPriv,MainNetPub,TestNet3Pub, TestNet3Priv)
private val all: Seq[ExtKeyVersion] = Seq(MainNetPriv, MainNetPub, TestNet3Pub, TestNet3Priv)
def apply(bytes: Seq[Byte]): Option[ExtKeyVersion] = all.find(_.bytes == bytes)
}

View file

@ -1,34 +1,33 @@
package org.bitcoins.core.crypto
import org.bitcoins.core.util.{Factory, BitcoinSUtil}
import org.bitcoins.core.util.{ Factory, BitcoinSUtil }
/**
* Created by chris on 5/24/16.
*/
* Created by chris on 5/24/16.
*/
sealed trait HashDigest {
/** The message digest represented in bytes */
def bytes : Seq[Byte]
def bytes: Seq[Byte]
/** The message digest represented in hexadecimal */
def hex : String = BitcoinSUtil.encodeHex(bytes)
def hex: String = BitcoinSUtil.encodeHex(bytes)
}
/**
* Represents the result of SHA1()
*/
* Represents the result of SHA1()
*/
sealed trait Sha1Digest extends HashDigest
object Sha1Digest extends Factory[Sha1Digest] {
private case class Sha1DigestImpl(bytes : Seq[Byte]) extends Sha1Digest {
private case class Sha1DigestImpl(bytes: Seq[Byte]) extends Sha1Digest {
override def toString = "Sha1DigestImpl(" + hex + ")"
}
override def fromBytes(bytes: Seq[Byte]) : Sha1Digest = Sha1DigestImpl(bytes)
override def fromBytes(bytes: Seq[Byte]): Sha1Digest = Sha1DigestImpl(bytes)
}
/**
* Represents the result of SHA256()
*/
* Represents the result of SHA256()
*/
sealed trait Sha256Digest extends HashDigest
object Sha256Digest extends Factory[Sha256Digest] {
@ -36,12 +35,12 @@ object Sha256Digest extends Factory[Sha256Digest] {
require(bytes.length == 32, "Sha256Digest must be 32 bytes in size, got: " + bytes.length)
override def toString = "Sha256DigestImpl(" + hex + ")"
}
override def fromBytes(bytes:Seq[Byte]) : Sha256Digest = Sha256DigestImpl(bytes)
override def fromBytes(bytes: Seq[Byte]): Sha256Digest = Sha256DigestImpl(bytes)
}
/**
* Represents the result of SHA256(SHA256())
*/
* Represents the result of SHA256(SHA256())
*/
sealed trait DoubleSha256Digest extends HashDigest
object DoubleSha256Digest extends Factory[DoubleSha256Digest] {
@ -49,32 +48,32 @@ object DoubleSha256Digest extends Factory[DoubleSha256Digest] {
require(bytes.length == 32, "DoubleSha256Digest must always be 32 bytes, got: " + bytes.length)
override def toString = "DoubleSha256DigestImpl(" + hex + ")"
}
override def fromBytes(bytes : Seq[Byte]) : DoubleSha256Digest = DoubleSha256DigestImpl(bytes)
override def fromBytes(bytes: Seq[Byte]): DoubleSha256Digest = DoubleSha256DigestImpl(bytes)
}
/**
* Represents the result of RIPEMD160()
*/
* Represents the result of RIPEMD160()
*/
sealed trait RipeMd160Digest extends HashDigest
object RipeMd160Digest extends Factory[RipeMd160Digest] {
private case class RipeMd160DigestImpl(bytes : Seq[Byte]) extends RipeMd160Digest {
private case class RipeMd160DigestImpl(bytes: Seq[Byte]) extends RipeMd160Digest {
require(bytes.length == 20, "RIPEMD160Digest must always be 20 bytes, got: " + bytes.length)
override def toString = "RipeMd160DigestImpl(" + hex + ")"
}
override def fromBytes(bytes : Seq[Byte]) : RipeMd160Digest = RipeMd160DigestImpl(bytes)
override def fromBytes(bytes: Seq[Byte]): RipeMd160Digest = RipeMd160DigestImpl(bytes)
}
/**
* Represents the result of RIPEMD160(SHA256())
*/
* Represents the result of RIPEMD160(SHA256())
*/
sealed trait Sha256Hash160Digest extends HashDigest
object Sha256Hash160Digest extends Factory[Sha256Hash160Digest] {
private case class Sha256Hash160DigestImpl(bytes : Seq[Byte]) extends Sha256Hash160Digest {
private case class Sha256Hash160DigestImpl(bytes: Seq[Byte]) extends Sha256Hash160Digest {
require(bytes.length == 20, "Sha256Hash160Digest must always be 20 bytes, got: " + bytes.length)
override def toString = "Sha256Hash160DigestImpl(" + hex + ")"
}
override def fromBytes(bytes : Seq[Byte]): Sha256Hash160Digest = Sha256Hash160DigestImpl(bytes)
override def fromBytes(bytes: Seq[Byte]): Sha256Hash160Digest = Sha256Hash160DigestImpl(bytes)
}

View file

@ -2,9 +2,9 @@ package org.bitcoins.core.crypto
import org.bitcoins.core.script.constant.ScriptToken
import org.bitcoins.core.script.crypto._
import org.bitcoins.core.script.flag.{ScriptFlag, ScriptFlagUtil}
import org.bitcoins.core.script.flag.{ ScriptFlag, ScriptFlagUtil }
import org.bitcoins.core.script.result.ScriptErrorWitnessPubKeyType
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil, BitcoinScriptUtil}
import org.bitcoins.core.util.{ BitcoinSLogger, BitcoinSUtil, BitcoinScriptUtil }
import scala.annotation.tailrec
@ -16,21 +16,21 @@ import scala.annotation.tailrec
trait TransactionSignatureChecker extends BitcoinSLogger {
/**
* Checks the signature of a scriptSig in the spending transaction against the
* given scriptPubKey & explicitly given public key
* This is useful for instances of non standard scriptSigs
*
* @param txSignatureComponent the relevant transaction information for signature checking
* @param script the current script state inside the interpreter - this is needed in the case of OP_CODESEPARATORS
* @param pubKey the public key the signature is being checked against
* @param signature the signature which is being checked against the transaction & the public key
* @param flags the script flags used to check validity of the signature
* @return a boolean indicating if the signature is valid or not
*/
def checkSignature(txSignatureComponent : TxSigComponent, script : Seq[ScriptToken],
pubKey: ECPublicKey, signature : ECDigitalSignature, flags : Seq[ScriptFlag]) : TransactionSignatureCheckerResult = {
* Checks the signature of a scriptSig in the spending transaction against the
* given scriptPubKey & explicitly given public key
* This is useful for instances of non standard scriptSigs
*
* @param txSignatureComponent the relevant transaction information for signature checking
* @param script the current script state inside the interpreter - this is needed in the case of OP_CODESEPARATORS
* @param pubKey the public key the signature is being checked against
* @param signature the signature which is being checked against the transaction & the public key
* @param flags the script flags used to check validity of the signature
* @return a boolean indicating if the signature is valid or not
*/
def checkSignature(txSignatureComponent: TxSigComponent, script: Seq[ScriptToken],
pubKey: ECPublicKey, signature: ECDigitalSignature, flags: Seq[ScriptFlag]): TransactionSignatureCheckerResult = {
logger.debug("Signature: " + signature)
val pubKeyEncodedCorrectly = BitcoinScriptUtil.isValidPubKeyEncoding(pubKey,flags)
val pubKeyEncodedCorrectly = BitcoinScriptUtil.isValidPubKeyEncoding(pubKey, flags)
if (ScriptFlagUtil.requiresStrictDerEncoding(flags) && !DERSignatureUtil.isValidSignatureEncoding(signature)) {
logger.error("Signature was not stricly encoded der: " + signature.hex)
SignatureValidationErrorNotStrictDerEncoding
@ -48,28 +48,30 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
logger.error("The public key given for signature checking was not encoded correctly, err: " + result)
result
} else {
val sigsRemovedScript = BitcoinScriptUtil.calculateScriptForChecking(txSignatureComponent,signature,script)
val sigsRemovedScript = BitcoinScriptUtil.calculateScriptForChecking(txSignatureComponent, signature, script)
val hashTypeByte = if (signature.bytes.nonEmpty) signature.bytes.last else 0x00.toByte
val hashType = HashType(Seq(0.toByte, 0.toByte, 0.toByte, hashTypeByte))
val hashForSignature = txSignatureComponent match {
case b : BaseTxSigComponent =>
TransactionSignatureSerializer.hashForSignature(txSignatureComponent.transaction,
case b: BaseTxSigComponent =>
TransactionSignatureSerializer.hashForSignature(
txSignatureComponent.transaction,
txSignatureComponent.inputIndex,
sigsRemovedScript, hashType)
case w : WitnessTxSigComponent =>
TransactionSignatureSerializer.hashForSignature(w.transaction,w.inputIndex,sigsRemovedScript, hashType,
w.amount,w.sigVersion)
sigsRemovedScript, hashType
)
case w: WitnessTxSigComponent =>
TransactionSignatureSerializer.hashForSignature(w.transaction, w.inputIndex, sigsRemovedScript, hashType,
w.amount, w.sigVersion)
case r: WitnessTxSigComponentRebuilt =>
TransactionSignatureSerializer.hashForSignature(r.transaction,r.inputIndex,sigsRemovedScript, hashType,
r.amount,r.sigVersion)
TransactionSignatureSerializer.hashForSignature(r.transaction, r.inputIndex, sigsRemovedScript, hashType,
r.amount, r.sigVersion)
}
logger.debug("Hash for signature: " + BitcoinSUtil.encodeHex(hashForSignature.bytes))
val sigWithoutHashType = stripHashType(signature)
val isValid = pubKey.verify(hashForSignature,sigWithoutHashType)
val isValid = pubKey.verify(hashForSignature, sigWithoutHashType)
if (isValid) SignatureValidationSuccess
else nullFailCheck(Seq(signature),SignatureValidationErrorIncorrectSignatures, flags)
else nullFailCheck(Seq(signature), SignatureValidationErrorIncorrectSignatures, flags)
}
}
@ -85,9 +87,9 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
* @return a boolean indicating if all of the signatures are valid against the given public keys
*/
@tailrec
final def multiSignatureEvaluator(txSignatureComponent : TxSigComponent, script : Seq[ScriptToken],
sigs : List[ECDigitalSignature], pubKeys : List[ECPublicKey], flags : Seq[ScriptFlag],
requiredSigs : Long) : TransactionSignatureCheckerResult = {
final def multiSignatureEvaluator(txSignatureComponent: TxSigComponent, script: Seq[ScriptToken],
sigs: List[ECDigitalSignature], pubKeys: List[ECPublicKey], flags: Seq[ScriptFlag],
requiredSigs: Long): TransactionSignatureCheckerResult = {
logger.debug("Signatures inside of helper: " + sigs)
logger.debug("Public keys inside of helper: " + pubKeys)
if (sigs.size > pubKeys.size) {
@ -95,44 +97,42 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
//signatures than public keys remaining we immediately return
//false https://github.com/bitcoin/bitcoin/blob/8c1dbc5e9ddbafb77e60e8c4e6eb275a3a76ac12/src/script/interpreter.cpp#L943-L945
logger.warn("We have more sigs than we have public keys remaining")
nullFailCheck(sigs,SignatureValidationErrorIncorrectSignatures,flags)
}
else if (requiredSigs > sigs.size) {
nullFailCheck(sigs, SignatureValidationErrorIncorrectSignatures, flags)
} else if (requiredSigs > sigs.size) {
//for the case when we do not have enough sigs left to check to meet the required signature threshold
//https://github.com/bitcoin/bitcoin/blob/8c1dbc5e9ddbafb77e60e8c4e6eb275a3a76ac12/src/script/interpreter.cpp#L990-L991
logger.warn("We do not have enough sigs to meet the threshold of requireSigs in the multiSignatureScriptPubKey")
nullFailCheck(sigs,SignatureValidationErrorSignatureCount,flags)
}
else if (sigs.nonEmpty && pubKeys.nonEmpty) {
nullFailCheck(sigs, SignatureValidationErrorSignatureCount, flags)
} else if (sigs.nonEmpty && pubKeys.nonEmpty) {
val sig = sigs.head
val pubKey = pubKeys.head
val result = checkSignature(txSignatureComponent,script,pubKey,sig,flags)
val result = checkSignature(txSignatureComponent, script, pubKey, sig, flags)
result match {
case SignatureValidationSuccess =>
multiSignatureEvaluator(txSignatureComponent, script, sigs.tail,pubKeys.tail,flags, requiredSigs - 1)
multiSignatureEvaluator(txSignatureComponent, script, sigs.tail, pubKeys.tail, flags, requiredSigs - 1)
case SignatureValidationErrorIncorrectSignatures | SignatureValidationErrorNullFail =>
//notice we pattern match on 'SignatureValidationErrorNullFail' here, this is because
//'checkSignature' may return that result, but we need to continue evaluating the signatures
//in the multisig script, we don't check for nullfail until evaluation the OP_CHECKMULTSIG is completely done
multiSignatureEvaluator(txSignatureComponent, script, sigs, pubKeys.tail,flags, requiredSigs)
multiSignatureEvaluator(txSignatureComponent, script, sigs, pubKeys.tail, flags, requiredSigs)
case x @ (SignatureValidationErrorNotStrictDerEncoding | SignatureValidationErrorSignatureCount |
SignatureValidationErrorPubKeyEncoding | SignatureValidationErrorHighSValue |
SignatureValidationErrorHashType | SignatureValidationErrorWitnessPubKeyType) =>
nullFailCheck(sigs,x,flags)
SignatureValidationErrorPubKeyEncoding | SignatureValidationErrorHighSValue |
SignatureValidationErrorHashType | SignatureValidationErrorWitnessPubKeyType) =>
nullFailCheck(sigs, x, flags)
}
} else if (sigs.isEmpty) {
//means that we have checked all of the sigs against the public keys
//validation succeeds
SignatureValidationSuccess
} else nullFailCheck(sigs,SignatureValidationErrorIncorrectSignatures,flags)
} else nullFailCheck(sigs, SignatureValidationErrorIncorrectSignatures, flags)
}
/** If the NULLFAIL flag is set as defined in BIP146, it checks to make sure all failed signatures were an empty byte vector
* [[https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki#NULLFAIL]]
* */
private def nullFailCheck(sigs: Seq[ECDigitalSignature], result: TransactionSignatureCheckerResult,flags: Seq[ScriptFlag]): TransactionSignatureCheckerResult = {
/**
* If the NULLFAIL flag is set as defined in BIP146, it checks to make sure all failed signatures were an empty byte vector
* [[https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki#NULLFAIL]]
*/
private def nullFailCheck(sigs: Seq[ECDigitalSignature], result: TransactionSignatureCheckerResult, flags: Seq[ScriptFlag]): TransactionSignatureCheckerResult = {
logger.info("Result before nullfail check:" + result)
val nullFailEnabled = ScriptFlagUtil.requireScriptVerifyNullFail(flags)
if (nullFailEnabled && !result.isValid && sigs.exists(_.bytes.nonEmpty)) {
@ -143,10 +143,9 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
/** Removes the hash type from the [[org.bitcoins.core.crypto.ECDigitalSignature]] */
private def stripHashType(sig: ECDigitalSignature): ECDigitalSignature = {
ECDigitalSignature(sig.bytes.slice(0,sig.bytes.length-1))
ECDigitalSignature(sig.bytes.slice(0, sig.bytes.length - 1))
}
}
object TransactionSignatureChecker extends TransactionSignatureChecker

View file

@ -1,6 +1,5 @@
package org.bitcoins.core.crypto
/**
* The result type returned by checking a signature
*/
@ -8,10 +7,9 @@ sealed trait TransactionSignatureCheckerResult {
/**
* Indicates if the transaction signature checker was successful or failed
*/
def isValid : Boolean
def isValid: Boolean
}
/**
* Represents the case that the signatures checked inside of the transaction were
* all validly encoded as per the script verify flag & that the signatures
@ -61,13 +59,15 @@ case object SignatureValidationErrorHighSValue extends SignatureValidationError
*/
case object SignatureValidationErrorHashType extends SignatureValidationError
/** Fails the script if the given public key was not compressed and the [[org.bitcoins.core.script.flag.ScriptVerifyWitnessPubKeyType]]
* flag was set */
/**
* Fails the script if the given public key was not compressed and the [[org.bitcoins.core.script.flag.ScriptVerifyWitnessPubKeyType]]
* flag was set
*/
case object SignatureValidationErrorWitnessPubKeyType extends SignatureValidationError
/**
* Fails the script if a an invalid signature is not an empty byte vector
* See BIP146
* [[https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki#nullfail]]
*/
* Fails the script if a an invalid signature is not an empty byte vector
* See BIP146
* [[https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki#nullfail]]
*/
case object SignatureValidationErrorNullFail extends SignatureValidationError

View file

@ -3,35 +3,35 @@ package org.bitcoins.core.crypto
import org.bitcoins.core.script.crypto.HashType
import org.bitcoins.core.util.BitcoinSLogger
import scala.concurrent.{ExecutionContext, Future}
import scala.concurrent.{ ExecutionContext, Future }
/**
* Created by chris on 7/21/16.
*/
* Created by chris on 7/21/16.
*/
sealed abstract class TransactionSignatureCreator {
private val logger = BitcoinSLogger.logger
/**
* Creates a signature from a tx signature component
*
* @param txSignatureComponent contains the tx, inputIndex which specify which input we are creating a sig for
* @param privateKey the private key which we are signing the hash with
* @param hashType the procedure to use for hashing to transaction
* @return
*/
* Creates a signature from a tx signature component
*
* @param txSignatureComponent contains the tx, inputIndex which specify which input we are creating a sig for
* @param privateKey the private key which we are signing the hash with
* @param hashType the procedure to use for hashing to transaction
* @return
*/
def createSig(txSignatureComponent: TxSigComponent, privateKey: ECPrivateKey, hashType: HashType): ECDigitalSignature = {
val sign: Seq[Byte] => ECDigitalSignature = privateKey.sign(_: Seq[Byte])
createSig(txSignatureComponent,sign, hashType)
createSig(txSignatureComponent, sign, hashType)
}
/**
* This is intended to be a low level hardware wallet API.
* At a fundamental level, a hardware wallet expects a Seq[Byte] as input, and returns an [[ECDigitalSignature]]
* if it is able to sign the Seq[Byte]'s correctly.
* @param component - the information needed to sign the transaction
* @param sign - the implementation of the hardware wallet protocol to sign the Seq[Byte] w/ the given public key
* @param hashType - the hash type to be appended on the digital signature when the hardware wallet is done being signed
* @return the digital signature returned by the hardware wallet
*/
* This is intended to be a low level hardware wallet API.
* At a fundamental level, a hardware wallet expects a Seq[Byte] as input, and returns an [[ECDigitalSignature]]
* if it is able to sign the Seq[Byte]'s correctly.
* @param component - the information needed to sign the transaction
* @param sign - the implementation of the hardware wallet protocol to sign the Seq[Byte] w/ the given public key
* @param hashType - the hash type to be appended on the digital signature when the hardware wallet is done being signed
* @return the digital signature returned by the hardware wallet
*/
def createSig(component: TxSigComponent, sign: Seq[Byte] => ECDigitalSignature, hashType: HashType): ECDigitalSignature = {
val hash = TransactionSignatureSerializer.hashForSignature(component, hashType)
val signature = sign(hash.bytes)

View file

@ -8,7 +8,7 @@ import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.script.constant.ScriptToken
import org.bitcoins.core.script.crypto._
import org.bitcoins.core.serializers.transaction.RawTransactionOutputParser
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil, BitcoinScriptUtil, CryptoUtil}
import org.bitcoins.core.util.{ BitcoinSLogger, BitcoinSUtil, BitcoinScriptUtil, CryptoUtil }
/**
* Created by chris on 2/16/16.
@ -26,14 +26,14 @@ sealed abstract class TransactionSignatureSerializer {
* Bitcoin Core's bug is that SignatureHash was supposed to return a hash and on this codepath it
* actually returns the constant "1" to indicate an error
*/
private def errorHash : DoubleSha256Digest = DoubleSha256Digest(BitcoinSUtil.decodeHex("0100000000000000000000000000000000000000000000000000000000000000"))
private def errorHash: DoubleSha256Digest = DoubleSha256Digest(BitcoinSUtil.decodeHex("0100000000000000000000000000000000000000000000000000000000000000"))
/**
* Serializes a transaction to be signed by an ECKey
* follows the bitcoinj implementation
* hashing is done in the [[hashForSignature]] function
*/
def serializeForSignature(spendingTransaction : Transaction, inputIndex : UInt32, script : Seq[ScriptToken], hashType : HashType) : Seq[Byte] = {
def serializeForSignature(spendingTransaction: Transaction, inputIndex: UInt32, script: Seq[ScriptToken], hashType: HashType): Seq[Byte] = {
logger.debug("Serializing for signature")
logger.debug("Script: " + script)
// Clear input scripts in preparation for signing. If we're signing a fresh
@ -42,11 +42,11 @@ sealed abstract class TransactionSignatureSerializer {
val inputSigsRemoved = for {
input <- spendingTransaction.inputs
s = input.scriptSignature
} yield TransactionInput(input.previousOutput,NonStandardScriptSignature(s.compactSizeUInt.hex), input.sequence)
} yield TransactionInput(input.previousOutput, NonStandardScriptSignature(s.compactSizeUInt.hex), input.sequence)
//make sure all scriptSigs have empty asm
inputSigsRemoved.map(input =>
require(input.scriptSignature.asm.isEmpty,"Input asm was not empty " + input.scriptSignature.asm))
require(input.scriptSignature.asm.isEmpty, "Input asm was not empty " + input.scriptSignature.asm))
// This step has no purpose beyond being synchronized with Bitcoin Core's bugs. OP_CODESEPARATOR
// is a legacy holdover from a previous, broken design of executing scripts that shipped in Bitcoin 0.1.
@ -56,7 +56,7 @@ sealed abstract class TransactionSignatureSerializer {
// ever put into scripts. Deleting OP_CODESEPARATOR is a step that should never be required but if we don't
// do it, we could split off the main chain.
logger.debug("Before Bitcoin-S Script to be connected: " + script)
val scriptWithOpCodeSeparatorsRemoved : Seq[ScriptToken] = removeOpCodeSeparators(script)
val scriptWithOpCodeSeparatorsRemoved: Seq[ScriptToken] = removeOpCodeSeparators(script)
logger.debug("After Bitcoin-S Script to be connected: " + scriptWithOpCodeSeparatorsRemoved)
@ -66,25 +66,24 @@ sealed abstract class TransactionSignatureSerializer {
// the signature covers the hash of the prevout transaction which obviously includes the output script
// already. Perhaps it felt safer to him in some way, or is another leftover from how the code was written.
val scriptSig = ScriptSignature.fromAsm(scriptWithOpCodeSeparatorsRemoved)
val inputWithConnectedScript = TransactionInput(inputToSign.previousOutput,scriptSig, inputToSign.sequence)
val inputWithConnectedScript = TransactionInput(inputToSign.previousOutput, scriptSig, inputToSign.sequence)
//update the input at index i with inputWithConnectScript
val updatedInputs = for {
(input,index) <- inputSigsRemoved.zipWithIndex
(input, index) <- inputSigsRemoved.zipWithIndex
} yield {
if (UInt32(index) == inputIndex) {
inputWithConnectedScript
}
else input
}
if (UInt32(index) == inputIndex) {
inputWithConnectedScript
} else input
}
val txWithInputSigsRemoved = BaseTransaction(spendingTransaction.version,updatedInputs, spendingTransaction.outputs, spendingTransaction.lockTime)
val txWithInputSigsRemoved = BaseTransaction(spendingTransaction.version, updatedInputs, spendingTransaction.outputs, spendingTransaction.lockTime)
val sigHashBytes = hashType.num.bytes.reverse
//check the hash type
//TODO: could probably be optimized w/ HO function
hashType match {
case _: SIGHASH_NONE =>
val sigHashNoneTx : Transaction = sigHashNone(txWithInputSigsRemoved,inputIndex)
val sigHashNoneTx: Transaction = sigHashNone(txWithInputSigsRemoved, inputIndex)
sigHashNoneTx.bytes ++ sigHashBytes
case _: SIGHASH_SINGLE =>
@ -100,72 +99,73 @@ sealed abstract class TransactionSignatureSerializer {
// actually returns the constant "1" to indicate an error, which is never checked for. Oops.
errorHash.bytes
} else {
val sigHashSingleTx = sigHashSingle(txWithInputSigsRemoved,inputIndex)
val sigHashSingleTx = sigHashSingle(txWithInputSigsRemoved, inputIndex)
sigHashSingleTx.bytes ++ sigHashBytes
}
case _: SIGHASH_ALL =>
val sigHashAllTx : Transaction = sigHashAll(txWithInputSigsRemoved,inputIndex)
val sigHashAllTx: Transaction = sigHashAll(txWithInputSigsRemoved, inputIndex)
sigHashAllTx.bytes ++ sigHashBytes
case _: SIGHASH_ANYONECANPAY =>
val txWithInputsRemoved = sigHashAnyoneCanPay(txWithInputSigsRemoved,inputWithConnectedScript)
val txWithInputsRemoved = sigHashAnyoneCanPay(txWithInputSigsRemoved, inputWithConnectedScript)
txWithInputsRemoved.bytes ++ sigHashBytes
case _: SIGHASH_ALL_ANYONECANPAY =>
val sigHashAllTx = sigHashAll(txWithInputSigsRemoved,inputIndex)
val sigHashAllAnyoneCanPayTx = sigHashAnyoneCanPay(sigHashAllTx,inputWithConnectedScript)
val sigHashAllTx = sigHashAll(txWithInputSigsRemoved, inputIndex)
val sigHashAllAnyoneCanPayTx = sigHashAnyoneCanPay(sigHashAllTx, inputWithConnectedScript)
sigHashAllAnyoneCanPayTx.bytes ++ sigHashBytes
case _: SIGHASH_NONE_ANYONECANPAY =>
val sigHashNoneTx = sigHashNone(txWithInputSigsRemoved,inputIndex)
val sigHashNoneAnyoneCanPay = sigHashAnyoneCanPay(sigHashNoneTx,inputWithConnectedScript)
val sigHashNoneTx = sigHashNone(txWithInputSigsRemoved, inputIndex)
val sigHashNoneAnyoneCanPay = sigHashAnyoneCanPay(sigHashNoneTx, inputWithConnectedScript)
sigHashNoneAnyoneCanPay.bytes ++ sigHashBytes
case _: SIGHASH_SINGLE_ANYONECANPAY =>
val sigHashSingleTx = sigHashSingle(txWithInputSigsRemoved,inputIndex)
val sigHashSingleAnyoneCanPay = sigHashAnyoneCanPay(sigHashSingleTx,inputWithConnectedScript)
sigHashSingleAnyoneCanPay.bytes ++ sigHashBytes
val sigHashSingleTx = sigHashSingle(txWithInputSigsRemoved, inputIndex)
val sigHashSingleAnyoneCanPay = sigHashAnyoneCanPay(sigHashSingleTx, inputWithConnectedScript)
sigHashSingleAnyoneCanPay.bytes ++ sigHashBytes
}
}
/**
* Serializes then hashes a transaction for signing
* this is an implementation of it's bitcoinj equivalent found here
* [[https://github.com/bitcoinj/bitcoinj/blob/master/core/src/main/java/org/bitcoinj/core/Transaction.java#L924]]
* @param spendingTransaction the transaction we are hashing
* @param inputIndex the inputIndex we are hashing for signing
* @param script the script which we are spending
* @param hashType the hash type we are serializing this tx for
* @return
*/
def hashForSignature(spendingTransaction : Transaction, inputIndex : UInt32, script : Seq[ScriptToken],
hashType : HashType) : DoubleSha256Digest = {
* Serializes then hashes a transaction for signing
* this is an implementation of it's bitcoinj equivalent found here
* [[https://github.com/bitcoinj/bitcoinj/blob/master/core/src/main/java/org/bitcoinj/core/Transaction.java#L924]]
* @param spendingTransaction the transaction we are hashing
* @param inputIndex the inputIndex we are hashing for signing
* @param script the script which we are spending
* @param hashType the hash type we are serializing this tx for
* @return
*/
def hashForSignature(spendingTransaction: Transaction, inputIndex: UInt32, script: Seq[ScriptToken],
hashType: HashType): DoubleSha256Digest = {
//these first two checks are in accordance with behavior in bitcoin core
//[[https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L1112-L1123]]
if (inputIndex >= UInt32(spendingTransaction.inputs.size)) {
logger.warn("Our inputIndex is out of the range of the inputs in the spending transaction")
errorHash
} else if((hashType.isInstanceOf[SIGHASH_SINGLE] || hashType.isInstanceOf[SIGHASH_SINGLE_ANYONECANPAY]) &&
} else if ((hashType.isInstanceOf[SIGHASH_SINGLE] || hashType.isInstanceOf[SIGHASH_SINGLE_ANYONECANPAY]) &&
inputIndex >= UInt32(spendingTransaction.outputs.size)) {
logger.warn("When we have a SIGHASH_SINGLE we cannot have more inputs than outputs")
errorHash
} else {
val serializedTxForSignature = serializeForSignature(spendingTransaction,inputIndex,script,hashType)
val serializedTxForSignature = serializeForSignature(spendingTransaction, inputIndex, script, hashType)
logger.debug("Serialized tx for signature: " + BitcoinSUtil.encodeHex(serializedTxForSignature))
logger.debug("HashType: " + hashType.num)
CryptoUtil.doubleSHA256(serializedTxForSignature)
}
}
/** Implements the new serialization algorithm defined in BIP143
* [[https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki]]
* [[https://github.com/bitcoin/bitcoin/blob/f8528134fc188abc5c7175a19680206964a8fade/src/script/interpreter.cpp#L1113]]
*/
/**
* Implements the new serialization algorithm defined in BIP143
* [[https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki]]
* [[https://github.com/bitcoin/bitcoin/blob/f8528134fc188abc5c7175a19680206964a8fade/src/script/interpreter.cpp#L1113]]
*/
def serializeForSignature(spendingTx: Transaction, inputIndex: UInt32, script: Seq[ScriptToken], hashType: HashType,
amount: CurrencyUnit, signatureVersion: SignatureVersion): Seq[Byte] = signatureVersion match {
case SigVersionBase =>
serializeForSignature(spendingTx,inputIndex,script,hashType)
serializeForSignature(spendingTx, inputIndex, script, hashType)
case SigVersionWitnessV0 =>
val isNotAnyoneCanPay = !HashType.isAnyoneCanPay(hashType)
val isNotSigHashSingle = !(HashType.isSIGHASH_SINGLE(hashType.num))
@ -203,91 +203,89 @@ sealed abstract class TransactionSignatureSerializer {
serializationForSig
}
/** Hashing function for a [[SigVersionWitnessV0]] as specified by BIP143
* [[https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki]]
* NOTE: This covers the amount of [[CurrencyUnit]] we are spending in the output
* */
/**
* Hashing function for a [[SigVersionWitnessV0]] as specified by BIP143
* [[https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki]]
* NOTE: This covers the amount of [[CurrencyUnit]] we are spending in the output
*/
def hashForSignature(spendingTx: Transaction, inputIndex: UInt32, script: Seq[ScriptToken], hashType: HashType,
amount: CurrencyUnit, sigVersion: SignatureVersion): DoubleSha256Digest = {
val serialization = serializeForSignature(spendingTx,inputIndex,script,hashType,amount,sigVersion)
val serialization = serializeForSignature(spendingTx, inputIndex, script, hashType, amount, sigVersion)
CryptoUtil.doubleSHA256(serialization)
}
/** Wrapper function for hashForSignature. */
def hashForSignature(txSigComponent: TxSigComponent, hashType: HashType): DoubleSha256Digest = {
val script = BitcoinScriptUtil.calculateScriptForSigning(txSigComponent,txSigComponent.scriptPubKey.asm)
val script = BitcoinScriptUtil.calculateScriptForSigning(txSigComponent, txSigComponent.scriptPubKey.asm)
txSigComponent match {
case t: BaseTxSigComponent =>
hashForSignature(t.transaction,t.inputIndex,script,hashType)
hashForSignature(t.transaction, t.inputIndex, script, hashType)
case w: WitnessTxSigComponent =>
hashForSignature(w.transaction,w.inputIndex, script, hashType,w.amount, w.sigVersion)
hashForSignature(w.transaction, w.inputIndex, script, hashType, w.amount, w.sigVersion)
case r: WitnessTxSigComponentRebuilt =>
hashForSignature(r.transaction,r.inputIndex,script,hashType,r.amount,r.sigVersion)
hashForSignature(r.transaction, r.inputIndex, script, hashType, r.amount, r.sigVersion)
}
}
/** Sets the input's sequence number to zero EXCEPT for the input at inputIndex. */
private def setSequenceNumbersZero(inputs : Seq[TransactionInput], inputIndex : UInt32) : Seq[TransactionInput] = for {
(input,index) <- inputs.zipWithIndex
private def setSequenceNumbersZero(inputs: Seq[TransactionInput], inputIndex: UInt32): Seq[TransactionInput] = for {
(input, index) <- inputs.zipWithIndex
} yield {
if (UInt32(index) == inputIndex) input
else TransactionInput(input.previousOutput, input.scriptSignature,UInt32.zero)
else TransactionInput(input.previousOutput, input.scriptSignature, UInt32.zero)
}
/** Executes the [[SIGHASH_NONE]] procedure on a spending transaction for the input specified by inputIndex. */
private def sigHashNone(spendingTransaction : Transaction, inputIndex : UInt32) : Transaction = {
private def sigHashNone(spendingTransaction: Transaction, inputIndex: UInt32): Transaction = {
//following this implementation from bitcoinj
//[[https://github.com/bitcoinj/bitcoinj/blob/09a2ca64d2134b0dcbb27b1a6eb17dda6087f448/core/src/main/java/org/bitcoinj/core/Transaction.java#L957]]
//means that no outputs are signed at all
//set the sequence number of all inputs to 0 EXCEPT the input at inputIndex
val updatedInputs : Seq[TransactionInput] = setSequenceNumbersZero(spendingTransaction.inputs,inputIndex)
val sigHashNoneTx = BaseTransaction(spendingTransaction.version,updatedInputs,Nil,spendingTransaction.lockTime)
val updatedInputs: Seq[TransactionInput] = setSequenceNumbersZero(spendingTransaction.inputs, inputIndex)
val sigHashNoneTx = BaseTransaction(spendingTransaction.version, updatedInputs, Nil, spendingTransaction.lockTime)
//append hash type byte onto the end of the tx bytes
sigHashNoneTx
}
/** Executes the [[SIGHASH_SINGLE]] procedure on a spending transaction for the input specified by inputIndex */
private def sigHashSingle(spendingTransaction : Transaction, inputIndex : UInt32) : Transaction = {
private def sigHashSingle(spendingTransaction: Transaction, inputIndex: UInt32): Transaction = {
//following this implementation from bitcoinj
//[[https://github.com/bitcoinj/bitcoinj/blob/09a2ca64d2134b0dcbb27b1a6eb17dda6087f448/core/src/main/java/org/bitcoinj/core/Transaction.java#L964]]
// In SIGHASH_SINGLE the outputs after the matching input index are deleted, and the outputs before
// that position are "nulled out". Unintuitively, the value in a "null" transaction is set to -1.
val updatedOutputsOpt : Seq[Option[TransactionOutput]] = for {
(output,index) <- spendingTransaction.outputs.zipWithIndex
val updatedOutputsOpt: Seq[Option[TransactionOutput]] = for {
(output, index) <- spendingTransaction.outputs.zipWithIndex
} yield {
if (UInt32(index) < inputIndex) {
logger.debug("Updating tx output to null in bitcoin core")
Some(EmptyTransactionOutput)
}
else if (UInt32(index) == inputIndex) Some(output)
} else if (UInt32(index) == inputIndex) Some(output)
else None
}
val updatedOutputs : Seq[TransactionOutput] = updatedOutputsOpt.flatten
val updatedOutputs: Seq[TransactionOutput] = updatedOutputsOpt.flatten
//create blank inputs with sequence numbers set to zero EXCEPT
//the input at the inputIndex
val updatedInputs : Seq[TransactionInput] = setSequenceNumbersZero(spendingTransaction.inputs,inputIndex)
val updatedInputs: Seq[TransactionInput] = setSequenceNumbersZero(spendingTransaction.inputs, inputIndex)
val sigHashSingleTx = BaseTransaction(spendingTransaction.version, updatedInputs, updatedOutputs, spendingTransaction.lockTime)
sigHashSingleTx
}
/** Executes the [[SIGHASH_ALL]] procedure on a spending transaction at inputIndex. */
private def sigHashAll(spendingTransaction : Transaction, inputIndex : UInt32) : Transaction = {
private def sigHashAll(spendingTransaction: Transaction, inputIndex: UInt32): Transaction = {
spendingTransaction
}
/** Executes the [[SIGHASH_ANYONECANPAY]] procedure on a spending transaction at inputIndex. */
private def sigHashAnyoneCanPay(spendingTransaction : Transaction, input : TransactionInput) : Transaction = {
BaseTransaction(spendingTransaction.version,Seq(input), spendingTransaction.outputs, spendingTransaction.lockTime)
private def sigHashAnyoneCanPay(spendingTransaction: Transaction, input: TransactionInput): Transaction = {
BaseTransaction(spendingTransaction.version, Seq(input), spendingTransaction.outputs, spendingTransaction.lockTime)
}
/** Removes [[OP_CODESEPARATOR]] operations then returns the script. */
def removeOpCodeSeparators(script : Seq[ScriptToken]) : Seq[ScriptToken] = {
def removeOpCodeSeparators(script: Seq[ScriptToken]): Seq[ScriptToken] = {
if (script.contains(OP_CODESEPARATOR)) {
//TODO: This needs to be tested
val scriptWithoutOpCodeSeparators : Seq[ScriptToken] = script.filterNot(_ == OP_CODESEPARATOR)
val scriptWithoutOpCodeSeparators: Seq[ScriptToken] = script.filterNot(_ == OP_CODESEPARATOR)
scriptWithoutOpCodeSeparators
} else script
}

View file

@ -3,10 +3,10 @@ package org.bitcoins.core.crypto
import org.bitcoins.core.currency.CurrencyUnit
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction.{Transaction, TransactionInput, WitnessTransaction}
import org.bitcoins.core.protocol.transaction.{ Transaction, TransactionInput, WitnessTransaction }
import org.bitcoins.core.script.flag.ScriptFlag
import scala.util.{Failure, Success, Try}
import scala.util.{ Failure, Success, Try }
/**
* Created by chris on 4/6/16.
@ -15,10 +15,10 @@ import scala.util.{Failure, Success, Try}
sealed abstract class TxSigComponent {
/** The transaction being checked for the validity of signatures */
def transaction : Transaction
def transaction: Transaction
/** The index of the input whose script signature is being checked */
def inputIndex : UInt32
def inputIndex: UInt32
def input: TransactionInput = transaction.inputs(inputIndex.toInt)
@ -26,28 +26,30 @@ sealed abstract class TxSigComponent {
def scriptSignature: ScriptSignature = input.scriptSignature
/** The scriptPubKey for which the input is being checked against */
def scriptPubKey : ScriptPubKey
def scriptPubKey: ScriptPubKey
/** The flags that are needed to verify if the signature is correct*/
def flags : Seq[ScriptFlag]
/** The flags that are needed to verify if the signature is correct */
def flags: Seq[ScriptFlag]
/** Represents the serialization algorithm used to verify/create signatures for Bitcoin */
def sigVersion: SignatureVersion
}
/** The [[TxSigComponent]] used to evaluate the the original Satoshi transaction digest algorithm.
* Basically this is every spk that is not a [[WitnessScriptPubKey]] EXCEPT in the case of a
* P2SH(witness script) [[ScriptPubKey]]
* */
/**
* The [[TxSigComponent]] used to evaluate the the original Satoshi transaction digest algorithm.
* Basically this is every spk that is not a [[WitnessScriptPubKey]] EXCEPT in the case of a
* P2SH(witness script) [[ScriptPubKey]]
*/
sealed abstract class BaseTxSigComponent extends TxSigComponent {
override def sigVersion = SigVersionBase
}
/** The [[TxSigComponent]] used to represent all the components necessarily for BIP143
* [[https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki]]
* Examples of these [[ScriptPubKey]]'s are [[P2WPKHWitnessSPKV0]],
* [[P2WSHWitnessSPKV0]], and P2SH(witness script)
*/
/**
* The [[TxSigComponent]] used to represent all the components necessarily for BIP143
* [[https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki]]
* Examples of these [[ScriptPubKey]]'s are [[P2WPKHWitnessSPKV0]],
* [[P2WSHWitnessSPKV0]], and P2SH(witness script)
*/
sealed abstract class WitnessTxSigComponent extends TxSigComponent {
override def transaction: WitnessTransaction
@ -82,24 +84,25 @@ sealed abstract class WitnessTxSigComponentP2SH extends WitnessTxSigComponent {
def witnessScriptPubKey: Try[WitnessScriptPubKey] = scriptSignature.redeemScript match {
case w: WitnessScriptPubKey => Success(w)
case x @ (_: P2PKScriptPubKey | _: P2PKHScriptPubKey | _: MultiSignatureScriptPubKey | _: P2SHScriptPubKey
| _: CSVScriptPubKey | _: CLTVScriptPubKey | _: EscrowTimeoutScriptPubKey | _: NonStandardScriptPubKey
| _: WitnessCommitment | EmptyScriptPubKey) =>
| _: CSVScriptPubKey | _: CLTVScriptPubKey | _: EscrowTimeoutScriptPubKey | _: NonStandardScriptPubKey
| _: WitnessCommitment | EmptyScriptPubKey) =>
Failure(new IllegalArgumentException("Must have a witness scriptPubKey as redeemScript for P2SHScriptPubKey in WitnessTxSigComponentP2SH, got: " + x))
}
override def witnessVersion: WitnessVersion = witnessScriptPubKey match {
case Success(w) => w.witnessVersion
case Success(w) => w.witnessVersion
case Failure(err) => throw err
}
}
/** This represents a 'rebuilt' [[ScriptPubKey]] that was constructed from [[WitnessScriptPubKey]]
* After the [[ScriptPubKey]] is rebuilt, we need to use that rebuilt scriptpubkey to evaluate the [[ScriptSignature]]
* See BIP141 for more info on rebuilding P2WSH and P2WPKH scriptpubkeys
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#witness-program]]
*/
/**
* This represents a 'rebuilt' [[ScriptPubKey]] that was constructed from [[WitnessScriptPubKey]]
* After the [[ScriptPubKey]] is rebuilt, we need to use that rebuilt scriptpubkey to evaluate the [[ScriptSignature]]
* See BIP141 for more info on rebuilding P2WSH and P2WPKH scriptpubkeys
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#witness-program]]
*/
sealed abstract class WitnessTxSigComponentRebuilt extends TxSigComponent {
override def scriptPubKey: ScriptPubKey
@ -115,12 +118,12 @@ sealed abstract class WitnessTxSigComponentRebuilt extends TxSigComponent {
object BaseTxSigComponent {
private case class BaseTxSigComponentImpl(transaction : Transaction, inputIndex : UInt32,
scriptPubKey : ScriptPubKey, flags : Seq[ScriptFlag]) extends BaseTxSigComponent
private case class BaseTxSigComponentImpl(transaction: Transaction, inputIndex: UInt32,
scriptPubKey: ScriptPubKey, flags: Seq[ScriptFlag]) extends BaseTxSigComponent
def apply(transaction : Transaction, inputIndex : UInt32,
scriptPubKey: ScriptPubKey, flags : Seq[ScriptFlag]): BaseTxSigComponent = {
BaseTxSigComponentImpl(transaction,inputIndex,scriptPubKey,flags)
def apply(transaction: Transaction, inputIndex: UInt32,
scriptPubKey: ScriptPubKey, flags: Seq[ScriptFlag]): BaseTxSigComponent = {
BaseTxSigComponentImpl(transaction, inputIndex, scriptPubKey, flags)
}
}
@ -129,46 +132,49 @@ object WitnessTxSigComponent {
def apply(transaction: WitnessTransaction, inputIndex: UInt32, scriptPubKey: WitnessScriptPubKey,
flags: Seq[ScriptFlag], amount: CurrencyUnit): WitnessTxSigComponent = {
WitnessTxSigComponentRaw(transaction,inputIndex, scriptPubKey, flags, amount)
WitnessTxSigComponentRaw(transaction, inputIndex, scriptPubKey, flags, amount)
}
def apply(transaction : WitnessTransaction, inputIndex : UInt32, scriptPubKey : P2SHScriptPubKey,
flags : Seq[ScriptFlag], amount: CurrencyUnit) : WitnessTxSigComponent = {
WitnessTxSigComponentP2SH(transaction,inputIndex,scriptPubKey,flags,amount)
def apply(transaction: WitnessTransaction, inputIndex: UInt32, scriptPubKey: P2SHScriptPubKey,
flags: Seq[ScriptFlag], amount: CurrencyUnit): WitnessTxSigComponent = {
WitnessTxSigComponentP2SH(transaction, inputIndex, scriptPubKey, flags, amount)
}
}
object WitnessTxSigComponentRaw {
private case class WitnessTxSigComponentRawImpl(transaction: WitnessTransaction,inputIndex: UInt32,
private case class WitnessTxSigComponentRawImpl(transaction: WitnessTransaction, inputIndex: UInt32,
scriptPubKey: WitnessScriptPubKey, flags: Seq[ScriptFlag],
amount: CurrencyUnit) extends WitnessTxSigComponentRaw
def apply(transaction: WitnessTransaction,inputIndex: UInt32,
def apply(transaction: WitnessTransaction, inputIndex: UInt32,
scriptPubKey: WitnessScriptPubKey, flags: Seq[ScriptFlag], amount: CurrencyUnit): WitnessTxSigComponentRaw = {
WitnessTxSigComponentRawImpl(transaction,inputIndex,scriptPubKey,flags,amount)
WitnessTxSigComponentRawImpl(transaction, inputIndex, scriptPubKey, flags, amount)
}
}
object WitnessTxSigComponentP2SH {
private case class WitnessTxSigComponentP2SHImpl(transaction: WitnessTransaction,inputIndex: UInt32,
scriptPubKey: P2SHScriptPubKey, flags: Seq[ScriptFlag],
amount: CurrencyUnit) extends WitnessTxSigComponentP2SH
def apply(transaction: WitnessTransaction,inputIndex: UInt32, scriptPubKey: P2SHScriptPubKey, flags: Seq[ScriptFlag],
private case class WitnessTxSigComponentP2SHImpl(transaction: WitnessTransaction, inputIndex: UInt32,
scriptPubKey: P2SHScriptPubKey, flags: Seq[ScriptFlag],
amount: CurrencyUnit) extends WitnessTxSigComponentP2SH
def apply(transaction: WitnessTransaction, inputIndex: UInt32, scriptPubKey: P2SHScriptPubKey, flags: Seq[ScriptFlag],
amount: CurrencyUnit): WitnessTxSigComponentP2SH = {
WitnessTxSigComponentP2SHImpl(transaction,inputIndex,scriptPubKey, flags, amount)
WitnessTxSigComponentP2SHImpl(transaction, inputIndex, scriptPubKey, flags, amount)
}
}
object WitnessTxSigComponentRebuilt {
private case class WitnessTxSigComponentRebuiltImpl(transaction: WitnessTransaction,inputIndex: UInt32,
scriptPubKey: ScriptPubKey, witnessScriptPubKey: WitnessScriptPubKey,
flags: Seq[ScriptFlag], amount: CurrencyUnit) extends WitnessTxSigComponentRebuilt
private case class WitnessTxSigComponentRebuiltImpl(transaction: WitnessTransaction, inputIndex: UInt32,
scriptPubKey: ScriptPubKey, witnessScriptPubKey: WitnessScriptPubKey,
flags: Seq[ScriptFlag], amount: CurrencyUnit) extends WitnessTxSigComponentRebuilt
def apply(wtx: WitnessTransaction, inputIndex: UInt32, scriptPubKey: ScriptPubKey, witScriptPubKey: WitnessScriptPubKey,
flags: Seq[ScriptFlag], amount: CurrencyUnit): WitnessTxSigComponentRebuilt = {
WitnessTxSigComponentRebuiltImpl(wtx,inputIndex,scriptPubKey,witScriptPubKey,flags,amount)
flags: Seq[ScriptFlag], amount: CurrencyUnit): WitnessTxSigComponentRebuilt = {
WitnessTxSigComponentRebuiltImpl(wtx, inputIndex, scriptPubKey, witScriptPubKey, flags, amount)
}
}

View file

@ -1,94 +1,100 @@
package org.bitcoins.core.currency
import org.bitcoins.core.consensus.Consensus
import org.bitcoins.core.number.{BaseNumbers, Int64}
import org.bitcoins.core.number.{ BaseNumbers, Int64 }
import org.bitcoins.core.protocol.NetworkElement
import org.bitcoins.core.serializers.RawSatoshisSerializer
import org.bitcoins.core.util.Factory
sealed abstract class CurrencyUnit extends NetworkElement {
type A
protected def underlying : A
def satoshis: Satoshis
def >=(c : CurrencyUnit) : Boolean = {
def >=(c: CurrencyUnit): Boolean = {
satoshis.underlying >= c.satoshis.underlying
}
def >(c : CurrencyUnit) : Boolean = {
def >(c: CurrencyUnit): Boolean = {
satoshis.underlying > c.satoshis.underlying
}
def <(c : CurrencyUnit) : Boolean = {
def <(c: CurrencyUnit): Boolean = {
satoshis.underlying < c.satoshis.underlying
}
def <=(c : CurrencyUnit) : Boolean = {
def <=(c: CurrencyUnit): Boolean = {
satoshis.underlying <= c.satoshis.underlying
}
def !=(c : CurrencyUnit) : Boolean = !(this == c)
def !=(c: CurrencyUnit): Boolean = !(this == c)
def ==(c: CurrencyUnit): Boolean = satoshis == c.satoshis
def +(c : CurrencyUnit) : CurrencyUnit = {
def +(c: CurrencyUnit): CurrencyUnit = {
Satoshis(satoshis.underlying + c.satoshis.underlying)
}
def -(c : CurrencyUnit) : CurrencyUnit = {
def -(c: CurrencyUnit): CurrencyUnit = {
Satoshis(satoshis.underlying - c.satoshis.underlying)
}
def *(c : CurrencyUnit) : CurrencyUnit = {
def *(c: CurrencyUnit): CurrencyUnit = {
Satoshis(satoshis.underlying * c.satoshis.underlying)
}
def unary_- : CurrencyUnit = {
Satoshis(- satoshis.underlying)
Satoshis(-satoshis.underlying)
}
override def bytes = satoshis.bytes
def toBigDecimal: BigDecimal
protected def underlying: A
}
sealed abstract class Satoshis extends CurrencyUnit {
override type A = Int64
override def bytes = RawSatoshisSerializer.write(this)
override def satoshis: Satoshis = this
def toLong = underlying.toLong
override def toBigDecimal = BigDecimal(toBigInt)
def toBigInt: BigInt = BigInt(toLong)
override def toBigDecimal = BigDecimal(toBigInt)
def toLong = underlying.toLong
def ==(satoshis: Satoshis): Boolean = underlying == satoshis.underlying
}
object Satoshis extends Factory[Satoshis] with BaseNumbers[Satoshis] {
private case class SatoshisImpl(underlying : Int64) extends Satoshis
val min = Satoshis(Int64.min)
val max = Satoshis(Int64.max)
val zero = Satoshis(Int64.zero)
val one = Satoshis(Int64.one)
override def fromBytes(bytes : Seq[Byte]): Satoshis = RawSatoshisSerializer.read(bytes)
override def fromBytes(bytes: Seq[Byte]): Satoshis = RawSatoshisSerializer.read(bytes)
def apply(int64: Int64): Satoshis = SatoshisImpl(int64)
private case class SatoshisImpl(underlying: Int64) extends Satoshis
}
sealed abstract class Bitcoins extends CurrencyUnit {
override type A = BigDecimal
override def toBigDecimal: BigDecimal = underlying
override def hex = satoshis.hex
override def satoshis: Satoshis = {
val sat = underlying * CurrencyUnits.btcToSatoshiScalar
Satoshis(Int64(sat.toLongExact))
}
override def toBigDecimal: BigDecimal = underlying
override def hex = satoshis.hex
}
object Bitcoins extends BaseNumbers[Bitcoins] {
@ -97,16 +103,15 @@ object Bitcoins extends BaseNumbers[Bitcoins] {
val zero = Bitcoins(Satoshis.zero)
val one = Bitcoins(1)
private case class BitcoinsImpl(underlying: BigDecimal) extends Bitcoins
def apply(underlying: BigDecimal): Bitcoins = BitcoinsImpl(underlying)
def apply(satoshis: Satoshis): Bitcoins = {
val b: BigDecimal = satoshis.toLong * CurrencyUnits.satoshisToBTCScalar
Bitcoins(b)
}
}
def apply(underlying: BigDecimal): Bitcoins = BitcoinsImpl(underlying)
private case class BitcoinsImpl(underlying: BigDecimal) extends Bitcoins
}
object CurrencyUnits {
@ -118,7 +123,7 @@ object CurrencyUnits {
val zero: CurrencyUnit = Satoshis.zero
val negativeSatoshi = Satoshis(Int64(-1))
def toSatoshis(unit : CurrencyUnit): Satoshis = unit match {
def toSatoshis(unit: CurrencyUnit): Satoshis = unit match {
case b: Bitcoins => b.satoshis
case x: Satoshis => x
}

View file

@ -1,32 +1,32 @@
package org.bitcoins.core.gen
import org.bitcoins.core.protocol.{Bech32Address, BitcoinAddress, P2PKHAddress, P2SHAddress}
import org.bitcoins.core.protocol.{ Bech32Address, BitcoinAddress, P2PKHAddress, P2SHAddress }
import org.scalacheck.Gen
/**
* Created by chris on 6/12/17.
*/
* Created by chris on 6/12/17.
*/
sealed trait AddressGenerator {
def p2pkhAddress: Gen[P2PKHAddress] = for {
hash <- CryptoGenerators.sha256Hash160Digest
network <- ChainParamsGenerator.networkParams
addr = P2PKHAddress(hash,network)
addr = P2PKHAddress(hash, network)
} yield addr
def p2shAddress: Gen[P2SHAddress] = for {
hash <- CryptoGenerators.sha256Hash160Digest
network <- ChainParamsGenerator.networkParams
addr = P2SHAddress(hash,network)
addr = P2SHAddress(hash, network)
} yield addr
def bech32Address: Gen[Bech32Address] = for {
(witSPK,_) <- ScriptGenerators.witnessScriptPubKey
(witSPK, _) <- ScriptGenerators.witnessScriptPubKey
network <- ChainParamsGenerator.networkParams
addr = Bech32Address(witSPK,network)
addr = Bech32Address(witSPK, network)
} yield addr.get
def address: Gen[BitcoinAddress] = Gen.oneOf(p2pkhAddress,p2shAddress,bech32Address)
def address: Gen[BitcoinAddress] = Gen.oneOf(p2pkhAddress, p2shAddress, bech32Address)
}
object AddressGenerator extends AddressGenerator

View file

@ -3,33 +3,35 @@ package org.bitcoins.core.gen
import org.bitcoins.core.consensus.Merkle
import org.bitcoins.core.crypto.DoubleSha256Digest
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.blockchain.{Block, BlockHeader}
import org.bitcoins.core.protocol.blockchain.{ Block, BlockHeader }
import org.bitcoins.core.protocol.transaction.Transaction
import org.scalacheck.Gen
import scala.annotation.tailrec
/**
* Created by tom on 7/6/16.
*/
* Created by tom on 7/6/16.
*/
sealed abstract class BlockchainElementsGenerator {
/** Generates a block that contains the given txs, plus some more randomly generated ones */
def block(txs: Seq[Transaction]): Gen[Block] = for {
randomNum <- Gen.choose(1,10)
randomNum <- Gen.choose(1, 10)
neededTxs = if ((randomNum - txs.size) >= 0) randomNum else 0
genTxs <- Gen.listOfN(neededTxs, TransactionGenerators.transaction)
allTxs = genTxs ++ txs
header <- blockHeader(allTxs)
} yield Block(header,allTxs)
/** Generates a random [[Block]], note that we limit this
* to 10 transactions currently */
def block : Gen[Block] = for {
} yield Block(header, allTxs)
/**
* Generates a random [[Block]], note that we limit this
* to 10 transactions currently
*/
def block: Gen[Block] = for {
header <- blockHeader
txs <- TransactionGenerators.smallTransactions
} yield Block(header, txs)
/** Generates a random [[BlockHeader]] */
def blockHeader : Gen[BlockHeader] = for {
def blockHeader: Gen[BlockHeader] = for {
previousBlockHash <- CryptoGenerators.doubleSha256Digest
b <- blockHeader(previousBlockHash)
} yield b
@ -37,14 +39,14 @@ sealed abstract class BlockchainElementsGenerator {
/** Generates a random [[BlockHeader]] with the specified previousBlockHash */
def blockHeader(previousBlockHash: DoubleSha256Digest): Gen[BlockHeader] = for {
nBits <- NumberGenerator.uInt32s
b <- blockHeader(previousBlockHash,nBits)
b <- blockHeader(previousBlockHash, nBits)
} yield b
/** Generates a random [[BlockHeader]] where you can specify the previousBlockHash and nBits */
def blockHeader(previousBlockHash: DoubleSha256Digest, nBits: UInt32): Gen[BlockHeader] = for {
numTxs <- Gen.choose(1,10)
numTxs <- Gen.choose(1, 10)
txs <- Gen.listOfN(numTxs, TransactionGenerators.transaction)
header<- blockHeader(previousBlockHash,nBits,txs)
header <- blockHeader(previousBlockHash, nBits, txs)
} yield header
/** Generates a [[BlockHeader]]] that has the fields set to the given values */
@ -53,21 +55,23 @@ sealed abstract class BlockchainElementsGenerator {
merkleRootHash = Merkle.computeMerkleRoot(txs)
time <- NumberGenerator.uInt32s
nonce <- NumberGenerator.uInt32s
} yield BlockHeader(version, previousBlockHash,merkleRootHash,time,nBits,nonce)
} yield BlockHeader(version, previousBlockHash, merkleRootHash, time, nBits, nonce)
/** Generates a [[BlockHeader]] that has a merkle root hash corresponding to the given txs */
def blockHeader(txs: Seq[Transaction]): Gen[BlockHeader] = for {
previousBlockHash <- CryptoGenerators.doubleSha256Digest
nBits <- NumberGenerator.uInt32s
header <- blockHeader(previousBlockHash,nBits,txs)
header <- blockHeader(previousBlockHash, nBits, txs)
} yield header
/** Generates a chain of valid headers of the size specified by num,
* 'valid' means their nBits are the same and each header properly
* references the previous block header's hash */
/**
* Generates a chain of valid headers of the size specified by num,
* 'valid' means their nBits are the same and each header properly
* references the previous block header's hash
*/
def validHeaderChain(num: Long): Gen[Seq[BlockHeader]] = {
val startHeader = blockHeader.sample.get
validHeaderChain(num,startHeader)
validHeaderChain(num, startHeader)
}
def validHeaderChain(num: Long, startHeader: BlockHeader): Gen[Seq[BlockHeader]] = {
@ -75,11 +79,11 @@ sealed abstract class BlockchainElementsGenerator {
def loop(remainingHeaders: Long, accum: Seq[BlockHeader]): Seq[BlockHeader] = {
if (remainingHeaders == 0) accum.reverse
else {
val nextHeader = blockHeader(accum.head.hash,accum.head.nBits).sample.get
loop(remainingHeaders-1, nextHeader +: accum)
val nextHeader = blockHeader(accum.head.hash, accum.head.nBits).sample.get
loop(remainingHeaders - 1, nextHeader +: accum)
}
}
loop(num-1, Seq(startHeader))
loop(num - 1, Seq(startHeader))
}
}

View file

@ -4,34 +4,33 @@ import org.bitcoins.core.bloom._
import org.scalacheck.Gen
/**
* Created by chris on 8/7/16.
*/
* Created by chris on 8/7/16.
*/
abstract class BloomFilterGenerator {
/** Builds a generic bloom filter loaded with no hashes and returns it */
def bloomFilter: Gen[BloomFilter] = for {
size <- Gen.choose(1,100)
size <- Gen.choose(1, 100)
falsePositiveRate <- Gen.choose(0.00001, 0.99999)
tweak <- NumberGenerator.uInt32s
flags <- bloomFlag
} yield BloomFilter(size,falsePositiveRate, tweak, flags)
} yield BloomFilter(size, falsePositiveRate, tweak, flags)
/** Loads a generic bloom filter with the given byte vectors and returns it */
def bloomFilter(byteVectors : Seq[Seq[Byte]]): Gen[BloomFilter] = for {
def bloomFilter(byteVectors: Seq[Seq[Byte]]): Gen[BloomFilter] = for {
filter <- bloomFilter
} yield filter.insertByteVectors(byteVectors)
/** Returns a bloom filter loaded with randomly generated byte vectors */
def loadedBloomFilter: Gen[(BloomFilter,Seq[Seq[Byte]])] = for {
def loadedBloomFilter: Gen[(BloomFilter, Seq[Seq[Byte]])] = for {
filter <- bloomFilter
randomNum <- Gen.choose(0,filter.filterSize.num.toInt)
randomNum <- Gen.choose(0, filter.filterSize.num.toInt)
hashes <- CryptoGenerators.doubleSha256DigestSeq(randomNum)
loaded = filter.insertHashes(hashes)
} yield (loaded,hashes.map(_.bytes))
} yield (loaded, hashes.map(_.bytes))
/** Generates a random bloom flag */
def bloomFlag: Gen[BloomFlag] = Gen.oneOf(BloomUpdateNone,BloomUpdateAll,BloomUpdateP2PKOnly)
def bloomFlag: Gen[BloomFlag] = Gen.oneOf(BloomUpdateNone, BloomUpdateAll, BloomUpdateP2PKOnly)
}

View file

@ -4,8 +4,8 @@ import org.bitcoins.core.config._
import org.scalacheck.Gen
/**
* Created by chris on 6/6/17.
*/
* Created by chris on 6/6/17.
*/
sealed abstract class ChainParamsGenerator {
def networkParams: Gen[NetworkParameters] = bitcoinNetworkParams

View file

@ -13,86 +13,88 @@ import org.scalacheck.Gen
import scala.annotation.tailrec
/**
* Created by chris on 4/18/17.
*/
* Created by chris on 4/18/17.
*/
sealed trait ChannelGenerators extends BitcoinSLogger {
/** Creates an [[Transaction]], [[EscrowTimeoutScriptPubKey]] and the [[ECPrivateKey]] need to spend from the SPK */
def anchorTx: Gen[(Transaction, EscrowTimeoutScriptPubKey, Seq[ECPrivateKey])] = for {
(redeemScript,privKeys) <- ScriptGenerators.escrowTimeoutScriptPubKey2Of2
(redeemScript, privKeys) <- ScriptGenerators.escrowTimeoutScriptPubKey2Of2
amount <- CurrencyUnitGenerator.satoshis.suchThat(_ >= Policy.minChannelAmount)
p2wsh = P2WSHWitnessSPKV0(redeemScript)
(aTx,_) = TransactionGenerators.buildCreditingTransaction(TransactionConstants.validLockVersion,p2wsh,amount)
} yield (aTx,redeemScript,privKeys)
(aTx, _) = TransactionGenerators.buildCreditingTransaction(TransactionConstants.validLockVersion, p2wsh, amount)
} yield (aTx, redeemScript, privKeys)
def channelAwaitingAnchorTxNotConfirmed: Gen[(ChannelAwaitingAnchorTx, Seq[ECPrivateKey])] = for {
(aTx,redeemScript,privKeys) <- anchorTx
} yield (ChannelAwaitingAnchorTx(aTx,redeemScript,0).left.get,privKeys)
(aTx, redeemScript, privKeys) <- anchorTx
} yield (ChannelAwaitingAnchorTx(aTx, redeemScript, 0).left.get, privKeys)
/** Creates a [[ChannelAwaitingAnchorTx]] and
* the private keys needed to spend from the locked output.
* This generator assumes that the anchor tx has sufficient confirmations */
/**
* Creates a [[ChannelAwaitingAnchorTx]] and
* the private keys needed to spend from the locked output.
* This generator assumes that the anchor tx has sufficient confirmations
*/
def channelAwaitingAnchorTx: Gen[(ChannelAwaitingAnchorTx, Seq[ECPrivateKey])] = for {
(aTx,redeemScript,privKeys) <- anchorTx
} yield (ChannelAwaitingAnchorTx(aTx,redeemScript,Policy.confirmations).left.get,privKeys)
(aTx, redeemScript, privKeys) <- anchorTx
} yield (ChannelAwaitingAnchorTx(aTx, redeemScript, Policy.confirmations).left.get, privKeys)
/** A [[ChannelInProgress]] that has paid the server exactly one time */
def freshChannelInProgress: Gen[(ChannelInProgress, Seq[ECPrivateKey])] = for {
(awaiting,privKeys) <- channelAwaitingAnchorTx
(s1,_) <- ScriptGenerators.scriptPubKey
(awaiting, privKeys) <- channelAwaitingAnchorTx
(s1, _) <- ScriptGenerators.scriptPubKey
amount = Policy.minChannelAmount
clientSigned = awaiting.clientSign(s1,amount,privKeys.head).left.get
clientSigned = awaiting.clientSign(s1, amount, privKeys.head).left.get
fullySigned = clientSigned.serverSign(privKeys(1))
} yield (fullySigned.left.get,privKeys)
} yield (fullySigned.left.get, privKeys)
/** A [[ChannelInProgress]] that has paid the server between 1 and 10 times */
def channelInProgress: Gen[(ChannelInProgress, Seq[ECPrivateKey])] = for {
(old,privKeys) <- freshChannelInProgress
runs <- Gen.choose(1,10)
(old, privKeys) <- freshChannelInProgress
runs <- Gen.choose(1, 10)
amount = Policy.dustThreshold
inProgress = simulate(runs,old,amount,privKeys.head,privKeys(1))
inProgress = simulate(runs, old, amount, privKeys.head, privKeys(1))
} yield (inProgress.left.get, privKeys)
/** Creates a Channel that has been signed by the client */
def channelInProgressClientSigned: Gen[(ChannelInProgressClientSigned, Seq[ECPrivateKey])] = for {
(old,privKeys) <- freshChannelInProgress
clientSigned = old.clientSign(Policy.dustThreshold,privKeys.head).left.get
} yield (clientSigned,privKeys)
(old, privKeys) <- freshChannelInProgress
clientSigned = old.clientSign(Policy.dustThreshold, privKeys.head).left.get
} yield (clientSigned, privKeys)
def baseInProgress: Gen[(BaseInProgress, Seq[ECPrivateKey])] = Gen.oneOf(channelInProgress, channelInProgressClientSigned)
/** Generator for a payment channel that opened, simulated, then closed */
def channelClosed: Gen[(ChannelClosed, Seq[ECPrivateKey])] = for {
(inProgress, privKeys) <- channelInProgress
(serverScriptPubKey,_) <- ScriptGenerators.scriptPubKey
(clientKey,serverKey) = (privKeys.head, privKeys(1))
(serverScriptPubKey, _) <- ScriptGenerators.scriptPubKey
(clientKey, serverKey) = (privKeys.head, privKeys(1))
amount = Policy.dustThreshold
fee = amount
clientSigned = inProgress.clientSign(amount,clientKey)
closed = clientSigned.left.flatMap(_.close(serverScriptPubKey,serverKey,fee))
} yield (closed.left.get,privKeys)
clientSigned = inProgress.clientSign(amount, clientKey)
closed = clientSigned.left.flatMap(_.close(serverScriptPubKey, serverKey, fee))
} yield (closed.left.get, privKeys)
/** Simulates the execution of a [[Channel]]
* @param runs the number of times the client pays the server
* @param inProgress the [[ChannelInProgress]] to simulate
* @param amount the amount we pay to the server every time
* @param clientKey key the client uses to sign the payment channel output
* @param serverKey key the server uses to sign the payment channel output
* @return
*/
/**
* Simulates the execution of a [[Channel]]
* @param runs the number of times the client pays the server
* @param inProgress the [[ChannelInProgress]] to simulate
* @param amount the amount we pay to the server every time
* @param clientKey key the client uses to sign the payment channel output
* @param serverKey key the server uses to sign the payment channel output
* @return
*/
def simulate(runs: Int, inProgress: ChannelInProgress, amount: CurrencyUnit,
clientKey: ECPrivateKey, serverKey: ECPrivateKey): Either[ChannelInProgress,TxBuilderError] = {
clientKey: ECPrivateKey, serverKey: ECPrivateKey): Either[ChannelInProgress, TxBuilderError] = {
@tailrec
def loop(old: Either[ChannelInProgress,TxBuilderError], remaining: Int): Either[ChannelInProgress,TxBuilderError] = {
def loop(old: Either[ChannelInProgress, TxBuilderError], remaining: Int): Either[ChannelInProgress, TxBuilderError] = {
if (old.isRight || remaining == 0) old
else {
val clientSigned = old.left.flatMap(_.clientSign(amount,clientKey))
val clientSigned = old.left.flatMap(_.clientSign(amount, clientKey))
val serverSigned = clientSigned.left.flatMap(c => c.serverSign(serverKey))
loop(serverSigned,remaining - 1)
loop(serverSigned, remaining - 1)
}
}
loop(Left(inProgress),runs)
loop(Left(inProgress), runs)
}
}

View file

@ -13,7 +13,7 @@ sealed abstract class CreditingTxGen {
private val max = 3
/** Note this generator does NOT generate outputs with negative values */
private def nonEmptyOutputs: Gen[Seq[TransactionOutput]] = Gen.choose(1,5).flatMap { n =>
private def nonEmptyOutputs: Gen[Seq[TransactionOutput]] = Gen.choose(1, 5).flatMap { n =>
Gen.listOfN(n, TransactionGenerators.realisticOutput)
}
@ -21,7 +21,7 @@ sealed abstract class CreditingTxGen {
Gen.oneOf(p2pkOutput, p2pkhOutput, multiSigOutput, cltvOutput, csvOutput)
}
def rawOutputs: Gen[Seq[CreditingTxGen.CreditingTxInfo]] = Gen.choose(min,max).flatMap(n => Gen.listOfN(n,rawOutput))
def rawOutputs: Gen[Seq[CreditingTxGen.CreditingTxInfo]] = Gen.choose(min, max).flatMap(n => Gen.listOfN(n, rawOutput))
def basicOutput: Gen[CreditingTxGen.CreditingTxInfo] = {
Gen.oneOf(p2pkOutput, p2pkhOutput, multiSigOutput)
@ -29,44 +29,48 @@ sealed abstract class CreditingTxGen {
def nonP2WSHOutput: Gen[CreditingTxGen.CreditingTxInfo] = rawOutput
def output: Gen[CreditingTxGen.CreditingTxInfo] = Gen.oneOf(p2pkOutput,
def output: Gen[CreditingTxGen.CreditingTxInfo] = Gen.oneOf(
p2pkOutput,
p2pkhOutput, multiSigOutput, p2shOutput,
csvOutput, cltvOutput,
p2wpkhOutput, p2wshOutput)
p2wpkhOutput, p2wshOutput
)
def outputs: Gen[Seq[CreditingTxGen.CreditingTxInfo]] = {
Gen.choose(min,5).flatMap(n => Gen.listOfN(n,output))
Gen.choose(min, 5).flatMap(n => Gen.listOfN(n, output))
}
/** Generates a crediting tx with a p2pk spk at the returned index */
def p2pkOutput: Gen[CreditingTxGen.CreditingTxInfo] = ScriptGenerators.p2pkScriptPubKey.flatMap { p2pk =>
val signer = (p2pk._2.sign(_: Seq[Byte]),None)
build(p2pk._1,Seq(signer), None, None)
val signer = (p2pk._2.sign(_: Seq[Byte]), None)
build(p2pk._1, Seq(signer), None, None)
}
/** Generates multiple crediting txs with p2pk spks at the returned index */
def p2pkOutputs: Gen[Seq[CreditingTxGen.CreditingTxInfo]] = {
Gen.choose(min,max).flatMap(n => Gen.listOfN(n,p2pkOutput))
Gen.choose(min, max).flatMap(n => Gen.listOfN(n, p2pkOutput))
}
/** Generates a transaction that has a p2pkh output at the returned index. This
* output can be spent by the returned ECPrivateKey */
/**
* Generates a transaction that has a p2pkh output at the returned index. This
* output can be spent by the returned ECPrivateKey
*/
def p2pkhOutput: Gen[CreditingTxGen.CreditingTxInfo] = ScriptGenerators.p2pkhScriptPubKey.flatMap { p2pkh =>
val signer = (p2pkh._2.sign(_: Seq[Byte]),Some(p2pkh._2.publicKey))
build(p2pkh._1,Seq(signer), None, None)
val signer = (p2pkh._2.sign(_: Seq[Byte]), Some(p2pkh._2.publicKey))
build(p2pkh._1, Seq(signer), None, None)
}
/** Generates a sequence of p2pkh outputs at the returned index */
def p2pkhOutputs: Gen[Seq[CreditingTxGen.CreditingTxInfo]] = {
Gen.choose(min,max).flatMap(n => Gen.listOfN(n,p2pkhOutput))
Gen.choose(min, max).flatMap(n => Gen.listOfN(n, p2pkhOutput))
}
def multiSigOutput: Gen[CreditingTxGen.CreditingTxInfo] = ScriptGenerators.multiSigScriptPubKey.flatMap { multisig =>
val signer = multisig._2.map(p => (p.sign(_ : Seq[Byte]),None))
val signer = multisig._2.map(p => (p.sign(_: Seq[Byte]), None))
build(multisig._1, signer, None, None)
}
def multiSigOutputs: Gen[Seq[CreditingTxGen.CreditingTxInfo]] = {
Gen.choose(min,max).flatMap(n => Gen.listOfN(n,multiSigOutput))
Gen.choose(min, max).flatMap(n => Gen.listOfN(n, multiSigOutput))
}
def p2shOutput: Gen[CreditingTxGen.CreditingTxInfo] = rawOutput.flatMap { o =>
@ -75,102 +79,106 @@ sealed abstract class CreditingTxGen {
val oldOutput = oldTx.outputs(o._2)
val redeemScript = oldTx.outputs(o._2).scriptPubKey
val p2sh = P2SHScriptPubKey(redeemScript)
val updatedOutput = TransactionOutput(oldOutput.value,p2sh)
val updatedOutput = TransactionOutput(oldOutput.value, p2sh)
val tx = oldTx match {
case btx: BaseTransaction => BaseTransaction(btx.version, btx.inputs,
btx.outputs.updated(o._2,updatedOutput), btx.lockTime)
btx.outputs.updated(o._2, updatedOutput), btx.lockTime)
case wtx: WitnessTransaction => WitnessTransaction(wtx.version, wtx.inputs,
wtx.outputs.updated(o._2, updatedOutput), wtx.lockTime, wtx.witness)
}
(tx,o._2,o._3,Some(redeemScript), o._5, hashType)
(tx, o._2, o._3, Some(redeemScript), o._5, hashType)
}
}
def p2shOutputs: Gen[Seq[CreditingTxGen.CreditingTxInfo]] = {
Gen.choose(min,max).flatMap(n => Gen.listOfN(n,p2shOutput))
Gen.choose(min, max).flatMap(n => Gen.listOfN(n, p2shOutput))
}
def cltvOutput: Gen[CreditingTxGen.CreditingTxInfo] = TransactionGenerators.spendableCLTVValues.flatMap { case (scriptNum,_) =>
basicOutput.flatMap { o =>
CryptoGenerators.hashType.map { hashType =>
val oldTx = o._1
val oldOutput = oldTx.outputs(o._2)
val csvSPK = CLTVScriptPubKey(scriptNum,oldOutput.scriptPubKey)
val updatedOutput = TransactionOutput(oldOutput.value,csvSPK)
val tx = oldTx match {
case btx: BaseTransaction => BaseTransaction(btx.version, btx.inputs,
btx.outputs.updated(o._2,updatedOutput), btx.lockTime)
case wtx: WitnessTransaction => WitnessTransaction(wtx.version, wtx.inputs,
wtx.outputs.updated(o._2, updatedOutput), wtx.lockTime, wtx.witness)
def cltvOutput: Gen[CreditingTxGen.CreditingTxInfo] = TransactionGenerators.spendableCLTVValues.flatMap {
case (scriptNum, _) =>
basicOutput.flatMap { o =>
CryptoGenerators.hashType.map { hashType =>
val oldTx = o._1
val oldOutput = oldTx.outputs(o._2)
val csvSPK = CLTVScriptPubKey(scriptNum, oldOutput.scriptPubKey)
val updatedOutput = TransactionOutput(oldOutput.value, csvSPK)
val tx = oldTx match {
case btx: BaseTransaction => BaseTransaction(btx.version, btx.inputs,
btx.outputs.updated(o._2, updatedOutput), btx.lockTime)
case wtx: WitnessTransaction => WitnessTransaction(wtx.version, wtx.inputs,
wtx.outputs.updated(o._2, updatedOutput), wtx.lockTime, wtx.witness)
}
(tx, o._2, o._3, o._4, o._5, hashType)
}
(tx,o._2,o._3,o._4, o._5, hashType)
}
}
}
def cltvOutputs: Gen[Seq[CreditingTxGen.CreditingTxInfo]] = Gen.choose(min,max).flatMap(n => Gen.listOfN(n,cltvOutput))
def cltvOutputs: Gen[Seq[CreditingTxGen.CreditingTxInfo]] = Gen.choose(min, max).flatMap(n => Gen.listOfN(n, cltvOutput))
def csvOutput: Gen[CreditingTxGen.CreditingTxInfo] = TransactionGenerators.spendableCSVValues.flatMap { case (scriptNum, _) =>
basicOutput.flatMap { o =>
CryptoGenerators.hashType.map { hashType =>
val oldTx = o._1
val oldOutput = oldTx.outputs(o._2)
val csvSPK = CSVScriptPubKey(scriptNum,oldOutput.scriptPubKey)
val updatedOutput = TransactionOutput(oldOutput.value,csvSPK)
val tx = oldTx match {
case btx: BaseTransaction => BaseTransaction(btx.version, btx.inputs,
btx.outputs.updated(o._2,updatedOutput), btx.lockTime)
case wtx: WitnessTransaction => WitnessTransaction(wtx.version, wtx.inputs,
wtx.outputs.updated(o._2, updatedOutput), wtx.lockTime, wtx.witness)
def csvOutput: Gen[CreditingTxGen.CreditingTxInfo] = TransactionGenerators.spendableCSVValues.flatMap {
case (scriptNum, _) =>
basicOutput.flatMap { o =>
CryptoGenerators.hashType.map { hashType =>
val oldTx = o._1
val oldOutput = oldTx.outputs(o._2)
val csvSPK = CSVScriptPubKey(scriptNum, oldOutput.scriptPubKey)
val updatedOutput = TransactionOutput(oldOutput.value, csvSPK)
val tx = oldTx match {
case btx: BaseTransaction => BaseTransaction(btx.version, btx.inputs,
btx.outputs.updated(o._2, updatedOutput), btx.lockTime)
case wtx: WitnessTransaction => WitnessTransaction(wtx.version, wtx.inputs,
wtx.outputs.updated(o._2, updatedOutput), wtx.lockTime, wtx.witness)
}
(tx, o._2, o._3, o._4, o._5, hashType)
}
(tx,o._2,o._3,o._4, o._5, hashType)
}
}
}
def csvOutputs: Gen[Seq[CreditingTxGen.CreditingTxInfo]] = Gen.choose(min,max).flatMap(n => Gen.listOfN(n,csvOutput))
def csvOutputs: Gen[Seq[CreditingTxGen.CreditingTxInfo]] = Gen.choose(min, max).flatMap(n => Gen.listOfN(n, csvOutput))
def p2wpkhOutput: Gen[CreditingTxGen.CreditingTxInfo] = ScriptGenerators.p2wpkhSPKV0.flatMap { witSPK =>
val signers = witSPK._2.map(p => (p.sign(_: Seq[Byte]), Some(p.publicKey)))
val scriptWit = P2WPKHWitnessV0(witSPK._2.head.publicKey)
build(witSPK._1,signers,None,Some(scriptWit))
build(witSPK._1, signers, None, Some(scriptWit))
}
def p2wpkhOutputs: Gen[Seq[CreditingTxGen.CreditingTxInfo]] = Gen.choose(min,max).flatMap(n => Gen.listOfN(n,p2wpkhOutput))
def p2wpkhOutputs: Gen[Seq[CreditingTxGen.CreditingTxInfo]] = Gen.choose(min, max).flatMap(n => Gen.listOfN(n, p2wpkhOutput))
def p2wshOutput: Gen[CreditingTxGen.CreditingTxInfo] = nonP2WSHOutput.flatMap { case (tx,outputIndex,signer,redeemScriptOpt,scriptWitOpt, _) =>
val spk = tx.outputs(outputIndex).scriptPubKey
val scriptWit = P2WSHWitnessV0(spk)
val witSPK = P2WSHWitnessSPKV0(spk)
build(witSPK,signer,None,Some(scriptWit))
def p2wshOutput: Gen[CreditingTxGen.CreditingTxInfo] = nonP2WSHOutput.flatMap {
case (tx, outputIndex, signer, redeemScriptOpt, scriptWitOpt, _) =>
val spk = tx.outputs(outputIndex).scriptPubKey
val scriptWit = P2WSHWitnessV0(spk)
val witSPK = P2WSHWitnessSPKV0(spk)
build(witSPK, signer, None, Some(scriptWit))
}
def p2wshOutputs: Gen[Seq[CreditingTxGen.CreditingTxInfo]] = Gen.choose(min,max).flatMap(n => Gen.listOfN(n,p2wshOutput))
def p2wshOutputs: Gen[Seq[CreditingTxGen.CreditingTxInfo]] = Gen.choose(min, max).flatMap(n => Gen.listOfN(n, p2wshOutput))
/** A nested output is a p2sh/p2wsh wrapped output */
def nestedOutput: Gen[CreditingTxGen.CreditingTxInfo] = Gen.oneOf(p2wshOutput, p2shOutput)
def nestedOutputs: Gen[Seq[CreditingTxGen.CreditingTxInfo]] = Gen.choose(min,max).flatMap(n => Gen.listOfN(n,nestedOutput))
def nestedOutputs: Gen[Seq[CreditingTxGen.CreditingTxInfo]] = Gen.choose(min, max).flatMap(n => Gen.listOfN(n, nestedOutput))
def random: Gen[CreditingTxGen.CreditingTxInfo] = nonEmptyOutputs.flatMap { outputs =>
Gen.choose(0,outputs.size-1).flatMap { outputIndex: Int =>
ScriptGenerators.scriptPubKey.flatMap { case (spk,keys) =>
WitnessGenerators.scriptWitness.flatMap { wit: ScriptWitness =>
CryptoGenerators.hashType.map { hashType: HashType =>
val tc = TransactionConstants
val signers: Seq[Signer.Sign] = keys.map(k => (k.sign(_: Seq[Byte]), Some(k.publicKey)))
val creditingTx = BaseTransaction(tc.validLockVersion, Nil, outputs, tc.lockTime)
(creditingTx,outputIndex,signers, Some(spk), Some(wit),hashType)
Gen.choose(0, outputs.size - 1).flatMap { outputIndex: Int =>
ScriptGenerators.scriptPubKey.flatMap {
case (spk, keys) =>
WitnessGenerators.scriptWitness.flatMap { wit: ScriptWitness =>
CryptoGenerators.hashType.map { hashType: HashType =>
val tc = TransactionConstants
val signers: Seq[Signer.Sign] = keys.map(k => (k.sign(_: Seq[Byte]), Some(k.publicKey)))
val creditingTx = BaseTransaction(tc.validLockVersion, Nil, outputs, tc.lockTime)
(creditingTx, outputIndex, signers, Some(spk), Some(wit), hashType)
}
}
}
}
}
}
def randoms: Gen[Seq[CreditingTxGen.CreditingTxInfo]] = Gen.choose(min,max).flatMap(n => Gen.listOfN(n,random))
def randoms: Gen[Seq[CreditingTxGen.CreditingTxInfo]] = Gen.choose(min, max).flatMap(n => Gen.listOfN(n, random))
private def build(spk: ScriptPubKey, signers: Seq[Signer.Sign],
redeemScript: Option[ScriptPubKey],
redeemScript: Option[ScriptPubKey],
scriptWitness: Option[ScriptWitness]): Gen[CreditingTxGen.CreditingTxInfo] = nonEmptyOutputs.flatMap { outputs =>
CryptoGenerators.hashType.flatMap { hashType =>
Gen.choose(0, outputs.size - 1).map { idx =>
@ -178,7 +186,7 @@ sealed abstract class CreditingTxGen {
val updated = outputs.updated(idx, TransactionOutput(old.value, spk))
val tc = TransactionConstants
val btx = BaseTransaction(tc.version, Nil, updated, tc.lockTime)
val data = (btx, idx, signers, redeemScript, scriptWitness,hashType)
val data = (btx, idx, signers, redeemScript, scriptWitness, hashType)
data
}
}

View file

@ -7,74 +7,71 @@ import org.bitcoins.core.util.CryptoUtil
import org.scalacheck.Gen
/**
* Created by chris on 6/22/16.
*/
* Created by chris on 6/22/16.
*/
sealed abstract class CryptoGenerators {
def privateKey : Gen[ECPrivateKey] = Gen.const(ECPrivateKey())
def privateKey: Gen[ECPrivateKey] = Gen.const(ECPrivateKey())
/**
* Generate a sequence of private keys
* @param num maximum number of keys to generate
* @return
*/
def privateKeySeq(num : Int): Gen[Seq[ECPrivateKey]] = Gen.listOfN(num,privateKey)
* Generate a sequence of private keys
* @param num maximum number of keys to generate
* @return
*/
def privateKeySeq(num: Int): Gen[Seq[ECPrivateKey]] = Gen.listOfN(num, privateKey)
/**
* Generates a sequence of private keys, and determines an amount of 'required' private keys
* that a transaction needs to be signed with
* @param num the maximum number of keys to generate
* @return
*/
* Generates a sequence of private keys, and determines an amount of 'required' private keys
* that a transaction needs to be signed with
* @param num the maximum number of keys to generate
* @return
*/
def privateKeySeqWithRequiredSigs(num: Int): Gen[(Seq[ECPrivateKey], Int)] = {
val privateKeys = privateKeySeq(num)
for {
keys <- privateKeys
requiredSigs <- Gen.choose(0,keys.size-1)
} yield (keys,requiredSigs)
requiredSigs <- Gen.choose(0, keys.size - 1)
} yield (keys, requiredSigs)
}
/**
* Generates a random number of private keys less than the max public keys setting in [[ScriptSettings]]
* also generates a random 'requiredSigs' number that a transaction needs to be signed with
*/
* Generates a random number of private keys less than the max public keys setting in [[ScriptSettings]]
* also generates a random 'requiredSigs' number that a transaction needs to be signed with
*/
def privateKeySeqWithRequiredSigs: Gen[(Seq[ECPrivateKey], Int)] = for {
num <- Gen.choose(0,15)
num <- Gen.choose(0, 15)
keysAndRequiredSigs <- privateKeySeqWithRequiredSigs(num)
} yield keysAndRequiredSigs
/** A generator with 7 or less private keys -- useful for creating smaller scripts */
def smallPrivateKeySeqWithRequiredSigs: Gen[(Seq[ECPrivateKey], Int)] = for {
num <- Gen.choose(0,7)
num <- Gen.choose(0, 7)
keysAndRequiredSigs <- privateKeySeqWithRequiredSigs(num)
} yield keysAndRequiredSigs
/** Generates a random public key */
def publicKey : Gen[ECPublicKey] = for {
def publicKey: Gen[ECPublicKey] = for {
privKey <- privateKey
} yield privKey.publicKey
/** Generates a random digital signature */
def digitalSignature : Gen[ECDigitalSignature] = for {
def digitalSignature: Gen[ECDigitalSignature] = for {
privKey <- privateKey
hash <- CryptoGenerators.doubleSha256Digest
} yield privKey.sign(hash)
/** Generates a random [[DoubleSha256Digest]] */
def doubleSha256Digest : Gen[DoubleSha256Digest] = for {
def doubleSha256Digest: Gen[DoubleSha256Digest] = for {
hex <- StringGenerators.hexString
digest = CryptoUtil.doubleSHA256(hex)
} yield digest
/**
* Generates a sequence of [[DoubleSha256Digest]]
* @param num the number of digets to generate
* @return
*/
def doubleSha256DigestSeq(num : Int): Gen[Seq[DoubleSha256Digest]] = Gen.listOfN(num,doubleSha256Digest)
* Generates a sequence of [[DoubleSha256Digest]]
* @param num the number of digets to generate
* @return
*/
def doubleSha256DigestSeq(num: Int): Gen[Seq[DoubleSha256Digest]] = Gen.listOfN(num, doubleSha256Digest)
/** Generates a random [[org.bitcoins.core.crypto.Sha256Hash160Digest]] */
def sha256Hash160Digest: Gen[Sha256Hash160Digest] = for {
@ -97,7 +94,7 @@ sealed abstract class CryptoGenerators {
def extPublicKey: Gen[ExtPublicKey] = extPrivateKey.map(_.extPublicKey)
def extKey: Gen[ExtKey] = Gen.oneOf(extPrivateKey,extPublicKey)
def extKey: Gen[ExtKey] = Gen.oneOf(extPrivateKey, extPublicKey)
}

View file

@ -1,15 +1,15 @@
package org.bitcoins.core.gen
import org.bitcoins.core.currency.{Bitcoins, CurrencyUnit, CurrencyUnits, Satoshis}
import org.bitcoins.core.currency.{ Bitcoins, CurrencyUnit, CurrencyUnits, Satoshis }
import org.bitcoins.core.number.Int64
import org.scalacheck.Gen
/**
* Created by chris on 6/23/16.
*/
* Created by chris on 6/23/16.
*/
trait CurrencyUnitGenerator {
def satoshis : Gen[Satoshis] = for {
def satoshis: Gen[Satoshis] = for {
int64 <- NumberGenerator.int64s
} yield Satoshis(int64)
@ -17,17 +17,17 @@ trait CurrencyUnitGenerator {
sat <- satoshis
} yield Bitcoins(sat)
def currencyUnit: Gen[CurrencyUnit] = Gen.oneOf(satoshis,bitcoins)
def currencyUnit: Gen[CurrencyUnit] = Gen.oneOf(satoshis, bitcoins)
def positiveSatoshis: Gen[Satoshis] = satoshis.suchThat(_ >= CurrencyUnits.zero)
/** Generates a postiive satoshi value that is 'realistic'. This current 'realistic' range
* is from 0 to 1,000,000 bitcoin
*/
def positiveRealistic: Gen[Satoshis] = Gen.choose(0,Bitcoins(1000000).satoshis.toLong).map { n =>
/**
* Generates a postiive satoshi value that is 'realistic'. This current 'realistic' range
* is from 0 to 1,000,000 bitcoin
*/
def positiveRealistic: Gen[Satoshis] = Gen.choose(0, Bitcoins(1000000).satoshis.toLong).map { n =>
Satoshis(Int64(n))
}
}
object CurrencyUnitGenerator extends CurrencyUnitGenerator

View file

@ -2,14 +2,14 @@ package org.bitcoins.core.gen
import org.bitcoins.core.bloom.BloomFilter
import org.bitcoins.core.crypto.DoubleSha256Digest
import org.bitcoins.core.protocol.blockchain.{Block, MerkleBlock, PartialMerkleTree}
import org.bitcoins.core.protocol.blockchain.{ Block, MerkleBlock, PartialMerkleTree }
import org.bitcoins.core.protocol.transaction.Transaction
import org.bitcoins.core.util.BitcoinSLogger
import org.scalacheck.Gen
/**
* Created by chris on 8/12/16.
*/
* Created by chris on 8/12/16.
*/
abstract class MerkleGenerator {
private val logger = BitcoinSLogger.logger
@ -17,50 +17,51 @@ abstract class MerkleGenerator {
def merkleBlockWithInsertedTxIds(txs: Seq[Transaction]): Gen[(MerkleBlock, Block, Seq[DoubleSha256Digest])] = for {
block <- BlockchainElementsGenerator.block(txs)
txIds = txs.map(_.txId)
merkleBlock = MerkleBlock(block,txIds)
merkleBlock = MerkleBlock(block, txIds)
} yield (merkleBlock, block, txIds)
/** Returns a [[MerkleBlock]] including the sequence of hashes inserted in to the bloom filter */
def merkleBlockWithInsertedTxIds: Gen[(MerkleBlock,Block,Seq[DoubleSha256Digest])] = for {
def merkleBlockWithInsertedTxIds: Gen[(MerkleBlock, Block, Seq[DoubleSha256Digest])] = for {
//TODO: Revisit this later, I've limited this to increase test speed. If I increase this from 5 tests take a lot longer
rand <- Gen.choose(1,5)
txs <- Gen.listOfN(rand,TransactionGenerators.transaction)
rand <- Gen.choose(1, 5)
txs <- Gen.listOfN(rand, TransactionGenerators.transaction)
result <- merkleBlockWithInsertedTxIds(txs)
} yield result
/** Returns a [[MerkleBlock]] created with a [[org.bitcoins.core.bloom.BloomFilter]], with the block it was created from
* and the transactions that were matched inside of that block
* NOTE: Since bloom filters can produce false positives, it is possible that there will be
* matches in the parital merkle tree that SHOULD NOT be matched. Bloom filters do not guaratnee no
* false negatives.
* @return
*/
def merkleBlockCreatedWithBloomFilter: Gen[(MerkleBlock, Block,Seq[DoubleSha256Digest], BloomFilter)] = for {
/**
* Returns a [[MerkleBlock]] created with a [[org.bitcoins.core.bloom.BloomFilter]], with the block it was created from
* and the transactions that were matched inside of that block
* NOTE: Since bloom filters can produce false positives, it is possible that there will be
* matches in the parital merkle tree that SHOULD NOT be matched. Bloom filters do not guaratnee no
* false negatives.
* @return
*/
def merkleBlockCreatedWithBloomFilter: Gen[(MerkleBlock, Block, Seq[DoubleSha256Digest], BloomFilter)] = for {
block <- BlockchainElementsGenerator.block
//choose some random txs in the block to put in the bloom filter
txIds <- Gen.someOf(block.transactions.map(_.txId))
initialFilter <- BloomFilterGenerator.bloomFilter(txIds.map(_.bytes))
(merkleBlock,loadedFilter) = MerkleBlock(block,initialFilter)
} yield (merkleBlock,block,txIds,loadedFilter)
(merkleBlock, loadedFilter) = MerkleBlock(block, initialFilter)
} yield (merkleBlock, block, txIds, loadedFilter)
/** Generates a partial merkle tree with a sequence of txids and a flag indicating if the txid was matched */
def partialMerkleTree: Gen[(PartialMerkleTree, Seq[(Boolean,DoubleSha256Digest)])] = for {
randomNum <- Gen.choose(1,25)
def partialMerkleTree: Gen[(PartialMerkleTree, Seq[(Boolean, DoubleSha256Digest)])] = for {
randomNum <- Gen.choose(1, 25)
txMatches <- txIdsWithMatchIndication(randomNum)
} yield (PartialMerkleTree(txMatches),txMatches)
} yield (PartialMerkleTree(txMatches), txMatches)
/** Generates a transaction ids with a boolean indicator if they match the bloom filter or not
* this is useful for testing partial merkle trees as this is how they are built.
* @return
*/
private def txIdWithMatchIndication: Gen[(Boolean,DoubleSha256Digest)] = for {
/**
* Generates a transaction ids with a boolean indicator if they match the bloom filter or not
* this is useful for testing partial merkle trees as this is how they are built.
* @return
*/
private def txIdWithMatchIndication: Gen[(Boolean, DoubleSha256Digest)] = for {
hash <- CryptoGenerators.doubleSha256Digest
bool <- Gen.choose(0,1)
bool <- Gen.choose(0, 1)
} yield (bool == 1, hash)
/** Generates a list of txids with a boolean indicator signifying if it matched the bloom filter or not */
def txIdsWithMatchIndication(num: Int): Gen[Seq[(Boolean,DoubleSha256Digest)]] = Gen.listOfN(num,txIdWithMatchIndication)
def txIdsWithMatchIndication(num: Int): Gen[Seq[(Boolean, DoubleSha256Digest)]] = Gen.listOfN(num, txIdWithMatchIndication)
}
object MerkleGenerator extends MerkleGenerator

View file

@ -8,85 +8,84 @@ import org.scalacheck.Gen
import org.scalacheck.Arbitrary.arbitrary
/**
* Created by chris on 6/16/16.
*/
* Created by chris on 6/16/16.
*/
trait NumberGenerator {
/** Creates a generator that generates positive long numbers */
def positiveLongs: Gen[Long] = Gen.choose(0, Long.MaxValue)
/** Creates a generator for positive longs without the number zero */
def positiveLongsNoZero : Gen[Long] = Gen.choose(1,Long.MaxValue)
def positiveLongsNoZero: Gen[Long] = Gen.choose(1, Long.MaxValue)
/** Creates a number generator that generates negative long numbers */
def negativeLongs: Gen[Long] = Gen.choose(Long.MinValue,-1)
def negativeLongs: Gen[Long] = Gen.choose(Long.MinValue, -1)
def uInt8: Gen[UInt8] = Gen.choose(0,255).map(n => UInt8(n.toShort))
def uInt8: Gen[UInt8] = Gen.choose(0, 255).map(n => UInt8(n.toShort))
def uInt8s: Gen[Seq[UInt8]] = Gen.listOf(uInt8)
/**
* Generates a number in the range 0 <= x <= 2 ^^32 - 1
* then wraps it in a UInt32 */
def uInt32s: Gen[UInt32] = Gen.choose(0L,(NumberUtil.pow2(32)-1).toLong).map(UInt32(_))
* Generates a number in the range 0 <= x <= 2 ^^32 - 1
* then wraps it in a UInt32
*/
def uInt32s: Gen[UInt32] = Gen.choose(0L, (NumberUtil.pow2(32) - 1).toLong).map(UInt32(_))
/** Chooses a BigInt in the ranges of 0 <= bigInt < 2^^64 */
def bigInts : Gen[BigInt] = Gen.chooseNum(Long.MinValue,Long.MaxValue)
def bigInts: Gen[BigInt] = Gen.chooseNum(Long.MinValue, Long.MaxValue)
.map(x => BigInt(x) + BigInt(2).pow(63))
def positiveBigInts : Gen[BigInt] = bigInts.filter(_ >= 0)
def positiveBigInts: Gen[BigInt] = bigInts.filter(_ >= 0)
def bigIntsUInt64Range : Gen[BigInt] = positiveBigInts.filter(_ < (BigInt(1) << 64))
def bigIntsUInt64Range: Gen[BigInt] = positiveBigInts.filter(_ < (BigInt(1) << 64))
/**
* Generates a number in the range 0 <= x < 2^^64
* then wraps it in a UInt64
*/
def uInt64s : Gen[UInt64] = for {
* Generates a number in the range 0 <= x < 2^^64
* then wraps it in a UInt64
*/
def uInt64s: Gen[UInt64] = for {
bigInt <- bigIntsUInt64Range
} yield UInt64(bigInt)
def int32s: Gen[Int32] = Gen.choose(Int32.min.toLong, Int32.max.toLong).map(Int32(_))
def int32s : Gen[Int32] = Gen.choose(Int32.min.toLong,Int32.max.toLong).map(Int32(_))
def int64s : Gen[Int64] = Gen.choose(Int64.min.toLong, Int64.max.toLong).map(Int64(_))
def int64s: Gen[Int64] = Gen.choose(Int64.min.toLong, Int64.max.toLong).map(Int64(_))
def scriptNumbers: Gen[ScriptNumber] = Gen.choose(Int64.min.toLong, Int64.max.toLong).map(ScriptNumber(_))
def positiveScriptNumbers: Gen[ScriptNumber] = Gen.choose(0L,Int64.max.toLong).map(ScriptNumber(_))
def positiveScriptNumbers: Gen[ScriptNumber] = Gen.choose(0L, Int64.max.toLong).map(ScriptNumber(_))
def compactSizeUInts : Gen[CompactSizeUInt] = uInt64s.map(CompactSizeUInt(_))
def compactSizeUInts: Gen[CompactSizeUInt] = uInt64s.map(CompactSizeUInt(_))
/** Generates an arbitrary [[Byte]] in Scala */
def byte: Gen[Byte] = arbitrary[Byte]
/** Generates a 100 byte sequence */
def bytes: Gen[Seq[Byte]] = for {
num <- Gen.choose(0,100)
num <- Gen.choose(0, 100)
b <- bytes(num)
} yield b
/**
* Generates the number of bytes specified by num
* @param num
* @return
*/
def bytes(num : Int): Gen[Seq[Byte]] = Gen.listOfN(num,byte)
* Generates the number of bytes specified by num
* @param num
* @return
*/
def bytes(num: Int): Gen[Seq[Byte]] = Gen.listOfN(num, byte)
/** Generates a random boolean */
def bool: Gen[Boolean] = for {
num <- Gen.choose(0,1)
num <- Gen.choose(0, 1)
} yield num == 1
/** Generates a bit vector */
def bitVector: Gen[Seq[Boolean]] = for {
vector <- Gen.listOfN(8,bool)
vector <- Gen.listOfN(8, bool)
} yield vector
/** Generates a sequence of bit vectors */
def bitVectors: Gen[Seq[Seq[Boolean]]] = for {
num <- Gen.choose(0,100)
vectors <- Gen.listOfN(num,bitVector)
num <- Gen.choose(0, 100)
vectors <- Gen.listOfN(num, bitVector)
} yield vectors
}

View file

@ -1,37 +1,37 @@
package org.bitcoins.core.gen
import org.bitcoins.core.crypto.{TransactionSignatureCreator, _}
import org.bitcoins.core.currency.{CurrencyUnit, CurrencyUnits}
import org.bitcoins.core.crypto.{ TransactionSignatureCreator, _ }
import org.bitcoins.core.currency.{ CurrencyUnit, CurrencyUnits }
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.policy.Policy
import org.bitcoins.core.protocol.script.{P2SHScriptPubKey, _}
import org.bitcoins.core.protocol.script.{ P2SHScriptPubKey, _ }
import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.script.ScriptSettings
import org.bitcoins.core.script.constant.{ScriptNumber, _}
import org.bitcoins.core.script.constant.{ ScriptNumber, _ }
import org.bitcoins.core.script.crypto.HashType
import org.bitcoins.core.util.BitcoinSLogger
import org.bitcoins.core.wallet.signer.{MultiSigSigner, P2PKHSigner, P2PKSigner}
import org.bitcoins.core.wallet.signer.{ MultiSigSigner, P2PKHSigner, P2PKSigner }
import org.scalacheck.Gen
/**
* Created by chris on 6/22/16.
*/
* Created by chris on 6/22/16.
*/
//TODO: Need to provide generators for [[NonStandardScriptSignature]] and [[NonStandardScriptPubKey]]
sealed abstract class ScriptGenerators extends BitcoinSLogger {
private val tc = TransactionConstants
def p2pkScriptSignature : Gen[P2PKScriptSignature] = for {
def p2pkScriptSignature: Gen[P2PKScriptSignature] = for {
digitalSignature <- CryptoGenerators.digitalSignature
} yield P2PKScriptSignature(digitalSignature)
def p2pkhScriptSignature : Gen[P2PKHScriptSignature] = for {
def p2pkhScriptSignature: Gen[P2PKHScriptSignature] = for {
privKey <- CryptoGenerators.privateKey
hash <- CryptoGenerators.doubleSha256Digest
signature = privKey.sign(hash)
} yield P2PKHScriptSignature(signature,privKey.publicKey)
} yield P2PKHScriptSignature(signature, privKey.publicKey)
def multiSignatureScriptSignature : Gen[MultiSignatureScriptSignature] = {
val signatures : Gen[Seq[ECDigitalSignature]] = for {
def multiSignatureScriptSignature: Gen[MultiSignatureScriptSignature] = {
val signatures: Gen[Seq[ECDigitalSignature]] = for {
numKeys <- Gen.choose(1, ScriptSettings.maxPublicKeysPerMultiSig)
hash <- CryptoGenerators.doubleSha256Digest
} yield for {
@ -44,84 +44,82 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
def emptyScriptSignature = p2pkhScriptSignature.map(_ => EmptyScriptSignature)
/**
* Generates a [[org.bitcoins.core.protocol.script.P2SHScriptSignature]]
* WARNING: the redeem script and the script signature DO NOT evaluate to true
* if executed by [[org.bitcoins.core.script.interpreter.ScriptInterpreter]]
*/
def p2shScriptSignature : Gen[P2SHScriptSignature] = for {
* Generates a [[org.bitcoins.core.protocol.script.P2SHScriptSignature]]
* WARNING: the redeem script and the script signature DO NOT evaluate to true
* if executed by [[org.bitcoins.core.script.interpreter.ScriptInterpreter]]
*/
def p2shScriptSignature: Gen[P2SHScriptSignature] = for {
(scriptPubKey, _) <- randomNonP2SHScriptPubKey
scriptSig <- pickCorrespondingScriptSignature(scriptPubKey)
p2shScriptSig = P2SHScriptSignature(scriptSig, scriptPubKey)
} yield p2shScriptSig
def cltvScriptSignature : Gen[CLTVScriptSignature] = for {
def cltvScriptSignature: Gen[CLTVScriptSignature] = for {
scriptSig <- randomNonLockTimeScriptSig
} yield CLTVScriptSignature(scriptSig)
def csvScriptSignature : Gen[CSVScriptSignature] = for {
def csvScriptSignature: Gen[CSVScriptSignature] = for {
scriptSig <- randomNonLockTimeScriptSig
} yield CSVScriptSignature(scriptSig)
def p2pkScriptPubKey : Gen[(P2PKScriptPubKey, ECPrivateKey)] = for {
def p2pkScriptPubKey: Gen[(P2PKScriptPubKey, ECPrivateKey)] = for {
privKey <- CryptoGenerators.privateKey
pubKey = privKey.publicKey
p2pk = P2PKScriptPubKey(pubKey)
} yield (p2pk,privKey)
} yield (p2pk, privKey)
def p2pkhScriptPubKey : Gen[(P2PKHScriptPubKey, ECPrivateKey)] = for {
def p2pkhScriptPubKey: Gen[(P2PKHScriptPubKey, ECPrivateKey)] = for {
privKey <- CryptoGenerators.privateKey
pubKey = privKey.publicKey
p2pkh = P2PKHScriptPubKey(pubKey)
} yield (p2pkh,privKey)
} yield (p2pkh, privKey)
def cltvScriptPubKey : Gen[(CLTVScriptPubKey, Seq[ECPrivateKey])] = for {
def cltvScriptPubKey: Gen[(CLTVScriptPubKey, Seq[ECPrivateKey])] = for {
num <- NumberGenerator.scriptNumbers
(cltv, privKeys, num) <- cltvScriptPubKey(num)
} yield (cltv, privKeys)
def cltvScriptPubKey(num : ScriptNumber) : Gen[(CLTVScriptPubKey, Seq[ECPrivateKey], ScriptNumber)] = for {
def cltvScriptPubKey(num: ScriptNumber): Gen[(CLTVScriptPubKey, Seq[ECPrivateKey], ScriptNumber)] = for {
(scriptPubKey, privKeys) <- randomNonLockTimeNonP2SHScriptPubKey
} yield {
val cltv = CLTVScriptPubKey(num, scriptPubKey)
(cltv, privKeys, num)
}
def csvScriptPubKey(num : ScriptNumber) : Gen[(CSVScriptPubKey, Seq[ECPrivateKey], ScriptNumber)] = for {
def csvScriptPubKey(num: ScriptNumber): Gen[(CSVScriptPubKey, Seq[ECPrivateKey], ScriptNumber)] = for {
(scriptPubKey, privKeys) <- randomNonLockTimeNonP2SHScriptPubKey
} yield {
val csv = CSVScriptPubKey(num, scriptPubKey)
(csv, privKeys, num)
}
def csvScriptPubKey : Gen[(CSVScriptPubKey, Seq[ECPrivateKey])] = for {
def csvScriptPubKey: Gen[(CSVScriptPubKey, Seq[ECPrivateKey])] = for {
(scriptPubKey, privKeys) <- randomNonLockTimeNonP2SHScriptPubKey
num <- NumberGenerator.scriptNumbers
csv = CSVScriptPubKey(num, scriptPubKey)
} yield (csv, privKeys)
def multiSigScriptPubKey : Gen[(MultiSignatureScriptPubKey, Seq[ECPrivateKey])] = for {
def multiSigScriptPubKey: Gen[(MultiSignatureScriptPubKey, Seq[ECPrivateKey])] = for {
(privateKeys, requiredSigs) <- CryptoGenerators.privateKeySeqWithRequiredSigs
pubKeys = privateKeys.map(_.publicKey)
multiSignatureScriptPubKey = MultiSignatureScriptPubKey(requiredSigs, pubKeys)
} yield (multiSignatureScriptPubKey, privateKeys)
def smallMultiSigScriptPubKey : Gen[(MultiSignatureScriptPubKey, Seq[ECPrivateKey])] = for {
def smallMultiSigScriptPubKey: Gen[(MultiSignatureScriptPubKey, Seq[ECPrivateKey])] = for {
(privateKeys, requiredSigs) <- CryptoGenerators.smallPrivateKeySeqWithRequiredSigs
pubKeys = privateKeys.map(_.publicKey)
multiSignatureScriptPubKey = MultiSignatureScriptPubKey(requiredSigs, pubKeys)
} yield (multiSignatureScriptPubKey, privateKeys)
def p2shScriptPubKey : Gen[(P2SHScriptPubKey, Seq[ECPrivateKey])] = for {
def p2shScriptPubKey: Gen[(P2SHScriptPubKey, Seq[ECPrivateKey])] = for {
(randomScriptPubKey, privKeys) <- randomNonP2SHScriptPubKey
p2sh = P2SHScriptPubKey(randomScriptPubKey)
} yield (p2sh, privKeys)
def emptyScriptPubKey : Gen [(ScriptPubKey, Seq[ECPrivateKey])] = (EmptyScriptPubKey, Nil)
def emptyScriptPubKey: Gen[(ScriptPubKey, Seq[ECPrivateKey])] = (EmptyScriptPubKey, Nil)
/** Creates a basic version 0 P2WPKH scriptpubkey */
def p2wpkhSPKV0: Gen[(P2WPKHWitnessSPKV0,Seq[ECPrivateKey])] = for {
def p2wpkhSPKV0: Gen[(P2WPKHWitnessSPKV0, Seq[ECPrivateKey])] = for {
privKey <- CryptoGenerators.privateKey
} yield (P2WPKHWitnessSPKV0(privKey.publicKey), Seq(privKey))
@ -133,44 +131,47 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
p2wpkhSPKV0, p2wshSPKV0
)
/** Creates an [[UnassignedWitnessScriptPubKey]],
* currently this is any witness script pubkey besides [[org.bitcoins.core.protocol.script.WitnessScriptPubKeyV0]
*/
/**
* Creates an [[UnassignedWitnessScriptPubKey]],
* currently this is any witness script pubkey besides [[org.bitcoins.core.protocol.script.WitnessScriptPubKeyV0]
*/
def unassignedWitnessScriptPubKey: Gen[(UnassignedWitnessScriptPubKey, Seq[ECPrivateKey])] = for {
(witV0,privKeys) <- p2wpkhSPKV0
(witV0, privKeys) <- p2wpkhSPKV0
version <- Gen.oneOf(WitnessScriptPubKey.unassignedWitVersions)
unassignedAsm = version +: witV0.asm.tail
} yield (UnassignedWitnessScriptPubKey(unassignedAsm),privKeys)
} yield (UnassignedWitnessScriptPubKey(unassignedAsm), privKeys)
/** Generates an arbitrary [[org.bitcoins.core.protocol.script.WitnessScriptPubKey]] */
def witnessScriptPubKey: Gen[(WitnessScriptPubKey, Seq[ECPrivateKey])] = Gen.oneOf(p2wpkhSPKV0,
p2wshSPKV0, unassignedWitnessScriptPubKey)
def witnessScriptPubKey: Gen[(WitnessScriptPubKey, Seq[ECPrivateKey])] = Gen.oneOf(
p2wpkhSPKV0,
p2wshSPKV0, unassignedWitnessScriptPubKey
)
def witnessCommitment: Gen[(WitnessCommitment, Seq[ECPrivateKey])] = for {
hash <- CryptoGenerators.doubleSha256Digest
} yield (WitnessCommitment(hash),Nil)
} yield (WitnessCommitment(hash), Nil)
def escrowTimeoutScriptPubKey: Gen[(EscrowTimeoutScriptPubKey, Seq[ECPrivateKey])] = for {
(escrow,k1) <- ScriptGenerators.smallMultiSigScriptPubKey
(escrow, k1) <- ScriptGenerators.smallMultiSigScriptPubKey
//We use a p2pkh scriptPubkey here to minimize the script size of EscrowTimeoutScriptPubKey
//otherwise we surpass the 520 byte push op limit
(p2pkh,_) <- ScriptGenerators.p2pkhScriptPubKey
(p2pkh, _) <- ScriptGenerators.p2pkhScriptPubKey
scriptNum <- NumberGenerator.scriptNumbers
timeout = CSVScriptPubKey(scriptNum,p2pkh)
} yield (EscrowTimeoutScriptPubKey(escrow,timeout), k1)
timeout = CSVScriptPubKey(scriptNum, p2pkh)
} yield (EscrowTimeoutScriptPubKey(escrow, timeout), k1)
def escrowTimeoutScriptPubKey2Of2: Gen[(EscrowTimeoutScriptPubKey, Seq[ECPrivateKey])] = for {
privKey1 <- CryptoGenerators.privateKey
privKey2 <- CryptoGenerators.privateKey
escrowPrivKeys = Seq(privKey1, privKey2)
escrow = MultiSignatureScriptPubKey(2,escrowPrivKeys.map(_.publicKey))
escrow = MultiSignatureScriptPubKey(2, escrowPrivKeys.map(_.publicKey))
//We use a p2pkh scriptPubkey here to minimize the script size of EscrowTimeoutScriptPubKey
//otherwise we surpass the 520 byte push op limit
(p2pkh,p2pkhPrivKey) <- ScriptGenerators.p2pkhScriptPubKey
(p2pkh, p2pkhPrivKey) <- ScriptGenerators.p2pkhScriptPubKey
privKeys = escrowPrivKeys ++ Seq(p2pkhPrivKey)
(scriptNum,_) <- TransactionGenerators.spendableCSVValues
timeout = CSVScriptPubKey(scriptNum,p2pkh)
} yield (EscrowTimeoutScriptPubKey(escrow,timeout), privKeys)
(scriptNum, _) <- TransactionGenerators.spendableCSVValues
timeout = CSVScriptPubKey(scriptNum, p2pkh)
} yield (EscrowTimeoutScriptPubKey(escrow, timeout), privKeys)
def escrowTimeoutScriptSig: Gen[EscrowTimeoutScriptSignature] = for {
scriptSig <- Gen.oneOf(lockTimeScriptSig, multiSignatureScriptSignature)
@ -182,21 +183,24 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
Gen.oneOf(p2pkScriptPubKey.map(privKeyToSeq(_)), p2pkhScriptPubKey.map(privKeyToSeq(_)),
cltvScriptPubKey.suchThat(!_._1.nestedScriptPubKey.isInstanceOf[CSVScriptPubKey]),
csvScriptPubKey.suchThat(!_._1.nestedScriptPubKey.isInstanceOf[CLTVScriptPubKey]),
multiSigScriptPubKey, p2wpkhSPKV0, unassignedWitnessScriptPubKey, escrowTimeoutScriptPubKey
multiSigScriptPubKey, p2wpkhSPKV0, unassignedWitnessScriptPubKey, escrowTimeoutScriptPubKey)
}
/**
* This is used for creating time locked scriptPubKeys, we cannot nest CSV/CLTV/P2SH/Witness
* ScriptPubKeys inside of timelock scriptPubKeys
*/
def randomNonLockTimeNonP2SHScriptPubKey: Gen[(ScriptPubKey, Seq[ECPrivateKey])] = {
Gen.oneOf(
p2pkScriptPubKey.map(privKeyToSeq(_)),
p2pkhScriptPubKey.map(privKeyToSeq(_)),
multiSigScriptPubKey
)
}
/** This is used for creating time locked scriptPubKeys, we cannot nest CSV/CLTV/P2SH/Witness
* ScriptPubKeys inside of timelock scriptPubKeys */
def randomNonLockTimeNonP2SHScriptPubKey : Gen[(ScriptPubKey, Seq[ECPrivateKey])] = {
Gen.oneOf(p2pkScriptPubKey.map(privKeyToSeq(_)),
p2pkhScriptPubKey.map(privKeyToSeq(_)),
multiSigScriptPubKey)
}
def randomNonLockTimeScriptSig: Gen[ScriptSignature] = {
Gen.oneOf(p2pkScriptSignature,p2pkhScriptSignature,multiSignatureScriptSignature,
emptyScriptSignature,p2shScriptSignature)
Gen.oneOf(p2pkScriptSignature, p2pkhScriptSignature, multiSignatureScriptSignature,
emptyScriptSignature, p2shScriptSignature)
}
def lockTimeScriptPubKey: Gen[(LockTimeScriptPubKey, Seq[ECPrivateKey])] = Gen.oneOf(cltvScriptPubKey, csvScriptPubKey)
@ -204,28 +208,28 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
def lockTimeScriptSig: Gen[LockTimeScriptSignature] = Gen.oneOf(csvScriptSignature, cltvScriptSignature)
/** Generates an arbitrary [[ScriptPubKey]] */
def scriptPubKey : Gen[(ScriptPubKey, Seq[ECPrivateKey])] = {
Gen.oneOf(p2pkScriptPubKey.map(privKeyToSeq(_)),p2pkhScriptPubKey.map(privKeyToSeq(_)),
multiSigScriptPubKey,emptyScriptPubKey,
cltvScriptPubKey,csvScriptPubKey,p2wpkhSPKV0, p2wshSPKV0, unassignedWitnessScriptPubKey,
def scriptPubKey: Gen[(ScriptPubKey, Seq[ECPrivateKey])] = {
Gen.oneOf(p2pkScriptPubKey.map(privKeyToSeq(_)), p2pkhScriptPubKey.map(privKeyToSeq(_)),
multiSigScriptPubKey, emptyScriptPubKey,
cltvScriptPubKey, csvScriptPubKey, p2wpkhSPKV0, p2wshSPKV0, unassignedWitnessScriptPubKey,
p2shScriptPubKey, witnessCommitment, escrowTimeoutScriptPubKey)
}
/** Generates an arbitrary [[ScriptSignature]] */
def scriptSignature : Gen[ScriptSignature] = {
Gen.oneOf(p2pkScriptSignature,p2pkhScriptSignature,multiSignatureScriptSignature,
emptyScriptSignature,p2shScriptSignature, escrowTimeoutScriptSig
//NOTE: This are commented out because it fail serializatoin symmetry
//sicne we cannot properly type CSV/CLTV ScriptSigs w/o it's SPK
//csvScriptSignature, cltvScriptSignature
def scriptSignature: Gen[ScriptSignature] = {
Gen.oneOf(p2pkScriptSignature, p2pkhScriptSignature, multiSignatureScriptSignature,
emptyScriptSignature, p2shScriptSignature, escrowTimeoutScriptSig
//NOTE: This are commented out because it fail serializatoin symmetry
//sicne we cannot properly type CSV/CLTV ScriptSigs w/o it's SPK
//csvScriptSignature, cltvScriptSignature
)
}
/**
* Generates a [[ScriptSignature]] corresponding to the type of [[ScriptPubKey]] given.
* Note: Does NOT generate a correct/valid signature
*/
private def pickCorrespondingScriptSignature(scriptPubKey : ScriptPubKey): Gen[ScriptSignature] = scriptPubKey match {
* Generates a [[ScriptSignature]] corresponding to the type of [[ScriptPubKey]] given.
* Note: Does NOT generate a correct/valid signature
*/
private def pickCorrespondingScriptSignature(scriptPubKey: ScriptPubKey): Gen[ScriptSignature] = scriptPubKey match {
case _: P2PKScriptPubKey => p2pkScriptSignature
case _: P2PKHScriptPubKey => p2pkhScriptSignature
case _: MultiSignatureScriptPubKey => multiSignatureScriptSignature
@ -233,19 +237,19 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
case _: CLTVScriptPubKey => cltvScriptSignature
case _: CSVScriptPubKey => csvScriptSignature
case _: EscrowTimeoutScriptPubKey => escrowTimeoutScriptSig
case _: WitnessScriptPubKeyV0 | _ : UnassignedWitnessScriptPubKey => emptyScriptSignature
case x @ (_: P2SHScriptPubKey | _: NonStandardScriptPubKey | _ : WitnessCommitment) =>
case _: WitnessScriptPubKeyV0 | _: UnassignedWitnessScriptPubKey => emptyScriptSignature
case x @ (_: P2SHScriptPubKey | _: NonStandardScriptPubKey | _: WitnessCommitment) =>
throw new IllegalArgumentException("Cannot pick for p2sh script pubkey, " +
"non standard script pubkey or witness commitment got: " + x)
}
/**
* Generates a signed [[P2PKScriptSignature]] that spends the [[P2PKScriptPubKey]] correctly
*
* @return the signed [[P2PKScriptSignature]], the [[P2PKScriptPubKey]] it spends, and the
* [[ECPrivateKey]] used to sign the scriptSig
*/
def signedP2PKScriptSignature: Gen[(P2PKScriptSignature,P2PKScriptPubKey,ECPrivateKey)] = for {
* Generates a signed [[P2PKScriptSignature]] that spends the [[P2PKScriptPubKey]] correctly
*
* @return the signed [[P2PKScriptSignature]], the [[P2PKScriptPubKey]] it spends, and the
* [[ECPrivateKey]] used to sign the scriptSig
*/
def signedP2PKScriptSignature: Gen[(P2PKScriptSignature, P2PKScriptPubKey, ECPrivateKey)] = for {
privateKey <- CryptoGenerators.privateKey
hashType <- CryptoGenerators.hashType
publicKey = privateKey.publicKey
@ -253,123 +257,119 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
(creditingTx, outputIndex) = TransactionGenerators.buildCreditingTransaction(scriptPubKey)
scriptSig = P2PKScriptSignature(EmptyDigitalSignature)
(spendingTx, inputIndex) = TransactionGenerators.buildSpendingTransaction(creditingTx, scriptSig, outputIndex)
signer = (privateKey.sign(_: Seq[Byte]),None)
signer = (privateKey.sign(_: Seq[Byte]), None)
txSigComponent = P2PKSigner.sign(Seq(signer), creditingTx.outputs(outputIndex.toInt), spendingTx, inputIndex, hashType).left.get
//add the signature to the scriptSig instead of having an empty scriptSig
signedScriptSig = txSigComponent.scriptSignature.asInstanceOf[P2PKScriptSignature]
} yield (signedScriptSig,scriptPubKey,privateKey)
} yield (signedScriptSig, scriptPubKey, privateKey)
/**
* Generates a signed [[P2PKHScriptSignature]] that spends the [[P2PKHScriptPubKey]] correctly
*
* @return the signed [[P2PKHScriptSignature]], the [[P2PKHScriptPubKey]] it spends, and the
* [[ECPrivateKey]] used to sign the scriptSig
*/
* Generates a signed [[P2PKHScriptSignature]] that spends the [[P2PKHScriptPubKey]] correctly
*
* @return the signed [[P2PKHScriptSignature]], the [[P2PKHScriptPubKey]] it spends, and the
* [[ECPrivateKey]] used to sign the scriptSig
*/
def signedP2PKHScriptSignature: Gen[(P2PKHScriptSignature, P2PKHScriptPubKey, ECPrivateKey)] = for {
privateKey <- CryptoGenerators.privateKey
hashType <- CryptoGenerators.hashType
publicKey = privateKey.publicKey
scriptPubKey = P2PKHScriptPubKey(publicKey)
(creditingTx,outputIndex) = TransactionGenerators.buildCreditingTransaction(scriptPubKey)
(unsignedTx,inputIndex) = TransactionGenerators.buildSpendingTransaction(creditingTx,EmptyScriptSignature,outputIndex)
signer = (privateKey.sign(_: Seq[Byte]),Some(privateKey.publicKey))
(creditingTx, outputIndex) = TransactionGenerators.buildCreditingTransaction(scriptPubKey)
(unsignedTx, inputIndex) = TransactionGenerators.buildSpendingTransaction(creditingTx, EmptyScriptSignature, outputIndex)
signer = (privateKey.sign(_: Seq[Byte]), Some(privateKey.publicKey))
txSigComponent = P2PKHSigner.sign(Seq(signer), creditingTx.outputs(outputIndex.toInt), unsignedTx, inputIndex, hashType).left.get
signedScriptSig = txSigComponent.scriptSignature.asInstanceOf[P2PKHScriptSignature]
} yield (signedScriptSig, scriptPubKey, privateKey)
/**
* Generates a signed [[MultiSignatureScriptSignature]] that spends the [[MultiSignatureScriptPubKey]] correctly
*ti
* @return the signed [[MultiSignatureScriptSignature]], the [[MultiSignatureScriptPubKey]] it spends and the
* sequence of [[ECPrivateKey]] used to sign the scriptSig
*/
* Generates a signed [[MultiSignatureScriptSignature]] that spends the [[MultiSignatureScriptPubKey]] correctly
* ti
* @return the signed [[MultiSignatureScriptSignature]], the [[MultiSignatureScriptPubKey]] it spends and the
* sequence of [[ECPrivateKey]] used to sign the scriptSig
*/
def signedMultiSignatureScriptSignature: Gen[(MultiSignatureScriptSignature, MultiSignatureScriptPubKey, Seq[ECPrivateKey])] = for {
(privateKeys, requiredSigs) <- CryptoGenerators.privateKeySeqWithRequiredSigs
hashType <- CryptoGenerators.hashType
publicKeys = privateKeys.map(_.publicKey)
multiSigScriptPubKey = MultiSignatureScriptPubKey(requiredSigs,publicKeys)
multiSigScriptPubKey = MultiSignatureScriptPubKey(requiredSigs, publicKeys)
emptyDigitalSignatures = privateKeys.map(_ => EmptyDigitalSignature)
scriptSig = MultiSignatureScriptSignature(emptyDigitalSignatures)
(creditingTx,outputIndex) = TransactionGenerators.buildCreditingTransaction(multiSigScriptPubKey)
(spendingTx,inputIndex) = TransactionGenerators.buildSpendingTransaction(creditingTx,scriptSig,outputIndex)
signers = privateKeys.map(p => (p.sign(_: Seq[Byte]),None))
txSigComponent = MultiSigSigner.sign(signers, creditingTx.outputs(outputIndex.toInt), spendingTx,inputIndex,hashType).left.get
(creditingTx, outputIndex) = TransactionGenerators.buildCreditingTransaction(multiSigScriptPubKey)
(spendingTx, inputIndex) = TransactionGenerators.buildSpendingTransaction(creditingTx, scriptSig, outputIndex)
signers = privateKeys.map(p => (p.sign(_: Seq[Byte]), None))
txSigComponent = MultiSigSigner.sign(signers, creditingTx.outputs(outputIndex.toInt), spendingTx, inputIndex, hashType).left.get
signedScriptSig = txSigComponent.scriptSignature.asInstanceOf[MultiSignatureScriptSignature]
} yield (signedScriptSig,multiSigScriptPubKey,privateKeys)
} yield (signedScriptSig, multiSigScriptPubKey, privateKeys)
/**
* Generates a signed [[P2SHScriptSignature]] that spends from a [[P2SHScriptPubKey]] correctly
*
* @return the signed [[P2SHScriptSignature]], the [[P2SHScriptPubKey]] it spends, and the sequence of [[ECPrivateKey]]
* used to sign the scriptSig
*/
* Generates a signed [[P2SHScriptSignature]] that spends from a [[P2SHScriptPubKey]] correctly
*
* @return the signed [[P2SHScriptSignature]], the [[P2SHScriptPubKey]] it spends, and the sequence of [[ECPrivateKey]]
* used to sign the scriptSig
*/
def signedP2SHScriptSignature: Gen[(P2SHScriptSignature, P2SHScriptPubKey, Seq[ECPrivateKey])] = for {
(scriptSig, redeemScript, privateKeys) <- chooseSignedScriptSig
p2SHScriptPubKey = P2SHScriptPubKey(redeemScript)
p2SHScriptSignature = P2SHScriptSignature(scriptSig,redeemScript)
p2SHScriptSignature = P2SHScriptSignature(scriptSig, redeemScript)
} yield (p2SHScriptSignature, p2SHScriptPubKey, privateKeys)
/**
* Generates a signed [[CLTVScriptSignature]] that spends from a [[CLTVScriptPubKey]] correctly
*
* @return the signed [[CLTVScriptSignature]], the [[CLTVScriptPubKey]] it spends, and the sequences of [[ECPrivateKey]]
* used to sign the scriptSig
*/
def signedCLTVScriptSignature(cltvLockTime : ScriptNumber, lockTime : UInt32, sequence : UInt32) : Gen[(CLTVScriptSignature,
CLTVScriptPubKey, Seq[ECPrivateKey])] = for {
* Generates a signed [[CLTVScriptSignature]] that spends from a [[CLTVScriptPubKey]] correctly
*
* @return the signed [[CLTVScriptSignature]], the [[CLTVScriptPubKey]] it spends, and the sequences of [[ECPrivateKey]]
* used to sign the scriptSig
*/
def signedCLTVScriptSignature(cltvLockTime: ScriptNumber, lockTime: UInt32, sequence: UInt32): Gen[(CLTVScriptSignature, CLTVScriptPubKey, Seq[ECPrivateKey])] = for {
(scriptPubKey, privKeys) <- randomNonLockTimeNonP2SHScriptPubKey
hashType <- CryptoGenerators.hashType
cltv = CLTVScriptPubKey(cltvLockTime, scriptPubKey)
} yield scriptPubKey match {
case m : MultiSignatureScriptPubKey =>
val requiredSigs = m.requiredSigs
val cltvScriptSig = lockTimeHelper(Some(lockTime), sequence, cltv, privKeys, Some(requiredSigs), hashType)
(cltvScriptSig.asInstanceOf[CLTVScriptSignature], cltv, privKeys)
case _ : P2PKHScriptPubKey | _ : P2PKScriptPubKey =>
val cltvScriptSig = lockTimeHelper(Some(lockTime), sequence, cltv, privKeys, None, hashType)
(cltvScriptSig.asInstanceOf[CLTVScriptSignature], cltv, privKeys)
case _: UnassignedWitnessScriptPubKey | _: WitnessScriptPubKeyV0 =>
throw new IllegalArgumentException("Cannot created a witness scriptPubKey for a CSVScriptSig since we do not have a witness")
case _ : P2SHScriptPubKey | _ : CLTVScriptPubKey | _ : CSVScriptPubKey | _ : NonStandardScriptPubKey
| _ : WitnessCommitment | _: EscrowTimeoutScriptPubKey | EmptyScriptPubKey => throw new IllegalArgumentException("We only " +
"want to generate P2PK, P2PKH, and MultiSig ScriptSignatures when creating a CSVScriptSignature")
case m: MultiSignatureScriptPubKey =>
val requiredSigs = m.requiredSigs
val cltvScriptSig = lockTimeHelper(Some(lockTime), sequence, cltv, privKeys, Some(requiredSigs), hashType)
(cltvScriptSig.asInstanceOf[CLTVScriptSignature], cltv, privKeys)
case _: P2PKHScriptPubKey | _: P2PKScriptPubKey =>
val cltvScriptSig = lockTimeHelper(Some(lockTime), sequence, cltv, privKeys, None, hashType)
(cltvScriptSig.asInstanceOf[CLTVScriptSignature], cltv, privKeys)
case _: UnassignedWitnessScriptPubKey | _: WitnessScriptPubKeyV0 =>
throw new IllegalArgumentException("Cannot created a witness scriptPubKey for a CSVScriptSig since we do not have a witness")
case _: P2SHScriptPubKey | _: CLTVScriptPubKey | _: CSVScriptPubKey | _: NonStandardScriptPubKey
| _: WitnessCommitment | _: EscrowTimeoutScriptPubKey | EmptyScriptPubKey => throw new IllegalArgumentException("We only " +
"want to generate P2PK, P2PKH, and MultiSig ScriptSignatures when creating a CSVScriptSignature")
}
/**
* Generates a signed [[CSVScriptSignature]] that spends from a [[CSVScriptPubKey]] correctly
*
* @return the signed [[CSVScriptSignature]], the [[CSVScriptPubKey]] it spends, and the sequences of [[ECPrivateKey]]
* used to sign the scriptSig
*/
def signedCSVScriptSignature(csvScriptNum : ScriptNumber, sequence : UInt32) : Gen[(CSVScriptSignature,
CSVScriptPubKey, Seq[ECPrivateKey])] = for {
* Generates a signed [[CSVScriptSignature]] that spends from a [[CSVScriptPubKey]] correctly
*
* @return the signed [[CSVScriptSignature]], the [[CSVScriptPubKey]] it spends, and the sequences of [[ECPrivateKey]]
* used to sign the scriptSig
*/
def signedCSVScriptSignature(csvScriptNum: ScriptNumber, sequence: UInt32): Gen[(CSVScriptSignature, CSVScriptPubKey, Seq[ECPrivateKey])] = for {
(scriptPubKey, privKeys) <- randomNonLockTimeNonP2SHScriptPubKey
hashType <- CryptoGenerators.hashType
csv = CSVScriptPubKey(csvScriptNum, scriptPubKey)
} yield scriptPubKey match {
case m : MultiSignatureScriptPubKey =>
val requiredSigs = m.requiredSigs
val csvScriptSig = lockTimeHelper(None,sequence, csv, privKeys, Some(requiredSigs), hashType)
(csvScriptSig.asInstanceOf[CSVScriptSignature], csv, privKeys)
case _ : P2PKHScriptPubKey | _ : P2PKScriptPubKey =>
val csvScriptSig = lockTimeHelper(None, sequence, csv, privKeys, None, hashType)
(csvScriptSig.asInstanceOf[CSVScriptSignature], csv, privKeys)
case _: UnassignedWitnessScriptPubKey | _: WitnessScriptPubKeyV0 =>
throw new IllegalArgumentException("Cannot created a witness scriptPubKey for a CSVScriptSig since we do not have a witness")
case _: P2SHScriptPubKey | _: CLTVScriptPubKey | _: CSVScriptPubKey | _: NonStandardScriptPubKey
| _: WitnessCommitment | _: EscrowTimeoutScriptPubKey | EmptyScriptPubKey => throw new IllegalArgumentException("We only " +
"want to generate P2PK, P2PKH, and MultiSig ScriptSignatures when creating a CLTVScriptSignature.")
case m: MultiSignatureScriptPubKey =>
val requiredSigs = m.requiredSigs
val csvScriptSig = lockTimeHelper(None, sequence, csv, privKeys, Some(requiredSigs), hashType)
(csvScriptSig.asInstanceOf[CSVScriptSignature], csv, privKeys)
case _: P2PKHScriptPubKey | _: P2PKScriptPubKey =>
val csvScriptSig = lockTimeHelper(None, sequence, csv, privKeys, None, hashType)
(csvScriptSig.asInstanceOf[CSVScriptSignature], csv, privKeys)
case _: UnassignedWitnessScriptPubKey | _: WitnessScriptPubKeyV0 =>
throw new IllegalArgumentException("Cannot created a witness scriptPubKey for a CSVScriptSig since we do not have a witness")
case _: P2SHScriptPubKey | _: CLTVScriptPubKey | _: CSVScriptPubKey | _: NonStandardScriptPubKey
| _: WitnessCommitment | _: EscrowTimeoutScriptPubKey | EmptyScriptPubKey => throw new IllegalArgumentException("We only " +
"want to generate P2PK, P2PKH, and MultiSig ScriptSignatures when creating a CLTVScriptSignature.")
}
def signedCSVScriptSignature : Gen[(CSVScriptSignature, CSVScriptPubKey, Seq[ECPrivateKey])] = for {
def signedCSVScriptSignature: Gen[(CSVScriptSignature, CSVScriptPubKey, Seq[ECPrivateKey])] = for {
(csv, privKeys) <- csvScriptPubKey
sequence <- NumberGenerator.uInt32s
scriptSig <- signedCSVScriptSignature(csv.locktime, sequence)
} yield scriptSig
def signedCLTVScriptSignature : Gen[(CLTVScriptSignature, CLTVScriptPubKey, Seq[ECPrivateKey])] = for {
def signedCLTVScriptSignature: Gen[(CLTVScriptSignature, CLTVScriptPubKey, Seq[ECPrivateKey])] = for {
(cltv, privKeys) <- cltvScriptPubKey
txLockTime <- NumberGenerator.uInt32s
sequence <- NumberGenerator.uInt32s
@ -383,67 +383,70 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
/** Generates a [[EscrowTimeoutScriptPubKey]] and [[EscrowTimeoutScriptSignature]] where the scriptsig spends the escrow branch */
def signedMultiSigEscrowTimeoutScriptSig(sequence: UInt32, outputs: Seq[TransactionOutput], amount: CurrencyUnit): Gen[(EscrowTimeoutScriptSignature, EscrowTimeoutScriptPubKey, Seq[ECPrivateKey])] = for {
(_,csvScriptPubkey,_) <- signedCSVScriptSignature
(_, multiSigScriptPubKey,multiSigPrivKeys) <- signedMultiSignatureScriptSignature
csvEscrowTimeout = EscrowTimeoutScriptPubKey(multiSigScriptPubKey,csvScriptPubkey)
r <- signedMultiSigEscrowTimeoutScriptSig(csvEscrowTimeout,multiSigPrivKeys,sequence,outputs,amount)
(_, csvScriptPubkey, _) <- signedCSVScriptSignature
(_, multiSigScriptPubKey, multiSigPrivKeys) <- signedMultiSignatureScriptSignature
csvEscrowTimeout = EscrowTimeoutScriptPubKey(multiSigScriptPubKey, csvScriptPubkey)
r <- signedMultiSigEscrowTimeoutScriptSig(csvEscrowTimeout, multiSigPrivKeys, sequence, outputs, amount)
} yield r
def signedMultiSigEscrowTimeoutScriptSig(escrowTimeout: EscrowTimeoutScriptPubKey, privKeys: Seq[ECPrivateKey],
sequence: UInt32, outputs: Seq[TransactionOutput], amount: CurrencyUnit): Gen[(EscrowTimeoutScriptSignature, EscrowTimeoutScriptPubKey, Seq[ECPrivateKey])] = for {
hashType <- CryptoGenerators.hashType
scriptSig = csvEscrowTimeoutHelper(sequence,escrowTimeout,privKeys,Some(escrowTimeout.escrow.requiredSigs),
hashType,true,outputs,amount)
} yield (scriptSig,escrowTimeout,privKeys)
scriptSig = csvEscrowTimeoutHelper(sequence, escrowTimeout, privKeys, Some(escrowTimeout.escrow.requiredSigs),
hashType, true, outputs, amount)
} yield (scriptSig, escrowTimeout, privKeys)
/** Generates a [[EscrowTimeoutScriptPubKey]] and [[EscrowTimeoutScriptSignature]] where the scriptsig spends the timeout branch */
def timeoutEscrowTimeoutScriptSig(scriptNum: ScriptNumber, sequence: UInt32, outputs: Seq[TransactionOutput]): Gen[(EscrowTimeoutScriptSignature, EscrowTimeoutScriptPubKey, Seq[ECPrivateKey])] = for {
(_,csv,csvPrivKeys) <- signedCSVScriptSignature(scriptNum, sequence)
(multiSigScriptPubKey,_) <- multiSigScriptPubKey
(_, csv, csvPrivKeys) <- signedCSVScriptSignature(scriptNum, sequence)
(multiSigScriptPubKey, _) <- multiSigScriptPubKey
hashType <- CryptoGenerators.hashType
csvEscrowTimeout = EscrowTimeoutScriptPubKey(multiSigScriptPubKey,csv)
csvEscrowTimeout = EscrowTimeoutScriptPubKey(multiSigScriptPubKey, csv)
requireSigs = if (csv.nestedScriptPubKey.isInstanceOf[MultiSignatureScriptPubKey]) {
val m = csv.nestedScriptPubKey.asInstanceOf[MultiSignatureScriptPubKey]
Some(m.requiredSigs)
} else None
scriptSig = csvEscrowTimeoutHelper(sequence,csvEscrowTimeout,csvPrivKeys,requireSigs,hashType,false,outputs)
} yield (scriptSig,csvEscrowTimeout,csvPrivKeys)
scriptSig = csvEscrowTimeoutHelper(sequence, csvEscrowTimeout, csvPrivKeys, requireSigs, hashType, false, outputs)
} yield (scriptSig, csvEscrowTimeout, csvPrivKeys)
/** Helper function to generate [[LockTimeScriptSignature]]s */
private def lockTimeHelper(lockTime: Option[UInt32], sequence: UInt32, lock: LockTimeScriptPubKey, privateKeys: Seq[ECPrivateKey], requiredSigs: Option[Int],
hashType: HashType): LockTimeScriptSignature = {
hashType: HashType): LockTimeScriptSignature = {
val pubKeys = privateKeys.map(_.publicKey)
val (creditingTx, outputIndex) = TransactionGenerators.buildCreditingTransaction(UInt32(2),lock)
val (creditingTx, outputIndex) = TransactionGenerators.buildCreditingTransaction(UInt32(2), lock)
val (unsignedSpendingTx, inputIndex) = TransactionGenerators.buildSpendingTransaction(UInt32(2), creditingTx,
EmptyScriptSignature, outputIndex,lockTime.getOrElse(TransactionConstants.lockTime), sequence)
EmptyScriptSignature, outputIndex, lockTime.getOrElse(TransactionConstants.lockTime), sequence)
val txSignatureComponent = BaseTxSigComponent(unsignedSpendingTx, inputIndex,
lock, Policy.standardScriptVerifyFlags)
val txSignatures : Seq[ECDigitalSignature] = for {
val txSignatures: Seq[ECDigitalSignature] = for {
i <- 0 until requiredSigs.getOrElse(1)
} yield TransactionSignatureCreator.createSig(txSignatureComponent,privateKeys(i), hashType)
} yield TransactionSignatureCreator.createSig(txSignatureComponent, privateKeys(i), hashType)
lock match {
case csv: CSVScriptPubKey =>
val nestedScriptSig = lockTimeHelperScriptSig(csv,txSignatures,pubKeys)
val nestedScriptSig = lockTimeHelperScriptSig(csv, txSignatures, pubKeys)
CSVScriptSignature(nestedScriptSig)
case cltv: CLTVScriptPubKey =>
val nestedScriptSig = lockTimeHelperScriptSig(cltv,txSignatures,pubKeys)
val nestedScriptSig = lockTimeHelperScriptSig(cltv, txSignatures, pubKeys)
CLTVScriptSignature(nestedScriptSig)
}
}
/** Helper function to generate a signed [[EscrowTimeoutScriptSignature]] */
private def csvEscrowTimeoutHelper(sequence: UInt32, csvEscrowTimeout: EscrowTimeoutScriptPubKey, privateKeys: Seq[ECPrivateKey],
requiredSigs: Option[Int], hashType: HashType, isMultiSig: Boolean,
outputs: Seq[TransactionOutput] = Nil, amount: CurrencyUnit = CurrencyUnits.zero): EscrowTimeoutScriptSignature = {
requiredSigs: Option[Int], hashType: HashType, isMultiSig: Boolean,
outputs: Seq[TransactionOutput] = Nil, amount: CurrencyUnit = CurrencyUnits.zero): EscrowTimeoutScriptSignature = {
val pubKeys = privateKeys.map(_.publicKey)
val (creditingTx, outputIndex) = TransactionGenerators.buildCreditingTransaction(
TransactionConstants.validLockVersion, csvEscrowTimeout,amount)
TransactionConstants.validLockVersion, csvEscrowTimeout, amount
)
val (unsignedSpendingTx, inputIndex) = {
if (outputs.isEmpty) {
TransactionGenerators.buildSpendingTransaction(TransactionConstants.validLockVersion,
creditingTx, EmptyScriptSignature, outputIndex, UInt32.zero, sequence)
TransactionGenerators.buildSpendingTransaction(
TransactionConstants.validLockVersion,
creditingTx, EmptyScriptSignature, outputIndex, UInt32.zero, sequence
)
} else {
TransactionGenerators.buildSpendingTransaction(TransactionConstants.validLockVersion, creditingTx,
EmptyScriptSignature, outputIndex, UInt32.zero, sequence, outputs)
@ -452,14 +455,14 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
val txSignatureComponent = BaseTxSigComponent(unsignedSpendingTx, inputIndex,
csvEscrowTimeout, Policy.standardScriptVerifyFlags)
val txSignatures : Seq[ECDigitalSignature] = for {
val txSignatures: Seq[ECDigitalSignature] = for {
i <- 0 until requiredSigs.getOrElse(1)
} yield TransactionSignatureCreator.createSig(txSignatureComponent,privateKeys(i), hashType)
} yield TransactionSignatureCreator.createSig(txSignatureComponent, privateKeys(i), hashType)
if (isMultiSig) {
EscrowTimeoutScriptSignature.fromMultiSig(MultiSignatureScriptSignature(txSignatures))
} else {
val nestedScriptSig = lockTimeHelperScriptSig(csvEscrowTimeout.timeout,txSignatures,pubKeys)
val nestedScriptSig = lockTimeHelperScriptSig(csvEscrowTimeout.timeout, txSignatures, pubKeys)
EscrowTimeoutScriptSignature(nestedScriptSig).get
}
}
@ -471,32 +474,36 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
} yield (p2shScriptSig, p2shScriptPubKey, privKeys, witness, wtxSigComponent.amount)
def signedP2SHP2WSHScriptSignature: Gen[(P2SHScriptSignature, P2SHScriptPubKey, Seq[ECPrivateKey], TransactionWitness, CurrencyUnit)] = for {
(witness,wtxSigComponent,privKeys) <- WitnessGenerators.signedP2WSHTransactionWitness
(witness, wtxSigComponent, privKeys) <- WitnessGenerators.signedP2WSHTransactionWitness
p2shScriptPubKey = P2SHScriptPubKey(wtxSigComponent.scriptPubKey)
p2shScriptSig = P2SHScriptSignature(wtxSigComponent.scriptPubKey)
} yield (p2shScriptSig, p2shScriptPubKey, privKeys, witness, wtxSigComponent.amount)
/**
* This function chooses a random signed [[ScriptSignature]] that is NOT a [[P2SHScriptSignature]], [[CSVScriptSignature]],
* [[CLTVScriptSignature]], or any witness type
*
* @return the signed [[ScriptSignature]], the [[ScriptPubKey]] it is spending,
* and the sequence of[[ECPrivateKey]] used to sign it
*/
* This function chooses a random signed [[ScriptSignature]] that is NOT a [[P2SHScriptSignature]], [[CSVScriptSignature]],
* [[CLTVScriptSignature]], or any witness type
*
* @return the signed [[ScriptSignature]], the [[ScriptPubKey]] it is spending,
* and the sequence of[[ECPrivateKey]] used to sign it
*/
def chooseSignedScriptSig: Gen[(ScriptSignature, ScriptPubKey, Seq[ECPrivateKey])] = {
Gen.oneOf(packageToSequenceOfPrivateKeys(signedP2PKScriptSignature),
Gen.oneOf(
packageToSequenceOfPrivateKeys(signedP2PKScriptSignature),
packageToSequenceOfPrivateKeys(signedP2PKHScriptSignature),
signedMultiSignatureScriptSignature)
signedMultiSignatureScriptSignature
)
}
/** Generates a random [[ScriptSignature]], the [[ScriptPubKey]] it is spending, and the [[ECPrivateKey]] needed to spend it. */
def randomScriptSig : Gen[(ScriptSignature, ScriptPubKey, Seq[ECPrivateKey])] = {
val witP2SHP2WPKH = signedP2SHP2WPKHScriptSignature.map(x => (x._1,x._2,x._3))
val witP2SHP2WSH = signedP2SHP2WSHScriptSignature.map(x => (x._1,x._2,x._3))
Gen.oneOf(packageToSequenceOfPrivateKeys(signedP2PKHScriptSignature),
def randomScriptSig: Gen[(ScriptSignature, ScriptPubKey, Seq[ECPrivateKey])] = {
val witP2SHP2WPKH = signedP2SHP2WPKHScriptSignature.map(x => (x._1, x._2, x._3))
val witP2SHP2WSH = signedP2SHP2WSHScriptSignature.map(x => (x._1, x._2, x._3))
Gen.oneOf(
packageToSequenceOfPrivateKeys(signedP2PKHScriptSignature),
packageToSequenceOfPrivateKeys(signedP2PKScriptSignature),
signedMultiSignatureScriptSignature, signedCLTVScriptSignature,
signedCSVScriptSignature,signedP2SHScriptSignature,witP2SHP2WPKH,witP2SHP2WSH)
signedCSVScriptSignature, signedP2SHScriptSignature, witP2SHP2WPKH, witP2SHP2WSH
)
}
/** Simply converts one private key in the generator to a sequence of private keys */
@ -505,21 +512,20 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
} yield (scriptSig, scriptPubKey, Seq(privateKey))
/** Simply converts one private key in the generator to a sequence of private keys */
private def privKeyToSeq(tuple :(ScriptPubKey, ECPrivateKey)): (ScriptPubKey, Seq[ECPrivateKey]) = {
val (s,key) = tuple
(s,Seq(key))
private def privKeyToSeq(tuple: (ScriptPubKey, ECPrivateKey)): (ScriptPubKey, Seq[ECPrivateKey]) = {
val (s, key) = tuple
(s, Seq(key))
}
private def lockTimeHelperScriptSig(lock: LockTimeScriptPubKey, sigs: Seq[ECDigitalSignature],
keys: Seq[ECPublicKey]): LockTimeScriptSignature = {
val nestedScriptSig = lock.nestedScriptPubKey match {
case p2pk: P2PKScriptPubKey => P2PKScriptSignature(sigs.head)
case p2pkh: P2PKHScriptPubKey => P2PKHScriptSignature(sigs.head,keys.head)
case p2pk: P2PKScriptPubKey => P2PKScriptSignature(sigs.head)
case p2pkh: P2PKHScriptPubKey => P2PKHScriptSignature(sigs.head, keys.head)
case multisig: MultiSignatureScriptPubKey => MultiSignatureScriptSignature(sigs)
case EmptyScriptPubKey => CSVScriptSignature(EmptyScriptSignature)
case _: WitnessScriptPubKeyV0 | _ : UnassignedWitnessScriptPubKey =>
case EmptyScriptPubKey => CSVScriptSignature(EmptyScriptSignature)
case _: WitnessScriptPubKeyV0 | _: UnassignedWitnessScriptPubKey =>
//bare segwit always has an empty script sig, see BIP141
CSVScriptSignature(EmptyScriptSignature)
case _: LockTimeScriptPubKey | _: EscrowTimeoutScriptPubKey =>
@ -531,7 +537,7 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
lock match {
case _: CLTVScriptPubKey => CLTVScriptSignature(nestedScriptSig)
case _: CSVScriptPubKey => CSVScriptSignature(nestedScriptSig)
case _: CSVScriptPubKey => CSVScriptSignature(nestedScriptSig)
}
}
}

View file

@ -3,27 +3,27 @@ package org.bitcoins.core.gen
import org.scalacheck.Gen
/**
* Created by chris on 6/20/16.
*/
* Created by chris on 6/20/16.
*/
trait StringGenerators {
lazy val validHexChars = "0123456789abcdef".toCharArray
/**
* Generates a hex char
*
* @return
*/
def hexChar : Gen[Char] = Gen.choose(0,validHexChars.length - 1).map(validHexChars(_))
* Generates a hex char
*
* @return
*/
def hexChar: Gen[Char] = Gen.choose(0, validHexChars.length - 1).map(validHexChars(_))
/**
* Generates a random hex string
*
* @return
*/
def hexString : Gen[String] = {
val int = Gen.choose(0,100)
val hexStringGen : Gen[List[Char]] = int.flatMap { i =>
* Generates a random hex string
*
* @return
*/
def hexString: Gen[String] = {
val int = Gen.choose(0, 100)
val hexStringGen: Gen[List[Char]] = int.flatMap { i =>
if (i % 2 == 0) Gen.listOfN(i, hexChar)
else Gen.listOfN(i * 2, hexChar)
}
@ -31,25 +31,25 @@ trait StringGenerators {
}
def strChar: Gen[Char] = {
val char : Gen[Gen[Char]] = for {
randomNum <- Gen.choose(0,4)
val char: Gen[Gen[Char]] = for {
randomNum <- Gen.choose(0, 4)
} yield {
if (randomNum == 0) Gen.numChar
else if (randomNum == 1) Gen.alphaUpperChar
else if (randomNum == 2) Gen.alphaLowerChar
else if (randomNum == 3) Gen.alphaChar
else Gen.alphaNumChar
else Gen.alphaNumChar
}
char.flatMap(g => g)
}
def genString(size : Int): Gen[String] = {
val l : Gen[Seq[Char]] = Gen.listOfN(size,strChar)
def genString(size: Int): Gen[String] = {
val l: Gen[Seq[Char]] = Gen.listOfN(size, strChar)
l.map(_.mkString)
}
def genString: Gen[String] = for {
randomNum <- Gen.choose(0,100)
randomNum <- Gen.choose(0, 100)
randomString <- genString(randomNum)
} yield randomString

View file

@ -1,11 +1,11 @@
package org.bitcoins.core.gen
import org.bitcoins.core.crypto._
import org.bitcoins.core.currency.{CurrencyUnit, CurrencyUnits, Satoshis}
import org.bitcoins.core.number.{Int64, UInt32}
import org.bitcoins.core.currency.{ CurrencyUnit, CurrencyUnits, Satoshis }
import org.bitcoins.core.number.{ Int64, UInt32 }
import org.bitcoins.core.policy.Policy
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction.{TransactionInput, TransactionOutPoint, TransactionOutput, _}
import org.bitcoins.core.protocol.transaction.{ TransactionInput, TransactionOutPoint, TransactionOutput, _ }
import org.bitcoins.core.script.constant.ScriptNumber
import org.bitcoins.core.script.interpreter.ScriptInterpreter
import org.bitcoins.core.script.locktime.LockTimeInterpreter
@ -15,87 +15,89 @@ import org.scalacheck.Gen
import scala.annotation.tailrec
/**
* Created by chris on 6/21/16.
*/
* Created by chris on 6/21/16.
*/
trait TransactionGenerators extends BitcoinSLogger {
/** Responsible for generating [[org.bitcoins.core.protocol.transaction.TransactionOutPoint]] */
def outPoint : Gen[TransactionOutPoint] = for {
def outPoint: Gen[TransactionOutPoint] = for {
txId <- CryptoGenerators.doubleSha256Digest
vout <- NumberGenerator.uInt32s
} yield TransactionOutPoint(txId, vout)
/** Generates a random [[org.bitcoins.core.protocol.transaction.TransactionOutput]] */
def output : Gen[TransactionOutput] = for {
def output: Gen[TransactionOutput] = for {
satoshis <- CurrencyUnitGenerator.satoshis
(scriptPubKey, _) <- ScriptGenerators.scriptPubKey
} yield TransactionOutput(satoshis, scriptPubKey)
def outputs = Gen.listOf(output)
/** Outputs that only have a positive amount of satoshis, techinically the bitcoin protocol allows you
* to have negative value outputs
*/
/**
* Outputs that only have a positive amount of satoshis, techinically the bitcoin protocol allows you
* to have negative value outputs
*/
def realisticOutput: Gen[TransactionOutput] = CurrencyUnitGenerator.positiveRealistic.flatMap { amt =>
ScriptGenerators.scriptPubKey.map(spk => TransactionOutput(amt,spk._1))
ScriptGenerators.scriptPubKey.map(spk => TransactionOutput(amt, spk._1))
}
def realisticOutputs: Gen[Seq[TransactionOutput]] = Gen.choose(0,5).flatMap(n => Gen.listOfN(n,realisticOutput))
def realisticOutputs: Gen[Seq[TransactionOutput]] = Gen.choose(0, 5).flatMap(n => Gen.listOfN(n, realisticOutput))
/** Generates a small list of [[TransactionOutput]] */
def smallOutputs: Gen[Seq[TransactionOutput]] = Gen.choose(0,5).flatMap(i => Gen.listOfN(i,output))
def smallOutputs: Gen[Seq[TransactionOutput]] = Gen.choose(0, 5).flatMap(i => Gen.listOfN(i, output))
/** Creates a small sequence of outputs whose total sum is <= totalAmount */
def smallOutputs(totalAmount: CurrencyUnit): Gen[Seq[TransactionOutput]] = {
val numOutputs = Gen.choose(0,5).sample.get
val numOutputs = Gen.choose(0, 5).sample.get
@tailrec
def loop(remaining: Int, remainingAmount: CurrencyUnit, accum: Seq[CurrencyUnit]): Seq[CurrencyUnit] = {
if (remaining <= 0) {
accum
} else {
val amt = Gen.choose(100,remainingAmount.toBigDecimal.toLongExact).map(n => Satoshis(Int64(n))).sample.get
loop(remaining-1,remainingAmount - amt, amt +: accum)
val amt = Gen.choose(100, remainingAmount.toBigDecimal.toLongExact).map(n => Satoshis(Int64(n))).sample.get
loop(remaining - 1, remainingAmount - amt, amt +: accum)
}
}
val amts = loop(numOutputs,totalAmount,Nil)
val spks = Gen.listOfN(numOutputs,ScriptGenerators.scriptPubKey.map(_._1))
val amts = loop(numOutputs, totalAmount, Nil)
val spks = Gen.listOfN(numOutputs, ScriptGenerators.scriptPubKey.map(_._1))
spks.flatMap { s =>
s.zip(amts).map { case (spk,amt) =>
TransactionOutput(amt,spk)
s.zip(amts).map {
case (spk, amt) =>
TransactionOutput(amt, spk)
}
}
}
/** Generates a random [[org.bitcoins.core.protocol.transaction.TransactionInput]] */
def input : Gen[TransactionInput] = for {
def input: Gen[TransactionInput] = for {
outPoint <- outPoint
scriptSig <- ScriptGenerators.scriptSignature
sequenceNumber <- NumberGenerator.uInt32s
randomNum <- Gen.choose(0,10)
randomNum <- Gen.choose(0, 10)
} yield {
if (randomNum == 0) {
//gives us a coinbase input
CoinbaseInput(scriptSig)
} else TransactionInput(outPoint,scriptSig,sequenceNumber)
} else TransactionInput(outPoint, scriptSig, sequenceNumber)
}
def inputs = Gen.listOf(input)
/** Generates a small list of [[TransactionInput]] */
def smallInputs: Gen[Seq[TransactionInput]] = Gen.choose(0,5).flatMap(i => Gen.listOfN(i, input))
def smallInputs: Gen[Seq[TransactionInput]] = Gen.choose(0, 5).flatMap(i => Gen.listOfN(i, input))
/** Generates a small non empty list of [[TransactionInput]] */
def smallInputsNonEmpty: Gen[Seq[TransactionInput]] = Gen.choose(1,5).flatMap(i => Gen.listOfN(i,input))
def smallInputsNonEmpty: Gen[Seq[TransactionInput]] = Gen.choose(1, 5).flatMap(i => Gen.listOfN(i, input))
/**
* Generates an arbitrary [[org.bitcoins.core.protocol.transaction.Transaction]]
* This transaction's [[TransactionInput]]s will not evaluate to true
* inside of the [[org.bitcoins.core.script.interpreter.ScriptInterpreter]]
*/
* Generates an arbitrary [[org.bitcoins.core.protocol.transaction.Transaction]]
* This transaction's [[TransactionInput]]s will not evaluate to true
* inside of the [[org.bitcoins.core.script.interpreter.ScriptInterpreter]]
*/
def transactions: Gen[Seq[Transaction]] = Gen.listOf(transaction)
/** Generates a small list of [[Transaction]] */
def smallTransactions: Gen[Seq[Transaction]] = Gen.choose(0,10).flatMap(i => Gen.listOfN(i,transaction))
def smallTransactions: Gen[Seq[Transaction]] = Gen.choose(0, 10).flatMap(i => Gen.listOfN(i, transaction))
def transaction: Gen[Transaction] = Gen.oneOf(baseTransaction,witnessTransaction)
def transaction: Gen[Transaction] = Gen.oneOf(baseTransaction, witnessTransaction)
def baseTransaction: Gen[BaseTransaction] = for {
version <- NumberGenerator.uInt32s
@ -117,257 +119,259 @@ trait TransactionGenerators extends BitcoinSLogger {
//notice we use the old serialization format if all witnesses are empty
//[[https://github.com/bitcoin/bitcoin/blob/e8cfe1ee2d01c493b758a67ad14707dca15792ea/src/primitives/transaction.h#L276-L281]]
witness <- WitnessGenerators.transactionWitness(is.size).suchThat(_.witnesses.exists(_ != EmptyScriptWitness))
} yield WitnessTransaction(version,is,os,lockTime, witness)
} yield WitnessTransaction(version, is, os, lockTime, witness)
/**
* Creates a [[ECPrivateKey]], then creates a [[P2PKScriptPubKey]] from that private key
* Finally creates a [[Transaction]] that spends the [[P2PKScriptPubKey]] correctly
*/
* Creates a [[ECPrivateKey]], then creates a [[P2PKScriptPubKey]] from that private key
* Finally creates a [[Transaction]] that spends the [[P2PKScriptPubKey]] correctly
*/
def signedP2PKTransaction: Gen[(BaseTxSigComponent, ECPrivateKey)] = for {
(signedScriptSig, scriptPubKey, privateKey) <- ScriptGenerators.signedP2PKScriptSignature
(creditingTx,outputIndex) = buildCreditingTransaction(scriptPubKey)
(signedTx,inputIndex) = buildSpendingTransaction(creditingTx,signedScriptSig,outputIndex)
signedTxSignatureComponent = BaseTxSigComponent(signedTx,inputIndex,
scriptPubKey,Policy.standardScriptVerifyFlags)
} yield (signedTxSignatureComponent,privateKey)
(creditingTx, outputIndex) = buildCreditingTransaction(scriptPubKey)
(signedTx, inputIndex) = buildSpendingTransaction(creditingTx, signedScriptSig, outputIndex)
signedTxSignatureComponent = BaseTxSigComponent(signedTx, inputIndex,
scriptPubKey, Policy.standardScriptVerifyFlags)
} yield (signedTxSignatureComponent, privateKey)
/**
* Creates a [[ECPrivateKey]], then creates a [[P2PKHScriptPubKey]] from that private key
* Finally creates a [[Transaction]] that spends the [[P2PKHScriptPubKey]] correctly
*/
* Creates a [[ECPrivateKey]], then creates a [[P2PKHScriptPubKey]] from that private key
* Finally creates a [[Transaction]] that spends the [[P2PKHScriptPubKey]] correctly
*/
def signedP2PKHTransaction: Gen[(BaseTxSigComponent, ECPrivateKey)] = for {
(signedScriptSig, scriptPubKey, privateKey) <- ScriptGenerators.signedP2PKHScriptSignature
(creditingTx,outputIndex) = buildCreditingTransaction(scriptPubKey)
(signedTx,inputIndex) = buildSpendingTransaction(creditingTx,signedScriptSig,outputIndex)
signedTxSignatureComponent = BaseTxSigComponent(signedTx,inputIndex,
scriptPubKey,Policy.standardScriptVerifyFlags)
} yield (signedTxSignatureComponent,privateKey)
(creditingTx, outputIndex) = buildCreditingTransaction(scriptPubKey)
(signedTx, inputIndex) = buildSpendingTransaction(creditingTx, signedScriptSig, outputIndex)
signedTxSignatureComponent = BaseTxSigComponent(signedTx, inputIndex,
scriptPubKey, Policy.standardScriptVerifyFlags)
} yield (signedTxSignatureComponent, privateKey)
/**
* Creates a sequence of [[ECPrivateKey]], then creates a [[MultiSignatureScriptPubKey]] from those private keys,
* Finally creates a [[Transaction]] that spends the [[MultiSignatureScriptPubKey]] correctly
*/
* Creates a sequence of [[ECPrivateKey]], then creates a [[MultiSignatureScriptPubKey]] from those private keys,
* Finally creates a [[Transaction]] that spends the [[MultiSignatureScriptPubKey]] correctly
*/
def signedMultiSigTransaction: Gen[(BaseTxSigComponent, Seq[ECPrivateKey])] = for {
(signedScriptSig, scriptPubKey, privateKey) <- ScriptGenerators.signedMultiSignatureScriptSignature
(creditingTx,outputIndex) = buildCreditingTransaction(scriptPubKey)
(signedTx,inputIndex) = buildSpendingTransaction(creditingTx,signedScriptSig,outputIndex)
signedTxSignatureComponent = BaseTxSigComponent(signedTx,inputIndex,
scriptPubKey,Policy.standardScriptVerifyFlags)
} yield (signedTxSignatureComponent,privateKey)
(creditingTx, outputIndex) = buildCreditingTransaction(scriptPubKey)
(signedTx, inputIndex) = buildSpendingTransaction(creditingTx, signedScriptSig, outputIndex)
signedTxSignatureComponent = BaseTxSigComponent(signedTx, inputIndex,
scriptPubKey, Policy.standardScriptVerifyFlags)
} yield (signedTxSignatureComponent, privateKey)
/**
* Creates a transaction which contains a [[P2SHScriptSignature]] that correctly spends a [[P2SHScriptPubKey]]
*/
* Creates a transaction which contains a [[P2SHScriptSignature]] that correctly spends a [[P2SHScriptPubKey]]
*/
def signedP2SHTransaction: Gen[(BaseTxSigComponent, Seq[ECPrivateKey])] = for {
(signedScriptSig, scriptPubKey, privateKey) <- ScriptGenerators.signedP2SHScriptSignature
(creditingTx,outputIndex) = buildCreditingTransaction(signedScriptSig.redeemScript)
(signedTx,inputIndex) = buildSpendingTransaction(creditingTx,signedScriptSig,outputIndex)
signedTxSignatureComponent = BaseTxSigComponent(signedTx,inputIndex,
scriptPubKey,Policy.standardScriptVerifyFlags)
} yield (signedTxSignatureComponent,privateKey)
(creditingTx, outputIndex) = buildCreditingTransaction(signedScriptSig.redeemScript)
(signedTx, inputIndex) = buildSpendingTransaction(creditingTx, signedScriptSig, outputIndex)
signedTxSignatureComponent = BaseTxSigComponent(signedTx, inputIndex,
scriptPubKey, Policy.standardScriptVerifyFlags)
} yield (signedTxSignatureComponent, privateKey)
/** Generates a validly constructed CLTV transaction, which has a 50/50 chance of being spendable or unspendable. */
def randomCLTVTransaction : Gen[(BaseTxSigComponent, Seq[ECPrivateKey])] = {
Gen.oneOf(unspendableCLTVTransaction,spendableCLTVTransaction)
def randomCLTVTransaction: Gen[(BaseTxSigComponent, Seq[ECPrivateKey])] = {
Gen.oneOf(unspendableCLTVTransaction, spendableCLTVTransaction)
}
/**
* Creates a [[ECPrivateKey]], then creates a [[CLTVScriptPubKey]] from that private key
* Finally creates a [[Transaction]] that CANNNOT spend the [[CLTVScriptPubKey]] because the LockTime requirement
* is not satisfied (i.e. the transaction's lockTime has not surpassed the CLTV value in the [[CLTVScriptPubKey]])
*
* @return
*/
def unspendableCLTVTransaction : Gen[(BaseTxSigComponent, Seq[ECPrivateKey])] = for {
(cltvLockTime,txLockTime) <- unspendableCLTVValues
* Creates a [[ECPrivateKey]], then creates a [[CLTVScriptPubKey]] from that private key
* Finally creates a [[Transaction]] that CANNNOT spend the [[CLTVScriptPubKey]] because the LockTime requirement
* is not satisfied (i.e. the transaction's lockTime has not surpassed the CLTV value in the [[CLTVScriptPubKey]])
*
* @return
*/
def unspendableCLTVTransaction: Gen[(BaseTxSigComponent, Seq[ECPrivateKey])] = for {
(cltvLockTime, txLockTime) <- unspendableCLTVValues
sequence <- NumberGenerator.uInt32s.suchThat(n => n < UInt32.max)
(scriptSig,scriptPubKey,privKeys) <- ScriptGenerators.signedCLTVScriptSignature(cltvLockTime,txLockTime,sequence)
unspendable = lockTimeTxHelper(scriptSig,scriptPubKey,privKeys,sequence,Some(txLockTime))
(scriptSig, scriptPubKey, privKeys) <- ScriptGenerators.signedCLTVScriptSignature(cltvLockTime, txLockTime, sequence)
unspendable = lockTimeTxHelper(scriptSig, scriptPubKey, privKeys, sequence, Some(txLockTime))
} yield unspendable
/**
* Creates a [[ECPrivateKey]], then creates a [[CLTVScriptPubKey]] from that private key
* Finally creates a [[Transaction]] that can successfully spend the [[CLTVScriptPubKey]]
*/
def spendableCLTVTransaction : Gen[(BaseTxSigComponent, Seq[ECPrivateKey])] = for {
(cltvLockTime,txLockTime) <- spendableCLTVValues
* Creates a [[ECPrivateKey]], then creates a [[CLTVScriptPubKey]] from that private key
* Finally creates a [[Transaction]] that can successfully spend the [[CLTVScriptPubKey]]
*/
def spendableCLTVTransaction: Gen[(BaseTxSigComponent, Seq[ECPrivateKey])] = for {
(cltvLockTime, txLockTime) <- spendableCLTVValues
sequence <- NumberGenerator.uInt32s.suchThat(n => n < UInt32.max)
(scriptSig,scriptPubKey,privKeys) <- ScriptGenerators.signedCLTVScriptSignature(cltvLockTime,txLockTime,sequence)
spendable = lockTimeTxHelper(scriptSig,scriptPubKey,privKeys,sequence,Some(txLockTime))
(scriptSig, scriptPubKey, privKeys) <- ScriptGenerators.signedCLTVScriptSignature(cltvLockTime, txLockTime, sequence)
spendable = lockTimeTxHelper(scriptSig, scriptPubKey, privKeys, sequence, Some(txLockTime))
} yield spendable
/**
* Creates a [[ECPrivateKey]], then creates a [[CSVScriptPubKey]] from that private key
* Finally creates a [[Transaction]] that can successfully spend the [[CSVScriptPubKey]]
*/
def spendableCSVTransaction : Gen[(BaseTxSigComponent, Seq[ECPrivateKey])] = for {
* Creates a [[ECPrivateKey]], then creates a [[CSVScriptPubKey]] from that private key
* Finally creates a [[Transaction]] that can successfully spend the [[CSVScriptPubKey]]
*/
def spendableCSVTransaction: Gen[(BaseTxSigComponent, Seq[ECPrivateKey])] = for {
(csvScriptNum, sequence) <- spendableCSVValues
tx <- csvTransaction(csvScriptNum,sequence)
tx <- csvTransaction(csvScriptNum, sequence)
} yield tx
/** Creates a CSV transaction that's timelock has not been met */
def unspendableCSVTransaction : Gen[(BaseTxSigComponent, Seq[ECPrivateKey])] = for {
def unspendableCSVTransaction: Gen[(BaseTxSigComponent, Seq[ECPrivateKey])] = for {
(csvScriptNum, sequence) <- unspendableCSVValues
tx <- csvTransaction(csvScriptNum, sequence)
} yield tx
def csvTransaction(csvScriptNum: ScriptNumber, sequence: UInt32): Gen[(BaseTxSigComponent, Seq[ECPrivateKey])] = for {
(signedScriptSig, csvScriptPubKey, privateKeys) <- ScriptGenerators.signedCSVScriptSignature(csvScriptNum, sequence)
} yield lockTimeTxHelper(signedScriptSig, csvScriptPubKey, privateKeys, sequence,None)
} yield lockTimeTxHelper(signedScriptSig, csvScriptPubKey, privateKeys, sequence, None)
/** Generates a [[Transaction]] that has a valid [[EscrowTimeoutScriptSignature]] that specifically spends the
* [[EscrowTimeoutScriptPubKey]] using the multisig escrow branch */
/**
* Generates a [[Transaction]] that has a valid [[EscrowTimeoutScriptSignature]] that specifically spends the
* [[EscrowTimeoutScriptPubKey]] using the multisig escrow branch
*/
def spendableMultiSigEscrowTimeoutTransaction(outputs: Seq[TransactionOutput]): Gen[BaseTxSigComponent] = for {
sequence <- NumberGenerator.uInt32s
amount <- CurrencyUnitGenerator.satoshis
(scriptSig, scriptPubKey,privKeys) <- ScriptGenerators.signedMultiSigEscrowTimeoutScriptSig(sequence,outputs,amount)
(creditingTx,outputIndex) = buildCreditingTransaction(TransactionConstants.validLockVersion,scriptPubKey,amount)
(spendingTx, inputIndex) = buildSpendingTransaction(TransactionConstants.validLockVersion,creditingTx,scriptSig,
outputIndex, TransactionConstants.lockTime,sequence,outputs)
baseTxSigComponent = BaseTxSigComponent(spendingTx,inputIndex,scriptPubKey,Policy.standardScriptVerifyFlags)
(scriptSig, scriptPubKey, privKeys) <- ScriptGenerators.signedMultiSigEscrowTimeoutScriptSig(sequence, outputs, amount)
(creditingTx, outputIndex) = buildCreditingTransaction(TransactionConstants.validLockVersion, scriptPubKey, amount)
(spendingTx, inputIndex) = buildSpendingTransaction(TransactionConstants.validLockVersion, creditingTx, scriptSig,
outputIndex, TransactionConstants.lockTime, sequence, outputs)
baseTxSigComponent = BaseTxSigComponent(spendingTx, inputIndex, scriptPubKey, Policy.standardScriptVerifyFlags)
} yield baseTxSigComponent
/** Generates a [[Transaction]] that has a valid [[EscrowTimeoutScriptSignature]] that specfically spends the
* [[EscrowTimeoutScriptPubKey]] using the timeout branch */
/**
* Generates a [[Transaction]] that has a valid [[EscrowTimeoutScriptSignature]] that specfically spends the
* [[EscrowTimeoutScriptPubKey]] using the timeout branch
*/
def spendableTimeoutEscrowTimeoutTransaction(outputs: Seq[TransactionOutput]): Gen[BaseTxSigComponent] = for {
(csvScriptNum,sequence) <- spendableCSVValues
(scriptSig, scriptPubKey,privKeys) <- ScriptGenerators.timeoutEscrowTimeoutScriptSig(csvScriptNum,sequence,outputs)
(creditingTx,outputIndex) = buildCreditingTransaction(TransactionConstants.validLockVersion,scriptPubKey)
(spendingTx, inputIndex) = buildSpendingTransaction(TransactionConstants.validLockVersion,creditingTx,
scriptSig,outputIndex,UInt32.zero,sequence,outputs)
baseTxSigComponent = BaseTxSigComponent(spendingTx,inputIndex,scriptPubKey,Policy.standardScriptVerifyFlags)
(csvScriptNum, sequence) <- spendableCSVValues
(scriptSig, scriptPubKey, privKeys) <- ScriptGenerators.timeoutEscrowTimeoutScriptSig(csvScriptNum, sequence, outputs)
(creditingTx, outputIndex) = buildCreditingTransaction(TransactionConstants.validLockVersion, scriptPubKey)
(spendingTx, inputIndex) = buildSpendingTransaction(TransactionConstants.validLockVersion, creditingTx,
scriptSig, outputIndex, UInt32.zero, sequence, outputs)
baseTxSigComponent = BaseTxSigComponent(spendingTx, inputIndex, scriptPubKey, Policy.standardScriptVerifyFlags)
} yield baseTxSigComponent
/** Generates a [[Transaction]] that has a valid [[EscrowTimeoutScriptSignature]] */
def spendableEscrowTimeoutTransaction(outputs: Seq[TransactionOutput] = Nil): Gen[BaseTxSigComponent] = {
Gen.oneOf(spendableMultiSigEscrowTimeoutTransaction(outputs),
spendableTimeoutEscrowTimeoutTransaction(outputs))
Gen.oneOf(
spendableMultiSigEscrowTimeoutTransaction(outputs),
spendableTimeoutEscrowTimeoutTransaction(outputs)
)
}
/** Generates a [[Transaction]] that has a valid [[EscrowTimeoutScriptSignature]] */
def spendableEscrowTimeoutTransaction: Gen[BaseTxSigComponent] = {
Gen.oneOf(spendableMultiSigEscrowTimeoutTransaction(Nil),
spendableTimeoutEscrowTimeoutTransaction(Nil))
Gen.oneOf(
spendableMultiSigEscrowTimeoutTransaction(Nil),
spendableTimeoutEscrowTimeoutTransaction(Nil)
)
}
/** Generates a CSVEscrowTimeoutTransaction that should evaluate to false when run through the [[ScriptInterpreter]] */
def unspendableTimeoutEscrowTimeoutTransaction: Gen[BaseTxSigComponent] = for {
(csvScriptNum, sequence) <- unspendableCSVValues
(scriptSig, scriptPubKey,privKeys) <- ScriptGenerators.timeoutEscrowTimeoutScriptSig(csvScriptNum,sequence,Nil)
(creditingTx,outputIndex) = buildCreditingTransaction(TransactionConstants.validLockVersion,scriptPubKey)
(spendingTx, inputIndex) = buildSpendingTransaction(TransactionConstants.validLockVersion,creditingTx,scriptSig,outputIndex,
TransactionConstants.lockTime,sequence)
baseTxSigComponent = BaseTxSigComponent(spendingTx,inputIndex,scriptPubKey,Policy.standardScriptVerifyFlags)
(scriptSig, scriptPubKey, privKeys) <- ScriptGenerators.timeoutEscrowTimeoutScriptSig(csvScriptNum, sequence, Nil)
(creditingTx, outputIndex) = buildCreditingTransaction(TransactionConstants.validLockVersion, scriptPubKey)
(spendingTx, inputIndex) = buildSpendingTransaction(TransactionConstants.validLockVersion, creditingTx, scriptSig, outputIndex,
TransactionConstants.lockTime, sequence)
baseTxSigComponent = BaseTxSigComponent(spendingTx, inputIndex, scriptPubKey, Policy.standardScriptVerifyFlags)
} yield baseTxSigComponent
def unspendableMultiSigEscrowTimeoutTransaction: Gen[BaseTxSigComponent] = for {
sequence <- NumberGenerator.uInt32s
(multiSigScriptPubKey,_) <- ScriptGenerators.multiSigScriptPubKey
(lock,_) <- ScriptGenerators.lockTimeScriptPubKey
escrow = EscrowTimeoutScriptPubKey(multiSigScriptPubKey,lock)
(multiSigScriptPubKey, _) <- ScriptGenerators.multiSigScriptPubKey
(lock, _) <- ScriptGenerators.lockTimeScriptPubKey
escrow = EscrowTimeoutScriptPubKey(multiSigScriptPubKey, lock)
multiSigScriptSig <- ScriptGenerators.multiSignatureScriptSignature
(creditingTx,outputIndex) = buildCreditingTransaction(TransactionConstants.validLockVersion,escrow)
(creditingTx, outputIndex) = buildCreditingTransaction(TransactionConstants.validLockVersion, escrow)
escrowScriptSig = EscrowTimeoutScriptSignature.fromMultiSig(multiSigScriptSig)
(spendingTx, inputIndex) = buildSpendingTransaction(TransactionConstants.validLockVersion,creditingTx,
escrowScriptSig,outputIndex,TransactionConstants.lockTime,sequence)
baseTxSigComponent = BaseTxSigComponent(spendingTx,inputIndex,escrow,Policy.standardScriptVerifyFlags)
(spendingTx, inputIndex) = buildSpendingTransaction(TransactionConstants.validLockVersion, creditingTx,
escrowScriptSig, outputIndex, TransactionConstants.lockTime, sequence)
baseTxSigComponent = BaseTxSigComponent(spendingTx, inputIndex, escrow, Policy.standardScriptVerifyFlags)
} yield baseTxSigComponent
def unspendableEscrowTimeoutTransaction: Gen[BaseTxSigComponent] = {
def unspendableEscrowTimeoutTransaction: Gen[BaseTxSigComponent] = {
Gen.oneOf(unspendableTimeoutEscrowTimeoutTransaction, unspendableMultiSigEscrowTimeoutTransaction)
}
/** Generates a [[WitnessTransaction]] that has all of it's inputs signed correctly */
def signedP2WPKHTransaction: Gen[(WitnessTxSigComponent,Seq[ECPrivateKey])] = for {
(_,wBaseTxSigComponent, privKeys) <- WitnessGenerators.signedP2WPKHTransactionWitness
} yield (wBaseTxSigComponent,privKeys)
def signedP2WPKHTransaction: Gen[(WitnessTxSigComponent, Seq[ECPrivateKey])] = for {
(_, wBaseTxSigComponent, privKeys) <- WitnessGenerators.signedP2WPKHTransactionWitness
} yield (wBaseTxSigComponent, privKeys)
/** Generates a [[WitnessTransaction]] that has an input spends a raw P2WSH [[WitnessScriptPubKey]] */
def signedP2WSHP2PKTransaction: Gen[(WitnessTxSigComponentRaw, Seq[ECPrivateKey])] = for {
(_,wBaseTxSigComponent, privKeys) <- WitnessGenerators.signedP2WSHP2PKTransactionWitness
} yield (wBaseTxSigComponent,privKeys)
(_, wBaseTxSigComponent, privKeys) <- WitnessGenerators.signedP2WSHP2PKTransactionWitness
} yield (wBaseTxSigComponent, privKeys)
/** Generates a [[WitnessTransaction]] that has an input spends a raw P2WSH [[WitnessScriptPubKey]] */
def signedP2WSHP2PKHTransaction: Gen[(WitnessTxSigComponentRaw, Seq[ECPrivateKey])] = for {
(_,wBaseTxSigComponent, privKeys) <- WitnessGenerators.signedP2WSHP2PKHTransactionWitness
} yield (wBaseTxSigComponent,privKeys)
(_, wBaseTxSigComponent, privKeys) <- WitnessGenerators.signedP2WSHP2PKHTransactionWitness
} yield (wBaseTxSigComponent, privKeys)
def signedP2WSHMultiSigTransaction: Gen[(WitnessTxSigComponentRaw, Seq[ECPrivateKey])] = for {
(_,wBaseTxSigComponent, privKeys) <- WitnessGenerators.signedP2WSHMultiSigTransactionWitness
} yield (wBaseTxSigComponent,privKeys)
(_, wBaseTxSigComponent, privKeys) <- WitnessGenerators.signedP2WSHMultiSigTransactionWitness
} yield (wBaseTxSigComponent, privKeys)
def signedP2WSHMultiSigEscrowTimeoutTransaction: Gen[(WitnessTxSigComponentRaw, Seq[ECPrivateKey])] = for {
(_,wBaseTxSigComponent,privKeys) <- WitnessGenerators.signedP2WSHMultiSigEscrowTimeoutWitness
} yield (wBaseTxSigComponent,privKeys)
(_, wBaseTxSigComponent, privKeys) <- WitnessGenerators.signedP2WSHMultiSigEscrowTimeoutWitness
} yield (wBaseTxSigComponent, privKeys)
def spendableP2WSHTimeoutEscrowTimeoutTransaction: Gen[(WitnessTxSigComponentRaw, Seq[ECPrivateKey])] = for {
(_,wBaseTxSigComponent,privKeys) <- WitnessGenerators.spendableP2WSHTimeoutEscrowTimeoutWitness
} yield (wBaseTxSigComponent,privKeys)
(_, wBaseTxSigComponent, privKeys) <- WitnessGenerators.spendableP2WSHTimeoutEscrowTimeoutWitness
} yield (wBaseTxSigComponent, privKeys)
def signedP2WSHEscrowTimeoutTransaction: Gen[(WitnessTxSigComponentRaw, Seq[ECPrivateKey])] = {
Gen.oneOf(signedP2WSHMultiSigEscrowTimeoutTransaction,spendableP2WSHTimeoutEscrowTimeoutTransaction)
Gen.oneOf(signedP2WSHMultiSigEscrowTimeoutTransaction, spendableP2WSHTimeoutEscrowTimeoutTransaction)
}
/** Creates a signed P2SH(P2WPKH) transaction */
def signedP2SHP2WPKHTransaction: Gen[(WitnessTxSigComponent, Seq[ECPrivateKey])] = for {
(signedScriptSig, scriptPubKey, privKeys, witness, amount) <- ScriptGenerators.signedP2SHP2WPKHScriptSignature
(creditingTx,outputIndex) = buildCreditingTransaction(signedScriptSig.redeemScript, amount)
(signedTx,inputIndex) = buildSpendingTransaction(TransactionConstants.validLockVersion,creditingTx,
(creditingTx, outputIndex) = buildCreditingTransaction(signedScriptSig.redeemScript, amount)
(signedTx, inputIndex) = buildSpendingTransaction(TransactionConstants.validLockVersion, creditingTx,
signedScriptSig, outputIndex, witness)
signedTxSignatureComponent = WitnessTxSigComponent(signedTx,inputIndex,
scriptPubKey, Policy.standardScriptVerifyFlags,amount)
signedTxSignatureComponent = WitnessTxSigComponent(signedTx, inputIndex,
scriptPubKey, Policy.standardScriptVerifyFlags, amount)
} yield (signedTxSignatureComponent, privKeys)
def signedP2WSHTransaction: Gen[(WitnessTxSigComponentRaw,Seq[ECPrivateKey])] = {
def signedP2WSHTransaction: Gen[(WitnessTxSigComponentRaw, Seq[ECPrivateKey])] = {
Gen.oneOf(signedP2WSHP2PKTransaction, signedP2WSHP2PKHTransaction, signedP2WSHMultiSigTransaction,
signedP2WSHEscrowTimeoutTransaction
)
signedP2WSHEscrowTimeoutTransaction)
}
/** Creates a signed P2SH(P2WSH) transaction */
def signedP2SHP2WSHTransaction: Gen[(WitnessTxSigComponent, Seq[ECPrivateKey])] = for {
(witness,wBaseTxSigComponent, privKeys) <- WitnessGenerators.signedP2WSHTransactionWitness
(witness, wBaseTxSigComponent, privKeys) <- WitnessGenerators.signedP2WSHTransactionWitness
p2shScriptPubKey = P2SHScriptPubKey(wBaseTxSigComponent.scriptPubKey)
p2shScriptSig = P2SHScriptSignature(wBaseTxSigComponent.scriptPubKey.asInstanceOf[WitnessScriptPubKey])
(creditingTx,outputIndex) = buildCreditingTransaction(p2shScriptSig.redeemScript, wBaseTxSigComponent.amount)
(creditingTx, outputIndex) = buildCreditingTransaction(p2shScriptSig.redeemScript, wBaseTxSigComponent.amount)
sequence = wBaseTxSigComponent.transaction.inputs(wBaseTxSigComponent.inputIndex.toInt).sequence
locktime = wBaseTxSigComponent.transaction.lockTime
(signedTx,inputIndex) = buildSpendingTransaction(TransactionConstants.validLockVersion,creditingTx,p2shScriptSig,outputIndex,locktime,sequence,witness)
signedTxSignatureComponent = WitnessTxSigComponent(signedTx,inputIndex,
(signedTx, inputIndex) = buildSpendingTransaction(TransactionConstants.validLockVersion, creditingTx, p2shScriptSig, outputIndex, locktime, sequence, witness)
signedTxSignatureComponent = WitnessTxSigComponent(signedTx, inputIndex,
p2shScriptPubKey, Policy.standardScriptVerifyFlags, wBaseTxSigComponent.amount)
} yield (signedTxSignatureComponent,privKeys)
} yield (signedTxSignatureComponent, privKeys)
/**
* Builds a spending transaction according to bitcoin core
* @return the built spending transaction and the input index for the script signature
*/
def buildSpendingTransaction(version : UInt32, creditingTx : Transaction,scriptSignature : ScriptSignature,
outputIndex : UInt32, locktime : UInt32, sequence : UInt32) : (Transaction,UInt32) = {
val output = TransactionOutput(CurrencyUnits.zero,EmptyScriptPubKey)
buildSpendingTransaction(version,creditingTx,scriptSignature,outputIndex,locktime,sequence,Seq(output))
* Builds a spending transaction according to bitcoin core
* @return the built spending transaction and the input index for the script signature
*/
def buildSpendingTransaction(version: UInt32, creditingTx: Transaction, scriptSignature: ScriptSignature,
outputIndex: UInt32, locktime: UInt32, sequence: UInt32): (Transaction, UInt32) = {
val output = TransactionOutput(CurrencyUnits.zero, EmptyScriptPubKey)
buildSpendingTransaction(version, creditingTx, scriptSignature, outputIndex, locktime, sequence, Seq(output))
}
def buildSpendingTransaction(version : UInt32, creditingTx : Transaction,scriptSignature : ScriptSignature,
outputIndex : UInt32, locktime : UInt32, sequence : UInt32, outputs: Seq[TransactionOutput]): (Transaction,UInt32) = {
def buildSpendingTransaction(version: UInt32, creditingTx: Transaction, scriptSignature: ScriptSignature,
outputIndex: UInt32, locktime: UInt32, sequence: UInt32, outputs: Seq[TransactionOutput]): (Transaction, UInt32) = {
val os = if (outputs.isEmpty) {
Seq(TransactionOutput(CurrencyUnits.zero,EmptyScriptPubKey))
Seq(TransactionOutput(CurrencyUnits.zero, EmptyScriptPubKey))
} else {
outputs
}
val outpoint = TransactionOutPoint(creditingTx.txId,outputIndex)
val input = TransactionInput(outpoint,scriptSignature, sequence)
val tx = BaseTransaction(version,Seq(input),os,locktime)
(tx,UInt32.zero)
val outpoint = TransactionOutPoint(creditingTx.txId, outputIndex)
val input = TransactionInput(outpoint, scriptSignature, sequence)
val tx = BaseTransaction(version, Seq(input), os, locktime)
(tx, UInt32.zero)
}
/**
* Builds a spending transaction according to bitcoin core with max sequence and a locktime of zero.
* @return the built spending transaction and the input index for the script signature
*/
def buildSpendingTransaction(creditingTx : Transaction,scriptSignature : ScriptSignature, outputIndex : UInt32) : (Transaction,UInt32) = {
* Builds a spending transaction according to bitcoin core with max sequence and a locktime of zero.
* @return the built spending transaction and the input index for the script signature
*/
def buildSpendingTransaction(creditingTx: Transaction, scriptSignature: ScriptSignature, outputIndex: UInt32): (Transaction, UInt32) = {
buildSpendingTransaction(TransactionConstants.version, creditingTx, scriptSignature, outputIndex,
TransactionConstants.lockTime, TransactionConstants.sequence)
}
@ -375,43 +379,43 @@ trait TransactionGenerators extends BitcoinSLogger {
/** Builds a spending [[WitnessTransaction]] with the given parameters */
def buildSpendingTransaction(creditingTx: Transaction, scriptSignature: ScriptSignature, outputIndex: UInt32,
locktime: UInt32, sequence: UInt32, witness: TransactionWitness): (WitnessTransaction, UInt32) = {
buildSpendingTransaction(TransactionConstants.version,creditingTx,scriptSignature,outputIndex,locktime,sequence,witness)
buildSpendingTransaction(TransactionConstants.version, creditingTx, scriptSignature, outputIndex, locktime, sequence, witness)
}
def buildSpendingTransaction(version: UInt32, creditingTx: Transaction, scriptSignature: ScriptSignature, outputIndex: UInt32,
locktime: UInt32, sequence: UInt32, witness: TransactionWitness): (WitnessTransaction, UInt32) = {
val outputs = dummyOutputs
buildSpendingTransaction(version,creditingTx,scriptSignature,outputIndex,locktime,sequence,witness,outputs)
buildSpendingTransaction(version, creditingTx, scriptSignature, outputIndex, locktime, sequence, witness, outputs)
}
def dummyOutputs: Seq[TransactionOutput] = Seq(TransactionOutput(CurrencyUnits.zero,EmptyScriptPubKey))
def dummyOutputs: Seq[TransactionOutput] = Seq(TransactionOutput(CurrencyUnits.zero, EmptyScriptPubKey))
def buildSpendingTransaction(version: UInt32, creditingTx: Transaction, scriptSignature: ScriptSignature, outputIndex: UInt32,
locktime: UInt32, sequence: UInt32, witness: TransactionWitness, outputs: Seq[TransactionOutput]): (WitnessTransaction, UInt32) = {
val outpoint = TransactionOutPoint(creditingTx.txId,outputIndex)
val input = TransactionInput(outpoint,scriptSignature,sequence)
(WitnessTransaction(version,Seq(input), outputs,locktime,witness), UInt32.zero)
val outpoint = TransactionOutPoint(creditingTx.txId, outputIndex)
val input = TransactionInput(outpoint, scriptSignature, sequence)
(WitnessTransaction(version, Seq(input), outputs, locktime, witness), UInt32.zero)
}
def buildSpendingTransaction(creditingTx: Transaction, scriptSignature: ScriptSignature, outputIndex: UInt32,
witness: TransactionWitness): (WitnessTransaction, UInt32) = {
buildSpendingTransaction(TransactionConstants.version,creditingTx,scriptSignature,outputIndex,witness)
buildSpendingTransaction(TransactionConstants.version, creditingTx, scriptSignature, outputIndex, witness)
}
def buildSpendingTransaction(version: UInt32, creditingTx: Transaction, scriptSignature: ScriptSignature, outputIndex: UInt32,
witness: TransactionWitness): (WitnessTransaction, UInt32) = {
val locktime = TransactionConstants.lockTime
val sequence = TransactionConstants.sequence
buildSpendingTransaction(version,creditingTx,scriptSignature,outputIndex,locktime,sequence,witness)
buildSpendingTransaction(version, creditingTx, scriptSignature, outputIndex, locktime, sequence, witness)
}
/**
* Mimics this test utility found in bitcoin core
* https://github.com/bitcoin/bitcoin/blob/605c17844ea32b6d237db6d83871164dc7d59dab/src/test/script_tests.cpp#L57
* @return the transaction and the output index of the scriptPubKey
*/
def buildCreditingTransaction(scriptPubKey : ScriptPubKey) : (Transaction,UInt32) = {
* Mimics this test utility found in bitcoin core
* https://github.com/bitcoin/bitcoin/blob/605c17844ea32b6d237db6d83871164dc7d59dab/src/test/script_tests.cpp#L57
* @return the transaction and the output index of the scriptPubKey
*/
def buildCreditingTransaction(scriptPubKey: ScriptPubKey): (Transaction, UInt32) = {
//this needs to be all zeros according to these 3 lines in bitcoin core
//https://github.com/bitcoin/bitcoin/blob/605c17844ea32b6d237db6d83871164dc7d59dab/src/test/script_tests.cpp#L64
//https://github.com/bitcoin/bitcoin/blob/80d1f2e48364f05b2cdf44239b3a1faa0277e58e/src/primitives/transaction.h#L32
@ -420,32 +424,32 @@ trait TransactionGenerators extends BitcoinSLogger {
}
def buildCreditingTransaction(scriptPubKey: ScriptPubKey, amount: CurrencyUnit): (Transaction, UInt32) = {
buildCreditingTransaction(TransactionConstants.version,scriptPubKey,amount)
buildCreditingTransaction(TransactionConstants.version, scriptPubKey, amount)
}
/**
* Builds a crediting transaction with a transaction version parameter.
* Example: useful for creating transactions with scripts containing OP_CHECKSEQUENCEVERIFY.
* @return
*/
def buildCreditingTransaction(version : UInt32, scriptPubKey: ScriptPubKey) : (Transaction, UInt32) = {
buildCreditingTransaction(version,scriptPubKey,CurrencyUnits.zero)
* Builds a crediting transaction with a transaction version parameter.
* Example: useful for creating transactions with scripts containing OP_CHECKSEQUENCEVERIFY.
* @return
*/
def buildCreditingTransaction(version: UInt32, scriptPubKey: ScriptPubKey): (Transaction, UInt32) = {
buildCreditingTransaction(version, scriptPubKey, CurrencyUnits.zero)
}
def buildCreditingTransaction(version: UInt32, output: TransactionOutput): (Transaction,UInt32) = {
def buildCreditingTransaction(version: UInt32, output: TransactionOutput): (Transaction, UInt32) = {
val outpoint = EmptyTransactionOutPoint
val scriptSignature = ScriptSignature("0000")
val input = TransactionInput(outpoint,scriptSignature,TransactionConstants.sequence)
val tx = BaseTransaction(version,Seq(input),Seq(output),TransactionConstants.lockTime)
(tx,UInt32.zero)
val input = TransactionInput(outpoint, scriptSignature, TransactionConstants.sequence)
val tx = BaseTransaction(version, Seq(input), Seq(output), TransactionConstants.lockTime)
(tx, UInt32.zero)
}
def buildCreditingTransaction(version: UInt32, scriptPubKey: ScriptPubKey, amount: CurrencyUnit): (Transaction,UInt32) = {
buildCreditingTransaction(version, TransactionOutput(amount,scriptPubKey))
def buildCreditingTransaction(version: UInt32, scriptPubKey: ScriptPubKey, amount: CurrencyUnit): (Transaction, UInt32) = {
buildCreditingTransaction(version, TransactionOutput(amount, scriptPubKey))
}
private def lockTimeTxHelper(signedScriptSig : LockTimeScriptSignature, lock : LockTimeScriptPubKey,
privKeys : Seq[ECPrivateKey], sequence : UInt32, lockTime: Option[UInt32]) : (BaseTxSigComponent, Seq[ECPrivateKey]) = {
private def lockTimeTxHelper(signedScriptSig: LockTimeScriptSignature, lock: LockTimeScriptPubKey,
privKeys: Seq[ECPrivateKey], sequence: UInt32, lockTime: Option[UInt32]): (BaseTxSigComponent, Seq[ECPrivateKey]) = {
val (creditingTx, outputIndex) = buildCreditingTransaction(TransactionConstants.validLockVersion, lock)
//Transaction version must not be less than 2 for a CSV transaction
val (signedSpendingTx, inputIndex) = buildSpendingTransaction(TransactionConstants.validLockVersion, creditingTx,
@ -456,41 +460,45 @@ trait TransactionGenerators extends BitcoinSLogger {
}
/**
* Determines if the transaction's lockTime value and CLTV script lockTime value are of the same type
* (i.e. determines whether both are a timestamp or block height)
*/
private def cltvLockTimesOfSameType(num: ScriptNumber, lockTime: UInt32) : Boolean = num.toLong match {
case negative if negative < 0 => false
case positive if positive >= 0 =>
if (!(
(lockTime < TransactionConstants.locktimeThreshold &&
num.toLong < TransactionConstants.locktimeThreshold.toLong) ||
(lockTime >= TransactionConstants.locktimeThreshold &&
num.toLong >= TransactionConstants.locktimeThreshold.toLong)
)) return false
true
* Determines if the transaction's lockTime value and CLTV script lockTime value are of the same type
* (i.e. determines whether both are a timestamp or block height)
*/
private def cltvLockTimesOfSameType(num: ScriptNumber, lockTime: UInt32): Boolean = num.toLong match {
case negative if negative < 0 => false
case positive if positive >= 0 =>
if (!(
(lockTime < TransactionConstants.locktimeThreshold &&
num.toLong < TransactionConstants.locktimeThreshold.toLong) ||
(lockTime >= TransactionConstants.locktimeThreshold &&
num.toLong >= TransactionConstants.locktimeThreshold.toLong)
)) return false
true
}
/**
* Determines if the transaction input's sequence value and CSV script sequence value are of the same type
* (i.e. determines whether both are a timestamp or block-height)
*/
private def csvLockTimesOfSameType(sequenceNumbers : (ScriptNumber, UInt32)) : Boolean = {
* Determines if the transaction input's sequence value and CSV script sequence value are of the same type
* (i.e. determines whether both are a timestamp or block-height)
*/
private def csvLockTimesOfSameType(sequenceNumbers: (ScriptNumber, UInt32)): Boolean = {
LockTimeInterpreter.isCSVLockByRelativeLockTime(sequenceNumbers._1, sequenceNumbers._2) ||
LockTimeInterpreter.isCSVLockByBlockHeight(sequenceNumbers._1, sequenceNumbers._2)
}
/**
* Generates a pair of CSV values: a transaction input sequence, and a CSV script sequence value, such that the txInput
* sequence mask is always greater than the script sequence mask (i.e. generates values for a validly constructed and spendable CSV transaction)
*/
def spendableCSVValues : Gen[(ScriptNumber, UInt32)] = {
Gen.oneOf(validScriptNumberAndSequenceForBlockHeight,
validScriptNumberAndSequenceForRelativeLockTime)
* Generates a pair of CSV values: a transaction input sequence, and a CSV script sequence value, such that the txInput
* sequence mask is always greater than the script sequence mask (i.e. generates values for a validly constructed and spendable CSV transaction)
*/
def spendableCSVValues: Gen[(ScriptNumber, UInt32)] = {
Gen.oneOf(
validScriptNumberAndSequenceForBlockHeight,
validScriptNumberAndSequenceForRelativeLockTime
)
}
/** To indicate that we should evaulate a OP_CSV operation based on
* blockheight we need 1 << 22 bit turned off. See BIP68 for more details */
/**
* To indicate that we should evaulate a OP_CSV operation based on
* blockheight we need 1 << 22 bit turned off. See BIP68 for more details
*/
private def lockByBlockHeightBitSet: UInt32 = UInt32("ffbfffff")
/** Generates a [[UInt32]] s.t. the block height bit is set according to BIP68 */
@ -501,7 +509,7 @@ trait TransactionGenerators extends BitcoinSLogger {
}
/** Generates a [[ScriptNumber]] and [[UInt32]] s.t. the pair can be spent by an OP_CSV operation */
private def validScriptNumberAndSequenceForBlockHeight: Gen[(ScriptNumber,UInt32)] = {
private def validScriptNumberAndSequenceForBlockHeight: Gen[(ScriptNumber, UInt32)] = {
sequenceForBlockHeight.flatMap { s =>
val seqMasked = TransactionConstants.sequenceLockTimeMask
val validScriptNums = s & seqMasked
@ -509,7 +517,7 @@ trait TransactionGenerators extends BitcoinSLogger {
val scriptNum = ScriptNumber(sn & lockByBlockHeightBitSet.toLong)
require(LockTimeInterpreter.isCSVLockByBlockHeight(scriptNum))
require(LockTimeInterpreter.isCSVLockByBlockHeight(s))
(scriptNum,s)
(scriptNum, s)
}
}
}
@ -522,7 +530,7 @@ trait TransactionGenerators extends BitcoinSLogger {
}
/** Generates a valid [[ScriptNumber]] and [[UInt32]] s.t. the pair will evaluate to true by a OP_CSV operation */
private def validScriptNumberAndSequenceForRelativeLockTime: Gen[(ScriptNumber,UInt32)] = {
private def validScriptNumberAndSequenceForRelativeLockTime: Gen[(ScriptNumber, UInt32)] = {
sequenceForRelativeLockTime.flatMap { s =>
val seqMasked = TransactionConstants.sequenceLockTimeMask
val validScriptNums = s & seqMasked
@ -530,7 +538,7 @@ trait TransactionGenerators extends BitcoinSLogger {
val scriptNum = ScriptNumber(sn | TransactionConstants.sequenceLockTimeTypeFlag.toLong)
require(LockTimeInterpreter.isCSVLockByRelativeLockTime(scriptNum))
require(LockTimeInterpreter.isCSVLockByRelativeLockTime(s))
(scriptNum,s)
(scriptNum, s)
}
}
}
@ -544,30 +552,30 @@ trait TransactionGenerators extends BitcoinSLogger {
}
/**
* Generates a pair of CSV values: a transaction input sequence, and a CSV script sequence value, such that the txInput
* sequence mask is always less than the script sequence mask (i.e. generates values for a validly constructed and NOT spendable CSV transaction).
*/
def unspendableCSVValues : Gen[(ScriptNumber, UInt32)] = ( for {
* Generates a pair of CSV values: a transaction input sequence, and a CSV script sequence value, such that the txInput
* sequence mask is always less than the script sequence mask (i.e. generates values for a validly constructed and NOT spendable CSV transaction).
*/
def unspendableCSVValues: Gen[(ScriptNumber, UInt32)] = (for {
sequence <- NumberGenerator.uInt32s
csvScriptNum <- NumberGenerator.uInt32s.map(x =>
ScriptNumber(x.toLong)).suchThat(x => LockTimeInterpreter.isLockTimeBitOff(x))
} yield (csvScriptNum, sequence)).suchThat(x => !csvLockTimesOfSameType(x))
/** generates a [[ScriptNumber]] and [[UInt32]] locktime for a transaction such that the tx will be spendable */
def spendableCLTVValues: Gen[(ScriptNumber,UInt32)] = for {
def spendableCLTVValues: Gen[(ScriptNumber, UInt32)] = for {
txLockTime <- NumberGenerator.uInt32s
cltvLockTime <- sameLockTimeTypeSpendable(txLockTime)
} yield (cltvLockTime,txLockTime)
} yield (cltvLockTime, txLockTime)
/** Generates a [[ScriptNumber]] and [[UInt32]] locktime for a transaction such that the tx will be unspendable */
def unspendableCLTVValues: Gen[(ScriptNumber,UInt32)] = for {
def unspendableCLTVValues: Gen[(ScriptNumber, UInt32)] = for {
txLockTime <- NumberGenerator.uInt32s
cltvLockTime <- sameLockTimeUnspendable(txLockTime)
} yield (cltvLockTime,txLockTime)
} yield (cltvLockTime, txLockTime)
private def sameLockTimeTypeSpendable(txLockTime: UInt32): Gen[ScriptNumber] = {
if (txLockTime < TransactionConstants.locktimeThreshold) {
Gen.choose(0,txLockTime.toLong).map(ScriptNumber(_))
Gen.choose(0, txLockTime.toLong).map(ScriptNumber(_))
} else {
Gen.choose(TransactionConstants.locktimeThreshold.toLong, txLockTime.toLong).map(ScriptNumber(_))
}
@ -575,9 +583,9 @@ trait TransactionGenerators extends BitcoinSLogger {
private def sameLockTimeUnspendable(txLockTime: UInt32): Gen[ScriptNumber] = {
if (txLockTime < TransactionConstants.locktimeThreshold) {
Gen.choose(txLockTime.toLong+1, TransactionConstants.locktimeThreshold.toLong).map(ScriptNumber(_))
Gen.choose(txLockTime.toLong + 1, TransactionConstants.locktimeThreshold.toLong).map(ScriptNumber(_))
} else {
Gen.choose(txLockTime.toLong+1, UInt32.max.toLong).map(ScriptNumber(_))
Gen.choose(txLockTime.toLong + 1, UInt32.max.toLong).map(ScriptNumber(_))
}
}
}

View file

@ -12,8 +12,8 @@ import org.bitcoins.core.wallet.EscrowTimeoutHelper
import org.scalacheck.Gen
/**
* Created by chris on 11/28/16.
*/
* Created by chris on 11/28/16.
*/
sealed abstract class WitnessGenerators extends BitcoinSLogger {
/** Generates a random [[org.bitcoins.core.protocol.script.ScriptWitness]] */
@ -36,16 +36,16 @@ sealed abstract class WitnessGenerators extends BitcoinSLogger {
val spk = if (spkBytes.isEmpty) EmptyScriptPubKey else NonStandardScriptPubKey(cmpctSPK.bytes ++ spkBytes)
P2WSHWitnessV0(spk,scriptSig)
}*/
Gen.oneOf(p2wpkhWitnessV0,p2wshWitnessV0)
Gen.oneOf(p2wpkhWitnessV0, p2wshWitnessV0)
}
/** Generates a [[TransactionWitness]] with the specified number of witnesses */
def transactionWitness(numWitnesses: Int): Gen[TransactionWitness] = for {
inputWitnesses <- Gen.listOfN(numWitnesses,Gen.option(scriptWitness))
inputWitnesses <- Gen.listOfN(numWitnesses, Gen.option(scriptWitness))
} yield TransactionWitness.fromWitOpt(inputWitnesses)
def transactionWitness: Gen[TransactionWitness] = for {
num <- Gen.choose(1,10)
num <- Gen.choose(1, 10)
wit <- transactionWitness(num)
} yield wit
@ -56,13 +56,12 @@ sealed abstract class WitnessGenerators extends BitcoinSLogger {
hashType <- CryptoGenerators.hashType
witScriptPubKey = P2WPKHWitnessSPKV0(privKey.publicKey)
unsignedScriptWitness = P2WPKHWitnessV0(privKey.publicKey)
unsignedWTxSigComponent = createUnsignedRawWTxSigComponent(witScriptPubKey,amount,
unsignedScriptWitness,None)
createdSig = TransactionSignatureCreator.createSig(unsignedWTxSigComponent,privKey, hashType)
unsignedWTxSigComponent = createUnsignedRawWTxSigComponent(witScriptPubKey, amount,
unsignedScriptWitness, None)
createdSig = TransactionSignatureCreator.createSig(unsignedWTxSigComponent, privKey, hashType)
scriptWitness = P2WPKHWitnessV0(privKey.publicKey, createdSig)
(witness,signedWtxSigComponent) = createSignedWTxComponent(scriptWitness,unsignedWTxSigComponent)
} yield (witness,signedWtxSigComponent,Seq(privKey))
(witness, signedWtxSigComponent) = createSignedWTxComponent(scriptWitness, unsignedWTxSigComponent)
} yield (witness, signedWtxSigComponent, Seq(privKey))
def signedP2WSHP2PKTransactionWitness: Gen[(TransactionWitness, WitnessTxSigComponentRaw, Seq[ECPrivateKey])] = for {
(scriptPubKey, privKeys) <- ScriptGenerators.p2pkScriptPubKey
@ -70,32 +69,30 @@ sealed abstract class WitnessGenerators extends BitcoinSLogger {
hashType <- CryptoGenerators.hashType
witScriptPubKey = P2WSHWitnessSPKV0(scriptPubKey)
unsignedScriptWitness = P2WSHWitnessV0(scriptPubKey)
u = createUnsignedRawWTxSigComponent(witScriptPubKey,amount,
unsignedScriptWitness,None)
createdSig = TransactionSignatureCreator.createSig(u,privKeys,hashType)
u = createUnsignedRawWTxSigComponent(witScriptPubKey, amount,
unsignedScriptWitness, None)
createdSig = TransactionSignatureCreator.createSig(u, privKeys, hashType)
signedScriptWitness = P2WSHWitnessV0(scriptPubKey, P2PKScriptSignature(createdSig))
oldTx = u.transaction
txWitness = TransactionWitness(oldTx.witness.witnesses.updated(u.inputIndex.toInt,signedScriptWitness))
wtx = WitnessTransaction(oldTx.version,oldTx.inputs,oldTx.outputs,oldTx.lockTime,txWitness)
signedWtxSigComponent = WitnessTxSigComponentRaw(wtx,u.inputIndex,witScriptPubKey,u.flags,u.amount)
} yield (txWitness,signedWtxSigComponent,Seq(privKeys))
txWitness = TransactionWitness(oldTx.witness.witnesses.updated(u.inputIndex.toInt, signedScriptWitness))
wtx = WitnessTransaction(oldTx.version, oldTx.inputs, oldTx.outputs, oldTx.lockTime, txWitness)
signedWtxSigComponent = WitnessTxSigComponentRaw(wtx, u.inputIndex, witScriptPubKey, u.flags, u.amount)
} yield (txWitness, signedWtxSigComponent, Seq(privKeys))
def signedP2WSHP2PKHTransactionWitness: Gen[(TransactionWitness, WitnessTxSigComponentRaw, Seq[ECPrivateKey])] = for {
def signedP2WSHP2PKHTransactionWitness: Gen[(TransactionWitness, WitnessTxSigComponentRaw, Seq[ECPrivateKey])] = for {
(scriptPubKey, privKey) <- ScriptGenerators.p2pkhScriptPubKey
amount <- CurrencyUnitGenerator.satoshis
hashType <- CryptoGenerators.hashType
witScriptPubKey = P2WSHWitnessSPKV0(scriptPubKey)
unsignedScriptWitness = P2WSHWitnessV0(scriptPubKey)
u = createUnsignedRawWTxSigComponent(witScriptPubKey,amount,unsignedScriptWitness,None)
createdSig = TransactionSignatureCreator.createSig(u,privKey,hashType)
signedScriptWitness = P2WSHWitnessV0(scriptPubKey, P2PKHScriptSignature(createdSig,privKey.publicKey))
u = createUnsignedRawWTxSigComponent(witScriptPubKey, amount, unsignedScriptWitness, None)
createdSig = TransactionSignatureCreator.createSig(u, privKey, hashType)
signedScriptWitness = P2WSHWitnessV0(scriptPubKey, P2PKHScriptSignature(createdSig, privKey.publicKey))
oldTx = u.transaction
txWitness = TransactionWitness(oldTx.witness.witnesses.updated(u.inputIndex.toInt,signedScriptWitness))
wtx = WitnessTransaction(oldTx.version,oldTx.inputs,oldTx.outputs,oldTx.lockTime,txWitness)
signedWtxSigComponent = WitnessTxSigComponentRaw(wtx,u.inputIndex,witScriptPubKey,u.flags,u.amount)
} yield (txWitness,signedWtxSigComponent,Seq(privKey))
txWitness = TransactionWitness(oldTx.witness.witnesses.updated(u.inputIndex.toInt, signedScriptWitness))
wtx = WitnessTransaction(oldTx.version, oldTx.inputs, oldTx.outputs, oldTx.lockTime, txWitness)
signedWtxSigComponent = WitnessTxSigComponentRaw(wtx, u.inputIndex, witScriptPubKey, u.flags, u.amount)
} yield (txWitness, signedWtxSigComponent, Seq(privKey))
def signedP2WSHMultiSigTransactionWitness: Gen[(TransactionWitness, WitnessTxSigComponentRaw, Seq[ECPrivateKey])] = for {
(scriptPubKey, privKeys) <- ScriptGenerators.multiSigScriptPubKey
@ -103,21 +100,23 @@ sealed abstract class WitnessGenerators extends BitcoinSLogger {
hashType <- CryptoGenerators.hashType
witScriptPubKey = P2WSHWitnessSPKV0(scriptPubKey)
unsignedScriptWitness = P2WSHWitnessV0(scriptPubKey)
u = createUnsignedRawWTxSigComponent(witScriptPubKey,amount,
unsignedScriptWitness,None)
u = createUnsignedRawWTxSigComponent(witScriptPubKey, amount,
unsignedScriptWitness, None)
signedScriptSig = multiSigScriptSigGenHelper(privKeys, scriptPubKey, u, hashType)
signedScriptWitness = P2WSHWitnessV0(scriptPubKey,signedScriptSig)
signedScriptWitness = P2WSHWitnessV0(scriptPubKey, signedScriptSig)
oldTx = u.transaction
txWitness = TransactionWitness(oldTx.witness.witnesses.updated(u.inputIndex.toInt,signedScriptWitness))
wtx = WitnessTransaction(oldTx.version,oldTx.inputs,oldTx.outputs,oldTx.lockTime,txWitness)
signedWtxSigComponent = WitnessTxSigComponentRaw(wtx,u.inputIndex,witScriptPubKey,u.flags,u.amount)
} yield (txWitness,signedWtxSigComponent,privKeys)
txWitness = TransactionWitness(oldTx.witness.witnesses.updated(u.inputIndex.toInt, signedScriptWitness))
wtx = WitnessTransaction(oldTx.version, oldTx.inputs, oldTx.outputs, oldTx.lockTime, txWitness)
signedWtxSigComponent = WitnessTxSigComponentRaw(wtx, u.inputIndex, witScriptPubKey, u.flags, u.amount)
} yield (txWitness, signedWtxSigComponent, privKeys)
/** Generates a random signed [[TransactionWitness]] with the corresponding [[WitnessTxSigComponent]]
* and [[ECPrivateKey]]s */
/**
* Generates a random signed [[TransactionWitness]] with the corresponding [[WitnessTxSigComponent]]
* and [[ECPrivateKey]]s
*/
def signedP2WSHTransactionWitness: Gen[(TransactionWitness, WitnessTxSigComponentRaw, Seq[ECPrivateKey])] = {
Gen.oneOf(signedP2WSHP2PKTransactionWitness, signedP2WSHP2PKHTransactionWitness,
signedP2WSHMultiSigTransactionWitness,signedP2WSHEscrowTimeoutWitness)
signedP2WSHMultiSigTransactionWitness, signedP2WSHEscrowTimeoutWitness)
}
def signedP2WSHMultiSigEscrowTimeoutWitness: Gen[(TransactionWitness, WitnessTxSigComponentRaw, Seq[ECPrivateKey])] = for {
@ -127,51 +126,54 @@ sealed abstract class WitnessGenerators extends BitcoinSLogger {
witScriptPubKey = P2WSHWitnessSPKV0(scriptPubKey)
unsignedScriptWitness = P2WSHWitnessV0(scriptPubKey)
u = createUnsignedRawWTxSigComponent(witScriptPubKey, amount,
unsignedScriptWitness,None)
signedScriptSig = csvEscrowTimeoutGenHelper(privKeys,scriptPubKey,u,hashType)
witness = EscrowTimeoutHelper.buildEscrowTimeoutScriptWitness(signedScriptSig,scriptPubKey,u)
unsignedScriptWitness, None)
signedScriptSig = csvEscrowTimeoutGenHelper(privKeys, scriptPubKey, u, hashType)
witness = EscrowTimeoutHelper.buildEscrowTimeoutScriptWitness(signedScriptSig, scriptPubKey, u)
oldTx = u.transaction
wTx = WitnessTransaction(oldTx.version,oldTx.inputs,oldTx.outputs,oldTx.lockTime,witness)
signedWTxSigComponent = WitnessTxSigComponentRaw(wTx,u.inputIndex,witScriptPubKey,u.flags,u.amount)
} yield (witness,signedWTxSigComponent,privKeys)
wTx = WitnessTransaction(oldTx.version, oldTx.inputs, oldTx.outputs, oldTx.lockTime, witness)
signedWTxSigComponent = WitnessTxSigComponentRaw(wTx, u.inputIndex, witScriptPubKey, u.flags, u.amount)
} yield (witness, signedWTxSigComponent, privKeys)
def spendableP2WSHTimeoutEscrowTimeoutWitness: Gen[(TransactionWitness, WitnessTxSigComponentRaw, Seq[ECPrivateKey])] = for {
(p2pkh,privKey) <- ScriptGenerators.p2pkhScriptPubKey
(p2pkh, privKey) <- ScriptGenerators.p2pkhScriptPubKey
(scriptNum, sequence) <- TransactionGenerators.spendableCSVValues
csv = CSVScriptPubKey(scriptNum,p2pkh)
(m,_) <- ScriptGenerators.smallMultiSigScriptPubKey
scriptPubKey = EscrowTimeoutScriptPubKey(m,csv)
csv = CSVScriptPubKey(scriptNum, p2pkh)
(m, _) <- ScriptGenerators.smallMultiSigScriptPubKey
scriptPubKey = EscrowTimeoutScriptPubKey(m, csv)
amount <- CurrencyUnitGenerator.satoshis
hashType <- CryptoGenerators.hashType
witScriptPubKey = P2WSHWitnessSPKV0(scriptPubKey)
unsignedScriptWitness = P2WSHWitnessV0(scriptPubKey)
u = createUnsignedRawWTxSigComponent(witScriptPubKey,
amount, unsignedScriptWitness,Some(sequence))
createdSig = TransactionSignatureCreator.createSig(u,privKey,hashType)
scriptSig = CSVScriptSignature(P2PKHScriptSignature(createdSig,privKey.publicKey))
signedScriptWitness = P2WSHWitnessV0(scriptPubKey,EscrowTimeoutScriptSignature.fromLockTime(scriptSig))
u = createUnsignedRawWTxSigComponent(
witScriptPubKey,
amount, unsignedScriptWitness, Some(sequence)
)
createdSig = TransactionSignatureCreator.createSig(u, privKey, hashType)
scriptSig = CSVScriptSignature(P2PKHScriptSignature(createdSig, privKey.publicKey))
signedScriptWitness = P2WSHWitnessV0(scriptPubKey, EscrowTimeoutScriptSignature.fromLockTime(scriptSig))
//ScriptWitness(scriptPubKey.asm.flatMap(_.bytes) +: Seq(ScriptNumber.zero.bytes, privKey.publicKey.bytes,
//createdSig.bytes))
oldTx = u.transaction
txWitness = TransactionWitness(oldTx.witness.witnesses.updated(u.inputIndex.toInt,signedScriptWitness))
wtx = WitnessTransaction(oldTx.version,oldTx.inputs,oldTx.outputs,oldTx.lockTime,txWitness)
signedWtxSigComponent = WitnessTxSigComponentRaw(wtx,u.inputIndex,witScriptPubKey,u.flags,u.amount)
} yield (txWitness,signedWtxSigComponent,Seq(privKey))
txWitness = TransactionWitness(oldTx.witness.witnesses.updated(u.inputIndex.toInt, signedScriptWitness))
wtx = WitnessTransaction(oldTx.version, oldTx.inputs, oldTx.outputs, oldTx.lockTime, txWitness)
signedWtxSigComponent = WitnessTxSigComponentRaw(wtx, u.inputIndex, witScriptPubKey, u.flags, u.amount)
} yield (txWitness, signedWtxSigComponent, Seq(privKey))
def signedP2WSHEscrowTimeoutWitness: Gen[(TransactionWitness, WitnessTxSigComponentRaw, Seq[ECPrivateKey])] = {
Gen.oneOf(signedP2WSHMultiSigEscrowTimeoutWitness, spendableP2WSHTimeoutEscrowTimeoutWitness)
}
/** Helps generate a signed [[MultiSignatureScriptSignature]] */
private def multiSigScriptSigGenHelper(privateKeys : Seq[ECPrivateKey],
scriptPubKey : MultiSignatureScriptPubKey,
unsignedWtxSigComponent: WitnessTxSigComponent,
hashType: HashType) : MultiSignatureScriptSignature = {
private def multiSigScriptSigGenHelper(
privateKeys: Seq[ECPrivateKey],
scriptPubKey: MultiSignatureScriptPubKey,
unsignedWtxSigComponent: WitnessTxSigComponent,
hashType: HashType
): MultiSignatureScriptSignature = {
val requiredSigs = scriptPubKey.requiredSigs
val txSignatures = for {
i <- 0 until requiredSigs
} yield TransactionSignatureCreator.createSig(unsignedWtxSigComponent,privateKeys(i), hashType)
} yield TransactionSignatureCreator.createSig(unsignedWtxSigComponent, privateKeys(i), hashType)
//add the signature to the scriptSig instead of having an empty scriptSig
val signedScriptSig = MultiSignatureScriptSignature(txSignatures)
@ -179,24 +181,23 @@ sealed abstract class WitnessGenerators extends BitcoinSLogger {
}
def csvEscrowTimeoutGenHelper(privateKeys: Seq[ECPrivateKey], scriptPubKey: EscrowTimeoutScriptPubKey,
unsignedWtxSigComponent: WitnessTxSigComponent,
hashType: HashType): EscrowTimeoutScriptSignature = {
unsignedWtxSigComponent: WitnessTxSigComponent,
hashType: HashType): EscrowTimeoutScriptSignature = {
if (scriptPubKey.escrow.requiredSigs == 0) {
EscrowTimeoutScriptSignature.fromMultiSig(MultiSignatureScriptSignature(Nil))
} else if (privateKeys.size == 1) {
val signature = csvEscrowTimeoutGenSignature(privateKeys.head, scriptPubKey, unsignedWtxSigComponent,hashType)
} else if (privateKeys.size == 1) {
val signature = csvEscrowTimeoutGenSignature(privateKeys.head, scriptPubKey, unsignedWtxSigComponent, hashType)
EscrowTimeoutScriptSignature.fromMultiSig(MultiSignatureScriptSignature(Seq(signature)))
} else {
val multiSig = multiSigScriptSigGenHelper(privateKeys,scriptPubKey.escrow,unsignedWtxSigComponent,hashType)
val multiSig = multiSigScriptSigGenHelper(privateKeys, scriptPubKey.escrow, unsignedWtxSigComponent, hashType)
EscrowTimeoutScriptSignature.fromMultiSig(multiSig)
}
}
def csvEscrowTimeoutGenSignature(privKey: ECPrivateKey, scriptPubKey: EscrowTimeoutScriptPubKey,
unsignedWtxSigComponent: WitnessTxSigComponent, hashType: HashType): ECDigitalSignature = {
unsignedWtxSigComponent: WitnessTxSigComponent, hashType: HashType): ECDigitalSignature = {
val signature = TransactionSignatureCreator.createSig(unsignedWtxSigComponent,privKey,hashType)
val signature = TransactionSignatureCreator.createSig(unsignedWtxSigComponent, privKey, hashType)
signature
}
@ -204,27 +205,27 @@ sealed abstract class WitnessGenerators extends BitcoinSLogger {
def p2wpkhWitnessV0: Gen[P2WPKHWitnessV0] = for {
publicKey <- CryptoGenerators.publicKey
sig <- CryptoGenerators.digitalSignature
} yield P2WPKHWitnessV0(publicKey,sig)
} yield P2WPKHWitnessV0(publicKey, sig)
/** Generates a random [[org.bitcoins.core.protocol.script.P2WSHWitnessV0]] */
def p2wshWitnessV0: Gen[P2WSHWitnessV0] = for {
(redeem,_) <- ScriptGenerators.scriptPubKey
(redeem, _) <- ScriptGenerators.scriptPubKey
scriptSig <- ScriptGenerators.scriptSignature
} yield P2WSHWitnessV0(redeem,scriptSig)
} yield P2WSHWitnessV0(redeem, scriptSig)
/** Takes a signed [[ScriptWitness]] and an unsignedTx and adds the witness to the unsigned [[WitnessTransaction]] */
def createSignedWTxComponent(witness: ScriptWitness, unsignedWTxComponent: WitnessTxSigComponent): (TransactionWitness,WitnessTxSigComponent) = {
def createSignedWTxComponent(witness: ScriptWitness, unsignedWTxComponent: WitnessTxSigComponent): (TransactionWitness, WitnessTxSigComponent) = {
val signedTxWitness = TransactionWitness.fromWitOpt(Seq(Some(witness)))
val unsignedSpendingTx = unsignedWTxComponent.transaction
val signedSpendingTx = WitnessTransaction(unsignedSpendingTx.version,unsignedSpendingTx.inputs,unsignedSpendingTx.outputs,
val signedSpendingTx = WitnessTransaction(unsignedSpendingTx.version, unsignedSpendingTx.inputs, unsignedSpendingTx.outputs,
unsignedSpendingTx.lockTime, signedTxWitness)
val signedWtxSigComponent = unsignedWTxComponent match {
case wtxP2SH: WitnessTxSigComponentP2SH =>
WitnessTxSigComponent(signedSpendingTx,unsignedWTxComponent.inputIndex,
wtxP2SH.scriptPubKey,unsignedWTxComponent.flags,unsignedWTxComponent.amount)
WitnessTxSigComponent(signedSpendingTx, unsignedWTxComponent.inputIndex,
wtxP2SH.scriptPubKey, unsignedWTxComponent.flags, unsignedWTxComponent.amount)
case wtxRaw: WitnessTxSigComponentRaw =>
WitnessTxSigComponent(signedSpendingTx,unsignedWTxComponent.inputIndex,
wtxRaw.scriptPubKey,unsignedWTxComponent.flags,unsignedWTxComponent.amount)
WitnessTxSigComponent(signedSpendingTx, unsignedWTxComponent.inputIndex,
wtxRaw.scriptPubKey, unsignedWTxComponent.flags, unsignedWTxComponent.amount)
}
(signedTxWitness, signedWtxSigComponent)
@ -236,11 +237,11 @@ sealed abstract class WitnessGenerators extends BitcoinSLogger {
val tc = TransactionConstants
val flags = Policy.standardScriptVerifyFlags
val witness = TransactionWitness.fromWitOpt(Seq(Some(unsignedScriptWitness)))
val (creditingTx,outputIndex) = TransactionGenerators.buildCreditingTransaction(witScriptPubKey,amount)
val (unsignedSpendingTx,inputIndex) = TransactionGenerators.buildSpendingTransaction(tc.validLockVersion,creditingTx,
val (creditingTx, outputIndex) = TransactionGenerators.buildCreditingTransaction(witScriptPubKey, amount)
val (unsignedSpendingTx, inputIndex) = TransactionGenerators.buildSpendingTransaction(tc.validLockVersion, creditingTx,
EmptyScriptSignature, outputIndex, tc.lockTime,
sequence.getOrElse(tc.sequence), witness)
val unsignedWtxSigComponent = WitnessTxSigComponentRaw(unsignedSpendingTx,inputIndex,witScriptPubKey,flags, amount)
val unsignedWtxSigComponent = WitnessTxSigComponentRaw(unsignedSpendingTx, inputIndex, witScriptPubKey, flags, amount)
unsignedWtxSigComponent
}
}

View file

@ -1,44 +1,46 @@
package org.bitcoins.core.number
import org.bitcoins.core.protocol.NetworkElement
import org.bitcoins.core.util.{BitcoinSUtil, Factory, NumberUtil}
import org.bitcoins.core.util.{ BitcoinSUtil, Factory, NumberUtil }
import scala.util.{Failure, Success, Try}
import scala.util.{ Failure, Success, Try }
/**
* Created by chris on 6/4/16.
*/
* Created by chris on 6/4/16.
*/
/** This abstract class is meant to represent a signed and unsigned number in C
* This is useful for dealing with codebases/protocols that rely on C's
* unsigned integer types
* */
/**
* This abstract class is meant to represent a signed and unsigned number in C
* This is useful for dealing with codebases/protocols that rely on C's
* unsigned integer types
*/
sealed abstract class Number[T <: Number[T]] extends NetworkElement {
type A = BigInt
/** The underlying scala number used to to hold the number */
protected def underlying : A
protected def underlying: A
def toInt : Int = toBigInt.bigInteger.intValueExact()
def toInt: Int = toBigInt.bigInteger.intValueExact()
def toLong: Long = toBigInt.bigInteger.longValueExact()
def toBigInt: BigInt = underlying
/** This is used to determine the valid amount of bytes in a number
* for instance a UInt8 has an andMask of 0xff
* a UInt32 has an andMask of 0xffffffff
*/
/**
* This is used to determine the valid amount of bytes in a number
* for instance a UInt8 has an andMask of 0xff
* a UInt32 has an andMask of 0xffffffff
*/
def andMask: BigInt
/** Factory function to create the underlying T, for instance a UInt32 */
def apply: A => T
def + (num: T): T = apply(checkResult(underlying + num.underlying))
def - (num: T): T = apply(checkResult(underlying - num.underlying))
def * (num: T): T = apply(checkResult(underlying * num.underlying))
def > (num: T): Boolean = underlying > num.underlying
def >= (num: T): Boolean = underlying >= num.underlying
def < (num: T): Boolean = underlying < num.underlying
def <= (num: T): Boolean = underlying <= num.underlying
def +(num: T): T = apply(checkResult(underlying + num.underlying))
def -(num: T): T = apply(checkResult(underlying - num.underlying))
def *(num: T): T = apply(checkResult(underlying * num.underlying))
def >(num: T): Boolean = underlying > num.underlying
def >=(num: T): Boolean = underlying >= num.underlying
def <(num: T): Boolean = underlying < num.underlying
def <=(num: T): Boolean = underlying <= num.underlying
def << (num: Int): T = this.<<(apply(num))
def >> (num: Int): T = this.>>(apply(num))
def <<(num: Int): T = this.<<(apply(num))
def >>(num: Int): T = this.>>(apply(num))
def <<(num: T): T = {
checkIfInt(num).map { _ =>
@ -57,8 +59,8 @@ sealed abstract class Number[T <: Number[T]] extends NetworkElement {
}
}
def | (num: T): T = apply(checkResult(underlying | num.underlying))
def & (num: T): T = apply(checkResult(underlying & num.underlying))
def |(num: T): T = apply(checkResult(underlying | num.underlying))
def &(num: T): T = apply(checkResult(underlying & num.underlying))
def unary_- : T = apply(-underlying)
private def checkResult(result: BigInt): A = {
require((result & andMask) == result, "Result was out of bounds, got: " + result)
@ -78,66 +80,65 @@ sealed abstract class Number[T <: Number[T]] extends NetworkElement {
}
/**
* Represents a signed number in our number system
* Instances of this are [[Int32]] or [[Int64]]
*/
* Represents a signed number in our number system
* Instances of this are [[Int32]] or [[Int64]]
*/
sealed abstract class SignedNumber[T <: Number[T]] extends Number[T]
/**
* Represents an unsigned number in our number system
* Instances of this are [[UInt32]] or [[UInt64]]
*/
* Represents an unsigned number in our number system
* Instances of this are [[UInt32]] or [[UInt64]]
*/
sealed abstract class UnsignedNumber[T <: Number[T]] extends Number[T]
sealed abstract class UInt8 extends UnsignedNumber[UInt8] {
override def apply: A => UInt8 = UInt8(_)
override def hex = BitcoinSUtil.encodeHex(toInt.toShort).slice(2,4)
override def hex = BitcoinSUtil.encodeHex(toInt.toShort).slice(2, 4)
override def andMask = 0xff
}
/**
* Represents a uint32_t in C
*/
* Represents a uint32_t in C
*/
sealed abstract class UInt32 extends UnsignedNumber[UInt32] {
override def apply: A => UInt32 = UInt32(_)
override def hex = BitcoinSUtil.encodeHex(toLong).slice(8,16)
override def hex = BitcoinSUtil.encodeHex(toLong).slice(8, 16)
override def andMask = 0xffffffffL
}
/**
* Represents a uint64_t in C
*/
* Represents a uint64_t in C
*/
sealed abstract class UInt64 extends UnsignedNumber[UInt64] {
override def hex = encodeHex(underlying)
override def apply: A => UInt64 = UInt64(_)
override def andMask = 0xffffffffffffffffL
/**
* The converts a [[BigInt]] to a 8 byte hex representation
* [[BigInt]] will only allocate 1 byte for numbers like 1 which require 1 byte, giving us the hex representation 01
* this function pads the hex chars to be 0000000000000001
* @param bigInt
* @return
*/
private def encodeHex(bigInt : BigInt): String = {
* The converts a [[BigInt]] to a 8 byte hex representation
* [[BigInt]] will only allocate 1 byte for numbers like 1 which require 1 byte, giving us the hex representation 01
* this function pads the hex chars to be 0000000000000001
* @param bigInt
* @return
*/
private def encodeHex(bigInt: BigInt): String = {
val hex = BitcoinSUtil.encodeHex(bigInt)
if (hex.length == 18) {
//means that encodeHex(BigInt) padded an extra byte, giving us 9 bytes instead of 8
hex.slice(2,hex.length)
hex.slice(2, hex.length)
} else {
val padding = for { _ <- 0 until 16 - hex.length} yield "0"
val padding = for { _ <- 0 until 16 - hex.length } yield "0"
padding.mkString ++ hex
}
}
}
/**
* Represents a int32_t in C
*/
* Represents a int32_t in C
*/
sealed abstract class Int32 extends SignedNumber[Int32] {
override def apply: A => Int32 = Int32(_)
override def andMask = 0xffffffff
@ -145,8 +146,8 @@ sealed abstract class Int32 extends SignedNumber[Int32] {
}
/**
* Represents a int64_t in C
*/
* Represents a int64_t in C
*/
sealed abstract class Int64 extends SignedNumber[Int64] {
override def apply: A => Int64 = Int64(_)
override def andMask = 0xffffffffffffffffL
@ -154,14 +155,14 @@ sealed abstract class Int64 extends SignedNumber[Int64] {
}
/**
* Represents various numbers that should be implemented
* inside of any companion object for a number
*/
* Represents various numbers that should be implemented
* inside of any companion object for a number
*/
trait BaseNumbers[T] {
def zero : T
def one : T
def min : T
def max : T
def zero: T
def one: T
def min: T
def max: T
}
object UInt8 extends Factory[UInt8] with BaseNumbers[UInt8] {
@ -180,10 +181,10 @@ object UInt8 extends Factory[UInt8] with BaseNumbers[UInt8] {
def apply(bigint: BigInt): UInt8 = UInt8Impl(bigint)
def isValid(bigInt: BigInt): Boolean = bigInt >= 0 && bigInt < 256
def isValid(bigInt: BigInt): Boolean = bigInt >= 0 && bigInt < 256
override def fromBytes(bytes: Seq[Byte]): UInt8 = {
require(bytes.size == 1,"Can only create a uint8 from a byte array of size one, got: " + bytes)
require(bytes.size == 1, "Can only create a uint8 from a byte array of size one, got: " + bytes)
val res = NumberUtil.toUnsignedInt(bytes)
checkBounds(res)
}
@ -206,7 +207,7 @@ object UInt8 extends Factory[UInt8] with BaseNumbers[UInt8] {
}
object UInt32 extends Factory[UInt32] with BaseNumbers[UInt32] {
private case class UInt32Impl(underlying : BigInt) extends UInt32 {
private case class UInt32Impl(underlying: BigInt) extends UInt32 {
require(underlying >= 0, "We cannot have a negative number in an unsigned number, got: " + underlying)
require(underlying <= 4294967295L, "We cannot have a number larger than 2^32 -1 in UInt32, got: " + underlying)
}
@ -223,9 +224,9 @@ object UInt32 extends Factory[UInt32] with BaseNumbers[UInt32] {
checkBounds(res)
}
def apply(long : Long): UInt32 = UInt32(BigInt(long))
def apply(long: Long): UInt32 = UInt32(BigInt(long))
def apply(bigInt: BigInt): UInt32 = UInt32Impl(bigInt)
def apply(bigInt: BigInt): UInt32 = UInt32Impl(bigInt)
def checkBounds(res: BigInt): UInt32 = {
if (res > max.underlying || res < min.underlying) {
@ -236,7 +237,7 @@ object UInt32 extends Factory[UInt32] with BaseNumbers[UInt32] {
}
object UInt64 extends Factory[UInt64] with BaseNumbers[UInt64] {
private case class UInt64Impl(underlying : BigInt) extends UInt64 {
private case class UInt64Impl(underlying: BigInt) extends UInt64 {
require(underlying >= 0, "We cannot have a negative number in an unsigned number: " + underlying)
require(underlying <= BigInt("18446744073709551615"), "We cannot have a number larger than 2^64 -1 in UInt64, got: " + underlying)
}
@ -255,12 +256,12 @@ object UInt64 extends Factory[UInt64] with BaseNumbers[UInt64] {
} else UInt64(res)
}
def apply(num : BigInt): UInt64 = UInt64Impl(num)
def apply(num: BigInt): UInt64 = UInt64Impl(num)
}
object Int32 extends Factory[Int32] with BaseNumbers[Int32] {
private case class Int32Impl(underlying : BigInt) extends Int32 {
private case class Int32Impl(underlying: BigInt) extends Int32 {
require(underlying >= -2147483648, "Number was too small for a int32, got: " + underlying)
require(underlying <= 2147483647, "Number was too large for a int32, got: " + underlying)
}
@ -271,18 +272,18 @@ object Int32 extends Factory[Int32] with BaseNumbers[Int32] {
lazy val min = Int32(-2147483648)
lazy val max = Int32(2147483647)
override def fromBytes(bytes : Seq[Byte]): Int32 = {
override def fromBytes(bytes: Seq[Byte]): Int32 = {
require(bytes.size <= 4, "We cannot have an Int32 be larger than 4 bytes")
Int32(BigInt(bytes.toArray).toInt)
}
def apply(int : Int): Int32 = Int32(BigInt(int))
def apply(int: Int): Int32 = Int32(BigInt(int))
def apply(bigInt: BigInt): Int32 = Int32Impl(bigInt)
}
object Int64 extends Factory[Int64] with BaseNumbers[Int64] {
private case class Int64Impl(underlying : BigInt) extends Int64 {
private case class Int64Impl(underlying: BigInt) extends Int64 {
require(underlying >= -9223372036854775808L, "Number was too small for a int64, got: " + underlying)
require(underlying <= 9223372036854775807L, "Number was too big for a int64, got: " + underlying)
}
@ -293,12 +294,12 @@ object Int64 extends Factory[Int64] with BaseNumbers[Int64] {
lazy val min = Int64(-9223372036854775808L)
lazy val max = Int64(9223372036854775807L)
override def fromBytes(bytes : Seq[Byte]): Int64 = {
override def fromBytes(bytes: Seq[Byte]): Int64 = {
require(bytes.size <= 8, "We cannot have an Int64 be larger than 8 bytes")
Int64(BigInt(bytes.toArray).toLong)
}
def apply(long : Long): Int64 = Int64(BigInt(long))
def apply(long: Long): Int64 = Int64(BigInt(long))
def apply(bigInt: BigInt): Int64 = Int64Impl(bigInt)
}

View file

@ -1,6 +1,6 @@
package org.bitcoins.core.policy
import org.bitcoins.core.currency.{CurrencyUnit, CurrencyUnits, Satoshis}
import org.bitcoins.core.currency.{ CurrencyUnit, CurrencyUnits, Satoshis }
import org.bitcoins.core.number.Int64
import org.bitcoins.core.script.flag._
@ -22,9 +22,11 @@ sealed abstract class Policy {
*/
def mandatoryScriptVerifyFlags: Seq[ScriptFlag] = Seq(ScriptVerifyP2SH)
/** The default script verify flags used to validate the blockchain
* and bitcoin transactions */
def standardScriptVerifyFlags : Seq[ScriptFlag] = mandatoryScriptVerifyFlags ++ Seq(ScriptVerifyDerSig, ScriptVerifyStrictEnc,
/**
* The default script verify flags used to validate the blockchain
* and bitcoin transactions
*/
def standardScriptVerifyFlags: Seq[ScriptFlag] = mandatoryScriptVerifyFlags ++ Seq(ScriptVerifyDerSig, ScriptVerifyStrictEnc,
ScriptVerifyMinimalData, ScriptVerifyDiscourageUpgradableNOPs,
ScriptVerifyCleanStack, ScriptVerifyCheckLocktimeVerify, ScriptVerifyCheckSequenceVerify,
ScriptVerifyLowS, ScriptVerifyWitness, ScriptVerifyMinimalIf, ScriptVerifyNullFail,
@ -35,10 +37,11 @@ sealed abstract class Policy {
/** The number of confirmations for a payment to be considered as accepted */
def confirmations: Long = 6
/** Minimum amount of [[org.bitcoins.core.currency.CurrencyUnit]]
* lock in a [[org.bitcoins.core.channels.Channel]]
* Currently set to 1 mBTC
* */
/**
* Minimum amount of [[org.bitcoins.core.currency.CurrencyUnit]]
* lock in a [[org.bitcoins.core.channels.Channel]]
* Currently set to 1 mBTC
*/
def minChannelAmount: CurrencyUnit = CurrencyUnits.oneMBTC
/** The minimum amount of satoshis we can spend to an output */

View file

@ -1,8 +1,8 @@
package org.bitcoins.core.protocol
import org.bitcoins.core.config._
import org.bitcoins.core.config.{MainNet, RegTest, TestNet3}
import org.bitcoins.core.crypto.{ECPublicKey, HashDigest, Sha256Digest, Sha256Hash160Digest}
import org.bitcoins.core.number.{UInt32, UInt8}
import org.bitcoins.core.config.{ MainNet, RegTest, TestNet3 }
import org.bitcoins.core.crypto.{ ECPublicKey, HashDigest, Sha256Digest, Sha256Hash160Digest }
import org.bitcoins.core.number.{ UInt32, UInt8 }
import org.bitcoins.core.protocol.transaction.TransactionOutput
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.script.constant.ScriptConstant
@ -10,7 +10,7 @@ import org.bitcoins.core.serializers.script.ScriptParser
import org.bitcoins.core.util._
import scala.annotation.tailrec
import scala.util.{Failure, Success, Try}
import scala.util.{ Failure, Success, Try }
sealed abstract class Address {
@ -18,7 +18,7 @@ sealed abstract class Address {
def networkParameters: NetworkParameters
/** The string representation of this address */
def value : String
def value: String
/** Every address is derived from a [[HashDigest]] in a [[TransactionOutput]] */
def hash: HashDigest
@ -31,7 +31,7 @@ sealed abstract class BitcoinAddress extends Address
sealed abstract class P2PKHAddress extends BitcoinAddress {
/** The base58 string representation of this address */
override def value : String = {
override def value: String = {
val versionByte = networkParameters.p2pkhNetworkByte
val bytes = versionByte ++ hash.bytes
val checksum = CryptoUtil.doubleSHA256(bytes).bytes.take(4)
@ -46,7 +46,7 @@ sealed abstract class P2PKHAddress extends BitcoinAddress {
sealed abstract class P2SHAddress extends BitcoinAddress {
/** The base58 string representation of this address */
override def value : String = {
override def value: String = {
val versionByte = networkParameters.p2shNetworkByte
val bytes = versionByte ++ hash.bytes
val checksum = CryptoUtil.doubleSHA256(bytes).bytes.take(4)
@ -59,8 +59,8 @@ sealed abstract class P2SHAddress extends BitcoinAddress {
}
/**
* https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki
*/
* https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki
*/
sealed abstract class Bech32Address extends BitcoinAddress {
private def logger = BitcoinSLogger.logger
@ -72,7 +72,7 @@ sealed abstract class Bech32Address extends BitcoinAddress {
override def networkParameters = hrp.network.get
override def value: String = {
val checksum = Bech32Address.createChecksum(hrp,data)
val checksum = Bech32Address.createChecksum(hrp, data)
val all = data ++ checksum
val encoding = Bech32Address.encodeToString(all)
hrp.toString + Bech32Address.separator + encoding
@ -90,7 +90,7 @@ sealed abstract class Bech32Address extends BitcoinAddress {
object Bech32Address {
private case class Bech32AddressImpl(hrp: HumanReadablePart, data: Seq[UInt8]) extends Bech32Address {
verifyChecksum(hrp,UInt8.toBytes(data))
verifyChecksum(hrp, UInt8.toBytes(data))
}
/** Separator used to separate the hrp & data parts of a bech32 addr */
@ -98,29 +98,30 @@ object Bech32Address {
private val logger = BitcoinSLogger.logger
def apply(witSPK: WitnessScriptPubKey,
networkParameters: NetworkParameters): Try[Bech32Address] = {
def apply(
witSPK: WitnessScriptPubKey,
networkParameters: NetworkParameters
): Try[Bech32Address] = {
//we don't encode the wit version or pushop for program into base5
val prog = UInt8.toUInt8s(witSPK.asmBytes.tail.tail)
val encoded = Bech32Address.encode(prog)
val hrp = networkParameters match {
case _: MainNet => bc
case _: MainNet => bc
case _: TestNet3 | _: RegTest => tb
}
val witVersion = witSPK.witnessVersion.version.toLong.toShort
encoded.map(e => Bech32Address(hrp,Seq(UInt8(witVersion)) ++ e))
encoded.map(e => Bech32Address(hrp, Seq(UInt8(witVersion)) ++ e))
}
def apply(hrp: HumanReadablePart, data: Seq[UInt8]): Bech32Address = {
Bech32AddressImpl(hrp,data)
Bech32AddressImpl(hrp, data)
}
/** Returns a base 5 checksum as specified by BIP173 */
def createChecksum(hrp: HumanReadablePart, bytes: Seq[UInt8]): Seq[UInt8] = {
val values: Seq[UInt8] = hrpExpand(hrp) ++ bytes
val z = UInt8.zero
val polymod: Long = polyMod(values ++ Seq(z,z,z,z,z,z)) ^ 1
val polymod: Long = polyMod(values ++ Seq(z, z, z, z, z, z)) ^ 1
//[(polymod >> 5 * (5 - i)) & 31 for i in range(6)]
val result: Seq[UInt8] = 0.until(6).map { i =>
val u = UInt8(i.toShort)
@ -144,9 +145,11 @@ object Bech32Address {
result
}
private def generators: Seq[Long] = Seq(UInt32("3b6a57b2").toLong,
private def generators: Seq[Long] = Seq(
UInt32("3b6a57b2").toLong,
UInt32("26508e6d").toLong, UInt32("1ea119fa").toLong,
UInt32("3d4233dd").toLong, UInt32("2a1462b3").toLong)
UInt32("3d4233dd").toLong, UInt32("2a1462b3").toLong
)
def polyMod(bytes: Seq[UInt8]): Long = {
var chk: Long = 1
@ -166,7 +169,7 @@ object Bech32Address {
def verifyChecksum(hrp: HumanReadablePart, data: Seq[Byte]): Boolean = {
val u8s = UInt8.toUInt8s(data)
verifyCheckSum(hrp,u8s)
verifyCheckSum(hrp, u8s)
}
def verifyCheckSum(hrp: HumanReadablePart, u8s: Seq[UInt8]): Boolean = {
@ -178,33 +181,34 @@ object Bech32Address {
/** Converts a byte array from base 8 to base 5 */
def encode(bytes: Seq[UInt8]): Try[Seq[UInt8]] = {
NumberUtil.convertUInt8s(bytes,u32Eight,u32Five,true)
NumberUtil.convertUInt8s(bytes, u32Eight, u32Five, true)
}
/** Decodes a byte array from base 5 to base 8 */
def decodeToBase8(b: Seq[UInt8]): Try[Seq[UInt8]] = {
NumberUtil.convertUInt8s(b,u32Five,u32Eight,false)
NumberUtil.convertUInt8s(b, u32Five, u32Eight, false)
}
/** Tries to convert the given string a to a [[org.bitcoins.core.protocol.script.WitnessScriptPubKey]] */
def fromStringToWitSPK(string: String): Try[WitnessScriptPubKey] = {
val decoded = fromString(string)
decoded.flatMap { case (_,bytes) =>
val (v,prog) = (bytes.head,bytes.tail)
val convertedProg = NumberUtil.convertBytes(prog,u32Five,u32Eight,false)
val progBytes = convertedProg.map(UInt8.toBytes(_))
val witVersion = WitnessVersion(v)
progBytes.flatMap { prog =>
val pushOp = BitcoinScriptUtil.calculatePushOp(prog)
witVersion match {
case Some(v) =>
WitnessScriptPubKey(Seq(v.version) ++ pushOp ++ Seq(ScriptConstant(prog))) match {
case Some(spk) => Success(spk)
case None => Failure(new IllegalArgumentException("Failed to decode bech32 into a witSPK"))
}
case None => Failure(new IllegalArgumentException("Witness version was not valid, got: " + v))
}
decoded.flatMap {
case (_, bytes) =>
val (v, prog) = (bytes.head, bytes.tail)
val convertedProg = NumberUtil.convertBytes(prog, u32Five, u32Eight, false)
val progBytes = convertedProg.map(UInt8.toBytes(_))
val witVersion = WitnessVersion(v)
progBytes.flatMap { prog =>
val pushOp = BitcoinScriptUtil.calculatePushOp(prog)
witVersion match {
case Some(v) =>
WitnessScriptPubKey(Seq(v.version) ++ pushOp ++ Seq(ScriptConstant(prog))) match {
case Some(spk) => Success(spk)
case None => Failure(new IllegalArgumentException("Failed to decode bech32 into a witSPK"))
}
case None => Failure(new IllegalArgumentException("Witness version was not valid, got: " + v))
}
}
}
}
}
/** Takes a base32 byte array and encodes it to a string */
@ -212,7 +216,7 @@ object Bech32Address {
b.map(b => charset(b.toInt)).mkString
}
/** Decodes bech32 string to the [[HumanReadablePart]] & data part */
def fromString(str: String): Try[(HumanReadablePart,Seq[Byte])] = {
def fromString(str: String): Try[(HumanReadablePart, Seq[Byte])] = {
val sepIndexes = str.zipWithIndex.filter(_._1 == separator)
if (str.size > 90 || str.size < 8) {
Failure(new IllegalArgumentException("bech32 payloads must be betwee 8 and 90 chars, got: " + str.size))
@ -220,7 +224,7 @@ object Bech32Address {
Failure(new IllegalArgumentException("Bech32 address did not have the correct separator"))
} else {
val sepIndex = sepIndexes.last._2
val (hrp,data) = (str.take(sepIndex), str.splitAt(sepIndex + 1)._2)
val (hrp, data) = (str.take(sepIndex), str.splitAt(sepIndex + 1)._2)
if (hrp.size < 1 || data.size < 6) {
Failure(new IllegalArgumentException("Hrp/data too short"))
} else {
@ -228,15 +232,14 @@ object Bech32Address {
val dataValid = checkDataValidity(data)
val isChecksumValid: Try[Seq[Byte]] = hrpValid.flatMap { h =>
dataValid.flatMap { d =>
if (verifyChecksum(h,d)) {
if (verifyChecksum(h, d)) {
if (d.size < 6) Success(Nil)
else Success(d.take(d.size - 6))
}
else Failure(new IllegalArgumentException("Checksum was invalid on the bech32 address"))
} else Failure(new IllegalArgumentException("Checksum was invalid on the bech32 address"))
}
}
isChecksumValid.flatMap { d =>
hrpValid.map(h => (h,d))
hrpValid.map(h => (h, d))
}
}
}
@ -252,7 +255,7 @@ object Bech32Address {
} else if (isLower && isUpper) {
Failure(new IllegalArgumentException("HRP had mixed case, got: " + hrp))
} else {
loop(t,UInt8(h.toByte) +: accum, h.isLower || isLower, h.isUpper || isUpper)
loop(t, UInt8(h.toByte) +: accum, h.isLower || isLower, h.isUpper || isUpper)
}
case Nil =>
if (isLower && isUpper) {
@ -262,14 +265,15 @@ object Bech32Address {
}
}
loop(hrp.toCharArray.toList,Nil,false,false).flatMap { _ =>
loop(hrp.toCharArray.toList, Nil, false, false).flatMap { _ =>
Success(HumanReadablePart(hrp.toLowerCase))
}
}
/** Takes in the data portion of a bech32 address and decodes it to a byte array
* It also checks the validity of the data portion according to BIP173
*/
/**
* Takes in the data portion of a bech32 address and decodes it to a byte array
* It also checks the validity of the data portion according to BIP173
*/
def checkDataValidity(data: String): Try[Seq[Byte]] = {
@tailrec
def loop(remaining: List[Char], accum: Seq[Byte], hasUpper: Boolean, hasLower: Boolean): Try[Seq[Byte]] = remaining match {
@ -283,11 +287,11 @@ object Bech32Address {
} else {
val byte = charset.indexOf(h.toLower).toByte
require(byte >= 0 && byte < 32, "Not in valid range, got: " + byte)
loop(t, byte +: accum, h.isUpper || hasUpper, h.isLower || hasLower)
loop(t, byte +: accum, h.isUpper || hasUpper, h.isLower || hasLower)
}
}
}
val payload: Try[Seq[Byte]] = loop(data.toCharArray.toList,Nil,false,false)
val payload: Try[Seq[Byte]] = loop(data.toCharArray.toList, Nil, false, false)
payload
}
@ -299,25 +303,27 @@ object Bech32Address {
}
object P2PKHAddress {
private case class P2PKHAddressImpl(hash: Sha256Hash160Digest,
networkParameters: NetworkParameters) extends P2PKHAddress {
private case class P2PKHAddressImpl(
hash: Sha256Hash160Digest,
networkParameters: NetworkParameters
) extends P2PKHAddress {
require(isP2PKHAddress(value), "Bitcoin address was invalid " + value)
}
def apply(hash: Sha256Hash160Digest, network: NetworkParameters): P2PKHAddress = P2PKHAddressImpl(hash,network)
def apply(hash: Sha256Hash160Digest, network: NetworkParameters): P2PKHAddress = P2PKHAddressImpl(hash, network)
def apply(pubKey: ECPublicKey, networkParameters: NetworkParameters): P2PKHAddress = {
val hash = CryptoUtil.sha256Hash160(pubKey.bytes)
P2PKHAddress(hash,networkParameters)
P2PKHAddress(hash, networkParameters)
}
def apply(spk: P2PKHScriptPubKey, networkParameters: NetworkParameters): P2PKHAddress = {
P2PKHAddress(spk.pubKeyHash,networkParameters)
P2PKHAddress(spk.pubKeyHash, networkParameters)
}
/** Checks if an address is a valid p2pkh address */
def isP2PKHAddress(address : String) : Boolean = {
val decodeCheckP2PKH : Try[Seq[Byte]] = Base58.decodeCheck(address)
def isP2PKHAddress(address: String): Boolean = {
val decodeCheckP2PKH: Try[Seq[Byte]] = Base58.decodeCheck(address)
decodeCheckP2PKH match {
case Success(bytes) =>
Networks.p2pkhNetworkBytes.find(bs => bytes.startsWith(bs)).isDefined
@ -326,32 +332,34 @@ object P2PKHAddress {
}
/** Checks if an address is a valid p2pkh address */
def isP2PKHAddress(address : BitcoinAddress) : Boolean = isP2PKHAddress(address.value)
def isP2PKHAddress(address: BitcoinAddress): Boolean = isP2PKHAddress(address.value)
}
object P2SHAddress {
private case class P2SHAddressImpl(hash: Sha256Hash160Digest,
networkParameters: NetworkParameters) extends P2SHAddress {
private case class P2SHAddressImpl(
hash: Sha256Hash160Digest,
networkParameters: NetworkParameters
) extends P2SHAddress {
require(isP2SHAddress(value), "Bitcoin address was invalid " + value)
}
/** Creates a [[P2SHScriptPubKey]] from the given [[ScriptPubKey]],
* then creates an address from that [[P2SHScriptPubKey]] */
def apply(scriptPubKey: ScriptPubKey,network: NetworkParameters): P2SHAddress = {
/**
* Creates a [[P2SHScriptPubKey]] from the given [[ScriptPubKey]],
* then creates an address from that [[P2SHScriptPubKey]]
*/
def apply(scriptPubKey: ScriptPubKey, network: NetworkParameters): P2SHAddress = {
val p2shScriptPubKey = P2SHScriptPubKey(scriptPubKey)
P2SHAddress(p2shScriptPubKey,network)
P2SHAddress(p2shScriptPubKey, network)
}
def apply(p2shScriptPubKey: P2SHScriptPubKey, network: NetworkParameters): P2SHAddress = P2SHAddress(p2shScriptPubKey.scriptHash,network)
def apply(p2shScriptPubKey: P2SHScriptPubKey, network: NetworkParameters): P2SHAddress = P2SHAddress(p2shScriptPubKey.scriptHash, network)
def apply(hash: Sha256Hash160Digest, network: NetworkParameters): P2SHAddress = P2SHAddressImpl(hash, network)
/** Checks if a address is a valid p2sh address */
def isP2SHAddress(address : String) : Boolean = {
val decodeCheckP2SH : Try[Seq[Byte]] = Base58.decodeCheck(address)
def isP2SHAddress(address: String): Boolean = {
val decodeCheckP2SH: Try[Seq[Byte]] = Base58.decodeCheck(address)
decodeCheckP2SH match {
case Success(bytes) =>
Networks.p2shNetworkBytes.find(bs => bytes.startsWith(bs)).isDefined
@ -360,7 +368,7 @@ object P2SHAddress {
}
/** Checks if a address is a valid p2sh address */
def isP2SHAddress(address : BitcoinAddress) : Boolean = isP2SHAddress(address.value)
def isP2SHAddress(address: BitcoinAddress): Boolean = isP2SHAddress(address.value)
}
@ -372,7 +380,6 @@ object BitcoinAddress {
decodeChecked.isSuccess
}
/** Creates a [[BitcoinAddress]] from the given base58 string value */
def apply(value: String): BitcoinAddress = {
val decodeChecked = Base58.decodeCheck(value)
@ -380,10 +387,9 @@ object BitcoinAddress {
case Success(bytes) =>
val network: Option[(NetworkParameters, Seq[Byte])] = matchNetwork(bytes)
if (network.isDefined && P2PKHAddress.isP2PKHAddress(value)) {
P2PKHAddress(Sha256Hash160Digest(network.get._2),network.get._1)
}
else if (network.isDefined && P2SHAddress.isP2SHAddress(value)) {
P2SHAddress(Sha256Hash160Digest(network.get._2),network.get._1)
P2PKHAddress(Sha256Hash160Digest(network.get._2), network.get._1)
} else if (network.isDefined && P2SHAddress.isP2SHAddress(value)) {
P2SHAddress(Sha256Hash160Digest(network.get._2), network.get._1)
} else throw new IllegalArgumentException("The address was not a p2pkh or p2sh address, got: " + value)
case Failure(exception) =>
throw exception
@ -409,28 +415,28 @@ object BitcoinAddress {
object Address {
def fromBytes(bytes : Seq[Byte]) : Try[Address] = {
def fromBytes(bytes: Seq[Byte]): Try[Address] = {
val encoded = Base58.encode(bytes)
Try(BitcoinAddress(encoded))
}
def fromHex(hex : String) : Try[Address] = fromBytes(BitcoinSUtil.decodeHex(hex))
def fromHex(hex: String): Try[Address] = fromBytes(BitcoinSUtil.decodeHex(hex))
def apply(bytes: Seq[Byte]): Try[Address] = fromBytes(bytes)
def apply(str : String) : Try[Address] = Try(BitcoinAddress(str))
def apply(str: String): Try[Address] = Try(BitcoinAddress(str))
def fromScriptPubKey(spk: ScriptPubKey, network: NetworkParameters): Try[BitcoinAddress] = spk match {
case p2pkh: P2PKHScriptPubKey => Success(P2PKHAddress(p2pkh,network))
case p2sh: P2SHScriptPubKey => Success(P2SHAddress(p2sh,network))
case witSPK: WitnessScriptPubKey => Bech32Address(witSPK,network)
case p2pkh: P2PKHScriptPubKey => Success(P2PKHAddress(p2pkh, network))
case p2sh: P2SHScriptPubKey => Success(P2SHAddress(p2sh, network))
case witSPK: WitnessScriptPubKey => Bech32Address(witSPK, network)
case x @ (_: P2PKScriptPubKey | _: MultiSignatureScriptPubKey | _: LockTimeScriptPubKey
| _: EscrowTimeoutScriptPubKey | _: NonStandardScriptPubKey
| _: WitnessCommitment | _: UnassignedWitnessScriptPubKey | EmptyScriptPubKey) =>
| _: EscrowTimeoutScriptPubKey | _: NonStandardScriptPubKey
| _: WitnessCommitment | _: UnassignedWitnessScriptPubKey | EmptyScriptPubKey) =>
Failure(new IllegalArgumentException("Cannot create a address for the scriptPubKey: " + x))
}
def apply(spk: ScriptPubKey, networkParameters: NetworkParameters): Try[BitcoinAddress] = {
fromScriptPubKey(spk,networkParameters)
fromScriptPubKey(spk, networkParameters)
}
}

View file

@ -1,27 +1,27 @@
package org.bitcoins.core.protocol
import org.bitcoins.core.number.{UInt32, UInt64}
import org.bitcoins.core.protocol.script.{ScriptPubKey, ScriptSignature}
import org.bitcoins.core.number.{ UInt32, UInt64 }
import org.bitcoins.core.protocol.script.{ ScriptPubKey, ScriptSignature }
import org.bitcoins.core.script.constant.ScriptNumberUtil
import org.bitcoins.core.util.{BitcoinSUtil, Factory}
import org.bitcoins.core.util.{ BitcoinSUtil, Factory }
/**
* Created by chris on 7/14/15.
*/
/**
* Compact sized unsigned integer as described in:
* https://bitcoin.org/en/developer-reference#compactsize-unsigned-integers
*/
* Compact sized unsigned integer as described in:
* https://bitcoin.org/en/developer-reference#compactsize-unsigned-integers
*/
sealed abstract class CompactSizeUInt extends NetworkElement {
/** The number parsed from VarInt. */
def num: UInt64
override def hex = size match {
case 1 => BitcoinSUtil.flipEndianness(num.hex.slice(14,16))
case 3 => "fd" + BitcoinSUtil.flipEndianness(num.hex.slice(12,16))
case 5 => "fe" + BitcoinSUtil.flipEndianness(num.hex.slice(8,16))
case 1 => BitcoinSUtil.flipEndianness(num.hex.slice(14, 16))
case 3 => "fd" + BitcoinSUtil.flipEndianness(num.hex.slice(12, 16))
case 5 => "fe" + BitcoinSUtil.flipEndianness(num.hex.slice(8, 16))
case _ => "ff" + BitcoinSUtil.flipEndianness(num.hex)
}
@ -37,22 +37,22 @@ sealed abstract class CompactSizeUInt extends NetworkElement {
}
object CompactSizeUInt extends Factory[CompactSizeUInt] {
private case class CompactSizeUIntImpl(num : UInt64, override val size: Int) extends CompactSizeUInt
private case class CompactSizeUIntImpl(num: UInt64, override val size: Int) extends CompactSizeUInt
override def fromBytes(bytes: Seq[Byte]): CompactSizeUInt = {
parseCompactSizeUInt(bytes)
}
def apply(num : UInt64, size : Int): CompactSizeUInt = {
CompactSizeUIntImpl(num,size)
def apply(num: UInt64, size: Int): CompactSizeUInt = {
CompactSizeUIntImpl(num, size)
}
def apply(num : UInt64): CompactSizeUInt = {
def apply(num: UInt64): CompactSizeUInt = {
val size = calcSizeForNum(num)
CompactSizeUInt(num,size)
CompactSizeUInt(num, size)
}
private def calcSizeForNum(num : UInt64) : Int = {
private def calcSizeForNum(num: UInt64): Int = {
if (num.toBigInt <= 252) 1
// can be represented with two bytes
else if (num.toBigInt <= 65535) 3
@ -60,48 +60,56 @@ object CompactSizeUInt extends Factory[CompactSizeUInt] {
else if (num.toBigInt <= UInt32.max.toBigInt) 5
else 9
}
/** This function is responsible for calculating what the compact size unsigned integer is for a
* sequence of bytes
* https://bitcoin.org/en/developer-reference#compactsize-unsigned-integers. */
def calculateCompactSizeUInt(bytes : Seq[Byte]) : CompactSizeUInt = {
/**
* This function is responsible for calculating what the compact size unsigned integer is for a
* sequence of bytes
* https://bitcoin.org/en/developer-reference#compactsize-unsigned-integers.
*/
def calculateCompactSizeUInt(bytes: Seq[Byte]): CompactSizeUInt = {
//means we can represent the number with a single byte
if (bytes.size <= 252) CompactSizeUInt(UInt64(bytes.size),1)
if (bytes.size <= 252) CompactSizeUInt(UInt64(bytes.size), 1)
// can be represented with two bytes
else if (bytes.size <= 65535) CompactSizeUInt(UInt64(bytes.size),3)
else if (bytes.size <= 65535) CompactSizeUInt(UInt64(bytes.size), 3)
//can be represented with 4 bytes
else if (bytes.size <= UInt32.max.toBigInt) CompactSizeUInt(UInt64(bytes.size),5)
else CompactSizeUInt(UInt64(bytes.size),9)
else if (bytes.size <= UInt32.max.toBigInt) CompactSizeUInt(UInt64(bytes.size), 5)
else CompactSizeUInt(UInt64(bytes.size), 9)
}
def calc(bytes: Seq[Byte]): CompactSizeUInt = calculateCompactSizeUInt(bytes)
/** Responsible for calculating what the [[CompactSizeUInt]] is for this hex string. */
def calculateCompactSizeUInt(hex : String) : CompactSizeUInt = calculateCompactSizeUInt(BitcoinSUtil.decodeHex(hex))
def calculateCompactSizeUInt(hex: String): CompactSizeUInt = calculateCompactSizeUInt(BitcoinSUtil.decodeHex(hex))
/** Parses a VarInt from a string of hex characters
* [[https://bitcoin.org/en/developer-reference#compactsize-unsigned-integers]] */
def parseCompactSizeUInt(hex : String) : CompactSizeUInt = parseCompactSizeUInt(BitcoinSUtil.decodeHex(hex))
/**
* Parses a VarInt from a string of hex characters
* [[https://bitcoin.org/en/developer-reference#compactsize-unsigned-integers]]
*/
def parseCompactSizeUInt(hex: String): CompactSizeUInt = parseCompactSizeUInt(BitcoinSUtil.decodeHex(hex))
/** Parses a [[CompactSizeUInt]] from a sequence of bytes
* [[https://bitcoin.org/en/developer-reference#compactsize-unsigned-integers]] */
def parseCompactSizeUInt(bytes : Seq[Byte]) : CompactSizeUInt = {
/**
* Parses a [[CompactSizeUInt]] from a sequence of bytes
* [[https://bitcoin.org/en/developer-reference#compactsize-unsigned-integers]]
*/
def parseCompactSizeUInt(bytes: Seq[Byte]): CompactSizeUInt = {
require(bytes.nonEmpty, "Cannot parse a VarInt if the byte array is size 0")
//8 bit number
if (UInt64(Seq(bytes.head)).toBigInt < 253)
CompactSizeUInt(UInt64(Seq(bytes.head)),1)
if (UInt64(Seq(bytes.head)).toBigInt < 253)
CompactSizeUInt(UInt64(Seq(bytes.head)), 1)
//16 bit number
else if (UInt64(Seq(bytes.head)).toInt == 253) CompactSizeUInt(UInt64(bytes.slice(1,3).reverse),3)
else if (UInt64(Seq(bytes.head)).toInt == 253) CompactSizeUInt(UInt64(bytes.slice(1, 3).reverse), 3)
//32 bit number
else if (UInt64(Seq(bytes.head)).toInt == 254) CompactSizeUInt(UInt64(bytes.slice(1,5).reverse),5)
else if (UInt64(Seq(bytes.head)).toInt == 254) CompactSizeUInt(UInt64(bytes.slice(1, 5).reverse), 5)
//64 bit number
else CompactSizeUInt(UInt64(bytes.slice(1,9).reverse),9)
else CompactSizeUInt(UInt64(bytes.slice(1, 9).reverse), 9)
}
def parse(bytes: Seq[Byte]): CompactSizeUInt = parseCompactSizeUInt(bytes)
/** Returns the size of a VarInt in the number of bytes
* https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer. */
def parseCompactSizeUIntSize(byte : Byte) : Long = {
/**
* Returns the size of a VarInt in the number of bytes
* https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer.
*/
def parseCompactSizeUIntSize(byte: Byte): Long = {
//8 bit number
if (parseLong(byte) < 253) 1
//16 bit number
@ -112,26 +120,24 @@ object CompactSizeUInt extends Factory[CompactSizeUInt] {
else 9
}
/** Parses the [[CompactSizeUInt]] from a [[ScriptSignature]].
* https://bitcoin.org/en/developer-reference#compactsize-unsigned-integers. */
def parseCompactSizeUInt(script : ScriptSignature) : CompactSizeUInt = {
if (script.bytes.size <= 252 ) {
CompactSizeUInt(UInt64(script.bytes.size),1)
/**
* Parses the [[CompactSizeUInt]] from a [[ScriptSignature]].
* https://bitcoin.org/en/developer-reference#compactsize-unsigned-integers.
*/
def parseCompactSizeUInt(script: ScriptSignature): CompactSizeUInt = {
if (script.bytes.size <= 252) {
CompactSizeUInt(UInt64(script.bytes.size), 1)
} else if (script.bytes.size <= 0xffff) {
CompactSizeUInt(UInt64(script.bytes.size),3)
CompactSizeUInt(UInt64(script.bytes.size), 3)
} else if (script.bytes.size <= 0xffffffffL) {
CompactSizeUInt(UInt64(script.bytes.size),5)
} else CompactSizeUInt(UInt64(script.bytes.size),9)
CompactSizeUInt(UInt64(script.bytes.size), 5)
} else CompactSizeUInt(UInt64(script.bytes.size), 9)
}
private def parseLong(hex : String): Long = java.lang.Long.parseLong(hex,16)
private def parseLong(hex: String): Long = java.lang.Long.parseLong(hex, 16)
private def parseLong(bytes : List[Byte]): Long = parseLong(BitcoinSUtil.encodeHex(bytes))
private def parseLong(bytes: List[Byte]): Long = parseLong(BitcoinSUtil.encodeHex(bytes))
private def parseLong(byte : Byte): Long = parseLong(List(byte))
private def parseLong(byte: Byte): Long = parseLong(List(byte))
}

View file

@ -1,10 +1,11 @@
package org.bitcoins.core.protocol
import org.bitcoins.core.config.{MainNet, NetworkParameters, RegTest, TestNet3}
import org.bitcoins.core.config.{ MainNet, NetworkParameters, RegTest, TestNet3 }
/** Represents the HumanReadablePart of a Bech32 address
* [[https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki]]
* */
/**
* Represents the HumanReadablePart of a Bech32 address
* [[https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki]]
*/
sealed abstract class HumanReadablePart {
def network: Option[NetworkParameters]
def bytes: Seq[Byte]
@ -32,11 +33,11 @@ object HumanReadablePart {
def apply(str: String) = str match {
case "bc" => bc
case "tb" => tb
case _ => UndefinedHRP(str.map(_.toByte))
case _ => UndefinedHRP(str.map(_.toByte))
}
def apply(network: NetworkParameters): HumanReadablePart = network match {
case _: MainNet => bc
case _: MainNet => bc
case _: TestNet3 | _: RegTest => tb
}
}

View file

@ -1,23 +1,22 @@
package org.bitcoins.core.protocol
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil}
import org.bitcoins.core.util.{ BitcoinSLogger, BitcoinSUtil }
/**
* Created by chris on 1/14/16.
* This represents a element that can be serialized to
* be sent over the network
*/
* Created by chris on 1/14/16.
* This represents a element that can be serialized to
* be sent over the network
*/
abstract class NetworkElement {
/** The size of the NetworkElement in bytes. */
def size : Int = bytes.size
def size: Int = bytes.size
/** The hexadecimal representation of the NetworkElement */
def hex : String = BitcoinSUtil.encodeHex(bytes)
def hex: String = BitcoinSUtil.encodeHex(bytes)
/** The byte representation of the NetworkElement */
def bytes : Seq[Byte]
def bytes: Seq[Byte]
lazy val logger = BitcoinSLogger.logger
}

View file

@ -1,65 +1,69 @@
package org.bitcoins.core.protocol.blockchain
import org.bitcoins.core.number.UInt64
import org.bitcoins.core.protocol.transaction.{BaseTransaction, Transaction, WitnessTransaction}
import org.bitcoins.core.protocol.{CompactSizeUInt, NetworkElement}
import org.bitcoins.core.protocol.transaction.{ BaseTransaction, Transaction, WitnessTransaction }
import org.bitcoins.core.protocol.{ CompactSizeUInt, NetworkElement }
import org.bitcoins.core.serializers.blockchain.RawBlockSerializer
import org.bitcoins.core.util.{BitcoinSLogger, Factory}
import org.bitcoins.core.util.{ BitcoinSLogger, Factory }
/**
* Created by chris on 5/19/16.
* Represents a block in our blockchain
* Bitcoin Core implementation:
* [[https://github.com/bitcoin/bitcoin/blob/master/src/primitives/block.h#L73]]
* Bitcoin Developer Reference link:
* [[https://bitcoin.org/en/developer-reference#serialized-blocks]]
*/
* Created by chris on 5/19/16.
* Represents a block in our blockchain
* Bitcoin Core implementation:
* [[https://github.com/bitcoin/bitcoin/blob/master/src/primitives/block.h#L73]]
* Bitcoin Developer Reference link:
* [[https://bitcoin.org/en/developer-reference#serialized-blocks]]
*/
sealed abstract class Block extends NetworkElement {
/** The block header for this block */
def blockHeader : BlockHeader
def blockHeader: BlockHeader
/** The total number of transactions in this block,
* including the coinbase transaction. */
def txCount : CompactSizeUInt
/**
* The total number of transactions in this block,
* including the coinbase transaction.
*/
def txCount: CompactSizeUInt
/** The transactions contained in this block */
def transactions : Seq[Transaction]
def transactions: Seq[Transaction]
override def bytes = RawBlockSerializer.write(this)
/** This is the new computation to determine the maximum size of a block as per BIP141
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#block-size]]
* The weight of a block is determined as follows:
*
* Base size is the block size in bytes with the original transaction serialization without any witness-related data
*
* Total size is the block size in bytes with transactions serialized as described in BIP144, including base data and witness data.
*
* Block weight is defined as Base size * 3 + Total size
* [[https://github.com/bitcoin/bitcoin/blob/7490ae8b699d2955b665cf849d86ff5bb5245c28/src/primitives/block.cpp#L35]]
*/
/**
* This is the new computation to determine the maximum size of a block as per BIP141
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#block-size]]
* The weight of a block is determined as follows:
*
* Base size is the block size in bytes with the original transaction serialization without any witness-related data
*
* Total size is the block size in bytes with transactions serialized as described in BIP144, including base data and witness data.
*
* Block weight is defined as Base size * 3 + Total size
* [[https://github.com/bitcoin/bitcoin/blob/7490ae8b699d2955b665cf849d86ff5bb5245c28/src/primitives/block.cpp#L35]]
*/
def blockWeight: Long = transactions.map(_.weight).sum
}
/**
* Companion object for creating Blocks
*/
* Companion object for creating Blocks
*/
object Block extends Factory[Block] {
private sealed case class BlockImpl(blockHeader : BlockHeader,
txCount : CompactSizeUInt, transactions : Seq[Transaction]) extends Block
private sealed case class BlockImpl(
blockHeader: BlockHeader,
txCount: CompactSizeUInt, transactions: Seq[Transaction]
) extends Block
def apply(blockHeader : BlockHeader, txCount : CompactSizeUInt, transactions : Seq[Transaction]) : Block = {
def apply(blockHeader: BlockHeader, txCount: CompactSizeUInt, transactions: Seq[Transaction]): Block = {
BlockImpl(blockHeader, txCount, transactions)
}
def apply(blockHeader : BlockHeader, transactions : Seq[Transaction]) : Block = {
def apply(blockHeader: BlockHeader, transactions: Seq[Transaction]): Block = {
val txCount = CompactSizeUInt(UInt64(transactions.size))
Block(blockHeader, txCount, transactions)
}
def fromBytes(bytes : Seq[Byte]) : Block = RawBlockSerializer.read(bytes)
def fromBytes(bytes: Seq[Byte]): Block = RawBlockSerializer.read(bytes)
}

View file

@ -4,103 +4,100 @@ import org.bitcoins.core.crypto.DoubleSha256Digest
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.NetworkElement
import org.bitcoins.core.serializers.blockchain.RawBlockHeaderSerializer
import org.bitcoins.core.util.{BitcoinSUtil, CryptoUtil, BitcoinSLogger, Factory}
import org.bitcoins.core.util.{ BitcoinSUtil, CryptoUtil, BitcoinSLogger, Factory }
/**
* Created by chris on 5/19/16.
* Nodes collect new transactions into a block, hash them into a hash tree,
* and scan through nonce values to make the block's hash satisfy proof-of-work
* requirements. When they solve the proof-of-work, they broadcast the block
* to everyone and the block is added to the block chain. The first transaction
* in the block is a special one that creates a new coin owned by the creator
* of the block.
* Bitcoin Developer reference link
* https://bitcoin.org/en/developer-reference#block-headers
* Bitcoin Core implementation:
* https://github.com/bitcoin/bitcoin/blob/master/src/primitives/block.h#L20
*/
* Created by chris on 5/19/16.
* Nodes collect new transactions into a block, hash them into a hash tree,
* and scan through nonce values to make the block's hash satisfy proof-of-work
* requirements. When they solve the proof-of-work, they broadcast the block
* to everyone and the block is added to the block chain. The first transaction
* in the block is a special one that creates a new coin owned by the creator
* of the block.
* Bitcoin Developer reference link
* https://bitcoin.org/en/developer-reference#block-headers
* Bitcoin Core implementation:
* https://github.com/bitcoin/bitcoin/blob/master/src/primitives/block.h#L20
*/
sealed trait BlockHeader extends NetworkElement {
/**
* The block version number indicates which set of block validation rules to follow.
* See the list of block versions below.
* See BIP9 for more information on what version number signify
* https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki
*
* @return the version number for this block
*/
def version : UInt32
* The block version number indicates which set of block validation rules to follow.
* See the list of block versions below.
* See BIP9 for more information on what version number signify
* https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki
*
* @return the version number for this block
*/
def version: UInt32
/**
* A SHA256(SHA256()) hash in internal byte order of the previous blocks header.
* This ensures no previous block can be changed without also changing this blocks header.
*
* @return the previous block's hash
*/
* A SHA256(SHA256()) hash in internal byte order of the previous blocks header.
* This ensures no previous block can be changed without also changing this blocks header.
*
* @return the previous block's hash
*/
def previousBlockHash : DoubleSha256Digest
def previousBlockHash: DoubleSha256Digest
/**
* A SHA256(SHA256()) hash in internal byte order.
* The merkle root is derived from the hashes of all transactions included in this block,
* ensuring that none of those transactions can be modified without modifying the header.
* https://bitcoin.org/en/developer-reference#merkle-trees
*
* @return the merkle root of the merkle tree
*/
* A SHA256(SHA256()) hash in internal byte order.
* The merkle root is derived from the hashes of all transactions included in this block,
* ensuring that none of those transactions can be modified without modifying the header.
* https://bitcoin.org/en/developer-reference#merkle-trees
*
* @return the merkle root of the merkle tree
*/
def merkleRootHash : DoubleSha256Digest
def merkleRootHash: DoubleSha256Digest
/**
* The block time is a Unix epoch time when the miner started hashing the header (according to the miner).
* Must be greater than or equal to the median time of the previous 11 blocks.
* Full nodes will not accept blocks with headers more than two hours in the future according to their clock.
*
* @return the time when the miner started solving the block
*/
def time : UInt32
* The block time is a Unix epoch time when the miner started hashing the header (according to the miner).
* Must be greater than or equal to the median time of the previous 11 blocks.
* Full nodes will not accept blocks with headers more than two hours in the future according to their clock.
*
* @return the time when the miner started solving the block
*/
def time: UInt32
/**
* An encoded version of the target threshold this blocks header hash must be less than or equal to.
* See the nBits format described below.
* https://bitcoin.org/en/developer-reference#target-nbits
*
* @return
*/
def nBits : UInt32
* An encoded version of the target threshold this blocks header hash must be less than or equal to.
* See the nBits format described below.
* https://bitcoin.org/en/developer-reference#target-nbits
*
* @return
*/
def nBits: UInt32
/**
* An arbitrary number miners change to modify the header hash in order to produce a hash below the target threshold.
* If all 32-bit values are tested, the time can be updated or the coinbase
* transaction can be changed and the merkle root updated.
*
* @return the nonce used to try and solve a block
*/
def nonce : UInt32
* An arbitrary number miners change to modify the header hash in order to produce a hash below the target threshold.
* If all 32-bit values are tested, the time can be updated or the coinbase
* transaction can be changed and the merkle root updated.
*
* @return the nonce used to try and solve a block
*/
def nonce: UInt32
/** Returns the block's hash */
def hash : DoubleSha256Digest = CryptoUtil.doubleSHA256(bytes)
def hash: DoubleSha256Digest = CryptoUtil.doubleSHA256(bytes)
override def bytes: Seq[Byte] = RawBlockHeaderSerializer.write(this)
}
/**
* Companion object used for creating BlockHeaders
*/
* Companion object used for creating BlockHeaders
*/
object BlockHeader extends Factory[BlockHeader] {
private sealed case class BlockHeaderImpl(version : UInt32, previousBlockHash : DoubleSha256Digest,
merkleRootHash : DoubleSha256Digest, time : UInt32, nBits : UInt32, nonce : UInt32) extends BlockHeader
private sealed case class BlockHeaderImpl(version: UInt32, previousBlockHash: DoubleSha256Digest,
merkleRootHash: DoubleSha256Digest, time: UInt32, nBits: UInt32, nonce: UInt32) extends BlockHeader
def apply(version : UInt32, previousBlockHash : DoubleSha256Digest, merkleRootHash : DoubleSha256Digest,
time : UInt32, nBits : UInt32, nonce : UInt32) : BlockHeader = {
BlockHeaderImpl(version,previousBlockHash,merkleRootHash,time,nBits,nonce)
def apply(version: UInt32, previousBlockHash: DoubleSha256Digest, merkleRootHash: DoubleSha256Digest,
time: UInt32, nBits: UInt32, nonce: UInt32): BlockHeader = {
BlockHeaderImpl(version, previousBlockHash, merkleRootHash, time, nBits, nonce)
}
def fromBytes(bytes : Seq[Byte]) : BlockHeader = RawBlockHeaderSerializer.read(bytes)
def fromBytes(bytes: Seq[Byte]): BlockHeader = RawBlockHeaderSerializer.read(bytes)
}

View file

@ -4,73 +4,78 @@ import java.nio.charset.StandardCharsets
import org.bitcoins.core.consensus.Merkle
import org.bitcoins.core.crypto.DoubleSha256Digest
import org.bitcoins.core.currency.{CurrencyUnit, Satoshis}
import org.bitcoins.core.number.{Int64, UInt32}
import org.bitcoins.core.protocol.script.{ScriptPubKey, ScriptSignature}
import org.bitcoins.core.currency.{ CurrencyUnit, Satoshis }
import org.bitcoins.core.number.{ Int64, UInt32 }
import org.bitcoins.core.protocol.script.{ ScriptPubKey, ScriptSignature }
import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.script.constant.{BytesToPushOntoStack, ScriptConstant, ScriptNumber}
import org.bitcoins.core.script.constant.{ BytesToPushOntoStack, ScriptConstant, ScriptNumber }
import org.bitcoins.core.script.crypto.OP_CHECKSIG
import org.bitcoins.core.util.{BitcoinSUtil, BitcoinScriptUtil}
import org.bitcoins.core.util.{ BitcoinSUtil, BitcoinScriptUtil }
/**
* Created by chris on 5/22/16.
* CChainParams defines various tweakable parameters of a given instance of the
* Bitcoin system. There are three: the main network on which people trade goods
* and services, the public test network which gets reset from time to time and
* a regression test mode which is intended for private networks only. It has
* minimal difficulty to ensure that blocks can be found instantly.
* Mimics this C++ interface
* https://github.com/bitcoin/bitcoin/blob/master/src/chainparams.h#L42
*/
* Created by chris on 5/22/16.
* CChainParams defines various tweakable parameters of a given instance of the
* Bitcoin system. There are three: the main network on which people trade goods
* and services, the public test network which gets reset from time to time and
* a regression test mode which is intended for private networks only. It has
* minimal difficulty to ensure that blocks can be found instantly.
* Mimics this C++ interface
* https://github.com/bitcoin/bitcoin/blob/master/src/chainparams.h#L42
*/
sealed abstract class ChainParams {
/** Return the BIP70 network string ([[MainNetChainParams]], [[TestNetChainParams]] or [[RegTestNetChainParams]].) */
def networkId : String
def networkId: String
/** The Genesis [[Block]] in the blockchain. */
def genesisBlock : Block
def genesisBlock: Block
/** Filter transactions that do not match well-defined patterns
* inside of [[org.bitcoins.core.policy.Policy]]. */
def requireStandardTransaction : Boolean = true
/**
* Filter transactions that do not match well-defined patterns
* inside of [[org.bitcoins.core.policy.Policy]].
*/
def requireStandardTransaction: Boolean = true
/** Takes in a [[Base58Type]] and returns its base58 prefix. */
def base58Prefix(base58 : Base58Type) : Seq[Byte] = base58Prefixes(base58)
def base58Prefix(base58: Base58Type): Seq[Byte] = base58Prefixes(base58)
/** The mapping from a [[Base58Type]]to a String.
* Base58 prefixes for various keys/hashes on the network.
* See: [[https://en.bitcoin.it/wiki/List_of_address_prefixes]]. */
def base58Prefixes : Map[Base58Type,Seq[Byte]]
/**
* The mapping from a [[Base58Type]]to a String.
* Base58 prefixes for various keys/hashes on the network.
* See: [[https://en.bitcoin.it/wiki/List_of_address_prefixes]].
*/
def base58Prefixes: Map[Base58Type, Seq[Byte]]
/** Creates the Genesis [[Block]] for this blockchain.
* Mimics this function in bitcoin core:
* [[https://github.com/bitcoin/bitcoin/blob/master/src/chainparams.cpp#L51]]
* @param time the time when the miner started hashing the block header
* @param nonce the nonce to mine the block
* @param nBits An encoded version of the target threshold this blocks header hash must be less than or equal to.
* @param version the block version
* @param amount the block reward for the genesis block (50 BTC in Bitcoin)
* @return the newly minted genesis block
*/
def createGenesisBlock(time : UInt32, nonce : UInt32, nBits : UInt32, version : UInt32, amount : CurrencyUnit) : Block = {
/**
* Creates the Genesis [[Block]] for this blockchain.
* Mimics this function in bitcoin core:
* [[https://github.com/bitcoin/bitcoin/blob/master/src/chainparams.cpp#L51]]
* @param time the time when the miner started hashing the block header
* @param nonce the nonce to mine the block
* @param nBits An encoded version of the target threshold this blocks header hash must be less than or equal to.
* @param version the block version
* @param amount the block reward for the genesis block (50 BTC in Bitcoin)
* @return the newly minted genesis block
*/
def createGenesisBlock(time: UInt32, nonce: UInt32, nBits: UInt32, version: UInt32, amount: CurrencyUnit): Block = {
val timestamp = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks"
val asm = Seq(BytesToPushOntoStack(65), ScriptConstant("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"), OP_CHECKSIG)
val genesisOutputScript = ScriptPubKey.fromAsm(asm)
createGenesisBlock(timestamp,genesisOutputScript,time,nonce,nBits,version,amount)
createGenesisBlock(timestamp, genesisOutputScript, time, nonce, nBits, version, amount)
}
/**
* @param timestamp a piece of data to signify when this block was first created - satoshi used an article headline
* @param scriptPubKey the scriptPubKey that needs to be satisfied in able to spend the genesis block reward
* @param time the time when the miner started hashing the block header
* @param nonce the nonce used to mine the block
* @param nBits An encoded version of the target threshold this block's header hash must be less than or equal to
* @param version the block version
* @param amount the block reward for the genesis block (50 BTC in Bitcoin)
* @return the newly minted genesis block
*/
def createGenesisBlock(timestamp : String, scriptPubKey : ScriptPubKey, time : UInt32, nonce : UInt32, nBits : UInt32,
version : UInt32, amount : CurrencyUnit) : Block = {
* @param timestamp a piece of data to signify when this block was first created - satoshi used an article headline
* @param scriptPubKey the scriptPubKey that needs to be satisfied in able to spend the genesis block reward
* @param time the time when the miner started hashing the block header
* @param nonce the nonce used to mine the block
* @param nBits An encoded version of the target threshold this block's header hash must be less than or equal to
* @param version the block version
* @param amount the block reward for the genesis block (50 BTC in Bitcoin)
* @return the newly minted genesis block
*/
def createGenesisBlock(timestamp: String, scriptPubKey: ScriptPubKey, time: UInt32, nonce: UInt32, nBits: UInt32,
version: UInt32, amount: CurrencyUnit): Block = {
val timestampBytes = timestamp.getBytes(StandardCharsets.UTF_8)
//see https://bitcoin.stackexchange.com/questions/13122/scriptsig-coinbase-structure-of-the-genesis-block
//for a full breakdown of the genesis block & its script signature
@ -78,12 +83,12 @@ sealed abstract class ChainParams {
val scriptSignature = ScriptSignature.fromAsm(Seq(BytesToPushOntoStack(4), ScriptNumber(486604799),
BytesToPushOntoStack(1), ScriptNumber(4)) ++ BitcoinScriptUtil.calculatePushOp(const) ++ Seq(const))
val input = CoinbaseInput(scriptSignature)
val output = TransactionOutput(amount,scriptPubKey)
val tx = BaseTransaction(TransactionConstants.version,Seq(input), Seq(output), TransactionConstants.lockTime)
val output = TransactionOutput(amount, scriptPubKey)
val tx = BaseTransaction(TransactionConstants.version, Seq(input), Seq(output), TransactionConstants.lockTime)
val prevBlockHash = DoubleSha256Digest("0000000000000000000000000000000000000000000000000000000000000000")
val merkleRootHash = Merkle.computeMerkleRoot(Seq(tx))
val genesisBlockHeader = BlockHeader(version,prevBlockHash,merkleRootHash,time,nBits,nonce)
val genesisBlock = Block(genesisBlockHeader,Seq(tx))
val genesisBlockHeader = BlockHeader(version, prevBlockHash, merkleRootHash, time, nBits, nonce)
val genesisBlock = Block(genesisBlockHeader, Seq(tx))
genesisBlock
}
}
@ -94,44 +99,42 @@ object MainNetChainParams extends BitcoinChainParams {
override def networkId = "main"
override def genesisBlock : Block = createGenesisBlock(UInt32(1231006505), UInt32(2083236893), UInt32(0x1d00ffff), UInt32.one, Satoshis(Int64(5000000000L)))
override def genesisBlock: Block = createGenesisBlock(UInt32(1231006505), UInt32(2083236893), UInt32(0x1d00ffff), UInt32.one, Satoshis(Int64(5000000000L)))
override def base58Prefixes : Map[Base58Type,Seq[Byte]] = Map(
override def base58Prefixes: Map[Base58Type, Seq[Byte]] = Map(
Base58Type.PubKeyAddress -> BitcoinSUtil.decodeHex("00"),
Base58Type.ScriptAddress -> BitcoinSUtil.decodeHex("05"),
Base58Type.SecretKey -> BitcoinSUtil.decodeHex("80"),
Base58Type.ExtPublicKey -> Seq(BitcoinSUtil.hexToByte("04"), BitcoinSUtil.hexToByte("88"),
BitcoinSUtil.hexToByte("b2"), BitcoinSUtil.hexToByte("1e")),
Base58Type.ExtSecretKey -> Seq(BitcoinSUtil.hexToByte("04"), BitcoinSUtil.hexToByte("88"),
BitcoinSUtil.hexToByte("ad"), BitcoinSUtil.hexToByte("e4")))
BitcoinSUtil.hexToByte("ad"), BitcoinSUtil.hexToByte("e4"))
)
}
object TestNetChainParams extends BitcoinChainParams {
override def networkId = "test"
override def genesisBlock : Block = createGenesisBlock(UInt32(1296688602), UInt32(414098458), UInt32(0x1d00ffff), UInt32.one, Satoshis(Int64(5000000000L)))
override def genesisBlock: Block = createGenesisBlock(UInt32(1296688602), UInt32(414098458), UInt32(0x1d00ffff), UInt32.one, Satoshis(Int64(5000000000L)))
override def base58Prefixes : Map[Base58Type,Seq[Byte]] = Map(
override def base58Prefixes: Map[Base58Type, Seq[Byte]] = Map(
Base58Type.PubKeyAddress -> BitcoinSUtil.decodeHex("6f"),
Base58Type.ScriptAddress -> BitcoinSUtil.decodeHex("c4"),
Base58Type.SecretKey -> BitcoinSUtil.decodeHex("ef"),
Base58Type.ExtPublicKey -> Seq(BitcoinSUtil.hexToByte("04"), BitcoinSUtil.hexToByte("35"),
BitcoinSUtil.hexToByte("87"), BitcoinSUtil.hexToByte("cf")),
Base58Type.ExtSecretKey -> Seq(BitcoinSUtil.hexToByte("04"), BitcoinSUtil.hexToByte("35"),
BitcoinSUtil.hexToByte("83"), BitcoinSUtil.hexToByte("94")))
BitcoinSUtil.hexToByte("83"), BitcoinSUtil.hexToByte("94"))
)
}
object RegTestNetChainParams extends BitcoinChainParams {
override def networkId = "regtest"
override def genesisBlock : Block = createGenesisBlock(UInt32(1296688602), UInt32(2), UInt32(0x207fffff), UInt32.one, Satoshis(Int64(5000000000L)))
override def base58Prefixes : Map[Base58Type, Seq[Byte]] = TestNetChainParams.base58Prefixes
override def genesisBlock: Block = createGenesisBlock(UInt32(1296688602), UInt32(2), UInt32(0x207fffff), UInt32.one, Satoshis(Int64(5000000000L)))
override def base58Prefixes: Map[Base58Type, Seq[Byte]] = TestNetChainParams.base58Prefixes
}
sealed abstract class Base58Type
object Base58Type {
case object PubKeyAddress extends Base58Type

View file

@ -2,17 +2,17 @@ package org.bitcoins.core.protocol.blockchain
import org.bitcoins.core.bloom.BloomFilter
import org.bitcoins.core.crypto.DoubleSha256Digest
import org.bitcoins.core.number.{UInt32, UInt64}
import org.bitcoins.core.number.{ UInt32, UInt64 }
import org.bitcoins.core.protocol.transaction.Transaction
import org.bitcoins.core.protocol.{CompactSizeUInt, NetworkElement}
import org.bitcoins.core.protocol.{ CompactSizeUInt, NetworkElement }
import org.bitcoins.core.serializers.blockchain.RawMerkleBlockSerializer
import org.bitcoins.core.util.Factory
import scala.annotation.tailrec
/**
* Created by chris on 8/7/16.
*/
* Created by chris on 8/7/16.
*/
sealed abstract class MerkleBlock extends NetworkElement {
/** The block header for the this merkle block */
@ -33,70 +33,65 @@ sealed abstract class MerkleBlock extends NetworkElement {
def bytes = RawMerkleBlockSerializer.write(this)
}
object MerkleBlock extends Factory[MerkleBlock] {
private case class MerkleBlockImpl(blockHeader: BlockHeader, transactionCount: UInt32,
partialMerkleTree: PartialMerkleTree) extends MerkleBlock
/**
* Creates a [[MerkleBlock]] from the given [[Block]] and [[BloomFilter]]
* This function iterates through each transaction inside our block checking if it is relevant to the given bloom filter
* If it is relevant, it will set a flag to indicate we should include it inside of our [[PartialMerkleTree]]
* @param block the block that we searching for transactions that match the bloom filter
* @param filter the filter we are comparing transactions in the block against
* @return the merkle block and the bloom filter loaded with information from the relevant txs in the block
*/
def apply(block: Block, filter: BloomFilter): (MerkleBlock,BloomFilter) = {
* Creates a [[MerkleBlock]] from the given [[Block]] and [[BloomFilter]]
* This function iterates through each transaction inside our block checking if it is relevant to the given bloom filter
* If it is relevant, it will set a flag to indicate we should include it inside of our [[PartialMerkleTree]]
* @param block the block that we searching for transactions that match the bloom filter
* @param filter the filter we are comparing transactions in the block against
* @return the merkle block and the bloom filter loaded with information from the relevant txs in the block
*/
def apply(block: Block, filter: BloomFilter): (MerkleBlock, BloomFilter) = {
@tailrec
def loop(remainingTxs: Seq[Transaction], accumFilter: BloomFilter,
txMatches: Seq[(Boolean,DoubleSha256Digest)]): (Seq[(Boolean,DoubleSha256Digest)], BloomFilter) = {
if (remainingTxs.isEmpty) (txMatches.reverse,accumFilter)
txMatches: Seq[(Boolean, DoubleSha256Digest)]): (Seq[(Boolean, DoubleSha256Digest)], BloomFilter) = {
if (remainingTxs.isEmpty) (txMatches.reverse, accumFilter)
else {
val tx = remainingTxs.head
val newTxMatches = (accumFilter.isRelevant(tx),tx.txId) +: txMatches
val newFilter = accumFilter.update(tx)
loop(remainingTxs.tail,newFilter,newTxMatches)
val newTxMatches = (accumFilter.isRelevant(tx), tx.txId) +: txMatches
val newFilter = accumFilter.update(tx)
loop(remainingTxs.tail, newFilter, newTxMatches)
}
}
val (matchedTxs,newFilter) = loop(block.transactions,filter,Nil)
val (matchedTxs, newFilter) = loop(block.transactions, filter, Nil)
val partialMerkleTree = PartialMerkleTree(matchedTxs)
val txCount = UInt32(block.transactions.size)
(MerkleBlock(block.blockHeader, txCount, partialMerkleTree),newFilter)
(MerkleBlock(block.blockHeader, txCount, partialMerkleTree), newFilter)
}
/** Creates a merkle block that matches the given txids if they appear inside the given block */
def apply(block: Block, txIds: Seq[DoubleSha256Digest]): MerkleBlock = {
//follows this function inside of bitcoin core
//https://github.com/bitcoin/bitcoin/blob/master/src/merkleblock.cpp#L40
@tailrec
def loop(remainingTxs: Seq[Transaction], txMatches: Seq[(Boolean,DoubleSha256Digest)]): (Seq[(Boolean,DoubleSha256Digest)]) = {
def loop(remainingTxs: Seq[Transaction], txMatches: Seq[(Boolean, DoubleSha256Digest)]): (Seq[(Boolean, DoubleSha256Digest)]) = {
if (remainingTxs.isEmpty) txMatches.reverse
else {
val tx = remainingTxs.head
val newTxMatches = (txIds.contains(tx.txId),tx.txId) +: txMatches
loop(remainingTxs.tail,newTxMatches)
val newTxMatches = (txIds.contains(tx.txId), tx.txId) +: txMatches
loop(remainingTxs.tail, newTxMatches)
}
}
val txMatches = loop(block.transactions,Nil)
val txMatches = loop(block.transactions, Nil)
val partialMerkleTree = PartialMerkleTree(txMatches)
val txCount = UInt32(block.transactions.size)
MerkleBlock(block.blockHeader,txCount,partialMerkleTree)
MerkleBlock(block.blockHeader, txCount, partialMerkleTree)
}
def apply(blockHeader: BlockHeader, txCount: UInt32,
partialMerkleTree: PartialMerkleTree): MerkleBlock = {
MerkleBlockImpl(blockHeader,txCount,partialMerkleTree)
MerkleBlockImpl(blockHeader, txCount, partialMerkleTree)
}
def apply(blockHeader: BlockHeader, txCount: UInt32, hashes: Seq[DoubleSha256Digest], bits: Seq[Boolean]): MerkleBlock = {
val partialMerkleTree = PartialMerkleTree(txCount,hashes,bits)
MerkleBlock(blockHeader,txCount,partialMerkleTree)
val partialMerkleTree = PartialMerkleTree(txCount, hashes, bits)
MerkleBlock(blockHeader, txCount, partialMerkleTree)
}
def fromBytes(bytes: Seq[Byte]): MerkleBlock = RawMerkleBlockSerializer.read(bytes)

View file

@ -1,6 +1,5 @@
package org.bitcoins.core.protocol.blockchain
import org.bitcoins.core.crypto.DoubleSha256Digest
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.util._
@ -9,29 +8,29 @@ import scala.annotation.tailrec
import scala.math._
/**
* Created by chris on 8/7/16.
* Represents a subset of known txids inside of a [[Block]]
* in a way that allows recovery of the txids & merkle root
* without having to store them all explicitly.
* See BIP37 for more details
* [[https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki#partial-merkle-branch-format]]
*
* Encoding procedure:
* [[https://bitcoin.org/en/developer-reference#creating-a-merkleblock-message]]
* [[https://github.com/bitcoin/bitcoin/blob/b7b48c8bbdf7a90861610b035d8b0a247ef78c45/src/merkleblock.cpp#L78]]
* Traverse the tree in depth first order, storing a bit for each traversal.
* This bit signifies if the node is a parent of at least one
* matched leaf txid (or a matched leaf txid) itself.
* In case we are the leaf level, or this bit is 0, it's merkle
* node hash is stored and it's children are not explored any further.
* Otherwise no hash is stored, but we recurse all of this node's child branches.
*
* Decoding procedure:
* [[https://bitcoin.org/en/developer-reference#parsing-a-merkleblock-message]]
* [[https://github.com/bitcoin/bitcoin/blob/b7b48c8bbdf7a90861610b035d8b0a247ef78c45/src/merkleblock.cpp#L96]]
* The same depth first decoding procedure is performed, but we consume the
* bits and hashes that we used during encoding
*/
* Created by chris on 8/7/16.
* Represents a subset of known txids inside of a [[Block]]
* in a way that allows recovery of the txids & merkle root
* without having to store them all explicitly.
* See BIP37 for more details
* [[https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki#partial-merkle-branch-format]]
*
* Encoding procedure:
* [[https://bitcoin.org/en/developer-reference#creating-a-merkleblock-message]]
* [[https://github.com/bitcoin/bitcoin/blob/b7b48c8bbdf7a90861610b035d8b0a247ef78c45/src/merkleblock.cpp#L78]]
* Traverse the tree in depth first order, storing a bit for each traversal.
* This bit signifies if the node is a parent of at least one
* matched leaf txid (or a matched leaf txid) itself.
* In case we are the leaf level, or this bit is 0, it's merkle
* node hash is stored and it's children are not explored any further.
* Otherwise no hash is stored, but we recurse all of this node's child branches.
*
* Decoding procedure:
* [[https://bitcoin.org/en/developer-reference#parsing-a-merkleblock-message]]
* [[https://github.com/bitcoin/bitcoin/blob/b7b48c8bbdf7a90861610b035d8b0a247ef78c45/src/merkleblock.cpp#L96]]
* The same depth first decoding procedure is performed, but we consume the
* bits and hashes that we used during encoding
*/
sealed trait PartialMerkleTree extends BitcoinSLogger {
/** The total number of transactions in this block */
@ -57,9 +56,11 @@ sealed trait PartialMerkleTree extends BitcoinSLogger {
//TODO: This is some really ugly that isn't tail recursive, try to clean this up eventually
logger.debug("Starting bits for extraction: " + bits)
logger.debug("Starting tree: " + tree)
def loop(subTree: BinaryTree[DoubleSha256Digest],
remainingBits: Seq[Boolean], height: Int, pos: Int, accumMatches: Seq[DoubleSha256Digest]): (Seq[DoubleSha256Digest], Seq[Boolean]) = {
if (height == maxHeight) extractLeafMatch(accumMatches,remainingBits,subTree)
def loop(
subTree: BinaryTree[DoubleSha256Digest],
remainingBits: Seq[Boolean], height: Int, pos: Int, accumMatches: Seq[DoubleSha256Digest]
): (Seq[DoubleSha256Digest], Seq[Boolean]) = {
if (height == maxHeight) extractLeafMatch(accumMatches, remainingBits, subTree)
else {
//means we have a nontxid node
if (remainingBits.head) {
@ -67,11 +68,11 @@ sealed trait PartialMerkleTree extends BitcoinSLogger {
subTree match {
case n: Node[DoubleSha256Digest] =>
//since we are just trying to extract bloom filter matches, recurse into the two subtrees
val (leftTreeMatches,leftRemainingBits) = loop(n.l,remainingBits.tail,height+1,(2 * pos), accumMatches)
val (leftTreeMatches, leftRemainingBits) = loop(n.l, remainingBits.tail, height + 1, (2 * pos), accumMatches)
//check to see if we have a right subtree
if (PartialMerkleTree.existsRightSubTree(pos,numTransactions,maxHeight,height)) {
if (PartialMerkleTree.existsRightSubTree(pos, numTransactions, maxHeight, height)) {
val (rightTreeMatches, rightRemainingBits) =
loop(n.r,leftRemainingBits,height+1, (2 * pos) + 1,leftTreeMatches)
loop(n.r, leftRemainingBits, height + 1, (2 * pos) + 1, leftTreeMatches)
(rightTreeMatches, rightRemainingBits)
} else (leftTreeMatches, leftRemainingBits)
case _: Leaf[DoubleSha256Digest] =>
@ -81,96 +82,93 @@ sealed trait PartialMerkleTree extends BitcoinSLogger {
} else (accumMatches, remainingBits.tail)
}
}
val (matches,remainingBits) = loop(tree,bits,0,0,Nil)
require(PartialMerkleTree.usedAllBits(bits,remainingBits), "We should not have any remaining matches " +
val (matches, remainingBits) = loop(tree, bits, 0, 0, Nil)
require(PartialMerkleTree.usedAllBits(bits, remainingBits), "We should not have any remaining matches " +
"except for those that pad our byte after building our partial merkle tree, got: " + remainingBits)
matches.reverse
}
/** Handles a leaf match when we are extracting matches from the partial merkle tree */
private def extractLeafMatch(accumMatches : Seq[DoubleSha256Digest], remainingBits: Seq[Boolean],
private def extractLeafMatch(accumMatches: Seq[DoubleSha256Digest], remainingBits: Seq[Boolean],
subTree: BinaryTree[DoubleSha256Digest]): (Seq[DoubleSha256Digest], Seq[Boolean]) = {
if (remainingBits.head) {
//means we have a txid node that matched the filter
subTree match {
case l : Leaf[DoubleSha256Digest] =>
case l: Leaf[DoubleSha256Digest] =>
val newAccumMatches = l.v +: accumMatches
(newAccumMatches, remainingBits.tail)
case x @ ( _ : Node[DoubleSha256Digest] | Empty) => throw new IllegalArgumentException("We cannot have a " +
case x @ (_: Node[DoubleSha256Digest] | Empty) => throw new IllegalArgumentException("We cannot have a " +
"Node or Empty node when we supposedly have a txid node -- txid nodes should always be leaves, got: " + x)
}
} else {
//means we have a txid node, but it did not match the filter
(accumMatches,remainingBits.tail)
(accumMatches, remainingBits.tail)
}
}
}
object PartialMerkleTree {
private val logger = BitcoinSLogger.logger
private case class PartialMerkleTreeImpl(tree: BinaryTree[DoubleSha256Digest], transactionCount: UInt32,
bits: Seq[Boolean], hashes: Seq[DoubleSha256Digest]) extends PartialMerkleTree
def apply(txMatches: Seq[(Boolean,DoubleSha256Digest)]): PartialMerkleTree = {
def apply(txMatches: Seq[(Boolean, DoubleSha256Digest)]): PartialMerkleTree = {
val txIds = txMatches.map(_._2)
val (bits,hashes) = build(txMatches)
val tree = reconstruct(txIds.size,hashes,bits)
PartialMerkleTree(tree,UInt32(txIds.size),bits,hashes)
val (bits, hashes) = build(txMatches)
val tree = reconstruct(txIds.size, hashes, bits)
PartialMerkleTree(tree, UInt32(txIds.size), bits, hashes)
}
/**
* @param txMatches indicates whether the given txid matches the bloom filter, the full merkle branch needs
* to be included inside of the [[PartialMerkleTree]]
* @return the binary tree that represents the partial merkle tree, the bits needed to reconstruct this partial merkle tree, and the hashes needed to be inserted
* according to the flags inside of bits
*/
private def build(txMatches: Seq[(Boolean,DoubleSha256Digest)]): (Seq[Boolean], Seq[DoubleSha256Digest]) = {
* @param txMatches indicates whether the given txid matches the bloom filter, the full merkle branch needs
* to be included inside of the [[PartialMerkleTree]]
* @return the binary tree that represents the partial merkle tree, the bits needed to reconstruct this partial merkle tree, and the hashes needed to be inserted
* according to the flags inside of bits
*/
private def build(txMatches: Seq[(Boolean, DoubleSha256Digest)]): (Seq[Boolean], Seq[DoubleSha256Digest]) = {
val maxHeight = calcMaxHeight(txMatches.size)
logger.debug("Tx matches: " + txMatches)
logger.debug("Tx matches size: " + txMatches.size)
logger.debug("max height: "+ maxHeight)
logger.debug("max height: " + maxHeight)
/**
* This loops through our merkle tree building [[bits]] so we can instruct another node how to create the partial merkle tree
* [[https://github.com/bitcoin/bitcoin/blob/b7b48c8bbdf7a90861610b035d8b0a247ef78c45/src/merkleblock.cpp#L78]]
* @param bits the accumulator for bits indicating how to reconsctruct the partial merkle tree
* @param hashes the relevant hashes used with bits to reconstruct the merkle tree
* @param height the transaction index we are currently looking at -- if it was matched in our bloom filter we need the entire merkle branch
* @return the binary tree that represents the partial merkle tree, the bits needed to reconstruct this partial merkle tree, and the hashes needed to be inserted
* according to the flags inside of bits
*/
* This loops through our merkle tree building [[bits]] so we can instruct another node how to create the partial merkle tree
* [[https://github.com/bitcoin/bitcoin/blob/b7b48c8bbdf7a90861610b035d8b0a247ef78c45/src/merkleblock.cpp#L78]]
* @param bits the accumulator for bits indicating how to reconsctruct the partial merkle tree
* @param hashes the relevant hashes used with bits to reconstruct the merkle tree
* @param height the transaction index we are currently looking at -- if it was matched in our bloom filter we need the entire merkle branch
* @return the binary tree that represents the partial merkle tree, the bits needed to reconstruct this partial merkle tree, and the hashes needed to be inserted
* according to the flags inside of bits
*/
def loop(bits: Seq[Boolean], hashes: Seq[DoubleSha256Digest], height: Int, pos: Int): (Seq[Boolean], Seq[DoubleSha256Digest]) = {
val parentOfMatch = matchesTx(maxHeight,maxHeight - height, pos, txMatches)
val parentOfMatch = matchesTx(maxHeight, maxHeight - height, pos, txMatches)
logger.debug("parent of match: " + parentOfMatch)
val newBits = parentOfMatch +: bits
if (height == 0 || !parentOfMatch) {
//means that we are either at the root of the merkle tree or there is nothing interesting below
//this node in our binary tree
val nodeHash = calcHash(height,pos,txMatches.map(_._2))
val nodeHash = calcHash(height, pos, txMatches.map(_._2))
val newHashes = nodeHash +: hashes
(newBits,newHashes)
(newBits, newHashes)
} else {
//process the left node
val (leftBits,leftHashes) = loop(newBits, hashes, height-1, pos*2)
if (existsRightSubTree(pos,txMatches.size,height)) {
val (leftBits, leftHashes) = loop(newBits, hashes, height - 1, pos * 2)
if (existsRightSubTree(pos, txMatches.size, height)) {
//process the right node if the tree's width is larger than the position we are looking at
loop(leftBits,leftHashes, height-1, (pos*2) + 1)
} else (leftBits,leftHashes)
loop(leftBits, leftHashes, height - 1, (pos * 2) + 1)
} else (leftBits, leftHashes)
}
}
val (bits,hashes) = loop(Nil, Nil, maxHeight,0)
val (bits, hashes) = loop(Nil, Nil, maxHeight, 0)
//pad the bit array to the nearest byte as required by BIP37
val bitsNeeded = if ((bits.size % 8) == 0) 0 else 8 - (bits.size % 8)
val paddingBits = for { _ <- 0 until bitsNeeded} yield false
val paddingBits = for { _ <- 0 until bitsNeeded } yield false
(bits.reverse ++ paddingBits, hashes.reverse)
}
/** Checks if a node at given the given height and position matches a transaction in the sequence */
def matchesTx(maxHeight: Int, height: Int, pos: Int, matchedTx: Seq[(Boolean,DoubleSha256Digest)]): Boolean = {
def matchesTx(maxHeight: Int, height: Int, pos: Int, matchedTx: Seq[(Boolean, DoubleSha256Digest)]): Boolean = {
//mimics this functionality inside of bitcoin core
//https://github.com/bitcoin/bitcoin/blob/b7b48c8bbdf7a90861610b035d8b0a247ef78c45/src/merkleblock.cpp#L78
val inverseHeight = maxHeight - height
@ -187,73 +185,75 @@ object PartialMerkleTree {
}
/** Simple way to calculate the maximum width of a binary tree */
private def calcTreeWidth(numTransactions: Int, height: Int) = (numTransactions+ (1 << height)-1) >> height
private def calcTreeWidth(numTransactions: Int, height: Int) = (numTransactions + (1 << height) - 1) >> height
/** Calculates the hash of a node in the merkle tree */
private def calcHash(height : Int, pos : Int, txIds: Seq[DoubleSha256Digest]): DoubleSha256Digest = {
private def calcHash(height: Int, pos: Int, txIds: Seq[DoubleSha256Digest]): DoubleSha256Digest = {
//TODO: Optimize this to tailrec function
//follows this function inside of bitcoin core
//https://github.com/bitcoin/bitcoin/blob/master/src/merkleblock.cpp#L63
if (height == 0) txIds(pos)
else {
val leftHash = calcHash(height-1, pos * 2, txIds)
val rightHash = if (existsRightSubTree(pos,txIds.size,height)) {
calcHash(height-1, (pos * 2) + 1, txIds)
val leftHash = calcHash(height - 1, pos * 2, txIds)
val rightHash = if (existsRightSubTree(pos, txIds.size, height)) {
calcHash(height - 1, (pos * 2) + 1, txIds)
} else leftHash
CryptoUtil.doubleSHA256(leftHash.bytes ++ rightHash.bytes)
}
}
/**
* Function to reconstruct a partial merkle tree
* @param transactionCount the number of transactions inside of the partial merkle tree
* @param hashes the hashes used to reconstruct the partial merkle tree
* @param bits the bits used indicate the structure of the partial merkle tree
* @return
*/
* Function to reconstruct a partial merkle tree
* @param transactionCount the number of transactions inside of the partial merkle tree
* @param hashes the hashes used to reconstruct the partial merkle tree
* @param bits the bits used indicate the structure of the partial merkle tree
* @return
*/
def apply(transactionCount: UInt32, hashes: Seq[DoubleSha256Digest], bits: Seq[Boolean]): PartialMerkleTree = {
val tree = reconstruct(transactionCount.toInt,hashes,bits)
PartialMerkleTree(tree,transactionCount, bits,hashes)
val tree = reconstruct(transactionCount.toInt, hashes, bits)
PartialMerkleTree(tree, transactionCount, bits, hashes)
}
/**
* This constructor creates a partial from this given [[BinaryTree]]
* You probably don't want to use this constructor, unless you manually constructed [[bits]] and the [[tree]]
* by hand
* @param tree the partial merkle tree -- note this is NOT the full merkle tree
* @param transactionCount the number of transactions there initially was in the full merkle tree
* @param bits the path to the matches in the partial merkle tree
* @param hashes the hashes used to reconstruct the binary tree according to [[bits]]
*/
def apply(tree: BinaryTree[DoubleSha256Digest], transactionCount: UInt32, bits: Seq[Boolean], hashes: Seq[DoubleSha256Digest]): PartialMerkleTree = {
PartialMerkleTreeImpl(tree,transactionCount, bits, hashes)
* This constructor creates a partial from this given [[BinaryTree]]
* You probably don't want to use this constructor, unless you manually constructed [[bits]] and the [[tree]]
* by hand
* @param tree the partial merkle tree -- note this is NOT the full merkle tree
* @param transactionCount the number of transactions there initially was in the full merkle tree
* @param bits the path to the matches in the partial merkle tree
* @param hashes the hashes used to reconstruct the binary tree according to [[bits]]
*/
def apply(tree: BinaryTree[DoubleSha256Digest], transactionCount: UInt32, bits: Seq[Boolean], hashes: Seq[DoubleSha256Digest]): PartialMerkleTree = {
PartialMerkleTreeImpl(tree, transactionCount, bits, hashes)
}
/** Builds a partial merkle tree
* [[https://bitcoin.org/en/developer-reference#parsing-a-merkleblock-message]]
* [[https://github.com/bitcoin/bitcoin/blob/b7b48c8bbdf7a90861610b035d8b0a247ef78c45/src/merkleblock.cpp#L96]]
*/
/**
* Builds a partial merkle tree
* [[https://bitcoin.org/en/developer-reference#parsing-a-merkleblock-message]]
* [[https://github.com/bitcoin/bitcoin/blob/b7b48c8bbdf7a90861610b035d8b0a247ef78c45/src/merkleblock.cpp#L96]]
*/
private def reconstruct(numTransaction: Int, hashes: Seq[DoubleSha256Digest], bits: Seq[Boolean]): BinaryTree[DoubleSha256Digest] = {
val maxHeight = calcMaxHeight(numTransaction)
//TODO: Optimize to tailrec function
def loop(remainingHashes: Seq[DoubleSha256Digest], remainingMatches: Seq[Boolean], height: Int, pos: Int) : (BinaryTree[DoubleSha256Digest],Seq[DoubleSha256Digest], Seq[Boolean]) = {
def loop(remainingHashes: Seq[DoubleSha256Digest], remainingMatches: Seq[Boolean], height: Int, pos: Int): (BinaryTree[DoubleSha256Digest], Seq[DoubleSha256Digest], Seq[Boolean]) = {
if (height == maxHeight) {
//means we have a txid node
(Leaf(remainingHashes.head),
(
Leaf(remainingHashes.head),
remainingHashes.tail,
remainingMatches.tail)
remainingMatches.tail
)
} else {
//means we have a non txid node
if (remainingMatches.head) {
val nextHeight = height+1
val nextHeight = height + 1
val leftNodePos = pos * 2
val rightNodePos = (pos * 2) + 1
val (leftNode,leftRemainingHashes,leftRemainingBits) = loop(remainingHashes,remainingMatches.tail,nextHeight, leftNodePos)
val (rightNode,rightRemainingHashes, rightRemainingBits) =
if (existsRightSubTree(pos,numTransaction,maxHeight,height)) {
val (rightNode,rightRemainingHashes, rightRemainingBits) =
loop(leftRemainingHashes,leftRemainingBits,nextHeight, rightNodePos)
val (leftNode, leftRemainingHashes, leftRemainingBits) = loop(remainingHashes, remainingMatches.tail, nextHeight, leftNodePos)
val (rightNode, rightRemainingHashes, rightRemainingBits) =
if (existsRightSubTree(pos, numTransaction, maxHeight, height)) {
val (rightNode, rightRemainingHashes, rightRemainingBits) =
loop(leftRemainingHashes, leftRemainingBits, nextHeight, rightNodePos)
//https://github.com/bitcoin/bitcoin/blob/b7b48c8bbdf7a90861610b035d8b0a247ef78c45/src/merkleblock.cpp#L121-L125
if (nextHeight != maxHeight) {
//we cannot the same two hashes as child nodes UNLESS we are the max height in the binary tree
@ -261,35 +261,36 @@ object PartialMerkleTree {
", \nleftRemainingHashes: " + leftRemainingHashes + " \nrightRemainingHashes: " + rightRemainingHashes +
"\nnumTransactions: " + numTransaction + "\nhashes: " + hashes + "\nbits: " + bits)
}
(rightNode,rightRemainingHashes, rightRemainingBits)
(rightNode, rightRemainingHashes, rightRemainingBits)
} else (leftNode, leftRemainingHashes, leftRemainingBits)
val nodeHash = CryptoUtil.doubleSHA256(leftNode.value.get.bytes ++ rightNode.value.get.bytes)
val node = Node(nodeHash,leftNode,rightNode)
(node,rightRemainingHashes,rightRemainingBits)
} else (Leaf(remainingHashes.head),remainingHashes.tail,remainingMatches.tail)
val node = Node(nodeHash, leftNode, rightNode)
(node, rightRemainingHashes, rightRemainingBits)
} else (Leaf(remainingHashes.head), remainingHashes.tail, remainingMatches.tail)
}
}
logger.debug("Original hashes: " + hashes)
logger.debug("Original bits: " + bits)
val (tree,remainingHashes,remainingBits) = loop(hashes,bits,0,0)
val (tree, remainingHashes, remainingBits) = loop(hashes, bits, 0, 0)
//we must have used all the hashes provided to us to reconstruct the partial merkle tree as per BIP37
require(remainingHashes.size == 0,"We should not have any left over hashes after building our partial merkle tree, got: " + remainingHashes )
require(remainingHashes.size == 0, "We should not have any left over hashes after building our partial merkle tree, got: " + remainingHashes)
//we must not have any matches remaining, unless the remaining bits were use to pad our byte vector to 8 bits
//for instance, we could have had 5 bits to indicate how to build the merkle tree, but we need to pad it with 3 bits
//to give us a full byte to serialize and send over the network
//https://github.com/bitcoin/bitcoin/blob/b7b48c8bbdf7a90861610b035d8b0a247ef78c45/src/merkleblock.cpp#L174-L175
require(usedAllBits(bits,remainingBits), "We should not have any remaining matches except for those that pad our byte after building our partial merkle tree, got: " + remainingBits)
require(usedAllBits(bits, remainingBits), "We should not have any remaining matches except for those that pad our byte after building our partial merkle tree, got: " + remainingBits)
tree
}
/** Calculates the maximum height for a binary tree with the number of transactions specified */
def calcMaxHeight(numTransactions: Int): Int = Math.ceil((log(numTransactions) / log(2))).toInt
/** Determines if the right sub tree can exists inside of the partial merkle tree
* This function should only be used to determine if a right sub tree exists when we
* are building a partial merkle tree from bottom up, NOT TOP DOWN. If we are building a
* tree from top down use it's counterpart that does NOT take a maxHeight parameter*/
/**
* Determines if the right sub tree can exists inside of the partial merkle tree
* This function should only be used to determine if a right sub tree exists when we
* are building a partial merkle tree from bottom up, NOT TOP DOWN. If we are building a
* tree from top down use it's counterpart that does NOT take a maxHeight parameter
*/
private def existsRightSubTree(pos: Int, numTransaction: Int, maxHeight: Int, height: Int): Boolean = {
(pos * 2) + 1 < calcTreeWidth(numTransaction, maxHeight - height - 1)
}
@ -299,13 +300,13 @@ object PartialMerkleTree {
(pos * 2) + 1 < calcTreeWidth(numTransaction, height - 1)
}
/** Enforces the invariant inside of bitcoin core saying we must use all bits
* in a byte array when reconstruction a partial merkle tree
* [[https://github.com/bitcoin/bitcoin/blob/b7b48c8bbdf7a90861610b035d8b0a247ef78c45/src/merkleblock.cpp#L174-L175]]
*/
/**
* Enforces the invariant inside of bitcoin core saying we must use all bits
* in a byte array when reconstruction a partial merkle tree
* [[https://github.com/bitcoin/bitcoin/blob/b7b48c8bbdf7a90861610b035d8b0a247ef78c45/src/merkleblock.cpp#L174-L175]]
*/
private def usedAllBits(bits: Seq[Boolean], remainingBits: Seq[Boolean]): Boolean = {
val bitsUsed = bits.size - remainingBits.size
((bitsUsed+7) / 8) ==((bits.size + 7) / 8)
((bitsUsed + 7) / 8) == ((bits.size + 7) / 8)
}
}

View file

@ -6,8 +6,8 @@ import org.bitcoins.core.serializers.script.ScriptParser
import org.bitcoins.core.util.Factory
/**
* Created by chris on 12/9/16.
*/
* Created by chris on 12/9/16.
*/
trait ScriptFactory[T] extends Factory[T] {
/** Builds a script from the given asm with the given constructor if the invariant holds true, else throws an error */
@ -25,7 +25,7 @@ trait ScriptFactory[T] extends Factory[T] {
def fromBytes(bytes: Seq[Byte]): T = {
val cpmct = CompactSizeUInt.parseCompactSizeUInt(bytes)
val (_,noCmpctUInt) = bytes.splitAt(cpmct.bytes.size)
val (_, noCmpctUInt) = bytes.splitAt(cpmct.bytes.size)
val asm = ScriptParser.fromBytes(noCmpctUInt)
fromAsm(asm)
}

View file

@ -5,17 +5,17 @@ import org.bitcoins.core.protocol._
import org.bitcoins.core.protocol.blockchain.Block
import org.bitcoins.core.protocol.transaction.WitnessTransaction
import org.bitcoins.core.script.ScriptSettings
import org.bitcoins.core.script.bitwise.{OP_EQUAL, OP_EQUALVERIFY}
import org.bitcoins.core.script.constant.{BytesToPushOntoStack, _}
import org.bitcoins.core.script.control.{OP_ELSE, OP_ENDIF, OP_IF, OP_RETURN}
import org.bitcoins.core.script.crypto.{OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY, OP_CHECKSIG, OP_HASH160}
import org.bitcoins.core.script.locktime.{OP_CHECKLOCKTIMEVERIFY, OP_CHECKSEQUENCEVERIFY}
import org.bitcoins.core.script.bitwise.{ OP_EQUAL, OP_EQUALVERIFY }
import org.bitcoins.core.script.constant.{ BytesToPushOntoStack, _ }
import org.bitcoins.core.script.control.{ OP_ELSE, OP_ENDIF, OP_IF, OP_RETURN }
import org.bitcoins.core.script.crypto.{ OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY, OP_CHECKSIG, OP_HASH160 }
import org.bitcoins.core.script.locktime.{ OP_CHECKLOCKTIMEVERIFY, OP_CHECKSEQUENCEVERIFY }
import org.bitcoins.core.script.reserved.UndefinedOP_NOP
import org.bitcoins.core.script.stack.{OP_DROP, OP_DUP}
import org.bitcoins.core.serializers.script.{RawScriptPubKeyParser, ScriptParser}
import org.bitcoins.core.script.stack.{ OP_DROP, OP_DUP }
import org.bitcoins.core.serializers.script.{ RawScriptPubKeyParser, ScriptParser }
import org.bitcoins.core.util._
import scala.util.{Failure, Success, Try}
import scala.util.{ Failure, Success, Try }
/**
* Created by chris on 12/26/15.
@ -23,23 +23,23 @@ import scala.util.{Failure, Success, Try}
sealed trait ScriptPubKey extends NetworkElement {
/** The size of the script, this is used for network serialization */
def compactSizeUInt : CompactSizeUInt = CompactSizeUInt.parseCompactSizeUInt(bytes)
def compactSizeUInt: CompactSizeUInt = CompactSizeUInt.parseCompactSizeUInt(bytes)
/**
* Representation of a scriptPubKey in a parsed assembly format
* this data structure can be run through the script interpreter to
* see if a script evaluates to true
* Note: The first byte(s) inside the byte array is the [[CompactSizeUInt]]
* used to represent the size of the script serialization
*/
lazy val asm : Seq[ScriptToken] = ScriptParser.fromBytes(bytes.splitAt(compactSizeUInt.size.toInt)._2)
* Representation of a scriptPubKey in a parsed assembly format
* this data structure can be run through the script interpreter to
* see if a script evaluates to true
* Note: The first byte(s) inside the byte array is the [[CompactSizeUInt]]
* used to represent the size of the script serialization
*/
lazy val asm: Seq[ScriptToken] = ScriptParser.fromBytes(bytes.splitAt(compactSizeUInt.size.toInt)._2)
/** The byte representation of [[asm]], this does NOT have the bytes
* for the [[org.bitcoins.core.protocol.CompactSizeUInt]] in the [[org.bitcoins.core.protocol.script.ScriptPubKey]]
*/
/**
* The byte representation of [[asm]], this does NOT have the bytes
* for the [[org.bitcoins.core.protocol.CompactSizeUInt]] in the [[org.bitcoins.core.protocol.script.ScriptPubKey]]
*/
lazy val asmBytes: Seq[Byte] = asm.flatMap(_.bytes)
}
/**
@ -48,16 +48,15 @@ sealed trait ScriptPubKey extends NetworkElement {
* Format: OP_DUP OP_HASH160 <PubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
*/
sealed trait P2PKHScriptPubKey extends ScriptPubKey {
def pubKeyHash : Sha256Hash160Digest = Sha256Hash160Digest(asm(asm.length - 3).bytes)
def pubKeyHash: Sha256Hash160Digest = Sha256Hash160Digest(asm(asm.length - 3).bytes)
}
object P2PKHScriptPubKey extends ScriptFactory[P2PKHScriptPubKey] {
private case class P2PKHScriptPubKeyImpl(bytes: Seq[Byte]) extends P2PKHScriptPubKey {
override def toString = "P2PKHScriptPubKeyImpl(" + hex + ")"
}
def apply(pubKey : ECPublicKey) : P2PKHScriptPubKey = {
def apply(pubKey: ECPublicKey): P2PKHScriptPubKey = {
val hash = CryptoUtil.sha256Hash160(pubKey.bytes)
P2PKHScriptPubKey(hash)
}
@ -69,14 +68,14 @@ object P2PKHScriptPubKey extends ScriptFactory[P2PKHScriptPubKey] {
}
def fromAsm(asm: Seq[ScriptToken]): P2PKHScriptPubKey = {
buildScript(asm, P2PKHScriptPubKeyImpl(_),isP2PKHScriptPubKey(_), "Given asm was not a p2pkh scriptPubKey, got: " + asm)
buildScript(asm, P2PKHScriptPubKeyImpl(_), isP2PKHScriptPubKey(_), "Given asm was not a p2pkh scriptPubKey, got: " + asm)
}
def apply(asm :Seq[ScriptToken]) : P2PKHScriptPubKey = fromAsm(asm)
def apply(asm: Seq[ScriptToken]): P2PKHScriptPubKey = fromAsm(asm)
/** Checks if the given asm matches the pattern for [[P2PKHScriptPubKey]] */
def isP2PKHScriptPubKey(asm: Seq[ScriptToken]): Boolean = asm match {
case List(OP_DUP, OP_HASH160, x : BytesToPushOntoStack, y : ScriptConstant, OP_EQUALVERIFY, OP_CHECKSIG) => true
case List(OP_DUP, OP_HASH160, x: BytesToPushOntoStack, y: ScriptConstant, OP_EQUALVERIFY, OP_CHECKSIG) => true
case _ => false
}
}
@ -89,14 +88,14 @@ object P2PKHScriptPubKey extends ScriptFactory[P2PKHScriptPubKey] {
sealed trait MultiSignatureScriptPubKey extends ScriptPubKey {
/** Returns the amount of required signatures for this multisignature script pubkey output */
def requiredSigs : Int = {
def requiredSigs: Int = {
val asmWithoutPushOps = asm.filterNot(_.isInstanceOf[BytesToPushOntoStack])
val opCheckMultiSigIndex = if (asm.indexOf(OP_CHECKMULTISIG) != -1) asmWithoutPushOps.indexOf(OP_CHECKMULTISIG) else asmWithoutPushOps.indexOf(OP_CHECKMULTISIGVERIFY)
//magic number 2 represents the maxSig operation and the OP_CHECKMULTISIG operation at the end of the asm
val numSigsRequired = asmWithoutPushOps(opCheckMultiSigIndex - maxSigs.toInt - 2)
numSigsRequired match {
case x : ScriptNumber => x.toInt
case c : ScriptConstant if ScriptNumber(c.hex).toLong <= ScriptSettings.maxPublicKeysPerMultiSig =>
case x: ScriptNumber => x.toInt
case c: ScriptConstant if ScriptNumber(c.hex).toLong <= ScriptSettings.maxPublicKeysPerMultiSig =>
ScriptNumber(c.hex).toInt
case _ => throw new RuntimeException("The first element of the multisignature pubkey must be a script number operation\n" +
"operation: " + numSigsRequired +
@ -105,28 +104,27 @@ sealed trait MultiSignatureScriptPubKey extends ScriptPubKey {
}
/** The maximum amount of signatures for this multisignature script pubkey output */
def maxSigs : Int = {
def maxSigs: Int = {
if (checkMultiSigIndex == -1 || checkMultiSigIndex == 0) {
//means that we do not have a max signature requirement
0
} else {
asm(checkMultiSigIndex - 1) match {
case x : ScriptNumber => x.toInt
case c : ScriptConstant if ScriptNumber(c.hex).toLong <= ScriptSettings.maxPublicKeysPerMultiSig =>
case x: ScriptNumber => x.toInt
case c: ScriptConstant if ScriptNumber(c.hex).toLong <= ScriptSettings.maxPublicKeysPerMultiSig =>
ScriptNumber(c.hex).toInt
case x => throw new RuntimeException("The element preceding a OP_CHECKMULTISIG operation in a multisignature pubkey must be a script number operation, got: " + x)
}
}
}
/** Gives the OP_CHECKMULTISIG or OP_CHECKMULTISIGVERIFY index inside of asm */
private def checkMultiSigIndex : Int = {
private def checkMultiSigIndex: Int = {
if (asm.indexOf(OP_CHECKMULTISIG) != -1) asm.indexOf(OP_CHECKMULTISIG) else asm.indexOf(OP_CHECKMULTISIGVERIFY)
}
/** Returns the public keys encoded into the scriptPubKey */
def publicKeys : Seq[ECPublicKey] = {
def publicKeys: Seq[ECPublicKey] = {
asm.filter(_.isInstanceOf[ScriptConstant]).slice(1, maxSigs + 1).map(key => ECPublicKey(key.hex))
}
}
@ -137,7 +135,7 @@ object MultiSignatureScriptPubKey extends ScriptFactory[MultiSignatureScriptPubK
override def toString = "MultiSignatureScriptPubKeyImpl(" + hex + ")"
}
def apply(requiredSigs : Int, pubKeys : Seq[ECPublicKey]): MultiSignatureScriptPubKey = {
def apply(requiredSigs: Int, pubKeys: Seq[ECPublicKey]): MultiSignatureScriptPubKey = {
require(requiredSigs <= ScriptSettings.maxPublicKeysPerMultiSig, "We cannot have more required signatures than: " +
ScriptSettings.maxPublicKeysPerMultiSig + " got: " + requiredSigs)
require(pubKeys.length <= ScriptSettings.maxPublicKeysPerMultiSig, "We cannot have more public keys than " +
@ -157,7 +155,7 @@ object MultiSignatureScriptPubKey extends ScriptFactory[MultiSignatureScriptPubK
val pushOps = BitcoinScriptUtil.calculatePushOp(scriptNum)
pushOps ++ Seq(scriptNum)
}
val pubKeysWithPushOps : Seq[Seq[ScriptToken]] = for {
val pubKeysWithPushOps: Seq[Seq[ScriptToken]] = for {
pubKey <- pubKeys
pushOps = BitcoinScriptUtil.calculatePushOp(pubKey.bytes)
constant = ScriptConstant(pubKey.bytes)
@ -167,13 +165,13 @@ object MultiSignatureScriptPubKey extends ScriptFactory[MultiSignatureScriptPubK
}
def fromAsm(asm: Seq[ScriptToken]): MultiSignatureScriptPubKey = {
buildScript(asm,MultiSignatureScriptPubKeyImpl(_),isMultiSignatureScriptPubKey(_), "Given asm was not a MultSignatureScriptPubKey, got: " + asm)
buildScript(asm, MultiSignatureScriptPubKeyImpl(_), isMultiSignatureScriptPubKey(_), "Given asm was not a MultSignatureScriptPubKey, got: " + asm)
}
def apply(asm :Seq[ScriptToken]) : MultiSignatureScriptPubKey = fromAsm(asm)
def apply(asm: Seq[ScriptToken]): MultiSignatureScriptPubKey = fromAsm(asm)
/** Determines if the given script tokens are a multisignature scriptPubKey */
def isMultiSignatureScriptPubKey(asm : Seq[ScriptToken]) : Boolean = {
def isMultiSignatureScriptPubKey(asm: Seq[ScriptToken]): Boolean = {
val isNotEmpty = asm.size > 0
val containsMultiSigOp = asm.contains(OP_CHECKMULTISIG) || asm.contains(OP_CHECKMULTISIGVERIFY)
//we need either the first or second asm operation to indicate how many signatures are required
@ -194,7 +192,7 @@ object MultiSignatureScriptPubKey extends ScriptFactory[MultiSignatureScriptPubK
}
}
val standardOps = asm.filter(op => op.isInstanceOf[ScriptNumber] || op == OP_CHECKMULTISIG ||
val standardOps = asm.filter(op => op.isInstanceOf[ScriptNumber] || op == OP_CHECKMULTISIG ||
op == OP_CHECKMULTISIGVERIFY || op.isInstanceOf[ScriptConstant] || op.isInstanceOf[BytesToPushOntoStack])
(hasRequiredSignaturesTry, hasMaximumSignaturesTry) match {
case (Success(hasRequiredSignatures), Success(hasMaximumSignatures)) =>
@ -208,13 +206,14 @@ object MultiSignatureScriptPubKey extends ScriptFactory[MultiSignatureScriptPubK
}
/**
* Checks that the given script token is with the range of the maximum amount of
* public keys we can have in a [[MultiSignatureScriptPubKey]] */
private def isValidPubKeyNumber(token : ScriptToken): Boolean = token match {
case constant : ScriptConstant =>
* Checks that the given script token is with the range of the maximum amount of
* public keys we can have in a [[MultiSignatureScriptPubKey]]
*/
private def isValidPubKeyNumber(token: ScriptToken): Boolean = token match {
case constant: ScriptConstant =>
constant.isInstanceOf[ScriptNumber] ||
ScriptNumber(constant.bytes) <= ScriptNumber(ScriptSettings.maxPublicKeysPerMultiSig)
case _ : ScriptToken => false
case _: ScriptToken => false
}
}
@ -225,7 +224,7 @@ object MultiSignatureScriptPubKey extends ScriptFactory[MultiSignatureScriptPubK
*/
sealed trait P2SHScriptPubKey extends ScriptPubKey {
/** The hash of the script for which this scriptPubKey is being created from */
def scriptHash : Sha256Hash160Digest = Sha256Hash160Digest(asm(asm.length - 2).bytes)
def scriptHash: Sha256Hash160Digest = Sha256Hash160Digest(asm(asm.length - 2).bytes)
}
object P2SHScriptPubKey extends ScriptFactory[P2SHScriptPubKey] {
@ -233,7 +232,7 @@ object P2SHScriptPubKey extends ScriptFactory[P2SHScriptPubKey] {
override def toString = "P2SHScriptPubKeyImpl(" + hex + ")"
}
def apply(scriptPubKey: ScriptPubKey) : P2SHScriptPubKey = {
def apply(scriptPubKey: ScriptPubKey): P2SHScriptPubKey = {
val hash = CryptoUtil.sha256Hash160(scriptPubKey.asmBytes)
P2SHScriptPubKey(hash)
}
@ -246,15 +245,15 @@ object P2SHScriptPubKey extends ScriptFactory[P2SHScriptPubKey] {
/** Checks if the given asm matches the pattern for [[P2SHScriptPubKey]] */
def isP2SHScriptPubKey(asm: Seq[ScriptToken]): Boolean = asm match {
case List(OP_HASH160, x : BytesToPushOntoStack, y : ScriptConstant, OP_EQUAL) => true
case List(OP_HASH160, x: BytesToPushOntoStack, y: ScriptConstant, OP_EQUAL) => true
case _ => false
}
def fromAsm(asm: Seq[ScriptToken]): P2SHScriptPubKey = {
buildScript(asm,P2SHScriptPubKeyImpl(_),isP2SHScriptPubKey(_),"Given asm was not a p2sh scriptPubkey, got: " + asm)
buildScript(asm, P2SHScriptPubKeyImpl(_), isP2SHScriptPubKey(_), "Given asm was not a p2sh scriptPubkey, got: " + asm)
}
def apply(asm :Seq[ScriptToken]) : P2SHScriptPubKey = fromAsm(asm)
def apply(asm: Seq[ScriptToken]): P2SHScriptPubKey = fromAsm(asm)
}
/**
@ -263,7 +262,7 @@ object P2SHScriptPubKey extends ScriptFactory[P2SHScriptPubKey] {
* Format: <pubkey> OP_CHECKSIG
*/
sealed trait P2PKScriptPubKey extends ScriptPubKey {
def publicKey : ECPublicKey = ECPublicKey(BitcoinScriptUtil.filterPushOps(asm).head.bytes)
def publicKey: ECPublicKey = ECPublicKey(BitcoinScriptUtil.filterPushOps(asm).head.bytes)
}
object P2PKScriptPubKey extends ScriptFactory[P2PKScriptPubKey] {
@ -272,21 +271,21 @@ object P2PKScriptPubKey extends ScriptFactory[P2PKScriptPubKey] {
override def toString = "P2PKScriptPubKeyImpl(" + hex + ")"
}
def apply(pubKey : ECPublicKey): P2PKScriptPubKey = {
def apply(pubKey: ECPublicKey): P2PKScriptPubKey = {
val pushOps = BitcoinScriptUtil.calculatePushOp(pubKey.bytes)
val asm = pushOps ++ Seq(ScriptConstant(pubKey.bytes), OP_CHECKSIG)
P2PKScriptPubKey(asm)
}
def fromAsm(asm: Seq[ScriptToken]): P2PKScriptPubKey = {
buildScript(asm,P2PKScriptPubKeyImpl(_), isP2PKScriptPubKey(_), "Given asm was not a p2pk scriptPubKey, got: " + asm)
buildScript(asm, P2PKScriptPubKeyImpl(_), isP2PKScriptPubKey(_), "Given asm was not a p2pk scriptPubKey, got: " + asm)
}
def apply(asm :Seq[ScriptToken]) : P2PKScriptPubKey = fromAsm(asm)
def apply(asm: Seq[ScriptToken]): P2PKScriptPubKey = fromAsm(asm)
/** Sees if the given asm matches the [[P2PKHScriptPubKey]] pattern */
def isP2PKScriptPubKey(asm: Seq[ScriptToken]): Boolean = asm match {
case List(b : BytesToPushOntoStack, x : ScriptConstant, OP_CHECKSIG) => true
case List(b: BytesToPushOntoStack, x: ScriptConstant, OP_CHECKSIG) => true
case _ => false
}
@ -294,20 +293,20 @@ object P2PKScriptPubKey extends ScriptFactory[P2PKScriptPubKey] {
sealed trait LockTimeScriptPubKey extends ScriptPubKey {
/** Determines the nested ScriptPubKey inside the LockTimeScriptPubKey */
def nestedScriptPubKey : ScriptPubKey = {
val bool : Boolean = asm.head.isInstanceOf[ScriptNumberOperation]
def nestedScriptPubKey: ScriptPubKey = {
val bool: Boolean = asm.head.isInstanceOf[ScriptNumberOperation]
bool match {
case true => ScriptPubKey(asm.slice(3, asm.length))
case true => ScriptPubKey(asm.slice(3, asm.length))
case false => ScriptPubKey(asm.slice(4, asm.length))
}
}
/** The relative locktime value (i.e. the amount of time the output should remain unspendable) */
def locktime : ScriptNumber = {
def locktime: ScriptNumber = {
asm.head match {
case scriptNumOp: ScriptNumberOperation => ScriptNumber(scriptNumOp.toLong)
case _: BytesToPushOntoStack => ScriptNumber(asm(1).hex)
case x @ (_ : ScriptConstant | _ : ScriptOperation) => throw new IllegalArgumentException("In a LockTimeScriptPubKey, " +
case _: BytesToPushOntoStack => ScriptNumber(asm(1).hex)
case x @ (_: ScriptConstant | _: ScriptOperation) => throw new IllegalArgumentException("In a LockTimeScriptPubKey, " +
"the first asm must be either a ScriptNumberOperation (i.e. OP_5), or the BytesToPushOntoStack for the proceeding ScriptConstant.")
}
}
@ -328,11 +327,11 @@ object LockTimeScriptPubKey extends ScriptFactory[LockTimeScriptPubKey] {
}
/**
* Represents a scriptPubKey that contains OP_CHECKLOCKTIMEVERIFY.
* Adds an absolute/defined locktime condition to any scriptPubKey.
* [[https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki]]
* Format: <locktime> OP_CLTV OP_DROP <scriptPubKey>
*/
* Represents a scriptPubKey that contains OP_CHECKLOCKTIMEVERIFY.
* Adds an absolute/defined locktime condition to any scriptPubKey.
* [[https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki]]
* Format: <locktime> OP_CLTV OP_DROP <scriptPubKey>
*/
sealed trait CLTVScriptPubKey extends LockTimeScriptPubKey
object CLTVScriptPubKey extends ScriptFactory[CLTVScriptPubKey] {
@ -340,19 +339,19 @@ object CLTVScriptPubKey extends ScriptFactory[CLTVScriptPubKey] {
override def toString = "CLTVScriptPubKeyImpl(" + hex + ")"
}
def fromAsm(asm : Seq[ScriptToken]) : CLTVScriptPubKey = {
buildScript(asm,CLTVScriptPubKeyImpl(_),isCLTVScriptPubKey(_),"Given asm was not a CLTVScriptPubKey, got: " + asm)
def fromAsm(asm: Seq[ScriptToken]): CLTVScriptPubKey = {
buildScript(asm, CLTVScriptPubKeyImpl(_), isCLTVScriptPubKey(_), "Given asm was not a CLTVScriptPubKey, got: " + asm)
}
def apply (asm: Seq[ScriptToken]) : CLTVScriptPubKey = fromAsm(asm)
def apply(asm: Seq[ScriptToken]): CLTVScriptPubKey = fromAsm(asm)
def apply(locktime : ScriptNumber, scriptPubKey : ScriptPubKey) : CLTVScriptPubKey = {
def apply(locktime: ScriptNumber, scriptPubKey: ScriptPubKey): CLTVScriptPubKey = {
val scriptOp = BitcoinScriptUtil.minimalScriptNumberRepresentation(locktime)
val scriptNum : Seq[ScriptToken] = if (scriptOp.isInstanceOf[ScriptNumberOperation]) {
val scriptNum: Seq[ScriptToken] = if (scriptOp.isInstanceOf[ScriptNumberOperation]) {
Seq(scriptOp)
} else {
val pushOpsLockTime= BitcoinScriptUtil.calculatePushOp(locktime.bytes)
val pushOpsLockTime = BitcoinScriptUtil.calculatePushOp(locktime.bytes)
pushOpsLockTime ++ Seq(ScriptConstant(locktime.bytes))
}
@ -362,46 +361,47 @@ object CLTVScriptPubKey extends ScriptFactory[CLTVScriptPubKey] {
CLTVScriptPubKey(asm)
}
def isCLTVScriptPubKey(asm : Seq[ScriptToken]) : Boolean = {
def isCLTVScriptPubKey(asm: Seq[ScriptToken]): Boolean = {
if (asm.head.isInstanceOf[BytesToPushOntoStack]) {
val tailTokens = asm.slice(4, asm.length)
if (P2SHScriptPubKey.isP2SHScriptPubKey(tailTokens) || tailTokens.contains(OP_CHECKLOCKTIMEVERIFY)) return false
asm.slice(0,4) match {
case List(lockTimeBytesToPush : BytesToPushOntoStack, lockTime : ScriptConstant, OP_CHECKLOCKTIMEVERIFY, OP_DROP) =>
asm.slice(0, 4) match {
case List(lockTimeBytesToPush: BytesToPushOntoStack, lockTime: ScriptConstant, OP_CHECKLOCKTIMEVERIFY, OP_DROP) =>
validScriptAfterLockTime(tailTokens)
case _ => false
}
} else {
val tailTokens = asm.slice(3, asm.length)
if (P2SHScriptPubKey.isP2SHScriptPubKey(tailTokens) || tailTokens.contains(OP_CHECKLOCKTIMEVERIFY)) return false
asm.slice(0,3) match {
case List(scriptNumOp : ScriptNumberOperation, OP_CHECKLOCKTIMEVERIFY, OP_DROP) =>
asm.slice(0, 3) match {
case List(scriptNumOp: ScriptNumberOperation, OP_CHECKLOCKTIMEVERIFY, OP_DROP) =>
validScriptAfterLockTime(tailTokens)
case _ => false
}
}
}
/** We need this check because sometimes we can get very lucky in having a non valid
* lock time script that has the first 4 bytes as a valid locktime script
* and then the bytes after the first 4 bytes gets lucky and is parsed by our [[ScriptParser]]
* A good way to see if this is _actually_ a valid script is by checking if we have any
* [[UndefinedOP_NOP]] in the script, which means we definitely don't have a valid locktime script
* See this example of what happened before we added this check:
*
* [[https://travis-ci.org/bitcoin-s/bitcoin-s-core/builds/201652191#L2526]]
*/
/**
* We need this check because sometimes we can get very lucky in having a non valid
* lock time script that has the first 4 bytes as a valid locktime script
* and then the bytes after the first 4 bytes gets lucky and is parsed by our [[ScriptParser]]
* A good way to see if this is _actually_ a valid script is by checking if we have any
* [[UndefinedOP_NOP]] in the script, which means we definitely don't have a valid locktime script
* See this example of what happened before we added this check:
*
* [[https://travis-ci.org/bitcoin-s/bitcoin-s-core/builds/201652191#L2526]]
*/
def validScriptAfterLockTime(asm: Seq[ScriptToken]): Boolean = {
!asm.exists(_.isInstanceOf[UndefinedOP_NOP])
}
}
/**
* Represents a scriptPubKey that contains OP_CHECKSEQUENCEVERIFY.
* Adds a relative lockTime condition to any scriptPubKey.
* https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki
* Format: <locktime> OP_CSV OP_DROP <scriptPubKey>
*/
* Represents a scriptPubKey that contains OP_CHECKSEQUENCEVERIFY.
* Adds a relative lockTime condition to any scriptPubKey.
* https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki
* Format: <locktime> OP_CSV OP_DROP <scriptPubKey>
*/
sealed trait CSVScriptPubKey extends LockTimeScriptPubKey
object CSVScriptPubKey extends ScriptFactory[CSVScriptPubKey] {
@ -409,19 +409,19 @@ object CSVScriptPubKey extends ScriptFactory[CSVScriptPubKey] {
override def toString = "CSVScriptPubKeyImpl(" + hex + ")"
}
def fromAsm (asm : Seq[ScriptToken]) : CSVScriptPubKey = {
buildScript(asm, CSVScriptPubKeyImpl(_), isCSVScriptPubKey(_), "Given asm was not a CSVScriptPubKey, got: " + asm)
def fromAsm(asm: Seq[ScriptToken]): CSVScriptPubKey = {
buildScript(asm, CSVScriptPubKeyImpl(_), isCSVScriptPubKey(_), "Given asm was not a CSVScriptPubKey, got: " + asm)
}
def apply(asm : Seq[ScriptToken]) : CSVScriptPubKey = fromAsm(asm)
def apply(asm: Seq[ScriptToken]): CSVScriptPubKey = fromAsm(asm)
def apply(relativeLockTime : ScriptNumber, scriptPubKey : ScriptPubKey) : CSVScriptPubKey = {
def apply(relativeLockTime: ScriptNumber, scriptPubKey: ScriptPubKey): CSVScriptPubKey = {
val scriptOp = BitcoinScriptUtil.minimalScriptNumberRepresentation(relativeLockTime)
val scriptNum : Seq[ScriptToken] = if (scriptOp.isInstanceOf[ScriptNumberOperation]) {
val scriptNum: Seq[ScriptToken] = if (scriptOp.isInstanceOf[ScriptNumberOperation]) {
Seq(scriptOp)
} else {
val pushOpsLockTime= BitcoinScriptUtil.calculatePushOp(relativeLockTime.bytes)
val pushOpsLockTime = BitcoinScriptUtil.calculatePushOp(relativeLockTime.bytes)
pushOpsLockTime ++ Seq(ScriptConstant(relativeLockTime.bytes))
}
@ -431,20 +431,20 @@ object CSVScriptPubKey extends ScriptFactory[CSVScriptPubKey] {
CSVScriptPubKey(asm)
}
def isCSVScriptPubKey(asm : Seq[ScriptToken]) : Boolean = {
def isCSVScriptPubKey(asm: Seq[ScriptToken]): Boolean = {
if (asm.head.isInstanceOf[BytesToPushOntoStack]) {
val tailTokens = asm.slice(4, asm.length)
if (P2SHScriptPubKey.isP2SHScriptPubKey(tailTokens) || tailTokens.contains(OP_CHECKSEQUENCEVERIFY)) return false
asm.slice(0,4) match {
case List(lockTimeBytesToPush : BytesToPushOntoStack, lockTime : ScriptConstant, OP_CHECKSEQUENCEVERIFY, OP_DROP) =>
asm.slice(0, 4) match {
case List(lockTimeBytesToPush: BytesToPushOntoStack, lockTime: ScriptConstant, OP_CHECKSEQUENCEVERIFY, OP_DROP) =>
CLTVScriptPubKey.validScriptAfterLockTime(tailTokens)
case _ => false
}
} else {
val tailTokens = asm.slice(3, asm.length)
if (P2SHScriptPubKey.isP2SHScriptPubKey(tailTokens) || tailTokens.contains(OP_CHECKSEQUENCEVERIFY)) return false
asm.slice(0,3) match {
case List(numberOp : ScriptNumberOperation, OP_CHECKSEQUENCEVERIFY, OP_DROP) =>
asm.slice(0, 3) match {
case List(numberOp: ScriptNumberOperation, OP_CHECKSEQUENCEVERIFY, OP_DROP) =>
CLTVScriptPubKey.validScriptAfterLockTime(tailTokens)
case _ => false
}
@ -462,10 +462,10 @@ object NonStandardScriptPubKey extends ScriptFactory[NonStandardScriptPubKey] {
def fromAsm(asm: Seq[ScriptToken]): NonStandardScriptPubKey = {
//everything can be a NonStandardScriptPubkey, thus the trivially true function
buildScript(asm,NonStandardScriptPubKeyImpl(_), {_ => true }, "")
buildScript(asm, NonStandardScriptPubKeyImpl(_), { _ => true }, "")
}
def apply(asm : Seq[ScriptToken]) : NonStandardScriptPubKey = fromAsm(asm)
def apply(asm: Seq[ScriptToken]): NonStandardScriptPubKey = fromAsm(asm)
}
/** Represents the empty ScriptPubKey */
@ -475,10 +475,10 @@ case object EmptyScriptPubKey extends ScriptPubKey {
/** Factory companion object used to create ScriptPubKey objects */
object ScriptPubKey extends Factory[ScriptPubKey] {
def empty : ScriptPubKey = fromAsm(Nil)
def empty: ScriptPubKey = fromAsm(Nil)
/** Creates a scriptPubKey from its asm representation */
def fromAsm(asm : Seq[ScriptToken]) : ScriptPubKey = asm match {
def fromAsm(asm: Seq[ScriptToken]): ScriptPubKey = asm match {
case Nil => EmptyScriptPubKey
case _ if P2PKHScriptPubKey.isP2PKHScriptPubKey(asm) => P2PKHScriptPubKey(asm)
case _ if P2SHScriptPubKey.isP2SHScriptPubKey(asm) => P2SHScriptPubKey(asm)
@ -492,9 +492,9 @@ object ScriptPubKey extends Factory[ScriptPubKey] {
case _ => NonStandardScriptPubKey(asm)
}
def fromBytes(bytes : Seq[Byte]) : ScriptPubKey = RawScriptPubKeyParser.read(bytes)
def fromBytes(bytes: Seq[Byte]): ScriptPubKey = RawScriptPubKeyParser.read(bytes)
def apply(asm : Seq[ScriptToken]) : ScriptPubKey = fromAsm(asm)
def apply(asm: Seq[ScriptToken]): ScriptPubKey = fromAsm(asm)
}
/** This type represents a [[ScriptPubKey]] to evaluate a [[ScriptWitness]] */
@ -506,7 +506,7 @@ sealed trait WitnessScriptPubKey extends ScriptPubKey {
object WitnessScriptPubKey {
/** Witness scripts must begin with one of these operations, see BIP141 */
val validWitVersions: Seq[ScriptNumberOperation] = Seq(OP_0,OP_1,OP_2,OP_3,OP_4,OP_5,OP_6, OP_7, OP_8,
val validWitVersions: Seq[ScriptNumberOperation] = Seq(OP_0, OP_1, OP_2, OP_3, OP_4, OP_5, OP_6, OP_7, OP_8,
OP_9, OP_10, OP_11, OP_12, OP_13, OP_14, OP_15, OP_16)
val unassignedWitVersions = validWitVersions.tail
@ -520,10 +520,11 @@ object WitnessScriptPubKey {
case _ => None
}
/** Checks if the given asm is a valid [[org.bitcoins.core.protocol.script.WitnessScriptPubKey]]
* Mimics this function inside of Bitcoin Core
* [[https://github.com/bitcoin/bitcoin/blob/14d01309bed59afb08651f2b701ff90371b15b20/src/script/script.cpp#L223-L237]]
*/
/**
* Checks if the given asm is a valid [[org.bitcoins.core.protocol.script.WitnessScriptPubKey]]
* Mimics this function inside of Bitcoin Core
* [[https://github.com/bitcoin/bitcoin/blob/14d01309bed59afb08651f2b701ff90371b15b20/src/script/script.cpp#L223-L237]]
*/
def isWitnessScriptPubKey(asm: Seq[ScriptToken]): Boolean = {
val bytes = asm.flatMap(_.bytes)
val firstOp = asm.headOption
@ -541,20 +542,22 @@ sealed abstract class WitnessScriptPubKeyV0 extends WitnessScriptPubKey {
object WitnessScriptPubKeyV0 {
/** Mimics the function to determine if a [[ScriptPubKey]] contains a witness
* A witness program is any valid [[ScriptPubKey]] that consists of a 1 byte push op and then a data push
* between 2 and 40 bytes
* Verison 0 witness program need to have an OP_0 as the first operation
* [[https://github.com/bitcoin/bitcoin/blob/449f9b8debcceb61a92043bc7031528a53627c47/src/script/script.cpp#L215-L229]]
* */
/**
* Mimics the function to determine if a [[ScriptPubKey]] contains a witness
* A witness program is any valid [[ScriptPubKey]] that consists of a 1 byte push op and then a data push
* between 2 and 40 bytes
* Verison 0 witness program need to have an OP_0 as the first operation
* [[https://github.com/bitcoin/bitcoin/blob/449f9b8debcceb61a92043bc7031528a53627c47/src/script/script.cpp#L215-L229]]
*/
def isValid(asm: Seq[ScriptToken]): Boolean = {
WitnessScriptPubKey.isWitnessScriptPubKey(asm) && asm.headOption == Some(OP_0)
}
}
/** Represents the pay-to-witness-pubkeyhash script pubkey type as defined in BIP141
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#P2WPKH]]
* */
/**
* Represents the pay-to-witness-pubkeyhash script pubkey type as defined in BIP141
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#P2WPKH]]
*/
sealed abstract class P2WPKHWitnessSPKV0 extends WitnessScriptPubKeyV0 {
def pubKeyHash: Sha256Hash160Digest = Sha256Hash160Digest(asm(3).bytes)
override def toString = s"P2WPKHWitnessSPKV0($hex)"
@ -564,13 +567,12 @@ object P2WPKHWitnessSPKV0 extends ScriptFactory[P2WPKHWitnessSPKV0] {
private case class P2WPKHWitnessSPKV0Impl(bytes: Seq[Byte]) extends P2WPKHWitnessSPKV0
override def fromAsm(asm: Seq[ScriptToken]): P2WPKHWitnessSPKV0 = {
buildScript(asm,P2WPKHWitnessSPKV0Impl(_), isValid(_), s"Given asm was not a P2WPKHWitnessSPKV0, got $asm")
buildScript(asm, P2WPKHWitnessSPKV0Impl(_), isValid(_), s"Given asm was not a P2WPKHWitnessSPKV0, got $asm")
}
def isValid(asm: Seq[ScriptToken]): Boolean = {
WitnessScriptPubKeyV0.isValid(asm) &&
asm.flatMap(_.bytes).size == 22
asm.flatMap(_.bytes).size == 22
}
/** Creates a P2WPKH witness script pubkey */
@ -581,9 +583,10 @@ object P2WPKHWitnessSPKV0 extends ScriptFactory[P2WPKHWitnessSPKV0] {
}
}
/** Reprents the pay-to-witness-scripthash script pubkey type as defined in BIP141
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#p2wsh]]
* */
/**
* Reprents the pay-to-witness-scripthash script pubkey type as defined in BIP141
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#p2wsh]]
*/
sealed abstract class P2WSHWitnessSPKV0 extends WitnessScriptPubKeyV0 {
def scriptHash: Sha256Digest = Sha256Digest(asm(3).bytes)
override def toString = s"P2WSHWitnessSPKV0($hex)"
@ -592,14 +595,13 @@ sealed abstract class P2WSHWitnessSPKV0 extends WitnessScriptPubKeyV0 {
object P2WSHWitnessSPKV0 extends ScriptFactory[P2WSHWitnessSPKV0] {
private case class P2WSHWitnessSPKV0Impl(bytes: Seq[Byte]) extends P2WSHWitnessSPKV0
override def fromAsm(asm: Seq[ScriptToken]): P2WSHWitnessSPKV0 = {
buildScript(asm,P2WSHWitnessSPKV0Impl(_), isValid(_), s"Given asm was not a P2WSHWitnessSPKV0, got $asm")
buildScript(asm, P2WSHWitnessSPKV0Impl(_), isValid(_), s"Given asm was not a P2WSHWitnessSPKV0, got $asm")
}
def isValid(asm: Seq[ScriptToken]): Boolean = {
WitnessScriptPubKeyV0.isValid(asm) &&
asm.flatMap(_.bytes).size == 34
asm.flatMap(_.bytes).size == 34
}
def apply(spk: ScriptPubKey): P2WSHWitnessSPKV0 = {
@ -626,12 +628,13 @@ object UnassignedWitnessScriptPubKey extends ScriptFactory[UnassignedWitnessScri
def apply(asm: Seq[ScriptToken]): UnassignedWitnessScriptPubKey = fromAsm(asm)
}
/** This trait represents the witness commitment found in the coinbase transaction
* This is needed to commit to the wtxids of all of the witness transactions, since the merkle tree
* does not commit to the witnesses for all [[org.bitcoins.core.protocol.transaction.WitnessTransaction]]
* See BIP141 for more info
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#commitment-structure]]
*/
/**
* This trait represents the witness commitment found in the coinbase transaction
* This is needed to commit to the wtxids of all of the witness transactions, since the merkle tree
* does not commit to the witnesses for all [[org.bitcoins.core.protocol.transaction.WitnessTransaction]]
* See BIP141 for more info
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#commitment-structure]]
*/
sealed trait WitnessCommitment extends ScriptPubKey {
/** The commitment to the [[WitnessTransaction]]s in the [[Block]] */
def witnessRootHash: DoubleSha256Digest = DoubleSha256Digest(asm(2).bytes.splitAt(4)._2)
@ -654,35 +657,37 @@ object WitnessCommitment extends ScriptFactory[WitnessCommitment] {
def apply(hash: DoubleSha256Digest): WitnessCommitment = {
WitnessCommitment(Seq(OP_RETURN, BytesToPushOntoStack(36), ScriptConstant(commitmentHeader + hash.hex)))
}
/** This determines if the given asm has the correct witness structure according to BIP141
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#commitment-structure]] */
/**
* This determines if the given asm has the correct witness structure according to BIP141
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#commitment-structure]]
*/
def isWitnessCommitment(asm: Seq[ScriptToken]): Boolean = {
if (asm.size < 3) false
else {
val minCommitmentSize = 38
val Seq(opReturn, pushOp, constant) = asm.take(3)
opReturn == OP_RETURN && pushOp == BytesToPushOntoStack(36) &&
constant.hex.take(8) == commitmentHeader && asm.flatMap(_.bytes).size >= minCommitmentSize
constant.hex.take(8) == commitmentHeader && asm.flatMap(_.bytes).size >= minCommitmentSize
}
}
}
/** Represents a [[ScriptPubKey]] that either times out allowing Alice to spend from the scriptpubkey
* or allows a federation to spend from the escrow
* [[https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki#contracts-with-expiration-deadlines]]
* Format: OP_IF <multsig scriptpubkey> OP_ELSE <locktime scriptPubKey> OP_ENDIF
*/
/**
* Represents a [[ScriptPubKey]] that either times out allowing Alice to spend from the scriptpubkey
* or allows a federation to spend from the escrow
* [[https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki#contracts-with-expiration-deadlines]]
* Format: OP_IF <multsig scriptpubkey> OP_ELSE <locktime scriptPubKey> OP_ENDIF
*/
sealed trait EscrowTimeoutScriptPubKey extends ScriptPubKey {
/** The [[MultiSignatureScriptPubKey]] that can be used to spend funds */
def escrow: MultiSignatureScriptPubKey = {
val escrowAsm = asm.slice(1,opElseIndex)
val escrowAsm = asm.slice(1, opElseIndex)
MultiSignatureScriptPubKey(escrowAsm)
}
/** The [[CSVScriptPubKey]] that you can spend funds from after a certain timeout */
def timeout: LockTimeScriptPubKey = {
val timeoutAsm = asm.slice(opElseIndex+1, asm.length-1)
val timeoutAsm = asm.slice(opElseIndex + 1, asm.length - 1)
LockTimeScriptPubKey.fromAsm(timeoutAsm)
}
@ -707,9 +712,9 @@ object EscrowTimeoutScriptPubKey extends ScriptFactory[EscrowTimeoutScriptPubKey
val opElseIndex = asm.indexOf(OP_ELSE)
val correctControlStructure = asm.headOption.contains(OP_IF) && asm.last == OP_ENDIF && opElseIndex != -1
if (correctControlStructure) {
val escrowAsm = asm.slice(1,opElseIndex)
val escrowAsm = asm.slice(1, opElseIndex)
val escrow = Try(MultiSignatureScriptPubKey(escrowAsm))
val timeoutAsm = asm.slice(opElseIndex+1, asm.length-1)
val timeoutAsm = asm.slice(opElseIndex + 1, asm.length - 1)
val timeout = Try(LockTimeScriptPubKey.fromAsm(timeoutAsm))
escrow.isSuccess && timeout.isSuccess
} else false
@ -720,4 +725,3 @@ object EscrowTimeoutScriptPubKey extends ScriptFactory[EscrowTimeoutScriptPubKey
}
}

View file

@ -7,6 +7,6 @@ import org.bitcoins.core.script.constant._
*/
sealed trait ScriptPubKeyUpdateIndicator
case class UpdateScriptPubKeyAsm(asm : Seq[ScriptToken]) extends ScriptPubKeyUpdateIndicator
case class UpdateScriptPubKeyBytes(bytes : Seq[Byte]) extends ScriptPubKeyUpdateIndicator
case class UpdateScriptPubKeyAsm(asm: Seq[ScriptToken]) extends ScriptPubKeyUpdateIndicator
case class UpdateScriptPubKeyBytes(bytes: Seq[Byte]) extends ScriptPubKeyUpdateIndicator

View file

@ -1,49 +1,47 @@
package org.bitcoins.core.protocol.script
import org.bitcoins.core.crypto.{ECDigitalSignature, ECPublicKey}
import org.bitcoins.core.protocol.{CompactSizeUInt, NetworkElement}
import org.bitcoins.core.crypto.{ ECDigitalSignature, ECPublicKey }
import org.bitcoins.core.protocol.{ CompactSizeUInt, NetworkElement }
import org.bitcoins.core.script.constant._
import org.bitcoins.core.serializers.script.{RawScriptSignatureParser, ScriptParser}
import org.bitcoins.core.serializers.script.{ RawScriptSignatureParser, ScriptParser }
import org.bitcoins.core.util._
import scala.util.{Failure, Success, Try}
import scala.util.{ Failure, Success, Try }
/**
* Created by chris on 12/26/15.
*
*/
* Created by chris on 12/26/15.
*
*/
sealed abstract class ScriptSignature extends NetworkElement {
def compactSizeUInt = CompactSizeUInt.parse(bytes)
/**
* Representation of a scriptSignature in a parsed assembly format
* this data structure can be run through the script interpreter to
* see if a script evaluates to true
*
* Note: The first byte(s) inside the byte array is the [[CompactSizeUInt]]
* used to represent the size of the script serialization
*/
lazy val asm : Seq[ScriptToken] = ScriptParser.fromBytes(bytes.splitAt(compactSizeUInt.size.toInt)._2)
* Representation of a scriptSignature in a parsed assembly format
* this data structure can be run through the script interpreter to
* see if a script evaluates to true
*
* Note: The first byte(s) inside the byte array is the [[CompactSizeUInt]]
* used to represent the size of the script serialization
*/
lazy val asm: Seq[ScriptToken] = ScriptParser.fromBytes(bytes.splitAt(compactSizeUInt.size.toInt)._2)
/** Byte vector for script program WITHOUT the [[CompactSizeUInt]], this is the raw byte vector that can be run */
lazy val asmBytes = asm.flatMap(_.bytes)
/**
* The digital signatures contained inside of the script signature
* p2pkh script signatures only have one sig
* p2pk script signatures only have one sigs
* p2sh script signatures can have m sigs
* multisignature scripts can have m sigs
*/
def signatures : Seq[ECDigitalSignature]
* The digital signatures contained inside of the script signature
* p2pkh script signatures only have one sig
* p2pk script signatures only have one sigs
* p2sh script signatures can have m sigs
* multisignature scripts can have m sigs
*/
def signatures: Seq[ECDigitalSignature]
}
sealed trait NonStandardScriptSignature extends ScriptSignature {
def signatures : Seq[ECDigitalSignature] = Nil
def signatures: Seq[ECDigitalSignature] = Nil
override def toString = "NonStandardScriptSignature(" + hex + ")"
}
@ -52,8 +50,8 @@ object NonStandardScriptSignature extends ScriptFactory[NonStandardScriptSignatu
}
def fromAsm(asm : Seq[ScriptToken]): NonStandardScriptSignature = {
buildScript(asm, NonStandardScriptSignatureImpl(_),{ _ => true}, "")
def fromAsm(asm: Seq[ScriptToken]): NonStandardScriptSignature = {
buildScript(asm, NonStandardScriptSignatureImpl(_), { _ => true }, "")
}
}
@ -66,12 +64,12 @@ object NonStandardScriptSignature extends ScriptFactory[NonStandardScriptSignatu
sealed trait P2PKHScriptSignature extends ScriptSignature {
/** P2PKH scriptSigs only have one signature */
def signature : ECDigitalSignature = signatures.head
def signature: ECDigitalSignature = signatures.head
/** Gives us the public key inside of a p2pkh script signature */
def publicKey : ECPublicKey = ECPublicKey(asm.last.bytes)
def publicKey: ECPublicKey = ECPublicKey(asm.last.bytes)
override def signatures : Seq[ECDigitalSignature] = {
override def signatures: Seq[ECDigitalSignature] = {
Seq(ECDigitalSignature(asm(1).hex))
}
@ -83,24 +81,25 @@ object P2PKHScriptSignature extends ScriptFactory[P2PKHScriptSignature] {
private case class P2PKHScriptSignatureImpl(bytes: Seq[Byte]) extends P2PKHScriptSignature
def fromAsm(asm: Seq[ScriptToken]): P2PKHScriptSignature = {
buildScript(asm, P2PKHScriptSignatureImpl(_),isP2PKHScriptSig(_), "Given asm was not a P2PKHScriptSignature, got: " + asm)
buildScript(asm, P2PKHScriptSignatureImpl(_), isP2PKHScriptSig(_), "Given asm was not a P2PKHScriptSignature, got: " + asm)
}
/**
* Builds a script signature from a digital signature and a public key
* this is a pay to public key hash script sig */
def apply(signature : ECDigitalSignature, pubKey : ECPublicKey) : P2PKHScriptSignature = {
* Builds a script signature from a digital signature and a public key
* this is a pay to public key hash script sig
*/
def apply(signature: ECDigitalSignature, pubKey: ECPublicKey): P2PKHScriptSignature = {
val signatureBytesToPushOntoStack = BitcoinScriptUtil.calculatePushOp(signature.bytes)
val pubKeyBytesToPushOntoStack = BitcoinScriptUtil.calculatePushOp(pubKey.bytes)
val asm : Seq[ScriptToken] = signatureBytesToPushOntoStack ++ Seq(ScriptConstant(signature.hex)) ++
val asm: Seq[ScriptToken] = signatureBytesToPushOntoStack ++ Seq(ScriptConstant(signature.hex)) ++
pubKeyBytesToPushOntoStack ++ Seq(ScriptConstant(pubKey.hex))
fromAsm(asm)
}
/** Determines if the given asm matches a [[P2PKHScriptSignature]] */
def isP2PKHScriptSig(asm: Seq[ScriptToken]): Boolean = asm match {
case List(w : BytesToPushOntoStack, x : ScriptConstant, y : BytesToPushOntoStack,
z : ScriptConstant) =>
case List(w: BytesToPushOntoStack, x: ScriptConstant, y: BytesToPushOntoStack,
z: ScriptConstant) =>
if (ECPublicKey.isFullyValid(z.bytes)) true
else !P2SHScriptSignature.isRedeemScript(z)
case _ => false
@ -116,13 +115,12 @@ object P2PKHScriptSignature extends ScriptFactory[P2PKHScriptSignature] {
sealed trait P2SHScriptSignature extends ScriptSignature {
/** The redeemScript represents the conditions that must be satisfied to spend the output */
def redeemScript : ScriptPubKey = {
def redeemScript: ScriptPubKey = {
//for P2SH(P2WSH) the entire scriptSig asm is technically the redeem script
//see BIP141
WitnessScriptPubKey(asm).getOrElse(ScriptPubKey(ScriptParser.fromBytes(asm.last.bytes)))
}
/** Returns the script signature of this p2shScriptSig with no serialized redeemScript */
def scriptSignatureNoRedeemScript: Try[ScriptSignature] = {
//witness scriptPubKeys always have EmptyScriptSigs
@ -131,37 +129,35 @@ sealed trait P2SHScriptSignature extends ScriptSignature {
val asmWithoutRedeemScriptAndPushOp: Try[Seq[ScriptToken]] = Try {
asm(asm.size - 2) match {
case b: BytesToPushOntoStack => asm.dropRight(2)
case _ => asm.dropRight(3)
case _ => asm.dropRight(3)
}
}
val script = asmWithoutRedeemScriptAndPushOp.getOrElse(EmptyScriptSignature.asm)
ScriptSignature.fromScriptPubKey(script,redeemScript)
ScriptSignature.fromScriptPubKey(script, redeemScript)
}
}
/** Returns the public keys for the p2sh scriptSignature */
def publicKeys : Seq[ECPublicKey] = {
val pubKeys : Seq[ScriptToken] = redeemScript.asm.filter(_.isInstanceOf[ScriptConstant])
def publicKeys: Seq[ECPublicKey] = {
val pubKeys: Seq[ScriptToken] = redeemScript.asm.filter(_.isInstanceOf[ScriptConstant])
.filterNot(_.isInstanceOf[ScriptNumberOperation])
pubKeys.map(k => ECPublicKey(k.hex))
}
/** The digital signatures inside of the scriptSig */
def signatures : Seq[ECDigitalSignature] = scriptSignatureNoRedeemScript match {
def signatures: Seq[ECDigitalSignature] = scriptSignatureNoRedeemScript match {
case Failure(_) => Nil
case Success(nonRedeemScript) =>
val sigs = nonRedeemScript.asm.filter(_.isInstanceOf[ScriptConstant]).filterNot(_.isInstanceOf[ScriptNumberOperation]).filterNot(_.hex.length < 100)
sigs.map(s => ECDigitalSignature(s.hex))
}
/**
* Splits the given asm into two parts
* the first part is the digital signatures
* the second part is the redeem script */
def splitAtRedeemScript(asm : Seq[ScriptToken]) : Try[(Seq[ScriptToken],Seq[ScriptToken])] = {
* Splits the given asm into two parts
* the first part is the digital signatures
* the second part is the redeem script
*/
def splitAtRedeemScript(asm: Seq[ScriptToken]): Try[(Seq[ScriptToken], Seq[ScriptToken])] = {
scriptSignatureNoRedeemScript.map { scriptSig =>
(scriptSig.asm, redeemScript.asm)
}
@ -170,10 +166,10 @@ sealed trait P2SHScriptSignature extends ScriptSignature {
override def toString = "P2SHScriptSignature(" + hex + ")"
}
object P2SHScriptSignature extends ScriptFactory[P2SHScriptSignature] {
object P2SHScriptSignature extends ScriptFactory[P2SHScriptSignature] {
private case class P2SHScriptSignatureImpl(bytes: Seq[Byte]) extends P2SHScriptSignature
def apply(scriptSig : ScriptSignature, redeemScript : ScriptPubKey): P2SHScriptSignature = {
def apply(scriptSig: ScriptSignature, redeemScript: ScriptPubKey): P2SHScriptSignature = {
//we need to calculate the size of the redeemScript and add the corresponding push op
val serializedRedeemScript = ScriptConstant(redeemScript.asmBytes)
val pushOps = BitcoinScriptUtil.calculatePushOp(serializedRedeemScript)
@ -182,17 +178,16 @@ object P2SHScriptSignature extends ScriptFactory[P2SHScriptSignature] {
}
def apply(witnessScriptPubKey: WitnessScriptPubKey): P2SHScriptSignature = {
P2SHScriptSignature(EmptyScriptSignature,witnessScriptPubKey)
P2SHScriptSignature(EmptyScriptSignature, witnessScriptPubKey)
}
def fromAsm(asm: Seq[ScriptToken]): P2SHScriptSignature = {
//everything can be a P2SHScriptSignature, thus passing the trivially true function
//the most important thing to note is we cannot have a P2SHScriptSignature unless
//we have a P2SHScriptPubKey
//previously P2SHScriptSignature's redeem script had to be standard scriptPubKey's, this
//was removed in 0.11 or 0.12 of Bitcoin Core
buildScript(asm, P2SHScriptSignatureImpl(_),{ _ => true}, "Given asm tokens are not a p2sh scriptSig, got: " + asm)
buildScript(asm, P2SHScriptSignatureImpl(_), { _ => true }, "Given asm tokens are not a p2sh scriptSig, got: " + asm)
}
/** Tests if the given asm tokens are a [[P2SHScriptSignature]] */
@ -203,28 +198,27 @@ object P2SHScriptSignature extends ScriptFactory[P2SHScriptSignature] {
}
/** Detects if the given script token is a redeem script */
def isRedeemScript(token : ScriptToken) : Boolean = {
val redeemScriptTry : Try[ScriptPubKey] = parseRedeemScript(token)
def isRedeemScript(token: ScriptToken): Boolean = {
val redeemScriptTry: Try[ScriptPubKey] = parseRedeemScript(token)
redeemScriptTry match {
case Success(redeemScript) =>
redeemScript match {
case _: P2PKHScriptPubKey | _: MultiSignatureScriptPubKey
| _: P2SHScriptPubKey | _: P2PKScriptPubKey
| _: CLTVScriptPubKey | _: CSVScriptPubKey
| _: WitnessScriptPubKeyV0 | _: UnassignedWitnessScriptPubKey
| _: EscrowTimeoutScriptPubKey => true
| _: P2SHScriptPubKey | _: P2PKScriptPubKey
| _: CLTVScriptPubKey | _: CSVScriptPubKey
| _: WitnessScriptPubKeyV0 | _: UnassignedWitnessScriptPubKey
| _: EscrowTimeoutScriptPubKey => true
case _: NonStandardScriptPubKey | _: WitnessCommitment => false
case EmptyScriptPubKey => false
case EmptyScriptPubKey => false
}
case Failure(_) => false
}
}
/** Parses a redeem script from the given script token */
def parseRedeemScript(scriptToken : ScriptToken) : Try[ScriptPubKey] = {
def parseRedeemScript(scriptToken: ScriptToken): Try[ScriptPubKey] = {
val asm = ScriptParser.fromBytes(scriptToken.bytes)
val redeemScript : Try[ScriptPubKey] = Try(ScriptPubKey(asm))
val redeemScript: Try[ScriptPubKey] = Try(ScriptPubKey(asm))
redeemScript
}
}
@ -238,7 +232,7 @@ object P2SHScriptSignature extends ScriptFactory[P2SHScriptSignature] {
sealed trait MultiSignatureScriptSignature extends ScriptSignature {
/** The digital signatures inside of the scriptSig */
def signatures : Seq[ECDigitalSignature] = {
def signatures: Seq[ECDigitalSignature] = {
asm.tail.filter(_.isInstanceOf[ScriptConstant])
.map(sig => ECDigitalSignature(sig.hex))
}
@ -250,8 +244,8 @@ object MultiSignatureScriptSignature extends ScriptFactory[MultiSignatureScriptS
private case class MultiSignatureScriptSignatureImpl(bytes: Seq[Byte]) extends MultiSignatureScriptSignature
def apply(signatures : Seq[ECDigitalSignature]): MultiSignatureScriptSignature = {
val sigsPushOpsPairs : Seq[Seq[ScriptToken]] = for {
def apply(signatures: Seq[ECDigitalSignature]): MultiSignatureScriptSignature = {
val sigsPushOpsPairs: Seq[Seq[ScriptToken]] = for {
sig <- signatures
constant = ScriptConstant(sig.bytes)
pushOps = BitcoinScriptUtil.calculatePushOp(sig.bytes)
@ -263,18 +257,18 @@ object MultiSignatureScriptSignature extends ScriptFactory[MultiSignatureScriptS
}
def fromAsm(asm: Seq[ScriptToken]): MultiSignatureScriptSignature = {
buildScript(asm, MultiSignatureScriptSignatureImpl(_),isMultiSignatureScriptSignature(_),
buildScript(asm, MultiSignatureScriptSignatureImpl(_), isMultiSignatureScriptSignature(_),
"The given asm tokens were not a multisignature script sig: " + asm)
}
/**
* Checks if the given script tokens are a multisignature script sig
* format: OP_0 <A sig> [B sig] [C sig...]
*
* @param asm the asm to check if it falls in the multisignature script sig format
* @return boolean indicating if the scriptsignature is a multisignature script signature
*/
def isMultiSignatureScriptSignature(asm : Seq[ScriptToken]) : Boolean = asm.isEmpty match {
* Checks if the given script tokens are a multisignature script sig
* format: OP_0 <A sig> [B sig] [C sig...]
*
* @param asm the asm to check if it falls in the multisignature script sig format
* @return boolean indicating if the scriptsignature is a multisignature script signature
*/
def isMultiSignatureScriptSignature(asm: Seq[ScriptToken]): Boolean = asm.isEmpty match {
case true => false
//case false if (asm.size == 1) => false
case false =>
@ -294,10 +288,10 @@ object MultiSignatureScriptSignature extends ScriptFactory[MultiSignatureScriptS
sealed trait P2PKScriptSignature extends ScriptSignature {
/** PubKey scriptSignatures only have one signature */
def signature : ECDigitalSignature = signatures.head
def signature: ECDigitalSignature = signatures.head
/** The digital signatures inside of the scriptSig */
def signatures : Seq[ECDigitalSignature] = {
def signatures: Seq[ECDigitalSignature] = {
Seq(ECDigitalSignature(BitcoinScriptUtil.filterPushOps(asm).head.hex))
}
@ -315,22 +309,22 @@ object P2PKScriptSignature extends ScriptFactory[P2PKScriptSignature] {
}
def fromAsm(asm: Seq[ScriptToken]): P2PKScriptSignature = {
buildScript(asm, P2PKScriptSignatureImpl(_),isP2PKScriptSignature(_),
buildScript(asm, P2PKScriptSignatureImpl(_), isP2PKScriptSignature(_),
"The given asm tokens were not a p2pk script sig: " + asm)
}
/** P2PK scriptSigs always have the pattern [pushop, digitalSignature] */
def isP2PKScriptSignature(asm: Seq[ScriptToken]): Boolean = asm match {
case List(w : BytesToPushOntoStack, x : ScriptConstant) => true
case List(w: BytesToPushOntoStack, x: ScriptConstant) => true
case _ => false
}
}
/** Parent type for all lock time script signatures, these spend [[LockTimeScriptPubKey]] */
sealed trait LockTimeScriptSignature extends ScriptSignature {
def scriptSig : ScriptSignature = ScriptSignature(hex)
def scriptSig: ScriptSignature = ScriptSignature(hex)
override def signatures : Seq[ECDigitalSignature] = scriptSig.signatures
override def signatures: Seq[ECDigitalSignature] = scriptSig.signatures
}
sealed trait CLTVScriptSignature extends LockTimeScriptSignature {
@ -340,15 +334,15 @@ sealed trait CLTVScriptSignature extends LockTimeScriptSignature {
object CLTVScriptSignature extends Factory[CLTVScriptSignature] {
private case class CLTVScriptSignatureImpl(bytes: Seq[Byte]) extends CLTVScriptSignature
override def fromBytes(bytes : Seq[Byte]) : CLTVScriptSignature = {
override def fromBytes(bytes: Seq[Byte]): CLTVScriptSignature = {
CLTVScriptSignatureImpl(bytes)
}
override def fromHex(hex : String) : CLTVScriptSignature = {
override def fromHex(hex: String): CLTVScriptSignature = {
CLTVScriptSignature(BitcoinSUtil.decodeHex(hex))
}
def apply(scriptSig : ScriptSignature) : CLTVScriptSignature = {
def apply(scriptSig: ScriptSignature): CLTVScriptSignature = {
fromHex(scriptSig.hex)
}
}
@ -360,20 +354,19 @@ sealed trait CSVScriptSignature extends LockTimeScriptSignature {
object CSVScriptSignature extends Factory[CSVScriptSignature] {
private case class CSVScriptSignatureImpl(bytes: Seq[Byte]) extends CSVScriptSignature
override def fromBytes(bytes : Seq[Byte]) : CSVScriptSignature = {
override def fromBytes(bytes: Seq[Byte]): CSVScriptSignature = {
CSVScriptSignatureImpl(bytes)
}
override def fromHex(hex : String) : CSVScriptSignature = {
override def fromHex(hex: String): CSVScriptSignature = {
CSVScriptSignature(BitcoinSUtil.decodeHex(hex))
}
def apply(scriptSig : ScriptSignature) : CSVScriptSignature = {
def apply(scriptSig: ScriptSignature): CSVScriptSignature = {
fromHex(scriptSig.hex)
}
}
/** Represents the empty script signature */
case object EmptyScriptSignature extends ScriptSignature {
def signatures = Nil
@ -382,16 +375,15 @@ case object EmptyScriptSignature extends ScriptSignature {
object ScriptSignature extends Factory[ScriptSignature] {
/** Returns an empty script signature */
def empty : ScriptSignature = EmptyScriptSignature
def empty: ScriptSignature = EmptyScriptSignature
def fromBytes(bytes : Seq[Byte]) : ScriptSignature = RawScriptSignatureParser.read(bytes)
def fromBytes(bytes: Seq[Byte]): ScriptSignature = RawScriptSignatureParser.read(bytes)
/** Creates a scriptSignature from the list of script tokens */
def fromAsm(tokens : Seq[ScriptToken]) : ScriptSignature = tokens match {
def fromAsm(tokens: Seq[ScriptToken]): ScriptSignature = tokens match {
case Nil => EmptyScriptSignature
case _ if (tokens.size > 1 && P2SHScriptSignature.isRedeemScript(tokens.last)) =>
case _ if (tokens.size > 1 && P2SHScriptSignature.isRedeemScript(tokens.last)) =>
P2SHScriptSignature.fromAsm(tokens)
case _ if EscrowTimeoutScriptSignature.isEscrowTimeoutScriptSig(tokens) =>
EscrowTimeoutScriptSignature.fromAsm(tokens)
@ -402,46 +394,45 @@ object ScriptSignature extends Factory[ScriptSignature] {
case _ => NonStandardScriptSignature.fromAsm(tokens)
}
/**
* Creates a script signature from the given tokens and scriptPubKey
* @param tokens the script signature's tokens
* @param scriptPubKey the scriptPubKey which the script signature is trying to spend
* @return
*/
def fromScriptPubKey(tokens : Seq[ScriptToken], scriptPubKey : ScriptPubKey): Try[ScriptSignature] = scriptPubKey match {
case _: P2SHScriptPubKey => Try(P2SHScriptSignature.fromAsm(tokens))
case _: P2PKHScriptPubKey => Try(P2PKHScriptSignature.fromAsm(tokens))
case _: P2PKScriptPubKey => Try(P2PKScriptSignature.fromAsm(tokens))
* Creates a script signature from the given tokens and scriptPubKey
* @param tokens the script signature's tokens
* @param scriptPubKey the scriptPubKey which the script signature is trying to spend
* @return
*/
def fromScriptPubKey(tokens: Seq[ScriptToken], scriptPubKey: ScriptPubKey): Try[ScriptSignature] = scriptPubKey match {
case _: P2SHScriptPubKey => Try(P2SHScriptSignature.fromAsm(tokens))
case _: P2PKHScriptPubKey => Try(P2PKHScriptSignature.fromAsm(tokens))
case _: P2PKScriptPubKey => Try(P2PKScriptSignature.fromAsm(tokens))
case _: MultiSignatureScriptPubKey => Try(MultiSignatureScriptSignature.fromAsm(tokens))
case _: NonStandardScriptPubKey => Try(NonStandardScriptSignature.fromAsm(tokens))
case s: CLTVScriptPubKey => fromScriptPubKey(tokens, s.nestedScriptPubKey)
case s: CSVScriptPubKey => fromScriptPubKey(tokens, s.nestedScriptPubKey)
case escrowWithTimeout : EscrowTimeoutScriptPubKey =>
case _: NonStandardScriptPubKey => Try(NonStandardScriptSignature.fromAsm(tokens))
case s: CLTVScriptPubKey => fromScriptPubKey(tokens, s.nestedScriptPubKey)
case s: CSVScriptPubKey => fromScriptPubKey(tokens, s.nestedScriptPubKey)
case escrowWithTimeout: EscrowTimeoutScriptPubKey =>
val isMultiSig = BitcoinScriptUtil.castToBool(tokens.last)
if (isMultiSig) {
val multiSig = Try(MultiSignatureScriptSignature.fromAsm(tokens.take(tokens.size - 1)))
multiSig.map(m => EscrowTimeoutScriptSignature.fromMultiSig(m))
}
else Try(EscrowTimeoutScriptSignature.fromAsm(tokens,escrowWithTimeout))
case _: WitnessScriptPubKeyV0 | _: UnassignedWitnessScriptPubKey => Success(EmptyScriptSignature)
} else Try(EscrowTimeoutScriptSignature.fromAsm(tokens, escrowWithTimeout))
case _: WitnessScriptPubKeyV0 | _: UnassignedWitnessScriptPubKey => Success(EmptyScriptSignature)
case EmptyScriptPubKey =>
if (tokens.isEmpty) Success(EmptyScriptSignature) else Try(NonStandardScriptSignature.fromAsm(tokens))
case _ : WitnessCommitment => Failure(new IllegalArgumentException("Cannot spend witness commitment scriptPubKey"))
case _: WitnessCommitment => Failure(new IllegalArgumentException("Cannot spend witness commitment scriptPubKey"))
}
def apply(tokens : Seq[ScriptToken], scriptPubKey : ScriptPubKey): Try[ScriptSignature] = {
def apply(tokens: Seq[ScriptToken], scriptPubKey: ScriptPubKey): Try[ScriptSignature] = {
fromScriptPubKey(tokens, scriptPubKey)
}
}
/** [[ScriptSignature]] that spends a [[EscrowTimeoutScriptPubKey]], the underlying script signature can be
* a [[MultiSignatureScriptSignature]] or a [[CSVScriptSignature]] as those are te two underlying scripts
* of a [[EscrowTimeoutScriptPubKey]]
*
* If the last element of the [[asm]] evaluates to true, it is a scriptsig that attempts to spend the escrow
* if the last element of the [[asm]] evaluates to false, it is a scriptsig that attempts to spend the timeout
* */
/**
* [[ScriptSignature]] that spends a [[EscrowTimeoutScriptPubKey]], the underlying script signature can be
* a [[MultiSignatureScriptSignature]] or a [[CSVScriptSignature]] as those are te two underlying scripts
* of a [[EscrowTimeoutScriptPubKey]]
*
* If the last element of the [[asm]] evaluates to true, it is a scriptsig that attempts to spend the escrow
* if the last element of the [[asm]] evaluates to false, it is a scriptsig that attempts to spend the timeout
*/
sealed trait EscrowTimeoutScriptSignature extends ScriptSignature {
def scriptSig: ScriptSignature = ScriptSignature(bytes.take(bytes.length - 1))
override def signatures = scriptSig.signatures
@ -454,7 +445,6 @@ sealed trait EscrowTimeoutScriptSignature extends ScriptSignature {
override def toString = "EscrowTimeoutScriptSignature(" + hex + ")"
}
object EscrowTimeoutScriptSignature extends Factory[EscrowTimeoutScriptSignature] {
private case class EscrowTimeoutScriptSignatureImpl(bytes: Seq[Byte]) extends EscrowTimeoutScriptSignature
@ -500,7 +490,7 @@ object EscrowTimeoutScriptSignature extends Factory[EscrowTimeoutScriptSignature
// is the timeout branch since we can nest ANY scriptPubKey inside of a [[LockTimeScriptPubKey]]
val isValidTimeout = scriptPubKey.map { s =>
val locktimeScript = s.timeout.nestedScriptPubKey
Try(ScriptSignature.fromScriptPubKey(asm,locktimeScript)).isSuccess
Try(ScriptSignature.fromScriptPubKey(asm, locktimeScript)).isSuccess
}
isValidTimeout.getOrElse(true)
} else false
@ -508,21 +498,25 @@ object EscrowTimeoutScriptSignature extends Factory[EscrowTimeoutScriptSignature
def apply(scriptSig: ScriptSignature): Try[EscrowTimeoutScriptSignature] = scriptSig match {
case m: MultiSignatureScriptSignature => Success(fromMultiSig(m))
case lock: LockTimeScriptSignature => Success(fromLockTime(lock))
case x @ (_: P2PKScriptSignature | _: P2PKHScriptSignature | _: P2SHScriptSignature | _:NonStandardScriptSignature
case lock: LockTimeScriptSignature => Success(fromLockTime(lock))
case x @ (_: P2PKScriptSignature | _: P2PKHScriptSignature | _: P2SHScriptSignature | _: NonStandardScriptSignature
| _: EscrowTimeoutScriptSignature | EmptyScriptSignature) => Failure(new IllegalArgumentException("Cannot create a EscrowTimeoutScriptSignature out of " + x))
}
/** Creates a [[org.bitcoins.core.protocol.script.EscrowTimeoutScriptSignature]] that spends the escrow
* branch of a [[EscrowTimeoutScriptPubKey]] */
/**
* Creates a [[org.bitcoins.core.protocol.script.EscrowTimeoutScriptSignature]] that spends the escrow
* branch of a [[EscrowTimeoutScriptPubKey]]
*/
def fromMultiSig(multiSigScriptSig: MultiSignatureScriptSignature): EscrowTimeoutScriptSignature = {
val asm = multiSigScriptSig.asm ++ Seq(OP_1)
fromAsm(asm)
}
/** Creates a [[org.bitcoins.core.protocol.script.EscrowTimeoutScriptSignature]] that spends the locktime branch
* of the [[EscrowTimeoutScriptPubKey]] */
/**
* Creates a [[org.bitcoins.core.protocol.script.EscrowTimeoutScriptSignature]] that spends the locktime branch
* of the [[EscrowTimeoutScriptPubKey]]
*/
def fromLockTime(l: LockTimeScriptSignature): EscrowTimeoutScriptSignature = {
val asm = l.asm ++ Seq(OP_0)
fromAsm(asm)

View file

@ -1,19 +1,19 @@
package org.bitcoins.core.protocol.script
import org.bitcoins.core.crypto.{ECDigitalSignature, ECPublicKey, EmptyDigitalSignature}
import org.bitcoins.core.protocol.{CompactSizeUInt, NetworkElement}
import org.bitcoins.core.crypto.{ ECDigitalSignature, ECPublicKey, EmptyDigitalSignature }
import org.bitcoins.core.protocol.{ CompactSizeUInt, NetworkElement }
import org.bitcoins.core.serializers.script.RawScriptWitnessParser
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil, BitcoinScriptUtil}
import org.bitcoins.core.util.{ BitcoinSLogger, BitcoinSUtil, BitcoinScriptUtil }
/**
* Created by chris on 11/10/16.
* The witness used to evaluate a [[ScriptPubKey]] inside of Bitcoin
* [[https://github.com/bitcoin/bitcoin/blob/57b34599b2deb179ff1bd97ffeab91ec9f904d85/src/script/script.h#L648-L660]]
*/
* Created by chris on 11/10/16.
* The witness used to evaluate a [[ScriptPubKey]] inside of Bitcoin
* [[https://github.com/bitcoin/bitcoin/blob/57b34599b2deb179ff1bd97ffeab91ec9f904d85/src/script/script.h#L648-L660]]
*/
sealed abstract class ScriptWitness extends NetworkElement {
/** The byte vectors that are placed on to the stack when evaluating a witness program */
def stack : Seq[Seq[Byte]]
def stack: Seq[Seq[Byte]]
override def bytes = RawScriptWitnessParser.write(this)
}
@ -26,16 +26,17 @@ case object EmptyScriptWitness extends ScriptWitness {
sealed abstract class ScriptWitnessV0 extends ScriptWitness
/** Represents a [[org.bitcoins.core.protocol.script.ScriptWitness]] that is needed to spend a
* [[P2WPKHWitnessV0]] scriptPubKey
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#p2wpkh-nested-in-bip16-p2sh]]
* Format: <pubKey> <signature>
*/
/**
* Represents a [[org.bitcoins.core.protocol.script.ScriptWitness]] that is needed to spend a
* [[P2WPKHWitnessV0]] scriptPubKey
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#p2wpkh-nested-in-bip16-p2sh]]
* Format: <pubKey> <signature>
*/
sealed abstract class P2WPKHWitnessV0 extends ScriptWitness {
def pubKey: ECPublicKey = ECPublicKey(stack.head)
def signature: ECDigitalSignature = stack(1) match {
case Nil => EmptyDigitalSignature
case Nil => EmptyDigitalSignature
case bytes: Seq[Byte] => ECDigitalSignature(bytes)
}
@ -57,11 +58,11 @@ object P2WPKHWitnessV0 {
}
/**
* Reprsents a [[ScriptWitness]] that is needed to spend a
* [[P2WSHWitnessV0]] scriptPubKey
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#p2wsh]]
* Format: <redeem script> <scriptSig data1> <scriptSig data2> ... <scriptSig dataN>
*/
* Reprsents a [[ScriptWitness]] that is needed to spend a
* [[P2WSHWitnessV0]] scriptPubKey
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#p2wsh]]
* Format: <redeem script> <scriptSig data1> <scriptSig data2> ... <scriptSig dataN>
*/
sealed abstract class P2WSHWitnessV0 extends ScriptWitness {
def redeemScript: ScriptPubKey = {
val cmpct = CompactSizeUInt.calc(stack.head)
@ -103,12 +104,12 @@ object ScriptWitness {
//TODO: eventually only compressed public keys will be allowed in v0 scripts
//https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki#restrictions-on-public-key-type
val isPubKey = stack.headOption.isDefined && ECPublicKey.isFullyValid(stack.head) && (stack.head.size == 33 || stack.head.size == 65)
if (stack.isEmpty) {
if (stack.isEmpty) {
EmptyScriptWitness
} else if (isPubKey && stack.size == 2) {
val pubKey = ECPublicKey(stack.head)
val sig = ECDigitalSignature(stack(1))
P2WPKHWitnessV0(pubKey,sig)
P2WPKHWitnessV0(pubKey, sig)
} else if (isPubKey && stack.size == 1) {
val pubKey = ECPublicKey(stack.head)
P2WPKHWitnessV0(pubKey)
@ -118,10 +119,10 @@ object ScriptWitness {
s match {
case Nil =>
EmptyScriptWitness
case h :: t =>
case h :: t =>
val cmpct = CompactSizeUInt.calc(h)
val spk = ScriptPubKey(cmpct.bytes ++ h)
P2WSHWitnessV0(spk,t)
P2WSHWitnessV0(spk, t)
}
}
}

View file

@ -1,11 +1,11 @@
package org.bitcoins.core.protocol.script
/** Represents the transaction digest algorithm for signature verification in Bitcoin Core
* With the implementation of segwit, we have added different algorithm, the first alternative being BIP143
* [[https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki]]
* [[https://github.com/bitcoin/bitcoin/blob/53133c1c041d113c2a480a18e6ff38681d135dca/src/script/interpreter.h#L120-L124]]
* */
/**
* Represents the transaction digest algorithm for signature verification in Bitcoin Core
* With the implementation of segwit, we have added different algorithm, the first alternative being BIP143
* [[https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki]]
* [[https://github.com/bitcoin/bitcoin/blob/53133c1c041d113c2a480a18e6ff38681d135dca/src/script/interpreter.h#L120-L124]]
*/
sealed trait SignatureVersion
/** The original digest algorithm created by Satoshi */
@ -14,5 +14,3 @@ case object SigVersionBase extends SignatureVersion
/** The digest algorithm implemented by BIP143 [[https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki]] */
case object SigVersionWitnessV0 extends SignatureVersion

View file

@ -1,23 +1,25 @@
package org.bitcoins.core.protocol.script
import org.bitcoins.core.crypto.{ECPublicKey, Sha256Digest, Sha256Hash160Digest}
import org.bitcoins.core.crypto.{ ECPublicKey, Sha256Digest, Sha256Hash160Digest }
import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.result._
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil, CryptoUtil}
import org.bitcoins.core.util.{ BitcoinSLogger, BitcoinSUtil, CryptoUtil }
import scala.util.Try
/**
* Created by chris on 11/10/16.
* The version of the [[WitnessScriptPubKey]], this indicates how a [[ScriptWitness]] is rebuilt
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#witness-program]]
*/
* Created by chris on 11/10/16.
* The version of the [[WitnessScriptPubKey]], this indicates how a [[ScriptWitness]] is rebuilt
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#witness-program]]
*/
sealed trait WitnessVersion extends BitcoinSLogger {
/** Rebuilds the full script from the given witness and [[ScriptPubKey]]
* Either returns the stack and the [[ScriptPubKey]] it needs to be executed against or
* the [[ScriptError]] that was encountered while rebuilding the witness*/
def rebuild(scriptWitness: ScriptWitness, witnessProgram: Seq[ScriptToken]): Either[(Seq[ScriptToken], ScriptPubKey),ScriptError]
/**
* Rebuilds the full script from the given witness and [[ScriptPubKey]]
* Either returns the stack and the [[ScriptPubKey]] it needs to be executed against or
* the [[ScriptError]] that was encountered while rebuilding the witness
*/
def rebuild(scriptWitness: ScriptWitness, witnessProgram: Seq[ScriptToken]): Either[(Seq[ScriptToken], ScriptPubKey), ScriptError]
def version: ScriptNumberOperation
}
@ -25,7 +27,7 @@ sealed trait WitnessVersion extends BitcoinSLogger {
case object WitnessVersion0 extends WitnessVersion {
/** Rebuilds a witness version 0 program, see BIP141 */
override def rebuild(scriptWitness: ScriptWitness, witnessProgram: Seq[ScriptToken]): Either[(Seq[ScriptToken], ScriptPubKey),ScriptError] = {
override def rebuild(scriptWitness: ScriptWitness, witnessProgram: Seq[ScriptToken]): Either[(Seq[ScriptToken], ScriptPubKey), ScriptError] = {
val programBytes = witnessProgram.flatMap(_.bytes)
programBytes.size match {
case 20 =>
@ -46,8 +48,7 @@ case object WitnessVersion0 extends WitnessVersion {
logger.debug("Witness hashes did not match Stack hash: " + stackHash)
logger.debug("Witness program: " + witnessProgram)
Right(ScriptErrorWitnessProgramMisMatch)
}
else {
} else {
val compactSizeUInt = CompactSizeUInt.calculateCompactSizeUInt(stackTop)
val scriptPubKey = ScriptPubKey(compactSizeUInt.bytes ++ stackTop)
val stack = scriptWitness.stack.tail.map(ScriptConstant(_))
@ -69,7 +70,7 @@ case object WitnessVersion0 extends WitnessVersion {
/** The witness version that represents all witnesses that have not been allocated yet */
case class UnassignedWitness(version: ScriptNumberOperation) extends WitnessVersion {
require(WitnessScriptPubKey.unassignedWitVersions.contains(version), "Cannot created an unassigend witness version from one that is assigned already, got: " + version)
override def rebuild(scriptWitness: ScriptWitness, witnessProgram: Seq[ScriptToken]): Either[(Seq[ScriptToken], ScriptPubKey),ScriptError] =
override def rebuild(scriptWitness: ScriptWitness, witnessProgram: Seq[ScriptToken]): Either[(Seq[ScriptToken], ScriptPubKey), ScriptError] =
Right(ScriptErrorDiscourageUpgradeableWitnessProgram)
}
@ -80,13 +81,13 @@ object WitnessVersion {
def apply(scriptNumberOp: ScriptNumberOperation): WitnessVersion = scriptNumberOp match {
case OP_0 | OP_FALSE => WitnessVersion0
case x @ (OP_1 | OP_TRUE | OP_2 | OP_3 | OP_4 | OP_5 | OP_6 | OP_7 | OP_8
| OP_9 | OP_10 | OP_11 | OP_12 | OP_13 | OP_14 | OP_15 | OP_16) => UnassignedWitness(x)
| OP_9 | OP_10 | OP_11 | OP_12 | OP_13 | OP_14 | OP_15 | OP_16) => UnassignedWitness(x)
case OP_1NEGATE => throw new IllegalArgumentException("OP_1NEGATE is not a valid witness version")
}
def apply(token: ScriptToken): WitnessVersion = token match {
case scriptNumberOp : ScriptNumberOperation => WitnessVersion(scriptNumberOp)
case _ : ScriptConstant | _ : ScriptNumber | _ : ScriptOperation =>
case scriptNumberOp: ScriptNumberOperation => WitnessVersion(scriptNumberOp)
case _: ScriptConstant | _: ScriptNumber | _: ScriptOperation =>
throw new IllegalArgumentException("We can only have witness version that is a script number operation, i.e OP_0 through OP_16")
}

View file

@ -3,20 +3,20 @@ package org.bitcoins.core.protocol.transaction
import org.bitcoins.core.crypto.DoubleSha256Digest
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.NetworkElement
import org.bitcoins.core.serializers.transaction.{RawBaseTransactionParser, RawWitnessTransactionParser}
import org.bitcoins.core.util.{BitcoinSUtil, CryptoUtil, Factory}
import org.bitcoins.core.serializers.transaction.{ RawBaseTransactionParser, RawWitnessTransactionParser }
import org.bitcoins.core.util.{ BitcoinSUtil, CryptoUtil, Factory }
import scala.util.{Failure, Success, Try}
import scala.util.{ Failure, Success, Try }
/**
* Created by chris on 7/14/15.
*/
sealed abstract class Transaction extends NetworkElement {
/**
* The sha256(sha256(tx)) of this transaction
* Note that this is the big endian encoding of the hash NOT the little endian encoding displayed on block explorers
*/
def txId : DoubleSha256Digest = CryptoUtil.doubleSHA256(bytes)
* The sha256(sha256(tx)) of this transaction
* Note that this is the big endian encoding of the hash NOT the little endian encoding displayed on block explorers
*/
def txId: DoubleSha256Digest = CryptoUtil.doubleSHA256(bytes)
/** The version number for this transaction */
def version: UInt32
@ -30,40 +30,41 @@ sealed abstract class Transaction extends NetworkElement {
/** The locktime for this transaction */
def lockTime: UInt32
/** This is used to indicate how 'expensive' the transction is on the blockchain.
* This use to be a simple calculation before segwit (BIP141). Each byte in the transaction
* counted as 4 'weight' units. Now with segwit, the [[TransactionWitness]] is counted as 1 weight unit per byte,
* while other parts of the transaction (outputs, inputs, locktime etc) count as 4 weight units.
* As we add more witness versions, this may be subject to change
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#Transaction_size_calculations]]
* [[https://github.com/bitcoin/bitcoin/blob/5961b23898ee7c0af2626c46d5d70e80136578d3/src/consensus/validation.h#L96]]
* */
/**
* This is used to indicate how 'expensive' the transction is on the blockchain.
* This use to be a simple calculation before segwit (BIP141). Each byte in the transaction
* counted as 4 'weight' units. Now with segwit, the [[TransactionWitness]] is counted as 1 weight unit per byte,
* while other parts of the transaction (outputs, inputs, locktime etc) count as 4 weight units.
* As we add more witness versions, this may be subject to change
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#Transaction_size_calculations]]
* [[https://github.com/bitcoin/bitcoin/blob/5961b23898ee7c0af2626c46d5d70e80136578d3/src/consensus/validation.h#L96]]
*/
def weight: Long
/**
* The transaction's virtual size
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#Transaction_size_calculations]]
*/
* The transaction's virtual size
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#Transaction_size_calculations]]
*/
def vsize: Long = Math.ceil(weight / 4.0).toLong
/**
* Base transaction size is the size of the transaction serialised with the witness data stripped
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#Transaction_size_calculations]]
*/
* Base transaction size is the size of the transaction serialised with the witness data stripped
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#Transaction_size_calculations]]
*/
def baseSize: Long = this match {
case btx: BaseTransaction => btx.size
case wtx: WitnessTransaction => BaseTransaction(wtx.version,wtx.inputs,wtx.outputs,wtx.lockTime).baseSize
case btx: BaseTransaction => btx.size
case wtx: WitnessTransaction => BaseTransaction(wtx.version, wtx.inputs, wtx.outputs, wtx.lockTime).baseSize
}
def totalSize: Long = bytes.size
/** Determines if this transaction is a coinbase transaction. */
def isCoinbase : Boolean = inputs.size match {
def isCoinbase: Boolean = inputs.size match {
case 1 => inputs.head match {
case coinbase : CoinbaseInput => true
case _ : TransactionInput => false
case coinbase: CoinbaseInput => true
case _: TransactionInput => false
}
case _ : Int => false
case _: Int => false
}
}
@ -72,7 +73,6 @@ sealed abstract class BaseTransaction extends Transaction {
override def weight = size * 4
}
case object EmptyTransaction extends BaseTransaction {
override def txId = CryptoUtil.emptyDoubleSha256Hash
override def version = TransactionConstants.version
@ -84,26 +84,29 @@ case object EmptyTransaction extends BaseTransaction {
sealed abstract class WitnessTransaction extends Transaction {
/** The txId for the witness transaction from satoshi's original serialization */
override def txId: DoubleSha256Digest = {
val btx = BaseTransaction(version,inputs,outputs,lockTime)
val btx = BaseTransaction(version, inputs, outputs, lockTime)
btx.txId
}
/** The witness used to evaluate [[org.bitcoins.core.protocol.script.ScriptSignature]]/[[org.bitcoins.core.protocol.script.ScriptPubKey]]s inside of a segwit tx
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki]]
*/
/**
* The witness used to evaluate [[org.bitcoins.core.protocol.script.ScriptSignature]]/[[org.bitcoins.core.protocol.script.ScriptPubKey]]s inside of a segwit tx
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki]]
*/
def witness: TransactionWitness
/** The witness transaction id as defined by BIP141
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#transaction-id]]
* */
/**
* The witness transaction id as defined by BIP141
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#transaction-id]]
*/
def wTxId: DoubleSha256Digest = CryptoUtil.doubleSHA256(bytes)
/** Weight calculation in bitcoin for witness txs
* [[https://github.com/bitcoin/bitcoin/blob/5961b23898ee7c0af2626c46d5d70e80136578d3/src/consensus/validation.h#L96]]
* @return
*/
/**
* Weight calculation in bitcoin for witness txs
* [[https://github.com/bitcoin/bitcoin/blob/5961b23898ee7c0af2626c46d5d70e80136578d3/src/consensus/validation.h#L96]]
* @return
*/
override def weight: Long = {
val base = BaseTransaction(version,inputs,outputs,lockTime)
val base = BaseTransaction(version, inputs, outputs, lockTime)
base.size * 3 + size
}
override def bytes = RawWitnessTransactionParser.write(this)
@ -112,7 +115,7 @@ sealed abstract class WitnessTransaction extends Transaction {
object Transaction extends Factory[Transaction] {
def fromBytes(bytes : Seq[Byte]) : Transaction = {
def fromBytes(bytes: Seq[Byte]): Transaction = {
val wtxTry = Try(RawWitnessTransactionParser.read(bytes))
wtxTry match {
case Success(wtx) =>
@ -125,25 +128,23 @@ object Transaction extends Factory[Transaction] {
}
object BaseTransaction extends Factory[BaseTransaction] {
private case class BaseTransactionImpl(version : UInt32, inputs : Seq[TransactionInput],
outputs : Seq[TransactionOutput], lockTime : UInt32) extends BaseTransaction
private case class BaseTransactionImpl(version: UInt32, inputs: Seq[TransactionInput],
outputs: Seq[TransactionOutput], lockTime: UInt32) extends BaseTransaction
override def fromBytes(bytes: Seq[Byte]): BaseTransaction = RawBaseTransactionParser.read(bytes)
override def fromBytes(bytes: Seq[Byte]): BaseTransaction = RawBaseTransactionParser.read(bytes)
def apply(version : UInt32, inputs : Seq[TransactionInput],
outputs : Seq[TransactionOutput], lockTime : UInt32) : BaseTransaction = BaseTransactionImpl(version,inputs,outputs,lockTime)
def apply(version: UInt32, inputs: Seq[TransactionInput],
outputs: Seq[TransactionOutput], lockTime: UInt32): BaseTransaction = BaseTransactionImpl(version, inputs, outputs, lockTime)
}
object WitnessTransaction extends Factory[WitnessTransaction] {
private case class WitnessTransactionImpl(version: UInt32,inputs: Seq[TransactionInput],
private case class WitnessTransactionImpl(version: UInt32, inputs: Seq[TransactionInput],
outputs: Seq[TransactionOutput], lockTime: UInt32,
witness: TransactionWitness) extends WitnessTransaction
def apply(version: UInt32, inputs: Seq[TransactionInput], outputs: Seq[TransactionOutput],
lockTime: UInt32, witness: TransactionWitness): WitnessTransaction =
WitnessTransactionImpl(version,inputs,outputs,lockTime,witness)
WitnessTransactionImpl(version, inputs, outputs, lockTime, witness)
override def fromBytes(bytes: Seq[Byte]): WitnessTransaction = RawWitnessTransactionParser.read(bytes)

View file

@ -3,7 +3,7 @@ package org.bitcoins.core.protocol.transaction
import org.bitcoins.core.number.UInt32
/**
* Created by chris on 2/12/16.
* Created by chris on 2/12/16.
*/
trait TransactionConstants {
@ -13,32 +13,32 @@ trait TransactionConstants {
lazy val sequence = UInt32(4294967295L)
/**
* If bit (1 << 31) of the sequence number is set,
* then no consensus meaning is applied to the sequence number and can be included
* in any block under all currently possible circumstances.
* @return the mask that ben used with a bitwise and to indicate if the sequence number has any meaning
*/
* If bit (1 << 31) of the sequence number is set,
* then no consensus meaning is applied to the sequence number and can be included
* in any block under all currently possible circumstances.
* @return the mask that ben used with a bitwise and to indicate if the sequence number has any meaning
*/
def locktimeDisabledFlag: UInt32 = UInt32(1L << 31)
/**
* If a transaction's input's sequence number encodes a relative lock-time, this mask is
* applied to extract that lock-time from the sequence field.
*/
* If a transaction's input's sequence number encodes a relative lock-time, this mask is
* applied to extract that lock-time from the sequence field.
*/
def sequenceLockTimeMask: UInt32 = UInt32(0x0000ffff)
def fullSequenceLockTimeMask: UInt32 = sequenceLockTimeTypeFlag | sequenceLockTimeMask
/**
* If the transaction input sequence number encodes a relative lock-time and this flag
* is set, the relative lock-time has units of 512 seconds,
* otherwise it specifies blocks with a granularity of 1.
*/
* If the transaction input sequence number encodes a relative lock-time and this flag
* is set, the relative lock-time has units of 512 seconds,
* otherwise it specifies blocks with a granularity of 1.
*/
def sequenceLockTimeTypeFlag: UInt32 = UInt32(1L << 22)
/**
* Threshold for nLockTime: below this value it is interpreted as block number,
* otherwise as UNIX timestamp. */
* Threshold for nLockTime: below this value it is interpreted as block number,
* otherwise as UNIX timestamp.
*/
def locktimeThreshold: UInt32 = UInt32(500000000)
}

View file

@ -5,5 +5,5 @@ package org.bitcoins.core.protocol.transaction
*/
sealed trait TransactionFactoryHelper
case class UpdateTransactionOutputs(outputs : Seq[TransactionOutput]) extends TransactionFactoryHelper
case class UpdateTransactionInputs(inputs : Seq[TransactionInput]) extends TransactionFactoryHelper
case class UpdateTransactionOutputs(outputs: Seq[TransactionOutput]) extends TransactionFactoryHelper
case class UpdateTransactionInputs(inputs: Seq[TransactionInput]) extends TransactionFactoryHelper

View file

@ -2,20 +2,20 @@ package org.bitcoins.core.protocol.transaction
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.NetworkElement
import org.bitcoins.core.protocol.script.{EmptyScriptSignature, ScriptSignature}
import org.bitcoins.core.protocol.script.{ EmptyScriptSignature, ScriptSignature }
import org.bitcoins.core.serializers.transaction.RawTransactionInputParser
import org.bitcoins.core.util.Factory
/**
* Created by chris on 12/26/15.
* Algebraic data type that represents a transaction input
* Algebraic data type that represents a transaction input
*/
sealed abstract class TransactionInput extends NetworkElement {
def previousOutput : TransactionOutPoint
def previousOutput: TransactionOutPoint
def scriptSignature : ScriptSignature
def sequence : UInt32
def scriptSignature: ScriptSignature
def sequence: UInt32
override def bytes = RawTransactionInputParser.write(this)
}
@ -26,42 +26,40 @@ case object EmptyTransactionInput extends TransactionInput {
}
/**
* This represents a coinbase input - these always have a EmptyTransactionOutPoint
* and arbitrary data inside the script signature
*/
* This represents a coinbase input - these always have a EmptyTransactionOutPoint
* and arbitrary data inside the script signature
*/
sealed abstract class CoinbaseInput extends TransactionInput {
override def previousOutput = EmptyTransactionOutPoint
override def sequence = TransactionConstants.sequence
}
object TransactionInput extends Factory[TransactionInput] {
private case class TransactionInputImpl(previousOutput : TransactionOutPoint,
scriptSignature : ScriptSignature, sequence : UInt32) extends TransactionInput
private case class TransactionInputImpl(
previousOutput: TransactionOutPoint,
scriptSignature: ScriptSignature, sequence: UInt32
) extends TransactionInput
def empty: TransactionInput = EmptyTransactionInput
def fromBytes(bytes : Seq[Byte]) : TransactionInput = RawTransactionInputParser.read(bytes)
def fromBytes(bytes: Seq[Byte]): TransactionInput = RawTransactionInputParser.read(bytes)
def apply(outPoint : TransactionOutPoint, scriptSignature : ScriptSignature,
sequenceNumber : UInt32) : TransactionInput = outPoint match {
def apply(outPoint: TransactionOutPoint, scriptSignature: ScriptSignature,
sequenceNumber: UInt32): TransactionInput = outPoint match {
case EmptyTransactionOutPoint => CoinbaseInput(scriptSignature)
case _: TransactionOutPoint => TransactionInputImpl(outPoint,scriptSignature,sequenceNumber)
case _: TransactionOutPoint => TransactionInputImpl(outPoint, scriptSignature, sequenceNumber)
}
}
object CoinbaseInput {
private case class CoinbaseInputImpl(scriptSignature : ScriptSignature) extends CoinbaseInput
private case class CoinbaseInputImpl(scriptSignature: ScriptSignature) extends CoinbaseInput
/**
* Creates a coinbase input - coinbase inputs always have an empty outpoint
* @param scriptSignature this can contain anything, miners use this to signify support for various protocol BIPs
* @return the coinbase input
*/
def apply(scriptSignature: ScriptSignature) : CoinbaseInput = {
* Creates a coinbase input - coinbase inputs always have an empty outpoint
* @param scriptSignature this can contain anything, miners use this to signify support for various protocol BIPs
* @return the coinbase input
*/
def apply(scriptSignature: ScriptSignature): CoinbaseInput = {
CoinbaseInputImpl(scriptSignature)
}
}

View file

@ -4,43 +4,44 @@ import org.bitcoins.core.crypto.DoubleSha256Digest
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.NetworkElement
import org.bitcoins.core.serializers.transaction.RawTransactionOutPointParser
import org.bitcoins.core.util.{BitcoinSUtil, Factory}
import org.bitcoins.core.util.{ BitcoinSUtil, Factory }
/**
* Created by chris on 12/26/15.
*
*/
sealed abstract class TransactionOutPoint extends NetworkElement {
/** The transaction id for the crediting transaction for this input */
def txId : DoubleSha256Digest
def txId: DoubleSha256Digest
/** The output index in the parent transaction for the output we are spending */
def vout : UInt32
def vout: UInt32
override def bytes = RawTransactionOutPointParser.write(this)
}
/**
* UInt32s cannot hold negative numbers, but sometimes the Bitcoin Protocol requires the vout to be -1, which is serialized
* as "0xFFFFFFFF".
* https://github.com/bitcoin/bitcoin/blob/d612837814020ae832499d18e6ee5eb919a87907/src/primitives/transaction.h
* http://stackoverflow.com/questions/2711522/what-happens-if-i-assign-a-negative-value-to-an-unsigned-variable
*/
* UInt32s cannot hold negative numbers, but sometimes the Bitcoin Protocol requires the vout to be -1, which is serialized
* as "0xFFFFFFFF".
* https://github.com/bitcoin/bitcoin/blob/d612837814020ae832499d18e6ee5eb919a87907/src/primitives/transaction.h
* http://stackoverflow.com/questions/2711522/what-happens-if-i-assign-a-negative-value-to-an-unsigned-variable
*/
case object EmptyTransactionOutPoint extends TransactionOutPoint {
def txId = DoubleSha256Digest(
BitcoinSUtil.decodeHex("0000000000000000000000000000000000000000000000000000000000000000"))
def vout = UInt32("ffffffff")
BitcoinSUtil.decodeHex("0000000000000000000000000000000000000000000000000000000000000000")
)
def vout = UInt32("ffffffff")
}
object TransactionOutPoint extends Factory[TransactionOutPoint] {
private case class TransactionOutPointImpl(txId : DoubleSha256Digest, vout : UInt32) extends TransactionOutPoint
private case class TransactionOutPointImpl(txId: DoubleSha256Digest, vout: UInt32) extends TransactionOutPoint
def fromBytes(bytes : Seq[Byte]) : TransactionOutPoint = RawTransactionOutPointParser.read(bytes)
def fromBytes(bytes: Seq[Byte]): TransactionOutPoint = RawTransactionOutPointParser.read(bytes)
def apply(txId : DoubleSha256Digest, index: UInt32) : TransactionOutPoint = {
def apply(txId: DoubleSha256Digest, index: UInt32): TransactionOutPoint = {
if (txId == EmptyTransactionOutPoint.txId && index == EmptyTransactionOutPoint.vout) {
EmptyTransactionOutPoint
} else TransactionOutPointImpl(txId,index)
} else TransactionOutPointImpl(txId, index)
}
}

View file

@ -1,6 +1,6 @@
package org.bitcoins.core.protocol.transaction
import org.bitcoins.core.currency.{CurrencyUnit, CurrencyUnits}
import org.bitcoins.core.currency.{ CurrencyUnit, CurrencyUnits }
import org.bitcoins.core.protocol.NetworkElement
import org.bitcoins.core.protocol.script.ScriptPubKey
import org.bitcoins.core.serializers.transaction.RawTransactionOutputParser
@ -11,8 +11,8 @@ import org.bitcoins.core.util.Factory
*/
sealed abstract class TransactionOutput extends NetworkElement {
def value : CurrencyUnit
def scriptPubKey : ScriptPubKey
def value: CurrencyUnit
def scriptPubKey: ScriptPubKey
//https://bitcoin.org/en/developer-reference#txout
override def size = scriptPubKey.size + 8
@ -26,11 +26,11 @@ case object EmptyTransactionOutput extends TransactionOutput {
}
object TransactionOutput extends Factory[TransactionOutput] {
private case class TransactionOutputImpl(value : CurrencyUnit, scriptPubKey: ScriptPubKey) extends TransactionOutput
private case class TransactionOutputImpl(value: CurrencyUnit, scriptPubKey: ScriptPubKey) extends TransactionOutput
def fromBytes(bytes : Seq[Byte]) : TransactionOutput = RawTransactionOutputParser.read(bytes)
def fromBytes(bytes: Seq[Byte]): TransactionOutput = RawTransactionOutputParser.read(bytes)
def apply(currencyUnit: CurrencyUnit, scriptPubKey: ScriptPubKey) : TransactionOutput = {
TransactionOutputImpl(currencyUnit,scriptPubKey)
def apply(currencyUnit: CurrencyUnit, scriptPubKey: ScriptPubKey): TransactionOutput = {
TransactionOutputImpl(currencyUnit, scriptPubKey)
}
}

View file

@ -1,15 +1,15 @@
package org.bitcoins.core.protocol.transaction
import org.bitcoins.core.protocol.NetworkElement
import org.bitcoins.core.protocol.script.{EmptyScriptWitness, ScriptWitness}
import org.bitcoins.core.protocol.script.{ EmptyScriptWitness, ScriptWitness }
import org.bitcoins.core.serializers.transaction.RawTransactionWitnessParser
import org.bitcoins.core.util.BitcoinSUtil
/**
* Created by chris on 11/21/16.
* The witness data for [[org.bitcoins.core.protocol.script.ScriptSignature]] in this transaction
* [[https://github.com/bitcoin/bitcoin/blob/b4e4ba475a5679e09f279aaf2a83dcf93c632bdb/src/primitives/transaction.h#L232-L268]]
*/
* Created by chris on 11/21/16.
* The witness data for [[org.bitcoins.core.protocol.script.ScriptSignature]] in this transaction
* [[https://github.com/bitcoin/bitcoin/blob/b4e4ba475a5679e09f279aaf2a83dcf93c632bdb/src/primitives/transaction.h#L232-L268]]
*/
sealed abstract class TransactionWitness extends NetworkElement {
def witnesses: Seq[ScriptWitness]
@ -32,22 +32,23 @@ object TransactionWitness {
EmptyWitness
}
}
/** Creates a [[TransactionWitness]] from a Seq[Option[ScriptWitness]].
* This constructor is for convinience if a certain input does not spend a [[org.bitcoins.core.protocol.script.WitnessScriptPubKey]]
* It simply transforms the `None` types to [[EmptyScriptWitness]] and then calls the normal TransactionWitness constructor
* @param witnesses
* @return
*/
/**
* Creates a [[TransactionWitness]] from a Seq[Option[ScriptWitness]].
* This constructor is for convinience if a certain input does not spend a [[org.bitcoins.core.protocol.script.WitnessScriptPubKey]]
* It simply transforms the `None` types to [[EmptyScriptWitness]] and then calls the normal TransactionWitness constructor
* @param witnesses
* @return
*/
def fromWitOpt(witnesses: Seq[Option[ScriptWitness]]): TransactionWitness = {
val replaced: Seq[ScriptWitness] = witnesses.map {
case Some(wit) => wit
case None => EmptyScriptWitness
case None => EmptyScriptWitness
}
TransactionWitness(replaced)
}
def fromBytes(bytes: Seq[Byte], numInputs: Int): TransactionWitness = RawTransactionWitnessParser.read(bytes,numInputs)
def fromBytes(bytes: Seq[Byte], numInputs: Int): TransactionWitness = RawTransactionWitnessParser.read(bytes, numInputs)
def apply(bytes: Seq[Byte], numInputs: Int): TransactionWitness = fromBytes(bytes,numInputs)
def apply(bytes: Seq[Byte], numInputs: Int): TransactionWitness = fromBytes(bytes, numInputs)
def apply(hex: String, numInputs: Int): TransactionWitness = fromBytes(BitcoinSUtil.decodeHex(hex),numInputs)
def apply(hex: String, numInputs: Int): TransactionWitness = fromBytes(BitcoinSUtil.decodeHex(hex), numInputs)
}

View file

@ -9,7 +9,7 @@ import org.bitcoins.core.script.locktime.LocktimeOperation
import org.bitcoins.core.script.reserved.ReservedOperation
import org.bitcoins.core.script.splice.SpliceOperation
import org.bitcoins.core.script.stack.StackOperation
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil}
import org.bitcoins.core.util.{ BitcoinSLogger, BitcoinSUtil }
/**
* Created by chris on 1/8/16.
@ -19,12 +19,13 @@ import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil}
trait ScriptOperationFactory[T <: ScriptOperation] extends BitcoinSLogger {
/** All of the [[ScriptOperation]]s for a particular T. */
def operations : Seq[T]
def operations: Seq[T]
/**
* Finds a [[ScriptOperation]] from a given string */
def fromString(str : String) : Option[T] = {
val result : Option[T] = operations.find(_.toString == str)
* Finds a [[ScriptOperation]] from a given string
*/
def fromString(str: String): Option[T] = {
val result: Option[T] = operations.find(_.toString == str)
if (result.isEmpty) {
//try and remove the 'OP_' prefix on the operations and see if it matches anything.
operations.find(op => removeOP_Prefix(op.toString) == removeOP_Prefix(str))
@ -32,34 +33,34 @@ trait ScriptOperationFactory[T <: ScriptOperation] extends BitcoinSLogger {
}
/**
* Finds a [[ScriptOperation]] from its hexadecimal representation. */
def fromHex(hex : String) : Option[T] = operations.find(_.hex == hex.toLowerCase)
* Finds a [[ScriptOperation]] from its hexadecimal representation.
*/
def fromHex(hex: String): Option[T] = operations.find(_.hex == hex.toLowerCase)
/**
* Removes the 'OP_' prefix from a given operation.
* Example: OP_EQUALVERIFY would be transformed into EQUALVERIFY
*/
private def removeOP_Prefix(str : String) : String = {
str.replace("OP_","")
private def removeOP_Prefix(str: String): String = {
str.replace("OP_", "")
}
/** Finds a [[ScriptOperation]] from a given [[Byte]]. */
def fromByte(byte : Byte) : Option[T] = {
def fromByte(byte: Byte): Option[T] = {
val hex = BitcoinSUtil.encodeHex(byte)
fromHex(hex)
}
def apply(byte : Byte) : Option[T] = fromByte(byte)
def apply(byte: Byte): Option[T] = fromByte(byte)
def apply(hex : String) : Option[T] = fromHex(hex)
def apply(hex: String): Option[T] = fromHex(hex)
}
object ScriptOperation extends ScriptOperationFactory[ScriptOperation] {
lazy val operations = ScriptNumberOperation.operations ++ Seq(OP_FALSE,OP_PUSHDATA1, OP_PUSHDATA2,OP_PUSHDATA4,OP_TRUE) ++ StackOperation.operations ++ LocktimeOperation.operations ++
lazy val operations = ScriptNumberOperation.operations ++ Seq(OP_FALSE, OP_PUSHDATA1, OP_PUSHDATA2, OP_PUSHDATA4, OP_TRUE) ++ StackOperation.operations ++ LocktimeOperation.operations ++
CryptoOperation.operations ++ ControlOperations.operations ++ BitwiseOperation.operations ++
ArithmeticOperation.operations ++ BytesToPushOntoStack.operations ++ SpliceOperation.operations ++
ArithmeticOperation.operations ++ BytesToPushOntoStack.operations ++ SpliceOperation.operations ++
ReservedOperation.operations
}

View file

@ -1,36 +1,35 @@
package org.bitcoins.core.script
import org.bitcoins.core.crypto._
import org.bitcoins.core.currency.CurrencyUnit
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction.{BaseTransaction, Transaction, WitnessTransaction}
import org.bitcoins.core.protocol.transaction.{ BaseTransaction, Transaction, WitnessTransaction }
import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.flag.ScriptFlag
import org.bitcoins.core.script.result._
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinScriptUtil}
import org.bitcoins.core.util.{ BitcoinSLogger, BitcoinScriptUtil }
/**
* Created by chris on 2/3/16.
* Created by chris on 2/3/16.
*/
sealed trait ScriptProgram {
/**
* This contains all relevant information for hashing and checking a [[org.bitcoins.core.protocol.script.ScriptSignature]] for a [[Transaction]].
*/
def txSignatureComponent : TxSigComponent
def txSignatureComponent: TxSigComponent
/** The current state of the stack for execution of the [[ScriptProgram]]. */
def stack : List[ScriptToken]
def stack: List[ScriptToken]
/** The script operations that need to still be executed. */
def script : List[ScriptToken]
def script: List[ScriptToken]
/** The original script that was given. */
def originalScript : List[ScriptToken]
def originalScript: List[ScriptToken]
/** The alternative stack is used in some Script op codes. */
def altStack : List[ScriptToken]
def altStack: List[ScriptToken]
/**
* [[ScriptFlag]] that are run with the script.
@ -38,16 +37,15 @@ sealed trait ScriptProgram {
* [[https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.h#L31]]
* @return
*/
def flags : Seq[ScriptFlag]
def flags: Seq[ScriptFlag]
/** Returns true if the stack top is true */
def stackTopIsTrue = stack.headOption.isDefined && BitcoinScriptUtil.castToBool(stack.head)
/** Returns true if the stack top is false */
def stackTopIsFalse : Boolean = !stackTopIsTrue
def stackTopIsFalse: Boolean = !stackTopIsTrue
}
/**
* This represents a [[ScriptProgram]] before any script operations have been executed in the [[org.bitcoins.core.script.interpreter.ScriptInterpreter]].
*/
@ -55,13 +53,13 @@ sealed trait PreExecutionScriptProgram extends ScriptProgram
sealed trait ExecutionInProgressScriptProgram extends ScriptProgram {
/** The index of the last [[org.bitcoins.core.script.crypto.OP_CODESEPARATOR]] */
def lastCodeSeparator : Option[Int]
def lastCodeSeparator: Option[Int]
}
sealed trait ExecutedScriptProgram extends ScriptProgram {
/** Indicates if the [[ScriptProgram]] has encountered a [[ScriptError]] in its execution.*/
def error : Option[ScriptError]
def error: Option[ScriptError]
}
/**
@ -69,29 +67,34 @@ sealed trait ExecutedScriptProgram extends ScriptProgram {
*/
object ScriptProgram extends BitcoinSLogger {
/**
* Implementation type for a [[PreExecutionScriptProgram]] - a [[ScriptProgram]] that has not yet begun being
* evaluated by the [[org.bitcoins.core.script.interpreter.ScriptInterpreter]].
*/
private case class PreExecutionScriptProgramImpl(txSignatureComponent : TxSigComponent,
stack : List[ScriptToken],script : List[ScriptToken], originalScript : List[ScriptToken], altStack : List[ScriptToken],
flags : Seq[ScriptFlag]) extends PreExecutionScriptProgram
* Implementation type for a [[PreExecutionScriptProgram]] - a [[ScriptProgram]] that has not yet begun being
* evaluated by the [[org.bitcoins.core.script.interpreter.ScriptInterpreter]].
*/
private case class PreExecutionScriptProgramImpl(
txSignatureComponent: TxSigComponent,
stack: List[ScriptToken], script: List[ScriptToken], originalScript: List[ScriptToken], altStack: List[ScriptToken],
flags: Seq[ScriptFlag]
) extends PreExecutionScriptProgram
/**
* Implementation type for a [[ExecutionInProgressScriptProgram]] - a [[ScriptProgram]] that is currently being
* evaluated by the [[org.bitcoins.core.script.interpreter.ScriptInterpreter]].
*/
private case class ExecutionInProgressScriptProgramImpl(txSignatureComponent : TxSigComponent,
stack : List[ScriptToken],script : List[ScriptToken], originalScript : List[ScriptToken], altStack : List[ScriptToken],
flags : Seq[ScriptFlag], lastCodeSeparator : Option[Int]) extends ExecutionInProgressScriptProgram
* Implementation type for a [[ExecutionInProgressScriptProgram]] - a [[ScriptProgram]] that is currently being
* evaluated by the [[org.bitcoins.core.script.interpreter.ScriptInterpreter]].
*/
private case class ExecutionInProgressScriptProgramImpl(
txSignatureComponent: TxSigComponent,
stack: List[ScriptToken], script: List[ScriptToken], originalScript: List[ScriptToken], altStack: List[ScriptToken],
flags: Seq[ScriptFlag], lastCodeSeparator: Option[Int]
) extends ExecutionInProgressScriptProgram
/**
* The implementation type for a [[ExecutedScriptProgram]] - a [[ScriptProgram]] that has been evaluated completely
* by the [[org.bitcoins.core.script.interpreter.ScriptInterpreter]].
*/
private case class ExecutedScriptProgramImpl(txSignatureComponent : TxSigComponent,
stack : List[ScriptToken],script : List[ScriptToken], originalScript : List[ScriptToken], altStack : List[ScriptToken],
flags : Seq[ScriptFlag], error : Option[ScriptError]) extends ExecutedScriptProgram
* The implementation type for a [[ExecutedScriptProgram]] - a [[ScriptProgram]] that has been evaluated completely
* by the [[org.bitcoins.core.script.interpreter.ScriptInterpreter]].
*/
private case class ExecutedScriptProgramImpl(
txSignatureComponent: TxSigComponent,
stack: List[ScriptToken], script: List[ScriptToken], originalScript: List[ScriptToken], altStack: List[ScriptToken],
flags: Seq[ScriptFlag], error: Option[ScriptError]
) extends ExecutedScriptProgram
//indicates whether the script or the stack needs to be updated
sealed trait UpdateIndicator
@ -100,250 +103,249 @@ object ScriptProgram extends BitcoinSLogger {
case object AltStack extends UpdateIndicator
case object OriginalScript extends UpdateIndicator
/**
* Sets a [[ScriptError]] on a given [[ScriptProgram]].
* @param oldProgram the program who has hit an invalid state
* @param error the error that the program hit while being executed in the script interpreter
* @return the ExecutedScriptProgram with the given error set inside of the trait
*/
def apply(oldProgram : ScriptProgram, error : ScriptError) : ExecutedScriptProgram = oldProgram match {
case program : PreExecutionScriptProgram =>
ScriptProgram(ScriptProgram.toExecutionInProgress(program),error)
case program : ExecutionInProgressScriptProgram =>
ExecutedScriptProgramImpl(program.txSignatureComponent, program.stack, program.script,program.originalScript,
program.altStack, program.flags, Some(error))
case program : ExecutedScriptProgram =>
ExecutedScriptProgramImpl(program.txSignatureComponent, program.stack, program.script,program.originalScript,
program.altStack, program.flags, Some(error))
* Sets a [[ScriptError]] on a given [[ScriptProgram]].
* @param oldProgram the program who has hit an invalid state
* @param error the error that the program hit while being executed in the script interpreter
* @return the ExecutedScriptProgram with the given error set inside of the trait
*/
def apply(oldProgram: ScriptProgram, error: ScriptError): ExecutedScriptProgram = oldProgram match {
case program: PreExecutionScriptProgram =>
ScriptProgram(ScriptProgram.toExecutionInProgress(program), error)
case program: ExecutionInProgressScriptProgram =>
ExecutedScriptProgramImpl(program.txSignatureComponent, program.stack, program.script, program.originalScript,
program.altStack, program.flags, Some(error))
case program: ExecutedScriptProgram =>
ExecutedScriptProgramImpl(program.txSignatureComponent, program.stack, program.script, program.originalScript,
program.altStack, program.flags, Some(error))
}
def apply(oldProgram : ScriptProgram, flags : Seq[ScriptFlag]) : ScriptProgram = oldProgram match {
case program : PreExecutionScriptProgram =>
PreExecutionScriptProgramImpl(program.txSignatureComponent,program.stack,program.script,program.originalScript,
program.altStack,flags)
case program : ExecutionInProgressScriptProgram =>
ExecutionInProgressScriptProgramImpl(program.txSignatureComponent, program.stack,program.script,program.originalScript,
def apply(oldProgram: ScriptProgram, flags: Seq[ScriptFlag]): ScriptProgram = oldProgram match {
case program: PreExecutionScriptProgram =>
PreExecutionScriptProgramImpl(program.txSignatureComponent, program.stack, program.script, program.originalScript,
program.altStack, flags)
case program: ExecutionInProgressScriptProgram =>
ExecutionInProgressScriptProgramImpl(program.txSignatureComponent, program.stack, program.script, program.originalScript,
program.altStack, flags, program.lastCodeSeparator)
case program : ExecutedScriptProgram =>
case program: ExecutedScriptProgram =>
throw new RuntimeException("Cannot update the script flags on a program that has been executed")
}
def apply(oldProgram : ScriptProgram, tokens : Seq[ScriptToken], indicator : UpdateIndicator) : ScriptProgram = {
def apply(oldProgram: ScriptProgram, tokens: Seq[ScriptToken], indicator: UpdateIndicator): ScriptProgram = {
indicator match {
case Stack =>
oldProgram match {
case program : PreExecutionScriptProgram =>
PreExecutionScriptProgramImpl(program.txSignatureComponent, tokens.toList,program.script,program.originalScript,
program.altStack,program.flags)
case program : ExecutionInProgressScriptProgram =>
ExecutionInProgressScriptProgramImpl(program.txSignatureComponent,tokens.toList,program.script,program.originalScript,
program.altStack,program.flags,program.lastCodeSeparator)
case program : ExecutedScriptProgram =>
case program: PreExecutionScriptProgram =>
PreExecutionScriptProgramImpl(program.txSignatureComponent, tokens.toList, program.script, program.originalScript,
program.altStack, program.flags)
case program: ExecutionInProgressScriptProgram =>
ExecutionInProgressScriptProgramImpl(program.txSignatureComponent, tokens.toList, program.script, program.originalScript,
program.altStack, program.flags, program.lastCodeSeparator)
case program: ExecutedScriptProgram =>
throw new RuntimeException("Cannot update stack for program that has been fully executed")
}
case Script =>
oldProgram match {
case program : PreExecutionScriptProgram =>
PreExecutionScriptProgramImpl(program.txSignatureComponent, program.stack,tokens.toList,program.originalScript,
program.altStack,program.flags)
case program : ExecutionInProgressScriptProgram =>
case program: PreExecutionScriptProgram =>
PreExecutionScriptProgramImpl(program.txSignatureComponent, program.stack, tokens.toList, program.originalScript,
program.altStack, program.flags)
case program: ExecutionInProgressScriptProgram =>
ExecutionInProgressScriptProgramImpl(program.txSignatureComponent, program.stack, tokens.toList, program.originalScript,
program.altStack, program.flags,program.lastCodeSeparator)
case program : ExecutedScriptProgram =>
program.altStack, program.flags, program.lastCodeSeparator)
case program: ExecutedScriptProgram =>
throw new RuntimeException("Cannot update the script for a program that has been fully executed")
}
case AltStack => oldProgram match {
case program : PreExecutionScriptProgram =>
PreExecutionScriptProgramImpl(program.txSignatureComponent, program.stack,program.script,program.originalScript,
tokens.toList,program.flags)
case program : ExecutionInProgressScriptProgram =>
case program: PreExecutionScriptProgram =>
PreExecutionScriptProgramImpl(program.txSignatureComponent, program.stack, program.script, program.originalScript,
tokens.toList, program.flags)
case program: ExecutionInProgressScriptProgram =>
ExecutionInProgressScriptProgramImpl(program.txSignatureComponent, program.stack, program.script, program.originalScript,
tokens.toList, program.flags, program.lastCodeSeparator)
case program : ExecutedScriptProgram =>
case program: ExecutedScriptProgram =>
throw new RuntimeException("Cannot update the alt stack for a program that has been fully executed")
}
case OriginalScript => oldProgram match {
case program : PreExecutionScriptProgram =>
PreExecutionScriptProgramImpl(program.txSignatureComponent, program.stack,program.script,tokens.toList,
program.altStack,program.flags)
case program : ExecutionInProgressScriptProgram =>
case program: PreExecutionScriptProgram =>
PreExecutionScriptProgramImpl(program.txSignatureComponent, program.stack, program.script, tokens.toList,
program.altStack, program.flags)
case program: ExecutionInProgressScriptProgram =>
ExecutionInProgressScriptProgramImpl(program.txSignatureComponent, program.stack, program.script, tokens.toList,
program.altStack, program.flags, program.lastCodeSeparator)
case program : ExecutedScriptProgram =>
case program: ExecutedScriptProgram =>
throw new RuntimeException("Cannot update the original script for a program that has been fully executed")
}
}
}
def apply(oldProgram : ScriptProgram, stackTokens : Seq[ScriptToken], scriptTokens : Seq[ScriptToken]) : ScriptProgram = {
val updatedStack = ScriptProgram(oldProgram,stackTokens,Stack)
val updatedScript = ScriptProgram(updatedStack,scriptTokens,Script)
def apply(oldProgram: ScriptProgram, stackTokens: Seq[ScriptToken], scriptTokens: Seq[ScriptToken]): ScriptProgram = {
val updatedStack = ScriptProgram(oldProgram, stackTokens, Stack)
val updatedScript = ScriptProgram(updatedStack, scriptTokens, Script)
require(updatedStack.stack == stackTokens)
require(updatedScript.script == scriptTokens)
updatedScript
}
/** Updates the last [[org.bitcoins.core.script.crypto.OP_CODESEPARATOR]] index. */
def apply(oldProgram : ExecutionInProgressScriptProgram, lastCodeSeparator : Int) : ExecutionInProgressScriptProgram = {
ExecutionInProgressScriptProgramImpl(oldProgram.txSignatureComponent,
def apply(oldProgram: ExecutionInProgressScriptProgram, lastCodeSeparator: Int): ExecutionInProgressScriptProgram = {
ExecutionInProgressScriptProgramImpl(
oldProgram.txSignatureComponent,
oldProgram.stack, oldProgram.script, oldProgram.originalScript,
oldProgram.altStack, oldProgram.flags,Some(lastCodeSeparator))
oldProgram.altStack, oldProgram.flags, Some(lastCodeSeparator)
)
}
/** Updates the [[ScriptToken]]s in either the stack or script and the last [[org.bitcoins.core.script.crypto.OP_CODESEPARATOR]] index */
def apply(oldProgram : ExecutionInProgressScriptProgram, tokens : Seq[ScriptToken], indicator: UpdateIndicator,
lastCodeSeparator : Int) : ExecutionInProgressScriptProgram = {
def apply(oldProgram: ExecutionInProgressScriptProgram, tokens: Seq[ScriptToken], indicator: UpdateIndicator,
lastCodeSeparator: Int): ExecutionInProgressScriptProgram = {
val updatedIndicator = ScriptProgram(oldProgram, tokens, indicator)
updatedIndicator match {
case e : ExecutionInProgressScriptProgram =>
ScriptProgram(e,lastCodeSeparator)
case _ : PreExecutionScriptProgram | _ : ExecutedScriptProgram =>
case e: ExecutionInProgressScriptProgram =>
ScriptProgram(e, lastCodeSeparator)
case _: PreExecutionScriptProgram | _: ExecutedScriptProgram =>
throw new RuntimeException("We must have a ExecutionInProgressScriptProgram to update the last OP_CODESEPARATOR index")
}
}
/** Updates the [[Stack]], [[Script]], [[AltStack]] of the given [[ScriptProgram]]. */
def apply(oldProgram : ScriptProgram, stack : Seq[ScriptToken], script : Seq[ScriptToken], altStack : Seq[ScriptToken],
updateIndicator: UpdateIndicator) : ScriptProgram = {
val updatedProgramStack = ScriptProgram(oldProgram,stack, Stack)
def apply(oldProgram: ScriptProgram, stack: Seq[ScriptToken], script: Seq[ScriptToken], altStack: Seq[ScriptToken],
updateIndicator: UpdateIndicator): ScriptProgram = {
val updatedProgramStack = ScriptProgram(oldProgram, stack, Stack)
val updatedProgramScript = ScriptProgram(updatedProgramStack, script, Script)
val updatedProgramAltStack = ScriptProgram(updatedProgramScript, altStack, AltStack)
updatedProgramAltStack
}
/**
* Creates a new [[ScriptProgram]] that can be used to verify if a [[Transaction]] at the given inputIndex
* spends a given [[ScriptPubKey]] correctly. Assumes that the [[Script]] to be executed is the [[org.bitcoins.core.protocol.script.ScriptSignature]]
* @param transaction the transaction that is being checked
* @param scriptPubKey the scriptPubKey for which the input is spending
* @param inputIndex the input's index inside of transaction which we are spending
* @param flags the flags which we are enforcing inside of the script interpreter
* @param amount the amount of [[CurrencyUnit]] we are spending in this input
* @return the script program representing all of this information
*/
def apply(transaction: WitnessTransaction, scriptPubKey : P2SHScriptPubKey, inputIndex : UInt32,
flags : Seq[ScriptFlag], amount: CurrencyUnit) : PreExecutionScriptProgram = {
* Creates a new [[ScriptProgram]] that can be used to verify if a [[Transaction]] at the given inputIndex
* spends a given [[ScriptPubKey]] correctly. Assumes that the [[Script]] to be executed is the [[org.bitcoins.core.protocol.script.ScriptSignature]]
* @param transaction the transaction that is being checked
* @param scriptPubKey the scriptPubKey for which the input is spending
* @param inputIndex the input's index inside of transaction which we are spending
* @param flags the flags which we are enforcing inside of the script interpreter
* @param amount the amount of [[CurrencyUnit]] we are spending in this input
* @return the script program representing all of this information
*/
def apply(transaction: WitnessTransaction, scriptPubKey: P2SHScriptPubKey, inputIndex: UInt32,
flags: Seq[ScriptFlag], amount: CurrencyUnit): PreExecutionScriptProgram = {
val script = transaction.inputs(inputIndex.toInt).scriptSignature.asm
ScriptProgram(transaction,scriptPubKey,inputIndex,script.toList,flags, amount)
ScriptProgram(transaction, scriptPubKey, inputIndex, script.toList, flags, amount)
}
def apply(transaction: WitnessTransaction, scriptPubKey : WitnessScriptPubKey, inputIndex : UInt32,
flags : Seq[ScriptFlag], amount: CurrencyUnit) : PreExecutionScriptProgram = {
def apply(transaction: WitnessTransaction, scriptPubKey: WitnessScriptPubKey, inputIndex: UInt32,
flags: Seq[ScriptFlag], amount: CurrencyUnit): PreExecutionScriptProgram = {
val script = transaction.inputs(inputIndex.toInt).scriptSignature.asm
ScriptProgram(transaction,scriptPubKey,inputIndex,script.toList,flags, amount)
ScriptProgram(transaction, scriptPubKey, inputIndex, script.toList, flags, amount)
}
def apply(transaction: Transaction, scriptPubKey : ScriptPubKey, inputIndex : UInt32,
stack : Seq[ScriptToken], script: Seq[ScriptToken], flags : Seq[ScriptFlag]): ScriptProgram = {
val p = ScriptProgram(transaction,scriptPubKey,inputIndex,flags)
ScriptProgram(p,stack,script)
def apply(transaction: Transaction, scriptPubKey: ScriptPubKey, inputIndex: UInt32,
stack: Seq[ScriptToken], script: Seq[ScriptToken], flags: Seq[ScriptFlag]): ScriptProgram = {
val p = ScriptProgram(transaction, scriptPubKey, inputIndex, flags)
ScriptProgram(p, stack, script)
}
def apply(transaction: WitnessTransaction, scriptPubKey : P2SHScriptPubKey, inputIndex : UInt32, script : Seq[ScriptToken],
flags : Seq[ScriptFlag], amount: CurrencyUnit) : PreExecutionScriptProgram = {
val w = WitnessTxSigComponent(transaction,inputIndex,scriptPubKey,flags,amount)
ScriptProgram(w,Nil,script,script,Nil)
def apply(transaction: WitnessTransaction, scriptPubKey: P2SHScriptPubKey, inputIndex: UInt32, script: Seq[ScriptToken],
flags: Seq[ScriptFlag], amount: CurrencyUnit): PreExecutionScriptProgram = {
val w = WitnessTxSigComponent(transaction, inputIndex, scriptPubKey, flags, amount)
ScriptProgram(w, Nil, script, script, Nil)
}
def apply(transaction: WitnessTransaction, scriptPubKey : WitnessScriptPubKey, inputIndex : UInt32, script : Seq[ScriptToken],
flags : Seq[ScriptFlag], amount: CurrencyUnit) : PreExecutionScriptProgram = {
val w = WitnessTxSigComponent(transaction,inputIndex,scriptPubKey,flags,amount)
ScriptProgram(w,Nil,script,script,Nil)
def apply(transaction: WitnessTransaction, scriptPubKey: WitnessScriptPubKey, inputIndex: UInt32, script: Seq[ScriptToken],
flags: Seq[ScriptFlag], amount: CurrencyUnit): PreExecutionScriptProgram = {
val w = WitnessTxSigComponent(transaction, inputIndex, scriptPubKey, flags, amount)
ScriptProgram(w, Nil, script, script, Nil)
}
def apply(t: TxSigComponent, stack: Seq[ScriptToken], script: Seq[ScriptToken], originalScript: Seq[ScriptToken],
altStack: Seq[ScriptToken]): PreExecutionScriptProgram = {
ScriptProgram(t,stack.toList,script.toList,originalScript.toList,altStack.toList,t.flags)
ScriptProgram(t, stack.toList, script.toList, originalScript.toList, altStack.toList, t.flags)
}
def apply(transaction: WitnessTransaction, scriptPubKey : WitnessScriptPubKey, inputIndex : UInt32, stack : Seq[ScriptToken],
script : Seq[ScriptToken], flags : Seq[ScriptFlag], witness: ScriptWitness,
amount: CurrencyUnit) : ScriptProgram = {
val program = ScriptProgram(transaction,scriptPubKey,inputIndex,flags, amount)
ScriptProgram(program,stack,script)
def apply(transaction: WitnessTransaction, scriptPubKey: WitnessScriptPubKey, inputIndex: UInt32, stack: Seq[ScriptToken],
script: Seq[ScriptToken], flags: Seq[ScriptFlag], witness: ScriptWitness,
amount: CurrencyUnit): ScriptProgram = {
val program = ScriptProgram(transaction, scriptPubKey, inputIndex, flags, amount)
ScriptProgram(program, stack, script)
}
def apply(transaction: WitnessTransaction, scriptPubKey : P2SHScriptPubKey, inputIndex : UInt32, stack : Seq[ScriptToken],
script : Seq[ScriptToken], flags : Seq[ScriptFlag], witness: ScriptWitness,
amount: CurrencyUnit) : ScriptProgram = {
val program = ScriptProgram(transaction,scriptPubKey,inputIndex,flags, amount)
ScriptProgram(program,stack,script)
def apply(transaction: WitnessTransaction, scriptPubKey: P2SHScriptPubKey, inputIndex: UInt32, stack: Seq[ScriptToken],
script: Seq[ScriptToken], flags: Seq[ScriptFlag], witness: ScriptWitness,
amount: CurrencyUnit): ScriptProgram = {
val program = ScriptProgram(transaction, scriptPubKey, inputIndex, flags, amount)
ScriptProgram(program, stack, script)
}
def apply(txSignatureComponent: TxSigComponent, stack: Seq[ScriptToken], script: Seq[ScriptToken],
originalScript: Seq[ScriptToken]): ScriptProgram = {
ScriptProgram(txSignatureComponent,stack.toList, script.toList,originalScript.toList,Nil)
ScriptProgram(txSignatureComponent, stack.toList, script.toList, originalScript.toList, Nil)
}
def apply(t: TxSigComponent, stack: Seq[ScriptToken], script: Seq[ScriptToken]): ScriptProgram = {
val original = t.scriptSignature.asm
ScriptProgram(t,stack,script,original)
ScriptProgram(t, stack, script, original)
}
/** Creates a fresh [[PreExecutionScriptProgram]] */
def apply(transaction: WitnessTransaction, scriptPubKey: WitnessScriptPubKey, inputIndex: UInt32, stack: Seq[ScriptToken],
script: Seq[ScriptToken], originalScript: Seq[ScriptToken], altStack: Seq[ScriptToken],
flags: Seq[ScriptFlag], amount: CurrencyUnit): PreExecutionScriptProgram = {
val t = WitnessTxSigComponent(transaction,inputIndex,
scriptPubKey,flags, amount)
ScriptProgram(t,stack.toList,script.toList,originalScript.toList,altStack.toList,flags)
val t = WitnessTxSigComponent(transaction, inputIndex,
scriptPubKey, flags, amount)
ScriptProgram(t, stack.toList, script.toList, originalScript.toList, altStack.toList, flags)
}
def apply(transaction: WitnessTransaction, scriptPubKey: P2SHScriptPubKey, inputIndex: UInt32, stack: Seq[ScriptToken],
script: Seq[ScriptToken], originalScript: Seq[ScriptToken], altStack: Seq[ScriptToken],
flags: Seq[ScriptFlag], amount: CurrencyUnit): PreExecutionScriptProgram = {
val t = WitnessTxSigComponent(transaction,inputIndex,
scriptPubKey,flags, amount)
ScriptProgram(t,stack.toList,script.toList,originalScript.toList,altStack.toList,flags)
val t = WitnessTxSigComponent(transaction, inputIndex,
scriptPubKey, flags, amount)
ScriptProgram(t, stack.toList, script.toList, originalScript.toList, altStack.toList, flags)
}
def apply(transaction: BaseTransaction, scriptPubKey: ScriptPubKey, inputIndex: UInt32, stack: Seq[ScriptToken],
script: Seq[ScriptToken], originalScript: Seq[ScriptToken], altStack: Seq[ScriptToken],
flags: Seq[ScriptFlag]): PreExecutionScriptProgram = {
val t = BaseTxSigComponent(transaction,inputIndex,scriptPubKey,flags)
ScriptProgram(t,stack.toList,script.toList,originalScript.toList,altStack.toList,flags)
val t = BaseTxSigComponent(transaction, inputIndex, scriptPubKey, flags)
ScriptProgram(t, stack.toList, script.toList, originalScript.toList, altStack.toList, flags)
}
def apply(t: TxSigComponent, stack: Seq[ScriptToken], script: Seq[ScriptToken], originalScript: Seq[ScriptToken],
altStack: Seq[ScriptToken], flags: Seq[ScriptFlag]): PreExecutionScriptProgram = {
PreExecutionScriptProgramImpl(t,stack.toList,script.toList,originalScript.toList,altStack.toList,flags)
PreExecutionScriptProgramImpl(t, stack.toList, script.toList, originalScript.toList, altStack.toList, flags)
}
/** Creates a fresh instance of [[org.bitcoins.core.script.PreExecutionScriptProgram]] */
def apply(transaction: Transaction, scriptPubKey: ScriptPubKey, inputIndex: UInt32, flags: Seq[ScriptFlag]): PreExecutionScriptProgram = {
val t = BaseTxSigComponent(transaction,inputIndex,scriptPubKey,flags)
val t = BaseTxSigComponent(transaction, inputIndex, scriptPubKey, flags)
ScriptProgram(t)
}
def apply(txSigComponent: TxSigComponent): PreExecutionScriptProgram = {
val script = txSigComponent.scriptSignature.asm
ScriptProgram(txSigComponent,Nil,script.toList,script.toList,Nil,txSigComponent.flags)
ScriptProgram(txSigComponent, Nil, script.toList, script.toList, Nil, txSigComponent.flags)
}
/** Changes a [[ScriptProgram]] that is a [[ExecutionInProgressScriptProgram]] and changes it to an [[ExecutedScriptProgram]].*/
def toExecutedProgram(executionInProgressScriptProgram: ExecutionInProgressScriptProgram) : ExecutedScriptProgram = {
def toExecutedProgram(executionInProgressScriptProgram: ExecutionInProgressScriptProgram): ExecutedScriptProgram = {
ExecutedScriptProgramImpl(executionInProgressScriptProgram.txSignatureComponent, executionInProgressScriptProgram.stack,
executionInProgressScriptProgram.script,executionInProgressScriptProgram.originalScript,executionInProgressScriptProgram.altStack,
executionInProgressScriptProgram.flags,None)
executionInProgressScriptProgram.script, executionInProgressScriptProgram.originalScript, executionInProgressScriptProgram.altStack,
executionInProgressScriptProgram.flags, None)
}
/** Changes a [[ScriptProgram]] that is a [[PreExecutionScriptProgram]] and changes it to an [[ExecutionInProgressScriptProgram]].*/
def toExecutionInProgress(preExecutionScriptProgram: PreExecutionScriptProgram) : ExecutionInProgressScriptProgram = {
toExecutionInProgress(preExecutionScriptProgram,None)
def toExecutionInProgress(preExecutionScriptProgram: PreExecutionScriptProgram): ExecutionInProgressScriptProgram = {
toExecutionInProgress(preExecutionScriptProgram, None)
}
/** Changes a [[ScriptProgram]] that is a [[PreExecutionScriptProgram]] and changes it to an [[ExecutionInProgressScriptProgram]] given the stack state. */
def toExecutionInProgress(preExecutionScriptProgram: PreExecutionScriptProgram, stack : Option[Seq[ScriptToken]]) : ExecutionInProgressScriptProgram = {
def toExecutionInProgress(preExecutionScriptProgram: PreExecutionScriptProgram, stack: Option[Seq[ScriptToken]]): ExecutionInProgressScriptProgram = {
stack match {
case Some(stackTokens) => ExecutionInProgressScriptProgramImpl(preExecutionScriptProgram.txSignatureComponent,stackTokens.toList,preExecutionScriptProgram.script,
preExecutionScriptProgram.originalScript,preExecutionScriptProgram.altStack,preExecutionScriptProgram.flags, None)
case Some(stackTokens) => ExecutionInProgressScriptProgramImpl(preExecutionScriptProgram.txSignatureComponent, stackTokens.toList, preExecutionScriptProgram.script,
preExecutionScriptProgram.originalScript, preExecutionScriptProgram.altStack, preExecutionScriptProgram.flags, None)
case None =>
ExecutionInProgressScriptProgramImpl(preExecutionScriptProgram.txSignatureComponent,preExecutionScriptProgram.stack,preExecutionScriptProgram.script,
preExecutionScriptProgram.originalScript,preExecutionScriptProgram.altStack,preExecutionScriptProgram.flags, None)
ExecutionInProgressScriptProgramImpl(preExecutionScriptProgram.txSignatureComponent, preExecutionScriptProgram.stack, preExecutionScriptProgram.script,
preExecutionScriptProgram.originalScript, preExecutionScriptProgram.altStack, preExecutionScriptProgram.flags, None)
}
}

View file

@ -8,7 +8,7 @@ trait ScriptSettings {
/**
* A integer representing the maximum number of public keys you can have in a
* OP_CHECKMULTISIG or OP_CHECKMULTISIGVERIFY operation
* https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L903
* https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L903
* @return
*/
def maxPublicKeysPerMultiSig = 20

View file

@ -1,11 +1,11 @@
package org.bitcoins.core.script.arithmetic
import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.control.{ControlOperationsInterpreter, OP_VERIFY}
import org.bitcoins.core.script.control.{ ControlOperationsInterpreter, OP_VERIFY }
import org.bitcoins.core.script.flag.ScriptFlagUtil
import org.bitcoins.core.script.result._
import org.bitcoins.core.script.{ExecutedScriptProgram, ExecutionInProgressScriptProgram, PreExecutionScriptProgram, ScriptProgram}
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinScriptUtil}
import org.bitcoins.core.script.{ ExecutedScriptProgram, ExecutionInProgressScriptProgram, PreExecutionScriptProgram, ScriptProgram }
import org.bitcoins.core.util.{ BitcoinSLogger, BitcoinScriptUtil }
import scala.annotation.tailrec
@ -15,60 +15,60 @@ import scala.annotation.tailrec
sealed abstract class ArithmeticInterpreter {
private def logger = BitcoinSLogger.logger
/** a is added to b. */
def opAdd(program : ScriptProgram) : ScriptProgram = {
def opAdd(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_ADD), "Script top must be OP_ADD")
performBinaryArithmeticOperation(program, (x,y) => x + y)
performBinaryArithmeticOperation(program, (x, y) => x + y)
}
/** Increments the stack top by 1. */
def op1Add(program : ScriptProgram) : ScriptProgram = {
def op1Add(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_1ADD), "Script top must be OP_1ADD")
performUnaryArithmeticOperation(program, x => x + ScriptNumber.one)
}
/** Decrements the stack top by 1. */
def op1Sub(program : ScriptProgram) : ScriptProgram = {
def op1Sub(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_1SUB), "Script top must be OP_1SUB")
performUnaryArithmeticOperation(program, x => x - ScriptNumber.one )
performUnaryArithmeticOperation(program, x => x - ScriptNumber.one)
}
/** b is subtracted from a. */
def opSub(program : ScriptProgram) : ScriptProgram = {
def opSub(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_SUB), "Script top must be OP_SUB")
performBinaryArithmeticOperation(program, (x,y) => y - x)
performBinaryArithmeticOperation(program, (x, y) => y - x)
}
/** Takes the absolute value of the stack top. */
def opAbs(program : ScriptProgram) : ScriptProgram = {
def opAbs(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_ABS), "Script top must be OP_ABS")
performUnaryArithmeticOperation(program, x => x match {
case ScriptNumber.zero => ScriptNumber.zero
case _ : ScriptNumber => ScriptNumber(x.toLong.abs)
case _: ScriptNumber => ScriptNumber(x.toLong.abs)
})
}
/** Negates the stack top. */
def opNegate(program : ScriptProgram) : ScriptProgram = {
def opNegate(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_NEGATE), "Script top must be OP_NEGATE")
performUnaryArithmeticOperation(program, x => -x)
}
/** If the input is 0 or 1, it is flipped. Otherwise the output will be 0. */
def opNot(program : ScriptProgram) : ScriptProgram = {
def opNot(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_NOT), "Script top must be OP_NOT")
performUnaryArithmeticOperation(program, x => if (program.stackTopIsFalse) OP_TRUE else OP_FALSE)
}
/** Returns 0 if the input is 0. 1 otherwise. */
def op0NotEqual(program : ScriptProgram) : ScriptProgram = {
def op0NotEqual(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_0NOTEQUAL), "Script top must be OP_0NOTEQUAL")
performUnaryArithmeticOperation(program, x => if(x.toLong == 0) OP_FALSE else OP_TRUE)
performUnaryArithmeticOperation(program, x => if (x.toLong == 0) OP_FALSE else OP_TRUE)
}
/** If both a and b are not 0, the output is 1. Otherwise 0. */
def opBoolAnd(program : ScriptProgram) : ScriptProgram = {
def opBoolAnd(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_BOOLAND), "Script top must be OP_BOOLAND")
performBinaryBooleanOperation(program,(x,y) => {
performBinaryBooleanOperation(program, (x, y) => {
val xIsFalse = (x == ScriptNumber.zero || x == OP_0)
val yIsFalse = (y == ScriptNumber.zero || y == OP_0)
if (xIsFalse || yIsFalse) false else true
@ -76,108 +76,126 @@ sealed abstract class ArithmeticInterpreter {
}
/** If a or b is not 0, the output is 1. Otherwise 0. */
def opBoolOr(program : ScriptProgram) : ScriptProgram = {
def opBoolOr(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_BOOLOR), "Script top must be OP_BOOLOR")
performBinaryBooleanOperation(program, (x,y) => {
performBinaryBooleanOperation(program, (x, y) => {
if (x == y && (x == ScriptNumber.zero || x == OP_0)) false else true
})
}
/** Returns 1 if the numbers are equal, 0 otherwise. */
def opNumEqual(program : ScriptProgram) : ScriptProgram = {
def opNumEqual(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_NUMEQUAL), "Script top must be OP_NUMEQUAL")
performBinaryBooleanOperation(program,(x,y) => x.numEqual(y))
performBinaryBooleanOperation(program, (x, y) => x.numEqual(y))
}
/** Same as [[OP_NUMEQUAL]], but runs [[OP_VERIFY]] afterward. */
def opNumEqualVerify(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.contains(OP_NUMEQUALVERIFY),
"Script top must be OP_NUMEQUALVERIFY")
def opNumEqualVerify(program: ScriptProgram): ScriptProgram = {
require(
program.script.headOption.contains(OP_NUMEQUALVERIFY),
"Script top must be OP_NUMEQUALVERIFY"
)
if (program.stack.size < 2) {
logger.error("OP_NUMEQUALVERIFY requires two stack elements")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
} else {
val numEqualProgram = ScriptProgram(program, program.stack, OP_NUMEQUAL :: program.script.tail)
val numEqualResult = opNumEqual(numEqualProgram)
numEqualResult match {
case _ : ExecutionInProgressScriptProgram =>
case _: ExecutionInProgressScriptProgram =>
val verifyProgram = ScriptProgram(numEqualResult, numEqualResult.stack, OP_VERIFY :: numEqualResult.script)
val verifyResult = ControlOperationsInterpreter.opVerify(verifyProgram)
verifyResult
case _ : PreExecutionScriptProgram | _ : ExecutedScriptProgram =>
case _: PreExecutionScriptProgram | _: ExecutedScriptProgram =>
numEqualResult
}
}
}
/** Returns 1 if the numbers are not equal, 0 otherwise. */
def opNumNotEqual(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.contains(OP_NUMNOTEQUAL),
"Script top must be OP_NUMNOTEQUAL")
performBinaryBooleanOperation(program, (x,y) => {
def opNumNotEqual(program: ScriptProgram): ScriptProgram = {
require(
program.script.headOption.contains(OP_NUMNOTEQUAL),
"Script top must be OP_NUMNOTEQUAL"
)
performBinaryBooleanOperation(program, (x, y) => {
x.toLong != y.toLong
})
}
/** Returns 1 if a is less than b, 0 otherwise. */
def opLessThan(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.contains(OP_LESSTHAN),
"Script top must be OP_LESSTHAN")
performBinaryBooleanOperation(program, (x,y) => y < x)
def opLessThan(program: ScriptProgram): ScriptProgram = {
require(
program.script.headOption.contains(OP_LESSTHAN),
"Script top must be OP_LESSTHAN"
)
performBinaryBooleanOperation(program, (x, y) => y < x)
}
/** Returns 1 if a is greater than b, 0 otherwise. */
def opGreaterThan(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.contains(OP_GREATERTHAN),
"Script top must be OP_GREATERTHAN")
performBinaryBooleanOperation(program, (x,y) => y > x)
def opGreaterThan(program: ScriptProgram): ScriptProgram = {
require(
program.script.headOption.contains(OP_GREATERTHAN),
"Script top must be OP_GREATERTHAN"
)
performBinaryBooleanOperation(program, (x, y) => y > x)
}
/** Returns 1 if a is less than or equal to b, 0 otherwise. */
def opLessThanOrEqual(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.contains(OP_LESSTHANOREQUAL),
"Script top must be OP_LESSTHANOREQUAL")
performBinaryBooleanOperation(program, (x,y) => y <= x)
def opLessThanOrEqual(program: ScriptProgram): ScriptProgram = {
require(
program.script.headOption.contains(OP_LESSTHANOREQUAL),
"Script top must be OP_LESSTHANOREQUAL"
)
performBinaryBooleanOperation(program, (x, y) => y <= x)
}
/** Returns 1 if a is greater than or equal to b, 0 otherwise. */
def opGreaterThanOrEqual(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.contains(OP_GREATERTHANOREQUAL),
"Script top must be OP_GREATERTHANOREQUAL")
performBinaryBooleanOperation(program, (x,y) => y >= x)
def opGreaterThanOrEqual(program: ScriptProgram): ScriptProgram = {
require(
program.script.headOption.contains(OP_GREATERTHANOREQUAL),
"Script top must be OP_GREATERTHANOREQUAL"
)
performBinaryBooleanOperation(program, (x, y) => y >= x)
}
/** Returns the smaller of a and b. */
def opMin(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.contains(OP_MIN),
"Script top must be OP_MIN")
def opMin(program: ScriptProgram): ScriptProgram = {
require(
program.script.headOption.contains(OP_MIN),
"Script top must be OP_MIN"
)
if (program.stack.size < 2) {
logger.error("OP_MAX requires at least two stack elements")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
} else {
performComparisonOnTwoStackTopItems(program, (x : ScriptNumber, y : ScriptNumber) => if (x < y) x else y)
performComparisonOnTwoStackTopItems(program, (x: ScriptNumber, y: ScriptNumber) => if (x < y) x else y)
}
}
/** Returns the larger of a and b. */
def opMax(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.contains(OP_MAX),
"Script top must be OP_MAX")
def opMax(program: ScriptProgram): ScriptProgram = {
require(
program.script.headOption.contains(OP_MAX),
"Script top must be OP_MAX"
)
if (program.stack.size < 2) {
logger.error("OP_MAX requires at least two stack elements")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
} else {
performComparisonOnTwoStackTopItems(program, (x : ScriptNumber, y : ScriptNumber) => if (x > y) x else y)
performComparisonOnTwoStackTopItems(program, (x: ScriptNumber, y: ScriptNumber) => if (x > y) x else y)
}
}
/** Returns 1 if x is within the specified range (left-inclusive), 0 otherwise. */
def opWithin(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.contains(OP_WITHIN),
"Script top must be OP_WITHIN")
def opWithin(program: ScriptProgram): ScriptProgram = {
require(
program.script.headOption.contains(OP_WITHIN),
"Script top must be OP_WITHIN"
)
if (program.stack.size < 3) {
logger.error("OP_WITHIN requires at least 3 elements on the stack")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
} else {
val c = ScriptNumber(program.stack.head.bytes)
val b = ScriptNumber(program.stack.tail.head.bytes)
@ -191,7 +209,7 @@ sealed abstract class ArithmeticInterpreter {
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L999-L1002
logger.error("Cannot perform arithmetic operation on a number larger than 4 bytes, one of these three numbers is larger than 4 bytes: "
+ c + " " + b + " " + a)
ScriptProgram(program,ScriptErrorUnknownError)
ScriptProgram(program, ScriptErrorUnknownError)
} else {
val isWithinRange = a >= b && a < c
val newStackTop = if (isWithinRange) OP_TRUE else OP_FALSE
@ -200,23 +218,26 @@ sealed abstract class ArithmeticInterpreter {
}
}
/** This function checks if a number is <= 4 bytes in size
/**
* This function checks if a number is <= 4 bytes in size
* We cannot perform arithmetic operations on bitcoin numbers that are larger than 4 bytes.
* https://github.com/bitcoin/bitcoin/blob/a6a860796a44a2805a58391a009ba22752f64e32/src/script/script.h#L214-L239. */
private def isLargerThan4Bytes(scriptNumber : ScriptNumber) : Boolean = scriptNumber.bytes.size > 4
* https://github.com/bitcoin/bitcoin/blob/a6a860796a44a2805a58391a009ba22752f64e32/src/script/script.h#L214-L239.
*/
private def isLargerThan4Bytes(scriptNumber: ScriptNumber): Boolean = scriptNumber.bytes.size > 4
/** Performs the given arithmetic operation on the stack head
/**
* Performs the given arithmetic operation on the stack head
* @param program the program whose stack top is used as an argument for the arithmetic operation
* @param op the arithmetic ooperation that needs to be executed on the number, for instance incrementing by 1
* @return the program with the result from performing the arithmetic operation pushed onto the top of the stack
*/
@tailrec
private def performUnaryArithmeticOperation(program : ScriptProgram, op : ScriptNumber => ScriptNumber) : ScriptProgram = {
private def performUnaryArithmeticOperation(program: ScriptProgram, op: ScriptNumber => ScriptNumber): ScriptProgram = {
program.stack.headOption match {
case None =>
logger.error("We need one stack element for performing a unary arithmetic operation")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
case Some(s : ScriptNumber) =>
ScriptProgram(program, ScriptErrorInvalidStackOperation)
case Some(s: ScriptNumber) =>
if (ScriptFlagUtil.requireMinimalData(program.flags) && !BitcoinScriptUtil.isShortestEncoding(s)) {
logger.error("The number you gave us is not encoded in the shortest way possible")
ScriptProgram(program, ScriptErrorMinimalData)
@ -224,41 +245,42 @@ sealed abstract class ArithmeticInterpreter {
logger.error("Cannot perform arithmetic operation on a number larger than 4 bytes, here is the number: " + s)
//pretty sure that an error is thrown inside of CScriptNum which in turn is caught by interpreter.cpp here
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L999-L1002
ScriptProgram(program,ScriptErrorUnknownError)
ScriptProgram(program, ScriptErrorUnknownError)
} else {
val newScriptNumber = op(s)
ScriptProgram(program, newScriptNumber :: program.stack.tail, program.script.tail)
}
case Some(s : ScriptConstant) =>
case Some(s: ScriptConstant) =>
if (ScriptFlagUtil.requireMinimalData(program.flags) && !BitcoinScriptUtil.isShortestEncoding(s)) {
logger.error("The number you gave us is not encoded in the shortest way possible")
ScriptProgram(program, ScriptErrorUnknownError)
} else {
val interpretedNumber = ScriptNumber(ScriptNumberUtil.toLong(s.hex))
val newProgram = ScriptProgram(program, interpretedNumber :: program.stack.tail, ScriptProgram.Stack)
val newProgram = ScriptProgram(program, interpretedNumber :: program.stack.tail, ScriptProgram.Stack)
performUnaryArithmeticOperation(newProgram, op)
}
case Some(s : ScriptToken) =>
case Some(s: ScriptToken) =>
//pretty sure that an error is thrown inside of CScriptNum which in turn is caught by interpreter.cpp here
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L999-L1002
logger.error("Stack top must be a script number/script constant to perform an arithmetic operation")
ScriptProgram(program,ScriptErrorUnknownError)
ScriptProgram(program, ScriptErrorUnknownError)
}
}
/** Performs the given arithmetic operation on the top two stack items
/**
* Performs the given arithmetic operation on the top two stack items
* @param program the program whose stack top is used as an argument for the arithmetic operation
* @param op the arithmetic ooperation that needs to be executed on the number, for instance incrementing by 1
* @return the program with the result from performing the arithmetic operation pushed onto the top of the stack
*/
@tailrec
private def performBinaryArithmeticOperation(program : ScriptProgram, op : (ScriptNumber, ScriptNumber) => ScriptNumber) : ScriptProgram = {
private def performBinaryArithmeticOperation(program: ScriptProgram, op: (ScriptNumber, ScriptNumber) => ScriptNumber): ScriptProgram = {
if (program.stack.size < 2) {
logger.error("We must have two elements to perform a binary arithmetic operation")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
} else {
(program.stack.head, program.stack.tail.head) match {
case (x : ScriptNumber, y : ScriptNumber) =>
case (x: ScriptNumber, y: ScriptNumber) =>
if (ScriptFlagUtil.requireMinimalData(program.flags) && (!BitcoinScriptUtil.isShortestEncoding(x) || !BitcoinScriptUtil.isShortestEncoding(y))) {
logger.error("The constant you gave us is not encoded in the shortest way possible")
ScriptProgram(program, ScriptErrorUnknownError)
@ -266,46 +288,47 @@ sealed abstract class ArithmeticInterpreter {
//pretty sure that an error is thrown inside of CScriptNum which in turn is caught by interpreter.cpp here
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L999-L1002
logger.error("Cannot perform arithmetic operation on a number larger than 4 bytes, one of these two numbers is larger than 4 bytes: " + x + " " + y)
ScriptProgram(program,ScriptErrorUnknownError)
ScriptProgram(program, ScriptErrorUnknownError)
} else {
val newStackTop = op(x,y)
ScriptProgram(program,newStackTop :: program.stack.tail.tail,program.script.tail)
val newStackTop = op(x, y)
ScriptProgram(program, newStackTop :: program.stack.tail.tail, program.script.tail)
}
case (x : ScriptConstant, y : ScriptNumber) =>
case (x: ScriptConstant, y: ScriptNumber) =>
//interpret x as a number
val interpretedNumber = ScriptNumber(x.hex)
val newProgram = ScriptProgram(program, interpretedNumber :: program.stack.tail, ScriptProgram.Stack)
performBinaryArithmeticOperation(newProgram, op)
case (x : ScriptNumber, y : ScriptConstant) =>
case (x: ScriptNumber, y: ScriptConstant) =>
val interpretedNumber = ScriptNumber(y.hex)
val newProgram = ScriptProgram(program, x :: interpretedNumber :: program.stack.tail, ScriptProgram.Stack)
val newProgram = ScriptProgram(program, x :: interpretedNumber :: program.stack.tail, ScriptProgram.Stack)
performBinaryArithmeticOperation(newProgram, op)
case (x : ScriptConstant, y : ScriptConstant) =>
case (x: ScriptConstant, y: ScriptConstant) =>
//interpret x and y as a number
val interpretedNumberX = ScriptNumber(x.hex)
val interpretedNumberY = ScriptNumber(y.hex)
val newProgram = ScriptProgram(program, interpretedNumberX :: interpretedNumberY :: program.stack.tail.tail, ScriptProgram.Stack)
val newProgram = ScriptProgram(program, interpretedNumberX :: interpretedNumberY :: program.stack.tail.tail, ScriptProgram.Stack)
performBinaryArithmeticOperation(newProgram, op)
case (x : ScriptToken, y : ScriptToken) =>
case (x: ScriptToken, y: ScriptToken) =>
//pretty sure that an error is thrown inside of CScriptNum which in turn is caught by interpreter.cpp here
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L999-L1002
logger.error("The top two stack items must be script numbers to perform an arithmetic operation")
ScriptProgram(program,ScriptErrorUnknownError)
ScriptProgram(program, ScriptErrorUnknownError)
}
}
}
/** Compares two script numbers with the given boolean operation
/**
* Compares two script numbers with the given boolean operation
* @param program the program whose two top stack elements are used for the comparison
* @param op the operation which compares the two script numbers
* @return the program with either OP_FALSE or OP_TRUE on the stack top
*/
private def performBinaryBooleanOperation(program : ScriptProgram, op : (ScriptNumber, ScriptNumber) => Boolean) : ScriptProgram = {
private def performBinaryBooleanOperation(program: ScriptProgram, op: (ScriptNumber, ScriptNumber) => Boolean): ScriptProgram = {
if (program.stack.size < 2) {
logger.error("We need two stack elements for a binary boolean operation")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
} else {
val (x,y) = parseTopTwoStackElementsAsScriptNumbers(program)
val (x, y) = parseTopTwoStackElementsAsScriptNumbers(program)
if (ScriptFlagUtil.requireMinimalData(program.flags) &&
(!BitcoinScriptUtil.isShortestEncoding(x) || !BitcoinScriptUtil.isShortestEncoding(y))) {
logger.error("The constant you gave us is not encoded in the shortest way possible")
@ -314,23 +337,26 @@ sealed abstract class ArithmeticInterpreter {
//pretty sure that an error is thrown inside of CScriptNum which in turn is caught by interpreter.cpp here
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L999-L1002
logger.error("Cannot perform boolean operation on a number larger than 4 bytes, one of these two numbers is larger than 4 bytes: " + x + " " + y)
ScriptProgram(program,ScriptErrorUnknownError)
ScriptProgram(program, ScriptErrorUnknownError)
} else {
val newStackTop = if(op(x,y)) OP_TRUE else OP_FALSE
ScriptProgram(program,newStackTop :: program.stack.tail.tail,program.script.tail)
val newStackTop = if (op(x, y)) OP_TRUE else OP_FALSE
ScriptProgram(program, newStackTop :: program.stack.tail.tail, program.script.tail)
}
}
}
/** Takes the top two stack items, parses them to numbers then executes the op function on them and places the result
/**
* Takes the top two stack items, parses them to numbers then executes the op function on them and places the result
* onto the stack top
* @param program the script program whose two top stack items are used as arguments for op
* @param op the operation that needs to be executed on the two stack top items
* @return the program with the result of op pushed onto the top of the stack
*/
private def performComparisonOnTwoStackTopItems(program : ScriptProgram,
op : (ScriptNumber, ScriptNumber) => ScriptNumber) : ScriptProgram = {
performBinaryArithmeticOperation(program,op)
private def performComparisonOnTwoStackTopItems(
program: ScriptProgram,
op: (ScriptNumber, ScriptNumber) => ScriptNumber
): ScriptProgram = {
performBinaryArithmeticOperation(program, op)
}
/**
@ -338,22 +364,22 @@ sealed abstract class ArithmeticInterpreter {
* @param program the program whose top two stack elements are being parsed as script numbers
* @return the tuple with the first element being the first stack element, the second element in the tuple being the second stack element
*/
private def parseTopTwoStackElementsAsScriptNumbers(program : ScriptProgram) : (ScriptNumber,ScriptNumber) = {
private def parseTopTwoStackElementsAsScriptNumbers(program: ScriptProgram): (ScriptNumber, ScriptNumber) = {
(program.stack.head, program.stack.tail.head) match {
case (x : ScriptNumber, y : ScriptNumber) => (x,y)
case (x : ScriptConstant, y : ScriptNumber) =>
val interpretedNumber = ScriptNumber(x.hex)
(interpretedNumber,y)
case (x : ScriptNumber, y : ScriptConstant) =>
//interpret y as a number
val interpretedNumber = ScriptNumber(y.hex)
(x,interpretedNumber)
case (x : ScriptConstant, y : ScriptConstant) =>
//interpret x and y as a number
val interpretedNumberX = ScriptNumber(x.hex)
val interpretedNumberY = ScriptNumber(y.hex)
(interpretedNumberX,interpretedNumberY)
case (x : ScriptToken, y : ScriptToken) =>
case (x: ScriptNumber, y: ScriptNumber) => (x, y)
case (x: ScriptConstant, y: ScriptNumber) =>
val interpretedNumber = ScriptNumber(x.hex)
(interpretedNumber, y)
case (x: ScriptNumber, y: ScriptConstant) =>
//interpret y as a number
val interpretedNumber = ScriptNumber(y.hex)
(x, interpretedNumber)
case (x: ScriptConstant, y: ScriptConstant) =>
//interpret x and y as a number
val interpretedNumberX = ScriptNumber(x.hex)
val interpretedNumberY = ScriptNumber(y.hex)
(interpretedNumberX, interpretedNumberY)
case (x: ScriptToken, y: ScriptToken) =>
//pretty sure that an error is thrown inside of CScriptNum which in turn is caught by interpreter.cpp here
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L999-L1002
logger.error("The top two stack items must be script numbers to perform an arithmetic operation")

View file

@ -64,7 +64,7 @@ case object OP_NUMEQUAL extends ArithmeticOperation {
/** Same as OP_NUMEQUAL, but runs OP_VERIFY afterward. */
case object OP_NUMEQUALVERIFY extends ArithmeticOperation {
override def opCode= 157
override def opCode = 157
}
/** Returns 1 if the numbers are not equal, 0 otherwise. */
@ -103,7 +103,7 @@ case object OP_MAX extends ArithmeticOperation {
}
/** Returns 1 if x is within the specified range (left-inclusive), 0 otherwise. */
case object OP_WITHIN extends ArithmeticOperation {
case object OP_WITHIN extends ArithmeticOperation {
override def opCode = 165
}
@ -135,12 +135,12 @@ case object OP_MOD extends ArithmeticOperation {
}
/** Shifts a left b bits, preserving sign. disabled. */
case object OP_LSHIFT extends ArithmeticOperation {
case object OP_LSHIFT extends ArithmeticOperation {
override def opCode = 152
}
/** Shifts a right b bits, preserving sign. disabled. */
case object OP_RSHIFT extends ArithmeticOperation {
case object OP_RSHIFT extends ArithmeticOperation {
override def opCode = 153
}
@ -148,7 +148,6 @@ object ArithmeticOperation extends ScriptOperationFactory[ArithmeticOperation] {
override def operations = Seq(OP_0NOTEQUAL, OP_1ADD, OP_1SUB, OP_ABS, OP_ADD, OP_BOOLAND, OP_BOOLOR,
OP_GREATERTHAN, OP_GREATERTHANOREQUAL, OP_LESSTHAN, OP_LESSTHANOREQUAL, OP_MAX, OP_MIN, OP_NEGATE,
OP_NEGATE, OP_NOT, OP_NUMEQUAL, OP_NUMEQUALVERIFY, OP_NUMNOTEQUAL, OP_SUB, OP_WITHIN,
OP_2MUL,OP_2DIV,OP_MUL,OP_DIV, OP_MOD, OP_LSHIFT, OP_RSHIFT)
OP_2MUL, OP_2DIV, OP_MUL, OP_DIV, OP_MOD, OP_LSHIFT, OP_RSHIFT)
}

View file

@ -1,10 +1,9 @@
package org.bitcoins.core.script.bitwise
import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.control.{ControlOperationsInterpreter, OP_VERIFY}
import org.bitcoins.core.script.control.{ ControlOperationsInterpreter, OP_VERIFY }
import org.bitcoins.core.script.result._
import org.bitcoins.core.script.{ExecutedScriptProgram, ExecutionInProgressScriptProgram, PreExecutionScriptProgram, ScriptProgram}
import org.bitcoins.core.script.{ ExecutedScriptProgram, ExecutionInProgressScriptProgram, PreExecutionScriptProgram, ScriptProgram }
import org.bitcoins.core.util.BitcoinSLogger
/**
@ -13,31 +12,31 @@ import org.bitcoins.core.util.BitcoinSLogger
sealed abstract class BitwiseInterpreter {
private def logger = BitcoinSLogger.logger
/** Returns 1 if the inputs are exactly equal, 0 otherwise. */
def opEqual(program : ScriptProgram) : ScriptProgram = {
def opEqual(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_EQUAL), "Script operation must be OP_EQUAL")
if (program.stack.size < 2) {
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
} else {
val h = program.stack.head
val h1 = program.stack.tail.head
val result = (h,h1) match {
case (OP_0,ScriptNumber.zero) | (ScriptNumber.zero, OP_0) =>
val result = (h, h1) match {
case (OP_0, ScriptNumber.zero) | (ScriptNumber.zero, OP_0) =>
OP_0.underlying == ScriptNumber.zero.toLong
case (OP_FALSE,ScriptNumber.zero) | (ScriptNumber.zero, OP_FALSE) =>
case (OP_FALSE, ScriptNumber.zero) | (ScriptNumber.zero, OP_FALSE) =>
OP_FALSE.underlying == ScriptNumber.zero.toLong
case (OP_TRUE,ScriptNumber.one) | (ScriptNumber.one, OP_TRUE) =>
case (OP_TRUE, ScriptNumber.one) | (ScriptNumber.one, OP_TRUE) =>
OP_TRUE.underlying == ScriptNumber.one.toLong
case (OP_1, ScriptNumber.one) | (ScriptNumber.one, OP_1) =>
OP_1.underlying == ScriptNumber.one.toLong
case _ => h.bytes == h1.bytes
}
val scriptBoolean = if (result) OP_TRUE else OP_FALSE
ScriptProgram(program,scriptBoolean :: program.stack.tail.tail, program.script.tail)
val scriptBoolean = if (result) OP_TRUE else OP_FALSE
ScriptProgram(program, scriptBoolean :: program.stack.tail.tail, program.script.tail)
}
}
/** Same as [[OP_EQUAL]], but runs [[OP_VERIFY]] afterward. */
def opEqualVerify(program : ScriptProgram) : ScriptProgram = {
def opEqualVerify(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_EQUALVERIFY), "Script operation must be OP_EQUALVERIFY")
if (program.stack.size > 1) {
//first replace OP_EQUALVERIFY with OP_EQUAL and OP_VERIFY
@ -50,9 +49,9 @@ sealed abstract class BitwiseInterpreter {
else p
case p: ExecutionInProgressScriptProgram => p
}
} else{
} else {
logger.error("OP_EQUALVERIFY requires at least 2 elements on the stack")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
}
}
}

View file

@ -8,34 +8,33 @@ import org.bitcoins.core.script.ScriptOperationFactory
*/
trait BytesToPushOntoStack extends ScriptOperation
object BytesToPushOntoStack extends ScriptOperationFactory[BytesToPushOntoStack] {
/**
* Represents that zero bytes need to be pushed onto the stack
* this really means we need to push an empty byte vector on the stack
*/
lazy val zero : BytesToPushOntoStack = apply(0)
* Represents that zero bytes need to be pushed onto the stack
* this really means we need to push an empty byte vector on the stack
*/
lazy val zero: BytesToPushOntoStack = apply(0)
private case class BytesToPushOntoStackImpl(num : Int) extends BytesToPushOntoStack {
private case class BytesToPushOntoStackImpl(num: Int) extends BytesToPushOntoStack {
/* //see the 'Constants; section in https://en.bitcoin.it/wiki/Script
require(num >= -1 && num <= 75, "A valid script number is between 1 and 75, the number passed in was: " + num)*/
require(num >= 0, "BytesToPushOntoStackImpl cannot be negative")
override def opCode = num
}
override def operations : Seq[BytesToPushOntoStack] =
override def operations: Seq[BytesToPushOntoStack] =
(for { i <- 0 to 75 } yield BytesToPushOntoStackImpl(i))
def fromNumber(num : Int) : BytesToPushOntoStack = {
def fromNumber(num: Int): BytesToPushOntoStack = {
if (num > 75) throw new IllegalArgumentException("We cannot have a BytesToPushOntoStack for greater than 75 bytes")
else {
val bytesToPushOntoStackOpt = operations.find(_.opCode == num)
bytesToPushOntoStackOpt match {
case Some(bytesToPushOntoStack) => bytesToPushOntoStack
case None => throw new IllegalArgumentException("We cannot have a BytesToPushOntoStack for greater than 75 bytes")
case None => throw new IllegalArgumentException("We cannot have a BytesToPushOntoStack for greater than 75 bytes")
}
}
}
def apply(num : Int) : BytesToPushOntoStack = fromNumber(num)
def apply(num: Int): BytesToPushOntoStack = fromNumber(num)
}

View file

@ -3,7 +3,7 @@ package org.bitcoins.core.script.constant
import org.bitcoins.core.script.ScriptProgram
import org.bitcoins.core.script.flag.ScriptFlagUtil
import org.bitcoins.core.script.result._
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil, BitcoinScriptUtil}
import org.bitcoins.core.util.{ BitcoinSLogger, BitcoinSUtil, BitcoinScriptUtil }
import scala.annotation.tailrec
@ -14,54 +14,54 @@ sealed abstract class ConstantInterpreter {
private def logger = BitcoinSLogger.logger
/** The next byte contains the number of bytes to be pushed onto the stack. */
def opPushData1(program : ScriptProgram) : ScriptProgram = {
def opPushData1(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_PUSHDATA1), "Top of script stack must be OP_PUSHDATA1")
opPushData(program)
}
/** The next two bytes contain the number of bytes to be pushed onto the stack. */
def opPushData2(program : ScriptProgram) : ScriptProgram = {
def opPushData2(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_PUSHDATA2), "Top of script stack must be OP_PUSHDATA2")
opPushData(program)
}
/** The next four bytes contain the number of bytes to be pushed onto the stack. */
def opPushData4(program : ScriptProgram) : ScriptProgram = {
def opPushData4(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_PUSHDATA4), "Top of script stack must be OP_PUSHDATA4")
opPushData(program)
}
/** Pushes the number of bytes onto the stack that is specified by script number on the script stack. */
def pushScriptNumberBytesToStack(program : ScriptProgram) : ScriptProgram = {
val bytesNeeded : Long = program.script.head match {
def pushScriptNumberBytesToStack(program: ScriptProgram): ScriptProgram = {
val bytesNeeded: Long = program.script.head match {
case OP_PUSHDATA1 | OP_PUSHDATA2 | OP_PUSHDATA4 =>
bytesNeededForPushOp(program.script(1))
case _ : ScriptToken => bytesNeededForPushOp(program.script.head)
case _: ScriptToken => bytesNeededForPushOp(program.script.head)
}
/** Parses the script tokens that need to be pushed onto our stack. */
@tailrec
def takeUntilBytesNeeded(scriptTokens : List[ScriptToken], accum : List[ScriptToken]) : (List[ScriptToken],List[ScriptToken]) = {
def takeUntilBytesNeeded(scriptTokens: List[ScriptToken], accum: List[ScriptToken]): (List[ScriptToken], List[ScriptToken]) = {
val bytesSum = accum.map(_.bytes.size).sum
if (bytesSum == bytesNeeded) (scriptTokens,accum)
else if (scriptTokens.isEmpty) (Nil,accum)
if (bytesSum == bytesNeeded) (scriptTokens, accum)
else if (scriptTokens.isEmpty) (Nil, accum)
else if (bytesSum > bytesNeeded) throw new RuntimeException("We cannot have more bytes than what our script number specified")
else {
//for the case when a ScriptNumberImpl(x) was parsed as a ByteToPushOntoStackImpl(x)
val scriptToken = scriptTokens.head match {
case x : BytesToPushOntoStack => ScriptNumber(x.opCode)
case x => x
case x: BytesToPushOntoStack => ScriptNumber(x.opCode)
case x => x
}
takeUntilBytesNeeded(scriptTokens.tail, scriptToken :: accum)
}
}
val (newScript,bytesToPushOntoStack) = program.script.head match {
val (newScript, bytesToPushOntoStack) = program.script.head match {
case OP_PUSHDATA1 | OP_PUSHDATA2 | OP_PUSHDATA4 => takeUntilBytesNeeded(program.script.tail.tail, Nil)
case _: ScriptToken => takeUntilBytesNeeded(program.script.tail, Nil)
case _: ScriptToken => takeUntilBytesNeeded(program.script.tail, Nil)
}
logger.debug("new script: " + newScript)
logger.debug("Bytes to push onto stack: " + bytesToPushOntoStack)
val constant : ScriptToken = if (bytesToPushOntoStack.size == 1) bytesToPushOntoStack.head
val constant: ScriptToken = if (bytesToPushOntoStack.size == 1) bytesToPushOntoStack.head
else ScriptConstant(BitcoinSUtil.flipEndianness(bytesToPushOntoStack.flatMap(_.bytes)))
logger.debug("Constant to be pushed onto stack: " + constant)
@ -71,36 +71,37 @@ sealed abstract class ConstantInterpreter {
else if (ScriptFlagUtil.requireMinimalData(program.flags) && bytesNeeded == 1 &&
constant.isInstanceOf[ScriptNumber] && constant.toLong <= 16) {
logger.error("We can push this constant onto the stack with OP_0 - OP_16 instead of using a script constant")
ScriptProgram(program,ScriptErrorMinimalData)
ScriptProgram(program, ScriptErrorMinimalData)
} else if (bytesNeeded != bytesToPushOntoStack.map(_.bytes.size).sum) {
logger.error("Incorrect amount of bytes being pushed onto the stack")
logger.error("Bytes needed: " + bytesNeeded)
logger.error("Number of byte received: " + bytesToPushOntoStack.map(_.bytes.size).sum)
ScriptProgram(program,ScriptErrorBadOpCode)
}
else if (ScriptFlagUtil.requireMinimalData(program.flags) && !BitcoinScriptUtil.isMinimalPush(program.script.head,constant)) {
ScriptProgram(program, ScriptErrorBadOpCode)
} else if (ScriptFlagUtil.requireMinimalData(program.flags) && !BitcoinScriptUtil.isMinimalPush(program.script.head, constant)) {
logger.debug("Pushing operation: " + program.script.head)
logger.debug("Constant parsed: " + constant)
logger.debug("Constant size: " + constant.bytes.size)
ScriptProgram(program,ScriptErrorMinimalData)
ScriptProgram(program, ScriptErrorMinimalData)
} else ScriptProgram.apply(program, constant :: program.stack, newScript)
}
/** Checks if the MINIMALDATA script flag is set, if so checks if we are using the minimal push operation
* if we are, then we push the bytes onto the stack. */
private def opPushData(program : ScriptProgram) : ScriptProgram = {
/**
* Checks if the MINIMALDATA script flag is set, if so checks if we are using the minimal push operation
* if we are, then we push the bytes onto the stack.
*/
private def opPushData(program: ScriptProgram): ScriptProgram = {
//for the case when we have the minimal data flag and the bytes to push onto stack is represented by the
//constant telling OP_PUSHDATA how many bytes need to go onto the stack
//for instance OP_PUSHDATA1 OP_0
val scriptNumOp = program.script(1).bytes match {
case h :: t => ScriptNumberOperation.fromNumber(h.toInt)
case Nil => None
case Nil => None
}
if (ScriptFlagUtil.requireMinimalData(program.flags) && program.script(1).bytes.size == 1 &&
scriptNumOp.isDefined) {
logger.error("We cannot use an OP_PUSHDATA operation for pushing " +
"a script number operation onto the stack, scriptNumberOperation: " + scriptNumOp)
ScriptProgram(program,ScriptErrorMinimalData)
ScriptProgram(program, ScriptErrorMinimalData)
} else if (ScriptFlagUtil.requireMinimalData(program.flags) && program.script.size > 2 &&
!BitcoinScriptUtil.isMinimalPush(program.script.head, program.script(2))) {
logger.error("We are not using the minimal push operation to push the bytes onto the stack for the constant")
@ -109,22 +110,22 @@ sealed abstract class ConstantInterpreter {
//for the case where we have to push 0 bytes onto the stack, which is technically the empty byte vector
program.script(1) match {
case OP_0 | BytesToPushOntoStack.zero | ScriptNumber.zero | ScriptNumber.negativeZero =>
if (ScriptFlagUtil.requireMinimalData(program.flags)) ScriptProgram(program,ScriptErrorMinimalData)
if (ScriptFlagUtil.requireMinimalData(program.flags)) ScriptProgram(program, ScriptErrorMinimalData)
else ScriptProgram(program, ScriptNumber.zero :: program.stack, program.script.tail.tail)
case _ : ScriptToken =>
case _: ScriptToken =>
pushScriptNumberBytesToStack(ScriptProgram(program, program.script, ScriptProgram.Script))
}
}
}
/** Parses the bytes needed for a push op (for instance OP_PUSHDATA1). */
private def bytesNeededForPushOp(token : ScriptToken) : Long = token match {
private def bytesNeededForPushOp(token: ScriptToken): Long = token match {
case scriptNumber: BytesToPushOntoStack => scriptNumber.opCode
case scriptNumOp: ScriptNumberOperation => scriptNumOp.opCode
case scriptNumber: ScriptNumber => scriptNumber.toLong
case scriptConstant : ScriptConstant =>
case scriptNumber: ScriptNumber => scriptNumber.toLong
case scriptConstant: ScriptConstant =>
val constantFlippedEndianness = BitcoinSUtil.flipEndianness(scriptConstant.hex)
java.lang.Long.parseLong(constantFlippedEndianness,16)
java.lang.Long.parseLong(constantFlippedEndianness, 16)
case _ => throw new IllegalArgumentException("Token must be BytesToPushOntoStack to push a number of bytes onto the stack")
}
}

View file

@ -2,9 +2,9 @@ package org.bitcoins.core.script.constant
import org.bitcoins.core.number.Int64
import org.bitcoins.core.script.ScriptOperationFactory
import org.bitcoins.core.util.{BitcoinSUtil, BitcoinScriptUtil, Factory}
import org.bitcoins.core.util.{ BitcoinSUtil, BitcoinScriptUtil, Factory }
import scala.util.{Failure, Success, Try}
import scala.util.{ Failure, Success, Try }
/**
* Created by chris on 1/6/16.
@ -16,100 +16,105 @@ import scala.util.{Failure, Success, Try}
*/
sealed trait ScriptToken {
/** The hexadecimal representation of this [[ScriptToken]]. */
def hex : String
def hex: String
/** The byte representation of this [[ScriptToken]]. */
def bytes : Seq[Byte] = BitcoinSUtil.decodeHex(hex)
def bytes: Seq[Byte] = BitcoinSUtil.decodeHex(hex)
/** The conversion from the byte representation of a [[ScriptToken]] to a number. */
def toLong = ScriptNumberUtil.toLong(hex)
}
/** A script operation is an instruction that takes an input and gives an output
* Think of these as functions.*/
/**
* A script operation is an instruction that takes an input and gives an output
* Think of these as functions.
*/
trait ScriptOperation extends ScriptToken {
def opCode : Int
override def hex : String = BitcoinSUtil.encodeHex(opCode.toByte)
def opCode: Int
override def hex: String = BitcoinSUtil.encodeHex(opCode.toByte)
}
/** A constant in the Script language for instance as String or a number. */
sealed abstract class ScriptConstant extends ScriptToken {
/** Returns if the [[ScriptConstant]] is encoded in the shortest possible way. */
def isShortestEncoding : Boolean = BitcoinScriptUtil.isShortestEncoding(this)
def isShortestEncoding: Boolean = BitcoinScriptUtil.isShortestEncoding(this)
}
/** Represents a [[ScriptNumber]] in the Script language. */
sealed abstract class ScriptNumber extends ScriptConstant {
/** The underlying number of the [[ScriptNumber]]. */
protected def underlying : Long
def +(that: ScriptNumber): ScriptNumber = ScriptNumber(underlying + that.underlying)
def + (that : ScriptNumber) : ScriptNumber = ScriptNumber(underlying + that.underlying)
def unary_- = ScriptNumber(-underlying)
def - (that : ScriptNumber) : ScriptNumber = ScriptNumber(underlying - that.underlying)
def * (that : ScriptNumber) : ScriptNumber = ScriptNumber(underlying * that.underlying)
def < (that : ScriptNumber) : Boolean = underlying < that.underlying
def <= (that : ScriptNumber) : Boolean = underlying <= that.underlying
def > (that : ScriptNumber) : Boolean = underlying > that.underlying
def >= (that : ScriptNumber) : Boolean = underlying >= that.underlying
def -(that: ScriptNumber): ScriptNumber = ScriptNumber(underlying - that.underlying)
def < (that : Int64) : Boolean = underlying < that.toLong
def <= (that : Int64) : Boolean = underlying <= that.toLong
def > (that : Int64) : Boolean = underlying > that.toLong
def >= (that : Int64) : Boolean = underlying >= that.toLong
def *(that: ScriptNumber): ScriptNumber = ScriptNumber(underlying * that.underlying)
def <(that: ScriptNumber): Boolean = underlying < that.underlying
def & (that : ScriptNumber) : ScriptNumber = ScriptNumber(underlying & that.underlying)
def & (that : Int64) : ScriptNumber = ScriptNumber(underlying & that.toLong)
def <=(that: ScriptNumber): Boolean = underlying <= that.underlying
def | (that : ScriptNumber) : ScriptNumber = ScriptNumber(underlying | that.underlying)
def >(that: ScriptNumber): Boolean = underlying > that.underlying
/** This equality just checks that the underlying scala numbers are equivalent, NOT if the numbers
def >=(that: ScriptNumber): Boolean = underlying >= that.underlying
def <(that: Int64): Boolean = underlying < that.toLong
def <=(that: Int64): Boolean = underlying <= that.toLong
def >(that: Int64): Boolean = underlying > that.toLong
def >=(that: Int64): Boolean = underlying >= that.toLong
def &(that: ScriptNumber): ScriptNumber = ScriptNumber(underlying & that.underlying)
def &(that: Int64): ScriptNumber = ScriptNumber(underlying & that.toLong)
def |(that: ScriptNumber): ScriptNumber = ScriptNumber(underlying | that.underlying)
/**
* This equality just checks that the underlying scala numbers are equivalent, NOT if the numbers
* are bitwise equivalent in Script. For instance ScriptNumber(0x01).numEqual(ScriptNumber(0x00000000001)) == true
* but (ScriptNumber(0x01) == (ScriptNumber(0x00000000001))) == false. */
def numEqual(that : ScriptNumber) : Boolean = underlying == that.underlying
override def toLong = underlying
* but (ScriptNumber(0x01) == (ScriptNumber(0x00000000001))) == false.
*/
def numEqual(that: ScriptNumber): Boolean = underlying == that.underlying
def toInt = {
val l = toLong
require(l <= Int.MaxValue && l >= Int.MinValue)
l.toInt
}
override def toLong = underlying
/** The underlying number of the [[ScriptNumber]]. */
protected def underlying: Long
}
object ScriptNumber extends Factory[ScriptNumber] {
/** This represents a [[ScriptNumber]] inside of bitcoin
*
* @param underlying the number being represented
* @param hex the hex representation of the number - this can be different than the obvious value for
* the number. For instance we could have padded the number with another word of zeros
*/
private case class ScriptNumberImpl(underlying : Long, override val hex : String) extends ScriptNumber
/** Represents the number zero inside of bitcoin's script language. */
lazy val zero : ScriptNumber = ScriptNumberImpl(0,"")
lazy val zero: ScriptNumber = ScriptNumberImpl(0, "")
/** Represents the number one inside of bitcoin's script language. */
lazy val one : ScriptNumber = ScriptNumberImpl(1)
lazy val one: ScriptNumber = ScriptNumberImpl(1)
/** Represents the number negative one inside of bitcoin's script language. */
lazy val negativeOne : ScriptNumber = ScriptNumberImpl(-1)
lazy val negativeOne: ScriptNumber = ScriptNumberImpl(-1)
/** Bitcoin has a numbering system which has a negative zero. */
lazy val negativeZero : ScriptNumber = fromHex("80")
lazy val negativeZero: ScriptNumber = fromHex("80")
def fromBytes(bytes : Seq[Byte]) = {
def fromBytes(bytes: Seq[Byte]) = {
if (bytes.isEmpty) zero
else ScriptNumberImpl(ScriptNumberUtil.toLong(bytes), BitcoinSUtil.encodeHex(bytes))
}
def apply(underlying : Long) : ScriptNumber = {
def apply(underlying: Long): ScriptNumber = {
if (underlying == 0) zero else apply(ScriptNumberUtil.longToHex(underlying))
}
def apply(hex : String, requireMinimal : Boolean) : Try[ScriptNumber] = {
def apply(bytes: Seq[Byte], requireMinimal: Boolean): Try[ScriptNumber] = apply(BitcoinSUtil.encodeHex(bytes), requireMinimal)
def apply(hex: String, requireMinimal: Boolean): Try[ScriptNumber] = {
if (requireMinimal && !BitcoinScriptUtil.isShortestEncoding(hex)) {
Failure(new IllegalArgumentException("The given hex was not the shortest encoding for the script number: " + hex))
} else {
@ -118,16 +123,29 @@ object ScriptNumber extends Factory[ScriptNumber] {
}
}
def apply(bytes : Seq[Byte], requireMinimal : Boolean) : Try[ScriptNumber] = apply(BitcoinSUtil.encodeHex(bytes),requireMinimal)
/**
* This represents a [[ScriptNumber]] inside of bitcoin
*
* @param underlying the number being represented
* @param hex the hex representation of the number - this can be different than the obvious value for
* the number. For instance we could have padded the number with another word of zeros
*/
private case class ScriptNumberImpl(underlying: Long, override val hex: String) extends ScriptNumber
/** Companion object for [[ScriptNumberImpl]] that gives us access to more constructor types for the
* [[ScriptNumberImpl]] case class. */
/**
* Companion object for [[ScriptNumberImpl]] that gives us access to more constructor types for the
* [[ScriptNumberImpl]] case class.
*/
private object ScriptNumberImpl {
def apply(underlying : Long) : ScriptNumber = ScriptNumberImpl(underlying, ScriptNumberUtil.longToHex(underlying))
def apply(hex : String) : ScriptNumber = ScriptNumberImpl(ScriptNumberUtil.toLong(hex), hex)
def apply(bytes : Seq[Byte]) : ScriptNumber = ScriptNumberImpl(ScriptNumberUtil.toLong(bytes))
def apply(int64: Int64) : ScriptNumber = ScriptNumberImpl(int64.toLong)
def apply(hex: String): ScriptNumber = ScriptNumberImpl(ScriptNumberUtil.toLong(hex), hex)
def apply(bytes: Seq[Byte]): ScriptNumber = ScriptNumberImpl(ScriptNumberUtil.toLong(bytes))
def apply(underlying: Long): ScriptNumber = ScriptNumberImpl(underlying, ScriptNumberUtil.longToHex(underlying))
def apply(int64: Int64): ScriptNumber = ScriptNumberImpl(int64.toLong)
}
}
/** The next byte contains the number of bytes to be pushed onto the stack. */
@ -154,8 +172,10 @@ case object OP_PUSHDATA4 extends ScriptOperation {
def max = 4294967295L
}
/** Represents a [[ScriptNumberOperation]] where the the number in the operation is pushed onto the stack
* i.e. OP_0 would be push 0 onto the stack, OP_1 would be push 1 onto the stack. */
/**
* Represents a [[ScriptNumberOperation]] where the the number in the operation is pushed onto the stack
* i.e. OP_0 would be push 0 onto the stack, OP_1 would be push 1 onto the stack.
*/
sealed abstract class ScriptNumberOperation extends ScriptNumber with ScriptOperation {
override def hex = opCode.toHexString
}
@ -163,143 +183,170 @@ sealed abstract class ScriptNumberOperation extends ScriptNumber with ScriptOper
/** An empty array of bytes is pushed onto the stack. (This is not a no-op: an item is added to the stack.) */
case object OP_0 extends ScriptNumberOperation {
override def opCode = 0
override def hex = "00"
override def underlying = 0
}
/** An empty array of bytes is pushed onto the stack. (This is not a no-op: an item is added to the stack.) */
case object OP_FALSE extends ScriptNumberOperation {
override def opCode = OP_0.opCode
override def hex = OP_0.hex
override def underlying = OP_0.underlying
override def bytes = OP_0.bytes
}
/** The number 1 is pushed onto the stack. */
case object OP_TRUE extends ScriptNumberOperation {
override def opCode = 81
override def underlying = 1
}
/** The number -1 is pushed onto the stack. */
case object OP_1NEGATE extends ScriptNumberOperation {
override def opCode = 79
override def underlying = -1
}
/** The number 1 is pushed onto the stack. */
case object OP_1 extends ScriptNumberOperation {
override def opCode = OP_TRUE.opCode
override def underlying = OP_TRUE.underlying
}
/** The number 2 is pushed onto the stack. */
case object OP_2 extends ScriptNumberOperation {
override def opCode = 82
override def underlying = 2
}
/** The number 3 is pushed onto the stack. */
case object OP_3 extends ScriptNumberOperation {
override def opCode = 83
override def underlying = 3
}
/** The number 4 is pushed onto the stack. */
case object OP_4 extends ScriptNumberOperation {
override def opCode = 84
override def underlying = 4
}
/** The number 5 is pushed onto the stack. */
case object OP_5 extends ScriptNumberOperation {
override def opCode = 85
override def underlying = 5
}
/** The number 6 is pushed onto the stack. */
case object OP_6 extends ScriptNumberOperation {
override def opCode = 86
override def underlying = 6
}
/** The number 7 is pushed onto the stack. */
case object OP_7 extends ScriptNumberOperation {
override def opCode = 87
override def underlying = 7
}
/** The number 8 is pushed onto the stack. */
case object OP_8 extends ScriptNumberOperation {
override def opCode = 88
override def underlying = 8
}
/** The number 9 is pushed onto the stack. */
case object OP_9 extends ScriptNumberOperation {
override def opCode = 89
override def underlying = 9
}
/** The number 10 is pushed onto the stack. */
case object OP_10 extends ScriptNumberOperation {
override def opCode = 90
override def underlying = 10
}
/** The number 11 is pushed onto the stack. */
case object OP_11 extends ScriptNumberOperation {
override def opCode = 91
override def underlying = 11
}
/** The number 12 is pushed onto the stack. */
case object OP_12 extends ScriptNumberOperation {
override def opCode = 92
override def underlying = 12
}
/** The number 13 is pushed onto the stack. */
case object OP_13 extends ScriptNumberOperation {
override def opCode = 93
override def underlying = 13
}
/** The number 14 is pushed onto the stack. */
case object OP_14 extends ScriptNumberOperation {
override def opCode = 94
override def underlying = 14
}
/** The number 15 is pushed onto the stack. */
case object OP_15 extends ScriptNumberOperation {
override def opCode = 95
override def underlying = 15
}
/** The number 16 is pushed onto the stack. */
case object OP_16 extends ScriptNumberOperation {
override def opCode = 96
override def underlying = 16
}
object ScriptNumberOperation extends ScriptOperationFactory[ScriptNumberOperation] {
def operations = Seq(OP_0,OP_1, OP_1NEGATE, OP_2,OP_3,OP_4,OP_5,OP_6,OP_7,OP_8,OP_9,OP_10,OP_11,OP_12,OP_13,OP_14,OP_15,OP_16)
/** Finds the [[ScriptNumberOperation]] based on the given integer. */
def fromNumber(underlying : Long) : Option[ScriptNumberOperation] = operations.find(_.underlying == underlying)
def fromNumber(underlying: Long): Option[ScriptNumberOperation] = operations.find(_.underlying == underlying)
def operations = Seq(OP_0, OP_1, OP_1NEGATE, OP_2, OP_3, OP_4, OP_5, OP_6, OP_7, OP_8, OP_9, OP_10, OP_11, OP_12, OP_13, OP_14, OP_15, OP_16)
}
object ScriptConstant extends Factory[ScriptConstant] {
/** Represent a public key or hash of a public key on our stack.*/
private case class ScriptConstantImpl(hex : String) extends ScriptConstant {
def this(bytes : List[Byte]) = this(BitcoinSUtil.encodeHex(bytes))
}
lazy val zero = ScriptConstant("00")
lazy val negativeZero = ScriptConstant("80")
lazy val negativeOne = ScriptConstant("81")
/** Creates a [[ScriptConstant]] from a sequence of bytes. */
def fromBytes(bytes : Seq[Byte]) : ScriptConstant = ScriptConstantImpl(BitcoinSUtil.encodeHex(bytes))
def fromBytes(bytes: Seq[Byte]): ScriptConstant = ScriptConstantImpl(BitcoinSUtil.encodeHex(bytes))
/** Represent a public key or hash of a public key on our stack. */
private case class ScriptConstantImpl(hex: String) extends ScriptConstant {
def this(bytes: List[Byte]) = this(BitcoinSUtil.encodeHex(bytes))
}
}

View file

@ -3,78 +3,118 @@ package org.bitcoins.core.script.constant
import org.bitcoins.core.util.BitcoinSUtil
/**
* Created by chris on 6/5/16.
* Numbers in script are unique in the fact that they don't follow a conventional signed numbering system
* such as ones complement or twos complement. The bitcoin protocol uses little endian notation which means the most
* significant bit indicates the sign on the number we are interpreting. The rest of the bits are used to determine
* what that number is. See this irc log for more info
* https://botbot.me/freenode/bitcoin-core-dev/2016-06-06/?tz=America/Chicago
*/
* Created by chris on 6/5/16.
* Numbers in script are unique in the fact that they don't follow a conventional signed numbering system
* such as ones complement or twos complement. The bitcoin protocol uses little endian notation which means the most
* significant bit indicates the sign on the number we are interpreting. The rest of the bits are used to determine
* what that number is. See this irc log for more info
* https://botbot.me/freenode/bitcoin-core-dev/2016-06-06/?tz=America/Chicago
*/
trait ScriptNumberUtil {
/**
* Takes a hex number and converts it into a signed number
* used in the bitcoin script's numbering system.
* This function interprets the bytes as little endian numbers
* This should only be used for numbers inside of Script
* @param hex
* @return
*/
def toLong(hex : String) : Long = toLong(BitcoinSUtil.decodeHex(hex))
* Takes a hex number and converts it into a signed number
* used in the bitcoin script's numbering system.
* This function interprets the bytes as little endian numbers
* This should only be used for numbers inside of Script
*
* @param hex
* @return
*/
def toLong(hex: String): Long = toLong(BitcoinSUtil.decodeHex(hex))
/**
* Takes a sequence of bytes and converts it in to signed number inside of bitcoin
* script's numbering system
* This function interprets the bytes as little endian numbers
* This should only be used for numbers inside of Script
* @param bytes
* @return
*/
def toLong(bytes : Seq[Byte]) : Long = {
* Takes in a hex string and converts it into a signed number
* This function interprets the bytes as little endian numbers
* This should only be used for numbers inside of Script
*
* @param hex
* @return
*/
def toInt(hex: String): Int = toInt(BitcoinSUtil.decodeHex(hex))
/**
* Takes in a sequence of bytes and converts it into a signed number
* This should only be used for numbers inside of Script
*
* @param bytes
* @return
*/
def toInt(bytes: Seq[Byte]): Int = {
require(bytes.size <= 4, "We cannot have an integer with more than 4 bytes (32 bits)")
toLong(bytes).toInt
}
/**
* Takes a sequence of bytes and converts it in to signed number inside of bitcoin
* script's numbering system
* This function interprets the bytes as little endian numbers
* This should only be used for numbers inside of Script
*
* @param bytes
* @return
*/
def toLong(bytes: Seq[Byte]): Long = {
val reversedBytes = bytes.reverse
if (bytes.size == 1 && bytes.head == -128) {
//the case for negative zero
0
} else if (isPositive(bytes)) {
if (firstByteAllZeros(reversedBytes.toList) && reversedBytes.size > 1) {
parseLong(reversedBytes.slice(1,reversedBytes.size))
parseLong(reversedBytes.slice(1, reversedBytes.size))
} else parseLong(reversedBytes)
} else {
//remove the sign bit
val removedSignBit = changeSignBitToPositive(reversedBytes.toList)
if (firstByteAllZeros(removedSignBit)) -parseLong(removedSignBit.slice(1,removedSignBit.size))
if (firstByteAllZeros(removedSignBit)) -parseLong(removedSignBit.slice(1, removedSignBit.size))
else -parseLong(removedSignBit)
}
}
/**
* Takes in a sequence of bytes and converts it into a signed number
* This should only be used for numbers inside of Script
* @param bytes
* @return
*/
def toInt(bytes : Seq[Byte]) : Int = {
require(bytes.size <= 4, "We cannot have an integer with more than 4 bytes (32 bits)")
toLong(bytes).toInt
* Determines if a byte array is a positive or negative number
*
* @param bytes
* @return
*/
def isPositive(bytes: Seq[Byte]): Boolean = {
if (bytes.isEmpty) false
else {
val result: Int = bytes(bytes.size - 1) & 0x80
if (result == 0x80) false else true
}
}
/**
* Takes in a hex string and converts it into a signed number
* This function interprets the bytes as little endian numbers
* This should only be used for numbers inside of Script
* @param hex
* @return
*/
def toInt(hex : String) : Int = toInt(BitcoinSUtil.decodeHex(hex))
* Change sign bit to positive
*
* @param bytes
* @return
*/
def changeSignBitToPositive(bytes: Seq[Byte]): Seq[Byte] = {
val newByte: Byte = (bytes.head & 0x7F).toByte
(newByte :: bytes.tail.toList)
}
def firstByteAllZeros(bytes: Seq[Byte]): Boolean = {
val lastByte = bytes.head
(lastByte & 0xFF) == 0
}
private def parseLong(bytes: Seq[Byte]): Long = parseLong(bytes.toList)
private def parseLong(bytes: List[Byte]): Long = parseLong(BitcoinSUtil.encodeHex(bytes))
private def parseLong(hex: String): Long = java.lang.Long.parseLong(hex, 16)
/**
* Converts a long number to the representation of number inside of Bitcoin script's number system
* @param long
* @return
*/
def longToHex(long : Long) : String = {
* Converts a long number to the representation of number inside of Bitcoin script's number system
*
* @param long
* @return
*/
def longToHex(long: Long): String = {
if (long > -1) {
val bytes = toByteSeq(long)
BitcoinSUtil.flipEndianness(BitcoinSUtil.encodeHex(bytes))
@ -87,73 +127,34 @@ trait ScriptNumberUtil {
}
}
/**
* Determines if a given hex string is a positive number
* @param hex
* @return
*/
def isPositive(hex : String) : Boolean = isPositive(BitcoinSUtil.decodeHex(hex))
def toByteSeq(long: Long): Seq[Byte] = BigInt(long).toByteArray
/**
* Determines if a byte array is a positive or negative number
* @param bytes
* @return
*/
def isPositive(bytes : Seq[Byte]) : Boolean = {
if (bytes.isEmpty) false
else {
val result: Int = bytes(bytes.size-1) & 0x80
if (result == 0x80) false else true
}
* Determines if a given hex string is a positive number
*
* @param hex
* @return
*/
def isPositive(hex: String): Boolean = isPositive(BitcoinSUtil.decodeHex(hex))
}
def isNegative(hex: String): Boolean = isNegative(BitcoinSUtil.decodeHex(hex))
def isNegative(hex : String) : Boolean = isNegative(BitcoinSUtil.decodeHex(hex))
def isNegative(bytes : Seq[Byte]) : Boolean = {
def isNegative(bytes: Seq[Byte]): Boolean = {
if (bytes.isEmpty) false else !isPositive(bytes)
}
/**
* Change sign bit to positive
* @param bytes
* @return
*/
def changeSignBitToPositive(bytes : Seq[Byte]) : Seq[Byte] = {
val newByte : Byte = (bytes.head & 0x7F).toByte
(newByte :: bytes.tail.toList)
}
def changeSignBitToPositive(hex: String): Seq[Byte] = changeSignBitToPositive(BitcoinSUtil.decodeHex(hex))
def changeSignBitToPositive(hex : String) : Seq[Byte] = changeSignBitToPositive(BitcoinSUtil.decodeHex(hex))
def changeSignBitToNegative(hex: String): Seq[Byte] = changeSignBitToNegative(BitcoinSUtil.decodeHex(hex))
def changeSignBitToNegative(bytes : Seq[Byte]) : Seq[Byte] = {
def changeSignBitToNegative(bytes: Seq[Byte]): Seq[Byte] = {
val newByte = (bytes.head | 0x80).toByte
(newByte :: bytes.tail.toList)
}
def changeSignBitToNegative(hex : String) : Seq[Byte] = changeSignBitToNegative(BitcoinSUtil.decodeHex(hex))
def firstByteAllZeros(hex: String): Boolean = firstByteAllZeros(BitcoinSUtil.decodeHex(hex))
def firstByteAllZeros(hex : String) : Boolean = firstByteAllZeros(BitcoinSUtil.decodeHex(hex))
def firstByteAllZeros(bytes : Seq[Byte]) : Boolean = {
val lastByte = bytes.head
(lastByte & 0xFF) == 0
}
def toByteSeq(long : Long) : Seq[Byte] = BigInt(long).toByteArray
private def parseLong(hex : String) : Long = java.lang.Long.parseLong(hex,16)
private def parseLong(bytes : List[Byte]) : Long = parseLong(BitcoinSUtil.encodeHex(bytes))
private def parseLong(byte : Byte) : Long = parseLong(List(byte))
private def parseLong(bytes : Seq[Byte]) : Long = parseLong(bytes.toList)
private def parseLong(byte: Byte): Long = parseLong(List(byte))
}

View file

@ -1,28 +1,28 @@
package org.bitcoins.core.script.constant
/**
* Created by chris on 3/28/16.
*/
trait StackPushOperationFactory {
/**
* Determines if the given token is a stack push operation
*
* @param token the token to be checked to see if it is a stack push operation
* @return a boolean indicating if the given token was a stack push operation
*/
def isPushOperation(token: ScriptToken): Boolean = operations.contains(token)
/**
* Gives back all of the script operations that can push data onto the stack
* The operations are determined according to BIP62
* https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#push-operators
*
* @return
*/
def operations = Seq(OP_PUSHDATA1, OP_PUSHDATA2, OP_PUSHDATA4) ++ BytesToPushOntoStack.operations ++
Seq(OP_0,OP_1,OP_1NEGATE, OP_2,OP_3,OP_4,OP_5,OP_6,OP_7,OP_8,
OP_9,OP_10,OP_11,OP_12,OP_13,OP_14,OP_15,OP_16,OP_FALSE,OP_TRUE)
/**
* Determines if the given token is a stack push operation
* @param token the token to be checked to see if it is a stack push operation
* @return a boolean indiciating if the given token was a stack push operation
*/
def isPushOperation(token : ScriptToken) : Boolean = operations.contains(token)
private def operations = Seq(OP_PUSHDATA1, OP_PUSHDATA2, OP_PUSHDATA4) ++ BytesToPushOntoStack.operations ++
Seq(OP_0, OP_1, OP_1NEGATE, OP_2, OP_3, OP_4, OP_5, OP_6, OP_7, OP_8,
OP_9, OP_10, OP_11, OP_12, OP_13, OP_14, OP_15, OP_16, OP_FALSE, OP_TRUE)
}

View file

@ -8,29 +8,30 @@ import org.bitcoins.core.script.constant.ScriptOperation
*/
sealed trait ControlOperations extends ScriptOperation
/** If the top stack value is not 0, the statements are executed. The top stack value is removed. */
case object OP_IF extends ControlOperations {
override def opCode = 99
}
/** If the top stack value is 0, the statements are executed. The top stack value is removed. */
case object OP_NOTIF extends ControlOperations {
override def opCode = 100
}
/** If the preceding OP_IF or OP_NOTIF or OP_ELSE was not executed then these statements are and
* if the preceding OP_IF or OP_NOTIF or OP_ELSE was executed then these statements are not. */
/**
* If the preceding OP_IF or OP_NOTIF or OP_ELSE was not executed then these statements are and
* if the preceding OP_IF or OP_NOTIF or OP_ELSE was executed then these statements are not.
*/
case object OP_ELSE extends ControlOperations {
override def opCode = 103
}
/** Ends an if/else block. All blocks must end, or the transaction is invalid.
* An OP_ENDIF without OP_IF earlier is also invalid. */
/**
* Ends an if/else block. All blocks must end, or the transaction is invalid.
* An OP_ENDIF without OP_IF earlier is also invalid.
*/
case object OP_ENDIF extends ControlOperations {
override def opCode = 104
override def opCode = 104
}
/** Marks transaction as invalid if top stack value is not true. */

View file

@ -1,6 +1,6 @@
package org.bitcoins.core.script.control
import org.bitcoins.core.protocol.script.{SigVersionWitnessV0, SignatureVersion}
import org.bitcoins.core.protocol.script.{ SigVersionWitnessV0, SignatureVersion }
import org.bitcoins.core.script.ScriptProgram
import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.flag.ScriptFlagUtil
@ -15,7 +15,7 @@ import scala.annotation.tailrec
sealed abstract class ControlOperationsInterpreter {
private def logger = BitcoinSLogger.logger
/** If the top stack value is not 0, the statements are executed. The top stack value is removed. */
def opIf(program : ScriptProgram) : ScriptProgram = {
def opIf(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_IF), "Script top was not OP_IF")
val sigVersion = program.txSignatureComponent.sigVersion
val flags = program.flags
@ -25,11 +25,11 @@ sealed abstract class ControlOperationsInterpreter {
logger.debug("Parsed binary tree: " + binaryTree)
if (!checkMatchingOpIfOpNotIfOpEndIf(program.originalScript)) {
logger.error("We do not have a matching OP_ENDIF for every OP_IF we have")
ScriptProgram(program,ScriptErrorUnbalancedConditional)
ScriptProgram(program, ScriptErrorUnbalancedConditional)
} else if (program.stack.isEmpty) {
logger.error("We do not have any stack elements for our OP_IF")
ScriptProgram(program,ScriptErrorUnbalancedConditional)
} else if (isNotMinimalStackTop(stackTop,sigVersion,minimalIfEnabled)) {
ScriptProgram(program, ScriptErrorUnbalancedConditional)
} else if (isNotMinimalStackTop(stackTop, sigVersion, minimalIfEnabled)) {
logger.error("OP_IF argument was not minimally encoded, got: " + stackTop)
ScriptProgram(program, ScriptErrorMinimalIf)
} else if (program.stackTopIsTrue) {
@ -41,12 +41,12 @@ sealed abstract class ControlOperationsInterpreter {
val newScript = newTreeWithoutOpElse.toList
logger.debug("New script after removing OP_ELSE branch " + newScript.tail)
logger.debug("New stack after removing OP_ELSE branch: " + program.stack.tail)
ScriptProgram(program, program.stack.tail,newScript.tail)
} else {
ScriptProgram(program, program.stack.tail, newScript.tail)
} else {
logger.debug("OP_IF stack top was false")
//remove the OP_IF
val scriptWithoutOpIf : BinaryTree[ScriptToken] = removeFirstOpIf(binaryTree)
ScriptProgram(program, program.stack.tail,scriptWithoutOpIf.toList)
val scriptWithoutOpIf: BinaryTree[ScriptToken] = removeFirstOpIf(binaryTree)
ScriptProgram(program, program.stack.tail, scriptWithoutOpIf.toList)
}
}
/** Checks if the stack top is NOT minimially encoded */
@ -57,126 +57,130 @@ sealed abstract class ControlOperationsInterpreter {
val isNotMinimal = stackTopOpt.map { stackTop =>
(sigVersion == SigVersionWitnessV0 && minimalIfEnabled
&& (stackTop.bytes.size > 1 ||
(stackTop.bytes.size == 1 && stackTop.bytes.head != 1)))
(stackTop.bytes.size == 1 && stackTop.bytes.head != 1)))
}
isNotMinimal.isDefined && isNotMinimal.get
}
/** If the top stack value is 0, the statements are executed. The top stack value is removed. */
def opNotIf(program : ScriptProgram) : ScriptProgram = {
def opNotIf(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_NOTIF), "Script top was not OP_NOTIF")
val minimalIfEnabled = ScriptFlagUtil.minimalIfEnabled(program.flags)
val sigVersion = program.txSignatureComponent.sigVersion
val oldStackTop = program.stack.headOption
if (isNotMinimalStackTop(oldStackTop,sigVersion,minimalIfEnabled)) {
if (isNotMinimalStackTop(oldStackTop, sigVersion, minimalIfEnabled)) {
//need to duplicate minimal check, we cannot accurately invert the stack
//top for OP_IF otherwise
ScriptProgram(program,ScriptErrorMinimalIf)
ScriptProgram(program, ScriptErrorMinimalIf)
} else {
val script = OP_IF :: program.script.tail
val stackTop = if (program.stackTopIsTrue) ScriptNumber.zero else ScriptNumber.one
val stack = if (program.stack.nonEmpty) stackTop :: program.stack.tail else Nil
val newProgram = ScriptProgram(program,stack,script)
val newProgram = ScriptProgram(program, stack, script)
opIf(newProgram)
}
}
/** Evaluates the [[OP_ELSE]] operator. */
def opElse(program : ScriptProgram) : ScriptProgram = {
def opElse(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_ELSE), "First script opt must be OP_ELSE")
if (!program.script.tail.contains(OP_ENDIF)) {
logger.error("OP_ELSE does not have a OP_ENDIF")
ScriptProgram(program,ScriptErrorUnbalancedConditional)
ScriptProgram(program, ScriptErrorUnbalancedConditional)
} else {
val tree = parseBinaryTree(program.script)
val treeWithNextOpElseRemoved = tree match {
case Empty => Empty
case leaf : Leaf[ScriptToken] => leaf
case node : Node[ScriptToken] =>
case Empty => Empty
case leaf: Leaf[ScriptToken] => leaf
case node: Node[ScriptToken] =>
removeFirstOpElse(node)
}
ScriptProgram(program, program.stack,treeWithNextOpElseRemoved.toList.tail)
ScriptProgram(program, program.stack, treeWithNextOpElseRemoved.toList.tail)
}
}
/** Evaluates an [[OP_ENDIF]] operator. */
def opEndIf(program : ScriptProgram) : ScriptProgram = {
def opEndIf(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_ENDIF), "Script top must be OP_ENDIF")
if (!checkMatchingOpIfOpNotIfOpEndIf(program.originalScript)) {
//means we do not have a matching OP_IF for our OP_ENDIF
logger.error("We do not have a matching OP_IF/OP_NOTIF for every OP_ENDIF we have")
ScriptProgram(program,ScriptErrorUnbalancedConditional)
} else ScriptProgram(program, program.stack,program.script.tail)
ScriptProgram(program, ScriptErrorUnbalancedConditional)
} else ScriptProgram(program, program.stack, program.script.tail)
}
/** Marks transaction as invalid. A standard way of attaching extra data to transactions is to add a zero-value output
/**
* Marks transaction as invalid. A standard way of attaching extra data to transactions is to add a zero-value output
* with a [[org.bitcoins.core.protocol.script.ScriptPubKey]] consisting of [[OP_RETURN]] followed by exactly one pushdata op. Such outputs are provably unspendable,
* reducing their cost to the network. Currently it is usually considered non-standard (though valid) for a transaction to
* have more than one OP_RETURN output or an OP_RETURN output with more than one pushdata op. */
def opReturn(program : ScriptProgram) : ScriptProgram = {
* have more than one OP_RETURN output or an OP_RETURN output with more than one pushdata op.
*/
def opReturn(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_RETURN))
ScriptProgram(program,ScriptErrorOpReturn)
ScriptProgram(program, ScriptErrorOpReturn)
}
/** Marks [[org.bitcoins.core.protocol.transaction.Transaction]] as invalid if top stack value is not true. */
def opVerify(program : ScriptProgram) : ScriptProgram = {
def opVerify(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_VERIFY), "Script top must be OP_VERIFY")
program.stack.nonEmpty match {
case true =>
logger.debug("Stack for OP_VERIFY: " + program.stack)
if (program.stackTopIsFalse) ScriptProgram(program,ScriptErrorVerify)
else ScriptProgram(program, program.stack.tail,program.script.tail)
if (program.stackTopIsFalse) ScriptProgram(program, ScriptErrorVerify)
else ScriptProgram(program, program.stack.tail, program.script.tail)
case false =>
logger.error("OP_VERIFY requires an element to be on the stack")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
}
}
/** Parses a list of [[ScriptToken]]s into its corresponding [[BinaryTree]] */
def parseBinaryTree(script : List[ScriptToken]): BinaryTree[ScriptToken] = {
def parseBinaryTree(script: List[ScriptToken]): BinaryTree[ScriptToken] = {
//@tailrec
def loop(remaining: List[ScriptToken], parentTree: BinaryTree[ScriptToken]): (BinaryTree[ScriptToken], List[ScriptToken]) = {
if (remaining.isEmpty) (parentTree,Nil)
if (remaining.isEmpty) (parentTree, Nil)
else {
if (parentTree.right.isDefined && parentTree.right.get.value == Some(OP_ELSE)) {
//for the case of OP_IF OP_1 OP_ELSE OP_2 OP_ELSE OP_3 ... OP_ELSE OP_N OP_ENDIF
val (elseTree,newRemaining) = loop(remaining, parentTree.right.getOrElse(Empty))
(Node(parentTree.value.get, parentTree.left.getOrElse(Empty), elseTree),newRemaining)
val (elseTree, newRemaining) = loop(remaining, parentTree.right.getOrElse(Empty))
(Node(parentTree.value.get, parentTree.left.getOrElse(Empty), elseTree), newRemaining)
} else {
val (tree, newRemaining) = parse(remaining,parentTree)
loop(newRemaining,tree)
val (tree, newRemaining) = parse(remaining, parentTree)
loop(newRemaining, tree)
}
}
}
val (t, remaining) = loop(script,Empty)
val (t, remaining) = loop(script, Empty)
require(remaining.isEmpty, "Should not have any script tokens after parsing a binary tree, got: " + remaining)
t
}
/** The loop that parses a list of [[ScriptToken]]s into a [[BinaryTree]]. */
private def parse(script : List[ScriptToken],
tree : BinaryTree[ScriptToken]): (BinaryTree[ScriptToken], List[ScriptToken]) = script match {
private def parse(
script: List[ScriptToken],
tree: BinaryTree[ScriptToken]
): (BinaryTree[ScriptToken], List[ScriptToken]) = script match {
case OP_ENDIF :: t =>
val ifTree = insertSubTree(tree,Leaf(OP_ENDIF))
(ifTree,t)
val ifTree = insertSubTree(tree, Leaf(OP_ENDIF))
(ifTree, t)
case h :: t if (h == OP_IF || h == OP_NOTIF) =>
val (ifTree,remaining) = parse(t, Leaf(h))
val fullTree = insertSubTree(tree,ifTree)
(fullTree,remaining)
val (ifTree, remaining) = parse(t, Leaf(h))
val fullTree = insertSubTree(tree, ifTree)
(fullTree, remaining)
case h :: t if h == OP_ELSE =>
val (subTree,remaining) = parse(t,Node(OP_ELSE,Empty,Empty))
val (subTree, remaining) = parse(t, Node(OP_ELSE, Empty, Empty))
val opElseTree = tree match {
case Empty => subTree
case l: Leaf[ScriptToken] => Node(l.v,Empty,subTree)
case n: Node[ScriptToken] => Node(n.v,n.l,insertSubTree(n.r,subTree))
case Empty => subTree
case l: Leaf[ScriptToken] => Node(l.v, Empty, subTree)
case n: Node[ScriptToken] => Node(n.v, n.l, insertSubTree(n.r, subTree))
}
(opElseTree,remaining)
case h :: t => parse(t,insertSubTree(tree,Leaf(h)))
(opElseTree, remaining)
case h :: t => parse(t, insertSubTree(tree, Leaf(h)))
case Nil =>
logger.debug("Done parsing tree, got: " + tree)
(tree,Nil)
logger.debug("Done parsing tree, got: " + tree)
(tree, Nil)
}
/**
@ -186,109 +190,110 @@ sealed abstract class ControlOperationsInterpreter {
* @return the full parse tree combined
*/
//@tailrec
private def insertSubTree(tree: BinaryTree[ScriptToken],
subTree: BinaryTree[ScriptToken]): BinaryTree[ScriptToken] = tree match {
private def insertSubTree(
tree: BinaryTree[ScriptToken],
subTree: BinaryTree[ScriptToken]
): BinaryTree[ScriptToken] = tree match {
case Empty => subTree
case leaf: Leaf[ScriptToken] =>
if (subTree == Empty) leaf
else if (subTree == Leaf(OP_ENDIF)) Node(leaf.v,Empty,subTree)
else Node(leaf.v,subTree,Empty)
case node : Node[ScriptToken] if (node.v == OP_IF || node.v == OP_NOTIF || node.v == OP_ELSE || node.v == OP_ENDIF) =>
if (subTree.value.isDefined && Seq(OP_ELSE,OP_ENDIF).contains(subTree.value.get)) {
Node(node.v,node.l,insertSubTree(node.r,subTree))
} else if (node.r != Empty && Seq(OP_ELSE,OP_ENDIF).contains(node.r.value.get)) {
Node(node.v,node.l,insertSubTree(node.r,subTree))
else if (subTree == Leaf(OP_ENDIF)) Node(leaf.v, Empty, subTree)
else Node(leaf.v, subTree, Empty)
case node: Node[ScriptToken] if (node.v == OP_IF || node.v == OP_NOTIF || node.v == OP_ELSE || node.v == OP_ENDIF) =>
if (subTree.value.isDefined && Seq(OP_ELSE, OP_ENDIF).contains(subTree.value.get)) {
Node(node.v, node.l, insertSubTree(node.r, subTree))
} else if (node.r != Empty && Seq(OP_ELSE, OP_ENDIF).contains(node.r.value.get)) {
Node(node.v, node.l, insertSubTree(node.r, subTree))
} else {
Node(node.v,insertSubTree(node.l,subTree),node.r)
Node(node.v, insertSubTree(node.l, subTree), node.r)
}
case node: Node[ScriptToken] =>
Node(node.v,insertSubTree(node.l,subTree), node.r)
Node(node.v, insertSubTree(node.l, subTree), node.r)
}
/** Checks if an [[OP_IF]]/[[OP_NOTIF]] [[ScriptToken]] has a matching [[OP_ENDIF]] */
def checkMatchingOpIfOpNotIfOpEndIf(script : List[ScriptToken]) : Boolean = {
def checkMatchingOpIfOpNotIfOpEndIf(script: List[ScriptToken]): Boolean = {
@tailrec
def loop(script : List[ScriptToken], counter : Int) : Boolean = script match {
case _ if (counter < 0) => false
case OP_ENDIF :: t => loop(t,counter-1)
case OP_IF :: t => loop(t, counter + 1)
case OP_NOTIF :: t => loop(t, counter + 1)
def loop(script: List[ScriptToken], counter: Int): Boolean = script match {
case _ if (counter < 0) => false
case OP_ENDIF :: t => loop(t, counter - 1)
case OP_IF :: t => loop(t, counter + 1)
case OP_NOTIF :: t => loop(t, counter + 1)
case (_: ScriptToken) :: t => loop(t, counter)
case Nil => counter == 0
case Nil => counter == 0
}
loop(script,0)
loop(script, 0)
}
/** Returns the first index of an [[OP_ENDIF]]. */
def findFirstOpEndIf(script : List[ScriptToken]) : Option[Int] = {
def findFirstOpEndIf(script: List[ScriptToken]): Option[Int] = {
val index = script.indexOf(OP_ENDIF)
index match {
case -1 => None
case _ => Some(index)
case _ => Some(index)
}
}
/** Finds the last [[OP_ENDIF]] in the given script. */
def findLastOpEndIf(script : List[ScriptToken]) : Option[Int] = {
def findLastOpEndIf(script: List[ScriptToken]): Option[Int] = {
val lastOpEndIf = findFirstOpEndIf(script.reverse)
if (lastOpEndIf.isDefined) Some(script.size - lastOpEndIf.get - 1)
else None
}
/** Returns the first index of an [[OP_ENDIF]]. */
def findFirstOpElse(script : List[ScriptToken]) : Option[Int] = {
def findFirstOpElse(script: List[ScriptToken]): Option[Int] = {
val index = script.indexOf(OP_ELSE)
index match {
case -1 => None
case _ => Some(index)
case _ => Some(index)
}
}
/** Removes the first [[OP_ELSE]] expression encountered in the script. */
def removeFirstOpElse(script : List[ScriptToken]) : List[ScriptToken] = {
def removeFirstOpElse(script: List[ScriptToken]): List[ScriptToken] = {
removeFirstOpElse(parseBinaryTree(script)).toList
}
/** Removes the first [[OP_ELSE]] in a [[BinaryTree]]. */
def removeFirstOpElse(tree : BinaryTree[ScriptToken]) : BinaryTree[ScriptToken] = {
def removeFirstOpElse(tree: BinaryTree[ScriptToken]): BinaryTree[ScriptToken] = {
//@tailrec
def loop(child: BinaryTree[ScriptToken], parent: Node[ScriptToken]): BinaryTree[ScriptToken] = child match {
case Empty => Empty
case Empty => Empty
case l: Leaf[ScriptToken] => l
case Node(OP_ELSE,_,r) => r
case Node(OP_ELSE, _, r) => r
case n: Node[ScriptToken] =>
Node(n.v,n.l,loop(n.r,n))
Node(n.v, n.l, loop(n.r, n))
}
tree match {
case Empty => Empty
case Empty => Empty
case l: Leaf[ScriptToken] => l
case n: Node[ScriptToken] =>
val result = Node(n.v,n.l,loop(n.r, n))
val result = Node(n.v, n.l, loop(n.r, n))
result
}
}
/** Removes the first [[OP_IF]] encountered in the script. */
def removeFirstOpIf(script : List[ScriptToken]) : List[ScriptToken] = {
def removeFirstOpIf(script: List[ScriptToken]): List[ScriptToken] = {
removeFirstOpIf(parseBinaryTree(script)).toList
}
/** Removes the first occurrence of [[OP_IF]] or [[OP_NOTIF]] in the [[BinaryTree]]. */
def removeFirstOpIf(tree : BinaryTree[ScriptToken]) : BinaryTree[ScriptToken] = {
require(tree.value.isDefined && (tree.value.get == OP_IF || tree.value.get == OP_NOTIF) , "Top of the tree must be OP_IF or OP_NOTIF to remove the OP_IF or OP_NOTIF")
def removeFirstOpIf(tree: BinaryTree[ScriptToken]): BinaryTree[ScriptToken] = {
require(tree.value.isDefined && (tree.value.get == OP_IF || tree.value.get == OP_NOTIF), "Top of the tree must be OP_IF or OP_NOTIF to remove the OP_IF or OP_NOTIF")
tree.right.getOrElse(Empty)
}
/** Finds the indexes of our [[OP_ELSE]] (if it exists) and our [[OP_ENDIF]]. */
def findFirstIndexesOpElseOpEndIf(script : List[ScriptToken]) : (Option[Int],Option[Int]) = {
def findFirstIndexesOpElseOpEndIf(script: List[ScriptToken]): (Option[Int], Option[Int]) = {
val indexOpElse = findFirstOpElse(script)
val indexOpEndIf = findFirstOpEndIf(script)
(indexOpElse,indexOpEndIf)
(indexOpElse, indexOpEndIf)
}
/** Returns the index of the matching [[OP_ENDIF]] for the [[OP_IF]] statement. */
def findMatchingOpEndIf(script : List[ScriptToken]) : Int = {
/** Returns the index of the matching [[OP_ENDIF]] for the [[OP_IF]] statement. */
def findMatchingOpEndIf(script: List[ScriptToken]): Int = {
val matchingOpEndIfIndex = findLastOpEndIf(script)
require(matchingOpEndIfIndex.isDefined, "Every OP_IF must have a matching OP_ENDIF: " + script)
matchingOpEndIfIndex.get

View file

@ -2,15 +2,14 @@ package org.bitcoins.core.script.crypto
import org.bitcoins.core.crypto._
import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.control.{ControlOperationsInterpreter, OP_VERIFY}
import org.bitcoins.core.script.control.{ ControlOperationsInterpreter, OP_VERIFY }
import org.bitcoins.core.script.flag.ScriptFlagUtil
import org.bitcoins.core.script.result._
import org.bitcoins.core.script.{ScriptProgram, _}
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinScriptUtil, CryptoUtil}
import org.bitcoins.core.script.{ ScriptProgram, _ }
import org.bitcoins.core.util.{ BitcoinSLogger, BitcoinScriptUtil, CryptoUtil }
import scala.annotation.tailrec
/**
* Created by chris on 1/6/16.
*/
@ -19,33 +18,33 @@ sealed abstract class CryptoInterpreter {
private def logger = BitcoinSLogger.logger
/** The input is hashed twice: first with SHA-256 and then with RIPEMD-160. */
def opHash160(program : ScriptProgram) : ScriptProgram = {
def opHash160(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_HASH160), "Script operation must be OP_HASH160")
executeHashFunction(program, CryptoUtil.sha256Hash160(_ : Seq[Byte]))
executeHashFunction(program, CryptoUtil.sha256Hash160(_: Seq[Byte]))
}
/** The input is hashed using RIPEMD-160. */
def opRipeMd160(program : ScriptProgram) : ScriptProgram = {
def opRipeMd160(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_RIPEMD160), "Script operation must be OP_RIPEMD160")
executeHashFunction(program, CryptoUtil.ripeMd160(_ : Seq[Byte]))
executeHashFunction(program, CryptoUtil.ripeMd160(_: Seq[Byte]))
}
/** The input is hashed using SHA-256. */
def opSha256(program : ScriptProgram) : ScriptProgram = {
def opSha256(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_SHA256), "Script operation must be OP_SHA256")
executeHashFunction(program, CryptoUtil.sha256(_ : Seq[Byte]))
executeHashFunction(program, CryptoUtil.sha256(_: Seq[Byte]))
}
/** The input is hashed two times with SHA-256. */
def opHash256(program : ScriptProgram) : ScriptProgram = {
def opHash256(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_HASH256), "Script operation must be OP_HASH256")
executeHashFunction(program, CryptoUtil.doubleSHA256(_ : Seq[Byte]))
executeHashFunction(program, CryptoUtil.doubleSHA256(_: Seq[Byte]))
}
/** The input is hashed using SHA-1. */
def opSha1(program : ScriptProgram) : ScriptProgram = {
def opSha1(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_SHA1), "Script top must be OP_SHA1")
executeHashFunction(program, CryptoUtil.sha1(_ : Seq[Byte]))
executeHashFunction(program, CryptoUtil.sha1(_: Seq[Byte]))
}
/**
@ -54,17 +53,17 @@ sealed abstract class CryptoInterpreter {
* The signature used by [[OP_CHECKSIG]] must be a valid signature for this hash and public key.
* [[https://github.com/bitcoin/bitcoin/blob/528472111b4965b1a99c4bcf08ac5ec93d87f10f/src/script/interpreter.cpp#L880]]
*/
def opCheckSig(program : ScriptProgram) : ScriptProgram = {
def opCheckSig(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_CHECKSIG), "Script top must be OP_CHECKSIG")
program match {
case preExecutionScriptProgram : PreExecutionScriptProgram =>
case preExecutionScriptProgram: PreExecutionScriptProgram =>
opCheckSig(ScriptProgram.toExecutionInProgress(preExecutionScriptProgram))
case executedScriptprogram : ExecutedScriptProgram =>
case executedScriptprogram: ExecutedScriptProgram =>
executedScriptprogram
case executionInProgressScriptProgram : ExecutionInProgressScriptProgram =>
case executionInProgressScriptProgram: ExecutionInProgressScriptProgram =>
if (executionInProgressScriptProgram.stack.size < 2) {
logger.error("OP_CHECKSIG requires at lest two stack elements")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
} else {
val pubKey = ECPublicKey(executionInProgressScriptProgram.stack.head.bytes)
val signature = ECDigitalSignature(executionInProgressScriptProgram.stack.tail.head.bytes)
@ -73,85 +72,91 @@ sealed abstract class CryptoInterpreter {
logger.debug("Program before removing OP_CODESEPARATOR: " + program.originalScript)
val removedOpCodeSeparatorsScript = BitcoinScriptUtil.removeOpCodeSeparator(executionInProgressScriptProgram)
logger.debug("Program after removing OP_CODESEPARATOR: " + removedOpCodeSeparatorsScript)
val result = TransactionSignatureChecker.checkSignature(executionInProgressScriptProgram.txSignatureComponent,
removedOpCodeSeparatorsScript, pubKey, signature, flags)
handleSignatureValidation(program,result,restOfStack)
val result = TransactionSignatureChecker.checkSignature(
executionInProgressScriptProgram.txSignatureComponent,
removedOpCodeSeparatorsScript, pubKey, signature, flags
)
handleSignatureValidation(program, result, restOfStack)
}
}
}
/** Runs [[OP_CHECKSIG]] with an [[OP_VERIFY]] afterwards. */
def opCheckSigVerify(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.contains(OP_CHECKSIGVERIFY),
"Script top must be OP_CHECKSIGVERIFY")
def opCheckSigVerify(program: ScriptProgram): ScriptProgram = {
require(
program.script.headOption.contains(OP_CHECKSIGVERIFY),
"Script top must be OP_CHECKSIGVERIFY"
)
if (program.stack.size < 2) {
logger.error("Stack must contain at least 3 items for OP_CHECKSIGVERIFY")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
} else {
val newScript = OP_CHECKSIG :: OP_VERIFY :: program.script.tail
val newProgram = ScriptProgram(program,newScript, ScriptProgram.Script)
val newProgram = ScriptProgram(program, newScript, ScriptProgram.Script)
val programFromOpCheckSig = opCheckSig(newProgram)
logger.debug("Stack after OP_CHECKSIG execution: " + programFromOpCheckSig.stack)
programFromOpCheckSig match {
case _ : PreExecutionScriptProgram | _ : ExecutedScriptProgram =>
case _: PreExecutionScriptProgram | _: ExecutedScriptProgram =>
programFromOpCheckSig
case _ : ExecutionInProgressScriptProgram => ControlOperationsInterpreter.opVerify(programFromOpCheckSig)
case _: ExecutionInProgressScriptProgram => ControlOperationsInterpreter.opVerify(programFromOpCheckSig)
}
}
}
/** All of the signature checking words will only match signatures to the data
* after the most recently-executed [[OP_CODESEPARATOR]]. */
def opCodeSeparator(program : ScriptProgram) : ScriptProgram = {
/**
* All of the signature checking words will only match signatures to the data
* after the most recently-executed [[OP_CODESEPARATOR]].
*/
def opCodeSeparator(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_CODESEPARATOR), "Script top must be OP_CODESEPARATOR")
val e = program match {
case e : PreExecutionScriptProgram =>
case e: PreExecutionScriptProgram =>
opCodeSeparator(ScriptProgram.toExecutionInProgress(e))
case e : ExecutionInProgressScriptProgram =>
case e: ExecutionInProgressScriptProgram =>
val indexOfOpCodeSeparator = program.originalScript.size - program.script.size
ScriptProgram(e,program.script.tail,ScriptProgram.Script,indexOfOpCodeSeparator)
case e : ExecutedScriptProgram =>
ScriptProgram(e,ScriptErrorUnknownError)
ScriptProgram(e, program.script.tail, ScriptProgram.Script, indexOfOpCodeSeparator)
case e: ExecutedScriptProgram =>
ScriptProgram(e, ScriptErrorUnknownError)
}
e
}
/**
* Compares the first signature against each public key until it finds an ECDSA match.
* Starting with the subsequent public key, it compares the second signature against each remaining
* public key until it finds an ECDSA match. The process is repeated until all signatures have been
* checked or not enough public keys remain to produce a successful result.
* All signatures need to match a public key.
* Because public keys are not checked again if they fail any signature comparison,
* signatures must be placed in the scriptSig using the same order as their corresponding public keys
* were placed in the scriptPubKey or redeemScript. If all signatures are valid, 1 is returned, 0 otherwise.
* Due to a bug, one extra unused value is removed from the stack.
*/
* Compares the first signature against each public key until it finds an ECDSA match.
* Starting with the subsequent public key, it compares the second signature against each remaining
* public key until it finds an ECDSA match. The process is repeated until all signatures have been
* checked or not enough public keys remain to produce a successful result.
* All signatures need to match a public key.
* Because public keys are not checked again if they fail any signature comparison,
* signatures must be placed in the scriptSig using the same order as their corresponding public keys
* were placed in the scriptPubKey or redeemScript. If all signatures are valid, 1 is returned, 0 otherwise.
* Due to a bug, one extra unused value is removed from the stack.
*/
@tailrec
final def opCheckMultiSig(program : ScriptProgram) : ScriptProgram = {
final def opCheckMultiSig(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_CHECKMULTISIG), "Script top must be OP_CHECKMULTISIG")
val flags = program.flags
program match {
case preExecutionScriptProgram : PreExecutionScriptProgram =>
case preExecutionScriptProgram: PreExecutionScriptProgram =>
opCheckMultiSig(ScriptProgram.toExecutionInProgress(preExecutionScriptProgram))
case executedScriptProgram : ExecutedScriptProgram =>
case executedScriptProgram: ExecutedScriptProgram =>
executedScriptProgram
case executionInProgressScriptProgram : ExecutionInProgressScriptProgram =>
case executionInProgressScriptProgram: ExecutionInProgressScriptProgram =>
if (program.stack.size < 1) {
logger.error("OP_CHECKMULTISIG requires at least 1 stack elements")
ScriptProgram(executionInProgressScriptProgram,ScriptErrorInvalidStackOperation)
ScriptProgram(executionInProgressScriptProgram, ScriptErrorInvalidStackOperation)
} else {
//these next lines remove the appropriate stack/script values after the signatures have been checked
val nPossibleSignatures : ScriptNumber = BitcoinScriptUtil.numPossibleSignaturesOnStack(program)
val nPossibleSignatures: ScriptNumber = BitcoinScriptUtil.numPossibleSignaturesOnStack(program)
if (nPossibleSignatures < ScriptNumber.zero) {
logger.error("We cannot have the number of pubkeys in the script be negative")
ScriptProgram(program,ScriptErrorPubKeyCount)
ScriptProgram(program, ScriptErrorPubKeyCount)
} else if (ScriptFlagUtil.requireMinimalData(flags) && !nPossibleSignatures.isShortestEncoding) {
logger.error("The required signatures and the possible signatures must be encoded as the shortest number possible")
ScriptProgram(executionInProgressScriptProgram, ScriptErrorUnknownError)
} else if (program.stack.size < 2) {
logger.error("We need at least 2 operations on the stack")
ScriptProgram(executionInProgressScriptProgram,ScriptErrorInvalidStackOperation)
ScriptProgram(executionInProgressScriptProgram, ScriptErrorInvalidStackOperation)
} else {
val mRequiredSignatures: ScriptNumber = BitcoinScriptUtil.numRequiredSignaturesOnStack(program)
@ -166,8 +171,10 @@ sealed abstract class CryptoInterpreter {
}
logger.debug("nPossibleSignatures: " + nPossibleSignatures)
val (pubKeysScriptTokens, stackWithoutPubKeys) =
(program.stack.tail.slice(0, nPossibleSignatures.toInt),
program.stack.tail.slice(nPossibleSignatures.toInt, program.stack.tail.size))
(
program.stack.tail.slice(0, nPossibleSignatures.toInt),
program.stack.tail.slice(nPossibleSignatures.toInt, program.stack.tail.size)
)
val pubKeys = pubKeysScriptTokens.map(key => ECPublicKey(key.bytes))
logger.debug("Public keys on the stack: " + pubKeys)
@ -175,8 +182,10 @@ sealed abstract class CryptoInterpreter {
logger.debug("mRequiredSignatures: " + mRequiredSignatures)
//+1 is for the fact that we have the # of sigs + the script token indicating the # of sigs
val signaturesScriptTokens = program.stack.tail.slice(nPossibleSignatures.toInt + 1,
nPossibleSignatures.toInt + mRequiredSignatures.toInt + 1)
val signaturesScriptTokens = program.stack.tail.slice(
nPossibleSignatures.toInt + 1,
nPossibleSignatures.toInt + mRequiredSignatures.toInt + 1
)
val signatures = signaturesScriptTokens.map(token => ECDigitalSignature(token.bytes))
logger.debug("Signatures on the stack: " + signatures)
@ -193,43 +202,45 @@ sealed abstract class CryptoInterpreter {
logger.error("OP_CHECKMULTISIG must have a remaining element on the stack afterk execution")
//this is because of a bug in bitcoin core for the implementation of OP_CHECKMULTISIG
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L966
ScriptProgram(executionInProgressScriptProgram,ScriptErrorInvalidStackOperation)
ScriptProgram(executionInProgressScriptProgram, ScriptErrorInvalidStackOperation)
} else if (ScriptFlagUtil.requireNullDummy(flags) &&
(stackWithoutPubKeysAndSignatures.nonEmpty && stackWithoutPubKeysAndSignatures.head.bytes.nonEmpty)) {
logger.error("Script flag null dummy was set however the first element in the script signature was not an OP_0, stackWithoutPubKeysAndSignatures: " + stackWithoutPubKeysAndSignatures)
ScriptProgram(executionInProgressScriptProgram,ScriptErrorSigNullDummy)
ScriptProgram(executionInProgressScriptProgram, ScriptErrorSigNullDummy)
} else {
//remove the last OP_CODESEPARATOR
val removedOpCodeSeparatorsScript = BitcoinScriptUtil.removeOpCodeSeparator(executionInProgressScriptProgram)
val isValidSignatures: TransactionSignatureCheckerResult =
TransactionSignatureChecker.multiSignatureEvaluator(executionInProgressScriptProgram.txSignatureComponent,
TransactionSignatureChecker.multiSignatureEvaluator(
executionInProgressScriptProgram.txSignatureComponent,
removedOpCodeSeparatorsScript, signatures,
pubKeys, flags, mRequiredSignatures.toLong)
pubKeys, flags, mRequiredSignatures.toLong
)
//remove the extra OP_0 (null dummy) for OP_CHECKMULTISIG from the stack
val restOfStack = stackWithoutPubKeysAndSignatures.tail
handleSignatureValidation(program,isValidSignatures,restOfStack)
handleSignatureValidation(program, isValidSignatures, restOfStack)
}
}
}
}
}
}
/** Runs [[OP_CHECKMULTISIG]] with an [[OP_VERIFY]] afterwards */
def opCheckMultiSigVerify(program : ScriptProgram) : ScriptProgram = {
def opCheckMultiSigVerify(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_CHECKMULTISIGVERIFY), "Script top must be OP_CHECKMULTISIGVERIFY")
if (program.stack.size < 3) {
logger.error("Stack must contain at least 3 items for OP_CHECKMULTISIGVERIFY")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
} else {
val newScript = OP_CHECKMULTISIG :: OP_VERIFY :: program.script.tail
val newProgram = ScriptProgram(program,newScript, ScriptProgram.Script)
val newProgram = ScriptProgram(program, newScript, ScriptProgram.Script)
val programFromOpCheckMultiSig = opCheckMultiSig(newProgram)
logger.debug("Stack after OP_CHECKMULTSIG execution: " + programFromOpCheckMultiSig.stack)
programFromOpCheckMultiSig match {
case _ : PreExecutionScriptProgram | _ : ExecutedScriptProgram =>
case _: PreExecutionScriptProgram | _: ExecutedScriptProgram =>
programFromOpCheckMultiSig
case _ : ExecutionInProgressScriptProgram => ControlOperationsInterpreter.opVerify(programFromOpCheckMultiSig)
case _: ExecutionInProgressScriptProgram => ControlOperationsInterpreter.opVerify(programFromOpCheckMultiSig)
}
}
}
@ -242,18 +253,17 @@ sealed abstract class CryptoInterpreter {
* @param hashFunction the hash function which needs to be used on the stack top (sha256,ripemd160,etc..)
* @return
*/
private def executeHashFunction(program : ScriptProgram, hashFunction : Seq[Byte] => HashDigest) : ScriptProgram = {
private def executeHashFunction(program: ScriptProgram, hashFunction: Seq[Byte] => HashDigest): ScriptProgram = {
if (program.stack.nonEmpty) {
val stackTop = program.stack.head
val hash = ScriptConstant(hashFunction(stackTop.bytes).bytes)
ScriptProgram(program, hash :: program.stack.tail, program.script.tail)
} else {
logger.error("We must have the stack top defined to execute a hash function")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
}
}
private def handleSignatureValidation(program: ScriptProgram, result: TransactionSignatureCheckerResult, restOfStack: Seq[ScriptToken]): ScriptProgram = result match {
case SignatureValidationSuccess =>
//means that all of the signatures were correctly encoded and
@ -281,9 +291,9 @@ sealed abstract class CryptoInterpreter {
case SignatureValidationErrorHashType =>
ScriptProgram(program, ScriptErrorSigHashType)
case SignatureValidationErrorWitnessPubKeyType =>
ScriptProgram(program,ScriptErrorWitnessPubKeyType)
ScriptProgram(program, ScriptErrorWitnessPubKeyType)
case SignatureValidationErrorNullFail =>
ScriptProgram(program,ScriptErrorSigNullFail)
ScriptProgram(program, ScriptErrorSigNullFail)
}
}

View file

@ -37,16 +37,20 @@ case object OP_HASH256 extends CryptoOperation {
override def opCode = 170
}
/** All of the signature checking words will only match signatures to
* the data after the most recently-executed OP_CODESEPARATOR. */
/**
* All of the signature checking words will only match signatures to
* the data after the most recently-executed OP_CODESEPARATOR.
*/
case object OP_CODESEPARATOR extends CryptoOperation {
override def opCode = 171
}
/** The entire transaction's outputs, inputs, and script
/**
* The entire transaction's outputs, inputs, and script
* (from the most recently-executed OP_CODESEPARATOR to the end) are hashed.
* The signature used by OP_CHECKSIG must be a valid signature for this hash and public key.
* If it is, 1 is returned, 0 otherwise. */
* If it is, 1 is returned, 0 otherwise.
*/
case object OP_CHECKSIG extends CryptoSignatureEvaluation {
override def opCode = 172
}
@ -56,7 +60,8 @@ case object OP_CHECKSIGVERIFY extends CryptoSignatureEvaluation {
override def opCode = 173
}
/** Compares the first signature against each public key until it finds an ECDSA match.
/**
* Compares the first signature against each public key until it finds an ECDSA match.
* Starting with the subsequent public key, it compares the second signature against each remaining public key
* until it finds an ECDSA match.
* The process is repeated until all signatures have been checked or not enough public keys remain to produce a successful result.
@ -64,7 +69,8 @@ case object OP_CHECKSIGVERIFY extends CryptoSignatureEvaluation {
* Because public keys are not checked again if they fail any signature comparison,
* signatures must be placed in the scriptSig using the same order as their corresponding public keys
* were placed in the scriptPubKey or redeemScript. If all signatures are valid, 1 is returned, 0 otherwise.
* Due to a bug, one extra unused value is removed from the stack. */
* Due to a bug, one extra unused value is removed from the stack.
*/
case object OP_CHECKMULTISIG extends CryptoSignatureEvaluation {
override def opCode = 174
}

View file

@ -5,10 +5,10 @@ import org.bitcoins.core.script.ScriptOperationFactory
/**
* Created by chris on 3/24/16.
*/
trait CryptoSignatureEvaluationFactory extends ScriptOperationFactory[CryptoSignatureEvaluation] {
trait CryptoSignatureEvaluationFactory extends ScriptOperationFactory[CryptoSignatureEvaluation] {
/** The current [[CryptoSignatureEvaluation]] operations. */
def operations = Seq(OP_CHECKMULTISIG,OP_CHECKMULTISIGVERIFY,OP_CHECKSIG, OP_CHECKSIGVERIFY)
def operations = Seq(OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY, OP_CHECKSIG, OP_CHECKSIGVERIFY)
}

View file

@ -8,26 +8,24 @@ import org.bitcoins.core.util.Factory
* Created by chris on 1/18/16.
*/
sealed trait HashType {
def num : Int32
def byte : Byte
def num: Int32
def byte: Byte
}
object HashType extends Factory[HashType] {
def fromBytes(bytes : Seq[Byte]) : HashType = {
def fromBytes(bytes: Seq[Byte]): HashType = {
val num = Int32(bytes)
fromNumber(num)
}
def fromByte(byte: Byte) : HashType = fromBytes(Seq(byte))
def fromByte(byte: Byte): HashType = fromBytes(Seq(byte))
def fromNumber(num : Int32) : HashType = {
def fromNumber(num: Int32): HashType = {
if (isSIGHASH_NONE(num)) {
if (isSIGHASH_NONE_ANYONECANPAY(num)) SIGHASH_NONE_ANYONECANPAY(num) else SIGHASH_NONE(num)
}
else if (isSIGHASH_SINGLE(num)) {
} else if (isSIGHASH_SINGLE(num)) {
if (isSIGHASH_SINGLE_ANYONECANPAY(num)) SIGHASH_SINGLE_ANYONECANPAY(num) else SIGHASH_SINGLE(num)
}
else if (isSIGHASH_ANYONECANPAY(num)) {
} else if (isSIGHASH_ANYONECANPAY(num)) {
if (isSIGHASH_ALL_ANYONECANPAY(num)) SIGHASH_ALL_ANYONECANPAY(num)
else {
require(isONLY_ANYONE_CANPAY(num))
@ -37,33 +35,33 @@ object HashType extends Factory[HashType] {
}
/** Returns a hashtype's default byte value */
def byte(hashType : HashType) : Byte = hashType match {
case _ : SIGHASH_ALL => sigHashAllByte
case h : HashType => h.byte
def byte(hashType: HashType): Byte = hashType match {
case _: SIGHASH_ALL => sigHashAllByte
case h: HashType => h.byte
}
def isSIGHASH_ALL_ONE(num : Int32) : Boolean = (num & Int32(0x1f)) == Int32(1)
def isSIGHASH_NONE(num : Int32) : Boolean = (num & Int32(0x1f)) == Int32(2)
def isSIGHASH_SINGLE(num : Int32) : Boolean = (num & Int32(0x1f)) == Int32(3)
def isSIGHASH_ANYONECANPAY(num : Int32) : Boolean = (num & Int32(0x80)) == Int32(0x80)
def isSIGHASH_ALL_ANYONECANPAY(num : Int32) : Boolean = isSIGHASH_ALL_ONE(num) && isSIGHASH_ANYONECANPAY(num)
def isSIGHASH_NONE_ANYONECANPAY(num : Int32) : Boolean = isSIGHASH_NONE(num) && isSIGHASH_ANYONECANPAY(num)
def isSIGHASH_SINGLE_ANYONECANPAY(num: Int32) : Boolean = isSIGHASH_SINGLE(num) && isSIGHASH_ANYONECANPAY(num)
def isSIGHASH_ALL(num : Int32) : Boolean = {
def isSIGHASH_ALL_ONE(num: Int32): Boolean = (num & Int32(0x1f)) == Int32(1)
def isSIGHASH_NONE(num: Int32): Boolean = (num & Int32(0x1f)) == Int32(2)
def isSIGHASH_SINGLE(num: Int32): Boolean = (num & Int32(0x1f)) == Int32(3)
def isSIGHASH_ANYONECANPAY(num: Int32): Boolean = (num & Int32(0x80)) == Int32(0x80)
def isSIGHASH_ALL_ANYONECANPAY(num: Int32): Boolean = isSIGHASH_ALL_ONE(num) && isSIGHASH_ANYONECANPAY(num)
def isSIGHASH_NONE_ANYONECANPAY(num: Int32): Boolean = isSIGHASH_NONE(num) && isSIGHASH_ANYONECANPAY(num)
def isSIGHASH_SINGLE_ANYONECANPAY(num: Int32): Boolean = isSIGHASH_SINGLE(num) && isSIGHASH_ANYONECANPAY(num)
def isSIGHASH_ALL(num: Int32): Boolean = {
if (!(isSIGHASH_NONE(num) || isSIGHASH_SINGLE(num) || isSIGHASH_ANYONECANPAY(num) || isSIGHASH_ALL_ANYONECANPAY(num) ||
isSIGHASH_SINGLE_ANYONECANPAY(num) || isSIGHASH_NONE_ANYONECANPAY(num))) true
else false
}
def isONLY_ANYONE_CANPAY(num : Int32) : Boolean = {
def isONLY_ANYONE_CANPAY(num: Int32): Boolean = {
!(HashType.isSIGHASH_ALL_ANYONECANPAY(num) || HashType.isSIGHASH_NONE_ANYONECANPAY(num) ||
HashType.isSIGHASH_SINGLE_ANYONECANPAY(num))
}
/** Checks if the given hash type has the ANYONECANPAY bit set */
def isAnyoneCanPay(hashType: HashType): Boolean = hashType match {
case _ : SIGHASH_ANYONECANPAY | _: SIGHASH_ALL_ANYONECANPAY | _: SIGHASH_SINGLE_ANYONECANPAY |
_: SIGHASH_NONE_ANYONECANPAY => true
case _ : SIGHASH_ALL | _: SIGHASH_SINGLE | _: SIGHASH_NONE => false
case _: SIGHASH_ANYONECANPAY | _: SIGHASH_ALL_ANYONECANPAY | _: SIGHASH_SINGLE_ANYONECANPAY |
_: SIGHASH_NONE_ANYONECANPAY => true
case _: SIGHASH_ALL | _: SIGHASH_SINGLE | _: SIGHASH_NONE => false
}
lazy val hashTypes = Seq(sigHashAll, sigHashNone, sigHashSingle, sigHashAnyoneCanPay,
@ -71,11 +69,11 @@ object HashType extends Factory[HashType] {
lazy val hashTypeBytes: Seq[Byte] = Seq(sigHashAllByte, sigHashSingleByte, sigHashNoneByte, sigHashAnyoneCanPayByte,
sigHashNoneAnyoneCanPayByte, sigHashSingleAnyoneCanPayByte, sigHashAllAnyoneCanPayByte)
def apply(num : Int32) : HashType = fromNumber(num)
def apply(num: Int32): HashType = fromNumber(num)
def apply(int : Int) : HashType = HashType(Int32(int))
def apply(int: Int): HashType = HashType(Int32(int))
def apply(byte : Byte) : HashType = fromByte(byte)
def apply(byte: Byte): HashType = fromByte(byte)
/** The default byte used to represent [[SIGHASH_ALL]] */
val sigHashAllByte = 1.toByte
@ -83,15 +81,16 @@ object HashType extends Factory[HashType] {
/** The default [[SIGHASH_ALL]] value */
val sigHashAll = SIGHASH_ALL(sigHashAllByte)
/** The default num for [[SIGHASH_ANYONECANPAY]]
* We need this for serialization of [[HashType]]
* flags inside of [[org.bitcoins.core.crypto.TransactionSignatureSerializer]]
*
* Have to be careful using this value, since native scala numbers are signed
* We need this because this serializes to 0x00000080 instead of 0xffffff80
* If we try to use Int32(sigHashAnyoneCanPayByte) we will get the latter serialization
* because all native scala numbers are signed
* */
/**
* The default num for [[SIGHASH_ANYONECANPAY]]
* We need this for serialization of [[HashType]]
* flags inside of [[org.bitcoins.core.crypto.TransactionSignatureSerializer]]
*
* Have to be careful using this value, since native scala numbers are signed
* We need this because this serializes to 0x00000080 instead of 0xffffff80
* If we try to use Int32(sigHashAnyoneCanPayByte) we will get the latter serialization
* because all native scala numbers are signed
*/
val sigHashAnyoneCanPayNum = Int32(0x80)
val sigHashAnyoneCanPayByte = 0x80.toByte
@ -127,19 +126,19 @@ object HashType extends Factory[HashType] {
val sigHashSingleAnyoneCanPay = SIGHASH_SINGLE_ANYONECANPAY(sigHashSingleAnyoneCanPayNum)
/**
* Checks if the given digital signature has a valid hash type
* Mimics this functionality inside of Bitcoin Core
* https://github.com/bitcoin/bitcoin/blob/b83264d9c7a8ddb79f64bd9540caddc8632ef31f/src/script/interpreter.cpp#L186
*/
* Checks if the given digital signature has a valid hash type
* Mimics this functionality inside of Bitcoin Core
* https://github.com/bitcoin/bitcoin/blob/b83264d9c7a8ddb79f64bd9540caddc8632ef31f/src/script/interpreter.cpp#L186
*/
def isDefinedHashtypeSignature(sig: ECDigitalSignature): Boolean = {
sig.bytes.nonEmpty && hashTypeBytes.contains(sig.bytes.last)
}
}
/**
* defaultValue is the underlying value of the HashType. The last byte of a signature determines the HashType.
* https://en.bitcoin.it/wiki/OP_CHECKSIG
*/
* defaultValue is the underlying value of the HashType. The last byte of a signature determines the HashType.
* https://en.bitcoin.it/wiki/OP_CHECKSIG
*/
case class SIGHASH_ALL(override val num: Int32) extends HashType {
require(HashType.isSIGHASH_ALL(num), "SIGHASH_ALL acts as a 'catch-all' for undefined hashtypes, and has a default " +
"value of one. Your input was: " + num + ", which is of hashType: " + HashType(num))
@ -151,7 +150,7 @@ object SIGHASH_ALL {
case class SIGHASH_NONE(override val num: Int32) extends HashType {
require(HashType.isSIGHASH_NONE(num), "The given number is not a SIGHASH_NONE number: " + num)
override def byte : Byte = HashType.sigHashNoneByte
override def byte: Byte = HashType.sigHashNoneByte
}
case class SIGHASH_SINGLE(override val num: Int32) extends HashType {
@ -166,15 +165,15 @@ case class SIGHASH_ANYONECANPAY(override val num: Int32) extends HashType {
case class SIGHASH_ALL_ANYONECANPAY(override val num: Int32) extends HashType {
require(HashType.isSIGHASH_ALL_ANYONECANPAY(num), "The given number was not a SIGHASH_ALL_ANYONECANPAY number: " + num)
override def byte : Byte = HashType.sigHashAllAnyoneCanPayByte
override def byte: Byte = HashType.sigHashAllAnyoneCanPayByte
}
case class SIGHASH_NONE_ANYONECANPAY(override val num: Int32) extends HashType {
require(HashType.isSIGHASH_NONE_ANYONECANPAY(num), "The given number was not a SIGHASH_NONE_ANYONECANPAY number: " + num)
override def byte : Byte = HashType.sigHashNoneAnyoneCanPayByte
override def byte: Byte = HashType.sigHashNoneAnyoneCanPayByte
}
case class SIGHASH_SINGLE_ANYONECANPAY(override val num: Int32) extends HashType {
require(HashType.isSIGHASH_SINGLE_ANYONECANPAY(num), "The given number was not a SIGHASH_SINGLE_ANYONECANPAY number: " + num)
override def byte : Byte = HashType.sigHashSingleAnyoneCanPayByte
override def byte: Byte = HashType.sigHashSingleAnyoneCanPayByte
}

View file

@ -7,34 +7,37 @@ package org.bitcoins.core.script.flag
*/
trait ScriptFlagFactory {
/** All the [[ScriptFlag]]s found inside of bitcoin core
* https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.h#L31. */
/**
* All the [[ScriptFlag]]s found inside of bitcoin core
* https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.h#L31.
*/
private def flags = Seq(ScriptVerifyNone, ScriptVerifyP2SH, ScriptVerifyStrictEnc,
ScriptVerifyDerSig, ScriptVerifyLowS, ScriptVerifySigPushOnly, ScriptVerifyMinimalData,
ScriptVerifyNullDummy, ScriptVerifyDiscourageUpgradableNOPs, ScriptVerifyCleanStack,
ScriptVerifyCheckLocktimeVerify, ScriptVerifyCheckSequenceVerify,ScriptVerifyWitness,
ScriptVerifyDiscourageUpgradableWitnessProgram, ScriptVerifyMinimalIf,ScriptVerifyNullFail,
ScriptVerifyCheckLocktimeVerify, ScriptVerifyCheckSequenceVerify, ScriptVerifyWitness,
ScriptVerifyDiscourageUpgradableWitnessProgram, ScriptVerifyMinimalIf, ScriptVerifyNullFail,
ScriptVerifyWitnessPubKeyType)
/** Takes in a string and tries to match it with a [[ScriptFlag]]. */
def fromString(str : String) : Option[ScriptFlag] = {
def fromString(str: String): Option[ScriptFlag] = {
flags.find(_.name == str)
}
/** Parses the given list into[[ScriptFlag]]s
* the strings that do not match a [[ScriptFlag]] are discarded. */
def fromList(list : Seq[String]) : Seq[ScriptFlag] = {
/**
* Parses the given list into[[ScriptFlag]]s
* the strings that do not match a [[ScriptFlag]] are discarded.
*/
def fromList(list: Seq[String]): Seq[ScriptFlag] = {
list.flatMap(fromString(_))
}
/** Parses a list of [[ScriptFlag]]s that is separated by commas. */
def fromList(str : String) : Seq[ScriptFlag] = {
def fromList(str: String): Seq[ScriptFlag] = {
fromList(str.split(","))
}
/** Empty script flag. */
def empty : Seq[ScriptFlag] = Nil
def empty: Seq[ScriptFlag] = Nil
}
object ScriptFlagFactory extends ScriptFlagFactory

View file

@ -12,7 +12,7 @@ trait ScriptFlagUtil {
* @param flags
* @return
*/
def requiresStrictDerEncoding(flags : Seq[ScriptFlag]) : Boolean = {
def requiresStrictDerEncoding(flags: Seq[ScriptFlag]): Boolean = {
flags.contains(ScriptVerifyDerSig) || flags.contains(ScriptVerifyStrictEnc)
}
@ -21,30 +21,30 @@ trait ScriptFlagUtil {
* @param flags
* @return
*/
def requireStrictEncoding(flags : Seq[ScriptFlag]) : Boolean = flags.contains(ScriptVerifyStrictEnc)
def requireStrictEncoding(flags: Seq[ScriptFlag]): Boolean = flags.contains(ScriptVerifyStrictEnc)
/**
* Checks if the script flag for checklocktimeverify is enabled
* @param flags
* @return
*/
def checkLockTimeVerifyEnabled(flags : Seq[ScriptFlag]) : Boolean = {
def checkLockTimeVerifyEnabled(flags: Seq[ScriptFlag]): Boolean = {
flags.contains(ScriptVerifyCheckLocktimeVerify)
}
/**
* Checks if the p2sh flag is enabled
* @param flags
* @return
*/
def p2shEnabled(flags : Seq[ScriptFlag]) : Boolean = flags.contains(ScriptVerifyP2SH)
* Checks if the p2sh flag is enabled
* @param flags
* @return
*/
def p2shEnabled(flags: Seq[ScriptFlag]): Boolean = flags.contains(ScriptVerifyP2SH)
/**
* Checks if the script flag for checksequenceverify is enabled
* @param flags
* @return
*/
def checkSequenceVerifyEnabled(flags : Seq[ScriptFlag]) : Boolean = {
* Checks if the script flag for checksequenceverify is enabled
* @param flags
* @return
*/
def checkSequenceVerifyEnabled(flags: Seq[ScriptFlag]): Boolean = {
flags.contains(ScriptVerifyCheckSequenceVerify)
}
@ -55,7 +55,7 @@ trait ScriptFlagUtil {
* @param flags
* @return
*/
def discourageUpgradableNOPs(flags : Seq[ScriptFlag]) : Boolean = {
def discourageUpgradableNOPs(flags: Seq[ScriptFlag]): Boolean = {
flags.contains(ScriptVerifyDiscourageUpgradableNOPs)
}
@ -65,8 +65,7 @@ trait ScriptFlagUtil {
* @param flags
* @return
*/
def requireMinimalData(flags : Seq[ScriptFlag]) : Boolean = flags.contains(ScriptVerifyMinimalData)
def requireMinimalData(flags: Seq[ScriptFlag]): Boolean = flags.contains(ScriptVerifyMinimalData)
/**
* Checks to see if the script flag is set to require low s values in digital signatures
@ -74,24 +73,22 @@ trait ScriptFlagUtil {
* @param flags
* @return
*/
def requireLowSValue(flags : Seq[ScriptFlag]) : Boolean = flags.contains(ScriptVerifyLowS)
def requireLowSValue(flags: Seq[ScriptFlag]): Boolean = flags.contains(ScriptVerifyLowS)
/**
* Checks to see if the script flag is set to require we only have push operations inside of a scriptSig
* @param flags
* @return
*/
def requirePushOnly(flags : Seq[ScriptFlag]) : Boolean = flags.contains(ScriptVerifySigPushOnly)
def requirePushOnly(flags: Seq[ScriptFlag]): Boolean = flags.contains(ScriptVerifySigPushOnly)
/**
* Checks to see if the script flag is set to require that we need a NULLDUMMY to be OP_0 for
* OP_CHECKMULTISIG operations
* @param flags
* @return
*/
def requireNullDummy(flags : Seq[ScriptFlag]) : Boolean = flags.contains(ScriptVerifyNullDummy)
* Checks to see if the script flag is set to require that we need a NULLDUMMY to be OP_0 for
* OP_CHECKMULTISIG operations
* @param flags
* @return
*/
def requireNullDummy(flags: Seq[ScriptFlag]): Boolean = flags.contains(ScriptVerifyNullDummy)
/** Checks to see if we have segwit enabled */
def segWitEnabled(flags: Seq[ScriptFlag]): Boolean = flags.contains(ScriptVerifyWitness)
@ -102,10 +99,11 @@ trait ScriptFlagUtil {
def requireScriptVerifyWitnessPubKeyType(flags: Seq[ScriptFlag]): Boolean = flags.contains(ScriptVerifyWitnessPubKeyType)
/** Requires that the argument to OP_IF/OP_NOTIF be minimally encoded
* See: https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2016-August/013014.html */
/**
* Requires that the argument to OP_IF/OP_NOTIF be minimally encoded
* See: https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2016-August/013014.html
*/
def minimalIfEnabled(flags: Seq[ScriptFlag]): Boolean = flags.contains(ScriptVerifyMinimalIf)
}
object ScriptFlagUtil extends ScriptFlagUtil

View file

@ -8,10 +8,10 @@ package org.bitcoins.core.script.flag
*/
sealed trait ScriptFlag {
/** The flag's representation represented as an integer. */
def flag : Int
def flag: Int
/** The name of the flag as found in bitcoin core. */
def name : String
def name: String
}
case object ScriptVerifyNone extends ScriptFlag {
@ -25,9 +25,11 @@ case object ScriptVerifyP2SH extends ScriptFlag {
override def name = "P2SH"
}
/** Passing a non-strict-DER signature or one with undefined hashtype to a checksig operation causes script failure.
/**
* Passing a non-strict-DER signature or one with undefined hashtype to a checksig operation causes script failure.
* Evaluating a pubkey that is not (0x04 + 64 bytes) or (0x02 or 0x03 + 32 bytes) by checksig causes script failure.
* (softfork safe, but not used or intended as a consensus rule). */
* (softfork safe, but not used or intended as a consensus rule).
*/
case object ScriptVerifyStrictEnc extends ScriptFlag {
override def flag = 1 << 1
override def name = "STRICTENC"
@ -39,8 +41,10 @@ case object ScriptVerifyDerSig extends ScriptFlag {
override def name = "DERSIG"
}
/** Passing a non-strict-DER signature or one with S > order/2 to a checksig operation causes script failure
* (softfork safe, BIP62 rule 5). */
/**
* Passing a non-strict-DER signature or one with S > order/2 to a checksig operation causes script failure
* (softfork safe, BIP62 rule 5).
*/
case object ScriptVerifyLowS extends ScriptFlag {
override def flag = 1 << 3
override def name = "LOW_S"
@ -58,33 +62,39 @@ case object ScriptVerifySigPushOnly extends ScriptFlag {
override def name = "SIGPUSHONLY"
}
/** Require minimal encodings for all push operations (OP_0... OP_16, OP_1NEGATE where possible, direct
/**
* Require minimal encodings for all push operations (OP_0... OP_16, OP_1NEGATE where possible, direct
* pushes up to 75 bytes, OP_PUSHDATA up to 255 bytes, OP_PUSHDATA2 for anything larger). Evaluating
* any other push causes the script to fail (BIP62 rule 3).
* In addition, whenever a stack element is interpreted as a number, it must be of minimal length (BIP62 rule 4).
* (softfork safe). */
* (softfork safe).
*/
case object ScriptVerifyMinimalData extends ScriptFlag {
override def flag = 1 << 6
override def name = "MINIMALDATA"
}
/** Discourage use of NOPs reserved for upgrades (NOP1-10)
/**
* Discourage use of NOPs reserved for upgrades (NOP1-10)
* Provided so that nodes can avoid accepting or mining transactions
* containing executed NOP's whose meaning may change after a soft-fork,
* thus rendering the script invalid; with this flag set executing
* discouraged NOPs fails the script. This verification flag will never be
* a mandatory flag applied to scripts in a block. NOPs that are not
* executed, e.g. within an unexecuted IF ENDIF block, are *not* rejected. */
* executed, e.g. within an unexecuted IF ENDIF block, are *not* rejected.
*/
case object ScriptVerifyDiscourageUpgradableNOPs extends ScriptFlag {
override def flag = 1 << 7
override def name = "DISCOURAGE_UPGRADABLE_NOPS"
}
/** Require that only a single stack element remains after evaluation. This changes the success criterion from
/**
* Require that only a single stack element remains after evaluation. This changes the success criterion from
* "At least one stack element must remain, and when interpreted as a boolean, it must be true" to
* "Exactly one stack element must remain, and when interpreted as a boolean, it must be true".
* (softfork safe, BIP62 rule 6)
* Note: CLEANSTACK should never be used without P2SH. */
* Note: CLEANSTACK should never be used without P2SH.
*/
case object ScriptVerifyCleanStack extends ScriptFlag {
override def flag = 1 << 8
override def name = "CLEANSTACK"
@ -132,4 +142,3 @@ case object ScriptVerifyWitnessPubKeyType extends ScriptFlag {
override def name = "WITNESS_PUBKEYTYPE"
}

View file

@ -2,26 +2,26 @@ package org.bitcoins.core.script.interpreter
import org.bitcoins.core.consensus.Consensus
import org.bitcoins.core.crypto._
import org.bitcoins.core.currency.{CurrencyUnit, CurrencyUnits}
import org.bitcoins.core.currency.{ CurrencyUnit, CurrencyUnits }
import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction.{BaseTransaction, EmptyTransactionOutPoint, Transaction, WitnessTransaction}
import org.bitcoins.core.protocol.transaction.{ BaseTransaction, EmptyTransactionOutPoint, Transaction, WitnessTransaction }
import org.bitcoins.core.script._
import org.bitcoins.core.script.arithmetic._
import org.bitcoins.core.script.bitwise._
import org.bitcoins.core.script.constant.{ScriptToken, _}
import org.bitcoins.core.script.constant.{ ScriptToken, _ }
import org.bitcoins.core.script.control._
import org.bitcoins.core.script.crypto._
import org.bitcoins.core.script.flag._
import org.bitcoins.core.script.locktime.{LockTimeInterpreter, OP_CHECKLOCKTIMEVERIFY, OP_CHECKSEQUENCEVERIFY}
import org.bitcoins.core.script.locktime.{ LockTimeInterpreter, OP_CHECKLOCKTIMEVERIFY, OP_CHECKSEQUENCEVERIFY }
import org.bitcoins.core.script.reserved._
import org.bitcoins.core.script.result._
import org.bitcoins.core.script.splice._
import org.bitcoins.core.script.stack._
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil, BitcoinScriptUtil}
import org.bitcoins.core.util.{ BitcoinSLogger, BitcoinSUtil, BitcoinScriptUtil }
import scala.annotation.tailrec
import scala.util.{Failure, Success, Try}
import scala.util.{ Failure, Success, Try }
/**
* Created by chris on 1/6/16.
@ -42,42 +42,42 @@ sealed abstract class ScriptInterpreter {
* Runs an entire script though our script programming language and
* returns a [[ScriptResult]] indicating if the script was valid, or if not what error it encountered
*/
def run(program : PreExecutionScriptProgram) : ScriptResult = {
def run(program: PreExecutionScriptProgram): ScriptResult = {
val scriptSig = program.txSignatureComponent.scriptSignature
val scriptPubKey = program.txSignatureComponent.scriptPubKey
val flags = program.flags
val p2shEnabled = ScriptFlagUtil.p2shEnabled(flags)
val segwitEnabled = ScriptFlagUtil.segWitEnabled(flags)
val executedProgram : ExecutedScriptProgram = if (ScriptFlagUtil.requirePushOnly(flags)
val executedProgram: ExecutedScriptProgram = if (ScriptFlagUtil.requirePushOnly(flags)
&& !BitcoinScriptUtil.isPushOnly(program.script)) {
logger.error("We can only have push operations inside of the script sig when the SIGPUSHONLY flag is set")
ScriptProgram(program,ScriptErrorSigPushOnly)
ScriptProgram(program, ScriptErrorSigPushOnly)
} else if (scriptSig.isInstanceOf[P2SHScriptSignature] && p2shEnabled &&
!BitcoinScriptUtil.isPushOnly(scriptSig.asm)) {
logger.error("P2SH scriptSigs are required to be push only by definition - see BIP16, got: " + scriptSig.asm)
ScriptProgram(program,ScriptErrorSigPushOnly)
ScriptProgram(program, ScriptErrorSigPushOnly)
} else {
val scriptSigExecutedProgram = loop(program,0)
val scriptSigExecutedProgram = loop(program, 0)
val t = scriptSigExecutedProgram.txSignatureComponent
val scriptPubKeyProgram = ScriptProgram(t, scriptSigExecutedProgram.stack, t.scriptPubKey.asm,
t.scriptPubKey.asm)
val scriptPubKeyExecutedProgram : ExecutedScriptProgram = loop(scriptPubKeyProgram,0)
val scriptPubKeyExecutedProgram: ExecutedScriptProgram = loop(scriptPubKeyProgram, 0)
if (scriptSigExecutedProgram.error.isDefined) {
scriptSigExecutedProgram
} else if (scriptPubKeyExecutedProgram.error.isDefined || scriptPubKeyExecutedProgram.stackTopIsFalse) {
scriptPubKeyExecutedProgram
} else {
scriptPubKey match {
case witness : WitnessScriptPubKey =>
case witness: WitnessScriptPubKey =>
//TODO: remove .get here
if (segwitEnabled) executeSegWitScript(scriptPubKeyExecutedProgram,witness).get
if (segwitEnabled) executeSegWitScript(scriptPubKeyExecutedProgram, witness).get
else scriptPubKeyExecutedProgram
case p2sh : P2SHScriptPubKey =>
case p2sh: P2SHScriptPubKey =>
if (p2shEnabled) executeP2shScript(scriptSigExecutedProgram, program, p2sh)
else scriptPubKeyExecutedProgram
case _: P2PKHScriptPubKey | _: P2PKScriptPubKey | _: MultiSignatureScriptPubKey | _: CSVScriptPubKey
| _: CLTVScriptPubKey | _: NonStandardScriptPubKey | _: WitnessCommitment
| _: EscrowTimeoutScriptPubKey | EmptyScriptPubKey =>
| _: CLTVScriptPubKey | _: NonStandardScriptPubKey | _: WitnessCommitment
| _: EscrowTimeoutScriptPubKey | EmptyScriptPubKey =>
scriptPubKeyExecutedProgram
}
}
@ -89,8 +89,7 @@ sealed abstract class ScriptInterpreter {
//as the 'executedProgram' may have had the scriptPubKey value changed to the rebuilt ScriptPubKey of the witness program
ScriptErrorWitnessUnexpected
}
else if (executedProgram.stackTopIsTrue && flags.contains(ScriptVerifyCleanStack)) {
} else if (executedProgram.stackTopIsTrue && flags.contains(ScriptVerifyCleanStack)) {
//require that the stack after execution has exactly one element on it
if (executedProgram.stack.size == 1) ScriptOk
else ScriptErrorCleanStack
@ -99,35 +98,35 @@ sealed abstract class ScriptInterpreter {
}
/**
* P2SH scripts are unique in their evaluation, first the scriptSignature must be added to the stack, next the
* p2sh scriptPubKey must be run to make sure the serialized redeem script hashes to the value found in the p2sh
* scriptPubKey, then finally the serialized redeemScript is decoded and run with the arguments in the p2sh script signature
* a p2sh script returns true if both of those intermediate steps evaluate to true
*
* @param scriptPubKeyExecutedProgram the program with the script signature pushed onto the stack
* @param originalProgram the original program, used for setting errors & checking that the original script signature contains push only tokens
* @param p2shScriptPubKey the p2sh scriptPubKey that contains the value the redeemScript must hash to
* @return the executed program
*/
private def executeP2shScript(scriptPubKeyExecutedProgram : ExecutedScriptProgram, originalProgram : ScriptProgram, p2shScriptPubKey : P2SHScriptPubKey) : ExecutedScriptProgram = {
* P2SH scripts are unique in their evaluation, first the scriptSignature must be added to the stack, next the
* p2sh scriptPubKey must be run to make sure the serialized redeem script hashes to the value found in the p2sh
* scriptPubKey, then finally the serialized redeemScript is decoded and run with the arguments in the p2sh script signature
* a p2sh script returns true if both of those intermediate steps evaluate to true
*
* @param scriptPubKeyExecutedProgram the program with the script signature pushed onto the stack
* @param originalProgram the original program, used for setting errors & checking that the original script signature contains push only tokens
* @param p2shScriptPubKey the p2sh scriptPubKey that contains the value the redeemScript must hash to
* @return the executed program
*/
private def executeP2shScript(scriptPubKeyExecutedProgram: ExecutedScriptProgram, originalProgram: ScriptProgram, p2shScriptPubKey: P2SHScriptPubKey): ExecutedScriptProgram = {
/** Helper function to actually run a p2sh script */
def run(p: ExecutedScriptProgram, stack : Seq[ScriptToken], s: ScriptPubKey): ExecutedScriptProgram = {
def run(p: ExecutedScriptProgram, stack: Seq[ScriptToken], s: ScriptPubKey): ExecutedScriptProgram = {
logger.debug("Running p2sh script: " + stack)
val p2shRedeemScriptProgram = ScriptProgram(p.txSignatureComponent,stack.tail,
val p2shRedeemScriptProgram = ScriptProgram(p.txSignatureComponent, stack.tail,
s.asm)
if (ScriptFlagUtil.requirePushOnly(p2shRedeemScriptProgram.flags) && !BitcoinScriptUtil.isPushOnly(s.asm)) {
logger.error("p2sh redeem script must be push only operations whe SIGPUSHONLY flag is set")
ScriptProgram(p2shRedeemScriptProgram,ScriptErrorSigPushOnly)
} else loop(p2shRedeemScriptProgram,0)
ScriptProgram(p2shRedeemScriptProgram, ScriptErrorSigPushOnly)
} else loop(p2shRedeemScriptProgram, 0)
}
val scriptSig = scriptPubKeyExecutedProgram.txSignatureComponent.scriptSignature
val scriptSigAsm : Seq[ScriptToken] = scriptSig.asm
val scriptSigAsm: Seq[ScriptToken] = scriptSig.asm
//need to check if the scriptSig is push only as required by bitcoin core
//https://github.com/bitcoin/bitcoin/blob/528472111b4965b1a99c4bcf08ac5ec93d87f10f/src/script/interpreter.cpp#L1419
if (!BitcoinScriptUtil.isPushOnly(scriptSigAsm)) {
ScriptProgram(scriptPubKeyExecutedProgram,ScriptErrorSigPushOnly)
ScriptProgram(scriptPubKeyExecutedProgram, ScriptErrorSigPushOnly)
} else if (scriptPubKeyExecutedProgram.error.isDefined) {
scriptPubKeyExecutedProgram
} else {
@ -140,7 +139,7 @@ sealed abstract class ScriptInterpreter {
val c = CompactSizeUInt.calculateCompactSizeUInt(redeemScriptBytes)
val redeemScript = ScriptPubKey(c.bytes ++ redeemScriptBytes)
redeemScript match {
case w : WitnessScriptPubKey =>
case w: WitnessScriptPubKey =>
val pushOp = BitcoinScriptUtil.calculatePushOp(redeemScriptBytes)
val expectedScriptBytes = pushOp.flatMap(_.bytes) ++ redeemScriptBytes
val flags = scriptPubKeyExecutedProgram.flags
@ -150,7 +149,7 @@ sealed abstract class ScriptInterpreter {
// reintroduce malleability.
logger.debug("redeem script was witness script pubkey, segwit was enabled, scriptSig was single push of redeemScript")
//TODO: remove .get here
executeSegWitScript(scriptPubKeyExecutedProgram,w).get
executeSegWitScript(scriptPubKeyExecutedProgram, w).get
} else if (segwitEnabled && (scriptSig.asmBytes != expectedScriptBytes)) {
logger.error("Segwit was enabled, but p2sh redeem script was malleated")
logger.error("ScriptSig bytes: " + scriptSig.hex)
@ -159,13 +158,13 @@ sealed abstract class ScriptInterpreter {
} else {
logger.warn("redeem script was witness script pubkey, segwit was NOT enabled")
//treat the segwit scriptpubkey as any other redeem script
run(scriptPubKeyExecutedProgram,stack,w)
run(scriptPubKeyExecutedProgram, stack, w)
}
case s @ (_ : P2SHScriptPubKey | _ : P2PKHScriptPubKey | _ : P2PKScriptPubKey | _ : MultiSignatureScriptPubKey
| _ : CLTVScriptPubKey | _ : CSVScriptPubKey | _: NonStandardScriptPubKey | _ : WitnessCommitment
| _: EscrowTimeoutScriptPubKey | EmptyScriptPubKey) =>
case s @ (_: P2SHScriptPubKey | _: P2PKHScriptPubKey | _: P2PKScriptPubKey | _: MultiSignatureScriptPubKey
| _: CLTVScriptPubKey | _: CSVScriptPubKey | _: NonStandardScriptPubKey | _: WitnessCommitment
| _: EscrowTimeoutScriptPubKey | EmptyScriptPubKey) =>
logger.debug("redeemScript: " + s.asm)
run(scriptPubKeyExecutedProgram,stack,s)
run(scriptPubKeyExecutedProgram, stack, s)
}
case false =>
logger.warn("P2SH scriptPubKey hash did not match the hash for the serialized redeemScript")
@ -175,39 +174,38 @@ sealed abstract class ScriptInterpreter {
}
/** Runs a segwit script through our interpreter, mimics this functionality in bitcoin core:
* [[https://github.com/bitcoin/bitcoin/blob/528472111b4965b1a99c4bcf08ac5ec93d87f10f/src/script/interpreter.cpp#L1441-L1452]]
* @param scriptPubKeyExecutedProgram the program with the [[ScriptPubKey]] executed
* @return
*/
/**
* Runs a segwit script through our interpreter, mimics this functionality in bitcoin core:
* [[https://github.com/bitcoin/bitcoin/blob/528472111b4965b1a99c4bcf08ac5ec93d87f10f/src/script/interpreter.cpp#L1441-L1452]]
* @param scriptPubKeyExecutedProgram the program with the [[ScriptPubKey]] executed
* @return
*/
private def executeSegWitScript(scriptPubKeyExecutedProgram: ExecutedScriptProgram, witnessScriptPubKey: WitnessScriptPubKey): Try[ExecutedScriptProgram] = {
scriptPubKeyExecutedProgram.txSignatureComponent match {
case b: BaseTxSigComponent =>
val scriptSig = scriptPubKeyExecutedProgram.txSignatureComponent.scriptSignature
if (scriptSig != EmptyScriptSignature && !b.scriptPubKey.isInstanceOf[P2SHScriptPubKey]) {
Success(ScriptProgram(scriptPubKeyExecutedProgram,ScriptErrorWitnessMalleated))
Success(ScriptProgram(scriptPubKeyExecutedProgram, ScriptErrorWitnessMalleated))
} else {
witnessScriptPubKey.witnessVersion match {
case WitnessVersion0 =>
logger.error("Cannot verify witness program with a BaseTxSigComponent")
Success(ScriptProgram(scriptPubKeyExecutedProgram,ScriptErrorWitnessProgramWitnessEmpty))
Success(ScriptProgram(scriptPubKeyExecutedProgram, ScriptErrorWitnessProgramWitnessEmpty))
case UnassignedWitness(_) =>
evaluateUnassignedWitness(b)
}
}
case w: WitnessTxSigComponent =>
val scriptSig = scriptPubKeyExecutedProgram.txSignatureComponent.scriptSignature
val (witnessVersion,witnessProgram) = (witnessScriptPubKey.witnessVersion, witnessScriptPubKey.witnessProgram)
val (witnessVersion, witnessProgram) = (witnessScriptPubKey.witnessVersion, witnessScriptPubKey.witnessProgram)
val witness = w.witness
//scriptsig must be empty if we have raw p2wsh
//if script pubkey is a P2SHScriptPubKey then we have P2SH(P2WSH)
if (scriptSig != EmptyScriptSignature && !w.scriptPubKey.isInstanceOf[P2SHScriptPubKey]) {
Success(ScriptProgram(scriptPubKeyExecutedProgram,ScriptErrorWitnessMalleated))
}
else if (witness.stack.exists(_.size > maxPushSize)) {
Success(ScriptProgram(scriptPubKeyExecutedProgram, ScriptErrorWitnessMalleated))
} else if (witness.stack.exists(_.size > maxPushSize)) {
Success(ScriptProgram(scriptPubKeyExecutedProgram, ScriptErrorPushSize))
}
else {
} else {
verifyWitnessProgram(witnessVersion, witness, witnessProgram, w)
}
case _: WitnessTxSigComponentRebuilt =>
@ -215,8 +213,10 @@ sealed abstract class ScriptInterpreter {
}
}
/** Verifies a segregated witness program by running it through the interpreter
* [[https://github.com/bitcoin/bitcoin/blob/f8528134fc188abc5c7175a19680206964a8fade/src/script/interpreter.cpp#L1302]]*/
/**
* Verifies a segregated witness program by running it through the interpreter
* [[https://github.com/bitcoin/bitcoin/blob/f8528134fc188abc5c7175a19680206964a8fade/src/script/interpreter.cpp#L1302]]
*/
private def verifyWitnessProgram(witnessVersion: WitnessVersion, scriptWitness: ScriptWitness, witnessProgram: Seq[ScriptToken],
wTxSigComponent: WitnessTxSigComponent): Try[ExecutedScriptProgram] = {
@ -224,22 +224,22 @@ sealed abstract class ScriptInterpreter {
def postSegWitProgramChecks(evaluated: ExecutedScriptProgram): ExecutedScriptProgram = {
logger.debug("Stack after evaluating witness: " + evaluated.stack)
if (evaluated.error.isDefined) evaluated
else if (evaluated.stack.size != 1 || evaluated.stackTopIsFalse) ScriptProgram(evaluated,ScriptErrorEvalFalse)
else if (evaluated.stack.size != 1 || evaluated.stackTopIsFalse) ScriptProgram(evaluated, ScriptErrorEvalFalse)
else evaluated
}
witnessVersion match {
case WitnessVersion0 =>
val either: Either[(Seq[ScriptToken], ScriptPubKey),ScriptError] = witnessVersion.rebuild(scriptWitness, witnessProgram)
val either: Either[(Seq[ScriptToken], ScriptPubKey), ScriptError] = witnessVersion.rebuild(scriptWitness, witnessProgram)
either match {
case Left((stack,scriptPubKey)) =>
val newWTxSigComponent = rebuildWTxSigComponent(wTxSigComponent,scriptPubKey)
val newProgram = newWTxSigComponent.map(comp => ScriptProgram(comp,stack,scriptPubKey.asm, scriptPubKey.asm,Nil))
val evaluated = newProgram.map(p => loop(p,0))
case Left((stack, scriptPubKey)) =>
val newWTxSigComponent = rebuildWTxSigComponent(wTxSigComponent, scriptPubKey)
val newProgram = newWTxSigComponent.map(comp => ScriptProgram(comp, stack, scriptPubKey.asm, scriptPubKey.asm, Nil))
val evaluated = newProgram.map(p => loop(p, 0))
evaluated.map(e => postSegWitProgramChecks(e))
case Right(err) =>
val program = ScriptProgram(wTxSigComponent,Nil,Nil,Nil)
Success(ScriptProgram(program,err))
val program = ScriptProgram(wTxSigComponent, Nil, Nil, Nil)
Success(ScriptProgram(program, err))
}
case UnassignedWitness(_) =>
evaluateUnassignedWitness(wTxSigComponent)
@ -247,234 +247,232 @@ sealed abstract class ScriptInterpreter {
}
/**
* The execution loop for a script
*
* @param program the program whose script needs to be evaluated
* @return program the final state of the program after being evaluated by the interpreter
*/
* The execution loop for a script
*
* @param program the program whose script needs to be evaluated
* @return program the final state of the program after being evaluated by the interpreter
*/
@tailrec
private def loop(program : ScriptProgram, opCount: Int) : ExecutedScriptProgram = {
private def loop(program: ScriptProgram, opCount: Int): ExecutedScriptProgram = {
logger.debug("Stack: " + program.stack)
logger.debug("Script: " + program.script)
if (opCount > maxScriptOps && !program.isInstanceOf[ExecutedScriptProgram]) {
logger.error("We have reached the maximum amount of script operations allowed")
logger.error("Here are the remaining operations in the script: " + program.script)
loop(ScriptProgram(program,ScriptErrorOpCount),opCount)
loop(ScriptProgram(program, ScriptErrorOpCount), opCount)
} else if (program.script.flatMap(_.bytes).size > 10000 && !program.isInstanceOf[ExecutedScriptProgram]) {
logger.error("We cannot run a script that is larger than 10,000 bytes")
program match {
case p : PreExecutionScriptProgram =>
loop(ScriptProgram(ScriptProgram.toExecutionInProgress(p), ScriptErrorScriptSize),opCount)
case _ : ExecutionInProgressScriptProgram | _ : ExecutedScriptProgram =>
loop(ScriptProgram(program, ScriptErrorScriptSize),opCount)
case p: PreExecutionScriptProgram =>
loop(ScriptProgram(ScriptProgram.toExecutionInProgress(p), ScriptErrorScriptSize), opCount)
case _: ExecutionInProgressScriptProgram | _: ExecutedScriptProgram =>
loop(ScriptProgram(program, ScriptErrorScriptSize), opCount)
}
} else {
program match {
case p : PreExecutionScriptProgram => loop(ScriptProgram.toExecutionInProgress(p,Some(p.stack)),opCount)
case p : ExecutedScriptProgram =>
case p: PreExecutionScriptProgram => loop(ScriptProgram.toExecutionInProgress(p, Some(p.stack)), opCount)
case p: ExecutedScriptProgram =>
val countedOps = program.originalScript.map(BitcoinScriptUtil.countsTowardsScriptOpLimit(_)).count(_ == true)
logger.debug("Counted ops: " + countedOps)
if (countedOps > maxScriptOps && p.error.isEmpty) {
loop(ScriptProgram(p,ScriptErrorOpCount),opCount)
loop(ScriptProgram(p, ScriptErrorOpCount), opCount)
} else p
case p : ExecutionInProgressScriptProgram =>
case p: ExecutionInProgressScriptProgram =>
p.script match {
//if at any time we see that the program is not valid
//cease script execution
case _ if p.script.intersect(Seq(OP_VERIF, OP_VERNOTIF)).nonEmpty =>
logger.error("Script is invalid even when a OP_VERIF or OP_VERNOTIF occurs in an unexecuted OP_IF branch")
loop(ScriptProgram(p, ScriptErrorBadOpCode),opCount)
loop(ScriptProgram(p, ScriptErrorBadOpCode), opCount)
//disabled splice operation
case _ if p.script.intersect(Seq(OP_CAT, OP_SUBSTR, OP_LEFT, OP_RIGHT)).nonEmpty =>
logger.error("Script is invalid because it contains a disabled splice operation")
loop(ScriptProgram(p, ScriptErrorDisabledOpCode),opCount)
loop(ScriptProgram(p, ScriptErrorDisabledOpCode), opCount)
//disabled bitwise operations
case _ if p.script.intersect(Seq(OP_INVERT, OP_AND, OP_OR, OP_XOR)).nonEmpty =>
logger.error("Script is invalid because it contains a disabled bitwise operation")
loop(ScriptProgram(p, ScriptErrorDisabledOpCode),opCount)
loop(ScriptProgram(p, ScriptErrorDisabledOpCode), opCount)
//disabled arithmetic operations
case _ if p.script.intersect(Seq(OP_MUL, OP_2MUL, OP_DIV, OP_2DIV, OP_MOD, OP_LSHIFT, OP_RSHIFT)).nonEmpty =>
logger.error("Script is invalid because it contains a disabled arithmetic operation")
loop(ScriptProgram(p, ScriptErrorDisabledOpCode),opCount)
loop(ScriptProgram(p, ScriptErrorDisabledOpCode), opCount)
//program cannot contain a push operation > 520 bytes
case _ if (p.script.exists(token => token.bytes.size > maxPushSize)) =>
logger.error("We have a script constant that is larger than 520 bytes, this is illegal: " + p.script)
loop(ScriptProgram(p, ScriptErrorPushSize),opCount)
loop(ScriptProgram(p, ScriptErrorPushSize), opCount)
//program stack size cannot be greater than 1000 elements
case _ if ((p.stack.size + p.altStack.size) > 1000) =>
logger.error("We cannot have a stack + alt stack size larger than 1000 elements")
loop(ScriptProgram(p, ScriptErrorStackSize),opCount)
loop(ScriptProgram(p, ScriptErrorStackSize), opCount)
//stack operations
case OP_DUP :: t => loop(StackInterpreter.opDup(p),calcOpCount(opCount,OP_DUP))
case OP_DEPTH :: t => loop(StackInterpreter.opDepth(p),calcOpCount(opCount,OP_DEPTH))
case OP_TOALTSTACK :: t => loop(StackInterpreter.opToAltStack(p),calcOpCount(opCount,OP_TOALTSTACK))
case OP_FROMALTSTACK :: t => loop(StackInterpreter.opFromAltStack(p),calcOpCount(opCount,OP_FROMALTSTACK))
case OP_DROP :: t => loop(StackInterpreter.opDrop(p),calcOpCount(opCount,OP_DROP))
case OP_IFDUP :: t => loop(StackInterpreter.opIfDup(p),calcOpCount(opCount,OP_IFDUP))
case OP_NIP :: t => loop(StackInterpreter.opNip(p),calcOpCount(opCount,OP_NIP))
case OP_OVER :: t => loop(StackInterpreter.opOver(p),calcOpCount(opCount,OP_OVER))
case OP_PICK :: t => loop(StackInterpreter.opPick(p),calcOpCount(opCount,OP_PICK))
case OP_ROLL :: t => loop(StackInterpreter.opRoll(p),calcOpCount(opCount,OP_ROLL))
case OP_ROT :: t => loop(StackInterpreter.opRot(p),calcOpCount(opCount,OP_ROT))
case OP_2ROT :: t => loop(StackInterpreter.op2Rot(p),calcOpCount(opCount,OP_2ROT))
case OP_2DROP :: t => loop(StackInterpreter.op2Drop(p),calcOpCount(opCount,OP_2DROP))
case OP_SWAP :: t => loop(StackInterpreter.opSwap(p),calcOpCount(opCount,OP_SWAP))
case OP_TUCK :: t => loop(StackInterpreter.opTuck(p),calcOpCount(opCount,OP_TUCK))
case OP_2DUP :: t => loop(StackInterpreter.op2Dup(p),calcOpCount(opCount,OP_2DUP))
case OP_3DUP :: t => loop(StackInterpreter.op3Dup(p),calcOpCount(opCount,OP_3DUP))
case OP_2OVER :: t => loop(StackInterpreter.op2Over(p),calcOpCount(opCount,OP_2OVER))
case OP_2SWAP :: t => loop(StackInterpreter.op2Swap(p),calcOpCount(opCount,OP_2SWAP))
case OP_DUP :: t => loop(StackInterpreter.opDup(p), calcOpCount(opCount, OP_DUP))
case OP_DEPTH :: t => loop(StackInterpreter.opDepth(p), calcOpCount(opCount, OP_DEPTH))
case OP_TOALTSTACK :: t => loop(StackInterpreter.opToAltStack(p), calcOpCount(opCount, OP_TOALTSTACK))
case OP_FROMALTSTACK :: t => loop(StackInterpreter.opFromAltStack(p), calcOpCount(opCount, OP_FROMALTSTACK))
case OP_DROP :: t => loop(StackInterpreter.opDrop(p), calcOpCount(opCount, OP_DROP))
case OP_IFDUP :: t => loop(StackInterpreter.opIfDup(p), calcOpCount(opCount, OP_IFDUP))
case OP_NIP :: t => loop(StackInterpreter.opNip(p), calcOpCount(opCount, OP_NIP))
case OP_OVER :: t => loop(StackInterpreter.opOver(p), calcOpCount(opCount, OP_OVER))
case OP_PICK :: t => loop(StackInterpreter.opPick(p), calcOpCount(opCount, OP_PICK))
case OP_ROLL :: t => loop(StackInterpreter.opRoll(p), calcOpCount(opCount, OP_ROLL))
case OP_ROT :: t => loop(StackInterpreter.opRot(p), calcOpCount(opCount, OP_ROT))
case OP_2ROT :: t => loop(StackInterpreter.op2Rot(p), calcOpCount(opCount, OP_2ROT))
case OP_2DROP :: t => loop(StackInterpreter.op2Drop(p), calcOpCount(opCount, OP_2DROP))
case OP_SWAP :: t => loop(StackInterpreter.opSwap(p), calcOpCount(opCount, OP_SWAP))
case OP_TUCK :: t => loop(StackInterpreter.opTuck(p), calcOpCount(opCount, OP_TUCK))
case OP_2DUP :: t => loop(StackInterpreter.op2Dup(p), calcOpCount(opCount, OP_2DUP))
case OP_3DUP :: t => loop(StackInterpreter.op3Dup(p), calcOpCount(opCount, OP_3DUP))
case OP_2OVER :: t => loop(StackInterpreter.op2Over(p), calcOpCount(opCount, OP_2OVER))
case OP_2SWAP :: t => loop(StackInterpreter.op2Swap(p), calcOpCount(opCount, OP_2SWAP))
//arithmetic operations
case OP_ADD :: t => loop(ArithmeticInterpreter.opAdd(p),calcOpCount(opCount,OP_ADD))
case OP_1ADD :: t => loop(ArithmeticInterpreter.op1Add(p),calcOpCount(opCount,OP_1ADD))
case OP_1SUB :: t => loop(ArithmeticInterpreter.op1Sub(p),calcOpCount(opCount,OP_1SUB))
case OP_SUB :: t => loop(ArithmeticInterpreter.opSub(p),calcOpCount(opCount,OP_SUB))
case OP_ABS :: t => loop(ArithmeticInterpreter.opAbs(p),calcOpCount(opCount,OP_ABS))
case OP_NEGATE :: t => loop(ArithmeticInterpreter.opNegate(p),calcOpCount(opCount,OP_NEGATE))
case OP_NOT :: t => loop(ArithmeticInterpreter.opNot(p),calcOpCount(opCount,OP_NOT))
case OP_0NOTEQUAL :: t => loop(ArithmeticInterpreter.op0NotEqual(p),calcOpCount(opCount,OP_0NOTEQUAL))
case OP_BOOLAND :: t => loop(ArithmeticInterpreter.opBoolAnd(p),calcOpCount(opCount,OP_BOOLAND))
case OP_BOOLOR :: t => loop(ArithmeticInterpreter.opBoolOr(p),calcOpCount(opCount,OP_BOOLOR))
case OP_NUMEQUAL :: t => loop(ArithmeticInterpreter.opNumEqual(p),calcOpCount(opCount,OP_NUMEQUAL))
case OP_NUMEQUALVERIFY :: t => loop(ArithmeticInterpreter.opNumEqualVerify(p),calcOpCount(opCount,OP_NUMEQUALVERIFY))
case OP_NUMNOTEQUAL :: t => loop(ArithmeticInterpreter.opNumNotEqual(p),calcOpCount(opCount,OP_NUMNOTEQUAL))
case OP_LESSTHAN :: t => loop(ArithmeticInterpreter.opLessThan(p),calcOpCount(opCount,OP_LESSTHAN))
case OP_GREATERTHAN :: t => loop(ArithmeticInterpreter.opGreaterThan(p),calcOpCount(opCount,OP_GREATERTHAN))
case OP_LESSTHANOREQUAL :: t => loop(ArithmeticInterpreter.opLessThanOrEqual(p),calcOpCount(opCount,OP_LESSTHANOREQUAL))
case OP_GREATERTHANOREQUAL :: t => loop(ArithmeticInterpreter.opGreaterThanOrEqual(p),calcOpCount(opCount,OP_GREATERTHANOREQUAL))
case OP_MIN :: t => loop(ArithmeticInterpreter.opMin(p),calcOpCount(opCount,OP_MIN))
case OP_MAX :: t => loop(ArithmeticInterpreter.opMax(p),calcOpCount(opCount,OP_MAX))
case OP_WITHIN :: t => loop(ArithmeticInterpreter.opWithin(p),calcOpCount(opCount,OP_WITHIN))
case OP_ADD :: t => loop(ArithmeticInterpreter.opAdd(p), calcOpCount(opCount, OP_ADD))
case OP_1ADD :: t => loop(ArithmeticInterpreter.op1Add(p), calcOpCount(opCount, OP_1ADD))
case OP_1SUB :: t => loop(ArithmeticInterpreter.op1Sub(p), calcOpCount(opCount, OP_1SUB))
case OP_SUB :: t => loop(ArithmeticInterpreter.opSub(p), calcOpCount(opCount, OP_SUB))
case OP_ABS :: t => loop(ArithmeticInterpreter.opAbs(p), calcOpCount(opCount, OP_ABS))
case OP_NEGATE :: t => loop(ArithmeticInterpreter.opNegate(p), calcOpCount(opCount, OP_NEGATE))
case OP_NOT :: t => loop(ArithmeticInterpreter.opNot(p), calcOpCount(opCount, OP_NOT))
case OP_0NOTEQUAL :: t => loop(ArithmeticInterpreter.op0NotEqual(p), calcOpCount(opCount, OP_0NOTEQUAL))
case OP_BOOLAND :: t => loop(ArithmeticInterpreter.opBoolAnd(p), calcOpCount(opCount, OP_BOOLAND))
case OP_BOOLOR :: t => loop(ArithmeticInterpreter.opBoolOr(p), calcOpCount(opCount, OP_BOOLOR))
case OP_NUMEQUAL :: t => loop(ArithmeticInterpreter.opNumEqual(p), calcOpCount(opCount, OP_NUMEQUAL))
case OP_NUMEQUALVERIFY :: t => loop(ArithmeticInterpreter.opNumEqualVerify(p), calcOpCount(opCount, OP_NUMEQUALVERIFY))
case OP_NUMNOTEQUAL :: t => loop(ArithmeticInterpreter.opNumNotEqual(p), calcOpCount(opCount, OP_NUMNOTEQUAL))
case OP_LESSTHAN :: t => loop(ArithmeticInterpreter.opLessThan(p), calcOpCount(opCount, OP_LESSTHAN))
case OP_GREATERTHAN :: t => loop(ArithmeticInterpreter.opGreaterThan(p), calcOpCount(opCount, OP_GREATERTHAN))
case OP_LESSTHANOREQUAL :: t => loop(ArithmeticInterpreter.opLessThanOrEqual(p), calcOpCount(opCount, OP_LESSTHANOREQUAL))
case OP_GREATERTHANOREQUAL :: t => loop(ArithmeticInterpreter.opGreaterThanOrEqual(p), calcOpCount(opCount, OP_GREATERTHANOREQUAL))
case OP_MIN :: t => loop(ArithmeticInterpreter.opMin(p), calcOpCount(opCount, OP_MIN))
case OP_MAX :: t => loop(ArithmeticInterpreter.opMax(p), calcOpCount(opCount, OP_MAX))
case OP_WITHIN :: t => loop(ArithmeticInterpreter.opWithin(p), calcOpCount(opCount, OP_WITHIN))
//bitwise operations
case OP_EQUAL :: t => loop(BitwiseInterpreter.opEqual(p),calcOpCount(opCount,OP_EQUAL))
case OP_EQUAL :: t => loop(BitwiseInterpreter.opEqual(p), calcOpCount(opCount, OP_EQUAL))
case OP_EQUALVERIFY :: t => loop(BitwiseInterpreter.opEqualVerify(p),calcOpCount(opCount,OP_EQUALVERIFY))
case OP_EQUALVERIFY :: t => loop(BitwiseInterpreter.opEqualVerify(p), calcOpCount(opCount, OP_EQUALVERIFY))
case OP_0 :: t => loop(ScriptProgram(p, ScriptNumber.zero :: p.stack, t),calcOpCount(opCount,OP_0))
case (scriptNumberOp : ScriptNumberOperation) :: t =>
loop(ScriptProgram(p, ScriptNumber(scriptNumberOp.toLong) :: p.stack, t),calcOpCount(opCount,scriptNumberOp))
case OP_0 :: t => loop(ScriptProgram(p, ScriptNumber.zero :: p.stack, t), calcOpCount(opCount, OP_0))
case (scriptNumberOp: ScriptNumberOperation) :: t =>
loop(ScriptProgram(p, ScriptNumber(scriptNumberOp.toLong) :: p.stack, t), calcOpCount(opCount, scriptNumberOp))
case (bytesToPushOntoStack: BytesToPushOntoStack) :: t =>
loop(ConstantInterpreter.pushScriptNumberBytesToStack(p),calcOpCount(opCount,bytesToPushOntoStack))
loop(ConstantInterpreter.pushScriptNumberBytesToStack(p), calcOpCount(opCount, bytesToPushOntoStack))
case (scriptNumber: ScriptNumber) :: t =>
loop(ScriptProgram(p, scriptNumber :: p.stack, t),calcOpCount(opCount,scriptNumber))
case OP_PUSHDATA1 :: t => loop(ConstantInterpreter.opPushData1(p),calcOpCount(opCount,OP_PUSHDATA1))
case OP_PUSHDATA2 :: t => loop(ConstantInterpreter.opPushData2(p),calcOpCount(opCount,OP_PUSHDATA2))
case OP_PUSHDATA4 :: t => loop(ConstantInterpreter.opPushData4(p),calcOpCount(opCount,OP_PUSHDATA4))
loop(ScriptProgram(p, scriptNumber :: p.stack, t), calcOpCount(opCount, scriptNumber))
case OP_PUSHDATA1 :: t => loop(ConstantInterpreter.opPushData1(p), calcOpCount(opCount, OP_PUSHDATA1))
case OP_PUSHDATA2 :: t => loop(ConstantInterpreter.opPushData2(p), calcOpCount(opCount, OP_PUSHDATA2))
case OP_PUSHDATA4 :: t => loop(ConstantInterpreter.opPushData4(p), calcOpCount(opCount, OP_PUSHDATA4))
case (x : ScriptConstant) :: t => loop(ScriptProgram(p, x :: p.stack, t),calcOpCount(opCount,x))
case (x: ScriptConstant) :: t => loop(ScriptProgram(p, x :: p.stack, t), calcOpCount(opCount, x))
//control operations
case OP_IF :: t => loop(ControlOperationsInterpreter.opIf(p),calcOpCount(opCount,OP_IF))
case OP_NOTIF :: t => loop(ControlOperationsInterpreter.opNotIf(p),calcOpCount(opCount,OP_NOTIF))
case OP_ELSE :: t => loop(ControlOperationsInterpreter.opElse(p),calcOpCount(opCount,OP_ELSE))
case OP_ENDIF :: t => loop(ControlOperationsInterpreter.opEndIf(p),calcOpCount(opCount,OP_ENDIF))
case OP_RETURN :: t => loop(ControlOperationsInterpreter.opReturn(p),calcOpCount(opCount,OP_RETURN))
case OP_IF :: t => loop(ControlOperationsInterpreter.opIf(p), calcOpCount(opCount, OP_IF))
case OP_NOTIF :: t => loop(ControlOperationsInterpreter.opNotIf(p), calcOpCount(opCount, OP_NOTIF))
case OP_ELSE :: t => loop(ControlOperationsInterpreter.opElse(p), calcOpCount(opCount, OP_ELSE))
case OP_ENDIF :: t => loop(ControlOperationsInterpreter.opEndIf(p), calcOpCount(opCount, OP_ENDIF))
case OP_RETURN :: t => loop(ControlOperationsInterpreter.opReturn(p), calcOpCount(opCount, OP_RETURN))
case OP_VERIFY :: t => loop(ControlOperationsInterpreter.opVerify(p),calcOpCount(opCount,OP_VERIFY))
case OP_VERIFY :: t => loop(ControlOperationsInterpreter.opVerify(p), calcOpCount(opCount, OP_VERIFY))
//crypto operations
case OP_HASH160 :: t => loop(CryptoInterpreter.opHash160(p),calcOpCount(opCount,OP_HASH160))
case OP_CHECKSIG :: t => loop(CryptoInterpreter.opCheckSig(p),calcOpCount(opCount,OP_CHECKSIG))
case OP_CHECKSIGVERIFY :: t => loop(CryptoInterpreter.opCheckSigVerify(p),calcOpCount(opCount,OP_CHECKSIGVERIFY))
case OP_SHA1 :: t => loop(CryptoInterpreter.opSha1(p),calcOpCount(opCount,OP_SHA1))
case OP_RIPEMD160 :: t => loop(CryptoInterpreter.opRipeMd160(p),calcOpCount(opCount,OP_RIPEMD160))
case OP_SHA256 :: t => loop(CryptoInterpreter.opSha256(p),calcOpCount(opCount,OP_SHA256))
case OP_HASH256 :: t => loop(CryptoInterpreter.opHash256(p),calcOpCount(opCount,OP_HASH256))
case OP_CODESEPARATOR :: t => loop(CryptoInterpreter.opCodeSeparator(p), calcOpCount(opCount,OP_CODESEPARATOR))
case OP_HASH160 :: t => loop(CryptoInterpreter.opHash160(p), calcOpCount(opCount, OP_HASH160))
case OP_CHECKSIG :: t => loop(CryptoInterpreter.opCheckSig(p), calcOpCount(opCount, OP_CHECKSIG))
case OP_CHECKSIGVERIFY :: t => loop(CryptoInterpreter.opCheckSigVerify(p), calcOpCount(opCount, OP_CHECKSIGVERIFY))
case OP_SHA1 :: t => loop(CryptoInterpreter.opSha1(p), calcOpCount(opCount, OP_SHA1))
case OP_RIPEMD160 :: t => loop(CryptoInterpreter.opRipeMd160(p), calcOpCount(opCount, OP_RIPEMD160))
case OP_SHA256 :: t => loop(CryptoInterpreter.opSha256(p), calcOpCount(opCount, OP_SHA256))
case OP_HASH256 :: t => loop(CryptoInterpreter.opHash256(p), calcOpCount(opCount, OP_HASH256))
case OP_CODESEPARATOR :: t => loop(CryptoInterpreter.opCodeSeparator(p), calcOpCount(opCount, OP_CODESEPARATOR))
case OP_CHECKMULTISIG :: t =>
CryptoInterpreter.opCheckMultiSig(p) match {
case newProgram : ExecutedScriptProgram =>
case newProgram: ExecutedScriptProgram =>
//script was marked invalid for other reasons, don't need to update the opcount
loop(newProgram,opCount)
case newProgram @ (_ : ExecutionInProgressScriptProgram | _ : PreExecutionScriptProgram) =>
val newOpCount = calcOpCount(opCount,OP_CHECKMULTISIG) + BitcoinScriptUtil.numPossibleSignaturesOnStack(program).toInt
loop(newProgram,newOpCount)
loop(newProgram, opCount)
case newProgram @ (_: ExecutionInProgressScriptProgram | _: PreExecutionScriptProgram) =>
val newOpCount = calcOpCount(opCount, OP_CHECKMULTISIG) + BitcoinScriptUtil.numPossibleSignaturesOnStack(program).toInt
loop(newProgram, newOpCount)
}
case OP_CHECKMULTISIGVERIFY :: t =>
CryptoInterpreter.opCheckMultiSigVerify(p) match {
case newProgram : ExecutedScriptProgram =>
case newProgram: ExecutedScriptProgram =>
//script was marked invalid for other reasons, don't need to update the opcount
loop(newProgram,opCount)
case newProgram @ (_ : ExecutionInProgressScriptProgram | _ : PreExecutionScriptProgram) =>
val newOpCount = calcOpCount(opCount,OP_CHECKMULTISIGVERIFY) + BitcoinScriptUtil.numPossibleSignaturesOnStack(program).toInt
loop(newProgram,newOpCount)
loop(newProgram, opCount)
case newProgram @ (_: ExecutionInProgressScriptProgram | _: PreExecutionScriptProgram) =>
val newOpCount = calcOpCount(opCount, OP_CHECKMULTISIGVERIFY) + BitcoinScriptUtil.numPossibleSignaturesOnStack(program).toInt
loop(newProgram, newOpCount)
}
//reserved operations
case OP_NOP :: t =>
//script discourage upgradeable flag does not apply to a OP_NOP
loop(ScriptProgram(p, p.stack, t),calcOpCount(opCount,OP_NOP))
loop(ScriptProgram(p, p.stack, t), calcOpCount(opCount, OP_NOP))
//if we see an OP_NOP and the DISCOURAGE_UPGRADABLE_OP_NOPS flag is set we must fail our program
case (nop: NOP) :: t if ScriptFlagUtil.discourageUpgradableNOPs(p.flags) =>
logger.error("We cannot execute a NOP when the ScriptVerifyDiscourageUpgradableNOPs is set")
loop(ScriptProgram(p, ScriptErrorDiscourageUpgradableNOPs),calcOpCount(opCount,nop))
case (nop: NOP) :: t => loop(ScriptProgram(p, p.stack, t),calcOpCount(opCount,nop))
loop(ScriptProgram(p, ScriptErrorDiscourageUpgradableNOPs), calcOpCount(opCount, nop))
case (nop: NOP) :: t => loop(ScriptProgram(p, p.stack, t), calcOpCount(opCount, nop))
case OP_RESERVED :: t =>
logger.error("OP_RESERVED automatically marks transaction invalid")
loop(ScriptProgram(p,ScriptErrorBadOpCode),calcOpCount(opCount,OP_RESERVED))
loop(ScriptProgram(p, ScriptErrorBadOpCode), calcOpCount(opCount, OP_RESERVED))
case OP_VER :: t =>
logger.error("Transaction is invalid when executing OP_VER")
loop(ScriptProgram(p,ScriptErrorBadOpCode),calcOpCount(opCount,OP_VER))
loop(ScriptProgram(p, ScriptErrorBadOpCode), calcOpCount(opCount, OP_VER))
case OP_RESERVED1 :: t =>
logger.error("Transaction is invalid when executing OP_RESERVED1")
loop(ScriptProgram(p,ScriptErrorBadOpCode),calcOpCount(opCount,OP_RESERVED1))
loop(ScriptProgram(p, ScriptErrorBadOpCode), calcOpCount(opCount, OP_RESERVED1))
case OP_RESERVED2 :: t =>
logger.error("Transaction is invalid when executing OP_RESERVED2")
loop(ScriptProgram(p,ScriptErrorBadOpCode),calcOpCount(opCount,OP_RESERVED2))
loop(ScriptProgram(p, ScriptErrorBadOpCode), calcOpCount(opCount, OP_RESERVED2))
case (reservedOperation : ReservedOperation) :: t =>
case (reservedOperation: ReservedOperation) :: t =>
logger.error("Undefined operation found which automatically fails the script: " + reservedOperation)
loop(ScriptProgram(p,ScriptErrorBadOpCode),calcOpCount(opCount,reservedOperation))
loop(ScriptProgram(p, ScriptErrorBadOpCode), calcOpCount(opCount, reservedOperation))
//splice operations
case OP_SIZE :: t => loop(SpliceInterpreter.opSize(p),calcOpCount(opCount,OP_SIZE))
case OP_SIZE :: t => loop(SpliceInterpreter.opSize(p), calcOpCount(opCount, OP_SIZE))
//locktime operations
case OP_CHECKLOCKTIMEVERIFY :: t =>
//check if CLTV is enforced yet
if (ScriptFlagUtil.checkLockTimeVerifyEnabled(p.flags)) {
loop(LockTimeInterpreter.opCheckLockTimeVerify(p),calcOpCount(opCount,OP_CHECKLOCKTIMEVERIFY))
}
//if not, check to see if we should discourage p
loop(LockTimeInterpreter.opCheckLockTimeVerify(p), calcOpCount(opCount, OP_CHECKLOCKTIMEVERIFY))
} //if not, check to see if we should discourage p
else if (ScriptFlagUtil.discourageUpgradableNOPs(p.flags)) {
logger.error("We cannot execute a NOP when the ScriptVerifyDiscourageUpgradableNOPs is set")
loop(ScriptProgram(p, ScriptErrorDiscourageUpgradableNOPs),calcOpCount(opCount,OP_CHECKLOCKTIMEVERIFY))
}
//in this case, just reat OP_CLTV just like a NOP and remove it from the stack
else loop(ScriptProgram(p, p.script.tail, ScriptProgram.Script),calcOpCount(opCount,OP_CHECKLOCKTIMEVERIFY))
loop(ScriptProgram(p, ScriptErrorDiscourageUpgradableNOPs), calcOpCount(opCount, OP_CHECKLOCKTIMEVERIFY))
} //in this case, just reat OP_CLTV just like a NOP and remove it from the stack
else loop(ScriptProgram(p, p.script.tail, ScriptProgram.Script), calcOpCount(opCount, OP_CHECKLOCKTIMEVERIFY))
case OP_CHECKSEQUENCEVERIFY :: t =>
//check if CLTV is enforced yet
if (ScriptFlagUtil.checkSequenceVerifyEnabled(p.flags)) {
loop(LockTimeInterpreter.opCheckSequenceVerify(p),calcOpCount(opCount,OP_CHECKSEQUENCEVERIFY))
}
//if not, check to see if we should discourage p
loop(LockTimeInterpreter.opCheckSequenceVerify(p), calcOpCount(opCount, OP_CHECKSEQUENCEVERIFY))
} //if not, check to see if we should discourage p
else if (ScriptFlagUtil.discourageUpgradableNOPs(p.flags)) {
logger.error("We cannot execute a NOP when the ScriptVerifyDiscourageUpgradableNOPs is set")
loop(ScriptProgram(p, ScriptErrorDiscourageUpgradableNOPs),calcOpCount(opCount,OP_CHECKSEQUENCEVERIFY))
}
//in this case, just read OP_CSV just like a NOP and remove it from the stack
else loop(ScriptProgram(p, p.script.tail, ScriptProgram.Script),calcOpCount(opCount,OP_CHECKSEQUENCEVERIFY))
loop(ScriptProgram(p, ScriptErrorDiscourageUpgradableNOPs), calcOpCount(opCount, OP_CHECKSEQUENCEVERIFY))
} //in this case, just read OP_CSV just like a NOP and remove it from the stack
else loop(ScriptProgram(p, p.script.tail, ScriptProgram.Script), calcOpCount(opCount, OP_CHECKSEQUENCEVERIFY))
//no more script operations to run, return whether the program is valid and the final state of the program
case Nil => loop(ScriptProgram.toExecutedProgram(p),opCount)
case Nil => loop(ScriptProgram.toExecutedProgram(p), opCount)
case h :: t => throw new RuntimeException(h + " was unmatched")
}
}
}
}
/** Checks the validity of a transaction in accordance to bitcoin core's CheckTransaction function
* https://github.com/bitcoin/bitcoin/blob/f7a21dae5dbf71d5bc00485215e84e6f2b309d0a/src/main.cpp#L939. */
def checkTransaction(transaction : Transaction) : Boolean = {
/**
* Checks the validity of a transaction in accordance to bitcoin core's CheckTransaction function
* https://github.com/bitcoin/bitcoin/blob/f7a21dae5dbf71d5bc00485215e84e6f2b309d0a/src/main.cpp#L939.
*/
def checkTransaction(transaction: Transaction): Boolean = {
val inputOutputsNotZero = !(transaction.inputs.isEmpty || transaction.outputs.isEmpty)
val txNotLargerThanBlock = transaction.bytes.size < Consensus.maxBlockSize
val outputsSpendValidAmountsOfMoney = !transaction.outputs.exists(o =>
o.value < CurrencyUnits.zero || o.value > Consensus.maxMoney)
val outputValues = transaction.outputs.map(_.value)
val totalSpentByOutputs : CurrencyUnit = outputValues.fold(CurrencyUnits.zero)(_ + _)
val totalSpentByOutputs: CurrencyUnit = outputValues.fold(CurrencyUnits.zero)(_ + _)
val allOutputsValidMoneyRange = validMoneyRange(totalSpentByOutputs)
val prevOutputTxIds = transaction.inputs.map(_.previousOutput.txId)
val noDuplicateInputs = prevOutputTxIds.distinct.size == prevOutputTxIds.size
@ -491,28 +489,30 @@ sealed abstract class ScriptInterpreter {
}
/** Determines if the given currency unit is within the valid range for the system */
def validMoneyRange(currencyUnit : CurrencyUnit) : Boolean = {
def validMoneyRange(currencyUnit: CurrencyUnit): Boolean = {
currencyUnit >= CurrencyUnits.zero && currencyUnit <= Consensus.maxMoney
}
/** Calculates the new op count after the execution of the given [[ScriptToken]] */
private def calcOpCount(oldOpCount: Int, token: ScriptToken):Int = BitcoinScriptUtil.countsTowardsScriptOpLimit(token) match {
case true => oldOpCount + 1
private def calcOpCount(oldOpCount: Int, token: ScriptToken): Int = BitcoinScriptUtil.countsTowardsScriptOpLimit(token) match {
case true => oldOpCount + 1
case false => oldOpCount
}
/** Checks if the transaction contained a witness that we did not use
* [[https://github.com/bitcoin/bitcoin/blob/528472111b4965b1a99c4bcf08ac5ec93d87f10f/src/script/interpreter.cpp#L1515-L1523]]
* Return true if witness was NOT used, return false if witness was used. */
private def hasUnexpectedWitness(program: ScriptProgram): Boolean = {
/**
* Checks if the transaction contained a witness that we did not use
* [[https://github.com/bitcoin/bitcoin/blob/528472111b4965b1a99c4bcf08ac5ec93d87f10f/src/script/interpreter.cpp#L1515-L1523]]
* Return true if witness was NOT used, return false if witness was used.
*/
private def hasUnexpectedWitness(program: ScriptProgram): Boolean = {
val txSigComponent = program.txSignatureComponent
logger.debug("TxSigComponent: " + txSigComponent)
val unexpectedWitness = txSigComponent match {
case b : BaseTxSigComponent =>
case b: BaseTxSigComponent =>
b.transaction match {
case wtx : WitnessTransaction =>
case wtx: WitnessTransaction =>
wtx.witness.witnesses(txSigComponent.inputIndex.toInt).stack.nonEmpty
case _ : BaseTransaction => false
case _: BaseTransaction => false
}
case _: WitnessTxSigComponentRaw => false
case w: WitnessTxSigComponentP2SH =>
@ -529,16 +529,18 @@ sealed abstract class ScriptInterpreter {
unexpectedWitness
}
/** Helper function used to rebuild a [[WitnessTxSigComponentRebuilt]]
* this converts a [[WitnessScriptPubKey]] into it's corresponding [[ScriptPubKey]] */
/**
* Helper function used to rebuild a [[WitnessTxSigComponentRebuilt]]
* this converts a [[WitnessScriptPubKey]] into it's corresponding [[ScriptPubKey]]
*/
private def rebuildWTxSigComponent(old: WitnessTxSigComponent, rebuildScriptPubKey: ScriptPubKey): Try[WitnessTxSigComponentRebuilt] = old match {
case wTxSigComponentRaw: WitnessTxSigComponentRaw =>
Success(WitnessTxSigComponentRebuilt(old.transaction,old.inputIndex,
rebuildScriptPubKey, wTxSigComponentRaw.scriptPubKey, old.flags,old.amount))
Success(WitnessTxSigComponentRebuilt(old.transaction, old.inputIndex,
rebuildScriptPubKey, wTxSigComponentRaw.scriptPubKey, old.flags, old.amount))
case wTxSigComponentP2SH: WitnessTxSigComponentP2SH =>
wTxSigComponentP2SH.witnessScriptPubKey.map { wit: WitnessScriptPubKey =>
WitnessTxSigComponentRebuilt(old.transaction,old.inputIndex,
rebuildScriptPubKey, wit, old.flags,old.amount)
WitnessTxSigComponentRebuilt(old.transaction, old.inputIndex,
rebuildScriptPubKey, wit, old.flags, old.amount)
}
}
@ -547,13 +549,13 @@ sealed abstract class ScriptInterpreter {
logger.warn("Unassigned witness inside of witness script pubkey")
val flags = txSigComponent.flags
val discourageUpgradableWitnessVersion = ScriptFlagUtil.discourageUpgradableWitnessProgram(flags)
val program = ScriptProgram(txSigComponent,Nil,Nil, txSigComponent.scriptPubKey.asm, Nil)
val program = ScriptProgram(txSigComponent, Nil, Nil, txSigComponent.scriptPubKey.asm, Nil)
if (discourageUpgradableWitnessVersion) {
Success(ScriptProgram(program,ScriptErrorDiscourageUpgradeableWitnessProgram))
Success(ScriptProgram(program, ScriptErrorDiscourageUpgradeableWitnessProgram))
} else {
//if we are not discouraging upgradable ops, we just trivially return the program with an OP_TRUE on the stack
//see: https://github.com/bitcoin/bitcoin/blob/b83264d9c7a8ddb79f64bd9540caddc8632ef31f/src/script/interpreter.cpp#L1386-L1389
val evaluated = loop(ScriptProgram(program,Seq(OP_TRUE),ScriptProgram.Stack),0)
val evaluated = loop(ScriptProgram(program, Seq(OP_TRUE), ScriptProgram.Stack), 0)
Success(evaluated)
}
}

View file

@ -1,10 +1,9 @@
package org.bitcoins.core.script.locktime
import org.bitcoins.core.number.{Int64, UInt32}
import org.bitcoins.core.number.{ Int64, UInt32 }
import org.bitcoins.core.protocol.transaction.TransactionConstants
import org.bitcoins.core.script.ScriptProgram
import org.bitcoins.core.script.constant.{ScriptConstant, ScriptNumber, ScriptToken}
import org.bitcoins.core.script.constant.{ ScriptConstant, ScriptNumber, ScriptToken }
import org.bitcoins.core.script.flag.ScriptFlagUtil
import org.bitcoins.core.script.result._
import org.bitcoins.core.util.BitcoinSLogger
@ -27,9 +26,11 @@ sealed abstract class LockTimeInterpreter {
* The precise semantics are described in BIP 0065
*/
@tailrec
final def opCheckLockTimeVerify(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.contains(OP_CHECKLOCKTIMEVERIFY),
"Script top must be OP_CHECKLOCKTIMEVERIFY")
final def opCheckLockTimeVerify(program: ScriptProgram): ScriptProgram = {
require(
program.script.headOption.contains(OP_CHECKLOCKTIMEVERIFY),
"Script top must be OP_CHECKLOCKTIMEVERIFY"
)
val input = program.txSignatureComponent.transaction.inputs(program.txSignatureComponent.inputIndex.toInt)
val transaction = program.txSignatureComponent.transaction
if (program.stack.size == 0) {
@ -40,76 +41,76 @@ sealed abstract class LockTimeInterpreter {
ScriptProgram(program, ScriptErrorUnsatisfiedLocktime)
} else {
program.stack.head match {
case s : ScriptNumber if (s < ScriptNumber.zero) =>
case s: ScriptNumber if (s < ScriptNumber.zero) =>
logger.error("OP_CHECKLOCKTIMEVERIFY marks tx as invalid if the stack top is negative")
ScriptProgram(program,ScriptErrorNegativeLockTime)
case s : ScriptNumber if (s >= ScriptNumber(500000000) && transaction.lockTime < UInt32(500000000)) =>
ScriptProgram(program, ScriptErrorNegativeLockTime)
case s: ScriptNumber if (s >= ScriptNumber(500000000) && transaction.lockTime < UInt32(500000000)) =>
logger.error("OP_CHECKLOCKTIMEVERIFY marks the tx as invalid if stack top >= 500000000 & tx locktime < 500000000")
ScriptProgram(program,ScriptErrorUnsatisfiedLocktime)
case s : ScriptNumber if (s < ScriptNumber(500000000) && transaction.lockTime >= UInt32(500000000)) =>
ScriptProgram(program, ScriptErrorUnsatisfiedLocktime)
case s: ScriptNumber if (s < ScriptNumber(500000000) && transaction.lockTime >= UInt32(500000000)) =>
logger.error("OP_CHECKLOCKTIMEVERIFY marks the tx as invalid if stack top < 500000000 & tx locktime >= 500000000")
ScriptProgram(program,ScriptErrorUnsatisfiedLocktime)
case s : ScriptNumber =>
ScriptProgram(program, ScriptErrorUnsatisfiedLocktime)
case s: ScriptNumber =>
if (s.bytes.size > 5) {
//if the number size is larger than 5 bytes the number is invalid
ScriptProgram(program,ScriptErrorUnknownError)
} else if (checkLockTime(program,s)) {
ScriptProgram(program, ScriptErrorUnknownError)
} else if (checkLockTime(program, s)) {
ScriptProgram(program, program.script.tail, ScriptProgram.Script)
} else {
logger.error("Stack top locktime and transaction locktime number comparison failed")
ScriptProgram(program, ScriptErrorUnsatisfiedLocktime)
}
case s : ScriptConstant =>
case s: ScriptConstant =>
opCheckLockTimeVerify(ScriptProgram(program, ScriptNumber(s.hex) :: program.stack.tail, ScriptProgram.Stack))
case _ : ScriptToken => ScriptProgram(program,ScriptErrorUnknownError)
case _: ScriptToken => ScriptProgram(program, ScriptErrorUnknownError)
}
}
}
/**
* When executed, if any of the following conditions are true, the script interpreter will terminate with an error:
* 1.) the stack is empty; or
* 2.) the top item on the stack is less than 0; or
* 3.) the top item on the stack has the disable flag (1 << 31) unset; and
* the transaction version is less than 2; or
* the transaction input sequence number disable flag (1 << 31) is set; or
* the relative lock-time type is not the same; or
* the top stack item is greater than the transaction sequence (when masked according to the BIP68);
* Otherwise, script execution will continue as if a NOP had been executed.
* See BIP112 for more information
* [[https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki]]
*/
* When executed, if any of the following conditions are true, the script interpreter will terminate with an error:
* 1.) the stack is empty; or
* 2.) the top item on the stack is less than 0; or
* 3.) the top item on the stack has the disable flag (1 << 31) unset; and
* the transaction version is less than 2; or
* the transaction input sequence number disable flag (1 << 31) is set; or
* the relative lock-time type is not the same; or
* the top stack item is greater than the transaction sequence (when masked according to the BIP68);
* Otherwise, script execution will continue as if a NOP had been executed.
* See BIP112 for more information
* [[https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki]]
*/
@tailrec
final def opCheckSequenceVerify(program : ScriptProgram) : ScriptProgram = {
final def opCheckSequenceVerify(program: ScriptProgram): ScriptProgram = {
if (program.stack.isEmpty) {
logger.error("Cannot execute OP_CHECKSEQUENCEVERIFY on an empty stack")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
} else {
program.stack.head match {
case ScriptNumber.negativeOne => ScriptProgram(program,ScriptErrorNegativeLockTime)
case s : ScriptNumber if (ScriptFlagUtil.requireMinimalData(program.flags) && !s.isShortestEncoding) =>
case ScriptNumber.negativeOne => ScriptProgram(program, ScriptErrorNegativeLockTime)
case s: ScriptNumber if (ScriptFlagUtil.requireMinimalData(program.flags) && !s.isShortestEncoding) =>
logger.error("Sequence number is not encoded in the shortest way possible")
ScriptProgram(program,ScriptErrorUnknownError)
case s : ScriptNumber if (!isLockTimeBitOff(s)) =>
ScriptProgram(program, ScriptErrorUnknownError)
case s: ScriptNumber if (!isLockTimeBitOff(s)) =>
//see BIP68 for semantic of locktimeDisableFlag
logger.info("Locktime disable flag was set so OP_CHECKSEQUENCEVERIFY is treated as a NOP")
ScriptProgram(program,program.script.tail,ScriptProgram.Script)
case s : ScriptNumber if (isLockTimeBitOff(s) && program.txSignatureComponent.transaction.version < UInt32(2)) =>
ScriptProgram(program, program.script.tail, ScriptProgram.Script)
case s: ScriptNumber if (isLockTimeBitOff(s) && program.txSignatureComponent.transaction.version < UInt32(2)) =>
logger.error("OP_CSV fails if locktime bit is not set and the tx version < 2")
ScriptProgram(program, ScriptErrorUnsatisfiedLocktime)
case s : ScriptNumber =>
case s: ScriptNumber =>
if (s.bytes.size > 5) {
//if the number size is larger than 5 bytes the number is invalid
logger.error("The OP_CSV value in the script was larger than 5 bytes in size.")
ScriptProgram(program,ScriptErrorUnknownError)
} else if (checkSequence(program,s)) {
ScriptProgram(program, ScriptErrorUnknownError)
} else if (checkSequence(program, s)) {
ScriptProgram(program, program.stack, program.script.tail)
} else {
ScriptProgram(program, ScriptErrorUnsatisfiedLocktime)
}
case s : ScriptConstant =>
case s: ScriptConstant =>
opCheckSequenceVerify(ScriptProgram(program, ScriptNumber(s.hex) :: program.stack.tail, ScriptProgram.Stack))
case token : ScriptToken =>
case token: ScriptToken =>
throw new RuntimeException("Stack top must be either a ScriptConstant or a ScriptNumber, we got: " + token)
}
@ -117,21 +118,21 @@ sealed abstract class LockTimeInterpreter {
}
/**
* Mimics this function inside of bitcoin core
* [[https://github.com/bitcoin/bitcoin/blob/e26b62093ae21e89ed7d36a24a6b863f38ec631d/src/script/interpreter.cpp#L1196]]
* [[https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki#specification]]
* @param program the program whose transaction input's sequence is being compared
* @param nSequence the script number on the stack top to compare to the input's sequence number
* @return if the given script number is valid or not
*/
def checkSequence(program : ScriptProgram, nSequence : ScriptNumber) : Boolean = {
* Mimics this function inside of bitcoin core
* [[https://github.com/bitcoin/bitcoin/blob/e26b62093ae21e89ed7d36a24a6b863f38ec631d/src/script/interpreter.cpp#L1196]]
* [[https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki#specification]]
* @param program the program whose transaction input's sequence is being compared
* @param nSequence the script number on the stack top to compare to the input's sequence number
* @return if the given script number is valid or not
*/
def checkSequence(program: ScriptProgram, nSequence: ScriptNumber): Boolean = {
val inputIndex = program.txSignatureComponent.inputIndex.toInt
logger.debug("inputIndex: " + inputIndex)
val transaction = program.txSignatureComponent.transaction
// Relative lock times are supported by comparing the passed
// in operand to the sequence number of the input.
val txToSequence : UInt32 = transaction.inputs(inputIndex).sequence
val txToSequence: UInt32 = transaction.inputs(inputIndex).sequence
// Fail if the transaction's version number is not set high
// enough to trigger BIP 68 rules.
@ -146,7 +147,7 @@ sealed abstract class LockTimeInterpreter {
// to get around a CHECKSEQUENCEVERIFY check.
if (!isLockTimeBitOff(Int64(txToSequence.toLong))) return false
val (nSequenceMasked,txToSequenceMasked) = (maskScriptNumber(nSequence),maskSequenceNumber(txToSequence))
val (nSequenceMasked, txToSequenceMasked) = (maskScriptNumber(nSequence), maskSequenceNumber(txToSequence))
logger.debug("tx sequence number: " + transaction.inputs(inputIndex).sequence)
logger.debug("txToSequenceMasked: " + txToSequenceMasked)
@ -161,7 +162,7 @@ sealed abstract class LockTimeInterpreter {
// We want to compare apples to apples, so fail the script
// unless the type of nSequenceMasked being tested is the same as
// the nSequenceMasked in the transaction.
if (!(isCSVLockByBlockHeight(nSequence,txToSequence) || isCSVLockByRelativeLockTime(nSequence,txToSequence))) {
if (!(isCSVLockByBlockHeight(nSequence, txToSequence) || isCSVLockByRelativeLockTime(nSequence, txToSequence))) {
logger.error("The txSequence and nSequence (OP_CSV value) are not of the same type (timestamp/block-height).")
return false
}
@ -177,19 +178,22 @@ sealed abstract class LockTimeInterpreter {
true
}
/** Checks if the given [[ScriptNumber]] and [[UInt32]] are valid values for spending
* a OP_CSV value by block height */
/**
* Checks if the given [[ScriptNumber]] and [[UInt32]] are valid values for spending
* a OP_CSV value by block height
*/
def isCSVLockByBlockHeight(scriptNumber: ScriptNumber, sequence: UInt32): Boolean = {
isCSVLockByBlockHeight(scriptNumber) && isCSVLockByBlockHeight(sequence)
}
def isCSVLockByBlockHeight(sequence: UInt32): Boolean = !isCSVLockByRelativeLockTime(sequence)
def isCSVLockByBlockHeight(scriptNumber: ScriptNumber): Boolean = !isCSVLockByRelativeLockTime(scriptNumber)
/** Checks if the given [[ScriptNumber]] and [[UInt32]] are valid values
* for spending an OP_CSV value by time based relative lock time */
/**
* Checks if the given [[ScriptNumber]] and [[UInt32]] are valid values
* for spending an OP_CSV value by time based relative lock time
*/
def isCSVLockByRelativeLockTime(number: ScriptNumber, sequence: UInt32): Boolean = {
isCSVLockByRelativeLockTime(number) && isCSVLockByRelativeLockTime(sequence)
}
@ -199,7 +203,7 @@ sealed abstract class LockTimeInterpreter {
scriptNumber.toLong) == TransactionConstants.sequenceLockTimeTypeFlag.toLong
}
def isCSVLockByRelativeLockTime(sequence: UInt32): Boolean = {
def isCSVLockByRelativeLockTime(sequence: UInt32): Boolean = {
val result = (TransactionConstants.sequenceLockTimeTypeFlag &
sequence) == TransactionConstants.sequenceLockTimeTypeFlag
result
@ -207,19 +211,20 @@ sealed abstract class LockTimeInterpreter {
/** Masks the given [[ScriptNumber]] according to BIP112 */
def maskScriptNumber(scriptNumber: ScriptNumber): ScriptNumber = {
val nSequenceMasked : ScriptNumber = scriptNumber & Int64(TransactionConstants.fullSequenceLockTimeMask.toLong)
val nSequenceMasked: ScriptNumber = scriptNumber & Int64(TransactionConstants.fullSequenceLockTimeMask.toLong)
nSequenceMasked
}
def maskSequenceNumber(sequence: UInt32): Int64 = {
val txToSequenceMasked : Int64 = Int64((sequence & TransactionConstants.fullSequenceLockTimeMask).toLong)
val txToSequenceMasked: Int64 = Int64((sequence & TransactionConstants.fullSequenceLockTimeMask).toLong)
txToSequenceMasked
}
/** Mimics this function inside of bitcoin core for checking the locktime of a transaction
* [[https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L1160]]. */
private def checkLockTime(program : ScriptProgram, locktime : ScriptNumber) : Boolean = {
/**
* Mimics this function inside of bitcoin core for checking the locktime of a transaction
* [[https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L1160]].
*/
private def checkLockTime(program: ScriptProgram, locktime: ScriptNumber): Boolean = {
// There are two kinds of nLockTime: lock-by-blockheight
// and lock-by-blocktime, distinguished by whether
// nLockTime < LOCKTIME_THRESHOLD.
@ -234,7 +239,7 @@ sealed abstract class LockTimeInterpreter {
locktime.toLong < TransactionConstants.locktimeThreshold.toLong) ||
(transaction.lockTime >= TransactionConstants.locktimeThreshold &&
locktime.toLong >= TransactionConstants.locktimeThreshold.toLong)
)) return false
)) return false
// Now that we know we're comparing apples-to-apples, the
// comparison is a simple numeric one.
@ -256,9 +261,9 @@ sealed abstract class LockTimeInterpreter {
}
/** The [[ScriptNumber]] on the stack has the disable flag (1 << 31) unset. */
def isLockTimeBitOff(s : ScriptNumber) : Boolean = (s.toLong & TransactionConstants.locktimeDisabledFlag.toLong) == 0
def isLockTimeBitOff(s: ScriptNumber): Boolean = (s.toLong & TransactionConstants.locktimeDisabledFlag.toLong) == 0
def isLockTimeBitOff(num : Int64) : Boolean = isLockTimeBitOff(ScriptNumber(num.hex))
def isLockTimeBitOff(num: Int64): Boolean = isLockTimeBitOff(ScriptNumber(num.hex))
}
object LockTimeInterpreter extends LockTimeInterpreter

View file

@ -22,18 +22,18 @@ case object OP_CHECKLOCKTIMEVERIFY extends LocktimeOperation {
}
/**
* When executed, if any of the following conditions are true, the script interpreter will terminate with an error:
* 1.) the stack is empty; or
* 2.) the top item on the stack is less than 0; or
* 3.) the top item on the stack has the disable flag (1 << 31) unset; and
* the transaction version is less than 2; or
* the transaction input sequence number disable flag (1 << 31) is set; or
* the relative lock-time type is not the same; or
* the top stack item is greater than the transaction sequence (when masked according to the BIP68);
* Otherwise, script execution will continue as if a NOP had been executed.
* See BIP112 for more information
* https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki
*/
* When executed, if any of the following conditions are true, the script interpreter will terminate with an error:
* 1.) the stack is empty; or
* 2.) the top item on the stack is less than 0; or
* 3.) the top item on the stack has the disable flag (1 << 31) unset; and
* the transaction version is less than 2; or
* the transaction input sequence number disable flag (1 << 31) is set; or
* the relative lock-time type is not the same; or
* the top stack item is greater than the transaction sequence (when masked according to the BIP68);
* Otherwise, script execution will continue as if a NOP had been executed.
* See BIP112 for more information
* https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki
*/
case object OP_CHECKSEQUENCEVERIFY extends LocktimeOperation {
override def opCode = 178
}

View file

@ -52,7 +52,7 @@ case object OP_RESERVED1 extends ReservedOperation {
/**
* Transaction is invalid unless occuring in an unexecuted OP_IF branch
*/
case object OP_RESERVED2 extends ReservedOperation {
case object OP_RESERVED2 extends ReservedOperation {
override def opCode = 138
}
@ -92,10 +92,10 @@ case object OP_NOP10 extends NOP {
override def opCode = 185
}
case class UndefinedOP_NOP(opCode : Int) extends ReservedOperation
case class UndefinedOP_NOP(opCode: Int) extends ReservedOperation
object ReservedOperation extends ScriptOperationFactory[ReservedOperation] {
lazy val undefinedOpCodes = for {i <- 0xba to 0xff} yield UndefinedOP_NOP(i)
def operations = Seq(OP_RESERVED,OP_VER,OP_VERIF,OP_VERNOTIF,OP_RESERVED, OP_RESERVED1, OP_RESERVED2,
OP_NOP, OP_NOP1,OP_NOP4,OP_NOP5,OP_NOP6,OP_NOP7,OP_NOP8, OP_NOP9, OP_NOP10) ++ undefinedOpCodes
lazy val undefinedOpCodes = for { i <- 0xba to 0xff } yield UndefinedOP_NOP(i)
def operations = Seq(OP_RESERVED, OP_VER, OP_VERIF, OP_VERNOTIF, OP_RESERVED, OP_RESERVED1, OP_RESERVED2,
OP_NOP, OP_NOP1, OP_NOP4, OP_NOP5, OP_NOP6, OP_NOP7, OP_NOP8, OP_NOP9, OP_NOP10) ++ undefinedOpCodes
}

View file

@ -1,191 +1,179 @@
package org.bitcoins.core.script.result
sealed trait ScriptResult {
def description : String
def description: String
}
/**
* [[https://github.com/bitcoin/bitcoin/blob/master/src/test/script_tests.cpp#L61]]
*/
* [[https://github.com/bitcoin/bitcoin/blob/master/src/test/script_tests.cpp#L61]]
*/
sealed trait ScriptError extends ScriptResult
//SCRIPT_ERR_OK = 0,
case object ScriptOk extends ScriptResult {
override def description : String = "OK"
override def description: String = "OK"
}
//SCRIPT_ERR_UNKNOWN_ERROR,
case object ScriptErrorUnknownError extends ScriptError {
override def description : String = "UNKNOWN_ERROR"
override def description: String = "UNKNOWN_ERROR"
}
//SCRIPT_ERR_EVAL_FALSE,
case object ScriptErrorEvalFalse extends ScriptError {
override def description : String = "EVAL_FALSE"
override def description: String = "EVAL_FALSE"
}
//SCRIPT_ERR_OP_RETURN,
case object ScriptErrorOpReturn extends ScriptError {
override def description : String = "OP_RETURN"
override def description: String = "OP_RETURN"
}
/* Max sizes */
//SCRIPT_ERR_SCRIPT_SIZE,
case object ScriptErrorScriptSize extends ScriptError {
override def description : String = "SCRIPT_SIZE"
override def description: String = "SCRIPT_SIZE"
}
//SCRIPT_ERR_PUSH_SIZE,
case object ScriptErrorPushSize extends ScriptError {
override def description : String = "PUSH_SIZE"
override def description: String = "PUSH_SIZE"
}
//SCRIPT_ERR_OP_COUNT,
case object ScriptErrorOpCount extends ScriptError {
override def description : String = "OP_COUNT"
override def description: String = "OP_COUNT"
}
//SCRIPT_ERR_STACK_SIZE,
case object ScriptErrorStackSize extends ScriptError {
override def description : String = "STACK_SIZE"
override def description: String = "STACK_SIZE"
}
//SCRIPT_ERR_SIG_COUNT,
case object ScriptErrorSigCount extends ScriptError {
override def description : String = "SIG_COUNT"
override def description: String = "SIG_COUNT"
}
//SCRIPT_ERR_PUBKEY_COUNT,
case object ScriptErrorPubKeyCount extends ScriptError {
override def description : String = "PUBKEY_COUNT"
override def description: String = "PUBKEY_COUNT"
}
/* Failed verify operations */
//SCRIPT_ERR_VERIFY,
case object ScriptErrorVerify extends ScriptError {
override def description : String = "VERIFY"
override def description: String = "VERIFY"
}
//SCRIPT_ERR_EQUALVERIFY,
case object ScriptErrorEqualVerify extends ScriptError {
override def description : String = "EQUALVERIFY"
override def description: String = "EQUALVERIFY"
}
//SCRIPT_ERR_CHECKMULTISIGVERIFY,
case object ScriptErrorCheckMultiSigVerify extends ScriptError {
override def description : String = "CHECKMULTISIGVERIFY"
override def description: String = "CHECKMULTISIGVERIFY"
}
//SCRIPT_ERR_CHECKSIGVERIFY,
case object ScriptErrorCheckSigVerify extends ScriptError {
override def description : String = "CHECKSIGVERIFY"
override def description: String = "CHECKSIGVERIFY"
}
//SCRIPT_ERR_NUMEQUALVERIFY,
case object ScriptErrorNumEqualVerify extends ScriptError {
override def description : String = "NUMEQUALVERIFY"
override def description: String = "NUMEQUALVERIFY"
}
/* Logical/Format/Canonical errors */
//SCRIPT_ERR_BAD_OPCODE,
case object ScriptErrorBadOpCode extends ScriptError {
override def description : String = "BAD_OPCODE"
override def description: String = "BAD_OPCODE"
}
//SCRIPT_ERR_DISABLED_OPCODE,
case object ScriptErrorDisabledOpCode extends ScriptError {
override def description : String = "DISABLED_OPCODE"
override def description: String = "DISABLED_OPCODE"
}
//SCRIPT_ERR_INVALID_STACK_OPERATION,
case object ScriptErrorInvalidStackOperation extends ScriptError {
override def description : String = "INVALID_STACK_OPERATION"
override def description: String = "INVALID_STACK_OPERATION"
}
//SCRIPT_ERR_INVALID_ALTSTACK_OPERATION,
case object ScriptErrorInvalidAltStackOperation extends ScriptError {
override def description : String = "INVALID_ALTSTACK_OPERATION"
override def description: String = "INVALID_ALTSTACK_OPERATION"
}
//SCRIPT_ERR_UNBALANCED_CONDITIONAL,
case object ScriptErrorUnbalancedConditional extends ScriptError {
override def description : String = "UNBALANCED_CONDITIONAL"
override def description: String = "UNBALANCED_CONDITIONAL"
}
/* CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY */
//SCRIPT_ERR_NEGATIVE_LOCKTIME,
case object ScriptErrorNegativeLockTime extends ScriptError {
override def description : String = "NEGATIVE_LOCKTIME"
override def description: String = "NEGATIVE_LOCKTIME"
}
//SCRIPT_ERR_UNSATISFIED_LOCKTIME,
case object ScriptErrorUnsatisfiedLocktime extends ScriptError {
override def description : String = "UNSATISFIED_LOCKTIME"
override def description: String = "UNSATISFIED_LOCKTIME"
}
/* BIP62 */
//SCRIPT_ERR_SIG_HASHTYPE,
case object ScriptErrorSigHashType extends ScriptError {
override def description : String = "SIG_HASHTYPE"
override def description: String = "SIG_HASHTYPE"
}
//SCRIPT_ERR_SIG_DER,
case object ScriptErrorSigDer extends ScriptError {
override def description : String = "SIG_DER"
override def description: String = "SIG_DER"
}
//SCRIPT_ERR_MINIMALDATA,
case object ScriptErrorMinimalData extends ScriptError {
override def description : String = "MINIMALDATA"
override def description: String = "MINIMALDATA"
}
//SCRIPT_ERR_SIG_PUSHONLY,
case object ScriptErrorSigPushOnly extends ScriptError {
override def description : String = "SIG_PUSHONLY"
override def description: String = "SIG_PUSHONLY"
}
//SCRIPT_ERR_SIG_HIGH_S,
case object ScriptErrorSigHighS extends ScriptError {
override def description : String = "SIG_HIGH_S"
override def description: String = "SIG_HIGH_S"
}
//SCRIPT_ERR_SIG_NULLDUMMY,
case object ScriptErrorSigNullDummy extends ScriptError {
override def description : String = "SIG_NULLDUMMY"
override def description: String = "SIG_NULLDUMMY"
}
//SCRIPT_ERR_PUBKEYTYPE,
case object ScriptErrorPubKeyType extends ScriptError {
override def description : String = "PUBKEYTYPE"
override def description: String = "PUBKEYTYPE"
}
//SCRIPT_ERR_CLEANSTACK,
case object ScriptErrorCleanStack extends ScriptError {
override def description : String = "CLEANSTACK"
override def description: String = "CLEANSTACK"
}
/* softfork safeness */
//SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS,
case object ScriptErrorDiscourageUpgradableNOPs extends ScriptError {
override def description : String = "DISCOURAGE_UPGRADABLE_NOPS"
override def description: String = "DISCOURAGE_UPGRADABLE_NOPS"
}
//SCRIPT_ERR_ERROR_COUNT
case object ScriptErrorCount extends ScriptError {
override def description : String = "ERROR_COUNT"
override def description: String = "ERROR_COUNT"
}
//SCRIPT_ERR_MINIMALIF
@ -237,21 +225,19 @@ case object ScriptErrorWitnessPubKeyType extends ScriptError {
override def description = "WITNESS_PUBKEYTYPE"
}
/**
* Factory companion object for creating ScriptError objects
*/
object ScriptResult {
def results : Seq[ScriptResult] = Seq(ScriptOk,ScriptErrorUnknownError,ScriptErrorEvalFalse, ScriptErrorOpReturn,
def results: Seq[ScriptResult] = Seq(ScriptOk, ScriptErrorUnknownError, ScriptErrorEvalFalse, ScriptErrorOpReturn,
ScriptErrorPushSize, ScriptErrorScriptSize, ScriptErrorOpCount, ScriptErrorStackSize, ScriptErrorSigCount,
ScriptErrorPubKeyCount,ScriptErrorVerify, ScriptErrorEqualVerify,ScriptErrorCheckSigVerify, ScriptErrorCheckMultiSigVerify,
ScriptErrorNumEqualVerify, ScriptErrorBadOpCode,ScriptErrorDisabledOpCode,ScriptErrorInvalidStackOperation,
ScriptErrorPubKeyCount, ScriptErrorVerify, ScriptErrorEqualVerify, ScriptErrorCheckSigVerify, ScriptErrorCheckMultiSigVerify,
ScriptErrorNumEqualVerify, ScriptErrorBadOpCode, ScriptErrorDisabledOpCode, ScriptErrorInvalidStackOperation,
ScriptErrorInvalidAltStackOperation, ScriptErrorUnbalancedConditional, ScriptErrorNegativeLockTime,
ScriptErrorUnsatisfiedLocktime, ScriptErrorSigHashType, ScriptErrorSigDer, ScriptErrorMinimalData, ScriptErrorSigPushOnly,
ScriptErrorSigHighS, ScriptErrorSigNullDummy,ScriptErrorPubKeyType, ScriptErrorCleanStack, ScriptErrorDiscourageUpgradableNOPs,
ScriptErrorCount, ScriptErrorMinimalIf, ScriptErrorSigNullFail,ScriptErrorDiscourageUpgradeableWitnessProgram, ScriptErrorWitnessProgramWrongLength,
ScriptErrorWitnessProgramWitnessEmpty,ScriptErrorWitnessProgramMisMatch, ScriptErrorWitnessMalleated,
ScriptErrorWitnessMalleatedP2SH,ScriptErrorWitnessUnexpected, ScriptErrorWitnessPubKeyType )
def apply(str : String) : ScriptResult = results.filter(_.description == str).head
ScriptErrorSigHighS, ScriptErrorSigNullDummy, ScriptErrorPubKeyType, ScriptErrorCleanStack, ScriptErrorDiscourageUpgradableNOPs,
ScriptErrorCount, ScriptErrorMinimalIf, ScriptErrorSigNullFail, ScriptErrorDiscourageUpgradeableWitnessProgram, ScriptErrorWitnessProgramWrongLength,
ScriptErrorWitnessProgramWitnessEmpty, ScriptErrorWitnessProgramMisMatch, ScriptErrorWitnessMalleated,
ScriptErrorWitnessMalleatedP2SH, ScriptErrorWitnessUnexpected, ScriptErrorWitnessPubKeyType)
def apply(str: String): ScriptResult = results.filter(_.description == str).head
}

View file

@ -20,7 +20,7 @@ sealed abstract class SpliceInterpreter {
} else {
val scriptNumber = program.stack.head match {
case ScriptNumber.zero => ScriptNumber.zero
case x: ScriptToken => ScriptNumber(x.bytes.size)
case x: ScriptToken => ScriptNumber(x.bytes.size)
}
ScriptProgram(program, scriptNumber :: program.stack, program.script.tail)
}

View file

@ -8,9 +8,8 @@ import org.bitcoins.core.script.constant.ScriptOperation
*/
sealed trait SpliceOperation extends ScriptOperation
case object OP_CAT extends SpliceOperation {
override def opCode = 126
override def opCode = 126
}
case object OP_SUBSTR extends SpliceOperation {

View file

@ -1,13 +1,12 @@
package org.bitcoins.core.script.stack
import org.bitcoins.core.script.ScriptProgram
import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.flag.ScriptFlagUtil
import org.bitcoins.core.script.result._
import org.bitcoins.core.util.BitcoinSLogger
import scala.util.{Failure, Success, Try}
import scala.util.{ Failure, Success, Try }
/**
* Created by chris on 1/6/16.
@ -17,106 +16,107 @@ import scala.util.{Failure, Success, Try}
sealed abstract class StackInterpreter {
private def logger = BitcoinSLogger.logger
/** Duplicates the element on top of the stack
* expects the first element in script to be the OP_DUP operation. */
def opDup(program : ScriptProgram) : ScriptProgram = {
/**
* Duplicates the element on top of the stack
* expects the first element in script to be the OP_DUP operation.
*/
def opDup(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_DUP), "Top of the script stack must be OP_DUP")
program.stack match {
case h :: t => ScriptProgram(program, h :: program.stack, program.script.tail)
case Nil =>
logger.error("Cannot duplicate the top element on an empty stack")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
}
}
/** If the top stack value is not 0, duplicate it. */
def opIfDup(program : ScriptProgram) : ScriptProgram = {
def opIfDup(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_IFDUP), "Top of the script stack must be OP_DUP")
if (program.stack.nonEmpty) {
if (program.stack.head == ScriptNumber.zero) return ScriptProgram(program, program.stack, program.script.tail)
ScriptProgram(program, program.stack.head :: program.stack, program.script.tail)
} else {
logger.error("Cannot duplicate the top element on an empty stack")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
}
}
/** Puts the number of stack items onto the stack. */
def opDepth(program : ScriptProgram) : ScriptProgram = {
def opDepth(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_DEPTH), "Top of script stack must be OP_DEPTH")
val stackSize = program.stack.size
val numberToPush : ScriptNumber = ScriptNumber(stackSize)
val numberToPush: ScriptNumber = ScriptNumber(stackSize)
ScriptProgram(program, numberToPush :: program.stack, program.script.tail)
}
/** Puts the input onto the top of the alt stack. Removes it from the main stack. */
def opToAltStack(program : ScriptProgram) : ScriptProgram = {
def opToAltStack(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_TOALTSTACK), "Top of script stack must be OP_TOALTSTACK")
if (program.stack.nonEmpty) {
ScriptProgram(program, program.stack.tail,
program.script.tail, program.stack.head :: program.altStack, ScriptProgram.AltStack)
} else {
logger.error("OP_TOALTSTACK requires an element to be on the stack")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
}
}
/** Puts the input onto the top of the main stack. Removes it from the alt stack. */
def opFromAltStack(program : ScriptProgram) : ScriptProgram = {
def opFromAltStack(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_FROMALTSTACK), "Top of script stack must be OP_FROMALTSTACK")
if (program.altStack.nonEmpty) {
ScriptProgram(program, program.altStack.head :: program.stack,
program.script.tail, program.altStack.tail, ScriptProgram.AltStack)
} else {
logger.error("Alt Stack must have at least one item on it for OP_FROMALTSTACK")
ScriptProgram(program,ScriptErrorInvalidAltStackOperation)
ScriptProgram(program, ScriptErrorInvalidAltStackOperation)
}
}
/** Removes the top stack item. */
def opDrop(program : ScriptProgram) : ScriptProgram = {
def opDrop(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_DROP), "Top of script stack must be OP_DROP")
if (program.stack.nonEmpty) {
ScriptProgram(program, program.stack.tail, program.script.tail)
} else {
logger.error("Stack must have at least one item on it for OP_DROP")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
}
}
/** Removes the second-to-top stack item. */
def opNip(program : ScriptProgram) : ScriptProgram = {
def opNip(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_NIP), "Top of script stack must be OP_NIP")
program.stack match {
case h :: _ :: t => ScriptProgram(program, h :: t, program.script.tail)
case h :: t =>
case h :: t =>
logger.error("Stack must have at least two items on it for OP_NIP")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
case Nil =>
logger.error("Stack must have at least two items on it for OP_NIP")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
}
}
/** Copies the second-to-top stack item to the top. */
def opOver(program : ScriptProgram) : ScriptProgram = {
def opOver(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_OVER), "Top of script stack must be OP_OVER")
program.stack match {
case _ :: h1 :: _ => ScriptProgram(program, h1 :: program.stack, program.script.tail)
case h :: t => logger.error("Stack must have at least two items on it for OP_OVER")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
case h :: t =>
logger.error("Stack must have at least two items on it for OP_OVER")
ScriptProgram(program, ScriptErrorInvalidStackOperation)
case Nil =>
logger.error("Stack must have at least two items on it for OP_OVER")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
}
}
/** The item n back in the stack is copied to the top. */
def opPick(program : ScriptProgram) : ScriptProgram = {
def opPick(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_PICK), "Top of script stack must be OP_PICK")
executeOpWithStackTopAsNumberArg(program, { number : ScriptNumber =>
executeOpWithStackTopAsNumberArg(program, { number: ScriptNumber =>
logger.info("Script number for OP_PICK: " + number)
//check if n is within the bound of the script
if (program.stack.size < 2) ScriptProgram(program, ScriptErrorInvalidStackOperation)
@ -131,9 +131,9 @@ sealed abstract class StackInterpreter {
}
/** The item n back in the stack is moved to the top. */
def opRoll(program : ScriptProgram) : ScriptProgram = {
def opRoll(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_ROLL), "Top of script stack must be OP_ROLL")
executeOpWithStackTopAsNumberArg(program, (number : ScriptNumber) =>
executeOpWithStackTopAsNumberArg(program, (number: ScriptNumber) =>
if (program.stack.size < 2) ScriptProgram(program, ScriptErrorInvalidStackOperation)
else if (number.toLong >= 0 && number.toLong < program.stack.tail.size) {
val newStackTop = program.stack.tail(number.toInt)
@ -142,80 +142,80 @@ sealed abstract class StackInterpreter {
ScriptProgram(program, newStack, program.script.tail)
} else {
logger.error("The index for OP_ROLL would have caused an index out of bounds exception")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
}
)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
})
}
/** The top three items on the stack are rotated to the left.
* Ex: x1 x2 x3 -> x2 x3 x1 */
def opRot(program : ScriptProgram) : ScriptProgram = {
/**
* The top three items on the stack are rotated to the left.
* Ex: x1 x2 x3 -> x2 x3 x1
*/
def opRot(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_ROT), "Top of script stack must be OP_ROT")
program.stack match {
case h :: h1 :: h2 :: t =>
val newStack = h2 :: h :: h1 :: t
ScriptProgram(program, newStack,program.script.tail)
ScriptProgram(program, newStack, program.script.tail)
case _ =>
logger.error("Stack must have at least 3 items on it for OP_ROT")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
}
}
/** The fifth and sixth items back are moved to the top of the stack.
* Ex. x1 x2 x3 x4 x5 x6 -> x3 x4 x5 x6 x1 x2 */
def op2Rot(program : ScriptProgram) : ScriptProgram = {
/**
* The fifth and sixth items back are moved to the top of the stack.
* Ex. x1 x2 x3 x4 x5 x6 -> x3 x4 x5 x6 x1 x2
*/
def op2Rot(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_2ROT), "Top of script stack must be OP_2ROT")
program.stack match {
case h :: h1 :: h2 :: h3 :: h4 :: h5 :: t =>
val newStack = h4 :: h5 :: h :: h1 :: h2 :: h3 :: t
ScriptProgram(program, newStack,program.script.tail)
val newStack = h4 :: h5 :: h :: h1 :: h2 :: h3 :: t
ScriptProgram(program, newStack, program.script.tail)
case _ =>
logger.error("OP_2ROT requires 6 elements on the stack")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
}
}
/** Removes the top two stack items. */
def op2Drop(program : ScriptProgram) : ScriptProgram = {
def op2Drop(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_2DROP), "Top of script stack must be OP_2DROP")
if (program.stack.size > 1) {
ScriptProgram(program, program.stack.tail.tail, program.script.tail)
} else {
logger.error("OP_2DROP requires two elements to be on the stack")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
}
}
/** The top two items on the stack are swapped. */
def opSwap(program : ScriptProgram) : ScriptProgram = {
def opSwap(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_SWAP), "Top of script stack must be OP_SWAP")
if (program.stack.size > 1) {
val newStack = program.stack.tail.head :: program.stack.head :: program.stack.tail.tail
ScriptProgram(program, newStack, program.script.tail)
} else {
logger.error("Stack must have at least 2 items on it for OP_SWAP")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
}
}
/** The item at the top of the stack is copied and inserted before the second-to-top item. */
def opTuck(program : ScriptProgram) : ScriptProgram = {
def opTuck(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_TUCK), "Top of script stack must be OP_TUCK")
program.stack match {
case h :: h1 :: t =>
val newStack = h :: h1 :: h:: t
val newStack = h :: h1 :: h :: t
ScriptProgram(program, newStack, program.script.tail)
case _ =>
logger.error("Stack must have at least 2 items on it for OP_TUCK")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
}
}
/** Duplicates the top two stack items. */
def op2Dup(program : ScriptProgram) : ScriptProgram = {
def op2Dup(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_2DUP), "Top of script stack must be OP_2DUP")
program.stack match {
case h :: h1 :: t =>
@ -223,46 +223,46 @@ sealed abstract class StackInterpreter {
ScriptProgram(program, newStack, program.script.tail)
case _ =>
logger.error("Stack must have at least 2 items on it for OP_2DUP")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
}
}
/** Duplicates the top three stack items. */
def op3Dup(program : ScriptProgram) : ScriptProgram = {
def op3Dup(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_3DUP), "Top of script stack must be OP_3DUP")
program.stack match {
case h :: h1 :: h2 :: t =>
val newStack = h :: h1 :: h2 :: h :: h1 :: h2 :: t
ScriptProgram(program,newStack,program.script.tail)
ScriptProgram(program, newStack, program.script.tail)
case _ =>
logger.error("Stack must have at least 3 items on it for OP_3DUP")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
}
}
/** Copies the pair of items two spaces back in the stack to the front. */
def op2Over(program : ScriptProgram) : ScriptProgram = {
def op2Over(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_2OVER), "Top of script stack must be OP_2OVER")
program.stack match {
case h :: h1 :: h2 :: h3 :: t =>
val newStack = h2 :: h3 :: h :: h1 :: h2 :: h3 :: t
ScriptProgram(program, newStack,program.script.tail)
ScriptProgram(program, newStack, program.script.tail)
case _ =>
logger.error("Stack must have at least 4 items on it for OP_2OVER")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
}
}
/** Swaps the top two pairs of items. */
def op2Swap(program : ScriptProgram) : ScriptProgram = {
def op2Swap(program: ScriptProgram): ScriptProgram = {
require(program.script.headOption.contains(OP_2SWAP), "Top of script stack must be OP_2SWAP")
program.stack match {
case h :: h1 :: h2 :: h3 :: t =>
case h :: h1 :: h2 :: h3 :: t =>
val newStack = h2 :: h3 :: h :: h1 :: t
ScriptProgram(program,newStack,program.script.tail)
ScriptProgram(program, newStack, program.script.tail)
case _ =>
logger.error("Stack must have at least 4 items on it for OP_2SWAP")
ScriptProgram(program,ScriptErrorInvalidStackOperation)
ScriptProgram(program, ScriptErrorInvalidStackOperation)
}
}
@ -272,17 +272,17 @@ sealed abstract class StackInterpreter {
* @param op the operation that is executed with the script number on the top of the stack
* @return the program with the result of the op pushed onto to the top of the stack
*/
private def executeOpWithStackTopAsNumberArg(program : ScriptProgram, op : ScriptNumber => ScriptProgram) : ScriptProgram = {
private def executeOpWithStackTopAsNumberArg(program: ScriptProgram, op: ScriptNumber => ScriptProgram): ScriptProgram = {
program.stack.head match {
case scriptNum : ScriptNumber => op(scriptNum)
case _ : ScriptToken =>
case scriptNum: ScriptNumber => op(scriptNum)
case _: ScriptToken =>
//interpret the stack top as a number
val number : Try[ScriptNumber] = ScriptNumber(program.stack.head.bytes, ScriptFlagUtil.requireMinimalData(program.flags))
val number: Try[ScriptNumber] = ScriptNumber(program.stack.head.bytes, ScriptFlagUtil.requireMinimalData(program.flags))
number match {
case Success(n) => op(n)
case Failure(_) =>
logger.error("Script number was not minimally encoded")
ScriptProgram(program,ScriptErrorUnknownError)
ScriptProgram(program, ScriptErrorUnknownError)
}
}
}

View file

@ -8,8 +8,6 @@ import org.bitcoins.core.script.constant.ScriptOperation
*/
sealed trait StackOperation extends ScriptOperation
/**
* Puts the input onto the top of the alt stack. Removes it from the main stack.
*/
@ -42,7 +40,7 @@ case object OP_DEPTH extends StackOperation {
* Removes the top stack item
*/
case object OP_DROP extends StackOperation {
override def opCode = 117
override def opCode = 117
}
/**
@ -144,9 +142,8 @@ case object OP_2SWAP extends StackOperation {
}
object StackOperation extends ScriptOperationFactory[StackOperation] {
override def operations = Seq(OP_TOALTSTACK,OP_FROMALTSTACK,OP_IFDUP,OP_DEPTH,
OP_DEPTH,OP_DROP,OP_DUP,OP_NIP,OP_OVER,OP_ROLL,OP_ROT,OP_SWAP,OP_TUCK,OP_2DROP,OP_2DUP,
OP_3DUP,OP_2OVER,OP_2ROT,OP_2SWAP, OP_PICK)
override def operations = Seq(OP_TOALTSTACK, OP_FROMALTSTACK, OP_IFDUP, OP_DEPTH,
OP_DEPTH, OP_DROP, OP_DUP, OP_NIP, OP_OVER, OP_ROLL, OP_ROT, OP_SWAP, OP_TUCK, OP_2DROP, OP_2DUP,
OP_3DUP, OP_2OVER, OP_2ROT, OP_2SWAP, OP_PICK)
}

View file

@ -1,6 +1,6 @@
package org.bitcoins.core.serializers
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil}
import org.bitcoins.core.util.{ BitcoinSLogger, BitcoinSUtil }
import org.slf4j.Logger
/**
@ -10,14 +10,14 @@ import org.slf4j.Logger
abstract class RawBitcoinSerializer[T] {
/** Reads a hexadecimal value and transforms it into the native scala type T. */
def read(hex : String) : T = read(BitcoinSUtil.decodeHex(hex))
def read(hex: String): T = read(BitcoinSUtil.decodeHex(hex))
/** Reads in bytes and transforms it into the appropriate scala type T. */
def read(bytes : List[Byte]) : T
def read(bytes: List[Byte]): T
/** Reads in bytes and transforms it into the appropriate scala type T. */
def read(bytes : Seq[Byte]) : T = read(bytes.toList)
def read(bytes: Seq[Byte]): T = read(bytes.toList)
/** Takes a type T and writes it into the appropriate hexadecimal serialization for type T. */
def write(t : T) : Seq[Byte]
def write(t: T): Seq[Byte]
}

View file

@ -1,7 +1,7 @@
package org.bitcoins.core.serializers
import org.bitcoins.core.number.UInt64
import org.bitcoins.core.protocol.{CompactSizeUInt, NetworkElement}
import org.bitcoins.core.protocol.{ CompactSizeUInt, NetworkElement }
import org.bitcoins.core.util.BitcoinSLogger
/**
@ -10,25 +10,26 @@ import org.bitcoins.core.util.BitcoinSLogger
sealed abstract class RawSerializerHelper {
private val logger = BitcoinSLogger.logger
/** Used parse a byte sequence to a Seq[TransactionInput], Seq[TransactionOutput], etc
* Makes sure that we parse the correct amount of elements
* */
def parseCmpctSizeUIntSeq[T <: NetworkElement](bytes: Seq[Byte], constructor: Seq[Byte] => T): (Seq[T],Seq[Byte]) = {
/**
* Used parse a byte sequence to a Seq[TransactionInput], Seq[TransactionOutput], etc
* Makes sure that we parse the correct amount of elements
*/
def parseCmpctSizeUIntSeq[T <: NetworkElement](bytes: Seq[Byte], constructor: Seq[Byte] => T): (Seq[T], Seq[Byte]) = {
val count = CompactSizeUInt.parse(bytes)
val payload = bytes.splitAt(count.size.toInt)._2
def loop(accum: Seq[T], remaining: Seq[Byte]): (Seq[T],Seq[Byte]) = {
def loop(accum: Seq[T], remaining: Seq[Byte]): (Seq[T], Seq[Byte]) = {
if (accum.size == count.num.toInt) {
(accum.reverse,remaining)
(accum.reverse, remaining)
} else {
val parsed = constructor(remaining)
val (_,newRemaining) = remaining.splitAt(parsed.size)
loop(parsed +: accum,newRemaining)
val (_, newRemaining) = remaining.splitAt(parsed.size)
loop(parsed +: accum, newRemaining)
}
}
val (parsed,remaining) = loop(Nil,payload)
val (parsed, remaining) = loop(Nil, payload)
require(parsed.size == count.num.toInt, "Could not parse the amount of required elements, got: " + parsed.size + " required: " + count)
(parsed,remaining)
(parsed, remaining)
}
/** Writes a Seq[TransactionInput]/Seq[TransactionOutput]/Seq[Transaction] -> Seq[Byte] */

View file

@ -4,11 +4,11 @@ import org.bitcoins.core.currency.Satoshis
import org.bitcoins.core.number.Int64
/**
* Created by chris on 6/23/16.
*/
* Created by chris on 6/23/16.
*/
trait RawSatoshisSerializer extends RawBitcoinSerializer[Satoshis] {
def read(bytes : List[Byte]): Satoshis = Satoshis(Int64(bytes.reverse))
def read(bytes: List[Byte]): Satoshis = Satoshis(Int64(bytes.reverse))
def write(satoshis: Satoshis): Seq[Byte] = {
Int64(satoshis.toLong).bytes.reverse

View file

@ -6,36 +6,36 @@ import org.bitcoins.core.protocol.blockchain.BlockHeader
import org.bitcoins.core.serializers.RawBitcoinSerializer
/**
* Created by chris on 5/19/16.
* Serializes block headers
* https://bitcoin.org/en/developer-reference#block-headers
*/
* Created by chris on 5/19/16.
* Serializes block headers
* https://bitcoin.org/en/developer-reference#block-headers
*/
sealed abstract class RawBlockHeaderSerializer extends RawBitcoinSerializer[BlockHeader] {
/** Converts a list of bytes into a block header */
def read(bytes : List[Byte]) : BlockHeader = {
def read(bytes: List[Byte]): BlockHeader = {
//version first 4 bytes
val version = UInt32(bytes.take(4).reverse)
//previous header hash next 32 bytes
val prevBlockHashBytes = bytes.slice(4, 36)
val prevBlockHash : DoubleSha256Digest = DoubleSha256Digest(prevBlockHashBytes)
val prevBlockHash: DoubleSha256Digest = DoubleSha256Digest(prevBlockHashBytes)
//merkle hash next 32 bytes
val merkleRootBytes = bytes.slice(36, 68)
val merkleRoot : DoubleSha256Digest = DoubleSha256Digest(merkleRootBytes)
val merkleRoot: DoubleSha256Digest = DoubleSha256Digest(merkleRootBytes)
//time 4 bytes
val timeBytes = bytes.slice(68,72)
val timeBytes = bytes.slice(68, 72)
val time = UInt32(timeBytes.reverse)
//nbits 4 bytes
val nBitsBytes = bytes.slice(72,76)
val nBitsBytes = bytes.slice(72, 76)
val nBits = UInt32(nBitsBytes.reverse)
//nonce 4 bytes
val nonceBytes = bytes.slice(76,80)
val nonceBytes = bytes.slice(76, 80)
val nonce = UInt32(nonceBytes.reverse)
BlockHeader(version,prevBlockHash, merkleRoot, time, nBits, nonce)
BlockHeader(version, prevBlockHash, merkleRoot, time, nBits, nonce)
}
/** Serializes the BlockHeader to a byte array */
def write(blockHeader: BlockHeader): Seq[Byte] = {
def write(blockHeader: BlockHeader): Seq[Byte] = {
val version = blockHeader.version.bytes.reverse
val prevHash = blockHeader.previousBlockHash.bytes

View file

@ -1,28 +1,28 @@
package org.bitcoins.core.serializers.blockchain
import org.bitcoins.core.protocol.blockchain.{Block, BlockHeader}
import org.bitcoins.core.protocol.blockchain.{ Block, BlockHeader }
import org.bitcoins.core.protocol.transaction.Transaction
import org.bitcoins.core.serializers.{RawBitcoinSerializer, RawSerializerHelper}
import org.bitcoins.core.serializers.{ RawBitcoinSerializer, RawSerializerHelper }
/**
* Created by chris on 5/20/16.
* Responsible for serializing blocks in our blockchain
* https://bitcoin.org/en/developer-reference#serialized-blocks
*/
* Created by chris on 5/20/16.
* Responsible for serializing blocks in our blockchain
* https://bitcoin.org/en/developer-reference#serialized-blocks
*/
sealed abstract class RawBlockSerializer extends RawBitcoinSerializer[Block] {
/** Takes a list of bytes and converts it into a Block */
def read(bytes : List[Byte]) : Block = {
val blockHeader : BlockHeader = BlockHeader(bytes.take(80))
val txBytes : Seq[Byte] = bytes.splitAt(80)._2
val (transactions,_) = RawSerializerHelper.parseCmpctSizeUIntSeq[Transaction](txBytes, Transaction(_: Seq[Byte]))
def read(bytes: List[Byte]): Block = {
val blockHeader: BlockHeader = BlockHeader(bytes.take(80))
val txBytes: Seq[Byte] = bytes.splitAt(80)._2
val (transactions, _) = RawSerializerHelper.parseCmpctSizeUIntSeq[Transaction](txBytes, Transaction(_: Seq[Byte]))
Block(blockHeader, transactions)
}
/** Takes in a block and converts it to a byte array */
def write(block : Block): Seq[Byte] = {
def write(block: Block): Seq[Byte] = {
val writtenHeader = block.blockHeader.bytes
val txBytes = RawSerializerHelper.writeCmpctSizeUInt(block.transactions, { tx: Transaction => tx.bytes})
val txBytes = RawSerializerHelper.writeCmpctSizeUInt(block.transactions, { tx: Transaction => tx.bytes })
writtenHeader ++ txBytes
}

View file

@ -1,8 +1,7 @@
package org.bitcoins.core.serializers.blockchain
import org.bitcoins.core.crypto.DoubleSha256Digest
import org.bitcoins.core.number.{UInt32, UInt64}
import org.bitcoins.core.number.{ UInt32, UInt64 }
import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.protocol.blockchain.MerkleBlock
import org.bitcoins.core.serializers.RawBitcoinSerializer
@ -11,25 +10,26 @@ import org.bitcoins.core.util.BitcoinSUtil
import scala.annotation.tailrec
/**
* Created by chris on 8/15/16.
* [[https://bitcoin.org/en/developer-reference#merkleblock]]
*/
* Created by chris on 8/15/16.
* [[https://bitcoin.org/en/developer-reference#merkleblock]]
*/
sealed abstract class RawMerkleBlockSerializer extends RawBitcoinSerializer[MerkleBlock] {
def read(bytes: List[Byte]): MerkleBlock = {
val blockHeader = RawBlockHeaderSerializer.read(bytes.take(80))
val bytesAfterBlockHeaderParsing = bytes.slice(blockHeader.bytes.size, bytes.size)
val transactionCount = UInt32(bytesAfterBlockHeaderParsing.slice(0,4).reverse)
val transactionCount = UInt32(bytesAfterBlockHeaderParsing.slice(0, 4).reverse)
val hashCount = CompactSizeUInt.parseCompactSizeUInt(
bytesAfterBlockHeaderParsing.slice(4,bytesAfterBlockHeaderParsing.size))
bytesAfterBlockHeaderParsing.slice(4, bytesAfterBlockHeaderParsing.size)
)
val txHashStartIndex = (4 + hashCount.size).toInt
val bytesAfterHashCountParsing = bytesAfterBlockHeaderParsing.slice(txHashStartIndex,bytesAfterBlockHeaderParsing.size)
val bytesAfterHashCountParsing = bytesAfterBlockHeaderParsing.slice(txHashStartIndex, bytesAfterBlockHeaderParsing.size)
val (hashes, bytesAfterTxHashParsing) = parseTransactionHashes(bytesAfterHashCountParsing,hashCount)
val (hashes, bytesAfterTxHashParsing) = parseTransactionHashes(bytesAfterHashCountParsing, hashCount)
val flagCount = CompactSizeUInt.parseCompactSizeUInt(bytesAfterTxHashParsing)
val flags = bytesAfterTxHashParsing.slice(flagCount.size.toInt, bytesAfterTxHashParsing.size)
val matches = BitcoinSUtil.bytesToBitVectors(flags).flatMap(_.reverse)
MerkleBlock(blockHeader,transactionCount,hashes,matches)
MerkleBlock(blockHeader, transactionCount, hashes, matches)
}
def write(merkleBlock: MerkleBlock): Seq[Byte] = {
@ -43,24 +43,22 @@ sealed abstract class RawMerkleBlockSerializer extends RawBitcoinSerializer[Merk
merkleBlock.hashes.flatMap(_.bytes) ++ flagCount.bytes ++ byteVectors
}
/**
* Parses a sequence of transactions hashes from inside of a merkle block message
* @param bytes the bytes from which the tx hashes are parsed from
* @param hashCount the amount of tx hashes we need to parse from bytes
* @return the sequence of tx hashes and the remaining bytes to be parsed into a MerkleBlockMessage
*/
private def parseTransactionHashes(bytes : Seq[Byte], hashCount : CompactSizeUInt) : (Seq[DoubleSha256Digest], Seq[Byte]) = {
* Parses a sequence of transactions hashes from inside of a merkle block message
* @param bytes the bytes from which the tx hashes are parsed from
* @param hashCount the amount of tx hashes we need to parse from bytes
* @return the sequence of tx hashes and the remaining bytes to be parsed into a MerkleBlockMessage
*/
private def parseTransactionHashes(bytes: Seq[Byte], hashCount: CompactSizeUInt): (Seq[DoubleSha256Digest], Seq[Byte]) = {
@tailrec
def loop(remainingHashes : Long, remainingBytes : Seq[Byte],
accum : List[DoubleSha256Digest]) : (Seq[DoubleSha256Digest], Seq[Byte]) = {
if (remainingHashes <= 0) (accum.reverse,remainingBytes)
else loop(remainingHashes-1, remainingBytes.slice(32,remainingBytes.size), DoubleSha256Digest(remainingBytes.take(32)) :: accum)
def loop(remainingHashes: Long, remainingBytes: Seq[Byte],
accum: List[DoubleSha256Digest]): (Seq[DoubleSha256Digest], Seq[Byte]) = {
if (remainingHashes <= 0) (accum.reverse, remainingBytes)
else loop(remainingHashes - 1, remainingBytes.slice(32, remainingBytes.size), DoubleSha256Digest(remainingBytes.take(32)) :: accum)
}
loop(hashCount.num.toInt, bytes, Nil)
}
/** Parses a sequence of bits to a sequence of bit vectors grouped into bytes */
private def parseToBytes(bits: Seq[Boolean]): Seq[Seq[Boolean]] = {
@tailrec
@ -76,7 +74,7 @@ sealed abstract class RawMerkleBlockSerializer extends RawBitcoinSerializer[Merk
loop(t, newBits +: accum.tail)
}
}
loop(bits,Seq(Nil))
loop(bits, Seq(Nil))
}
}

View file

@ -1,25 +1,25 @@
package org.bitcoins.core.serializers.bloom
import org.bitcoins.core.bloom.{BloomFilter, BloomFlag}
import org.bitcoins.core.bloom.{ BloomFilter, BloomFlag }
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.serializers.RawBitcoinSerializer
/**
* Created by chris on 8/4/16.
* [[https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki#new-messages]]
*/
* Created by chris on 8/4/16.
* [[https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki#new-messages]]
*/
sealed abstract class RawBloomFilterSerializer extends RawBitcoinSerializer[BloomFilter] {
override def read(bytes: List[Byte]): BloomFilter = {
val filterSize = CompactSizeUInt.parseCompactSizeUInt(bytes)
val filter = bytes.slice(filterSize.size.toInt, filterSize.size.toInt + filterSize.num.toInt)
val hashFuncsIndex = (filterSize.size + filterSize.num.toInt).toInt
val hashFuncs = UInt32(bytes.slice(hashFuncsIndex,hashFuncsIndex + 4).reverse)
val hashFuncs = UInt32(bytes.slice(hashFuncsIndex, hashFuncsIndex + 4).reverse)
val tweakIndex = hashFuncsIndex + 4
val tweak = UInt32(bytes.slice(tweakIndex, tweakIndex + 4).reverse)
val flags = BloomFlag(bytes(tweakIndex+4))
BloomFilter(filterSize,filter,hashFuncs,tweak,flags)
val flags = BloomFlag(bytes(tweakIndex + 4))
BloomFilter(filterSize, filter, hashFuncs, tweak, flags)
}

View file

@ -1,7 +1,7 @@
package org.bitcoins.core.serializers.script
import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.protocol.script.{EmptyScriptPubKey, ScriptPubKey}
import org.bitcoins.core.protocol.script.{ EmptyScriptPubKey, ScriptPubKey }
import org.bitcoins.core.script.constant.ScriptToken
import org.bitcoins.core.serializers.RawBitcoinSerializer
@ -12,7 +12,7 @@ import scala.util.Try
*/
trait RawScriptPubKeyParser extends RawBitcoinSerializer[ScriptPubKey] {
override def read(bytes : List[Byte]) : ScriptPubKey = {
override def read(bytes: List[Byte]): ScriptPubKey = {
if (bytes.isEmpty) EmptyScriptPubKey
else {
val compactSizeUInt = CompactSizeUInt.parseCompactSizeUInt(bytes)
@ -20,12 +20,12 @@ trait RawScriptPubKeyParser extends RawBitcoinSerializer[ScriptPubKey] {
//but scala collections don't allow you to use 'slice' with longs
val len = Try(compactSizeUInt.num.toInt).getOrElse(Int.MaxValue)
val scriptPubKeyBytes = bytes.slice(compactSizeUInt.size.toInt, len + compactSizeUInt.size.toInt)
val script : List[ScriptToken] = ScriptParser.fromBytes(scriptPubKeyBytes)
val script: List[ScriptToken] = ScriptParser.fromBytes(scriptPubKeyBytes)
ScriptPubKey.fromAsm(script)
}
}
override def write(scriptPubKey : ScriptPubKey): Seq[Byte] = scriptPubKey.bytes
override def write(scriptPubKey: ScriptPubKey): Seq[Byte] = scriptPubKey.bytes
}
object RawScriptPubKeyParser extends RawScriptPubKeyParser

View file

@ -13,19 +13,21 @@ import scala.util.Try
*/
sealed abstract class RawScriptSignatureParser extends RawBitcoinSerializer[ScriptSignature] {
def read(bytes : List[Byte]) : ScriptSignature = {
def read(bytes: List[Byte]): ScriptSignature = {
if (bytes.isEmpty) EmptyScriptSignature
else {
val compactSizeUInt = CompactSizeUInt.parseCompactSizeUInt(bytes)
//TODO: Figure out a better way to do this, we can theoretically have numbers larger than Int.MaxValue,
val scriptSigBytes = bytes.slice(compactSizeUInt.size.toInt,
compactSizeUInt.num.toInt + compactSizeUInt.size.toInt)
val scriptTokens : List[ScriptToken] = ScriptParser.fromBytes(scriptSigBytes)
val scriptSigBytes = bytes.slice(
compactSizeUInt.size.toInt,
compactSizeUInt.num.toInt + compactSizeUInt.size.toInt
)
val scriptTokens: List[ScriptToken] = ScriptParser.fromBytes(scriptSigBytes)
ScriptSignature.fromAsm(scriptTokens)
}
}
def write(scriptSig : ScriptSignature): Seq[Byte] = scriptSig.bytes
def write(scriptSig: ScriptSignature): Seq[Byte] = scriptSig.bytes
}
object RawScriptSignatureParser extends RawScriptSignatureParser

View file

@ -9,27 +9,27 @@ import org.bitcoins.core.util.BitcoinSUtil
import scala.annotation.tailrec
/**
* Created by chris on 12/14/16.
*/
* Created by chris on 12/14/16.
*/
sealed abstract class RawScriptWitnessParser extends RawBitcoinSerializer[ScriptWitness] {
def read(bytes: List[Byte]): ScriptWitness = {
//first byte is the number of stack items
val stackSize = CompactSizeUInt.parseCompactSizeUInt(bytes)
val (_,stackBytes) = bytes.splitAt(stackSize.size.toInt)
val (_, stackBytes) = bytes.splitAt(stackSize.size.toInt)
@tailrec
def loop(remainingBytes: Seq[Byte], accum: Seq[Seq[Byte]], remainingStackItems: UInt64): Seq[Seq[Byte]] = {
if (remainingStackItems <= UInt64.zero) accum
else {
val elementSize = CompactSizeUInt.parseCompactSizeUInt(remainingBytes)
val (_,stackElementBytes) = remainingBytes.splitAt(elementSize.size.toInt)
val (_, stackElementBytes) = remainingBytes.splitAt(elementSize.size.toInt)
val stackElement = stackElementBytes.take(elementSize.num.toInt)
val (_,newRemainingBytes) = stackElementBytes.splitAt(stackElement.size)
val (_, newRemainingBytes) = stackElementBytes.splitAt(stackElement.size)
loop(newRemainingBytes, stackElement +: accum, remainingStackItems - UInt64.one)
}
}
//note there is no 'reversing' the accum, in bitcoin-s we assume the top of the stack is the 'head' element in the sequence
val stack = loop(stackBytes,Nil,stackSize.num)
val stack = loop(stackBytes, Nil, stackSize.num)
val witness = ScriptWitness(stack)
witness
}
@ -44,7 +44,7 @@ sealed abstract class RawScriptWitnessParser extends RawBitcoinSerializer[Script
loop(remainingStack.tail, serialization +: accum)
}
}
val stackItems: Seq[Seq[Byte]] = loop(scriptWitness.stack.reverse,Nil)
val stackItems: Seq[Seq[Byte]] = loop(scriptWitness.stack.reverse, Nil)
val size = CompactSizeUInt(UInt64(stackItems.size))
(size.bytes +: stackItems).flatten
}

View file

@ -3,7 +3,7 @@ package org.bitcoins.core.serializers.script
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.script._
import org.bitcoins.core.script.constant._
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil, Factory}
import org.bitcoins.core.util.{ BitcoinSLogger, BitcoinSUtil, Factory }
import scala.annotation.tailrec
import scala.util.Try
@ -14,23 +14,24 @@ import scala.util.Try
sealed abstract class ScriptParser extends Factory[List[ScriptToken]] {
/** Parses a list of bytes into a list of script tokens */
def fromBytes(bytes : Seq[Byte]) : List[ScriptToken] = {
val scriptTokens : List[ScriptToken] = parse(bytes)
def fromBytes(bytes: Seq[Byte]): List[ScriptToken] = {
val scriptTokens: List[ScriptToken] = parse(bytes)
scriptTokens
}
/** Parses an asm output script of a transaction
/**
* Parses an asm output script of a transaction
* example: "OP_DUP OP_HASH160 e2e7c1ab3f807151e832dd1accb3d4f5d7d19b4b OP_EQUALVERIFY OP_CHECKSIG"
* example: ["0", "IF 0x50 ENDIF 1", "P2SH,STRICTENC", "0x50 is reserved (ok if not executed)"] (from script_valid.json) */
def fromString(str : String) : List[ScriptToken] = {
if (str.size > 1 && str.substring(0,2) == "0x" && str.split(" ").size == 1) {
* example: ["0", "IF 0x50 ENDIF 1", "P2SH,STRICTENC", "0x50 is reserved (ok if not executed)"] (from script_valid.json)
*/
def fromString(str: String): List[ScriptToken] = {
if (str.size > 1 && str.substring(0, 2) == "0x" && str.split(" ").size == 1) {
//parse this as a byte array that is led with a 0x for example
//0x4e03000000ffff
val hex = str.substring(2,str.size)
val hex = str.substring(2, str.size)
fromBytes(BitcoinSUtil.decodeHex(hex))
} else {
val scriptTokens : List[ScriptToken] = parse(str)
val scriptTokens: List[ScriptToken] = parse(str)
scriptTokens
}
}
@ -38,49 +39,49 @@ sealed abstract class ScriptParser extends Factory[List[ScriptToken]] {
/**
* Parses a string to a sequence of script tokens
* example: "OP_DUP OP_HASH160 e2e7c1ab3f807151e832dd1accb3d4f5d7d19b4b OP_EQUALVERIFY OP_CHECKSIG"
* example: ["0", "IF 0x50 ENDIF 1", "P2SH,STRICTENC", "0x50 is reserved (ok if not executed)"] (from script_valid.json) */
private def parse(str : String) : List[ScriptToken] = {
* example: ["0", "IF 0x50 ENDIF 1", "P2SH,STRICTENC", "0x50 is reserved (ok if not executed)"] (from script_valid.json)
*/
private def parse(str: String): List[ScriptToken] = {
@tailrec
def loop(operations : List[String], accum : List[Byte]) : List[Byte] = {
/* logger.debug("Attempting to parse: " + operations.headOption)
def loop(operations: List[String], accum: List[Byte]): List[Byte] = {
/* logger.debug("Attempting to parse: " + operations.headOption)
logger.debug("Accum: " + accum)*/
operations match {
//for parsing strings like 'Az', need to remove single quotes
//example: [[https://github.com/bitcoin/bitcoin/blob/master/src/test/data/script_valid.json#L24]]
case h :: t if (h.size > 0 && h.head == ''' && h.last == ''') =>
val strippedQuotes = h.replace("'","")
val strippedQuotes = h.replace("'", "")
if (strippedQuotes.size == 0) {
loop(t, OP_0.bytes.toList ++ accum)
} else {
val bytes : Seq[Byte] = BitcoinSUtil.decodeHex(BitcoinSUtil.flipEndianness(strippedQuotes.getBytes.toList))
val bytes: Seq[Byte] = BitcoinSUtil.decodeHex(BitcoinSUtil.flipEndianness(strippedQuotes.getBytes.toList))
val bytesToPushOntoStack : List[ScriptToken] = (bytes.size > 75) match {
val bytesToPushOntoStack: List[ScriptToken] = (bytes.size > 75) match {
case true =>
val scriptNumber = ScriptNumber(BitcoinSUtil.flipEndianness(ScriptNumberUtil.longToHex(bytes.size)))
bytes.size match {
case size if size < Byte.MaxValue =>
List(scriptNumber,OP_PUSHDATA1)
List(scriptNumber, OP_PUSHDATA1)
case size if size < Short.MaxValue =>
List(scriptNumber,OP_PUSHDATA2)
List(scriptNumber, OP_PUSHDATA2)
case size if size < Int.MaxValue =>
List(scriptNumber,OP_PUSHDATA4)
List(scriptNumber, OP_PUSHDATA4)
}
case false => List(BytesToPushOntoStack(bytes.size))
}
loop(t, bytes.toList ++ bytesToPushOntoStack.flatMap(_.bytes) ++ accum)
loop(t, bytes.toList ++ bytesToPushOntoStack.flatMap(_.bytes) ++ accum)
}
//if we see a byte constant in the form of "0x09adb"
case h :: t if (h.size > 1 && h.substring(0,2) == "0x") =>
loop(t,BitcoinSUtil.decodeHex(h.substring(2,h.size).toLowerCase).toList.reverse ++ accum)
case h :: t if (h.size > 1 && h.substring(0, 2) == "0x") =>
loop(t, BitcoinSUtil.decodeHex(h.substring(2, h.size).toLowerCase).toList.reverse ++ accum)
//skip the empty string
case h :: t if (h == "") => loop(t,accum)
case h :: t if (h == "") => loop(t, accum)
case h :: t if (h == "0") => loop(t, OP_0.bytes.toList ++ accum)
case h :: t if (ScriptOperation.fromString(h).isDefined) =>
val op = ScriptOperation.fromString(h).get
loop(t,op.bytes.toList ++ accum)
loop(t, op.bytes.toList ++ accum)
case h :: t if (tryParsingLong(h)) =>
val hexLong = BitcoinSUtil.flipEndianness(ScriptNumberUtil.longToHex(h.toLong))
val bytesToPushOntoStack = BytesToPushOntoStack(hexLong.size / 2)
@ -94,14 +95,13 @@ sealed abstract class ScriptParser extends Factory[List[ScriptToken]] {
case Nil => accum
}
}
if (tryParsingLong(str) && str.size > 1 && str.substring(0,2) != "0x") {
if (tryParsingLong(str) && str.size > 1 && str.substring(0, 2) != "0x") {
//for the case when there is just a single decimal constant
//i.e. "8388607"
val scriptNumber = ScriptNumber(parseLong(str))
val bytesToPushOntoStack = BytesToPushOntoStack(scriptNumber.bytes.size)
List(bytesToPushOntoStack,scriptNumber)
}
else if (BitcoinSUtil.isHex(str) && str.toLowerCase == str) {
List(bytesToPushOntoStack, scriptNumber)
} else if (BitcoinSUtil.isHex(str) && str.toLowerCase == str) {
//if the given string is hex, it is pretty straight forward to parse it
//convert the hex string to a byte array and parse it
val bytes = BitcoinSUtil.decodeHex(str)
@ -116,19 +116,19 @@ sealed abstract class ScriptParser extends Factory[List[ScriptToken]] {
}
}
/**
* Parses a byte array into a the asm operations for a script
* will throw an exception if it fails to parse a op code */
private def parse(bytes : List[Byte]) : List[ScriptToken] = {
* will throw an exception if it fails to parse a op code
*/
private def parse(bytes: List[Byte]): List[ScriptToken] = {
@tailrec
def loop(bytes : List[Byte], accum : List[ScriptToken]) : List[ScriptToken] = {
def loop(bytes: List[Byte], accum: List[ScriptToken]): List[ScriptToken] = {
//logger.debug("Byte to be parsed: " + bytes.headOption)
bytes match {
case h :: t =>
val op = ScriptOperation(h).get
val parsingHelper : ParsingHelper[Byte] = parseOperationByte(op,accum,t)
loop(parsingHelper.tail,parsingHelper.accum)
val op = ScriptOperation(h).get
val parsingHelper: ParsingHelper[Byte] = parseOperationByte(op, accum, t)
loop(parsingHelper.tail, parsingHelper.accum)
case Nil => accum
}
}
@ -136,74 +136,73 @@ sealed abstract class ScriptParser extends Factory[List[ScriptToken]] {
}
private def parse(bytes : Seq[Byte]) : List[ScriptToken] = parse(bytes.toList)
private def parse(bytes: Seq[Byte]): List[ScriptToken] = parse(bytes.toList)
/** Parses a redeem script from the given script token */
def parseRedeemScript(scriptToken : ScriptToken) : Try[List[ScriptToken]] = {
val redeemScript : Try[List[ScriptToken]] = Try(parse(scriptToken.bytes))
def parseRedeemScript(scriptToken: ScriptToken): Try[List[ScriptToken]] = {
val redeemScript: Try[List[ScriptToken]] = Try(parse(scriptToken.bytes))
redeemScript
}
/**
* Slices the amount of bytes specified in the bytesToPushOntoStack parameter and then creates a script constant
* from those bytes. Returns the script constant and the byte array without the script constant */
private def sliceConstant[T](bytesToPushOntoStack: BytesToPushOntoStack, data : List[T]) : (List[T], List[T]) = {
* from those bytes. Returns the script constant and the byte array without the script constant
*/
private def sliceConstant[T](bytesToPushOntoStack: BytesToPushOntoStack, data: List[T]): (List[T], List[T]) = {
val finalIndex = bytesToPushOntoStack.opCode
val dataConstant = data.slice(0,finalIndex)
(dataConstant,data.slice(finalIndex,data.size))
val dataConstant = data.slice(0, finalIndex)
(dataConstant, data.slice(finalIndex, data.size))
}
/**
* Parses the bytes in string format, an example input would look like this
* "0x09 0x00000000 0x00000000 0x10"
* see [[https://github.com/bitcoin/bitcoin/blob/master/src/test/data/script_valid.json#L21-L25]]
* for examples of this */
def parseBytesFromString(s: String) : List[ScriptConstant] = {
* for examples of this
*/
def parseBytesFromString(s: String): List[ScriptConstant] = {
//logger.debug("Parsing bytes from string " + s)
val scriptConstants : List[ScriptConstant] = (raw"\b0x([0-9a-f]+)\b".r
val scriptConstants: List[ScriptConstant] = (raw"\b0x([0-9a-f]+)\b".r
.findAllMatchIn(s.toLowerCase)
.map(g =>
// 1 hex = 4 bits therefore 16 hex characters * 4 bits = 64
// if it is not smaller than 16 hex characters it cannot
//fit inside of a scala long
//therefore store it as a script constant
if (g.group(1).size <= 16) {
ScriptNumber(g.group(1))
} else {
ScriptConstant(g.group(1))
}).toList)
// 1 hex = 4 bits therefore 16 hex characters * 4 bits = 64
// if it is not smaller than 16 hex characters it cannot
//fit inside of a scala long
//therefore store it as a script constant
if (g.group(1).size <= 16) {
ScriptNumber(g.group(1))
} else {
ScriptConstant(g.group(1))
}).toList)
scriptConstants
}
private sealed case class ParsingHelper[T](tail : List[T], accum : List[ScriptToken])
private sealed case class ParsingHelper[T](tail: List[T], accum: List[ScriptToken])
/**
* Parses an operation if the tail is a List[Byte]
* If the operation is a bytesToPushOntoStack, it pushes the number of bytes onto the stack
* specified by the bytesToPushOntoStack
* i.e. If the operation was BytesToPushOntoStackImpl(5), it would slice 5 bytes off of the tail and
* places them into a ScriptConstant and add them to the accumulator. */
private def parseOperationByte(op : ScriptOperation, accum : List[ScriptToken], tail : List[Byte]) : ParsingHelper[Byte] = {
* places them into a ScriptConstant and add them to the accumulator.
*/
private def parseOperationByte(op: ScriptOperation, accum: List[ScriptToken], tail: List[Byte]): ParsingHelper[Byte] = {
op match {
case bytesToPushOntoStack : BytesToPushOntoStack =>
case bytesToPushOntoStack: BytesToPushOntoStack =>
//logger.debug("Parsing operation byte: " +bytesToPushOntoStack )
//means that we need to push x amount of bytes on to the stack
val (constant,newTail) = sliceConstant(bytesToPushOntoStack,tail)
val (constant, newTail) = sliceConstant(bytesToPushOntoStack, tail)
val scriptConstant = ScriptConstant(constant)
ParsingHelper(newTail,scriptConstant :: bytesToPushOntoStack :: accum)
case OP_PUSHDATA1 => parseOpPushData(op,accum,tail)
case OP_PUSHDATA2 => parseOpPushData(op,accum,tail)
case OP_PUSHDATA4 => parseOpPushData(op,accum,tail)
ParsingHelper(newTail, scriptConstant :: bytesToPushOntoStack :: accum)
case OP_PUSHDATA1 => parseOpPushData(op, accum, tail)
case OP_PUSHDATA2 => parseOpPushData(op, accum, tail)
case OP_PUSHDATA4 => parseOpPushData(op, accum, tail)
case _ =>
//means that we need to push the operation onto the stack
ParsingHelper(tail,op :: accum)
ParsingHelper(tail, op :: accum)
}
}
/**
* Parses OP_PUSHDATA operations correctly. Slices the appropriate amount of bytes off of the tail and pushes
* them onto the accumulator.
@ -212,20 +211,20 @@ sealed abstract class ScriptParser extends Factory[List[ScriptToken]] {
* @param tail the bytes to be parsed still
* @return
*/
private def parseOpPushData(op : ScriptOperation, accum : List[ScriptToken], tail : List[Byte]) : ParsingHelper[Byte] = {
private def parseOpPushData(op: ScriptOperation, accum: List[ScriptToken], tail: List[Byte]): ParsingHelper[Byte] = {
def parseOpPushDataHelper(numBytes : Int) : ParsingHelper[Byte] = {
def parseOpPushDataHelper(numBytes: Int): ParsingHelper[Byte] = {
//next numBytes is the size of the script constant
val scriptConstantHex = tail.slice(0,numBytes)
val scriptConstantHex = tail.slice(0, numBytes)
val uInt32Push = UInt32(BitcoinSUtil.flipEndianness(scriptConstantHex))
//need this for the case where we have an OP_PUSHDATA4 with a number larger than a int32 can hold
//TODO: Review this more, see this transaction's scriptSig as an example: b30d3148927f620f5b1228ba941c211fdabdae75d0ba0b688a58accbf018f3cc
val bytesForPushOp = Try(uInt32Push.toInt).getOrElse(Int.MaxValue)
val bytesToPushOntoStack = ScriptConstant(scriptConstantHex)
val scriptConstantBytes = tail.slice(numBytes,bytesForPushOp + numBytes)
val scriptConstantBytes = tail.slice(numBytes, bytesForPushOp + numBytes)
val scriptConstant = ScriptConstant(scriptConstantBytes)
val restOfBytes = tail.slice(bytesForPushOp + numBytes,tail.size)
buildParsingHelper(op,bytesToPushOntoStack,scriptConstant,restOfBytes,accum)
val restOfBytes = tail.slice(bytesForPushOp + numBytes, tail.size)
buildParsingHelper(op, bytesToPushOntoStack, scriptConstant, restOfBytes, accum)
}
op match {
@ -235,7 +234,7 @@ sealed abstract class ScriptParser extends Factory[List[ScriptToken]] {
parseOpPushDataHelper(2)
case OP_PUSHDATA4 =>
parseOpPushDataHelper(4)
case _ : ScriptToken => throw new RuntimeException("parseOpPushData can only parse OP_PUSHDATA operations")
case _: ScriptToken => throw new RuntimeException("parseOpPushData can only parse OP_PUSHDATA operations")
}
}
@ -248,22 +247,26 @@ sealed abstract class ScriptParser extends Factory[List[ScriptToken]] {
* @param accum the accumulator filled with script tokens that have already been parsed
* @return
*/
private def buildParsingHelper(op : ScriptOperation, bytesToPushOntoStack : ScriptConstant,
scriptConstant : ScriptConstant, restOfBytes : List[Byte], accum : List[ScriptToken]) : ParsingHelper[Byte] = {
private def buildParsingHelper(op: ScriptOperation, bytesToPushOntoStack: ScriptConstant,
scriptConstant: ScriptConstant, restOfBytes: List[Byte], accum: List[ScriptToken]): ParsingHelper[Byte] = {
if (bytesToPushOntoStack.hex == "00") {
//if we need to push 0 bytes onto the stack we do not add the script constant
ParsingHelper[Byte](restOfBytes,
bytesToPushOntoStack :: op :: accum)
} else ParsingHelper[Byte](restOfBytes,
scriptConstant :: bytesToPushOntoStack :: op :: accum)
ParsingHelper[Byte](
restOfBytes,
bytesToPushOntoStack :: op :: accum
)
} else ParsingHelper[Byte](
restOfBytes,
scriptConstant :: bytesToPushOntoStack :: op :: accum
)
}
/** Checks if a string can be cast to an int */
private def tryParsingLong(str : String): Boolean = Try(parseLong(str)).isSuccess
private def tryParsingLong(str: String): Boolean = Try(parseLong(str)).isSuccess
private def parseLong(str : String) = {
if (str.substring(0,2) == "0x") {
val strRemoveHex = str.substring(2,str.size)
private def parseLong(str: String) = {
if (str.substring(0, 2) == "0x") {
val strRemoveHex = str.substring(2, str.size)
ScriptNumberUtil.toLong(strRemoveHex)
} else str.toLong
}

View file

@ -1,8 +1,8 @@
package org.bitcoins.core.serializers.transaction
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.transaction.{BaseTransaction, TransactionInput, TransactionOutput}
import org.bitcoins.core.serializers.{RawBitcoinSerializer, RawSerializerHelper}
import org.bitcoins.core.protocol.transaction.{ BaseTransaction, TransactionInput, TransactionOutput }
import org.bitcoins.core.serializers.{ RawBitcoinSerializer, RawSerializerHelper }
/**
* Created by chris on 1/14/16.
@ -12,27 +12,32 @@ import org.bitcoins.core.serializers.{RawBitcoinSerializer, RawSerializerHelper}
sealed abstract class RawBaseTransactionParser extends RawBitcoinSerializer[BaseTransaction] {
val helper = RawSerializerHelper
def read(bytes : List[Byte]): BaseTransaction = {
def read(bytes: List[Byte]): BaseTransaction = {
val versionBytes = bytes.take(4)
val version = UInt32(versionBytes.reverse)
val txInputBytes = bytes.slice(4,bytes.size)
val (inputs,outputBytes) = helper.parseCmpctSizeUIntSeq(txInputBytes,RawTransactionInputParser.read(_))
val (outputs,lockTimeBytes) = helper.parseCmpctSizeUIntSeq(outputBytes,
RawTransactionOutputParser.read(_))
val txInputBytes = bytes.slice(4, bytes.size)
val (inputs, outputBytes) = helper.parseCmpctSizeUIntSeq(txInputBytes, RawTransactionInputParser.read(_))
val (outputs, lockTimeBytes) = helper.parseCmpctSizeUIntSeq(
outputBytes,
RawTransactionOutputParser.read(_)
)
val lockTime = UInt32(lockTimeBytes.take(4).reverse)
BaseTransaction(version,inputs,outputs,lockTime)
BaseTransaction(version, inputs, outputs, lockTime)
}
def write(tx : BaseTransaction): Seq[Byte] = {
def write(tx: BaseTransaction): Seq[Byte] = {
val version = tx.version.bytes.reverse
val inputs: Seq[Byte] = helper.writeCmpctSizeUInt[TransactionInput](tx.inputs,
RawTransactionInputParser.write(_))
val outputs: Seq[Byte] = helper.writeCmpctSizeUInt[TransactionOutput](tx.outputs,
RawTransactionOutputParser.write(_))
val inputs: Seq[Byte] = helper.writeCmpctSizeUInt[TransactionInput](
tx.inputs,
RawTransactionInputParser.write(_)
)
val outputs: Seq[Byte] = helper.writeCmpctSizeUInt[TransactionOutput](
tx.outputs,
RawTransactionOutputParser.write(_)
)
val lockTime = tx.lockTime.bytes.reverse
version ++ inputs ++ outputs ++ lockTime
}
}
object RawBaseTransactionParser extends RawBaseTransactionParser

View file

@ -2,7 +2,7 @@ package org.bitcoins.core.serializers.transaction
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.script.ScriptSignature
import org.bitcoins.core.protocol.transaction.{TransactionInput, TransactionOutPoint}
import org.bitcoins.core.protocol.transaction.{ TransactionInput, TransactionOutPoint }
import org.bitcoins.core.serializers.RawBitcoinSerializer
import org.bitcoins.core.serializers.script.RawScriptSignatureParser
import org.bitcoins.core.util.BitcoinSUtil
@ -13,26 +13,23 @@ import org.bitcoins.core.util.BitcoinSUtil
*/
sealed abstract class RawTransactionInputParser extends RawBitcoinSerializer[TransactionInput] {
override def read(bytes : List[Byte]) : TransactionInput = {
override def read(bytes: List[Byte]): TransactionInput = {
val outPoint = TransactionOutPoint(bytes.take(36))
val scriptSigBytes = bytes.slice(36,bytes.size)
val scriptSigBytes = bytes.slice(36, bytes.size)
val scriptSig: ScriptSignature = RawScriptSignatureParser.read(scriptSigBytes)
val endOfScriptSigBytes = 36 + scriptSig.bytes.size
val lastInputByte = endOfScriptSigBytes + 4
val sequenceBytes = bytes.slice(endOfScriptSigBytes,lastInputByte)
val sequenceNumber : UInt32 = UInt32(sequenceBytes.reverse)
val txInput = TransactionInput(outPoint,scriptSig,sequenceNumber)
val sequenceBytes = bytes.slice(endOfScriptSigBytes, lastInputByte)
val sequenceNumber: UInt32 = UInt32(sequenceBytes.reverse)
val txInput = TransactionInput(outPoint, scriptSig, sequenceNumber)
txInput
}
/** Writes a single transaction input */
def write(input : TransactionInput): Seq[Byte] = {
def write(input: TransactionInput): Seq[Byte] = {
input.previousOutput.bytes ++ input.scriptSignature.bytes ++ input.sequence.bytes.reverse
}
}
object RawTransactionInputParser extends RawTransactionInputParser

Some files were not shown because too many files have changed in this diff Show more