mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-26 13:25:49 +01:00
Reformatting using scalariform
This commit is contained in:
parent
9c69ddc1a8
commit
74ceaeb0d4
281 changed files with 8457 additions and 8296 deletions
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
|
||||
}
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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(_))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
}
|
|
@ -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 block’s header.
|
||||
* This ensures no previous block can be changed without also changing this block’s header.
|
||||
*
|
||||
* @return the previous block's hash
|
||||
*/
|
||||
* A SHA256(SHA256()) hash in internal byte order of the previous block’s header.
|
||||
* This ensures no previous block can be changed without also changing this block’s 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 block’s 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 block’s 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)
|
||||
|
||||
}
|
|
@ -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 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(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 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(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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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]
|
||||
}
|
||||
|
|
|
@ -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] */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
Loading…
Add table
Reference in a new issue