This commit is contained in:
Tom McCabe 2016-12-14 13:25:56 -06:00
commit 8b1f4db05f
70 changed files with 2207 additions and 1756 deletions

View file

@ -30,32 +30,22 @@ trait DERSignatureUtil extends BitcoinSLogger {
else if (bytes.nonEmpty) {
//first byte must be 0x30
val firstByteIs0x30 = bytes.head == 0x30
logger.debug("firstByteIs0x30: " + firstByteIs0x30)
//second byte must indicate the length of the remaining byte array
val signatureSize = bytes(1).toLong
logger.debug("Encoded Sisgnature Size: " + signatureSize)
logger.debug("Actual Signature Size: " + bytes.slice(2,bytes.size).size)
//checks to see if the signature length is the same as the signatureSize val
val signatureLengthIsCorrect = signatureSize == bytes.slice(2,bytes.size).size
logger.debug("Signature length is correct: " + signatureLengthIsCorrect)
//third byte must be 0x02
val thirdByteIs0x02 = bytes(2) == 0x02
logger.debug("Third byte is 0x02: " + thirdByteIs0x02)
//this is the size of the r value in the signature
val rSize = bytes(3)
logger.debug("R size: " + rSize)
//r value in the signature
val r = bytes.slice(3,rSize+3)
logger.debug("R: " + BitcoinSUtil.encodeHex(r))
//this 0x02 separates the r and s value )in the signature
val second0x02Exists = bytes(rSize + 4) == 0x02
logger.debug("Second 0x02 exists: " + second0x02Exists)
//this is the size of the s value in the signature
val sSize = bytes(rSize + 4)
logger.debug("S Size: " + sSize)
val s = bytes.slice(rSize + 4 + 1, bytes.size)
logger.debug("S: " + BitcoinSUtil.encodeHex(s))
firstByteIs0x30 && signatureLengthIsCorrect && thirdByteIs0x02 &&
second0x02Exists
} else true
@ -152,71 +142,66 @@ trait DERSignatureUtil extends BitcoinSLogger {
//empty signature is given to us, aka 0 bytes.
if (bytes.size == 0) return true
//check if the bytes are ATLEAST der encoded
/* val isDerEncoded = isDEREncoded(bytes)
if (!isDerEncoded) return false
logger.debug("Signature is at minimum DER encoded")*/
if (bytes.size < 9) return false
logger.debug("signature is the minimum size for strict der encoding")
//logger.debug("signature is the minimum size for strict der encoding")
if (bytes.size > 73) return false
logger.debug("signature is under the maximum size for strict der encoding")
//logger.debug("signature is under the maximum size for strict der encoding")
// A signature is of type 0x30 (compound)
if (bytes.head != 0x30) return false
logger.debug("First byte is 0x30")
//logger.debug("First byte is 0x30")
// Make sure the length covers the entire signature.
if (bytes(1) != bytes.size - 3) return false
logger.debug("Signature length covers the entire signature")
//logger.debug("Signature length covers the entire signature")
val rSize = bytes(3)
logger.debug("rSize: " + rSize)
//logger.debug("rSize: " + rSize)
// Make sure the length of the S element is still inside the signature.
if (5 + rSize >= bytes.size) return false
logger.debug("Length of S element is contained in the signature")
//logger.debug("Length of S element is contained in the signature")
// Extract the length of the S element.
val sSize = bytes(5 + rSize)
logger.debug("sSize: " + sSize)
//logger.debug("sSize: " + sSize)
// Verify that the length of the signature matches the sum of the length
// of the elements.
if ((rSize + sSize + 7) != bytes.size) return false
logger.debug("Verify that the length of the signature matches the sum of the length of the elements.")
//logger.debug("Verify that the length of the signature matches the sum of the length of the elements.")
// Check whether the R element is an integer.
if (bytes(2) != 0x02) return false
logger.debug("R element is an integer")
//logger.debug("R element is an integer")
// Zero-length integers are not allowed for R.
if (rSize == 0) return false
logger.debug("r is not a zero length integer")
//logger.debug("r is not a zero length integer")
// Negative numbers are not allowed for R.
if ((bytes(4) & 0x80) != 0) return false
logger.debug("r is not a negative number")
//logger.debug("r is not a negative number")
// 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
logger.debug("There were not any null bytes at the start of R")
//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
logger.debug("The S element is an integer")
//logger.debug("The S element is an integer")
// Zero-length integers are not allowed for S.
if (sSize == 0) return false
logger.debug("S was not a zero length integer")
//logger.debug("S was not a zero length integer")
// Negative numbers are not allowed for S.
if ((bytes(rSize + 6) & 0x80) != 0) return false
logger.debug("s was not a negative number")
//logger.debug("s was not a negative number")
// Null bytes at the start of S are not allowed, unless S would otherwise be
// interpreted as a negative number.
if (sSize > 1 && (bytes(rSize + 6) == 0x00) && !((bytes(rSize + 7) & 0x80) != 0)) return false
logger.debug("There were not any null bytes at the start of S")
//logger.debug("There were not any null bytes at the start of S")
//if we made it to this point without returning false this must be a valid strictly encoded der sig
true
}

View file

@ -2,12 +2,14 @@ package org.bitcoins.core.crypto
import org.bitcoins.core.config.TestNet3
import org.bitcoins.core.number.Int32
import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction.{Transaction, TransactionInput}
import org.bitcoins.core.script.ScriptProgram
import org.bitcoins.core.script.constant.{ScriptConstant, ScriptToken}
import org.bitcoins.core.script.crypto._
import org.bitcoins.core.script.flag.{ScriptFlag, ScriptFlagUtil, ScriptVerifyDerSig}
import org.bitcoins.core.script.result.ScriptErrorWitnessPubKeyType
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil, BitcoinScriptUtil}
import org.slf4j.LoggerFactory
@ -24,7 +26,7 @@ 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
@ -35,7 +37,7 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
def checkSignature(txSignatureComponent : TransactionSignatureComponent, script : Seq[ScriptToken],
pubKey: ECPublicKey, signature : ECDigitalSignature, flags : Seq[ScriptFlag]) : TransactionSignatureCheckerResult = {
logger.info("Signature: " + signature)
val pubKeyEncodedCorrectly = BitcoinScriptUtil.checkPubKeyEncoding(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)
SignatureValidationFailureNotStrictDerEncoding
@ -47,42 +49,25 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
logger.error("signature: " + signature.bytes)
logger.error("Hash type was not defined on the signature")
ScriptValidationFailureHashType
} else if (!pubKeyEncodedCorrectly) {
logger.error("The public key given for signature checking was not encoded correctly")
SignatureValidationFailurePubKeyEncoding
} else if (pubKeyEncodedCorrectly.isDefined) {
val err = pubKeyEncodedCorrectly.get
val result = if (err == ScriptErrorWitnessPubKeyType) ScriptValidationFailureWitnessPubKeyType else SignatureValidationFailurePubKeyEncoding
logger.error("The public key given for signature checking was not encoded correctly, err: " + result)
result
} else {
//we need to check if the scriptSignature has a redeemScript
//in that case, we need to pass the redeemScript to the TransactionSignatureChecker
//we do this by setting the scriptPubKey inside of txSignatureComponent to the redeemScript
//instead of the p2sh scriptPubKey it was previously
//as the scriptPubKey instead of the one inside of ScriptProgram
val sigsRemovedScript : Seq[ScriptToken] = txSignatureComponent.scriptSignature match {
case s : P2SHScriptSignature =>
//needs to be here for removing all sigs from OP_CHECKMULTISIG
//https://github.com/bitcoin/bitcoin/blob/master/src/test/data/tx_valid.json#L177
//Finally CHECKMULTISIG removes all signatures prior to hashing the script containing those signatures.
//In conjunction with the SIGHASH_SINGLE bug this lets us test whether or not FindAndDelete() is actually
// present in scriptPubKey/redeemScript evaluation by including a signature of the digest 0x01
// We can compute in advance for our pubkey, embed it it in the scriptPubKey, and then also
// using a normal SIGHASH_ALL signature. If FindAndDelete() wasn't run, the 'bugged'
//signature would still be in the hashed script, and the normal signature would fail."
logger.info("Replacing redeemScript in txSignature component")
logger.info("Redeem script: " + s.redeemScript)
val sigsRemoved = removeSignaturesFromScript(s.signatures, s.redeemScript.asm)
sigsRemoved
case _ : P2PKHScriptSignature | _ : P2PKScriptSignature | _ : NonStandardScriptSignature
| _ : MultiSignatureScriptSignature | _ : CLTVScriptSignature | _ : CSVScriptSignature | EmptyScriptSignature =>
logger.debug("Script before sigRemoved: " + script)
logger.debug("Signature: " + signature)
logger.debug("PubKey: " + pubKey)
val sigsRemoved = removeSignatureFromScript(signature,script)
sigsRemoved
}
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 = TransactionSignatureSerializer.hashForSignature(txSignatureComponent.transaction,
txSignatureComponent.inputIndex,
sigsRemovedScript, hashType)
val hashForSignature = txSignatureComponent match {
case b : BaseTransactionSignatureComponent =>
TransactionSignatureSerializer.hashForSignature(txSignatureComponent.transaction,
txSignatureComponent.inputIndex,
sigsRemovedScript, hashType)
case w : WitnessV0TransactionSignatureComponent =>
TransactionSignatureSerializer.hashForSignature(w.transaction,w.inputIndex,sigsRemovedScript, hashType, w.amount)
}
logger.info("Hash for signature: " + BitcoinSUtil.encodeHex(hashForSignature.bytes))
val isValid = pubKey.verify(hashForSignature,signature)
if (isValid) SignatureValidationSuccess else SignatureValidationFailureIncorrectSignatures
@ -93,7 +78,6 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
* This is a helper function to check digital signatures against public keys
* if the signature does not match this public key, check it against the next
* public key in the sequence
*
* @param txSignatureComponent the tx signature component that contains all relevant transaction information
* @param script the script state this is needed in case there is an OP_CODESEPARATOR inside the script
* @param sigs the signatures that are being checked for validity
@ -105,19 +89,19 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
final def multiSignatureEvaluator(txSignatureComponent : TransactionSignatureComponent, script : Seq[ScriptToken],
sigs : List[ECDigitalSignature], pubKeys : List[ECPublicKey], flags : Seq[ScriptFlag],
requiredSigs : Long) : TransactionSignatureCheckerResult = {
logger.info("Signatures inside of helper: " + sigs)
logger.info("Public keys inside of helper: " + pubKeys)
logger.debug("Signatures inside of helper: " + sigs)
logger.debug("Public keys inside of helper: " + pubKeys)
if (sigs.size > pubKeys.size) {
//this is how bitcoin core treats this. If there are ever any more
//signatures than public keys remaining we immediately return
//false https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L955-L959
logger.info("We have more sigs than we have public keys remaining")
logger.warn("We have more sigs than we have public keys remaining")
SignatureValidationFailureIncorrectSignatures
}
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/master/src/script/interpreter.cpp#L914-915
logger.info("We do not have enough sigs to meet the threshold of requireSigs in the multiSignatureScriptPubKey")
logger.warn("We do not have enough sigs to meet the threshold of requireSigs in the multiSignatureScriptPubKey")
SignatureValidationFailureSignatureCount
}
else if (sigs.nonEmpty && pubKeys.nonEmpty) {
@ -129,14 +113,10 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
multiSignatureEvaluator(txSignatureComponent, script, sigs.tail,pubKeys.tail,flags, requiredSigs - 1)
case SignatureValidationFailureIncorrectSignatures =>
multiSignatureEvaluator(txSignatureComponent, script, sigs, pubKeys.tail,flags, requiredSigs)
case SignatureValidationFailureNotStrictDerEncoding =>
SignatureValidationFailureNotStrictDerEncoding
case SignatureValidationFailureSignatureCount =>
SignatureValidationFailureSignatureCount
case SignatureValidationFailurePubKeyEncoding =>
SignatureValidationFailurePubKeyEncoding
case ScriptValidationFailureHighSValue => ScriptValidationFailureHighSValue
case ScriptValidationFailureHashType => ScriptValidationFailureHashType
case x @ (SignatureValidationFailureNotStrictDerEncoding | SignatureValidationFailureSignatureCount |
SignatureValidationFailurePubKeyEncoding | ScriptValidationFailureHighSValue |
ScriptValidationFailureHashType | ScriptValidationFailureWitnessPubKeyType) =>
x
}
} else if (sigs.isEmpty) {
//means that we have checked all of the sigs against the public keys
@ -144,40 +124,6 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
SignatureValidationSuccess
} else SignatureValidationFailureIncorrectSignatures
}
/**
* Removes the given [[ECDigitalSignature]] from the list of [[ScriptToken]] if it exists
* @return
*/
def removeSignatureFromScript(signature : ECDigitalSignature, script : Seq[ScriptToken]) : Seq[ScriptToken] = {
if (script.contains(ScriptConstant(signature.hex))) {
//replicates this line in bitcoin core
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L872
val sigIndex = script.indexOf(ScriptConstant(signature.hex))
logger.debug("SigIndex: " + sigIndex)
//remove sig and it's corresponding BytesToPushOntoStack
script.slice(0,sigIndex-1) ++ script.slice(sigIndex+1,script.size)
} else script
}
/**
* Removes the list of [[ECDigitalSignature]] from the list of [[ScriptToken]]
* @return
*/
def removeSignaturesFromScript(sigs : Seq[ECDigitalSignature], script : Seq[ScriptToken]) : Seq[ScriptToken] = {
@tailrec
def loop(remainingSigs : Seq[ECDigitalSignature], scriptTokens : Seq[ScriptToken]) : Seq[ScriptToken] = {
remainingSigs match {
case Nil => scriptTokens
case h :: t =>
val newScriptTokens = removeSignatureFromScript(h,scriptTokens)
loop(t,newScriptTokens)
}
}
loop(sigs,script)
}
}
object TransactionSignatureChecker extends TransactionSignatureChecker

View file

@ -7,11 +7,11 @@ package org.bitcoins.core.crypto
sealed trait TransactionSignatureCheckerResult {
/**
* Indicates if the transaction signature checker was successful or failed
* @return
*/
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
@ -21,51 +21,46 @@ case object SignatureValidationSuccess extends TransactionSignatureCheckerResult
def isValid = true
}
/**
* Signature validation failed because a signature was not encoded
* per the BIP66 rules https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki#specification
*/
case object SignatureValidationFailureNotStrictDerEncoding extends TransactionSignatureCheckerResult {
/** Indicates that there was an error when evaluating the signature of a transaction */
sealed trait SignatureValidationError extends TransactionSignatureCheckerResult {
def isValid = false
}
/**
* Signature validation failed because a signature was not encoded
* per the BIP66 rules [[https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki#specification]]
*/
case object SignatureValidationFailureNotStrictDerEncoding extends SignatureValidationError
/**
* Signature validation failed because there were not enough correct signatures for the transaction
* we were given
*/
case object SignatureValidationFailureIncorrectSignatures extends TransactionSignatureCheckerResult {
def isValid = false
}
case object SignatureValidationFailureIncorrectSignatures extends SignatureValidationError
/**
* This indicates that the signature validation failed because we have more signatures left to check
* than public keys remaining to check them against
* see https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L914-915
* see [[https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L914-915]]
*/
case object SignatureValidationFailureSignatureCount extends TransactionSignatureCheckerResult {
def isValid = false
}
case object SignatureValidationFailureSignatureCount extends SignatureValidationError
/**
* This indicates that the public key was not encoded correctly according to this function
* https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L202
* [[https://github.com/bitcoin/bitcoin/blob/528472111b4965b1a99c4bcf08ac5ec93d87f10f/src/script/interpreter.cpp#L214-L223]]
*/
case object SignatureValidationFailurePubKeyEncoding extends TransactionSignatureCheckerResult {
def isValid = false
}
case object SignatureValidationFailurePubKeyEncoding extends SignatureValidationError
/**
* This indicates that the digital signature did not have a Low S value as per BIP62
* https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#Low_S_values_in_signatures
* [[https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#Low_S_values_in_signatures]]
*/
case object ScriptValidationFailureHighSValue extends TransactionSignatureCheckerResult {
def isValid = false
}
case object ScriptValidationFailureHighSValue extends SignatureValidationError
/**
* Fails the script if the hash type is not defined on a digital signature
*/
case object ScriptValidationFailureHashType extends TransactionSignatureCheckerResult {
def isValid = false
}
case object ScriptValidationFailureHashType extends SignatureValidationError
/** Fails the script if the given public key was not compressed and the [[org.bitcoins.core.script.flag.ScriptVerifyWitnessPubKeyType]]
* flag was set */
case object ScriptValidationFailureWitnessPubKeyType extends SignatureValidationError

View file

@ -1,8 +1,9 @@
package org.bitcoins.core.crypto
import org.bitcoins.core.currency.CurrencyUnit
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.script.ScriptPubKey
import org.bitcoins.core.protocol.transaction.Transaction
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction.{Transaction, TransactionOutput, WitnessTransaction}
import org.bitcoins.core.script.flag.ScriptFlag
/**
@ -10,63 +11,95 @@ import org.bitcoins.core.script.flag.ScriptFlag
* Represents a transaction whose input is being checked against the spending conditions of the
* scriptPubKey
*/
trait TransactionSignatureComponent {
sealed trait TransactionSignatureComponent {
/**
* The transaction being checked for the validity of signatures
*
* @return
*/
/** The transaction being checked for the validity of signatures */
def transaction : Transaction
/**
* The index of the input whose script signature is being checked
*
* @return
*/
/** The index of the input whose script signature is being checked */
def inputIndex : UInt32
/**
* The script signature being checked
*
* @return
*/
/** The script signature being checked */
def scriptSignature = transaction.inputs(inputIndex.toInt).scriptSignature
/**
* The scriptPubKey for which the input is being checked against
*
* @return
*/
/** The scriptPubKey for which the input is being checked against */
def scriptPubKey : ScriptPubKey
/**
* The flags that are needed to verify if the signature is correct
*
* @return
*/
/** 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 [[TransactionSignatureComponent]] used to evaluate the the original Satoshi transaction digest algorithm */
sealed trait BaseTransactionSignatureComponent extends TransactionSignatureComponent {
override def sigVersion = SigVersionBase
}
/** The [[TransactionSignatureComponent]] used to represent all the components necessarily for BIP143
* [[https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki]]
*/
sealed trait WitnessV0TransactionSignatureComponent extends TransactionSignatureComponent {
override def transaction: WitnessTransaction
def witness: ScriptWitness = transaction.witness.witnesses(inputIndex.toInt)
/** The amount of [[CurrencyUnit]] this input is spending */
def amount: CurrencyUnit
/** The digest algorithm used to serialized/hash a transaction for signature creation/verification */
override def sigVersion: SignatureVersion = SigVersionWitnessV0
}
object TransactionSignatureComponent {
private sealed case class TransactionSignatureComponentImpl(transaction : Transaction, inputIndex : UInt32,
scriptPubKey : ScriptPubKey, flags : Seq[ScriptFlag]) extends TransactionSignatureComponent
private case class BaseTransactionSignatureComponentImpl(transaction : Transaction, inputIndex : UInt32,
scriptPubKey : ScriptPubKey, flags : Seq[ScriptFlag]) extends BaseTransactionSignatureComponent
def apply(transaction : Transaction, inputIndex : UInt32, scriptPubKey : ScriptPubKey,
flags : Seq[ScriptFlag]) : TransactionSignatureComponent = {
TransactionSignatureComponentImpl(transaction,inputIndex,scriptPubKey, flags)
def apply(transaction : WitnessTransaction, inputIndex : UInt32, scriptPubKey : ScriptPubKey,
flags : Seq[ScriptFlag], amount: CurrencyUnit) : WitnessV0TransactionSignatureComponent = {
WitnessV0TransactionSignatureComponent(transaction,inputIndex,scriptPubKey,flags,amount)
}
/**
* This factory method is used for changing the scriptPubKey inside of a txSignatureComponent
*
* @param oldTxSignatureComponent
* @param scriptPubKey
* @return
*/
def apply(oldTxSignatureComponent : TransactionSignatureComponent, scriptPubKey : ScriptPubKey) : TransactionSignatureComponent = {
TransactionSignatureComponentImpl(oldTxSignatureComponent.transaction,
oldTxSignatureComponent.inputIndex,scriptPubKey, oldTxSignatureComponent.flags)
def apply(transaction : Transaction, inputIndex : UInt32,
scriptPubKey : ScriptPubKey, flags : Seq[ScriptFlag]): BaseTransactionSignatureComponent = {
BaseTransactionSignatureComponentImpl(transaction,inputIndex,scriptPubKey,flags)
}
/** This factory method is used for changing the scriptPubKey inside of a txSignatureComponent */
def apply(oldTxSignatureComponent : TransactionSignatureComponent, scriptPubKey : ScriptPubKey) : TransactionSignatureComponent = oldTxSignatureComponent match {
case base: BaseTransactionSignatureComponent =>
TransactionSignatureComponent(base.transaction,
base.inputIndex,scriptPubKey, base.flags)
case w: WitnessV0TransactionSignatureComponent =>
TransactionSignatureComponent(w.transaction,w.inputIndex,scriptPubKey,w.flags,w.amount)
}
}
object WitnessV0TransactionSignatureComponent {
private case class WitnessV0TransactionSignatureComponentImpl(transaction : WitnessTransaction, inputIndex : UInt32,
scriptPubKey : ScriptPubKey, flags : Seq[ScriptFlag],
amount: CurrencyUnit) extends WitnessV0TransactionSignatureComponent
def apply(transaction : WitnessTransaction, inputIndex : UInt32, scriptPubKey : ScriptPubKey,
flags : Seq[ScriptFlag], amount: CurrencyUnit) : WitnessV0TransactionSignatureComponent = {
WitnessV0TransactionSignatureComponentImpl(transaction,inputIndex, scriptPubKey, flags, amount)
}
/** Note: The output passed here is the output we are spending,
* we use the [[CurrencyUnit]] and [[ScriptPubKey]] in that output for signing */
def apply(transaction : WitnessTransaction, inputIndex : UInt32, output : TransactionOutput,
flags : Seq[ScriptFlag]): WitnessV0TransactionSignatureComponent = {
WitnessV0TransactionSignatureComponent(transaction,inputIndex,output.scriptPubKey,flags,output.value)
}
}

View file

@ -1,12 +1,15 @@
package org.bitcoins.core.crypto
import org.bitcoins.core.currency.{CurrencyUnit, Satoshis}
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.script.constant.ScriptToken
import org.bitcoins.core.script.crypto._
import org.bitcoins.core.serializers.RawBitcoinSerializerHelper
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil, CryptoUtil}
import org.bitcoins.core.serializers.transaction.RawTransactionOutputParser
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil, BitcoinScriptUtil, CryptoUtil}
/**
* Created by chris on 2/16/16.
@ -21,31 +24,28 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper with Bit
/**
* 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
* @return
*/
private def errorHash : DoubleSha256Digest = DoubleSha256Digest(BitcoinSUtil.decodeHex("0100000000000000000000000000000000000000000000000000000000000000"))
/**
* Serializes a transaction to be signed by an ECKey
* follows the bitcoinj implementation which can be found here
* hashing is done in the hashForSignature function
* @param inputIndex
* @param script
* @param hashType
* @return
* follows the bitcoinj implementation
* hashing is done in the [[hashForSignature]] function
*/
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
// transaction that step isn't very helpful, but it doesn't add much cost relative to the actual
// EC math so we'll do it anyway.
// CScript's inside the Bitcoin Core codebase retain their compactSizeUInt
// while clearing out all of the actual asm operations in the CScript
val inputSigsRemoved = for {
input <- spendingTransaction.inputs
} yield TransactionInput(input,ScriptSignature.empty)
s = input.scriptSignature
} yield TransactionInput(input,NonStandardScriptSignature(s.compactSizeUInt.hex))
//make sure all scriptSigs have empty asm
inputSigsRemoved.map(input =>
require(input.scriptSignature.bytes.size == 0,"Input byte size was " + input.scriptSignature.bytes))
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.
@ -80,11 +80,11 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper with Bit
val sigHashBytes = hashType.num.bytes.reverse
//check the hash type
hashType match {
case _ : SIGHASH_NONE =>
case _: SIGHASH_NONE =>
val sigHashNoneTx : Transaction = sigHashNone(txWithInputSigsRemoved,inputIndex)
sigHashNoneTx.bytes ++ sigHashBytes
case _ : SIGHASH_SINGLE =>
case _: SIGHASH_SINGLE =>
if (inputIndex >= UInt32(spendingTransaction.outputs.size)) {
// comment copied from bitcoinj
// The input index is beyond the number of outputs, it's a buggy signature made by a broken
@ -101,25 +101,25 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper with Bit
sigHashSingleTx.bytes ++ sigHashBytes
}
case _ : SIGHASH_ALL =>
case _: SIGHASH_ALL =>
val sigHashAllTx : Transaction = sigHashAll(txWithInputSigsRemoved,inputIndex)
sigHashAllTx.bytes ++ sigHashBytes
case _ : SIGHASH_ANYONECANPAY =>
case _: SIGHASH_ANYONECANPAY =>
val txWithInputsRemoved = sigHashAnyoneCanPay(txWithInputSigsRemoved,inputWithConnectedScript)
txWithInputsRemoved.bytes ++ sigHashBytes
case _ : SIGHASH_ALL_ANYONECANPAY =>
case _: SIGHASH_ALL_ANYONECANPAY =>
val sigHashAllTx = sigHashAll(txWithInputSigsRemoved,inputIndex)
val sigHashAllAnyoneCanPayTx = sigHashAnyoneCanPay(sigHashAllTx,inputWithConnectedScript)
sigHashAllAnyoneCanPayTx.bytes ++ sigHashBytes
case _ : SIGHASH_NONE_ANYONECANPAY =>
case _: SIGHASH_NONE_ANYONECANPAY =>
val sigHashNoneTx = sigHashNone(txWithInputSigsRemoved,inputIndex)
val sigHashNoneAnyoneCanPay = sigHashAnyoneCanPay(sigHashNoneTx,inputWithConnectedScript)
sigHashNoneAnyoneCanPay.bytes ++ sigHashBytes
case _ : SIGHASH_SINGLE_ANYONECANPAY =>
case _: SIGHASH_SINGLE_ANYONECANPAY =>
val sigHashSingleTx = sigHashSingle(txWithInputSigsRemoved,inputIndex)
val sigHashSingleAnyoneCanPay = sigHashAnyoneCanPay(sigHashSingleTx,inputWithConnectedScript)
sigHashSingleAnyoneCanPay.bytes ++ sigHashBytes
@ -127,6 +127,8 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper with Bit
}
/**
* Serializes then hashes a transaction for signing
* this is an implementation of it's bitcoinj equivalent found here
@ -137,7 +139,8 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper with Bit
* @param hashType the hash type we are serializing this tx for
* @return
*/
def hashForSignature(spendingTransaction : Transaction, inputIndex : UInt32, script : Seq[ScriptToken], hashType : HashType) : DoubleSha256Digest = {
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)) {
@ -154,15 +157,79 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper with Bit
}
}
/** Implements the new serialization algorithm defined in BIP141
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki]]
* [[https://github.com/bitcoin/bitcoin/blob/f8528134fc188abc5c7175a19680206964a8fade/src/script/interpreter.cpp#L1113]]
*/
def serializeForSignature(spendingTx: WitnessTransaction, inputIndex: UInt32, script: Seq[ScriptToken], hashType: HashType,
amount: CurrencyUnit): Seq[Byte] = {
val isNotAnyoneCanPay = !HashType.isAnyoneCanPay(hashType)
val isNotSigHashSingle = !(hashType.isInstanceOf[SIGHASH_SINGLE])
val isNotSigHashNone = !(hashType.isInstanceOf[SIGHASH_NONE])
val inputIndexInt = inputIndex.toInt
val outPointHash: Option[Seq[Byte]] = if (isNotAnyoneCanPay) {
val bytes: Seq[Byte] = spendingTx.inputs.flatMap(_.previousOutput.bytes)
Some(CryptoUtil.doubleSHA256(bytes).bytes)
} else None
logger.debug("outPointHash: " + outPointHash.map(BitcoinSUtil.encodeHex(_)))
val sequenceHash: Option[Seq[Byte]] = if (isNotAnyoneCanPay && isNotSigHashNone && isNotSigHashSingle) {
val bytes = spendingTx.inputs.flatMap(_.sequence.bytes)
Some(CryptoUtil.doubleSHA256(bytes).bytes)
} else None
logger.debug("sequenceHash: " + sequenceHash.map(BitcoinSUtil.encodeHex(_)))
val outputHash: Option[Seq[Byte]] = if (isNotSigHashSingle && isNotSigHashNone) {
logger.debug("Not SIGHASH_SINGLE & Not SIGHASH_NONE")
//val bytes = spendingTx.outputs.flatMap(_.bytes)
val bytes = spendingTx.outputs.flatMap(o => BitcoinSUtil.decodeHex(RawTransactionOutputParser.write(o)))
Some(CryptoUtil.doubleSHA256(bytes).bytes)
} else if (hashType.isInstanceOf[SIGHASH_SINGLE] && inputIndex < UInt32(spendingTx.outputs.size)) {
logger.debug("SIGHASH_SINGLE and input index < outputs size")
val output = spendingTx.outputs(inputIndexInt)
val bytes = CryptoUtil.doubleSHA256(output.bytes).bytes
Some(bytes)
} else None
logger.debug("outputHash: " + outputHash.map(BitcoinSUtil.encodeHex(_)))
logger.debug("Script: " + script)
val scriptBytes = script.flatMap(_.bytes)
//helper function to flip endianness
val fe: Seq[Byte] => Seq[Byte] = {bytes: Seq[Byte] => BitcoinSUtil.decodeHex(BitcoinSUtil.flipEndianness(bytes)) }
val serializationForSig: Seq[Byte] = fe(spendingTx.version.bytes) ++ outPointHash.getOrElse(Nil) ++ sequenceHash.getOrElse(Nil) ++
spendingTx.inputs(inputIndexInt).previousOutput.bytes ++ CompactSizeUInt.calculateCompactSizeUInt(scriptBytes).bytes ++
scriptBytes ++ fe(amount.bytes) ++ fe(spendingTx.inputs(inputIndexInt).sequence.bytes) ++
outputHash.getOrElse(Nil) ++ fe(spendingTx.lockTime.bytes) ++ hashType.num.bytes.reverse
logger.info("Serialization for signature for WitnessV0Sig: " + BitcoinSUtil.encodeHex(serializationForSig))
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
* */
def hashForSignature(spendingTx: WitnessTransaction, inputIndex: UInt32, script: Seq[ScriptToken], hashType: HashType,
amount: CurrencyUnit): DoubleSha256Digest = {
val serialization = serializeForSignature(spendingTx,inputIndex,script,hashType,amount)
CryptoUtil.doubleSHA256(serialization)
}
/**
* Wrapper function for hashForSignature
* @param txSignatureComponent this contains the transaction and inputIndex for hashForSignature
* @param txSigComponent this contains the transaction and inputIndex for hashForSignature
* @param hashType
* @return
*/
def hashForSignature(txSignatureComponent: TransactionSignatureComponent, hashType: HashType): DoubleSha256Digest = {
hashForSignature(txSignatureComponent.transaction,txSignatureComponent.inputIndex,
txSignatureComponent.scriptPubKey.asm,hashType)
def hashForSignature(txSigComponent: TransactionSignatureComponent, hashType: HashType): DoubleSha256Digest = {
val script = BitcoinScriptUtil.calculateScriptForSigning(txSigComponent,txSigComponent.scriptPubKey.asm)
txSigComponent match {
case t : BaseTransactionSignatureComponent =>
hashForSignature(t.transaction,t.inputIndex,script,hashType)
case t : WitnessV0TransactionSignatureComponent =>
hashForSignature(t.transaction,t.inputIndex, script, hashType,t.amount)
}
}
@ -173,13 +240,11 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper with Bit
* @param inputIndex
* @return
*/
private def setSequenceNumbersZero(inputs : Seq[TransactionInput], inputIndex : UInt32) : Seq[TransactionInput] = {
for {
(input,index) <- inputs.zipWithIndex
} yield {
if (UInt32(index) == inputIndex) input
else TransactionInput(input,UInt32.zero)
}
private def setSequenceNumbersZero(inputs : Seq[TransactionInput], inputIndex : UInt32) : Seq[TransactionInput] = for {
(input,index) <- inputs.zipWithIndex
} yield {
if (UInt32(index) == inputIndex) input
else TransactionInput(input,UInt32.zero)
}
/**
@ -200,12 +265,7 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper with Bit
sigHashNoneTx
}
/**
* Executes the SIGHASH_SINGLE procedure on a spending transaction for the input specified by inputIndex
* @param spendingTransaction
* @param inputIndex
* @return
*/
/** Executes the SIGHASH_SINGLE procedure on a spending transaction for the input specified by inputIndex */
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

View file

@ -19,6 +19,7 @@ trait Policy {
* details.
*/
def mandatoryScriptVerifyFlags : Seq[ScriptFlag] = Seq(ScriptVerifyP2SH)
/**
* The default script verify flags used to validate the blockchain
* and bitcoin transactions
@ -27,7 +28,8 @@ trait Policy {
def standardScriptVerifyFlags : Seq[ScriptFlag] = mandatoryScriptVerifyFlags ++ Seq(ScriptVerifyDerSig, ScriptVerifyStrictEnc,
ScriptVerifyMinimalData, ScriptVerifyNullDummy, ScriptVerifyDiscourageUpgradableNOPs,
ScriptVerifyCleanStack, ScriptVerifyCheckLocktimeVerify, ScriptVerifyCheckSequenceVerify,
ScriptVerifyLowS)
ScriptVerifyLowS, ScriptVerifyWitness, ScriptVerifyMinimalIf, ScriptVerifyNullFail,
ScriptVerifyNullDummy, ScriptVerifyWitnessPubKeyType)
}

View file

@ -20,6 +20,7 @@ trait CompactSizeUInt {
* @return
*/
def num: UInt64
/**
* The length of the VarInt in bytes
* @return
@ -32,6 +33,8 @@ trait CompactSizeUInt {
case 5 => "fe" + BitcoinSUtil.flipEndianness(num.hex.slice(8,16))
case _ => "ff" + BitcoinSUtil.flipEndianness(num.hex)
}
def bytes: Seq[Byte] = BitcoinSUtil.decodeHex(hex)
}
object CompactSizeUInt extends Factory[CompactSizeUInt] {

View file

@ -1,6 +1,6 @@
package org.bitcoins.core.protocol
import org.bitcoins.core.util.BitcoinSUtil
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil}
/**
@ -8,23 +8,14 @@ import org.bitcoins.core.util.BitcoinSUtil
* This represents a element that can be serialized to
* be sent over the network
*/
trait NetworkElement {
trait NetworkElement extends BitcoinSLogger {
/**
* The size of the TransactionElement in bytes.
* @return
*/
/** The size of the NetworkElement in bytes. */
def size : Int = bytes.size
/**
* The hexadecimal representation of the transaction element
* @return
*/
/** The hexadecimal representation of the NetworkElement */
def hex : String
/**
* The byte representation of the transaction element
* @return
*/
/** The byte representation of the NetworkElement */
def bytes : Seq[Byte] = BitcoinSUtil.decodeHex(hex)
}

View file

@ -0,0 +1,24 @@
package org.bitcoins.core.protocol.script
import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.script.constant.ScriptToken
import org.bitcoins.core.util.Factory
/**
* 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 */
def buildScript(asm: Seq[ScriptToken], constructor: String => T, invariant: Seq[ScriptToken] => Boolean, errorMsg: String): T = {
if (invariant(asm)) {
val asmHex = asm.map(_.hex).mkString
val compactSizeUInt = CompactSizeUInt.calculateCompactSizeUInt(asmHex)
constructor(compactSizeUInt.hex + asmHex)
} else throw new IllegalArgumentException(errorMsg)
}
/** Creates a T from the given [[ScriptToken]]s */
def fromAsm(asm: Seq[ScriptToken]): T
}

View file

@ -1,7 +1,9 @@
package org.bitcoins.core.protocol.script
import org.bitcoins.core.crypto.{ECPublicKey, Sha256Hash160Digest}
import org.bitcoins.core.crypto.{ECPublicKey, HashDigest, Sha256Hash160Digest}
import org.bitcoins.core.number.Int64
import org.bitcoins.core.protocol._
import org.bitcoins.core.protocol.script.WitnessScriptPubKeyV0.WitnessScriptPubKeyV0Impl
import org.bitcoins.core.script.{ScriptOperation, ScriptSettings}
import org.bitcoins.core.script.bitwise.{OP_EQUAL, OP_EQUALVERIFY}
import org.bitcoins.core.script.constant._
@ -18,14 +20,22 @@ import scala.util.{Failure, Success, Try}
*/
sealed trait ScriptPubKey extends NetworkElement with BitcoinSLogger {
/** The size of the script, this is used for network serialization */
def compactSizeUInt : CompactSizeUInt = CompactSizeUInt.parseCompactSizeUInt(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
*
* @return
*/
lazy val asm : Seq[ScriptToken] = ScriptParser.fromBytes(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)
/** 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)
}
@ -39,12 +49,12 @@ sealed trait P2PKHScriptPubKey extends ScriptPubKey {
}
object P2PKHScriptPubKey extends Factory[P2PKHScriptPubKey] {
object P2PKHScriptPubKey extends ScriptFactory[P2PKHScriptPubKey] {
private case class P2PKHScriptPubKeyImpl(hex : String) extends P2PKHScriptPubKey
override def fromBytes(bytes : Seq[Byte]): P2PKHScriptPubKey = {
val asm = ScriptParser.fromBytes(bytes)
val asm = RawScriptPubKeyParser.read(bytes).asm
P2PKHScriptPubKey(asm)
}
@ -60,23 +70,18 @@ object P2PKHScriptPubKey extends Factory[P2PKHScriptPubKey] {
}
def fromAsm(asm: Seq[ScriptToken]): P2PKHScriptPubKey = {
require(isP2PKHScriptPubKey(asm), "Given asm was not a p2pkh scriptPubKey, got: " + asm)
val hex = asm.map(_.hex).mkString
P2PKHScriptPubKeyImpl(hex)
buildScript(asm, P2PKHScriptPubKeyImpl(_),isP2PKHScriptPubKey(_), "Given asm was not a p2pkh scriptPubKey, got: " + asm)
}
def apply(asm :Seq[ScriptToken]) : P2PKHScriptPubKey = fromAsm(asm)
/**
* Checks if the given asm matches the pattern for [[P2PKHScriptPubKey]]
*
* @param asm
* @return
*/
/** 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 _ => false
}
}
/**
* Represents a multisignature script public key
* https://bitcoin.org/en/developer-guide#multisig
@ -84,68 +89,56 @@ object P2PKHScriptPubKey extends Factory[P2PKHScriptPubKey] {
*/
sealed trait MultiSignatureScriptPubKey extends ScriptPubKey {
/**
* Returns the amount of required signatures for this multisignature script pubkey output
*
* @return
*/
def requiredSigs : Long = {
/** Returns the amount of required signatures for this multisignature script pubkey output */
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.underlying
case x : ScriptNumber => x.underlying.toInt
case c : ScriptConstant if ScriptNumber(c.hex).underlying <= ScriptSettings.maxPublicKeysPerMultiSig =>
ScriptNumber(c.hex).underlying.toInt
case _ => throw new RuntimeException("The first element of the multisignature pubkey must be a script number operation\n" +
"operation: " + numSigsRequired +
"\nscriptPubKey: " + this)
}
}
/**
* The maximum amount of signatures for this multisignature script pubkey output
*
* @return
*/
def maxSigs : Long = {
/** The maximum amount of signatures for this multisignature script pubkey output */
def maxSigs : Int = {
if (checkMultiSigIndex == -1 || checkMultiSigIndex == 0) {
//means that we do not have a max signature requirement
0.toLong
0
} else {
asm(checkMultiSigIndex - 1) match {
case x : ScriptNumber => x.underlying
case _ => throw new RuntimeException("The element preceding a OP_CHECKMULTISIG operation in a multisignature pubkey must be a script number operation, got: " + this)
case x : ScriptNumber => x.underlying.toInt
case c : ScriptConstant if ScriptNumber(c.hex).underlying <= ScriptSettings.maxPublicKeysPerMultiSig =>
ScriptNumber(c.hex).underlying.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
*
* @return the index of OP_CHECKMULTISIG or OP_CHECKMULTISIGVERIFY
*/
/** Gives the OP_CHECKMULTISIG or OP_CHECKMULTISIGVERIFY index inside of asm */
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
*
* @return
*/
/** Returns the public keys encoded into the scriptPubKey */
def publicKeys : Seq[ECPublicKey] = {
asm.filter(_.isInstanceOf[ScriptConstant]).slice(1, maxSigs.toInt + 1).map(key => ECPublicKey(key.hex))
asm.filter(_.isInstanceOf[ScriptConstant]).slice(1, maxSigs + 1).map(key => ECPublicKey(key.hex))
}
}
object MultiSignatureScriptPubKey extends Factory[MultiSignatureScriptPubKey] {
object MultiSignatureScriptPubKey extends ScriptFactory[MultiSignatureScriptPubKey] with BitcoinSLogger {
private case class MultiSignatureScriptPubKeyImpl(hex : String) extends MultiSignatureScriptPubKey
override def fromBytes(bytes : Seq[Byte]): MultiSignatureScriptPubKey = {
val asm = ScriptParser.fromBytes(bytes)
MultiSignatureScriptPubKey(asm)
val s = RawScriptPubKeyParser.read(bytes)
MultiSignatureScriptPubKey(s.asm)
}
def apply(requiredSigs : Int, pubKeys : Seq[ECPublicKey]): MultiSignatureScriptPubKey = {
@ -178,19 +171,12 @@ object MultiSignatureScriptPubKey extends Factory[MultiSignatureScriptPubKey] {
}
def fromAsm(asm: Seq[ScriptToken]): MultiSignatureScriptPubKey = {
require(isMultiSignatureScriptPubKey(asm), "Given asm was not a MultSignatureScriptPubKey, got: " + asm)
val hex = asm.map(_.hex).mkString
MultiSignatureScriptPubKeyImpl(hex)
buildScript(asm,MultiSignatureScriptPubKeyImpl(_),isMultiSignatureScriptPubKey(_), "Given asm was not a MultSignatureScriptPubKey, got: " + asm)
}
def apply(asm :Seq[ScriptToken]) : MultiSignatureScriptPubKey = fromAsm(asm)
/**
* Determines if the given script tokens are a multisignature scriptPubKey
*
* @param asm the tokens to check
* @return a boolean indicating if the given tokens are a multisignature scriptPubKey
*/
/** Determines if the given script tokens are a multisignature scriptPubKey */
def isMultiSignatureScriptPubKey(asm : Seq[ScriptToken]) : Boolean = {
val isNotEmpty = asm.size > 0
val containsMultiSigOp = asm.contains(OP_CHECKMULTISIG) || asm.contains(OP_CHECKMULTISIGVERIFY)
@ -227,11 +213,7 @@ object MultiSignatureScriptPubKey extends Factory[MultiSignatureScriptPubKey] {
/**
* Checks that the given script token is with the range of the maximum amount of
* public keys we can have in a [[MultiSignatureScriptPubKey]]
*
* @param token
* @return
*/
* public keys we can have in a [[MultiSignatureScriptPubKey]] */
private def isValidPubKeyNumber(token : ScriptToken): Boolean = token match {
case constant : ScriptConstant =>
constant.isInstanceOf[ScriptNumber] ||
@ -250,37 +232,34 @@ sealed trait P2SHScriptPubKey extends ScriptPubKey {
def scriptHash : Sha256Hash160Digest = Sha256Hash160Digest(asm(asm.length - 2).bytes)
}
object P2SHScriptPubKey extends Factory[P2SHScriptPubKey] with BitcoinSLogger {
object P2SHScriptPubKey extends ScriptFactory[P2SHScriptPubKey] with BitcoinSLogger {
private case class P2SHScriptPubKeyImpl(hex : String) extends P2SHScriptPubKey
override def fromBytes(bytes : Seq[Byte]): P2SHScriptPubKey = {
val asm = ScriptParser.fromBytes(bytes)
val asm = RawScriptPubKeyParser.read(bytes).asm
P2SHScriptPubKey(asm)
}
def apply(scriptPubKey: ScriptPubKey) : P2SHScriptPubKey = {
val hash = CryptoUtil.sha256Hash160(scriptPubKey.bytes)
val hash = CryptoUtil.sha256Hash160(scriptPubKey.asmBytes)
P2SHScriptPubKey(hash)
}
def apply(hash: Sha256Hash160Digest): P2SHScriptPubKey = {
val pushOps = BitcoinScriptUtil.calculatePushOp(hash.bytes)
val asm = Seq(OP_HASH160) ++ pushOps ++ Seq(ScriptConstant(hash.bytes), OP_EQUAL)
P2SHScriptPubKey(asm)
}
/**
* Checks if the given asm matches the pattern for [[P2SHScriptPubKey]]
*
* @param asm
* @return
*/
/** 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 _ => false
}
def fromAsm(asm: Seq[ScriptToken]): P2SHScriptPubKey = {
require(isP2SHScriptPubKey(asm), "Given asm was not a p2sh scriptPubkey, got: " + asm)
val hex = asm.map(_.hex).mkString
P2SHScriptPubKeyImpl(hex)
buildScript(asm,P2SHScriptPubKeyImpl(_),isP2SHScriptPubKey(_),"Given asm was not a p2sh scriptPubkey, got: " + asm)
}
def apply(asm :Seq[ScriptToken]) : P2SHScriptPubKey = fromAsm(asm)
@ -295,12 +274,12 @@ sealed trait P2PKScriptPubKey extends ScriptPubKey {
def publicKey : ECPublicKey = ECPublicKey(BitcoinScriptUtil.filterPushOps(asm).head.bytes)
}
object P2PKScriptPubKey extends Factory[P2PKScriptPubKey] {
object P2PKScriptPubKey extends ScriptFactory[P2PKScriptPubKey] {
private case class P2PKScriptPubKeyImpl(hex : String) extends P2PKScriptPubKey
override def fromBytes(bytes : Seq[Byte]) = {
val asm = ScriptParser.fromBytes(bytes)
val asm = RawScriptPubKeyParser.read(bytes).asm
P2PKScriptPubKey(asm)
}
@ -311,19 +290,12 @@ object P2PKScriptPubKey extends Factory[P2PKScriptPubKey] {
}
def fromAsm(asm: Seq[ScriptToken]): P2PKScriptPubKey = {
require(isP2PKScriptPubKey(asm), "Given asm was not a p2pk scriptPubKey, got: " + asm)
val hex = asm.map(_.hex).mkString
P2PKScriptPubKeyImpl(hex)
buildScript(asm,P2PKScriptPubKeyImpl(_), isP2PKScriptPubKey(_), "Given asm was not a p2pk scriptPubKey, got: " + asm)
}
def apply(asm :Seq[ScriptToken]) : P2PKScriptPubKey = fromAsm(asm)
/**
* Sees if the given asm matches the [[P2PKHScriptPubKey]] pattern
*
* @param asm
* @return
*/
/** 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 _ => false
@ -338,11 +310,8 @@ object P2PKScriptPubKey extends Factory[P2PKScriptPubKey] {
* Format: <locktime> OP_CLTV OP_DROP <scriptPubKey>
*/
sealed trait CLTVScriptPubKey extends ScriptPubKey {
/**
* Determines the nested ScriptPubKey inside the CLTVScriptPubKey
*
* @return
*/
/** Determines the nested ScriptPubKey inside the CLTVScriptPubKey */
def scriptPubKeyAfterCLTV : ScriptPubKey = {
val bool : Boolean = asm.head.isInstanceOf[ScriptNumberOperation]
bool match {
@ -351,11 +320,7 @@ sealed trait CLTVScriptPubKey extends ScriptPubKey {
}
}
/**
* The absolute CLTV-LockTime value (i.e. the output will remain unspendable until this timestamp or block height)
*
* @return
*/
/** The absolute CLTV-LockTime value (i.e. the output will remain unspendable until this timestamp or block height */
def locktime : ScriptNumber = {
asm.head match {
case scriptNumOp: ScriptNumberOperation => ScriptNumber(scriptNumOp.underlying)
@ -366,17 +331,15 @@ sealed trait CLTVScriptPubKey extends ScriptPubKey {
}
}
object CLTVScriptPubKey extends Factory[CLTVScriptPubKey] {
object CLTVScriptPubKey extends ScriptFactory[CLTVScriptPubKey] {
private case class CLTVScriptPubKeyImpl(hex : String) extends CLTVScriptPubKey
override def fromBytes (bytes : Seq[Byte]) : CLTVScriptPubKey = {
val asm = ScriptParser.fromBytes(bytes)
val asm = RawScriptPubKeyParser.read(bytes).asm
CLTVScriptPubKey(asm)
}
def fromAsm (asm : Seq[ScriptToken]) : CLTVScriptPubKey = {
require(isCLTVScriptPubKey(asm), "Given asm was not a CLTVScriptPubKey, got: " + asm)
val hex = asm.map(_.hex).mkString
CLTVScriptPubKeyImpl(hex)
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)
@ -423,11 +386,8 @@ object CLTVScriptPubKey extends Factory[CLTVScriptPubKey] {
* Format: <locktime> OP_CSV OP_DROP <scriptPubKey>
*/
sealed trait CSVScriptPubKey extends ScriptPubKey {
/**
* Determines the nested ScriptPubKey inside the CSVScriptPubKey
*
* @return
*/
/** Determines the nested ScriptPubKey inside the CSVScriptPubKey */
def scriptPubKeyAfterCSV : ScriptPubKey = {
val bool : Boolean = asm.head.isInstanceOf[ScriptNumberOperation]
bool match {
@ -436,11 +396,7 @@ sealed trait CSVScriptPubKey extends ScriptPubKey {
}
}
/**
* The relative CSV-LockTime value (i.e. the amount of time the output should remain unspendable)
*
* @return
*/
/** The relative CSV-LockTime value (i.e. the amount of time the output should remain unspendable) */
def locktime : ScriptNumber = {
asm.head match {
case scriptNumOp: ScriptNumberOperation => ScriptNumber(scriptNumOp.underlying)
@ -452,18 +408,16 @@ sealed trait CSVScriptPubKey extends ScriptPubKey {
}
object CSVScriptPubKey extends Factory[CSVScriptPubKey] {
object CSVScriptPubKey extends ScriptFactory[CSVScriptPubKey] {
private case class CSVScriptPubKeyImpl(hex : String) extends CSVScriptPubKey
override def fromBytes(bytes : Seq[Byte]) : CSVScriptPubKey = {
val asm = ScriptParser.fromBytes(bytes)
val asm = RawScriptPubKeyParser.read(bytes).asm
CSVScriptPubKey(asm)
}
def fromAsm (asm : Seq[ScriptToken]) : CSVScriptPubKey = {
require(isCSVScriptPubKey(asm), "Given asm was not a CSVScriptPubKey, got: " + asm)
val hex = asm.map(_.hex).mkString
CSVScriptPubKeyImpl(hex)
buildScript(asm, CSVScriptPubKeyImpl(_), isCSVScriptPubKey(_), "Given asm was not a CSVScriptPubKey, got: " + asm)
}
def apply(asm : Seq[ScriptToken]) : CSVScriptPubKey = fromAsm(asm)
@ -506,53 +460,139 @@ object CSVScriptPubKey extends Factory[CSVScriptPubKey] {
sealed trait NonStandardScriptPubKey extends ScriptPubKey
object NonStandardScriptPubKey extends Factory[NonStandardScriptPubKey] {
object NonStandardScriptPubKey extends ScriptFactory[NonStandardScriptPubKey] {
private case class NonStandardScriptPubKeyImpl(hex : String) extends NonStandardScriptPubKey
override def fromBytes(bytes: Seq[Byte]): NonStandardScriptPubKey = {
val asm = ScriptParser.fromBytes(bytes)
val asm = RawScriptPubKeyParser.read(bytes).asm
NonStandardScriptPubKey(asm)
}
def fromAsm(asm: Seq[ScriptToken]): NonStandardScriptPubKey = {
val hex = asm.map(_.hex).mkString
NonStandardScriptPubKeyImpl(hex)
//everything can be a NonStandardScriptPubkey, thus the trivially true function
buildScript(asm,NonStandardScriptPubKeyImpl(_), {_ => true }, "")
}
def apply(asm : Seq[ScriptToken]) : NonStandardScriptPubKey = fromAsm(asm)
}
/**
* Represents the empty ScriptPubKey
*/
/** Represents the empty ScriptPubKey */
case object EmptyScriptPubKey extends ScriptPubKey {
def hex = ""
def hex = "00"
}
/**
* Factory companion object used to create ScriptPubKey objects
*/
/** Factory companion object used to create ScriptPubKey objects */
object ScriptPubKey extends Factory[ScriptPubKey] with BitcoinSLogger {
def empty : ScriptPubKey = fromAsm(List())
def empty : ScriptPubKey = fromAsm(Nil)
/**
* Creates a scriptPubKey from its asm representation
*
* @param asm
* @return
*/
/** Creates a scriptPubKey from its asm representation */
def fromAsm(asm : Seq[ScriptToken]) : ScriptPubKey = asm match {
case Seq() => EmptyScriptPubKey
case Nil => EmptyScriptPubKey
case _ if P2PKHScriptPubKey.isP2PKHScriptPubKey(asm) => P2PKHScriptPubKey(asm)
case _ if P2SHScriptPubKey.isP2SHScriptPubKey(asm) => P2SHScriptPubKey(asm)
case _ if P2PKScriptPubKey.isP2PKScriptPubKey(asm) => P2PKScriptPubKey(asm)
case _ if MultiSignatureScriptPubKey.isMultiSignatureScriptPubKey(asm) => MultiSignatureScriptPubKey(asm)
case _ if CLTVScriptPubKey.isCLTVScriptPubKey(asm) => CLTVScriptPubKey(asm)
case _ if CSVScriptPubKey.isCSVScriptPubKey(asm) => CSVScriptPubKey(asm)
case _ if WitnessScriptPubKey.isWitnessScriptPubKey(asm) => WitnessScriptPubKey(asm).get
case _ => NonStandardScriptPubKey(asm)
}
def fromBytes(bytes : Seq[Byte]) : ScriptPubKey = RawScriptPubKeyParser.read(bytes)
def apply(asm : Seq[ScriptToken]) : ScriptPubKey = fromAsm(asm)
}
}
/** This type represents a [[ScriptPubKey]] to evaluate a [[ScriptWitness]] */
sealed trait WitnessScriptPubKey extends ScriptPubKey {
def witnessProgram: Seq[ScriptToken]
def witnessVersion = WitnessVersion(asm.head.toLong)
}
object WitnessScriptPubKey {
/** Witness scripts must begin with one of these operations, see BIP141 */
val validFirstOps = 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)
def apply(asm: Seq[ScriptToken]): Option[WitnessScriptPubKey] = fromAsm(asm)
def fromAsm(asm: Seq[ScriptToken]): Option[WitnessScriptPubKey] = asm match {
case _ if WitnessScriptPubKeyV0.isWitnessScriptPubKeyV0(asm) => Some(WitnessScriptPubKeyV0(asm))
case _ if WitnessScriptPubKey.isWitnessScriptPubKey(asm) => Some(UnassignedWitnessScriptPubKey(asm))
case _ => None
}
def isWitnessScriptPubKey(asm: Seq[ScriptToken]): Boolean = {
val bytes = asm.flatMap(_.bytes)
val firstOp = asm.headOption
if (bytes.size < 4 || bytes.size > 42) false
else if (!validFirstOps.contains(firstOp.getOrElse(OP_1NEGATE))) false
else if (asm(1).toLong + 2 == bytes.size) true
else false
}
}
/** Represents a [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#witness-program]] */
sealed trait WitnessScriptPubKeyV0 extends WitnessScriptPubKey {
override def witnessProgram: Seq[ScriptToken] = asm.tail.tail
}
object WitnessScriptPubKeyV0 extends ScriptFactory[WitnessScriptPubKeyV0] {
private case class WitnessScriptPubKeyV0Impl(hex: String) extends WitnessScriptPubKeyV0
override def fromBytes(bytes: Seq[Byte]): WitnessScriptPubKeyV0 = {
val asm = RawScriptPubKeyParser.read(bytes).asm
WitnessScriptPubKeyV0(asm)
}
def fromAsm(asm: Seq[ScriptToken]): WitnessScriptPubKeyV0 = {
buildScript(asm,WitnessScriptPubKeyV0Impl(_),isWitnessScriptPubKeyV0(_), "Given asm was not a WitnessScriptPubKeyV0, got: " + asm )
}
def apply(asm: Seq[ScriptToken]): WitnessScriptPubKeyV0 = fromAsm(asm)
/** Creates a P2WPKH witness script pubkey */
def apply(pubKey: ECPublicKey): WitnessScriptPubKeyV0 = build(pubKey.bytes, CryptoUtil.sha256Hash160 _)
/** Creates a raw P2WSH script pubkey from the given [[ScriptPubKey]] */
def apply(scriptPubKey: ScriptPubKey): WitnessScriptPubKey = build(scriptPubKey.asmBytes, CryptoUtil.sha256 _)
/** Helper function to build [[WitnessScriptPubKey]], applies given hash function to the bytes,
* then places it inside of [[WitnessScriptPubKey]] */
private def build(bytes: Seq[Byte], hashFunc: Seq[Byte] => HashDigest): WitnessScriptPubKeyV0 = {
val hash = hashFunc(bytes)
val pushOp = BitcoinScriptUtil.calculatePushOp(hash.bytes)
WitnessScriptPubKeyV0(Seq(OP_0) ++ pushOp ++ Seq(ScriptConstant(hash.bytes)))
}
/** 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
* [[https://github.com/bitcoin/bitcoin/blob/449f9b8debcceb61a92043bc7031528a53627c47/src/script/script.cpL215-L229]]
* Returns None if it is not a witness program, else returns the script and script version
* */
def isWitnessScriptPubKeyV0(asm: Seq[ScriptToken]): Boolean = {
WitnessScriptPubKey.isWitnessScriptPubKey(asm) && asm.headOption == Some(OP_0)
}
}
/** Type to represent all [[org.bitcoins.core.protocol.script.WitnessScriptPubKey]]s we have not used yet in the bitcoin protocol */
sealed trait UnassignedWitnessScriptPubKey extends WitnessScriptPubKey {
override def witnessProgram: Seq[ScriptToken] = asm.tail.tail
}
object UnassignedWitnessScriptPubKey extends ScriptFactory[UnassignedWitnessScriptPubKey] {
private case class UnassignedWitnessScriptPubKeyImpl(hex: String) extends UnassignedWitnessScriptPubKey
override def fromBytes(bytes: Seq[Byte]): UnassignedWitnessScriptPubKey = {
val asm = RawScriptPubKeyParser.read(bytes).asm
UnassignedWitnessScriptPubKey(asm)
}
override def fromAsm(asm: Seq[ScriptToken]): UnassignedWitnessScriptPubKey = {
buildScript(asm, UnassignedWitnessScriptPubKeyImpl(_), WitnessScriptPubKey.isWitnessScriptPubKey(_),
"Given asm was not a valid witness script pubkey: " + asm)
}
def apply(asm: Seq[ScriptToken]): UnassignedWitnessScriptPubKey = fromAsm(asm)
}

View file

@ -1,10 +1,14 @@
package org.bitcoins.core.protocol.script
import org.bitcoins.core.crypto.{ECDigitalSignature, ECPublicKey}
import org.bitcoins.core.protocol.NetworkElement
import org.bitcoins.core.protocol.script.MultiSignatureScriptSignature.MultiSignatureScriptSignatureImpl
import org.bitcoins.core.protocol.script.NonStandardScriptSignature.NonStandardScriptSignatureImpl
import org.bitcoins.core.protocol.script.P2PKHScriptSignature.P2PKHScriptSignatureImpl
import org.bitcoins.core.protocol.script.P2SHScriptSignature.P2SHScriptSignatureImpl
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.util.{BitcoinSLogger, BitcoinSUtil, BitcoinScriptUtil, Factory}
import org.bitcoins.core.util._
import scala.util.{Failure, Success, Try}
@ -13,15 +17,22 @@ import scala.util.{Failure, Success, Try}
*
*/
sealed trait ScriptSignature extends NetworkElement with BitcoinSLogger {
def compactSizeUInt = CompactSizeUInt.parseCompactSizeUInt(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
*
* @return
* 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.fromHex(hex)
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)
/**
@ -30,29 +41,26 @@ sealed trait ScriptSignature extends NetworkElement with BitcoinSLogger {
* p2pk script signatures only have one sigs
* p2sh script signatures can have m sigs
* multisignature scripts can have m sigs
*
* @return
*/
def signatures : Seq[ECDigitalSignature]
}
sealed trait NonStandardScriptSignature extends ScriptSignature {
def signatures : Seq[ECDigitalSignature] = Seq()
def signatures : Seq[ECDigitalSignature] = Nil
}
object NonStandardScriptSignature extends Factory[NonStandardScriptSignature] {
object NonStandardScriptSignature extends ScriptFactory[NonStandardScriptSignature] {
private case class NonStandardScriptSignatureImpl(hex : String) extends NonStandardScriptSignature
override def fromBytes(bytes: Seq[Byte]): NonStandardScriptSignature = {
//make sure we can parse the bytes
val asm = ScriptParser.fromBytes(bytes)
val asm = RawScriptSignatureParser.read(bytes).asm
NonStandardScriptSignature.fromAsm(asm)
}
def fromAsm(asm : Seq[ScriptToken]): NonStandardScriptSignature = {
val hex = asm.map(_.hex).mkString
NonStandardScriptSignatureImpl(hex)
buildScript(asm, NonStandardScriptSignatureImpl(_),{ _ => true}, "")
}
}
@ -64,18 +72,10 @@ object NonStandardScriptSignature extends Factory[NonStandardScriptSignature] {
*/
sealed trait P2PKHScriptSignature extends ScriptSignature {
/**
* P2PKH scriptSigs only have one signature
*
* @return
*/
/** P2PKH scriptSigs only have one signature */
def signature : ECDigitalSignature = signatures.head
/**
* Gives us the public key inside of a p2pkh script signature
*
* @return
*/
/** Gives us the public key inside of a p2pkh script signature */
def publicKey : ECPublicKey = ECPublicKey(asm.last.bytes)
override def signatures : Seq[ECDigitalSignature] = {
@ -84,28 +84,21 @@ sealed trait P2PKHScriptSignature extends ScriptSignature {
}
object P2PKHScriptSignature extends Factory[P2PKHScriptSignature] {
object P2PKHScriptSignature extends ScriptFactory[P2PKHScriptSignature] {
private case class P2PKHScriptSignatureImpl(hex : String) extends P2PKHScriptSignature
override def fromBytes(bytes : Seq[Byte]): P2PKHScriptSignature = {
val asm = ScriptParser.fromBytes(bytes)
val asm = RawScriptSignatureParser.read(bytes).asm
P2PKHScriptSignature.fromAsm(asm)
}
def fromAsm(asm: Seq[ScriptToken]): P2PKHScriptSignature = {
require(isP2PKHScriptSig(asm), "Given asm was not a P2PKHScriptSignature, got: " + asm)
val hex = asm.map(_.hex).mkString
P2PKHScriptSignatureImpl(hex)
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
*
* @param signature
* @param pubKey
* @return
*/
* 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)
@ -114,12 +107,7 @@ object P2PKHScriptSignature extends Factory[P2PKHScriptSignature] {
fromAsm(asm)
}
/**
* Determines if the given asm matches a [[P2PKHScriptSignature]]
*
* @param asm
* @return
*/
/** 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) => true
@ -135,27 +123,27 @@ object P2PKHScriptSignature extends Factory[P2PKHScriptSignature] {
*/
sealed trait P2SHScriptSignature extends ScriptSignature {
/**
* The redeemScript represents the conditions that must be satisfied to spend the output
*
* @return
*/
def redeemScript : ScriptPubKey = ScriptPubKey(asm.last.bytes)
/** The redeemScript represents the conditions that must be satisfied to spend the output */
def redeemScript : ScriptPubKey = {
if (WitnessScriptPubKey.isWitnessScriptPubKey(asm)) WitnessScriptPubKey(asm).get
else ScriptPubKey(ScriptParser.fromBytes(asm.last.bytes))
}
/**
* Returns the script signature of this p2shScriptSig with no serialized redeemScript
*
* @return
*/
def scriptSignatureNoRedeemScript = ScriptSignature.fromAsm(splitAtRedeemScript(asm)._1)
/** Returns the script signature of this p2shScriptSig with no serialized redeemScript */
def scriptSignatureNoRedeemScript: ScriptSignature = {
if (WitnessScriptPubKey.isWitnessScriptPubKey(asm)) EmptyScriptSignature
else {
val asmWithoutRedeemScriptAndPushOp = asm(asm.size - 2) match {
case b : BytesToPushOntoStack => asm.dropRight(2)
case _ => asm.dropRight(3)
}
ScriptSignature.fromAsm(asmWithoutRedeemScriptAndPushOp)
}
}
/**
* Returns the public keys for the p2sh scriptSignature
*
* @return
*/
/** Returns the public keys for the p2sh scriptSignature */
def publicKeys : Seq[ECPublicKey] = {
val pubKeys : Seq[ScriptToken] = redeemScript.asm.filter(_.isInstanceOf[ScriptConstant])
.filterNot(_.isInstanceOf[ScriptNumberOperation])
@ -163,11 +151,7 @@ sealed trait P2SHScriptSignature extends ScriptSignature {
}
/**
* The digital signatures inside of the scriptSig
*
* @return
*/
/** The digital signatures inside of the scriptSig */
def signatures : Seq[ECDigitalSignature] = {
val nonRedeemScript = splitAtRedeemScript(asm)._1
val sigs = nonRedeemScript.filter(_.isInstanceOf[ScriptConstant]).filterNot(_.isInstanceOf[ScriptNumberOperation]).filterNot(_.hex.length < 100)
@ -178,61 +162,51 @@ sealed trait P2SHScriptSignature extends ScriptSignature {
/**
* Splits the given asm into two parts
* the first part is the digital signatures
* the second part is the redeem script
*
* @param asm
* @return
*/
* the second part is the redeem script */
def splitAtRedeemScript(asm : Seq[ScriptToken]) : (Seq[ScriptToken],Seq[ScriptToken]) = {
//call .tail twice to remove the serialized redeemScript & it's bytesToPushOntoStack constant
(asm.reverse.tail.tail.reverse, Seq(asm.last))
(scriptSignatureNoRedeemScript.asm, redeemScript.asm)
}
}
object P2SHScriptSignature extends Factory[P2SHScriptSignature] with BitcoinSLogger {
object P2SHScriptSignature extends ScriptFactory[P2SHScriptSignature] {
private case class P2SHScriptSignatureImpl(hex : String) extends P2SHScriptSignature
override def fromBytes(bytes : Seq[Byte]): P2SHScriptSignature = {
val asm = ScriptParser.fromBytes(bytes)
val asm = RawScriptSignatureParser.read(bytes).asm
P2SHScriptSignature.fromAsm(asm)
}
def apply(scriptSig : ScriptSignature, redeemScript : ScriptPubKey): P2SHScriptSignature = {
//we need to calculate the size of the redeemScript and add the corresponding push op
val pushOps = BitcoinScriptUtil.calculatePushOp(ScriptConstant(redeemScript.bytes))
val bytes = scriptSig.bytes ++ pushOps.flatMap(_.bytes) ++ redeemScript.bytes
fromBytes(bytes)
val serializedRedeemScript = ScriptConstant(redeemScript.asmBytes)
val pushOps = BitcoinScriptUtil.calculatePushOp(serializedRedeemScript)
val asm: Seq[ScriptToken] = scriptSig.asm ++ pushOps ++ Seq(serializedRedeemScript)
fromAsm(asm)
}
def apply(witnessScriptPubKey: WitnessScriptPubKey): P2SHScriptSignature = {
P2SHScriptSignature(EmptyScriptSignature,witnessScriptPubKey)
}
def fromAsm(asm: Seq[ScriptToken]): P2SHScriptSignature = {
require(isP2SHScriptSig(asm), "Given asm tokens are not a p2sh scriptSig, got: " + asm)
val hex = asm.map(_.hex).mkString
P2SHScriptSignatureImpl(hex)
buildScript(asm, P2SHScriptSignatureImpl(_),isP2SHScriptSig(_), "Given asm tokens are not a p2sh scriptSig, got: " + asm)
}
/**
* Tests if the given asm tokens are a [[P2SHScriptSignature]]
*
* @param asm
* @return
*/
/** Tests if the given asm tokens are a [[P2SHScriptSignature]] */
def isP2SHScriptSig(asm: Seq[ScriptToken]): Boolean = asm match {
case _ if asm.size > 1 && isRedeemScript(asm.last) => true
case _ if WitnessScriptPubKey.isWitnessScriptPubKey(asm) => true
case _ => false
}
/**
* Detects if the given script token is a redeem script
*
* @param token
* @return
*/
/** Detects if the given script token is a redeem script */
def isRedeemScript(token : ScriptToken) : Boolean = {
logger.debug("Checking if last token is redeem script")
val redeemScriptTry : Try[ScriptPubKey] = parseRedeemScript(token)
redeemScriptTry match {
case Success(redeemScript) =>
logger.debug("Possible redeemScript: " + redeemScript)
logger.debug("Possible redeemScript: " + redeemScript.asm)
redeemScript match {
case x : P2PKHScriptPubKey => true
case x : MultiSignatureScriptPubKey => true
@ -240,6 +214,8 @@ object P2SHScriptSignature extends Factory[P2SHScriptSignature] with BitcoinSLog
case x : P2PKScriptPubKey => true
case x : CLTVScriptPubKey => true
case x : CSVScriptPubKey => true
case x : WitnessScriptPubKeyV0 => true
case x : UnassignedWitnessScriptPubKey => false
case x : NonStandardScriptPubKey => false
case EmptyScriptPubKey => false
}
@ -248,14 +224,11 @@ object P2SHScriptSignature extends Factory[P2SHScriptSignature] with BitcoinSLog
}
/**
* Parses a redeem script from the given script token
*
* @param scriptToken
* @return
*/
/** Parses a redeem script from the given script token */
def parseRedeemScript(scriptToken : ScriptToken) : Try[ScriptPubKey] = {
val redeemScript : Try[ScriptPubKey] = Try(ScriptPubKey(scriptToken.bytes))
val asm = ScriptParser.fromBytes(scriptToken.bytes)
logger.debug("Asm for redeem script: " + asm)
val redeemScript : Try[ScriptPubKey] = Try(ScriptPubKey(asm))
redeemScript
}
}
@ -268,23 +241,19 @@ object P2SHScriptSignature extends Factory[P2SHScriptSignature] with BitcoinSLog
*/
sealed trait MultiSignatureScriptSignature extends ScriptSignature {
/**
* The digital signatures inside of the scriptSig
*
* @return
*/
/** The digital signatures inside of the scriptSig */
def signatures : Seq[ECDigitalSignature] = {
asm.tail.filter(_.isInstanceOf[ScriptConstant])
.map(sig => ECDigitalSignature(sig.hex))
}
}
object MultiSignatureScriptSignature extends Factory[MultiSignatureScriptSignature] {
object MultiSignatureScriptSignature extends ScriptFactory[MultiSignatureScriptSignature] {
private case class MultiSignatureScriptSignatureImpl(hex : String) extends MultiSignatureScriptSignature
override def fromBytes(bytes : Seq[Byte]): MultiSignatureScriptSignature = {
val asm = ScriptParser.fromBytes(bytes)
val asm = RawScriptSignatureParser.read(bytes).asm
MultiSignatureScriptSignature.fromAsm(asm)
}
@ -301,9 +270,8 @@ object MultiSignatureScriptSignature extends Factory[MultiSignatureScriptSignatu
}
def fromAsm(asm: Seq[ScriptToken]): MultiSignatureScriptSignature = {
require(isMultiSignatureScriptSignature(asm), "The given asm tokens were not a multisignature script sig: " + asm)
val hex = asm.map(_.hex).mkString
MultiSignatureScriptSignatureImpl(hex)
buildScript(asm, MultiSignatureScriptSignatureImpl(_),isMultiSignatureScriptSignature(_),
"The given asm tokens were not a multisignature script sig: " + asm)
}
/**
@ -334,24 +302,16 @@ object MultiSignatureScriptSignature extends Factory[MultiSignatureScriptSignatu
*/
sealed trait P2PKScriptSignature extends ScriptSignature {
/**
* PubKey scriptSignatures only have one signature
*
* @return
*/
/** PubKey scriptSignatures only have one signature */
def signature : ECDigitalSignature = signatures.head
/**
* The digital signatures inside of the scriptSig
*
* @return
*/
/** The digital signatures inside of the scriptSig */
def signatures : Seq[ECDigitalSignature] = {
Seq(ECDigitalSignature(BitcoinScriptUtil.filterPushOps(asm).head.hex))
}
}
object P2PKScriptSignature extends Factory[P2PKScriptSignature] {
object P2PKScriptSignature extends ScriptFactory[P2PKScriptSignature] {
private case class P2PKScriptSignatureImpl(hex : String) extends P2PKScriptSignature
def apply(signature: ECDigitalSignature): P2PKScriptSignature = {
@ -362,22 +322,16 @@ object P2PKScriptSignature extends Factory[P2PKScriptSignature] {
}
def fromAsm(asm: Seq[ScriptToken]): P2PKScriptSignature = {
require(isP2PKScriptSignature(asm), "The given asm tokens were not a p2pk scriptSig, got: " + asm)
val hex = asm.map(_.hex).mkString
P2PKScriptSignatureImpl(hex)
buildScript(asm, P2PKScriptSignatureImpl(_),isP2PKScriptSignature(_),
"The given asm tokens were not a p2pk script sig: " + asm)
}
override def fromBytes(bytes: Seq[Byte]): P2PKScriptSignature = {
val asm = ScriptParser.fromBytes(bytes)
val asm = RawScriptSignatureParser.read(bytes).asm
P2PKScriptSignature.fromAsm(asm)
}
/**
* P2PK scriptSigs always have the pattern [pushop, digitalSignature]
*
* @param asm
* @return
*/
/** P2PK scriptSigs always have the pattern [pushop, digitalSignature] */
def isP2PKScriptSignature(asm: Seq[ScriptToken]): Boolean = asm match {
case List(w : BytesToPushOntoStack, x : ScriptConstant) => true
case _ => false
@ -420,6 +374,9 @@ object CLTVScriptSignature extends Factory[CLTVScriptSignature] {
case cltvScriptPubKey : CLTVScriptPubKey => apply(cltvScriptPubKey.scriptPubKeyAfterCLTV, sigs, pubKeys)
case csvScriptPubKey : CSVScriptPubKey => apply(csvScriptPubKey.scriptPubKeyAfterCSV, sigs, pubKeys)
case EmptyScriptPubKey => CLTVScriptSignature(EmptyScriptSignature)
case _: WitnessScriptPubKeyV0 | _ : UnassignedWitnessScriptPubKey =>
//bare segwit always has an empty script sig, see BIP141
CLTVScriptSignature(EmptyScriptSignature)
case x @ (_ : NonStandardScriptPubKey | _ : P2SHScriptPubKey) => throw new IllegalArgumentException("A NonStandardScriptSignature or P2SHScriptSignature cannot be" +
"the underlying scriptSig in a CLTVScriptSignature. Got: " + x)
}
@ -458,41 +415,34 @@ object CSVScriptSignature extends Factory[CSVScriptSignature] {
case p2pkScriptPubKey : P2PKScriptPubKey => CSVScriptSignature(P2PKScriptSignature(sigs.head))
case p2pkhScriptPubKey : P2PKHScriptPubKey => CSVScriptSignature(P2PKHScriptSignature(sigs.head, pubKeys.head))
case multiSigScriptPubKey : MultiSignatureScriptPubKey => CSVScriptSignature(MultiSignatureScriptSignature(sigs))
case cltvScriptPubKey : CLTVScriptPubKey => apply(cltvScriptPubKey.scriptPubKeyAfterCLTV, sigs, pubKeys)
case csvScriptPubKey : CSVScriptPubKey => apply(csvScriptPubKey.scriptPubKeyAfterCSV, sigs, pubKeys)
case cltvScriptPubKey : CLTVScriptPubKey => CSVScriptSignature(cltvScriptPubKey.scriptPubKeyAfterCLTV, sigs, pubKeys)
case csvScriptPubKey : CSVScriptPubKey => CSVScriptSignature(csvScriptPubKey.scriptPubKeyAfterCSV, sigs, pubKeys)
case EmptyScriptPubKey => CSVScriptSignature(EmptyScriptSignature)
case x @ (_ : NonStandardScriptPubKey | _ : P2SHScriptPubKey) => throw new IllegalArgumentException("A NonStandardScriptSignature or P2SHScriptSignature cannot be" +
case _ @ (_: WitnessScriptPubKeyV0 | _ : UnassignedWitnessScriptPubKey) =>
//bare segwit always has an empty script sig, see BIP141
CSVScriptSignature(EmptyScriptSignature)
case x @ (_ : NonStandardScriptPubKey | _ : P2SHScriptPubKey) =>
throw new IllegalArgumentException("A NonStandardScriptPubKey/P2SHScriptPubKey cannot be" +
"the underlying scriptSig in a CSVScriptSignature. Got: " + x)
}
}
/**
* Represents the empty script signature
*/
/** Represents the empty script signature */
case object EmptyScriptSignature extends ScriptSignature {
def signatures = List()
def hex = ""
def signatures = Nil
def hex = "00"
}
object ScriptSignature extends Factory[ScriptSignature] with BitcoinSLogger {
/**
* Returns an empty script signature
*
* @return
*/
/** Returns an empty script signature */
def empty : ScriptSignature = EmptyScriptSignature
def fromBytes(bytes : Seq[Byte]) : ScriptSignature = RawScriptSignatureParser.read(bytes)
/**
* Creates a scriptSignature from the list of script tokens
*
* @param tokens
* @return
*/
/** Creates a scriptSignature from the list of script tokens */
def fromAsm(tokens : Seq[ScriptToken]) : ScriptSignature = tokens match {
case Nil => EmptyScriptSignature
case _ if (tokens.size > 1 && P2SHScriptSignature.isRedeemScript(tokens.last)) =>
@ -508,7 +458,6 @@ object ScriptSignature extends Factory[ScriptSignature] with BitcoinSLogger {
/**
* 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
@ -521,6 +470,7 @@ object ScriptSignature extends Factory[ScriptSignature] with BitcoinSLogger {
case s : NonStandardScriptPubKey => NonStandardScriptSignature.fromAsm(tokens)
case s : CLTVScriptPubKey => fromScriptPubKey(tokens, s.scriptPubKeyAfterCLTV)
case s : CSVScriptPubKey => fromScriptPubKey(tokens, s.scriptPubKeyAfterCSV)
case _ @ (_: WitnessScriptPubKeyV0 | _: UnassignedWitnessScriptPubKey) => EmptyScriptSignature
case EmptyScriptPubKey => if (tokens.isEmpty) EmptyScriptSignature else NonStandardScriptSignature.fromAsm(tokens)
}

View file

@ -0,0 +1,40 @@
package org.bitcoins.core.protocol.script
import org.bitcoins.core.crypto.{ECDigitalSignature, ECPublicKey}
import org.bitcoins.core.protocol.NetworkElement
import org.bitcoins.core.script.constant.ScriptToken
import org.bitcoins.core.serializers.script.RawScriptWitnessParser
import org.bitcoins.core.util.{BitcoinSUtil, Factory}
/**
* 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 trait ScriptWitness extends NetworkElement {
/** The byte vectors that are placed on to the stack when evaluating a witness program */
def stack : Seq[Seq[Byte]]
override def toString = stack.map(BitcoinSUtil.encodeHex(_)).toString
override def hex = RawScriptWitnessParser.write(this)
}
object ScriptWitness extends Factory[ScriptWitness] {
private case class ScriptWitnessImpl(stack: Seq[Seq[Byte]]) extends ScriptWitness
def apply(stack: Seq[Seq[Byte]]): ScriptWitness = ScriptWitnessImpl(stack)
override def fromBytes(bytes: Seq[Byte]): ScriptWitness = RawScriptWitnessParser.read(bytes)
/*
def fromHex(stack: Seq[Seq[String]]): ScriptWitness = ScriptWitness(stack.flatMap(_.map(BitcoinSUtil.decodeHex(_))))
*/
def apply(signature: ECDigitalSignature, publicKey: ECPublicKey): ScriptWitness = {
val sigConstant = signature.bytes
val pubKeyConstant = publicKey.bytes
ScriptWitness(Seq(sigConstant, pubKeyConstant))
}
}

View file

@ -0,0 +1,18 @@
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]]
* */
sealed trait SignatureVersion
/** The original digest algorithm created by Satoshi */
case object SigVersionBase extends SignatureVersion
/** The digest algorithm implemented by BIP143 [[https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki]] */
case object SigVersionWitnessV0 extends SignatureVersion

View file

@ -0,0 +1,75 @@
package org.bitcoins.core.protocol.script
import org.bitcoins.core.crypto.{ECPublicKey, Sha256Digest, Sha256Hash160Digest}
import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.script.constant.{ScriptConstant, ScriptToken}
import org.bitcoins.core.script.result._
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil, CryptoUtil}
/**
* 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]
}
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] = {
val programBytes = witnessProgram.flatMap(_.bytes)
programBytes.size match {
case 20 =>
//p2wpkh
if (scriptWitness.stack.size != 2) Right(ScriptErrorWitnessProgramMisMatch)
else {
val hash = Sha256Hash160Digest(programBytes)
Left((scriptWitness.stack.map(ScriptConstant(_)), P2PKHScriptPubKey(hash)))
}
case 32 =>
logger.info("Script witness stack: " + scriptWitness)
//p2wsh
if (scriptWitness.stack.isEmpty) Right(ScriptErrorWitnessProgramWitnessEmpty)
else {
//need to check if the hashes match
val stackTop = scriptWitness.stack.head
val stackHash = CryptoUtil.sha256(stackTop)
logger.debug("Stack top: " + BitcoinSUtil.encodeHex(stackTop))
logger.debug("Stack hash: " + stackHash)
logger.debug("Witness program: " + witnessProgram)
if (stackHash != Sha256Digest(witnessProgram.head.bytes)) Right(ScriptErrorWitnessProgramMisMatch)
else {
val compactSizeUInt = CompactSizeUInt.calculateCompactSizeUInt(stackTop)
val scriptPubKey = ScriptPubKey(compactSizeUInt.bytes ++ stackTop)
logger.debug("Script pub key for p2wsh: " + scriptPubKey.asm)
val stack = scriptWitness.stack.tail.map(ScriptConstant(_))
Left(stack, scriptPubKey)
}
}
case _ =>
logger.error("Invalid witness program length for witness version 0, got: " + programBytes.size)
logger.error("Witness: " + scriptWitness)
logger.error("Witness program: " + witnessProgram)
//witness version 0 programs need to be 20 bytes or 32 bytes in size
Right(ScriptErrorWitnessProgramWrongLength)
}
}
}
/** The witness version that represents all witnesses that have not been allocated yet */
case object UnassignedWitness extends WitnessVersion {
override def rebuild(scriptWitness: ScriptWitness, witnessProgram: Seq[ScriptToken]): Either[(Seq[ScriptToken], ScriptPubKey),ScriptError] =
Right(ScriptErrorDiscourageUpgradeableWitnessProgram)
}
object WitnessVersion {
def apply(num: Long): WitnessVersion = num match {
case 0 => WitnessVersion0
case _ => UnassignedWitness
}
}

View file

@ -2,10 +2,12 @@ package org.bitcoins.core.protocol.transaction
import org.bitcoins.core.crypto.DoubleSha256Digest
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.{CompactSizeUInt, NetworkElement}
import org.bitcoins.core.serializers.transaction.RawTransactionParser
import org.bitcoins.core.protocol.NetworkElement
import org.bitcoins.core.serializers.transaction.{RawBaseTransactionParser, RawWitnessTransactionParser}
import org.bitcoins.core.util.{BitcoinSUtil, CryptoUtil, Factory}
import scala.util.{Failure, Success, Try}
/**
* Created by chris on 7/14/15.
*/
@ -13,40 +15,24 @@ sealed trait 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
*
* @return
*/
def txId : DoubleSha256Digest = CryptoUtil.doubleSHA256(bytes)
/**
* The version number for this transaction
* @return
*/
/** The version number for this transaction */
def version : UInt32
/**
* The inputs for this transaction
* @return
*/
/** The inputs for this transaction */
def inputs : Seq[TransactionInput]
/**
* The outputs for this transaction
* @return
*/
/** The outputs for this transaction */
def outputs : Seq[TransactionOutput]
/**
* The locktime for this transaction
* @return
*/
/** The locktime for this transaction */
def lockTime : UInt32
override def hex = RawTransactionParser.write(this)
/**
* Determines if this transaction is a coinbase transaction
*
* @return
*/
def isCoinbase : Boolean = inputs.size match {
@ -58,63 +44,63 @@ sealed trait Transaction extends NetworkElement {
}
}
case object EmptyTransaction extends Transaction {
override def txId = DoubleSha256Digest(BitcoinSUtil.decodeHex("0000000000000000000000000000000000000000000000000000000000000000"))
override def version = TransactionConstants.version
override def inputs = Seq()
override def outputs = Seq()
override def lockTime = TransactionConstants.lockTime
sealed trait BaseTransaction extends Transaction {
override def hex = RawBaseTransactionParser.write(this)
}
case object EmptyTransaction extends BaseTransaction {
override def txId = DoubleSha256Digest(BitcoinSUtil.decodeHex("0000000000000000000000000000000000000000000000000000000000000000"))
override def version = TransactionConstants.version
override def inputs = Nil
override def outputs = Nil
override def lockTime = TransactionConstants.lockTime
}
sealed trait WitnessTransaction extends Transaction {
/** 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
override def hex = RawWitnessTransactionParser.write(this)
}
object Transaction extends Factory[Transaction] {
private sealed case class TransactionImpl(version : UInt32, inputs : Seq[TransactionInput],
outputs : Seq[TransactionOutput], lockTime : UInt32) extends Transaction
/**
* Updates a transaction outputs
* @param updatedOutputs
* @return
*/
/** Updates a transaction outputs */
def factory(oldTx : Transaction, updatedOutputs : UpdateTransactionOutputs) : Transaction = {
TransactionImpl(oldTx.version,oldTx.inputs,updatedOutputs.outputs,oldTx.lockTime)
Transaction(oldTx.version,oldTx.inputs,updatedOutputs.outputs,oldTx.lockTime)
}
/**
* Updates a transaction's inputs
* @param updatedInputs
* @return
*/
/** Updates a transaction's input */
def factory(oldTx : Transaction,updatedInputs : UpdateTransactionInputs) : Transaction = {
TransactionImpl(oldTx.version,updatedInputs.inputs,oldTx.outputs,oldTx.lockTime)
Transaction(oldTx.version,updatedInputs.inputs,oldTx.outputs,oldTx.lockTime)
}
/**
* Factory function that modifies a transactions locktime
* @param oldTx
* @param lockTime
* @return
*/
/** Factory function that modifies a transactions locktime */
def factory(oldTx : Transaction, lockTime : UInt32) : Transaction = {
TransactionImpl(oldTx.version,oldTx.inputs,oldTx.outputs,lockTime)
Transaction(oldTx.version,oldTx.inputs,oldTx.outputs,lockTime)
}
/**
* Removes the inputs of the transactions
* @return
*/
def emptyInputs(oldTx : Transaction) : Transaction = TransactionImpl(oldTx.version,Seq(),oldTx.outputs,oldTx.lockTime)
/** Removes the inputs of the transactions */
def emptyInputs(oldTx : Transaction) : Transaction = Transaction(oldTx.version,Nil,oldTx.outputs,oldTx.lockTime)
/**
* Removes the outputs of the transactions
* @return
*/
def emptyOutputs(oldTx : Transaction) : Transaction = TransactionImpl(oldTx.version,oldTx.inputs,Seq(),oldTx.lockTime)
/** Removes the outputs of the transactions */
def emptyOutputs(oldTx : Transaction) : Transaction = Transaction(oldTx.version,oldTx.inputs,Nil,oldTx.lockTime)
def factory(bytes : Array[Byte]) : Transaction = fromBytes(bytes.toSeq)
def fromBytes(bytes : Seq[Byte]) : Transaction = RawTransactionParser.read(bytes)
def fromBytes(bytes : Seq[Byte]) : Transaction = {
val wtxTry = Try(RawWitnessTransactionParser.read(bytes))
wtxTry match {
case Success(wtx) => wtx
case Failure(_) => RawBaseTransactionParser.read(bytes)
}
}
def apply(bytes : Array[Byte]) : Transaction = factory(bytes)
def apply(oldTx : Transaction, lockTime : UInt32) : Transaction = factory(oldTx,lockTime)
@ -123,6 +109,31 @@ object Transaction extends Factory[Transaction] {
def apply(version : UInt32, inputs : Seq[TransactionInput],
outputs : Seq[TransactionOutput], lockTime : UInt32) : Transaction = {
TransactionImpl(version,inputs,outputs,lockTime)
BaseTransaction(version,inputs,outputs,lockTime)
}
}
object BaseTransaction extends Factory[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)
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],
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)
override def fromBytes(bytes: Seq[Byte]): WitnessTransaction = RawWitnessTransactionParser.read(bytes)
}

View file

@ -1,8 +1,5 @@
package org.bitcoins.core.protocol.transaction
import org.bitcoins.core.serializers.transaction.{RawTransactionParser, RawTransactionInputParser}
import org.bitcoins.core.util.Factory
/**
* Created by chris on 2/21/16.
*/

View file

@ -19,11 +19,8 @@ sealed trait TransactionInput extends NetworkElement {
def sequence : UInt32
def scriptSigCompactSizeUInt : CompactSizeUInt = CompactSizeUInt.calculateCompactSizeUInt(scriptSignature.bytes)
//https://bitcoin.org/en/developer-reference#txin
override def size = previousOutput.size + scriptSignature.size +
scriptSigCompactSizeUInt.size.toInt + 4
override def size = previousOutput.size + scriptSignature.size + 4
def hex = RawTransactionInputParser.write(Seq(this))
}
@ -32,7 +29,6 @@ case object EmptyTransactionInput extends TransactionInput {
override def previousOutput = TransactionInput.empty.previousOutput
override def scriptSignature = TransactionInput.empty.scriptSignature
override def sequence = TransactionInput.empty.sequence
override def scriptSigCompactSizeUInt = TransactionInput.empty.scriptSigCompactSizeUInt
}
/**
@ -65,14 +61,7 @@ object TransactionInput extends Factory[TransactionInput] {
TransactionInputImpl(oldInput.previousOutput, oldInput.scriptSignature,sequenceNumber)
}
/**
* Creates a transaction input from a given output and the output's transaction
*
* @param oldInput
* @param output
* @param outputsTransaction
* @return
*/
/** Creates a transaction input from a given output and the output's transaction */
private def factory(oldInput : TransactionInput,output : TransactionOutput, outputsTransaction : Transaction) : TransactionInput = {
val outPoint = TransactionOutPoint(output,outputsTransaction)
factory(oldInput,outPoint)
@ -118,7 +107,6 @@ object TransactionInput extends Factory[TransactionInput] {
/**
* 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
*/

View file

@ -14,9 +14,9 @@ sealed trait TransactionOutput extends NetworkElement {
def value : CurrencyUnit
def scriptPubKey : ScriptPubKey
def scriptPubKeyCompactSizeUInt : CompactSizeUInt = CompactSizeUInt.parseCompactSizeUInt(scriptPubKey)
//https://bitcoin.org/en/developer-reference#txout
override def size = scriptPubKey.size + scriptPubKeyCompactSizeUInt.size.toInt + 8
override def size = scriptPubKey.size + 8
override def hex = RawTransactionOutputParser.write(Seq(this))
}

View file

@ -0,0 +1,35 @@
package org.bitcoins.core.protocol.transaction
import org.bitcoins.core.protocol.NetworkElement
import org.bitcoins.core.protocol.script.ScriptWitness
import org.bitcoins.core.serializers.transaction.RawTransactionWitnessParser
import org.bitcoins.core.util.{BitcoinSUtil, Factory}
/**
* 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 trait TransactionWitness extends NetworkElement {
def witnesses: Seq[ScriptWitness]
override def hex = RawTransactionWitnessParser.write(this)
}
/** Used to represent a transaction witness pre segwit, see BIP141 for details */
case object EmptyWitness extends TransactionWitness {
override def hex = "00"
override def witnesses = Nil
}
object TransactionWitness {
private case class TransactionWitnessImpl(witnesses: Seq[ScriptWitness]) extends TransactionWitness
def apply(witnesses: Seq[ScriptWitness]): TransactionWitness = TransactionWitnessImpl(witnesses)
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(hex: String, numInputs: Int): TransactionWitness = fromBytes(BitcoinSUtil.decodeHex(hex),numInputs)
}

View file

@ -1,10 +1,11 @@
package org.bitcoins.core.script
import org.bitcoins.core.crypto.TransactionSignatureComponent
import org.bitcoins.core.crypto.{BaseTransactionSignatureComponent, TransactionSignatureComponent, WitnessV0TransactionSignatureComponent}
import org.bitcoins.core.currency.CurrencyUnit
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.script.ScriptPubKey
import org.bitcoins.core.protocol.transaction.Transaction
import org.bitcoins.core.protocol.script.{ScriptPubKey, ScriptWitness, SignatureVersion}
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._
@ -17,60 +18,33 @@ sealed trait ScriptProgram {
/**
* This contains all relevant information for hashing and checking a [[org.bitcoins.core.protocol.script.ScriptSignature]] for a [[Transaction]].
*
* @return
*/
def txSignatureComponent : TransactionSignatureComponent
/**
* The current state of the stack for execution of the [[ScriptProgram]].
*
* @return
*/
/** The current state of the stack for execution of the [[ScriptProgram]]. */
def stack : List[ScriptToken]
/**
* The script operations that need to still be executed.
*
* @return
*/
/** The script operations that need to still be executed. */
def script : List[ScriptToken]
/**
* The original script that was given.
*
* @return
*/
/** The original script that was given. */
def originalScript : List[ScriptToken]
/**
* The alternative stack is used in some Script op codes.
*
* @return
*/
/** The alternative stack is used in some Script op codes. */
def altStack : List[ScriptToken]
/**
* [[ScriptFlag]] that are run with the script.
* These flags indicate special conditions that a script needs to be run with.
* https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.h#L31
*
* [[https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.h#L31]]
* @return
*/
def flags : Seq[ScriptFlag]
/**
* Returns true if the stack top is true
* @return
*/
/** Returns true if the stack top is true */
def stackTopIsTrue = !stackTopIsFalse
/**
* Returns true if the stack top is false
* @return
*/
/** Returns true if the stack top is false */
def stackTopIsFalse : Boolean = {
if (stack.headOption.isDefined &&
(stack.head.hex == OP_FALSE.hex || stack.head.hex == ScriptNumber.negativeZero.hex ||
@ -79,7 +53,6 @@ sealed trait ScriptProgram {
else false
}
}
@ -88,19 +61,14 @@ sealed trait ScriptProgram {
*/
sealed trait PreExecutionScriptProgram extends ScriptProgram
sealed trait ExecutionInProgressScriptProgram extends ScriptProgram {
/**
* The index of the last [[org.bitcoins.core.script.crypto.OP_CODESEPARATOR]]
* @return
*/
/** The index of the last [[org.bitcoins.core.script.crypto.OP_CODESEPARATOR]] */
def lastCodeSeparator : Option[Int]
}
sealed trait ExecutedScriptProgram extends ScriptProgram {
/**
* Indicates if the [[ScriptProgram]] has encountered a [[ScriptError]] in its execution.
* @return
*/
/** Indicates if the [[ScriptProgram]] has encountered a [[ScriptError]] in its execution.*/
def error : Option[ScriptError]
}
@ -109,25 +77,25 @@ sealed trait ExecutedScriptProgram extends ScriptProgram {
*/
object ScriptProgram {
/**
* Implementation type for a [[PreExecutionScriptProgram]] - a [[ScriptProgram]] that has not yet begun being
* Implementation type for a [[PreExecutionScriptProgram]] - a [[ScriptProgram]] that has not yet begun being
* evaluated by the [[org.bitcoins.core.script.interpreter.ScriptInterpreter]].
*/
*/
private sealed case class PreExecutionScriptProgramImpl(txSignatureComponent : TransactionSignatureComponent,
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
* Implementation type for a [[ExecutionInProgressScriptProgram]] - a [[ScriptProgram]] that is currently being
* evaluated by the [[org.bitcoins.core.script.interpreter.ScriptInterpreter]].
*/
*/
private sealed case class ExecutionInProgressScriptProgramImpl(txSignatureComponent : TransactionSignatureComponent,
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
* The implementation type for a [[ExecutedScriptProgram]] - a [[ScriptProgram]] that has been evaluated completely
* by the [[org.bitcoins.core.script.interpreter.ScriptInterpreter]].
*/
*/
private sealed case class ExecutedScriptProgramImpl(txSignatureComponent : TransactionSignatureComponent,
stack : List[ScriptToken],script : List[ScriptToken], originalScript : List[ScriptToken], altStack : List[ScriptToken],
flags : Seq[ScriptFlag], error : Option[ScriptError]) extends ExecutedScriptProgram
@ -142,14 +110,14 @@ object ScriptProgram {
/**
* 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 factory(oldProgram : ScriptProgram, error : ScriptError) : ExecutedScriptProgram = oldProgram match {
* 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 =>
throw new RuntimeException("We cannot set an error on the script program before it is executed")
ScriptProgram(ScriptProgram.toExecutionInProgress(program),error)
case program : ExecutionInProgressScriptProgram =>
ExecutedScriptProgramImpl(program.txSignatureComponent, program.stack, program.script,program.originalScript,
program.altStack, program.flags, Some(error))
@ -158,15 +126,10 @@ object ScriptProgram {
program.altStack, program.flags, Some(error))
}
/**
* Updates the [[ScriptFlag]] on a given [[ScriptProgram]].
* @return
*/
def factory(oldProgram : ScriptProgram, flags : Seq[ScriptFlag]) : ScriptProgram = oldProgram match {
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)
program.altStack,flags)
case program : ExecutionInProgressScriptProgram =>
ExecutionInProgressScriptProgramImpl(program.txSignatureComponent, program.stack,program.script,program.originalScript,
program.altStack, flags, program.lastCodeSeparator)
@ -174,12 +137,7 @@ object ScriptProgram {
throw new RuntimeException("Cannot update the script flags on a program that has been executed")
}
/**
* Updates the [[ScriptProgram]] with a sequence of [[ScriptToken]]s depending on the [[UpdateIndicator]].
* @return
*/
def factory(oldProgram : ScriptProgram, tokens : Seq[ScriptToken], indicator : UpdateIndicator) : ScriptProgram = {
def apply(oldProgram : ScriptProgram, tokens : Seq[ScriptToken], indicator : UpdateIndicator) : ScriptProgram = {
indicator match {
case Stack =>
oldProgram match {
@ -228,151 +186,144 @@ object ScriptProgram {
}
}
/**
* Changes the [[ScriptToken]] assembly-construction of the script or stack inside a [[ScriptProgram]].
* @return
*/
def factory(oldProgram : ScriptProgram, stackTokens : Seq[ScriptToken], scriptTokens : Seq[ScriptToken]) : ScriptProgram = {
val updatedStack = apply(oldProgram,stackTokens,Stack)
val updatedScript = apply(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.
* @return
*/
def factory(oldProgram : ExecutionInProgressScriptProgram, lastCodeSeparator : Int) : ExecutionInProgressScriptProgram = {
def apply(oldProgram : ExecutionInProgressScriptProgram, lastCodeSeparator : Int) : ExecutionInProgressScriptProgram = {
ExecutionInProgressScriptProgramImpl(oldProgram.txSignatureComponent,
oldProgram.stack, oldProgram.script, oldProgram.originalScript,
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
* @return
*/
def factory(oldProgram : ExecutionInProgressScriptProgram, tokens : Seq[ScriptToken], indicator: UpdateIndicator,
lastCodeSeparator : Int) : ExecutionInProgressScriptProgram = {
val updatedIndicator = apply(oldProgram, tokens, indicator)
def apply(oldProgram : ExecutionInProgressScriptProgram, tokens : Seq[ScriptToken], indicator: UpdateIndicator,
lastCodeSeparator : Int) : ExecutionInProgressScriptProgram = {
val updatedIndicator = ScriptProgram(oldProgram, tokens, indicator)
updatedIndicator match {
case e : ExecutionInProgressScriptProgram =>
apply(e,lastCodeSeparator)
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]].
* @return
*/
def factory(oldProgram : ScriptProgram, stack : Seq[ScriptToken], script : Seq[ScriptToken], altStack : Seq[ScriptToken]) : ScriptProgram = {
val updatedProgramStack = apply(oldProgram,stack, Stack)
val updatedProgramScript = apply(updatedProgramStack, script, Script)
val updatedProgramAltStack = apply(updatedProgramScript, altStack, AltStack)
/** 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)
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 ???
* 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 factory(transaction: Transaction, scriptPubKey : ScriptPubKey, inputIndex : UInt32,
flags : Seq[ScriptFlag]) : PreExecutionScriptProgram = {
def apply(transaction: WitnessTransaction, scriptPubKey : ScriptPubKey, inputIndex : UInt32,
flags : Seq[ScriptFlag],
amount: CurrencyUnit) : PreExecutionScriptProgram = {
val script = transaction.inputs(inputIndex.toInt).scriptSignature.asm
apply(transaction,scriptPubKey,inputIndex,script.toList,flags)
ScriptProgram(transaction,scriptPubKey,inputIndex,script.toList,flags, amount)
}
/**
* Creates a new [[ScriptProgram]] that can be used to verify if a [[Transaction]] at the given inputIndex
* spends a given [[ScriptPubKey]] correctly.
*
* @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 script the script that we are currently executing
* @param flags the flags which we are enforcing inside of the script interpreter
* @return the script program representing all of this information
*/
def factory(transaction: Transaction, scriptPubKey : ScriptPubKey, inputIndex : UInt32, script : Seq[ScriptToken],
flags : Seq[ScriptFlag]) : PreExecutionScriptProgram = {
val txSignatureComponent = TransactionSignatureComponent(transaction,inputIndex,scriptPubKey,flags)
PreExecutionScriptProgramImpl(txSignatureComponent,List(),script.toList,script.toList,List(),flags)
}
/**
* The intention for this factory function is to allow us to create a [[ScriptProgram]] that already has a stack state. This
* is useful for after execution of a scriptSig, copying the stack into this program with the [[ScriptPubKey]] to
* run inside the script.
* @param transaction the transaction being checked
* @param scriptPubKey the scriptPubKey which the input is spending
* @param inputIndex the input's index inside of the transaction we are spending
* @param stack the current stack state of the program
* @param script the script that we need to execute
* @param flags the flags which we are enforcing inside of the script interpreter
*/
def factory(transaction: Transaction, scriptPubKey : ScriptPubKey, inputIndex : UInt32, stack : Seq[ScriptToken],
script : Seq[ScriptToken], flags : Seq[ScriptFlag]) : ScriptProgram = {
val program = factory(transaction,scriptPubKey,inputIndex,script,flags)
apply(program,stack,Stack)
}
/**
* The intention for this factory function is to allow us to create a [[ScriptProgram]] that already has a stack state. This
* is useful for after execution of a scriptSig, copying the stack into this program with the scriptPubKey read to
* run inside the script variable
* @param txSignatureComponent the relevant transaction information for execution of a script program
* @param stack the current stack state of the program
* @param script the script that we need to execute
* @return
*/
def factory(txSignatureComponent : TransactionSignatureComponent, stack : Seq[ScriptToken], script : Seq[ScriptToken]) : ScriptProgram = {
apply(txSignatureComponent.transaction,txSignatureComponent.scriptPubKey,txSignatureComponent.inputIndex,
stack,script,txSignatureComponent.flags)
}
def apply(oldProgram : ScriptProgram, error : ScriptError) : ExecutedScriptProgram = factory(oldProgram, error)
def apply(oldProgram : ScriptProgram, flags : Seq[ScriptFlag]) : ScriptProgram = factory(oldProgram, flags)
def apply(oldProgram : ScriptProgram, tokens : Seq[ScriptToken], indicator : UpdateIndicator) : ScriptProgram =
factory(oldProgram, tokens, indicator)
def apply(oldProgram : ScriptProgram, stackTokens : Seq[ScriptToken], scriptTokens : Seq[ScriptToken]) : ScriptProgram =
factory(oldProgram, stackTokens, scriptTokens)
def apply(oldProgram : ExecutionInProgressScriptProgram, lastCodeSeparator : Int) : ExecutionInProgressScriptProgram = factory(oldProgram, lastCodeSeparator)
def apply(oldProgram : ExecutionInProgressScriptProgram, tokens : Seq[ScriptToken], indicator: UpdateIndicator, lastCodeSeparator : Int) : ExecutionInProgressScriptProgram =
factory(oldProgram, tokens, indicator, lastCodeSeparator)
def apply(oldProgram : ScriptProgram, stack : Seq[ScriptToken], script : Seq[ScriptToken], altStack : Seq[ScriptToken],
updateIndicator: UpdateIndicator) : ScriptProgram = factory(oldProgram, stack, script, altStack)
def apply(transaction: Transaction, scriptPubKey : ScriptPubKey, inputIndex : UInt32,
flags : Seq[ScriptFlag]) : PreExecutionScriptProgram = factory(transaction, scriptPubKey, inputIndex, flags)
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, script : Seq[ScriptToken],
flags : Seq[ScriptFlag]) : PreExecutionScriptProgram = factory(transaction, scriptPubKey, inputIndex, script, flags)
def apply(transaction: WitnessTransaction, scriptPubKey : ScriptPubKey, inputIndex : UInt32, script : Seq[ScriptToken],
flags : Seq[ScriptFlag], amount: CurrencyUnit) : PreExecutionScriptProgram = {
val txSignatureComponent = TransactionSignatureComponent(transaction,inputIndex,scriptPubKey,flags, amount)
PreExecutionScriptProgramImpl(txSignatureComponent,Nil,script.toList,script.toList,Nil,flags)
}
def apply(transaction: Transaction, scriptPubKey : ScriptPubKey, inputIndex : UInt32, stack : Seq[ScriptToken],
script : Seq[ScriptToken], flags : Seq[ScriptFlag]) : ScriptProgram = factory(transaction, scriptPubKey, inputIndex, stack, script, flags)
def apply(txSignatureComponent : TransactionSignatureComponent, stack : Seq[ScriptToken], script : Seq[ScriptToken]) : ScriptProgram =
factory(txSignatureComponent, stack, script)
def apply(transaction: WitnessTransaction, scriptPubKey : ScriptPubKey, 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: TransactionSignatureComponent): PreExecutionScriptProgram = {
ScriptProgram(txSignatureComponent.transaction, txSignatureComponent.scriptPubKey, txSignatureComponent.inputIndex,txSignatureComponent.flags)
def apply(txSignatureComponent : TransactionSignatureComponent, stack : Seq[ScriptToken],
script : Seq[ScriptToken]) : ScriptProgram = txSignatureComponent match {
case b: BaseTransactionSignatureComponent =>
ScriptProgram(b.transaction, b.scriptPubKey, b.inputIndex, stack, script, b.flags)
case w: WitnessV0TransactionSignatureComponent =>
ScriptProgram(w.transaction, w.scriptPubKey, w.inputIndex,
stack, script, w.flags, w.witness, w.amount)
}
def apply(txSignatureComponent: TransactionSignatureComponent, stack: Seq[ScriptToken], script: Seq[ScriptToken],
originalScript: Seq[ScriptToken]): ScriptProgram = txSignatureComponent match {
case b : BaseTransactionSignatureComponent =>
ExecutionInProgressScriptProgramImpl(b,stack.toList,
script.toList,originalScript.toList,Nil,b.flags,None)
case w: WitnessV0TransactionSignatureComponent =>
ScriptProgram(w,stack,script,originalScript,Nil,w.flags,w.amount)
}
def apply(txSignatureComponent: TransactionSignatureComponent): PreExecutionScriptProgram = txSignatureComponent match {
case b : BaseTransactionSignatureComponent =>
ScriptProgram(b.transaction, b.scriptPubKey,
b.inputIndex, b.flags)
case w : WitnessV0TransactionSignatureComponent =>
ScriptProgram(w.transaction, w.scriptPubKey,
w.inputIndex, w.flags, w.amount)
}
/** Creates a fresh [[PreExecutionScriptProgram]] */
def apply(transaction: WitnessTransaction, scriptPubKey: ScriptPubKey, inputIndex: UInt32, stack: Seq[ScriptToken],
script: Seq[ScriptToken], originalScript: Seq[ScriptToken], altStack: Seq[ScriptToken],
flags: Seq[ScriptFlag], sigVersion: SignatureVersion,
amount: CurrencyUnit): PreExecutionScriptProgram = {
val t = TransactionSignatureComponent(transaction,inputIndex,
scriptPubKey,flags, amount)
PreExecutionScriptProgramImpl(t,stack.toList,script.toList,originalScript.toList,altStack.toList,flags)
}
/** Creates a fresh instance of [[PreExecutionScriptProgram]] */
def apply(txSigComponent: WitnessV0TransactionSignatureComponent, stack: Seq[ScriptToken], script: Seq[ScriptToken],
originalScript: Seq[ScriptToken], altStack: Seq[ScriptToken], flags: Seq[ScriptFlag], amount: CurrencyUnit): PreExecutionScriptProgram = {
ScriptProgram(txSigComponent.transaction, txSigComponent.scriptPubKey, txSigComponent.inputIndex, stack,
script,originalScript, altStack,flags, txSigComponent.sigVersion, amount)
}
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 = TransactionSignatureComponent(transaction,inputIndex,scriptPubKey,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 = TransactionSignatureComponent(transaction,inputIndex,scriptPubKey,flags)
val script = t.scriptSignature.asm
PreExecutionScriptProgramImpl(t,Nil,script.toList,script.toList,Nil,t.flags)
}
/**
@ -397,13 +348,10 @@ object ScriptProgram {
/**
* Changes a [[ScriptProgram]] that is a [[PreExecutionScriptProgram]] and changes it to an [[ExecutionInProgressScriptProgram]] given the stack state.
* @param preExecutionScriptProgram
* @param stack
* @return
*/
def toExecutionInProgress(preExecutionScriptProgram: PreExecutionScriptProgram, stack : Option[List[ScriptToken]]) : ExecutionInProgressScriptProgram = {
def toExecutionInProgress(preExecutionScriptProgram: PreExecutionScriptProgram, stack : Option[Seq[ScriptToken]]) : ExecutionInProgressScriptProgram = {
stack match {
case Some(stackTokens) => ExecutionInProgressScriptProgramImpl(preExecutionScriptProgram.txSignatureComponent,stackTokens,preExecutionScriptProgram.script,
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,
@ -411,8 +359,5 @@ object ScriptProgram {
}
}
}

View file

@ -24,7 +24,7 @@ object BytesToPushOntoStack extends ScriptOperationFactory[BytesToPushOntoStack]
}
override def operations : Seq[BytesToPushOntoStack] =
(for { i <- 0 to 75 } yield BytesToPushOntoStackImpl(i)).toList
(for { i <- 0 to 75 } yield BytesToPushOntoStackImpl(i))
def fromNumber(num : Int) : BytesToPushOntoStack = {
if (num > 75) throw new IllegalArgumentException("We cannot have a BytesToPushOntoStack for greater than 75 bytes")

View file

@ -1,8 +1,10 @@
package org.bitcoins.core.script.control
import org.bitcoins.core.protocol.script.SigVersionWitnessV0
import org.bitcoins.core.script.result._
import org.bitcoins.core.script.{ScriptProgram}
import org.bitcoins.core.script.ScriptProgram
import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.flag.ScriptFlagUtil
import org.bitcoins.core.util._
import org.slf4j.LoggerFactory
@ -15,15 +17,14 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
/**
* If the top stack value is not 0, the statements are executed. The top stack value is removed.
*
* @param program
* @return
*/
/** If the top stack value is not 0, the statements are executed. The top stack value is removed. */
def opIf(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.isDefined && program.script.head == OP_IF, "Script top was not OP_IF")
val sigVersion = program.txSignatureComponent.sigVersion
val flags = program.flags
val minimalIfEnabled = ScriptFlagUtil.minimalIfEnabled(flags)
val binaryTree = parseBinaryTree(program.script)
val stackTop = program.stack.headOption
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")
@ -31,6 +32,13 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
} else if (program.stack.isEmpty) {
logger.error("We do not have any stack elements for our OP_IF")
ScriptProgram(program,ScriptErrorUnbalancedConditional)
} else if (sigVersion == SigVersionWitnessV0 && minimalIfEnabled
&& (stackTop.get.bytes.size > 1 ||
(stackTop.get.bytes.size == 1 && stackTop.get.bytes.head != 1))) {
//see: https://github.com/bitcoin/bitcoin/blob/528472111b4965b1a99c4bcf08ac5ec93d87f10f/src/script/interpreter.cpp#L447-L452
//https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2016-August/013014.html
logger.error("OP_IF argument was not minimally encoded, got: " + stackTop)
ScriptProgram(program, ScriptErrorMinimalIf)
} else if (program.stackTopIsTrue) {
logger.debug("OP_IF stack top was true")
logger.debug("Stack top: " + program.stack)
@ -40,7 +48,7 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
val newScript = newTreeWithoutOpElse.toList
logger.debug("New script after removing OP_ELSE branch " + newScript)
ScriptProgram(program, program.stack.tail,newScript.tail)
} else {
} else {
logger.debug("OP_IF stack top was false")
//remove the OP_IF
val scriptWithoutOpIf : BinaryTree[ScriptToken] = removeFirstOpIf(binaryTree)
@ -50,15 +58,15 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
}
/**
* If the top stack value is 0, the statements are executed. The top stack value is removed.
*
* @param program
* @return
*/
/** If the top stack value is 0, the statements are executed. The top stack value is removed. */
def opNotIf(program : ScriptProgram) : ScriptProgram = {
//TODO: Try and reduce this down to using OP_IF by inverting the stack top
require(program.script.headOption.isDefined && program.script.head == OP_NOTIF, "Script top was not OP_NOTIF")
val binaryTree = parseBinaryTree(program.script)
val sigVersion = program.txSignatureComponent.sigVersion
val flags = program.flags
val minimalIfEnabled = ScriptFlagUtil.minimalIfEnabled(flags)
val stackTop = program.stack.headOption
logger.debug("Parsed binary tree: " + binaryTree)
if (!checkMatchingOpIfOpNotIfOpEndIf(program.originalScript)) {
@ -67,6 +75,13 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
} else if (program.stack.isEmpty) {
logger.error("We do not have any stack elements for our OP_NOTIF")
ScriptProgram(program,ScriptErrorUnbalancedConditional)
} else if (sigVersion == SigVersionWitnessV0 && minimalIfEnabled
&& (stackTop.get.bytes.size > 1 ||
(stackTop.get.bytes.size == 1 && stackTop.get.bytes.head != 1))) {
//see: https://github.com/bitcoin/bitcoin/blob/528472111b4965b1a99c4bcf08ac5ec93d87f10f/src/script/interpreter.cpp#L447-L452
//https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2016-August/013014.html
logger.error("OP_NOTIF argument was not minimally encoded, got: " + stackTop)
ScriptProgram(program, ScriptErrorMinimalIf)
} else if (program.stackTopIsTrue) {
//remove the OP_NOTIF
val scriptWithoutOpIf : BinaryTree[ScriptToken] = removeFirstOpIf(binaryTree)
@ -80,12 +95,7 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
}
}
/**
* Evaluates the OP_ELSE operator
*
* @param program
* @return
*/
/** Evaluates the OP_ELSE operator */
def opElse(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.isDefined && program.script.head == OP_ELSE, "First script opt must be OP_ELSE")
@ -116,12 +126,7 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
}
/**
* Evaluates an OP_ENDIF operator
*
* @param program
* @return
*/
/** Evaluates an OP_ENDIF operator */
def opEndIf(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.isDefined && program.script.head == OP_ENDIF, "Script top must be OP_ENDIF")
if (!checkMatchingOpIfOpNotIfOpEndIf(program.originalScript)) {
@ -138,7 +143,6 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
* with a 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.
*
* @param program
* @return
*/
@ -148,12 +152,7 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
}
/**
* Marks transaction as invalid if top stack value is not true.
*
* @param program
* @return
*/
/** Marks transaction as invalid if top stack value is not true. */
def opVerify(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.isDefined && program.script.head == OP_VERIFY, "Script top must be OP_VERIFY")
program.stack.size > 0 match {
@ -169,23 +168,13 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
}
/**
* Parses a list of script tokens into its corresponding binary tree
*
* @param script
* @return
*/
/** Parses a list of script tokens into its corresponding binary tree */
def parseBinaryTree(script : List[ScriptToken]) : BinaryTree[ScriptToken] = {
val bTree = loop(script,Empty)
bTree
}
/**
* The loop that parses a list of script tokens into a binary tree
*
* @param script
* @return
*/
/** The loop that parses a list of script tokens into a binary tree */
@tailrec
private def loop(script : List[ScriptToken], tree : BinaryTree[ScriptToken]) : BinaryTree[ScriptToken] = {
/* logger.debug("Script : " + script)
@ -217,7 +206,6 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
/**
* Inserts a sub tree into the parse tree of Script.
*
* @param tree the parse tree of the control flow of the Script program
* @param subTree the parse tree that needs to be inserted into the control flow of the program
* @return the full parse tree combined
@ -250,12 +238,7 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
}
/**
* Parses an OP_IF script token
*
* @param script
* @return
*/
/** Parses an OP_IF script token */
private def parseOpIf(script : List[ScriptToken]) : (List[ScriptToken],BinaryTree[ScriptToken]) = script match {
case OP_IF :: t => (t, Node(OP_IF,Empty,Empty))
case h :: t => throw new IllegalArgumentException("Cannot parse " + h + " as an OP_IF")
@ -263,24 +246,14 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
}
/**
* Parses an OP_NOTIF script token
*
* @param script
* @return
*/
/** Parses an OP_NOTIF script token */
private def parseOpNotIf(script : List[ScriptToken]) : (List[ScriptToken],BinaryTree[ScriptToken]) = script match {
case OP_NOTIF :: t => (t, Node(OP_NOTIF,Empty,Empty))
case h :: t => throw new IllegalArgumentException("Cannot parse " + h + " as an OP_NOTIF")
case Nil => (script,Empty)
}
/**
* Parses and OP_ELSE expression
*
* @param script
* @return
*/
/** Parses and OP_ELSE expression */
private def parseOpElse(script : List[ScriptToken]) : (List[ScriptToken],BinaryTree[ScriptToken]) = script match {
case OP_ELSE :: t => (t,Node(OP_ELSE,Empty,Empty))
case h :: t => throw new RuntimeException("Cannot parse " + h + " as an OP_ELSE")
@ -294,12 +267,7 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
}
/**
* Checks if an OP_IF/OP_NOTIF script token has a matching OP_ENDIF
*
* @param script
* @return
*/
/** Checks if an [[OP_IF]]/[[OP_NOTIF]] script token has a matching [[OP_ENDIF]] */
def checkMatchingOpIfOpNotIfOpEndIf(script : List[ScriptToken]) : Boolean = {
@tailrec
def loop(script : List[ScriptToken], counter : Int) : Boolean = script match {
@ -315,12 +283,7 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
/**
* Returns the first index of an OP_ENDIF
*
* @param script
* @return
*/
/** Returns the first index of an OP_ENDIF */
def findFirstOpEndIf(script : List[ScriptToken]) : Option[Int] = {
val index = script.indexOf(OP_ENDIF)
index match {
@ -330,24 +293,14 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
}
/**
* Finds the last OP_ENDIF in the given script
*
* @param script
* @return
*/
/** Finds the last OP_ENDIF in the given script */
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
*
* @param script
* @return
*/
/** Returns the first index of an OP_ENDIF */
def findFirstOpElse(script : List[ScriptToken]) : Option[Int] = {
val index = script.indexOf(OP_ELSE)
index match {
@ -356,12 +309,7 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
}
}
/**
* Removes the first OP_ELSE expression encountered in the script
*
* @param script
* @return
*/
/** Removes the first OP_ELSE expression encountered in the script */
def removeFirstOpElse(script : List[ScriptToken]) : List[ScriptToken] = {
if (script.contains(OP_ELSE)) {
val firstOpElseIndex = findFirstOpElse(script)
@ -377,12 +325,7 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
}
/**
* Removes the first OP_ELSE {expression} in a binary tree
*
* @param tree
* @return
*/
/** Removes the first OP_ELSE {expression} in a binary tree */
def removeFirstOpElse(tree : BinaryTree[ScriptToken]) : BinaryTree[ScriptToken] = {
tree match {
case Empty => Empty
@ -413,12 +356,7 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
}
/**
* Removes the first OP_IF { expression } encountered in the script
*
* @param script
* @return
*/
/** Removes the first OP_IF { expression } encountered in the script */
def removeFirstOpIf(script : List[ScriptToken]) : List[ScriptToken] = {
val firstOpIfIndex = script.indexOf(OP_IF)
val matchingOpEndIfIndex = findMatchingOpEndIf(script)
@ -437,24 +375,14 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
}
/**
* Removes the first occurrence of OP_IF or OP_NOTIF in the binary tree
*
* @param tree
* @return
*/
/** Removes the first occurrence of OP_IF or OP_NOTIF in the binary tree */
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")
if (tree.right.isDefined && tree.right.get.value == Some(OP_ELSE)) tree.right.getOrElse(Empty)
else tree.findFirstDFS[ScriptToken](OP_ENDIF)().getOrElse(Empty)
}
/**
* Finds the indexes of our OP_ELSE (if it exists) and our OP_ENDIF
*
* @param script
* @return
*/
/** Finds the indexes of our OP_ELSE (if it exists) and our OP_ENDIF */
def findFirstIndexesOpElseOpEndIf(script : List[ScriptToken]) : (Option[Int],Option[Int]) = {
val indexOpElse = findFirstOpElse(script)
val indexOpEndIf = findFirstOpEndIf(script)
@ -462,12 +390,7 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
}
/**
* Returns the index of the matching OP_ENDIF for the OP_IF statement
*
* @param script
* @return
*/
/** 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)

View file

@ -1,15 +1,14 @@
package org.bitcoins.core.script.crypto
import org.bitcoins.core.crypto.{SignatureValidationFailureIncorrectSignatures, _}
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction.Transaction
import org.bitcoins.core.script.control.{ControlOperationsInterpreter, OP_VERIFY}
import org.bitcoins.core.script.result._
import org.bitcoins.core.script.flag.{ScriptFlagUtil, ScriptVerifyDerSig, ScriptVerifyNullDummy}
import org.bitcoins.core.script._
import org.bitcoins.core.script.constant._
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil, BitcoinScriptUtil, CryptoUtil}
import org.slf4j.LoggerFactory
import org.bitcoins.core.script.control.{ControlOperationsInterpreter, OP_VERIFY}
import org.bitcoins.core.script.flag.{ScriptFlag, ScriptFlagUtil}
import org.bitcoins.core.script.result._
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinScriptUtil, CryptoUtil}
import scala.annotation.tailrec
/**
@ -17,52 +16,32 @@ import org.slf4j.LoggerFactory
*/
trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger {
/**
* The input is hashed twice: first with SHA-256 and then with RIPEMD-160.
* @param program
* @return
*/
/** The input is hashed twice: first with SHA-256 and then with RIPEMD-160. */
def opHash160(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.isDefined && program.script.head == OP_HASH160, "Script operation must be OP_HASH160")
executeHashFunction(program, CryptoUtil.sha256Hash160(_ : Seq[Byte]))
}
/**
* The input is hashed using RIPEMD-160.
* @param program
* @return
*/
/** The input is hashed using RIPEMD-160. */
def opRipeMd160(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.isDefined && program.script.head == OP_RIPEMD160, "Script operation must be OP_RIPEMD160")
executeHashFunction(program, CryptoUtil.ripeMd160(_ : Seq[Byte]))
}
/**
* The input is hashed using SHA-256.
* @param program
* @return
*/
/** The input is hashed using SHA-256. */
def opSha256(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.isDefined && program.script.head == OP_SHA256, "Script operation must be OP_SHA256")
executeHashFunction(program, CryptoUtil.sha256(_ : Seq[Byte]))
}
/**
* The input is hashed two times with SHA-256.
* @param program
* @return
*/
/** The input is hashed two times with SHA-256. */
def opHash256(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.isDefined && program.script.head == OP_HASH256, "Script operation must be OP_HASH256")
executeHashFunction(program, CryptoUtil.doubleSHA256(_ : Seq[Byte]))
}
/**
* The input is hashed using SHA-1.
* @param program
* @return
*/
/** The input is hashed using SHA-1. */
def opSha1(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.isDefined && program.script.head == OP_SHA1, "Script top must be OP_SHA1")
executeHashFunction(program, CryptoUtil.sha1(_ : Seq[Byte]))
@ -72,10 +51,7 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
* 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.
* https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L818
* @param program
* @return
* [[https://github.com/bitcoin/bitcoin/blob/528472111b4965b1a99c4bcf08ac5ec93d87f10f/src/script/interpreter.cpp#L880]]
*/
def opCheckSig(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.isDefined && program.script.head == OP_CHECKSIG, "Script top must be OP_CHECKSIG")
@ -91,8 +67,8 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
} else {
val pubKey = ECPublicKey(executionInProgressScriptProgram.stack.head.bytes)
val signature = ECDigitalSignature(executionInProgressScriptProgram.stack.tail.head.bytes)
if (ScriptFlagUtil.requiresStrictDerEncoding(executionInProgressScriptProgram.flags) &&
val flags = executionInProgressScriptProgram.flags
if (ScriptFlagUtil.requiresStrictDerEncoding(flags) &&
!DERSignatureUtil.isValidSignatureEncoding(signature)) {
//this means all of the signatures must encoded according to BIP66 strict dersig
//https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki
@ -100,47 +76,58 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
logger.error("Since the ScriptVerifyDerSig flag is set the signature being checked must be a strict dersig signature as per BIP 66\n" +
"Sig: " + signature.hex)
ScriptProgram(executionInProgressScriptProgram,ScriptErrorSigDer)
} else if (BitcoinScriptUtil.isValidPubKeyEncoding(pubKey,flags).isDefined) {
logger.error("Pubkey had an invalid encoding")
val err = BitcoinScriptUtil.isValidPubKeyEncoding(pubKey,flags).get
logger.error("PubKey error: " + err)
ScriptProgram(executionInProgressScriptProgram,err)
} else {
val restOfStack = executionInProgressScriptProgram.stack.tail.tail
logger.debug("Program before removing OP_CODESEPARATOR: " + program.originalScript)
val removedOpCodeSeparatorsScript = removeOpCodeSeparator(executionInProgressScriptProgram)
val removedOpCodeSeparatorsScript = BitcoinScriptUtil.removeOpCodeSeparator(executionInProgressScriptProgram)
logger.debug("Program after removing OP_CODESEPARATOR: " + removedOpCodeSeparatorsScript)
val result = TransactionSignatureChecker.checkSignature(executionInProgressScriptProgram.txSignatureComponent,
removedOpCodeSeparatorsScript, pubKey, signature, program.flags)
removedOpCodeSeparatorsScript, pubKey, signature, flags)
logger.debug("signature verification isValid: " + result)
result match {
case SignatureValidationSuccess => ScriptProgram(program,
OP_TRUE :: restOfStack,program.script.tail)
case SignatureValidationFailureNotStrictDerEncoding =>
logger.info("Signature validation failed: " + SignatureValidationFailureNotStrictDerEncoding)
ScriptProgram(program, ScriptErrorSigDer)
case SignatureValidationFailureIncorrectSignatures =>
logger.info("Signature validation failed: " + SignatureValidationFailureIncorrectSignatures)
ScriptProgram(program, OP_FALSE :: restOfStack,program.script.tail)
case SignatureValidationFailureSignatureCount =>
logger.info("Signature validation failed: " + SignatureValidationFailureSignatureCount)
ScriptProgram(program, OP_FALSE :: restOfStack,program.script.tail)
case SignatureValidationFailurePubKeyEncoding =>
logger.info("Signature validation failed: " + SignatureValidationFailurePubKeyEncoding)
//means that a public key was not encoded correctly
ScriptProgram(program,ScriptErrorPubKeyType)
case ScriptValidationFailureHighSValue =>
logger.info("Signature validation failed: " + ScriptValidationFailureHighSValue)
ScriptProgram(program,ScriptErrorSigHighS)
case ScriptValidationFailureHashType =>
logger.info("Signature validation failed: " + ScriptValidationFailureHashType)
ScriptProgram(program,ScriptErrorSigHashType)
OP_TRUE :: restOfStack, program.script.tail)
case err : SignatureValidationError =>
if (ScriptFlagUtil.requireScriptVerifyNullFail(flags) && signature.bytes.nonEmpty) {
ScriptProgram(executionInProgressScriptProgram, ScriptErrorSigNullFail)
} else {
err match {
case SignatureValidationFailureNotStrictDerEncoding =>
logger.info("Signature validation failed: " + SignatureValidationFailureNotStrictDerEncoding)
ScriptProgram(program, ScriptErrorSigDer)
case SignatureValidationFailureIncorrectSignatures =>
logger.info("Signature validation failed: " + SignatureValidationFailureIncorrectSignatures)
ScriptProgram(program, OP_FALSE :: restOfStack,program.script.tail)
case SignatureValidationFailureSignatureCount =>
logger.info("Signature validation failed: " + SignatureValidationFailureSignatureCount)
ScriptProgram(program, OP_FALSE :: restOfStack,program.script.tail)
case SignatureValidationFailurePubKeyEncoding =>
logger.info("Signature validation failed: " + SignatureValidationFailurePubKeyEncoding)
//means that a public key was not encoded correctly
ScriptProgram(program,ScriptErrorPubKeyType)
case ScriptValidationFailureHighSValue =>
logger.info("Signature validation failed: " + ScriptValidationFailureHighSValue)
ScriptProgram(program,ScriptErrorSigHighS)
case ScriptValidationFailureHashType =>
logger.info("Signature validation failed: " + ScriptValidationFailureHashType)
ScriptProgram(program,ScriptErrorSigHashType)
case ScriptValidationFailureWitnessPubKeyType =>
ScriptProgram(program,ScriptErrorWitnessPubKeyType)
}
}
}
}
}
}
}
/**
* Runs OP_CHECKSIG with an OP_VERIFY afterwards
* @param program
* @return
*/
/** Runs OP_CHECKSIG with an OP_VERIFY afterwards */
def opCheckSigVerify(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.isDefined && program.script.head == OP_CHECKSIGVERIFY,
"Script top must be OP_CHECKSIGVERIFY")
@ -162,10 +149,7 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
/**
* All of the signature checking words will only match signatures to the data
* after the most recently-executed OP_CODESEPARATOR.
* @param program
* @return
*/
* after the most recently-executed OP_CODESEPARATOR. */
def opCodeSeparator(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.isDefined && program.script.head == OP_CODESEPARATOR, "Script top must be OP_CODESEPARATOR")
val e = program match {
@ -191,12 +175,11 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
* 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.
* @param program
* @return
*/
def opCheckMultiSig(program : ScriptProgram) : ScriptProgram = {
@tailrec
final def opCheckMultiSig(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.isDefined && program.script.head == OP_CHECKMULTISIG, "Script top must be OP_CHECKMULTISIG")
val flags = program.flags
program match {
case preExecutionScriptProgram : PreExecutionScriptProgram =>
opCheckMultiSig(ScriptProgram.toExecutionInProgress(preExecutionScriptProgram))
@ -212,7 +195,7 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
if (nPossibleSignatures < ScriptNumber.zero) {
logger.error("We cannot have the number of pubkeys in the script be negative")
ScriptProgram(program,ScriptErrorPubKeyCount)
} else if (ScriptFlagUtil.requireMinimalData(program.flags) && !nPossibleSignatures.isShortestEncoding) {
} 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) {
@ -221,7 +204,7 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
} else {
val mRequiredSignatures: ScriptNumber = BitcoinScriptUtil.numRequiredSignaturesOnStack(program)
if (ScriptFlagUtil.requireMinimalData(program.flags) && !mRequiredSignatures.isShortestEncoding) {
if (ScriptFlagUtil.requireMinimalData(flags) && !mRequiredSignatures.isShortestEncoding) {
logger.error("The required signatures val must be the shortest encoding as possible")
return ScriptProgram(executionInProgressScriptProgram, ScriptErrorUnknownError)
}
@ -260,20 +243,21 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
//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)
} else if (ScriptFlagUtil.requireNullDummy(program.flags) &&
!(Seq(Some(OP_0), Some(ScriptNumber.zero)).contains(stackWithoutPubKeysAndSignatures.headOption))) {
} else if (ScriptFlagUtil.requireNullDummy(flags) &&
(stackWithoutPubKeysAndSignatures.headOption.isDefined && 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)
} else {
//remove the last OP_CODESEPARATOR
val removedOpCodeSeparatorsScript = removeOpCodeSeparator(executionInProgressScriptProgram)
val removedOpCodeSeparatorsScript = BitcoinScriptUtil.removeOpCodeSeparator(executionInProgressScriptProgram)
val isValidSignatures: TransactionSignatureCheckerResult =
TransactionSignatureChecker.multiSignatureEvaluator(executionInProgressScriptProgram.txSignatureComponent,
removedOpCodeSeparatorsScript, signatures,
pubKeys, program.flags, mRequiredSignatures.underlying)
pubKeys, flags, mRequiredSignatures.underlying)
//remove the extra op for OP_CHECKMULTISIG from the stack
val restOfStack = stackWithoutPubKeysAndSignatures.tail
isValidSignatures match {
case SignatureValidationSuccess =>
//means that all of the signatures were correctly encoded and
@ -287,9 +271,14 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
//https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki#specification
ScriptProgram(executionInProgressScriptProgram, ScriptErrorSigDer)
case SignatureValidationFailureIncorrectSignatures =>
//this means that signature verification failed, however all signatures were encoded correctly
//just push a ScriptFalse onto the stack
ScriptProgram(executionInProgressScriptProgram, OP_FALSE :: restOfStack, program.script.tail)
val nullFailEnabled = ScriptFlagUtil.requireScriptVerifyNullFail(flags)
if (nullFailEnabled && signatures.exists(_.bytes.nonEmpty)) {
ScriptProgram(executionInProgressScriptProgram,ScriptErrorSigNullFail) }
else {
//this means that signature verification failed, however all signatures were encoded correctly
//just push a ScriptFalse onto the stack
ScriptProgram(executionInProgressScriptProgram, OP_FALSE :: restOfStack, program.script.tail)
}
case SignatureValidationFailureSignatureCount =>
//means that we did not have enough signatures for OP_CHECKMULTISIG
ScriptProgram(executionInProgressScriptProgram, ScriptErrorInvalidStackOperation)
@ -300,6 +289,8 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
ScriptProgram(program, ScriptErrorSigHighS)
case ScriptValidationFailureHashType =>
ScriptProgram(executionInProgressScriptProgram, ScriptErrorSigHashType)
case ScriptValidationFailureWitnessPubKeyType =>
ScriptProgram(executionInProgressScriptProgram,ScriptErrorWitnessPubKeyType)
}
}
}
@ -308,11 +299,7 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
}
/**
* Runs OP_CHECKMULTISIG with an OP_VERIFY afterwards
* @param program
* @return
*/
/** Runs OP_CHECKMULTISIG with an OP_VERIFY afterwards */
def opCheckMultiSigVerify(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.isDefined && program.script.head == OP_CHECKMULTISIGVERIFY, "Script top must be OP_CHECKMULTISIGVERIFY")
if (program.stack.size < 3) {
@ -351,17 +338,4 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
}
}
/**
* Removes the OP_CODESEPARATOR in the original script according to
* the last code separator index in the script
* @param program
* @return
*/
def removeOpCodeSeparator(program : ExecutionInProgressScriptProgram) : Seq[ScriptToken] = {
if (program.lastCodeSeparator.isDefined) {
program.originalScript.slice(program.lastCodeSeparator.get+1, program.originalScript.size)
} else program.originalScript
}
}

View file

@ -32,25 +32,13 @@ object HashType extends Factory[HashType] {
require(isONLY_ANYONE_CANPAY(num))
SIGHASH_ANYONECANPAY(num)
}
}
else {
SIGHASH_ALL(num)
}
} else SIGHASH_ALL(num)
}
/**
* Returns a hashtype's default byte value
* @param hashType
* @return
*/
def byte (hashType : HashType) : Byte = hashType match {
case _ : SIGHASH_ALL => SIGHASH_ALL.defaultValue.num.underlying.toByte
case _ : SIGHASH_NONE => SIGHASH_NONE.defaultValue.num.underlying.toByte
case _ : SIGHASH_SINGLE => SIGHASH_SINGLE.defaultValue.num.underlying.toByte
case _ : SIGHASH_ANYONECANPAY => SIGHASH_ANYONECANPAY.defaultValue.num.underlying.toByte
case _ : SIGHASH_ALL_ANYONECANPAY => SIGHASH_ALL_ANYONECANPAY.defaultValue.num.underlying.toByte
case _ : SIGHASH_NONE_ANYONECANPAY => SIGHASH_NONE_ANYONECANPAY.defaultValue.num.underlying.toByte
case _ : SIGHASH_SINGLE_ANYONECANPAY => SIGHASH_SINGLE_ANYONECANPAY.defaultValue.num.underlying.toByte
/** Returns a hashtype's default byte value */
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)
@ -66,107 +54,117 @@ object HashType extends Factory[HashType] {
else false
}
def isONLY_ANYONE_CANPAY(num : Int32) : Boolean = {
!(HashType.isSIGHASH_ALL_ANYONECANPAY(num) || HashType.isSIGHASH_NONE_ANYONECANPAY(num) || HashType.isSIGHASH_SINGLE_ANYONECANPAY(num))
!(HashType.isSIGHASH_ALL_ANYONECANPAY(num) || HashType.isSIGHASH_NONE_ANYONECANPAY(num) ||
HashType.isSIGHASH_SINGLE_ANYONECANPAY(num))
}
val hashTypes = Seq(SIGHASH_ALL.defaultValue, SIGHASH_NONE, SIGHASH_SINGLE, SIGHASH_ANYONECANPAY,
SIGHASH_NONE_ANYONECANPAY, SIGHASH_ALL_ANYONECANPAY, SIGHASH_SINGLE_ANYONECANPAY)
/** 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
}
lazy val hashTypes = Seq(sigHashAll, sigHashNone, sigHashSingle, sigHashAnyoneCanPay,
sigHashNoneAnyoneCanPay, sigHashAllAnyoneCanPay, sigHashSingleAnyoneCanPay)
def apply(num : Int32) : HashType = fromNumber(num)
def apply(int : Int) : HashType = {
val num : Int32 = Int32(int)
fromNumber(num)
}
def apply (byte : Byte) : HashType = fromByte(byte)
def apply(int : Int) : HashType = HashType(Int32(int))
def apply(byte : Byte) : HashType = fromByte(byte)
/** The default byte used to represent [[SIGHASH_ALL]] */
val sigHashAllByte = 1.toByte
/** 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
* */
val sigHashAnyoneCanPayNum = Int32(0x80)
val sigHashAnyoneCanPayByte = 0x80.toByte
val sigHashAnyoneCanPay: SIGHASH_ANYONECANPAY = SIGHASH_ANYONECANPAY(sigHashAnyoneCanPayNum)
/** The default byte for [[SIGHASH_NONE]] */
val sigHashNoneByte: Byte = 2.toByte
val sigHashNone: SIGHASH_NONE = SIGHASH_NONE(Int32(sigHashNoneByte))
/** The default byte for [[SIGHASH_SINGLE]] */
val sigHashSingleByte: Byte = 3.toByte
val sigHashSingle: SIGHASH_SINGLE = SIGHASH_SINGLE(Int32(sigHashSingleByte))
val sigHashAllAnyoneCanPayByte = (HashType.sigHashAllByte | HashType.sigHashAnyoneCanPayByte).toByte
val sigHashAllAnyoneCanPayNum = (Int32(sigHashAllByte) | sigHashAnyoneCanPayNum)
val sigHashAllAnyoneCanPay = SIGHASH_ALL_ANYONECANPAY(sigHashAllAnyoneCanPayNum)
val sigHashNoneAnyoneCanPayByte = (HashType.sigHashNoneByte | HashType.sigHashAnyoneCanPayByte).toByte
val sigHashNoneAnyoneCanPayNum = (Int32(sigHashNoneByte) | sigHashAnyoneCanPayNum)
val sigHashNoneAnyoneCanPay = SIGHASH_NONE_ANYONECANPAY(sigHashNoneAnyoneCanPayNum)
val sigHashSingleAnyoneCanPayByte = (HashType.sigHashSingleByte | HashType.sigHashAnyoneCanPayByte).toByte
val sigHashSingleAnyoneCanPayNum = (Int32(sigHashSingleByte) | sigHashAnyoneCanPayNum)
val sigHashSingleAnyoneCanPay = SIGHASH_SINGLE_ANYONECANPAY(sigHashSingleAnyoneCanPayNum)
}
/**
* 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))
object SIGHASH_ALL extends Factory[SIGHASH_ALL] {
private case class SIGHASH_ALLImpl(num: Int32, byte : Byte = 0x01.toByte) extends SIGHASH_ALL {
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))
}
def byte : Byte = HashType.byte(defaultValue)
def defaultValue : SIGHASH_ALL = SIGHASH_ALL(Int32.one)
def apply(num : Int32) : SIGHASH_ALL = SIGHASH_ALLImpl(num)
override def fromBytes (bytes : Seq[Byte]) : SIGHASH_ALL = SIGHASH_ALL(Int32(bytes))
override def byte = HashType.sigHashAllByte
}
object SIGHASH_ALL {
def apply(byte: Byte): SIGHASH_ALL = SIGHASH_ALL(Int32(byte))
}
object SIGHASH_NONE extends Factory[SIGHASH_NONE] {
private case class SIGHASH_NONEImpl(num : Int32, byte : Byte = 0x02.toByte) extends SIGHASH_NONE {
require(HashType.isSIGHASH_NONE(num), "The bitwise AND of 'num & 0x1f' must be 2 for a hashtype of SIGHASH_NONE. " +
"Your input was: " + num + ", which is of hashType: " + HashType(num))
}
def byte : Byte = HashType.byte(defaultValue)
def defaultValue : SIGHASH_NONE = SIGHASH_NONE(Int32(2))
def apply(num : Int32) : SIGHASH_NONE = SIGHASH_NONEImpl(num)
override def fromBytes (bytes : Seq[Byte]) : SIGHASH_NONE = SIGHASH_NONE(Int32(bytes))
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
}
object SIGHASH_SINGLE extends Factory[SIGHASH_SINGLE] {
private case class SIGHASH_SINGLEImpl(num : Int32, byte : Byte = 0x03.toByte) extends SIGHASH_SINGLE {
require(HashType.isSIGHASH_SINGLE(num), "The bitwise AND of 'num & 0x1f' must be 3 for a hashtype of SIGHASH_SINGLE." +
" Your input was: " + num + ", which is of hashType: " + HashType(num))
}
def byte : Byte = HashType.byte(defaultValue)
def defaultValue : SIGHASH_SINGLE = SIGHASH_SINGLE(Int32(3))
def apply(num : Int32) : SIGHASH_SINGLE = SIGHASH_SINGLEImpl(num)
override def fromBytes (bytes : Seq[Byte]) : SIGHASH_SINGLE = SIGHASH_SINGLE(Int32(bytes))
case class SIGHASH_SINGLE(override val num: Int32) extends HashType {
require(HashType.isSIGHASH_SINGLE(num), "The given number is not a SIGHASH_SINGLE number: " + num)
override def byte: Byte = HashType.sigHashSingleByte
}
object SIGHASH_ANYONECANPAY extends Factory[SIGHASH_ANYONECANPAY] {
private case class SIGHASH_ANYONECANPAYImpl(num : Int32, byte : Byte = 0x80.toByte) extends SIGHASH_ANYONECANPAY {
require(HashType.isSIGHASH_ANYONECANPAY(num) && HashType.isONLY_ANYONE_CANPAY(num), "The bitwise AND of 'num & 0x80' must be 0x80 (or 128) for a hashtype of " +
"SIGHASH_ANYONECANPAY. Your input was: " + num + ", which is of hashType: " + HashType(num))
}
def byte : Byte = HashType.byte(defaultValue)
def defaultValue : SIGHASH_ANYONECANPAY = SIGHASH_ANYONECANPAY(Int32(0x80))
def apply(num : Int32) : SIGHASH_ANYONECANPAY = SIGHASH_ANYONECANPAYImpl(num)
override def fromBytes (bytes : Seq[Byte]) : SIGHASH_ANYONECANPAY = SIGHASH_ANYONECANPAY(Int32(bytes))
case class SIGHASH_ANYONECANPAY(override val num: Int32) extends HashType {
require(HashType.isSIGHASH_ANYONECANPAY(num), "The given number was not a SIGHASH_ANYONECANPAY number: " + num)
override def byte: Byte = HashType.sigHashAnyoneCanPayByte
}
object SIGHASH_ALL_ANYONECANPAY extends Factory[SIGHASH_ALL_ANYONECANPAY] {
private case class SIGHASH_ALL_ANYONECANPAYImpl(num : Int32, byte : Byte = 0x81.toByte) extends SIGHASH_ALL_ANYONECANPAY {
require(HashType.isSIGHASH_ALL_ANYONECANPAY(num), "SIGHASH_ALL_ANYONECANPAY must be of both hashTypes: SIGHASH_ALL, and " +
"SIGHASH_ANYONECANPAY. Your input was: " + num + ", which is of hashType: " + HashType(num))
}
def byte : Byte = HashType.byte(defaultValue)
def defaultValue : SIGHASH_ALL_ANYONECANPAY = SIGHASH_ALL_ANYONECANPAY(SIGHASH_ANYONECANPAY.defaultValue.num | SIGHASH_ALL.defaultValue.num)
def apply(num : Int32) : SIGHASH_ALL_ANYONECANPAY = SIGHASH_ALL_ANYONECANPAYImpl(num)
override def fromBytes (bytes : Seq[Byte]) : SIGHASH_ALL_ANYONECANPAY = SIGHASH_ALL_ANYONECANPAY(Int32(bytes))
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
}
object SIGHASH_NONE_ANYONECANPAY extends Factory[SIGHASH_NONE_ANYONECANPAY] {
private case class SIGHASH_NONE_ANYONECANPAYImpl(num : Int32, byte : Byte = 0x82.toByte) extends SIGHASH_NONE_ANYONECANPAY {
require(HashType.isSIGHASH_NONE_ANYONECANPAY(num), "SIGHASH_NONE_ANYONECANPAY must be of both hashTypes: SIGHASH_NONE, and " +
"SIGHASH_ANYONECANPAY. Your input was: " + num + ", which is of hashType: " + HashType(num))
}
def byte : Byte = HashType.byte(defaultValue)
def defaultValue : SIGHASH_NONE_ANYONECANPAY = SIGHASH_NONE_ANYONECANPAY(SIGHASH_ANYONECANPAY.defaultValue.num | SIGHASH_NONE.defaultValue.num)
def apply(num : Int32) : SIGHASH_NONE_ANYONECANPAY = SIGHASH_NONE_ANYONECANPAYImpl(num)
override def fromBytes (bytes : Seq[Byte]) : SIGHASH_NONE_ANYONECANPAY = SIGHASH_NONE_ANYONECANPAY(Int32(bytes))
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
}
object SIGHASH_SINGLE_ANYONECANPAY extends Factory[SIGHASH_SINGLE_ANYONECANPAY] {
private case class SIGHASH_SINGLE_ANYONECANPAYImpl(num : Int32, byte : Byte = 0x83.toByte) extends SIGHASH_SINGLE_ANYONECANPAY {
require(HashType.isSIGHASH_SINGLE_ANYONECANPAY(num), "SIGHASH_SINGLE_ANYONECANPAY must be of both hashTypes: SIGHASH_SINGLE, " +
"and SIGHASH_ANYONECANPAY. Your input was: " + num + ", which is of hashType: " + HashType(num))
}
def byte : Byte = HashType.byte(defaultValue)
def defaultValue : SIGHASH_SINGLE_ANYONECANPAY = SIGHASH_SINGLE_ANYONECANPAY(SIGHASH_ANYONECANPAY.defaultValue.num | SIGHASH_SINGLE.defaultValue.num)
def apply(num : Int32) : SIGHASH_SINGLE_ANYONECANPAY = SIGHASH_SINGLE_ANYONECANPAYImpl(num)
override def fromBytes (bytes : Seq[Byte]) : SIGHASH_SINGLE_ANYONECANPAY = SIGHASH_SINGLE_ANYONECANPAY(Int32(bytes))
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
}
sealed trait SIGHASH_ALL extends HashType
sealed trait SIGHASH_NONE extends HashType
sealed trait SIGHASH_SINGLE extends HashType
sealed trait SIGHASH_ANYONECANPAY extends HashType
sealed trait SIGHASH_ALL_ANYONECANPAY extends HashType
sealed trait SIGHASH_NONE_ANYONECANPAY extends HashType
sealed trait SIGHASH_SINGLE_ANYONECANPAY extends HashType

View file

@ -15,7 +15,9 @@ trait ScriptFlagFactory {
private def flags = Seq(ScriptVerifyNone, ScriptVerifyP2SH, ScriptVerifyStrictEnc,
ScriptVerifyDerSig, ScriptVerifyLowS, ScriptVerifySigPushOnly, ScriptVerifyMinimalData,
ScriptVerifyNullDummy, ScriptVerifyDiscourageUpgradableNOPs, ScriptVerifyCleanStack,
ScriptVerifyCheckLocktimeVerify, ScriptVerifyCheckSequenceVerify)
ScriptVerifyCheckLocktimeVerify, ScriptVerifyCheckSequenceVerify,ScriptVerifyWitness,
ScriptVerifyDiscourageUpgradableWitnessProgram, ScriptVerifyMinimalIf,ScriptVerifyNullFail,
ScriptVerifyWitnessPubKeyType)
/**
* Takes in a string and tries to match it with a script flag
@ -34,7 +36,7 @@ trait ScriptFlagFactory {
* @return the sequence of script flags
*/
def fromList(list : Seq[String]) : Seq[ScriptFlag] = {
list.map(fromString(_)).flatten
list.flatMap(fromString(_))
}
/**
@ -50,7 +52,7 @@ trait ScriptFlagFactory {
* Empty script flag
* @return
*/
def empty : Seq[ScriptFlag] = Seq()
def empty : Seq[ScriptFlag] = Nil
}
object ScriptFlagFactory extends ScriptFlagFactory

View file

@ -93,6 +93,18 @@ trait ScriptFlagUtil {
*/
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)
def discourageUpgradableWitnessProgram(flags: Seq[ScriptFlag]): Boolean = flags.contains(ScriptVerifyDiscourageUpgradableWitnessProgram)
def requireScriptVerifyNullFail(flags: Seq[ScriptFlag]): Boolean = flags.contains(ScriptVerifyNullFail)
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 */
def minimalIfEnabled(flags: Seq[ScriptFlag]): Boolean = flags.contains(ScriptVerifyMinimalIf)
}

View file

@ -136,4 +136,34 @@ case object ScriptVerifyCheckSequenceVerify extends ScriptFlag {
override def name = "CHECKSEQUENCEVERIFY"
}
// Support segregated witness
case object ScriptVerifyWitness extends ScriptFlag {
override def flag = 1 << 11
override def name = "WITNESS"
}
// Making v1-v16 witness program non-standard
case object ScriptVerifyDiscourageUpgradableWitnessProgram extends ScriptFlag {
override def flag = 1 << 12
override def name = "DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"
}
// Segwit script only: Require the argument of OP_IF/NOTIF to be exactly 0x01 or empty vector
case object ScriptVerifyMinimalIf extends ScriptFlag {
override def flag = 1 << 13
override def name = "MINIMALIF"
}
// Signature(s) must be empty vector if an CHECK(MULTI)SIG operation failed
case object ScriptVerifyNullFail extends ScriptFlag {
override def flag = 1 << 14
override def name = "NULLFAIL"
}
// Public keys in segregated witness scripts must be compressed
case object ScriptVerifyWitnessPubKeyType extends ScriptFlag {
override def flag = 1 << 15
override def name = "WITNESS_PUBKEYTYPE"
}

View file

@ -1,8 +1,10 @@
package org.bitcoins.core.script.interpreter
import org.bitcoins.core.script.constant.ScriptToken
import org.bitcoins.core.consensus.Consensus
import org.bitcoins.core.crypto.{BaseTransactionSignatureComponent, WitnessV0TransactionSignatureComponent}
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.{EmptyTransactionOutPoint, Transaction}
import org.bitcoins.core.script._
@ -17,7 +19,7 @@ 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, BitcoinScriptUtil}
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil, BitcoinScriptUtil}
import scala.annotation.tailrec
@ -37,51 +39,54 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
/**
* Runs an entire script though our script programming language and
* returns true or false depending on if the script was valid
*
* @param program the program to be interpreted
* @return
* returns a [[ScriptResult]] indicating if the script was valid, or if not what error it encountered
*/
def run(program : PreExecutionScriptProgram) : ScriptResult = {
val scriptSig = program.txSignatureComponent.scriptSignature
val scriptPubKey = program.txSignatureComponent.scriptPubKey
val programBeingExecuted = ScriptProgram.toExecutionInProgress(program)
val executedProgram : ExecutedScriptProgram = if (ScriptFlagUtil.requirePushOnly(program.flags)
val flags = program.flags
val p2shEnabled = ScriptFlagUtil.p2shEnabled(flags)
val segwitEnabled = ScriptFlagUtil.segWitEnabled(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(programBeingExecuted,ScriptErrorSigPushOnly)
} else if (scriptSig.isInstanceOf[P2SHScriptSignature] && ScriptFlagUtil.p2shEnabled(program.flags) &&
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")
ScriptProgram(programBeingExecuted,ScriptErrorSigPushOnly)
logger.error("P2SH scriptSigs are required to be push only by definition - see BIP16, got: " + scriptSig.asm)
ScriptProgram(program,ScriptErrorSigPushOnly)
} else {
val scriptSigExecutedProgram = loop(program,0)
scriptPubKey match {
case p2shScriptPubKey : P2SHScriptPubKey if (ScriptFlagUtil.p2shEnabled(program.flags)) =>
executeP2shScript(scriptSigExecutedProgram, programBeingExecuted, p2shScriptPubKey)
case _ : MultiSignatureScriptPubKey | _ : P2SHScriptPubKey | _ : P2PKHScriptPubKey |
_ : P2PKScriptPubKey | _ : CLTVScriptPubKey | _ : CSVScriptPubKey | _ : NonStandardScriptPubKey | EmptyScriptPubKey =>
logger.info("Stack state after scriptSig execution: " + scriptSigExecutedProgram.stack)
if (!scriptSigExecutedProgram.error.isDefined) {
logger.debug("We do not check a redeemScript against a non p2sh scriptSig")
//now run the scriptPubKey script through the interpreter with the scriptSig as the stack arguments
val scriptPubKeyProgram = ScriptProgram(scriptSigExecutedProgram.txSignatureComponent,
scriptSigExecutedProgram.stack,scriptSigExecutedProgram.txSignatureComponent.scriptPubKey.asm)
require(scriptPubKeyProgram.script == scriptSigExecutedProgram.txSignatureComponent.scriptPubKey.asm)
val scriptPubKeyExecutedProgram : ExecutedScriptProgram = loop(scriptPubKeyProgram,0)
logger.info("Stack state after scriptPubKey execution: " + scriptPubKeyExecutedProgram.stack)
//if the program is valid, return if the stack top is true
//else the program is false since something illegal happened during script evaluation
val t = scriptSigExecutedProgram.txSignatureComponent
val scriptPubKeyProgram = ScriptProgram(t, scriptSigExecutedProgram.stack, t.scriptPubKey.asm,
t.scriptPubKey.asm)
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 =>
if (segwitEnabled) executeSegWitScript(scriptPubKeyExecutedProgram,witness)
else scriptPubKeyExecutedProgram
case p2sh : P2SHScriptPubKey =>
if (p2shEnabled) executeP2shScript(scriptSigExecutedProgram, program, p2sh)
else scriptPubKeyExecutedProgram
case _ @ (_ : P2PKHScriptPubKey | _: P2PKScriptPubKey | _: MultiSignatureScriptPubKey | _: CSVScriptPubKey |
_ : CLTVScriptPubKey | _ : NonStandardScriptPubKey | EmptyScriptPubKey) =>
scriptPubKeyExecutedProgram
} else scriptSigExecutedProgram
}
}
}
logger.debug("Executed Script Program: " + executedProgram)
if (executedProgram.error.isDefined) executedProgram.error.get
else if (executedProgram.stackTopIsTrue && executedProgram.flags.contains(ScriptVerifyCleanStack)) {
else if (hasUnexpectedWitness(program)) {
//note: the 'program' value we pass above is intetional, we need to check the original program
//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)) {
//require that the stack after execution has exactly one element on it
executedProgram.stack.size == 1 match {
case true => ScriptOk
@ -97,45 +102,127 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
* 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 scriptSigExecutedProgram the program with the script signature pushed onto the stack
*
* @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(scriptSigExecutedProgram : ExecutedScriptProgram, originalProgram : ScriptProgram, p2shScriptPubKey : P2SHScriptPubKey) : ExecutedScriptProgram = {
val originalScriptSigAsm : Seq[ScriptToken] = scriptSigExecutedProgram.txSignatureComponent.scriptSignature.asm
//need to check if the scriptSig is push only as required by bitcoin core
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L1268
if (!BitcoinScriptUtil.isPushOnly(originalScriptSigAsm)) {
ScriptProgram(originalProgram,ScriptErrorSigPushOnly)
} else if (scriptSigExecutedProgram.error.isDefined) {
scriptSigExecutedProgram
} else {
val hashCheckProgram = ScriptProgram(originalProgram, scriptSigExecutedProgram.stack, p2shScriptPubKey.asm)
val hashesMatchProgram = loop(hashCheckProgram,0)
hashesMatchProgram.stackTopIsTrue match {
case true =>
logger.info("Hashes matched between the p2shScriptSignature & the p2shScriptPubKey")
//we need to run the deserialized redeemScript & the scriptSignature without the serialized redeemScript
val stack = scriptSigExecutedProgram.stack
private def executeP2shScript(scriptPubKeyExecutedProgram : ExecutedScriptProgram, originalProgram : ScriptProgram, p2shScriptPubKey : P2SHScriptPubKey) : ExecutedScriptProgram = {
logger.debug("P2sh stack: " + stack)
val redeemScript = ScriptPubKey(stack.head.bytes)
val p2shRedeemScriptProgram = ScriptProgram(scriptSigExecutedProgram.txSignatureComponent,stack.tail,
redeemScript.asm)
if (ScriptFlagUtil.requirePushOnly(p2shRedeemScriptProgram.flags) && !BitcoinScriptUtil.isPushOnly(redeemScript.asm)) {
logger.error("p2sh redeem script must be push only operations whe SIGPUSHONLY flag is set")
ScriptProgram(p2shRedeemScriptProgram,ScriptErrorSigPushOnly)
} else loop(p2shRedeemScriptProgram,0)
/** Helper function to actually run a p2sh script */
def run(p: ExecutedScriptProgram, stack : Seq[ScriptToken], s: ScriptPubKey): ExecutedScriptProgram = {
logger.error("Running p2sh script: " + stack)
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)
}
val scriptSig = scriptPubKeyExecutedProgram.txSignatureComponent.scriptSignature
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)
} else if (scriptPubKeyExecutedProgram.error.isDefined) {
scriptPubKeyExecutedProgram
} else {
scriptPubKeyExecutedProgram.stackTopIsTrue match {
case true =>
logger.debug("Hashes matched between the p2shScriptSignature & the p2shScriptPubKey")
//we need to run the deserialized redeemScript & the scriptSignature without the serialized redeemScript
val stack = scriptPubKeyExecutedProgram.stack
val redeemScriptBytes = stack.head.bytes
val c = CompactSizeUInt.calculateCompactSizeUInt(redeemScriptBytes)
val redeemScript = ScriptPubKey(c.bytes ++ redeemScriptBytes)
redeemScript match {
case w : WitnessScriptPubKey =>
val pushOp = BitcoinScriptUtil.calculatePushOp(redeemScriptBytes)
val expectedScriptBytes = pushOp.flatMap(_.bytes) ++ redeemScriptBytes
val flags = scriptPubKeyExecutedProgram.flags
val segwitEnabled = ScriptFlagUtil.segWitEnabled(flags)
if (segwitEnabled && (scriptSig.asmBytes == expectedScriptBytes)) {
// The scriptSig must be _exactly_ a single push of the redeemScript. Otherwise we
// reintroduce malleability.
logger.info("redeem script was witness script pubkey, segwit was enabled, scriptSig was single push of redeemScript")
executeSegWitScript(scriptPubKeyExecutedProgram,w)
} else if (segwitEnabled && (scriptSig.asmBytes != expectedScriptBytes)) {
logger.error("Segwit was enabled, but p2sh redeem script was malleated")
logger.error("ScriptSig bytes: " + scriptSig.hex)
logger.error("expected scriptsig bytes: " + BitcoinSUtil.encodeHex(expectedScriptBytes))
ScriptProgram(scriptPubKeyExecutedProgram, ScriptErrorWitnessMalleatedP2SH)
} 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)
}
case s @ (_ : P2SHScriptPubKey | _ : P2PKHScriptPubKey | _ : P2PKScriptPubKey | _ : MultiSignatureScriptPubKey |
_ : CLTVScriptPubKey | _ : CSVScriptPubKey | _: NonStandardScriptPubKey | EmptyScriptPubKey) =>
logger.debug("redeemScript: " + s.asm)
run(scriptPubKeyExecutedProgram,stack,s)
}
case false =>
logger.warn("P2SH scriptPubKey hash did not match the hash for the serialized redeemScript")
hashesMatchProgram
scriptPubKeyExecutedProgram
}
}
}
/** 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): ExecutedScriptProgram = {
scriptPubKeyExecutedProgram.txSignatureComponent match {
case b : BaseTransactionSignatureComponent =>
logger.error("Cannot verify witness program with a BaseTransactionSignatureComponent")
ScriptProgram(scriptPubKeyExecutedProgram,ScriptErrorWitnessProgramWitnessEmpty)
case w : WitnessV0TransactionSignatureComponent =>
val scriptSig = scriptPubKeyExecutedProgram.txSignatureComponent.scriptSignature
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.asm.nonEmpty && !w.scriptPubKey.isInstanceOf[P2SHScriptPubKey]) ScriptProgram(scriptPubKeyExecutedProgram,ScriptErrorWitnessMalleated)
else verifyWitnessProgram(witnessVersion, witness, witnessProgram, w)
}
}
/** 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],
witnessTxSigComponent: WitnessV0TransactionSignatureComponent): ExecutedScriptProgram = witnessVersion match {
case WitnessVersion0 =>
val either: Either[(Seq[ScriptToken], ScriptPubKey),ScriptError] = witnessVersion.rebuild(scriptWitness, witnessProgram)
either match {
case Left((stack,scriptPubKey)) =>
val w = witnessTxSigComponent
val newProgram = ScriptProgram(w.transaction,scriptPubKey,w.inputIndex,stack,scriptPubKey.asm, scriptPubKey.asm,Nil,
w.flags,w.sigVersion,w.amount)
val evaluated = loop(newProgram,0)
logger.info("Stack after evaluating witness: " + evaluated.stack)
if (evaluated.error.isDefined) evaluated
else if (evaluated.stack.size != 1 || evaluated.stackTopIsFalse) ScriptProgram(evaluated,ScriptErrorEvalFalse)
else evaluated
case Right(err) =>
val program = ScriptProgram.toExecutionInProgress(ScriptProgram(witnessTxSigComponent))
ScriptProgram(program,err)
}
case UnassignedWitness =>
logger.warn("Unassigned witness inside of witness script pubkey")
val program = ScriptProgram(witnessTxSigComponent)
ScriptProgram(program,UnassignedWitness.rebuild(scriptWitness, witnessProgram).right.get)
}
/**
* 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
*/
@ -143,7 +230,6 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
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)
@ -160,31 +246,29 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
program match {
case p : PreExecutionScriptProgram => loop(ScriptProgram.toExecutionInProgress(p,Some(p.stack)),opCount)
case p : ExecutedScriptProgram =>
//reset opCount variable to zero since we may need to count the ops
//in the scriptPubKey - we don't want the op count of the scriptSig
//to count towards the scriptPubKey op count
logger.info("Final op count: " + opCount)
p
val countedOps = program.originalScript.map(BitcoinScriptUtil.countsTowardsScriptOpLimit(_)).count(_ == true)
logger.info("Counted ops: " + countedOps)
if (countedOps > maxScriptOps && p.error.isEmpty) {
loop(ScriptProgram(p,ScriptErrorOpCount),opCount)
} else p
case p : ExecutionInProgressScriptProgram =>
//increment the op count
/* if (p.script.headOption.isDefined &&
BitcoinScriptUtil.countsTowardsScriptOpLimit(p.script.head)) opCount = opCount + 1*/
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)).isEmpty =>
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)
//disabled splice operation
case _ if !p.script.intersect(Seq(OP_CAT, OP_SUBSTR, OP_LEFT, OP_RIGHT)).isEmpty =>
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)
//disabled bitwise operations
case _ if !p.script.intersect(Seq(OP_INVERT, OP_AND, OP_OR, OP_XOR)).isEmpty =>
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)
//disabled arithmetic operations
case _ if !p.script.intersect(Seq(OP_MUL, OP_2MUL, OP_DIV, OP_2DIV, OP_MOD, OP_LSHIFT, OP_RSHIFT)).isEmpty =>
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)
//program cannot contain a push operation > 520 bytes
@ -284,7 +368,6 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
val newOpCount = calcOpCount(opCount,OP_CHECKMULTISIG) + BitcoinScriptUtil.numPossibleSignaturesOnStack(program).toInt
loop(newProgram,newOpCount)
}
case OP_CHECKMULTISIGVERIFY :: t =>
opCheckMultiSigVerify(p) match {
case newProgram : ExecutedScriptProgram =>
@ -374,8 +457,8 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
val noDuplicateInputs = prevOutputTxIds.distinct.size == prevOutputTxIds.size
val isValidScriptSigForCoinbaseTx = transaction.isCoinbase match {
case true => transaction.inputs.head.scriptSignature.size >= 2 &&
transaction.inputs.head.scriptSignature.size <= 100
case true => transaction.inputs.head.scriptSignature.asmBytes.size >= 2 &&
transaction.inputs.head.scriptSignature.asmBytes.size <= 100
case false =>
//since this is not a coinbase tx we cannot have any empty previous outs inside of inputs
!transaction.inputs.exists(_.previousOutput == EmptyTransactionOutPoint)
@ -385,27 +468,41 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
}
/**
* Determines if the given currency unit is within the valid range for the system
*
* @param currencyUnit
* @return
*/
/** Determines if the given currency unit is within the valid range for the system */
def validMoneyRange(currencyUnit : CurrencyUnit) : Boolean = {
currencyUnit >= CurrencyUnits.zero && currencyUnit <= Consensus.maxMoney
}
/**
* Calculates the new op count after the execution of the given [[ScriptToken]]
* @param oldOpCount
* @param token
* @return
*/
/** 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
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 = {
val txSigComponent = program.txSignatureComponent
txSigComponent match {
case b : BaseTransactionSignatureComponent =>
//base transactions never have witnesses
false
case w : WitnessV0TransactionSignatureComponent =>
val witnessedUsed = w.scriptPubKey match {
case _ : WitnessScriptPubKey => true
case _ : P2SHScriptPubKey => txSigComponent.scriptSignature match {
case p2shScriptSig: P2SHScriptSignature =>
p2shScriptSig.redeemScript.isInstanceOf[WitnessScriptPubKey]
case _ : CLTVScriptSignature | _ : CSVScriptSignature| _ : MultiSignatureScriptSignature| _ : NonStandardScriptSignature |
_ : P2PKScriptSignature| _ : P2PKHScriptSignature | EmptyScriptSignature => false
}
case _ : CLTVScriptPubKey | _ : CSVScriptPubKey | _ : MultiSignatureScriptPubKey | _ : NonStandardScriptPubKey |
_ : P2PKScriptPubKey | _ : P2PKHScriptPubKey | EmptyScriptPubKey => false
}
!witnessedUsed
}
}
}
object ScriptInterpreter extends ScriptInterpreter

View file

@ -25,9 +25,6 @@ trait LockTimeInterpreter extends BitcoinSLogger {
* or vice versa; or
* 4. the input's nSequence field is equal to 0xffffffff.
* The precise semantics are described in BIP 0065
*
* @param program
* @return
*/
@tailrec
final def opCheckLockTimeVerify(program : ScriptProgram) : ScriptProgram = {
@ -81,9 +78,6 @@ trait LockTimeInterpreter extends BitcoinSLogger {
* 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
*
* @param program
* @return
*/
@tailrec
final def opCheckSequenceVerify(program : ScriptProgram) : ScriptProgram = {

View file

@ -7,8 +7,8 @@ sealed trait ScriptResult {
}
/**
* Created by chris on 4/18/16.
*/
* [[https://github.com/bitcoin/bitcoin/blob/master/src/test/script_tests.cpp#L61]]
*/
sealed trait ScriptError extends ScriptResult
//SCRIPT_ERR_OK = 0,
@ -177,7 +177,6 @@ case object ScriptErrorCleanStack extends ScriptError {
override def description : String = "CLEANSTACK"
}
/* softfork safeness */
//SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS,
case object ScriptErrorDiscourageUpgradableNOPs extends ScriptError {
@ -189,6 +188,55 @@ case object ScriptErrorCount extends ScriptError {
override def description : String = "ERROR_COUNT"
}
//SCRIPT_ERR_MINIMALIF
case object ScriptErrorMinimalIf extends ScriptError {
override def description = "MINIMALIF"
}
//SCRIPT_ERR_SIG_NULLFAIL
case object ScriptErrorSigNullFail extends ScriptError {
override def description = "NULLFAIL"
}
//SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM
case object ScriptErrorDiscourageUpgradeableWitnessProgram extends ScriptError {
override def description = "DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"
}
//SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH
case object ScriptErrorWitnessProgramWrongLength extends ScriptError {
override def description = "WITNESS_PROGRAM_WRONG_LENGTH"
}
//SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY
case object ScriptErrorWitnessProgramWitnessEmpty extends ScriptError {
override def description = "WITNESS_PROGRAM_WITNESS_EMPTY"
}
//SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH
case object ScriptErrorWitnessProgramMisMatch extends ScriptError {
override def description = "WITNESS_PROGRAM_MISMATCH"
}
//SCRIPT_ERR_WITNESS_MALLEATED
case object ScriptErrorWitnessMalleated extends ScriptError {
override def description = "WITNESS_MALLEATED"
}
//SCRIPT_ERR_WITNESS_MALLEATED_P2SH
case object ScriptErrorWitnessMalleatedP2SH extends ScriptError {
override def description = "WITNESS_MALLEATED_P2SH"
}
//SCRIPT_ERR_WITNESS_UNEXPECTED
case object ScriptErrorWitnessUnexpected extends ScriptError {
override def description = "WITNESS_UNEXPECTED"
}
//SCRIPT_ERR_WITNESS_PUBKEYTYPE
case object ScriptErrorWitnessPubKeyType extends ScriptError {
override def description = "WITNESS_PUBKEYTYPE"
}
/**
@ -202,6 +250,8 @@ object ScriptResult {
ScriptErrorInvalidAltStackOperation, ScriptErrorUnbalancedConditional, ScriptErrorNegativeLockTime,
ScriptErrorUnsatisfiedLocktime, ScriptErrorSigHashType, ScriptErrorSigDer, ScriptErrorMinimalData, ScriptErrorSigPushOnly,
ScriptErrorSigHighS, ScriptErrorSigNullDummy,ScriptErrorPubKeyType, ScriptErrorCleanStack, ScriptErrorDiscourageUpgradableNOPs,
ScriptErrorCount)
ScriptErrorCount, ScriptErrorMinimalIf, ScriptErrorSigNullFail,ScriptErrorDiscourageUpgradeableWitnessProgram, ScriptErrorWitnessProgramWrongLength,
ScriptErrorWitnessProgramWitnessEmpty,ScriptErrorWitnessProgramMisMatch, ScriptErrorWitnessMalleated,
ScriptErrorWitnessMalleatedP2SH,ScriptErrorWitnessUnexpected, ScriptErrorWitnessPubKeyType )
def apply(str : String) : ScriptResult = results.filter(_.description == str).head
}

View file

@ -1,11 +1,9 @@
package org.bitcoins.core.serializers.blockchain
import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.protocol.blockchain.{BlockHeader, Block}
import org.bitcoins.core.protocol.blockchain.{Block, BlockHeader}
import org.bitcoins.core.protocol.transaction.Transaction
import org.bitcoins.core.serializers.RawBitcoinSerializer
import org.bitcoins.core.serializers.transaction.RawTransactionParser
import org.bitcoins.core.util.BitcoinSUtil
import scala.annotation.tailrec
@ -49,7 +47,7 @@ trait RawBlockSerializer extends RawBitcoinSerializer[Block] {
if (remainingTxs <= 0) {
(accum.reverse, remainingBytes)
} else {
val transaction = RawTransactionParser.read(remainingBytes)
val transaction = Transaction(remainingBytes)
val newRemainingBytes = remainingBytes.slice(transaction.size, remainingBytes.size)
loop(remainingTxs - 1, newRemainingBytes, transaction :: accum)
}

View file

@ -1,24 +1,33 @@
package org.bitcoins.core.serializers.script
import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.serializers.RawBitcoinSerializer
import org.bitcoins.core.protocol.script.{ScriptPubKey}
import org.bitcoins.core.protocol.script.{EmptyScriptPubKey, ScriptPubKey}
import org.bitcoins.core.script.constant.ScriptToken
import org.bitcoins.core.util.BitcoinSLogger
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil}
import org.slf4j.LoggerFactory
import scala.util.Try
/**
* Created by chris on 1/12/16.
*/
trait RawScriptPubKeyParser extends RawBitcoinSerializer[ScriptPubKey] with BitcoinSLogger {
trait RawScriptPubKeyParser extends RawBitcoinSerializer[ScriptPubKey] {
override def read(bytes : List[Byte]) : ScriptPubKey = {
val script : List[ScriptToken] = ScriptParser.fromBytes(bytes)
ScriptPubKey.fromAsm(script)
if (bytes.isEmpty) EmptyScriptPubKey
else {
val compactSizeUInt = CompactSizeUInt.parseCompactSizeUInt(bytes)
//TODO: Figure out a better way to do this, we can theoretically have numbers larger than Int.MaxValue,
//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)
ScriptPubKey.fromAsm(script)
}
}
override def write(scriptPubKey : ScriptPubKey) : String = {
scriptPubKey.hex
}
override def write(scriptPubKey : ScriptPubKey) : String = scriptPubKey.hex
}
object RawScriptPubKeyParser extends RawScriptPubKeyParser

View file

@ -1,9 +1,10 @@
package org.bitcoins.core.serializers.script
import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.serializers.RawBitcoinSerializer
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.script.constant.{ScriptConstant, ScriptToken}
import org.bitcoins.core.script.crypto.{OP_CHECKMULTISIGVERIFY, OP_CHECKMULTISIG}
import org.bitcoins.core.script.crypto.{OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY}
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil}
import org.slf4j.LoggerFactory
@ -13,15 +14,16 @@ import org.slf4j.LoggerFactory
trait RawScriptSignatureParser extends RawBitcoinSerializer[ScriptSignature] with BitcoinSLogger {
def read(bytes : List[Byte]) : ScriptSignature = {
val scriptTokens : List[ScriptToken] = ScriptParser.fromBytes(bytes)
logger.info("Script tokens inside of RawScriptSig: " + scriptTokens)
ScriptSignature.fromAsm(scriptTokens)
if (bytes.isEmpty) EmptyScriptSignature
else {
val compactSizeUInt = CompactSizeUInt.parseCompactSizeUInt(bytes)
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) : String = scriptSig.hex
}
object RawScriptSignatureParser extends RawScriptSignatureParser

View file

@ -0,0 +1,59 @@
package org.bitcoins.core.serializers.script
import org.bitcoins.core.number.UInt64
import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.protocol.script.ScriptWitness
import org.bitcoins.core.serializers.RawBitcoinSerializer
import org.bitcoins.core.util.BitcoinSUtil
import scala.annotation.tailrec
/**
* Created by chris on 12/14/16.
*/
trait RawScriptWitnessParser extends RawBitcoinSerializer[ScriptWitness] {
def read(bytes: List[Byte]): ScriptWitness = {
logger.debug("Bytes for witness: " + BitcoinSUtil.encodeHex(bytes))
//first byte is the number of stack items
val stackSize = CompactSizeUInt.parseCompactSizeUInt(bytes)
logger.debug("Stack size: " + stackSize)
val (_,stackBytes) = bytes.splitAt(stackSize.size.toInt)
logger.debug("Stack bytes: " + BitcoinSUtil.encodeHex(stackBytes))
@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 stackElement = stackElementBytes.take(elementSize.num.toInt)
logger.debug("Parsed stack element: " + BitcoinSUtil.encodeHex(stackElement))
val (_,newRemainingBytes) = stackElementBytes.splitAt(stackElement.size)
logger.debug("New remaining bytes: " + BitcoinSUtil.encodeHex(newRemainingBytes))
loop(newRemainingBytes, stackElement +: accum, remainingStackItems - UInt64.one)
}
}
//note there is no 'reversing' the accum, in bitcoin-s we assume the stop of the stack is the 'head' element in the sequence
val stack = loop(stackBytes,Nil,stackSize.num)
val witness = ScriptWitness(stack)
witness
}
def write(scriptWitness: ScriptWitness): String = {
@tailrec
def loop(remainingStack: Seq[Seq[Byte]], accum: Seq[String]): Seq[String] = {
if (remainingStack.isEmpty) accum.reverse
else {
val compactSizeUInt: CompactSizeUInt = CompactSizeUInt.calculateCompactSizeUInt(remainingStack.head)
val serialization: Seq[Byte] = compactSizeUInt.bytes ++ remainingStack.head
loop(remainingStack.tail, BitcoinSUtil.encodeHex(serialization) +: accum)
}
}
val stackItems: Seq[String] = loop(scriptWitness.stack.reverse,Nil)
val size = CompactSizeUInt(UInt64(stackItems.size))
val stackHex = stackItems.mkString
size.hex + stackHex
}
}
object RawScriptWitnessParser extends RawScriptWitnessParser

View file

@ -1,9 +1,10 @@
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.script.crypto.{OP_CHECKMULTISIGVERIFY, OP_CHECKMULTISIG}
import org.bitcoins.core.util.{BitcoinSLogger, Factory, BitcoinSUtil}
import org.bitcoins.core.script.crypto.{OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY}
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil, Factory, NumberUtil}
import org.slf4j.LoggerFactory
import scala.annotation.tailrec
@ -15,26 +16,16 @@ import scala.util.{Failure, Success, Try}
trait ScriptParser extends Factory[List[ScriptToken]] with BitcoinSLogger {
/**
* Parses a list of bytes into a list of script tokens
*
* @param bytes
* @return
*/
/** Parses a list of bytes into a list of script tokens */
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)
*
* @param str
* @return
*/
* 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) {
@ -48,19 +39,10 @@ trait ScriptParser extends Factory[List[ScriptToken]] with BitcoinSLogger {
}
}
/**
* 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)
*
* @param str
* @return
*/
* 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] = {
logger.debug("Parsing string: " + str + " into a list of script tokens")
@ -151,11 +133,7 @@ trait ScriptParser extends Factory[List[ScriptToken]] with BitcoinSLogger {
/**
* Parses a byte array into a the asm operations for a script
* will throw an exception if it fails to parse a op code
*
* @param bytes
* @return
*/
* 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] = {
@ -174,12 +152,7 @@ trait ScriptParser extends Factory[List[ScriptToken]] with BitcoinSLogger {
private def parse(bytes : Seq[Byte]) : List[ScriptToken] = parse(bytes.toList)
/**
* Parses a redeem script from the given script token
*
* @param scriptToken
* @return
*/
/** 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))
redeemScript
@ -188,13 +161,7 @@ trait ScriptParser extends Factory[List[ScriptToken]] with BitcoinSLogger {
/**
* 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
*
* @param bytesToPushOntoStack
* @param data
* @tparam T
* @return
*/
* 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)
@ -206,11 +173,7 @@ trait ScriptParser extends Factory[List[ScriptToken]] with BitcoinSLogger {
* 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
*
* @param s
* @return
*/
* 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
@ -236,13 +199,7 @@ trait ScriptParser extends Factory[List[ScriptToken]] with BitcoinSLogger {
* 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.
*
* @param op
* @param accum
* @param tail
* @return
*/
* 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 =>
@ -264,7 +221,6 @@ trait ScriptParser extends Factory[List[ScriptToken]] with BitcoinSLogger {
/**
* Parses OP_PUSHDATA operations correctly. Slices the appropriate amount of bytes off of the tail and pushes
* them onto the accumulator.
*
* @param op the script operation that is being parsed, this should be OP_PUSHDATA1, OP_PUSHDATA2, OP_PUSHDATA4 or else it throws an exception
* @param accum the parsed script tokens so far
* @param tail the bytes to be parsed still
@ -275,8 +231,11 @@ trait ScriptParser extends Factory[List[ScriptToken]] with BitcoinSLogger {
def parseOpPushDataHelper(numBytes : Int) : ParsingHelper[Byte] = {
//next numBytes is the size of the script constant
val scriptConstantHex = tail.slice(0,numBytes)
logger.debug("Script constant hex: " + scriptConstantHex)
val bytesForPushOp = Integer.parseInt(BitcoinSUtil.flipEndianness(scriptConstantHex),16)
logger.debug("Script constant hex: " + BitcoinSUtil.encodeHex(scriptConstantHex))
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)
logger.debug("BytesToPushOntoStack: " + bytesToPushOntoStack)
val scriptConstantBytes = tail.slice(numBytes,bytesForPushOp + numBytes)
@ -286,6 +245,7 @@ trait ScriptParser extends Factory[List[ScriptToken]] with BitcoinSLogger {
buildParsingHelper(op,bytesToPushOntoStack,scriptConstant,restOfBytes,accum)
}
logger.debug("Push op: " + op)
op match {
case OP_PUSHDATA1 =>
parseOpPushDataHelper(1)
@ -317,17 +277,8 @@ trait ScriptParser extends Factory[List[ScriptToken]] with BitcoinSLogger {
scriptConstant :: bytesToPushOntoStack :: op :: accum)
}
/** Checks if a string can be cast to an int
*
* @param str
* @return
*/
private def tryParsingLong(str : String) = try {
parseLong(str)
true
} catch {
case _ : Throwable => false
}
/** Checks if a string can be cast to an int */
private def tryParsingLong(str : String): Boolean = Try(parseLong(str)).isSuccess
private def parseLong(str : String) = {
if (str.substring(0,2) == "0x") {

View file

@ -19,7 +19,7 @@ trait RawTransactionInputParser extends RawBitcoinSerializer[Seq[TransactionInpu
override def read(bytes : List[Byte]) : Seq[TransactionInput] = {
require(bytes.nonEmpty, "You passed in an empty list to read")
val numInputs = bytes.head.toInt
val numInputs = CompactSizeUInt.parseCompactSizeUInt(bytes)
@tailrec
def loop(bytes : List[Byte], accum : List[TransactionInput], inputsLeftToParse : Int) : Seq[TransactionInput] = {
if (inputsLeftToParse > 0) {
@ -29,8 +29,9 @@ trait RawTransactionInputParser extends RawBitcoinSerializer[Seq[TransactionInpu
loop(bytesToBeParsed.toList, newAccum,inputsLeft)
} else accum
}
loop(bytes.tail, List(), numInputs).reverse
logger.debug("Num inputs: " + numInputs.num)
val inputBytes = bytes.slice(numInputs.size.toInt, bytes.size)
loop(inputBytes, Nil, numInputs.num.toInt).reverse
}
override def write(inputs : Seq[TransactionInput]) = {
@ -42,47 +43,36 @@ trait RawTransactionInputParser extends RawBitcoinSerializer[Seq[TransactionInpu
}
/**
* Writes a single transaction input
* @param input
* @return
*/
/** Writes a single transaction input */
def write(input : TransactionInput) : String = {
val outPoint = RawTransactionOutPointParser.write(input.previousOutput)
val varInt = input.scriptSigCompactSizeUInt.hex
val scriptSig = RawScriptSignatureParser.write(input.scriptSignature)
val sequence = addPadding(8,BitcoinSUtil.flipEndianness(input.sequence.hex))
outPoint + varInt + scriptSig + sequence
outPoint + scriptSig + sequence
}
/**
* Parses a single [[TransactionInput]] from a sequence of bytes
* @param bytes
* @return
*/
/** Parses a single [[TransactionInput]] from a sequence of bytes */
private def parseTransactionInput(bytes : Seq[Byte]): (TransactionInput,Seq[Byte]) = {
logger.debug("Bytes to parse for input: " + BitcoinSUtil.encodeHex(bytes))
val outPointBytesSize = 36
val outPointBytes = bytes.take(outPointBytesSize)
val outPoint = TransactionOutPoint(outPointBytes)
val scriptSigCompactSizeUInt : CompactSizeUInt = CompactSizeUInt.parseCompactSizeUInt(
bytes.slice(outPointBytesSize,bytes.length))
val scriptSigBytes = bytes.slice(outPointBytesSize + scriptSigCompactSizeUInt.size.toInt,
outPointBytesSize + scriptSigCompactSizeUInt.size.toInt + scriptSigCompactSizeUInt.num.toInt)
val scriptSigBytes = bytes.slice(outPointBytesSize,bytes.size)
logger.debug("Scriptsig bytes: " + BitcoinSUtil.encodeHex(scriptSigBytes))
val scriptSig : ScriptSignature = RawScriptSignatureParser.read(scriptSigBytes)
logger.debug("Script sig parsed bytes: " + BitcoinSUtil.encodeHex(scriptSig.bytes))
val sequenceBytesSize = 4
val endOfScriptSigBytes = outPointBytesSize + scriptSigCompactSizeUInt.size.toInt + scriptSigBytes.length
val endOfScriptSigBytes = outPointBytesSize + scriptSig.bytes.size
val lastInputByte = endOfScriptSigBytes + sequenceBytesSize
val sequenceBytes = bytes.slice(endOfScriptSigBytes,lastInputByte)
val sequenceNumberHex : String = BitcoinSUtil.encodeHex(sequenceBytes)
val sequenceNumberFlippedEndianness = BitcoinSUtil.flipEndianness(sequenceNumberHex)
logger.debug("Sequence number hex: " + sequenceNumberFlippedEndianness)
val sequenceNumber : UInt32 = UInt32(sequenceNumberFlippedEndianness)
val txInput = TransactionInput(outPoint,scriptSig,sequenceNumber)
logger.debug("Parsed input: " + txInput)
(txInput, bytes.slice(lastInputByte, bytes.length))
}
}

View file

@ -17,36 +17,24 @@ import scala.annotation.tailrec
*/
trait RawTransactionOutputParser extends RawBitcoinSerializer[Seq[TransactionOutput]] with ScriptParser with BitcoinSLogger {
/** Reads a sequence of outputs, expects first element in the byte array to be a [[org.bitcoins.core.protocol.CompactSizeUInt]]
* indicating how many outputs we need to read
*/
override def read(bytes : List[Byte]) : Seq[TransactionOutput] = {
//TODO: Possible bug here, this needs to be parsed as a CompactSizeUInt I think
val numOutputs = bytes.head.toInt
@tailrec
def loop(bytes : List[Byte], accum : List[TransactionOutput], outputsLeftToParse : Int) : List[TransactionOutput] = {
if (outputsLeftToParse > 0) {
//TODO: this needs to be refactored to, need to create a function that returns a single TransactionOutput
//then call that function multiple times to get a Seq[TransactionOutput]
val satoshisHex = BitcoinSUtil.encodeHex(bytes.take(8).reverse)
logger.debug("Satoshi hex: " + satoshisHex)
val satoshis = parseSatoshis(satoshisHex)
//it doesn't include itself towards the size, thats why it is incremented by one
val firstScriptPubKeyByte = 8
val scriptCompactSizeUIntSize : Int = CompactSizeUInt.parseCompactSizeUIntSize(bytes(firstScriptPubKeyByte)).toInt
logger.debug("VarInt hex: " + BitcoinSUtil.encodeHex(bytes.slice(firstScriptPubKeyByte,firstScriptPubKeyByte + scriptCompactSizeUIntSize)))
val scriptSigCompactSizeUInt : CompactSizeUInt =
CompactSizeUInt.parseCompactSizeUInt(bytes.slice(firstScriptPubKeyByte,firstScriptPubKeyByte + scriptCompactSizeUIntSize))
val scriptPubKeyBytes = bytes.slice(firstScriptPubKeyByte + scriptCompactSizeUIntSize,
firstScriptPubKeyByte + scriptCompactSizeUIntSize + scriptSigCompactSizeUInt.num.toInt)
val scriptPubKey = RawScriptPubKeyParser.read(scriptPubKeyBytes)
val parsedOutput = TransactionOutput(satoshis,scriptPubKey)
val parsedOutput = readSingleOutput(bytes)
val newAccum = parsedOutput :: accum
val bytesToBeParsed = bytes.slice(parsedOutput.size, bytes.size)
val outputsLeft = outputsLeftToParse-1
logger.debug("Parsed output: " + parsedOutput)
logger.debug("Outputs left to parse: " + outputsLeft)
loop(bytesToBeParsed, newAccum, outputsLeft)
} else accum
}
loop(bytes.tail,List(),numOutputs).reverse
loop(bytes.tail,Nil,numOutputs).reverse
}
override def write(outputs : Seq[TransactionOutput]) : String = {
@ -58,26 +46,33 @@ trait RawTransactionOutputParser extends RawBitcoinSerializer[Seq[TransactionOut
}
/**
* Writes a single transaction output
*
* @param output
* @return
*/
/** Writes a single transaction output */
def write(output : TransactionOutput) : String = {
val satoshis = CurrencyUnits.toSatoshis(output.value)
val compactSizeUIntHex = output.scriptPubKeyCompactSizeUInt.hex
val satoshisHexWithoutPadding : String = BitcoinSUtil.flipEndianness(satoshis.hex)
val satoshisHex = addPadding(16,satoshisHexWithoutPadding)
logger.debug("compactSizeUIntHex: " + compactSizeUIntHex)
logger.debug("satoshis: " + satoshisHex)
if (compactSizeUIntHex == "00") satoshisHex + compactSizeUIntHex
else satoshisHex + compactSizeUIntHex + output.scriptPubKey.hex
/* if (compactSizeUIntHex == "00") satoshisHex + compactSizeUIntHex
else */ satoshisHex + output.scriptPubKey.hex
}
/** Reads a single output from the given bytes, note this is different than [[org.bitcoins.core.serializers.transaction.RawTransactionOutputParser.read]]
* because it does NOT expect a [[CompactSizeUInt]] to be the first element in the byte array indicating how many outputs we have
*/
def readSingleOutput(bytes: Seq[Byte]): TransactionOutput = {
val satoshisHex = BitcoinSUtil.encodeHex(bytes.take(8).reverse)
logger.debug("Satoshi hex: " + satoshisHex)
val satoshis = parseSatoshis(satoshisHex)
//it doesn't include itself towards the size, thats why it is incremented by one
val scriptPubKeyBytes = bytes.slice(8, bytes.size)
val scriptPubKey = RawScriptPubKeyParser.read(scriptPubKeyBytes)
val parsedOutput = TransactionOutput(satoshis,scriptPubKey)
logger.debug("Parsed output: " + parsedOutput)
parsedOutput
}
/**
* Parses the amount of satoshis a hex string represetns
*
* @param hex the hex string that is being parsed to satoshis
* @return the value of the hex string in satoshis
*/

View file

@ -0,0 +1,38 @@
package org.bitcoins.core.serializers.transaction
import org.bitcoins.core.protocol.script.ScriptWitness
import org.bitcoins.core.protocol.transaction.TransactionWitness
import scala.annotation.tailrec
/**
* Created by chris on 11/21/16.
* Serialization of [[TransactionWitness]] as defined inside of BIP141
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#specification]]
* [[https://github.com/bitcoin/bitcoin/blob/b4e4ba475a5679e09f279aaf2a83dcf93c632bdb/src/primitives/transaction.h#L232-L268]]
*/
trait RawTransactionWitnessParser {
/** We can only tell how many [[org.bitcoins.core.protocol.transaction.TransactionInputWitness]]
* we have if we have the number of inputs the transaction creates
*/
def read(bytes: Seq[Byte], numInputs: Int): TransactionWitness = {
@tailrec
def loop(remainingBytes: Seq[Byte], remainingInputs: Int, accum: Seq[ScriptWitness]): Seq[ScriptWitness] = {
if (remainingInputs != 0) {
val w = ScriptWitness(remainingBytes)
val (_,newRemainingBytes) = remainingBytes.splitAt(w.bytes.size)
loop(newRemainingBytes,remainingInputs - 1, w +: accum)
} else accum.reverse
}
val witnesses = loop(bytes,numInputs,Nil)
TransactionWitness(witnesses)
}
def write(witness: TransactionWitness): String = {
val hex = witness.witnesses.map(_.hex).mkString
hex
}
}
object RawTransactionWitnessParser extends RawTransactionWitnessParser

View file

@ -1,12 +1,14 @@
package org.bitcoins.core.util
import org.bitcoins.core.crypto.ECPublicKey
import org.bitcoins.core.crypto._
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.script.ScriptPubKey
import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.protocol.script.{CLTVScriptPubKey, CSVScriptPubKey, EmptyScriptPubKey, _}
import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.crypto.{OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY, OP_CHECKSIG, OP_CHECKSIGVERIFY}
import org.bitcoins.core.script.flag.{ScriptFlag, ScriptFlagUtil}
import org.bitcoins.core.script.{ScriptOperation, ScriptProgram, ScriptSettings}
import org.bitcoins.core.script.result.{ScriptError, ScriptErrorPubKeyType, ScriptErrorWitnessPubKeyType}
import org.bitcoins.core.script.{ExecutionInProgressScriptProgram, ScriptOperation, ScriptProgram, ScriptSettings}
import scala.annotation.tailrec
import scala.util.Try
@ -14,36 +16,23 @@ import scala.util.Try
/**
* Created by chris on 3/2/16.
*/
trait BitcoinScriptUtil {
trait BitcoinScriptUtil extends BitcoinSLogger {
/**
* Takes in a sequence of script tokens and converts them to their hexadecimal value
*
* @param asm
* @return
*/
/** Takes in a sequence of script tokens and converts them to their hexadecimal value */
def asmToHex(asm : Seq[ScriptToken]) : String = {
val hex = asm.map(_.hex).mkString
hex
}
/**
* Converts a sequence of script tokens to them to their byte values
*
* @param asm
* @return
*/
/** Converts a sequence of script tokens to them to their byte values */
def asmToBytes(asm : Seq[ScriptToken]) : Seq[Byte] = BitcoinSUtil.decodeHex(asmToHex(asm))
/**
* Filters out push operations in our sequence of script tokens
* this removes OP_PUSHDATA1, OP_PUSHDATA2, OP_PUSHDATA4 and all ByteToPushOntoStack tokens
*
* @param asm
* @return
*/
* this removes OP_PUSHDATA1, OP_PUSHDATA2, OP_PUSHDATA4 and all ByteToPushOntoStack tokens */
def filterPushOps(asm : Seq[ScriptToken]) : Seq[ScriptToken] = {
//TODO: This does not remove the following script number after a OP_PUSHDATA
asm.filterNot(op => op.isInstanceOf[BytesToPushOntoStack]
|| op == OP_PUSHDATA1
|| op == OP_PUSHDATA2
@ -53,21 +42,19 @@ trait BitcoinScriptUtil {
/**
* Returns true if the given script token counts towards our max script operations in a script
* See https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L269-L271
* which is how bitcoin core handles this
*
* @param token
* @return
*/
* which is how bitcoin core handles this */
def countsTowardsScriptOpLimit(token : ScriptToken) : Boolean = token match {
case scriptOp : ScriptOperation if (scriptOp.opCode > OP_16.opCode) => true
case _ : ScriptToken => false
}
/**
* Counts the amount of sigops in a script
* https://github.com/bitcoin/bitcoin/blob/master/src/script/script.cpp#L156-L202
*
* @param script the script whose sigops are being counted
* @return the number of signature operations in the script
*/
@ -92,9 +79,6 @@ trait BitcoinScriptUtil {
* This can only be called when an OP_CHECKMULTISIG operation is about to be executed
* on the stack
* For instance if this was a 2/3 multisignature script, it would return the number 3
*
* @param program
* @return
*/
def numPossibleSignaturesOnStack(program : ScriptProgram) : ScriptNumber = {
require(program.script.headOption == Some(OP_CHECKMULTISIG) || program.script.headOption == Some(OP_CHECKMULTISIGVERIFY),
@ -110,9 +94,6 @@ trait BitcoinScriptUtil {
/**
* Returns the number of required signatures on the stack, for instance if this was a
* 2/3 multisignature script, it would return the number 2
*
* @param program
* @return
*/
def numRequiredSignaturesOnStack(program : ScriptProgram) : ScriptNumber = {
require(program.script.headOption == Some(OP_CHECKMULTISIG) || program.script.headOption == Some(OP_CHECKMULTISIGVERIFY),
@ -132,9 +113,6 @@ trait BitcoinScriptUtil {
* Determines if a script contains only script operations
* This is equivalent to
* https://github.com/bitcoin/bitcoin/blob/master/src/script/script.cpp#L213
*
* @param script
* @return
*/
def isPushOnly(script : Seq[ScriptToken]) : Boolean = {
@tailrec
@ -153,7 +131,6 @@ trait BitcoinScriptUtil {
* Determines if the token being pushed onto the stack is being pushed by the SMALLEST push operation possible
* This is equivalent to
* https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L209
*
* @param pushOp the operation that is pushing the data onto the stack
* @param token the token that is being pushed onto the stack by the pushOp
* @return
@ -181,12 +158,7 @@ trait BitcoinScriptUtil {
}
}
/**
* Calculates the push operation for the given [[ScriptToken]]
*
* @param scriptToken
* @return
*/
/** Calculates the push operation for the given [[ScriptToken]] */
def calculatePushOp(scriptToken : ScriptToken) : Seq[ScriptToken] = {
//push ops following an OP_PUSHDATA operation are interpreted as unsigned numbers
val scriptTokenSize = UInt32(scriptToken.bytes.size)
@ -216,20 +188,13 @@ trait BitcoinScriptUtil {
/**
* Whenever a script constant is interpreted to a number BIP62 could enforce that number to be encoded
* in the smallest encoding possible
* https://github.com/bitcoin/bitcoin/blob/a6a860796a44a2805a58391a009ba22752f64e32/src/script/script.h#L220-L237
*
* @param constant
* @return
*/
* https://github.com/bitcoin/bitcoin/blob/a6a860796a44a2805a58391a009ba22752f64e32/src/script/script.h#L220-L237 */
def isShortestEncoding(constant : ScriptConstant) : Boolean = isShortestEncoding(constant.bytes)
/**
* Whenever a script constant is interpreted to a number BIP62 could enforce that number to be encoded
* in the smallest encoding possible
* https://github.com/bitcoin/bitcoin/blob/a6a860796a44a2805a58391a009ba22752f64e32/src/script/script.h#L220-L237
*
* @param bytes
* @return
*/
def isShortestEncoding(bytes : Seq[Byte]) : Boolean = {
// If the most-significant-byte - excluding the sign bit - is zero
@ -251,16 +216,12 @@ trait BitcoinScriptUtil {
* Whenever a script constant is interpreted to a number BIP62 should enforce that number to be encoded
* in the smallest encoding possible
* https://github.com/bitcoin/bitcoin/blob/a6a860796a44a2805a58391a009ba22752f64e32/src/script/script.h#L220-L237
*
* @param hex
* @return
*/
def isShortestEncoding(hex : String) : Boolean = isShortestEncoding(BitcoinSUtil.decodeHex(hex))
/**
* Checks the public key encoding according to bitcoin core's function
* https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L202
*
* @param key the key whose encoding we are checking
* @param key the key whose encoding we are checking
* @param program the program whose flags which dictate the rules for the public keys encoding
* @return if the key is encoded correctly against the rules give in the flags parameter
*/
@ -269,8 +230,7 @@ trait BitcoinScriptUtil {
/**
* Checks the public key encoding according to bitcoin core's function
* https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L202
*
* @param key the key whose encoding we are checking
* @param key the key whose encoding we are checking
* @param flags the flags which dictate the rules for the public keys encoding
* @return if the key is encoded correctly against the rules givein the flags parameter
*/
@ -283,8 +243,7 @@ trait BitcoinScriptUtil {
/**
* Returns true if the key is compressed or uncompressed, false otherwise
* https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L66
*
* @param key the public key that is being checked
* @param key the public key that is being checked
* @return true if the key is compressed/uncompressed otherwise false
*/
def isCompressedOrUncompressedPubKey(key : ECPublicKey) : Boolean = {
@ -297,11 +256,8 @@ trait BitcoinScriptUtil {
// Non-canonical public key: invalid length for uncompressed key
return false
}
} else if (key.bytes.head == 0x02 || key.bytes.head == 0x03) {
if (key.bytes.size != 33) {
// Non-canonical public key: invalid length for compressed key
return false
}
} else if (isCompressedPubKey(key)) {
return true
} else {
// Non-canonical public key: neither compressed nor uncompressed
return false
@ -309,10 +265,156 @@ trait BitcoinScriptUtil {
return true
}
/** Checks if the given public key is a compressed public key */
def isCompressedPubKey(key: ECPublicKey): Boolean = {
(key.bytes.size == 33) && (key.bytes.head == 0x02 || key.bytes.head == 0x03)
}
def minimalScriptNumberRepresentation(num : ScriptNumber) : ScriptNumber = {
val op = ScriptNumberOperation.fromNumber(num.toInt)
if (op.isDefined) op.get else num
}
/**
* Determines if the given pubkey is valid in accordance to the given [[ScriptFlag]]s
* Mimics this function inside of Bitcoin Core
* [[https://github.com/bitcoin/bitcoin/blob/528472111b4965b1a99c4bcf08ac5ec93d87f10f/src/script/interpreter.cpp#L214-L223]]
*/
def isValidPubKeyEncoding(pubKey: ECPublicKey, flags: Seq[ScriptFlag]): Option[ScriptError] = {
if (ScriptFlagUtil.requireStrictEncoding(flags) &&
!BitcoinScriptUtil.isCompressedOrUncompressedPubKey(pubKey)) {
Some(ScriptErrorPubKeyType)
}
else if (ScriptFlagUtil.requireScriptVerifyWitnessPubKeyType(flags) &&
!BitcoinScriptUtil.isCompressedPubKey(pubKey)) {
Some(ScriptErrorWitnessPubKeyType)
} else None
}
/** Prepares the script we spending to be serialized for our transaction signature serialization algorithm
* We need to check if the scriptSignature has a redeemScript
* In that case, we need to pass the redeemScript to the TransactionSignatureChecker
*
* In the case we have a P2SH(P2WSH) we need to pass the witness's redeem script to the [[TransactionSignatureChecker]]
* instead of passing the [[WitnessScriptPubKey]] inside of the [[P2SHScriptSignature]]'s redeem script.
* */
def calculateScriptForChecking(txSignatureComponent: TransactionSignatureComponent,
signature: ECDigitalSignature,script: Seq[ScriptToken]): Seq[ScriptToken] = {
val scriptWithSigRemoved = calculateScriptForSigning(txSignatureComponent, script)
removeSignatureFromScript(signature,scriptWithSigRemoved)
}
def calculateScriptForSigning(txSignatureComponent: TransactionSignatureComponent, script: Seq[ScriptToken]): Seq[ScriptToken] = txSignatureComponent match {
case base: BaseTransactionSignatureComponent =>
txSignatureComponent.scriptSignature match {
case s : P2SHScriptSignature =>
//needs to be here for removing all sigs from OP_CHECKMULTISIG
//https://github.com/bitcoin/bitcoin/blob/master/src/test/data/tx_valid.json#L177
//Finally CHECKMULTISIG removes all signatures prior to hashing the script containing those signatures.
//In conjunction with the SIGHASH_SINGLE bug this lets us test whether or not FindAndDelete() is actually
// present in scriptPubKey/redeemScript evaluation by including a signature of the digest 0x01
// We can compute in advance for our pubkey, embed it it in the scriptPubKey, and then also
// using a normal SIGHASH_ALL signature. If FindAndDelete() wasn't run, the 'bugged'
//signature would still be in the hashed script, and the normal signature would fail."
logger.info("Replacing redeemScript in txSignature component")
logger.info("Redeem script: " + s.redeemScript)
val sigsRemoved = removeSignaturesFromScript(s.signatures,s.redeemScript.asm)
sigsRemoved
case _ : P2PKHScriptSignature | _ : P2PKScriptSignature | _ : NonStandardScriptSignature
| _ : MultiSignatureScriptSignature | _ : CLTVScriptSignature | _ : CSVScriptSignature | EmptyScriptSignature =>
script
}
case wtxSigComponent : WitnessV0TransactionSignatureComponent =>
txSignatureComponent.scriptSignature match {
case s : P2SHScriptSignature =>
// this is for the case of P2SH(P2WSH) or P2SH(P2WPKH)
// in the case of P2SH(P2WPKH) we need to sign the P2WPKH asm
// in the case of P2SH(P2WSH) we need to sign the redeem script inside of the
// witness, NOT the witnessScriptPubKey redeemScript inside of the P2SHScriptSignature
logger.debug("Redeem script: " + s.redeemScript)
s.redeemScript match {
case w : WitnessScriptPubKey =>
//rebuild scriptPubKey asm
val scriptEither: Either[(Seq[ScriptToken], ScriptPubKey), ScriptError] = w.witnessVersion.rebuild(wtxSigComponent.witness,w.witnessProgram)
parseScriptEither(scriptEither)
case _ : P2SHScriptPubKey | _ : P2PKHScriptPubKey | _ : P2PKScriptPubKey | _ : MultiSignatureScriptPubKey |
_ : NonStandardScriptPubKey | _ : CLTVScriptPubKey | _ : CSVScriptPubKey | EmptyScriptPubKey =>
val sigsRemoved = removeSignaturesFromScript(s.signatures, s.redeemScript.asm)
sigsRemoved
}
case EmptyScriptSignature =>
logger.info("wtxSigComponent.scriptPubKey: " + wtxSigComponent.scriptPubKey)
wtxSigComponent.scriptPubKey match {
case w : WitnessScriptPubKeyV0 =>
//for bare P2WPKH
logger.debug("wtxSigComponent.witness: " + wtxSigComponent.witness)
logger.debug("w.witnessProgram: " + w.witnessProgram)
val scriptEither = w.witnessVersion.rebuild(wtxSigComponent.witness,w.witnessProgram)
logger.debug("scriptEither: " + scriptEither)
val s = parseScriptEither(scriptEither)
logger.debug("P2WPKH: " + s)
s
case _ : P2SHScriptPubKey | _ : P2PKHScriptPubKey | _ : P2PKScriptPubKey | _ : MultiSignatureScriptPubKey |
_ : NonStandardScriptPubKey | _ : CLTVScriptPubKey | _ : CSVScriptPubKey |
_: UnassignedWitnessScriptPubKey | EmptyScriptPubKey =>
script
}
case _ : P2PKHScriptSignature | _ : P2PKScriptSignature | _ : NonStandardScriptSignature
| _ : MultiSignatureScriptSignature | _ : CLTVScriptSignature | _ : CSVScriptSignature =>
script
}
}
/**
* Removes the given [[ECDigitalSignature]] from the list of [[ScriptToken]] if it exists
*/
def removeSignatureFromScript(signature : ECDigitalSignature, script : Seq[ScriptToken]) : Seq[ScriptToken] = {
if (script.contains(ScriptConstant(signature.hex))) {
//replicates this line in bitcoin core
//https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L872
val sigIndex = script.indexOf(ScriptConstant(signature.hex))
logger.debug("SigIndex: " + sigIndex)
//remove sig and it's corresponding BytesToPushOntoStack
script.slice(0,sigIndex-1) ++ script.slice(sigIndex+1,script.size)
} else script
}
/** Removes the list of [[ECDigitalSignature]] from the list of [[ScriptToken]] */
def removeSignaturesFromScript(sigs : Seq[ECDigitalSignature], script : Seq[ScriptToken]) : Seq[ScriptToken] = {
@tailrec
def loop(remainingSigs : Seq[ECDigitalSignature], scriptTokens : Seq[ScriptToken]) : Seq[ScriptToken] = {
remainingSigs match {
case Nil => scriptTokens
case h :: t =>
val newScriptTokens = removeSignatureFromScript(h,scriptTokens)
loop(t,newScriptTokens)
}
}
loop(sigs,script)
}
/**
* Removes the OP_CODESEPARATOR in the original script according to
* the last code separator index in the script
* @param program
* @return
*/
def removeOpCodeSeparator(program : ExecutionInProgressScriptProgram) : Seq[ScriptToken] = {
if (program.lastCodeSeparator.isDefined) {
program.originalScript.slice(program.lastCodeSeparator.get+1, program.originalScript.size)
} else program.originalScript
}
def parseScriptEither(scriptEither: Either[(Seq[ScriptToken], ScriptPubKey), ScriptError]): Seq[ScriptToken] = scriptEither match {
case Left((_,scriptPubKey)) =>
logger.debug("Script pubkey asm inside calculateForSigning: " + scriptPubKey.asm)
scriptPubKey.asm
case Right(_) => Nil //error
}
}

View file

@ -12,12 +12,4 @@ import org.scalatest.{FlatSpec, MustMatchers}
* Created by chris on 2/29/16.
*/
class TransactionSignatureCheckerTest extends FlatSpec with MustMatchers {
"TransactionSignatureChecker" must "remove the signatures from a p2sh scriptSig" in {
val p2shScriptSig = TestUtil.p2sh2Of3ScriptSig
val signatures = p2shScriptSig.signatures
val asmWithoutSigs = TransactionSignatureChecker.removeSignaturesFromScript(signatures,p2shScriptSig.asm)
val sigExists = signatures.map(sig => asmWithoutSigs.exists(_ == ScriptConstant(sig.hex)))
sigExists.exists(_ == true) must be (false)
}
}

View file

@ -36,9 +36,7 @@ class TransactionSignatureCreatorSpec extends Properties("TransactionSignatureCr
case (txSignatureComponent: TransactionSignatureComponent, _) =>
//run it through the interpreter
val program = ScriptProgram(txSignatureComponent)
val result = ScriptInterpreter.run(program)
result == ScriptOk
}
@ -94,4 +92,49 @@ class TransactionSignatureCreatorSpec extends Properties("TransactionSignatureCr
Seq(ScriptErrorUnsatisfiedLocktime, ScriptErrorPushSize).contains(result)
}
property("generate a valid signature for a p2wpkh witness transaction") =
Prop.forAllNoShrink(TransactionGenerators.signedP2WPKHTransaction) { case (wtxSigComponent, privKeys) =>
val program = ScriptProgram(wtxSigComponent)
val result = ScriptInterpreter.run(program)
result == ScriptOk
}
property("generate a valid signature for a p2wsh(p2pk) witness transaction") =
Prop.forAllNoShrink(TransactionGenerators.signedP2WSHP2PKTransaction) { case (wtxSigComponent, privKeys) =>
val program = ScriptProgram(wtxSigComponent)
val result = ScriptInterpreter.run(program)
result == ScriptOk
}
property("generate a valid signature for a p2wsh(p2pkh) witness transaction") =
Prop.forAllNoShrink(TransactionGenerators.signedP2WSHP2PKHTransaction) { case (wtxSigComponent, privKeys) =>
val program = ScriptProgram(wtxSigComponent)
val result = ScriptInterpreter.run(program)
result == ScriptOk
}
property("generate a valid signature for a p2wsh(multisig) witness transaction") =
Prop.forAllNoShrink(TransactionGenerators.signedP2WSHMultiSigTransaction) { case (wtxSigComponent, privKeys) =>
val program = ScriptProgram(wtxSigComponent)
val result = ScriptInterpreter.run(program)
if (result != ScriptOk) logger.warn("Result: " + result)
result == ScriptOk
}
property("generate a valid signature from a p2sh(p2wpkh) witness transaction") =
Prop.forAllNoShrink(TransactionGenerators.signedP2SHP2WPKHTransaction) { case (wtxSigComponent, privKeys) =>
val program = ScriptProgram(wtxSigComponent)
val result = ScriptInterpreter.run(program)
if (result != ScriptOk) logger.warn("Result: " + result)
result == ScriptOk
}
property("generate a valid signature from a p2sh(p2wsh) witness tranasction") =
Prop.forAllNoShrink(TransactionGenerators.signedP2SHP2WSHTransaction) { case (wtxSigComponent, privKeys) =>
val program = ScriptProgram(wtxSigComponent)
val result = ScriptInterpreter.run(program)
if (result != ScriptOk) logger.warn("Result: " + result)
result == ScriptOk
}
}

View file

@ -5,7 +5,7 @@ import org.bitcoins.core.policy.Policy
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.script.ScriptProgram
import org.bitcoins.core.script.crypto.SIGHASH_ALL
import org.bitcoins.core.script.crypto.{HashType, SIGHASH_ALL}
import org.bitcoins.core.script.interpreter.ScriptInterpreter
import org.bitcoins.core.script.result.ScriptOk
import org.bitcoins.core.util.{BitcoinSLogger, TransactionTestUtil}
@ -23,25 +23,26 @@ class TransactionSignatureCreatorTest extends FlatSpec with MustMatchers with Bi
val expectedSig = ECDigitalSignature("30440220357864ae2beba3d6ec34c0ce42262c1c12939502f0f8f4bd338c9d8b307593420220656687c327589dc3e464700fa7b784c7efc2b465c627a60c2f1ce402d05fc39d01")
val rawTx = "01000000021d50bf7c05b6169ea8d8fb5b79dd2978bbd2ac756a656a777279da43b19fd9d9000000006b4830450221008f2c818a55045a1c9dcda54fcd5b6377f5d09723a9ccd8c71df76ee4bdf7c16802201817cbd71d8148a5d53b11d33c9c58ad1086fe7ddf308da2a7cceb7d85df293e01210381c82dc267a958be06f1c920dc635bcd191d698c167e67a45a882a551c57ce1dfeffffffd4a6a37abfe003a9d10155df215e662f88d5b878b908d1a3772a9fbd195d008d010000006a4730440220357864ae2beba3d6ec34c0ce42262c1c12939502f0f8f4bd338c9d8b307593420220656687c327589dc3e464700fa7b784c7efc2b465c627a60c2f1ce402d05fc39d0121036301d848aec3dfc47789a63ee3c85c6d3bf757162ef77cb1580981b422838ed7feffffff0200e1f505000000001976a9146d39bac171d0bf450698fa0ebd93f51e79dcb6ac88ac35a96d00000000001976a914e11753f499ac7a910148e53156ab273557ed517e88acd6090b00"
val transaction = Transaction(rawTx)
val scriptPubKey = ScriptPubKey("76a914d7b4717a934386601ac3f980d01b48c83b8a0b4b88ac")
val txSignatureComponent = TransactionSignatureComponent(transaction, UInt32.one, scriptPubKey, Policy.standardScriptVerifyFlags)
val scriptPubKey = ScriptPubKey("1976a914d7b4717a934386601ac3f980d01b48c83b8a0b4b88ac")
val txSignatureComponent = TransactionSignatureComponent(transaction, UInt32.one,
scriptPubKey, Policy.standardScriptVerifyFlags)
val privateKey = ECPrivateKey.fromWIFToPrivateKey("cTPg4Zc5Jis2EZXy3NXShgbn487GWBTapbU63BerLDZM3w2hQSjC")
val txSignature = TransactionSignatureCreator.createSig(txSignatureComponent, privateKey, SIGHASH_ALL.defaultValue)
val txSignature = TransactionSignatureCreator.createSig(txSignatureComponent, privateKey, HashType.sigHashAll)
txSignature.r must be (expectedSig.r)
txSignature.s must be (expectedSig.s)
txSignature.hex must be (expectedSig.hex)
}
it must "create the correct digital signature for a transaction with 1 input" in {
//66f48fa8ef5db20a3b4be6b13f024b6e23480fd83df26ffbe7449110b113a665 on testnet
val expectedSig = ECDigitalSignature("3044022075b4ab08ff34799ee6f8048a5044be98dff493fc5a0b8a36dcaee3bd7a9993ae02207bc532ceab09c10f1d54035d03ff9aad0e1004c3e0325a8b97b6be04b7d6c3a201")
val rawTx = "0100000001b8a1278696acfa85f1f576836aa30d335207b69bdaff43d9464cc1db40fe19ae000000006a473044022075b4ab08ff34799ee6f8048a5044be98dff493fc5a0b8a36dcaee3bd7a9993ae02207bc532ceab09c10f1d54035d03ff9aad0e1004c3e0325a8b97b6be04b7d6c3a2012102a01aaa27b468ec3fb2ae0c2a9fa1d5dce9b79b35062178f479156d8daa6c0e50feffffff02a0860100000000001976a914775bd9c79a9e988c0d6177a9205a611a50b7229188acb6342900000000001976a914f23a46f930320ab3cc7ad8c1660325f4c434d11688ac63b70d00"
val transaction = Transaction(rawTx)
val scriptPubKey = ScriptPubKey("76a914cd0385f813ec73f8fc340b7069daf566878a0d6b88ac")
val txSignatureComponent = TransactionSignatureComponent(transaction, UInt32.zero, scriptPubKey, Policy.standardScriptVerifyFlags)
val scriptPubKey = ScriptPubKey("1976a914cd0385f813ec73f8fc340b7069daf566878a0d6b88ac")
val txSignatureComponent = TransactionSignatureComponent(transaction, UInt32.zero, scriptPubKey,
Policy.standardScriptVerifyFlags)
val privateKey = ECPrivateKey.fromWIFToPrivateKey("cTTh7jNtZhg3vHTjvYK8zcHkLfsMAS8iqL7pfZ6eVAVHHF8fN1qy")
val txSignature = TransactionSignatureCreator.createSig(txSignatureComponent, privateKey, SIGHASH_ALL.defaultValue)
val txSignature = TransactionSignatureCreator.createSig(txSignatureComponent, privateKey, HashType.sigHashAll)
txSignature.r must be (expectedSig.r)
txSignature.s must be (expectedSig.s)
txSignature.hex must be (expectedSig.hex)
@ -55,8 +56,9 @@ class TransactionSignatureCreatorTest extends FlatSpec with MustMatchers with Bi
val (creditingTx,outputIndex) = TransactionTestUtil.buildCreditingTransaction(scriptPubKey)
val scriptSig = P2PKScriptSignature(EmptyDigitalSignature)
val (spendingTx,inputIndex) = TransactionTestUtil.buildSpendingTransaction(creditingTx,scriptSig,outputIndex)
val txSignatureComponent = TransactionSignatureComponent(spendingTx,inputIndex,scriptPubKey,Policy.standardScriptVerifyFlags)
val txSignature = TransactionSignatureCreator.createSig(txSignatureComponent,privateKey,SIGHASH_ALL.defaultValue)
val txSignatureComponent = TransactionSignatureComponent(spendingTx,inputIndex,scriptPubKey,
Policy.standardScriptVerifyFlags)
val txSignature = TransactionSignatureCreator.createSig(txSignatureComponent,privateKey,HashType.sigHashAll)
//add the signature to the scriptSig instead of having an empty scriptSig
val signedScriptSig = P2PKScriptSignature(txSignature)
@ -78,8 +80,9 @@ class TransactionSignatureCreatorTest extends FlatSpec with MustMatchers with Bi
val (creditingTx,outputIndex) = TransactionTestUtil.buildCreditingTransaction(scriptPubKey)
val scriptSig = P2PKHScriptSignature(EmptyDigitalSignature,publicKey)
val (spendingTx,inputIndex) = TransactionTestUtil.buildSpendingTransaction(creditingTx,scriptSig,outputIndex)
val txSignatureComponent = TransactionSignatureComponent(spendingTx,inputIndex,scriptPubKey,Policy.standardScriptVerifyFlags)
val txSignature = TransactionSignatureCreator.createSig(txSignatureComponent,privateKey, SIGHASH_ALL.defaultValue)
val txSignatureComponent = TransactionSignatureComponent(spendingTx,inputIndex,scriptPubKey,
Policy.standardScriptVerifyFlags)
val txSignature = TransactionSignatureCreator.createSig(txSignatureComponent,privateKey, HashType.sigHashAll)
//add the signature to the scriptSig instead of having an empty scriptSig
val signedScriptSig = P2PKHScriptSignature(txSignature,publicKey)
@ -101,8 +104,9 @@ class TransactionSignatureCreatorTest extends FlatSpec with MustMatchers with Bi
val (creditingTx,outputIndex) = TransactionTestUtil.buildCreditingTransaction(scriptPubKey)
val scriptSig = MultiSignatureScriptSignature(Seq(EmptyDigitalSignature))
val (spendingTx,inputIndex) = TransactionTestUtil.buildSpendingTransaction(creditingTx,scriptSig,outputIndex)
val txSignatureComponent = TransactionSignatureComponent(spendingTx,inputIndex,scriptPubKey,Policy.standardScriptVerifyFlags)
val txSignature = TransactionSignatureCreator.createSig(txSignatureComponent,privateKey, SIGHASH_ALL.defaultValue)
val txSignatureComponent = TransactionSignatureComponent(spendingTx,inputIndex,scriptPubKey,
Policy.standardScriptVerifyFlags)
val txSignature = TransactionSignatureCreator.createSig(txSignatureComponent,privateKey, HashType.sigHashAll)
//add the signature to the scriptSig instead of having an empty scriptSig
val signedScriptSig = MultiSignatureScriptSignature(Seq(txSignature))
@ -126,8 +130,9 @@ class TransactionSignatureCreatorTest extends FlatSpec with MustMatchers with Bi
val scriptSig = MultiSignatureScriptSignature(Seq(EmptyDigitalSignature))
val (spendingTx,inputIndex) = TransactionTestUtil.buildSpendingTransaction(creditingTx,scriptSig,outputIndex)
val txSignatureComponent = TransactionSignatureComponent(spendingTx,inputIndex,redeemScript,Policy.standardScriptVerifyFlags)
val txSignature = TransactionSignatureCreator.createSig(txSignatureComponent, privateKey, SIGHASH_ALL.defaultValue)
val txSignatureComponent = TransactionSignatureComponent(spendingTx,inputIndex,redeemScript,
Policy.standardScriptVerifyFlags)
val txSignature = TransactionSignatureCreator.createSig(txSignatureComponent, privateKey, HashType.sigHashAll)
val signedScriptSig = MultiSignatureScriptSignature(Seq(txSignature))
val p2shScriptSig = P2SHScriptSignature(signedScriptSig,redeemScript)
@ -138,4 +143,5 @@ class TransactionSignatureCreatorTest extends FlatSpec with MustMatchers with Bi
val result = ScriptInterpreter.run(program)
result must be (ScriptOk)
}
}

View file

@ -6,7 +6,9 @@ import org.bitcoinj.core.{DumpedPrivateKey, Sha256Hash, Utils}
import org.bitcoinj.core.Transaction.SigHash
import org.bitcoinj.params.TestNet3Params
import org.bitcoinj.script.{ScriptBuilder, ScriptChunk, ScriptOpCodes}
import org.bitcoins.core.number.{Int32, UInt32}
import org.bitcoins.core.currency.Satoshis
import org.bitcoins.core.number.{Int32, Int64, UInt32}
import org.bitcoins.core.policy.Policy
import org.bitcoins.core.serializers.script.ScriptParser
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction._
@ -28,28 +30,30 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with
val scriptPubKey = BitcoinjConversions.toScriptPubKey(BitcoinJTestUtil.multiSigScript)
"TransactionSignatureSerializer" must "serialize a transaction for SIGHASH_ALL correctly" in {
val spendingTx = Transaction(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize())
spendingTx.hex must be (BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
val sigBytes : Seq[Byte] = TransactionSignatureSerializer.serializeForSignature(spendingTx,UInt32.zero,scriptPubKey.asm,SIGHASH_ALL.defaultValue)
val sigBytes : Seq[Byte] = TransactionSignatureSerializer.serializeForSignature(spendingTx,UInt32.zero,scriptPubKey.asm,HashType.sigHashAll)
val bitcoinjSerialization = BitcoinSUtil.encodeHex(
BitcoinJSignatureSerialization.serializeForSignature(BitcoinJTestUtil.multiSigTransaction,0,
BitcoinJTestUtil.multiSigScript.getProgram(),SIGHASH_ALL.byte)
BitcoinJTestUtil.multiSigScript.getProgram(),HashType.sigHashAllByte)
)
BitcoinSUtil.encodeHex(sigBytes) must be (bitcoinjSerialization)
}
it must "hash a tranasction with SIGHASH_ALL correctly" in {
it must "hash a tranasction with SIGHASH_ALL correfctly" in {
val spendingTx = Transaction(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize())
spendingTx.hex must be (BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
val bitcoinsTxSigHash = TransactionSignatureSerializer.hashForSignature(spendingTx,UInt32.zero,scriptPubKey.asm,SIGHASH_ALL.defaultValue)
val bitcoinsTxSigHash = TransactionSignatureSerializer.hashForSignature(spendingTx,UInt32.zero,scriptPubKey.asm,HashType.sigHashAll)
val bitcoinjTxSigHash = BitcoinSUtil.encodeHex(
BitcoinJSignatureSerialization.hashForSignature(BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram(),SIGHASH_ALL.byte)
BitcoinJSignatureSerialization.hashForSignature(BitcoinJTestUtil.multiSigTransaction,0,
BitcoinJTestUtil.multiSigScript.getProgram(),HashType.sigHashAllByte)
)
bitcoinsTxSigHash.hex must be (bitcoinjTxSigHash)
}
@ -60,10 +64,10 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with
spendingTx.hex must be (BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
val serializedTxForSig : Seq[Byte] =
TransactionSignatureSerializer.serializeForSignature(spendingTx,UInt32.zero,scriptPubKey.asm,SIGHASH_SINGLE.defaultValue)
TransactionSignatureSerializer.serializeForSignature(spendingTx,UInt32.zero,scriptPubKey.asm,HashType.sigHashSingle)
val bitcoinjSigSerialization = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.serializeForSignature(
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,SIGHASH_SINGLE.byte))
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,HashType.sigHashSingleByte))
BitcoinSUtil.encodeHex(serializedTxForSig) must be (bitcoinjSigSerialization)
}
@ -73,10 +77,10 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with
spendingTx.hex must be (BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
val hashedTxForSig =
TransactionSignatureSerializer.hashForSignature(spendingTx,UInt32.zero,scriptPubKey.asm,SIGHASH_SINGLE.defaultValue)
TransactionSignatureSerializer.hashForSignature(spendingTx,UInt32.zero,scriptPubKey.asm,HashType.sigHashSingle)
val bitcoinjSigSerialization = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.hashForSignature(
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,SIGHASH_SINGLE.byte))
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,HashType.sigHashSingleByte))
hashedTxForSig.hex must be (bitcoinjSigSerialization)
}
@ -86,10 +90,10 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with
spendingTx.hex must be (BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
val serializedTxForSig : Seq[Byte] =
TransactionSignatureSerializer.serializeForSignature(spendingTx,UInt32.zero,scriptPubKey.asm,SIGHASH_NONE.defaultValue)
TransactionSignatureSerializer.serializeForSignature(spendingTx,UInt32.zero,scriptPubKey.asm,HashType.sigHashNone)
val bitcoinjSigSerialization = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.serializeForSignature(
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,SIGHASH_NONE.byte))
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,HashType.sigHashNoneByte))
BitcoinSUtil.encodeHex(serializedTxForSig) must be (bitcoinjSigSerialization)
}
@ -99,10 +103,10 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with
spendingTx.hex must be (BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
val hashedTxForSig =
TransactionSignatureSerializer.hashForSignature(spendingTx,UInt32.zero,scriptPubKey.asm,SIGHASH_NONE.defaultValue)
TransactionSignatureSerializer.hashForSignature(spendingTx,UInt32.zero,scriptPubKey.asm,HashType.sigHashNone)
val bitcoinjSigSerialization = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.hashForSignature(
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,SIGHASH_NONE.byte))
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,HashType.sigHashNoneByte))
hashedTxForSig.hex must be (bitcoinjSigSerialization)
}
@ -113,9 +117,10 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with
spendingTx.hex must be (BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
val serializedTxForSig : Seq[Byte] =
TransactionSignatureSerializer.serializeForSignature(spendingTx,UInt32.zero,scriptPubKey.asm, SIGHASH_ANYONECANPAY.defaultValue)
TransactionSignatureSerializer.serializeForSignature(spendingTx,UInt32.zero,scriptPubKey.asm,
HashType.sigHashAnyoneCanPay)
val bitcoinjSigSerialization = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.serializeForSignature(
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,SIGHASH_ANYONECANPAY.byte))
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,HashType.sigHashAnyoneCanPayByte))
BitcoinSUtil.encodeHex(serializedTxForSig) must be (bitcoinjSigSerialization)
@ -127,9 +132,9 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with
spendingTx.hex must be (BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
val hashedTxForSig =
TransactionSignatureSerializer.hashForSignature(spendingTx,UInt32.zero,scriptPubKey.asm, SIGHASH_ANYONECANPAY.defaultValue)
TransactionSignatureSerializer.hashForSignature(spendingTx,UInt32.zero,scriptPubKey.asm, HashType.sigHashAnyoneCanPay)
val bitcoinjSigSerialization = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.hashForSignature(
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,SIGHASH_ANYONECANPAY.byte))
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,HashType.sigHashAnyoneCanPayByte))
hashedTxForSig.hex must be (bitcoinjSigSerialization)
}
@ -140,9 +145,9 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with
spendingTx.hex must be (BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
val serializedTxForSig : Seq[Byte] =
TransactionSignatureSerializer.serializeForSignature(spendingTx,UInt32.zero,scriptPubKey.asm, SIGHASH_ALL_ANYONECANPAY.defaultValue)
TransactionSignatureSerializer.serializeForSignature(spendingTx,UInt32.zero,scriptPubKey.asm, HashType.sigHashAnyoneCanPay)
val bitcoinjSigSerialization = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.serializeForSignature(
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,SIGHASH_ALL_ANYONECANPAY.byte))
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,HashType.sigHashAnyoneCanPayByte))
BitcoinSUtil.encodeHex(serializedTxForSig) must be (bitcoinjSigSerialization)
}
@ -153,9 +158,11 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with
spendingTx.hex must be (BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
val serializedTxForSig : Seq[Byte] =
TransactionSignatureSerializer.serializeForSignature(spendingTx,UInt32.zero,scriptPubKey.asm, SIGHASH_SINGLE_ANYONECANPAY.defaultValue)
TransactionSignatureSerializer.serializeForSignature(spendingTx,UInt32.zero,scriptPubKey.asm,
HashType.sigHashSingleAnyoneCanPay)
val bitcoinjSigSerialization = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.serializeForSignature(
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,SIGHASH_SINGLE_ANYONECANPAY.byte))
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,
HashType.sigHashSingleAnyoneCanPayByte))
BitcoinSUtil.encodeHex(serializedTxForSig) must be (bitcoinjSigSerialization)
}
@ -166,9 +173,9 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with
spendingTx.hex must be (BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
val serializedTxForSig : Seq[Byte] =
TransactionSignatureSerializer.serializeForSignature(spendingTx,UInt32.zero,scriptPubKey.asm, SIGHASH_NONE_ANYONECANPAY.defaultValue)
TransactionSignatureSerializer.serializeForSignature(spendingTx,UInt32.zero,scriptPubKey.asm, HashType.sigHashNoneAnyoneCanPay)
val bitcoinjSigSerialization = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.serializeForSignature(
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,SIGHASH_NONE_ANYONECANPAY.byte))
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,HashType.sigHashNoneAnyoneCanPayByte))
BitcoinSUtil.encodeHex(serializedTxForSig) must be (bitcoinjSigSerialization)
}
@ -179,9 +186,9 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with
spendingTx.hex must be (BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
val hashedTxForSig =
TransactionSignatureSerializer.hashForSignature(spendingTx,UInt32.zero,scriptPubKey.asm, SIGHASH_ALL_ANYONECANPAY.defaultValue)
TransactionSignatureSerializer.hashForSignature(spendingTx,UInt32.zero,scriptPubKey.asm, HashType.sigHashAllAnyoneCanPay)
val bitcoinjTxHashForSig = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.hashForSignature(
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram, SIGHASH_ALL_ANYONECANPAY.byte))
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram, HashType.sigHashAllAnyoneCanPayByte))
hashedTxForSig.hex must be (bitcoinjTxHashForSig)
}
@ -192,9 +199,9 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with
spendingTx.hex must be (BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
val hashedTxForSig =
TransactionSignatureSerializer.hashForSignature(spendingTx,UInt32.zero,scriptPubKey.asm, SIGHASH_SINGLE_ANYONECANPAY.defaultValue)
TransactionSignatureSerializer.hashForSignature(spendingTx,UInt32.zero,scriptPubKey.asm, HashType.sigHashSingleAnyoneCanPay)
val bitcoinjTxHashForSig = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.hashForSignature(
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,SIGHASH_SINGLE_ANYONECANPAY.byte))
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,HashType.sigHashSingleAnyoneCanPayByte))
hashedTxForSig.hex must be (bitcoinjTxHashForSig)
}
@ -205,14 +212,13 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with
spendingTx.hex must be (BitcoinSUtil.encodeHex(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize()))
val hashedTxForSig =
TransactionSignatureSerializer.hashForSignature(spendingTx,UInt32.zero,scriptPubKey.asm, SIGHASH_NONE_ANYONECANPAY.defaultValue)
TransactionSignatureSerializer.hashForSignature(spendingTx,UInt32.zero,scriptPubKey.asm, HashType.sigHashNoneAnyoneCanPay)
val bitcoinjTxHashForSig = BitcoinSUtil.encodeHex(BitcoinJSignatureSerialization.hashForSignature(
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,SIGHASH_NONE_ANYONECANPAY.byte))
BitcoinJTestUtil.multiSigTransaction,0,BitcoinJTestUtil.multiSigScript.getProgram,HashType.sigHashNoneAnyoneCanPayByte))
hashedTxForSig.hex must be (bitcoinjTxHashForSig)
}
it must "serialize a simple transaction with one p2pkh input for signing" in {
val (spendingTx,spendingInput, inputIndex, creditingOutput) =
TransactionTestUtil.transactionWithSpendingInputAndCreditingOutput
@ -233,7 +239,7 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with
val signature : Array[Byte] = scriptSig.getChunks().get(0).data
val bitcoinjSerializeForSig : Seq[Byte] =
BitcoinJSignatureSerialization.serializeForSignature(bitcoinjTx,inputIndex.toInt,
parentOutput.getScriptBytes, SIGHASH_ALL.byte)
parentOutput.getScriptBytes, HashType.sigHashAllByte)
val hashType = HashType(spendingInput.scriptSignature.signatures.head.bytes.last)
@ -259,7 +265,7 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with
val bitcoinjSerializeForSig : Seq[Byte] =
BitcoinJSignatureSerialization.serializeForSignature(bitcoinjTx,inputIndex.toInt,
parentOutput.getScriptBytes, SIGHASH_ALL.byte)
parentOutput.getScriptBytes, HashType.sigHashAllByte)
val hashType = HashType(spendingInput.scriptSignature.signatures.head.bytes.last)
val serializedTxForSig : String = BitcoinSUtil.encodeHex(
@ -271,6 +277,7 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with
}
it must "serialize a transaction that has a p2sh input script" in {
val (spendingTx,spendingInput,inputIndex,creditingOutput) =
TransactionTestUtil.p2shTransactionWithSpendingInputAndCreditingOutput
@ -282,7 +289,7 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with
val bitcoinjTx = BitcoinjConversions.transaction(spendingTx)
val hashType = HashType(spendingInput.scriptSignature.signatures.head.bytes.last)
val bitcoinjHashForSig : Seq[Byte] = BitcoinJSignatureSerialization.serializeForSignature(
bitcoinjTx, inputIndex.toInt, creditingOutput.scriptPubKey.bytes.toArray, spendingInput.scriptSignature.signatures.head.bytes.last
bitcoinjTx, inputIndex.toInt, creditingOutput.scriptPubKey.asm.flatMap(_.bytes).toArray, spendingInput.scriptSignature.signatures.head.bytes.last
)
val hashedTxForSig : String = BitcoinSUtil.encodeHex(
TransactionSignatureSerializer.serializeForSignature(spendingTx,inputIndex,creditingOutput.scriptPubKey.asm,hashType
@ -303,7 +310,7 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with
val bitcoinjTx = BitcoinjConversions.transaction(spendingTx)
val hashType = HashType(spendingInput.scriptSignature.signatures.head.bytes.last)
val bitcoinjHashForSig : Seq[Byte] = BitcoinJSignatureSerialization.hashForSignature(
bitcoinjTx, inputIndex.toInt, creditingOutput.scriptPubKey.bytes.toArray, spendingInput.scriptSignature.signatures.head.bytes.last
bitcoinjTx, inputIndex.toInt, creditingOutput.scriptPubKey.asm.flatMap(_.bytes).toArray, spendingInput.scriptSignature.signatures.head.bytes.last
)
val hashedTxForSig =
TransactionSignatureSerializer.hashForSignature(spendingTx,inputIndex,creditingOutput.scriptPubKey.asm, hashType)
@ -328,11 +335,11 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with
val bitcoinjTx = BitcoinjConversions.transaction(spendingTx)
val bitcoinjHashForSig : Seq[Byte] = BitcoinJSignatureSerialization.hashForSignature(
bitcoinjTx, inputIndex.toInt, scriptPubKey.bytes.toArray, SIGHASH_ALL.byte
bitcoinjTx, inputIndex.toInt, scriptPubKey.asm.flatMap(_.bytes).toArray, HashType.sigHashAllByte
)
val hashedTxForSig =
TransactionSignatureSerializer.hashForSignature(spendingTx,inputIndex,scriptPubKey.asm,SIGHASH_ALL.defaultValue)
TransactionSignatureSerializer.hashForSignature(spendingTx,inputIndex,scriptPubKey.asm,HashType.sigHashAll)
hashedTxForSig.hex must be (BitcoinSUtil.encodeHex(bitcoinjHashForSig))
}
@ -343,13 +350,11 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with
val rawTx = "01000000020001000000000000000000000000000000000000000000000000000000000000000000004948304502203a0f5f0e1f2bdbcd04db3061d18f3af70e07f4f467cbc1b8116f267025f5360b022100c792b6e215afc5afc721a351ec413e714305cb749aae3d7fee76621313418df101010000000002000000000000000000000000000000000000000000000000000000000000000000004847304402205f7530653eea9b38699e476320ab135b74771e1c48b81a5d041e2ca84b9be7a802200ac8d1f40fb026674fe5a5edd3dea715c27baa9baca51ed45ea750ac9dc0a55e81ffffffff010100000000000000015100000000"
val inputIndex = UInt32.zero
val spendingTx = Transaction(rawTx)
require(spendingTx.inputs(inputIndex.toInt).scriptSignature.hex == "48304502203a0f5f0e1f2bdbcd04db3061d18f3af70e07f4f467cbc1b8116f267025f5360b022100c792b6e215afc5afc721a351ec413e714305cb749aae3d7fee76621313418df101",
"Script sig not right")
val scriptPubKeyFromString = ScriptParser.fromString("0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG")
val scriptPubKey = ScriptPubKey.fromAsm(scriptPubKeyFromString)
val serializedTxForSig : String = BitcoinSUtil.encodeHex(
TransactionSignatureSerializer.serializeForSignature(spendingTx,inputIndex,scriptPubKey.asm, SIGHASH_ALL.defaultValue
TransactionSignatureSerializer.serializeForSignature(spendingTx,inputIndex,scriptPubKey.asm, HashType.sigHashAll
))
//serialization is from bitcoin core
@ -368,11 +373,12 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with
val bitcoinjTx = BitcoinjConversions.transaction(spendingTx)
val bitcoinjHashForSig : Seq[Byte] = BitcoinJSignatureSerialization.hashForSignature(
bitcoinjTx, inputIndex.toInt, scriptPubKey.bytes.toArray, SIGHASH_ALL_ANYONECANPAY.byte
bitcoinjTx, inputIndex.toInt, scriptPubKey.bytes.toArray, HashType.sigHashAllAnyoneCanPayByte
)
val hashedTxForSig =
TransactionSignatureSerializer.hashForSignature(spendingTx,inputIndex,scriptPubKey.asm,SIGHASH_ALL_ANYONECANPAY.defaultValue)
TransactionSignatureSerializer.hashForSignature(spendingTx,inputIndex,scriptPubKey.asm,HashType.sigHashAllAnyoneCanPay)
//hash is from bitcoin core
hashedTxForSig.hex must be ("57f5a54d548db73fa8ef7a43d011120f9935fe792f0a0630d28ee70b4c72a7e8")
}
@ -387,7 +393,7 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with
val scriptPubKey = ScriptPubKey.fromAsm(scriptPubKeyFromString)
val serializedTxForSig : String = BitcoinSUtil.encodeHex(
TransactionSignatureSerializer.serializeForSignature(spendingTx,inputIndex,scriptPubKey.asm,SIGHASH_SINGLE.defaultValue))
TransactionSignatureSerializer.serializeForSignature(spendingTx,inputIndex,scriptPubKey.asm,HashType.sigHashSingle))
//serialization is from bitcoin core
serializedTxForSig must be ("010000000370ac0a1ae588aaf284c308d67ca92c69a39e2db81337e563bf40c59da0a5cf63000000001976a914dcf72c4fd02f5a987cf9b02f2fabfcac3341a87d88acffffffff7d815b6447e35fbea097e00e028fb7dfbad4f3f0987b4734676c84f3fcd0e8040100000000000000003f1f097333e4d46d51f5e77b53264db8f7f5d2e18217e1099957d0f5af7713ee0100000000000000000180841e00000000001976a914bfb282c70c4191f45b5a6665cad1682f2c9cfdfb88ac0000000003000000")
@ -404,7 +410,7 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with
val scriptPubKey = ScriptPubKey.fromAsm(scriptPubKeyFromString)
val serializedTxForSig : String = BitcoinSUtil.encodeHex(
TransactionSignatureSerializer.serializeForSignature(spendingTx,inputIndex,scriptPubKey.asm,SIGHASH_SINGLE.defaultValue))
TransactionSignatureSerializer.serializeForSignature(spendingTx,inputIndex,scriptPubKey.asm,HashType.sigHashSingle))
//serialization is from bitcoin core
serializedTxForSig must be ("010000000370ac0a1ae588aaf284c308d67ca92c69a39e2db81337e563bf40c59da0a5cf630000000000000000007d815b6447e35fbea097e00e028fb7dfbad4f3f0987b4734676c84f3fcd0e8040100000000000000003f1f097333e4d46d51f5e77b53264db8f7f5d2e18217e1099957d0f5af7713ee010000001976a914dcf72c4fd02f5a987cf9b02f2fabfcac3341a87d88acffffffff03ffffffffffffffff00ffffffffffffffff00e0fd1c00000000001976a91443c52850606c872403c0601e69fa34b26f62db4a88ac0000000003000000")
@ -419,7 +425,7 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with
val scriptPubKeyFromString = ScriptParser.fromString("0x4c 0xae 0x606563686f2022553246736447566b58312b5a536e587574356542793066794778625456415675534a6c376a6a334878416945325364667657734f53474f36633338584d7439435c6e543249584967306a486956304f376e775236644546673d3d22203e20743b206f70656e73736c20656e63202d7061737320706173733a5b314a564d7751432d707269766b65792d6865785d202d64202d6165732d3235362d636263202d61202d696e207460 DROP DUP HASH160 0x14 0xbfd7436b6265aa9de506f8a994f881ff08cc2872 EQUALVERIFY CHECKSIG")
val scriptPubKey = ScriptPubKey.fromAsm(scriptPubKeyFromString)
val serializedTxForSig : String = BitcoinSUtil.encodeHex(
TransactionSignatureSerializer.serializeForSignature(spendingTx,inputIndex,scriptPubKey.asm,SIGHASH_ALL.defaultValue))
TransactionSignatureSerializer.serializeForSignature(spendingTx,inputIndex,scriptPubKey.asm,HashType.sigHashAll))
//serialization is from bitcoin core
serializedTxForSig must be ("0100000001482f7a028730a233ac9b48411a8edfb107b749e61faf7531f4257ad95d0a51c500000000ca4cae606563686f2022553246736447566b58312b5a536e587574356542793066794778625456415675534a6c376a6a334878416945325364667657734f53474f36633338584d7439435c6e543249584967306a486956304f376e775236644546673d3d22203e20743b206f70656e73736c20656e63202d7061737320706173733a5b314a564d7751432d707269766b65792d6865785d202d64202d6165732d3235362d636263202d61202d696e2074607576a914bfd7436b6265aa9de506f8a994f881ff08cc287288acffffffff0180969800000000001976a914e336d0017a9d28de99d16472f6ca6d5a3a8ebc9988ac0000000001000000")
}
@ -434,9 +440,29 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with
val scriptPubKey = ScriptPubKey.fromAsm(scriptPubKeyFromString)
val serializedTxForSig : String = BitcoinSUtil.encodeHex(
TransactionSignatureSerializer.serializeForSignature(spendingTx,inputIndex,scriptPubKey.asm,SIGHASH_ALL.defaultValue))
TransactionSignatureSerializer.serializeForSignature(spendingTx,inputIndex,scriptPubKey.asm,HashType.sigHashAll))
serializedTxForSig must be ("01000000020001000000000000000000000000000000000000000000000000000000000000000000002321035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efcac01000000000200000000000000000000000000000000000000000000000000000000000000000000000100000001010000000000000001510000000001000000")
}
it must "serialize a basic p2wsh transaction correctly" in {
val expected = "01000000ff78d7a91d1d9f2defd4b9d7e17c8b2182565453e83ceaacc78dd2ee095681f13bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e7066504490491d88b9f0dc24d271f0f67179bce5914afe1ac0f83f6cd205f8b807436d6f0000000043410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac0100000000000000ffffffffe5d196bfb21caca9dbd654cafb3b4dc0c4882c8927d2eb300d9539dd0b9342280000000001000000"
val hex = "0100000000010190491d88b9f0dc24d271f0f67179bce5914afe1ac0f83f6cd205f8b807436d6f0000000000ffffffff010100000000000000000247304402200d461c140cfdfcf36b94961db57ae8c18d1cb80e9d95a9e47ac22470c1bf125502201c8dc1cbfef6a3ef90acbbb992ca22fe9466ee6f9d4898eda277a7ac3ab4b2510143410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac00000000"
val spendingTx: WitnessTransaction = WitnessTransaction(hex)
spendingTx.isInstanceOf[WitnessTransaction] must be (true)
spendingTx.hex must be (hex)
val inputIndex = UInt32.zero
val witnessRedeemScript = ScriptPubKey("43410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac")
val amount = Satoshis(Int64(1))
val witnessStack = Seq("304402200d461c140cfdfcf36b94961db57ae8c18d1cb80e9d95a9e47ac22470c1bf125502201c8dc1cbfef6a3ef90acbbb992ca22fe9466ee6f9d4898eda277a7ac3ab4b25101",
"410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac")
val serializedForSig = TransactionSignatureSerializer.serializeForSignature(spendingTx,inputIndex, witnessRedeemScript.asm,
HashType.sigHashAll,amount)
BitcoinSUtil.encodeHex(serializedForSig) must be (expected)
}
}

View file

@ -55,7 +55,7 @@ class BitcoinAddressTest extends FlatSpec with MustMatchers {
it must "encode a scriptPubKey to an address" in {
//redeemScript from https://en.bitcoin.it/wiki/Pay_to_script_hash
val hex = "5141042f90074d7a5bf30c72cf3a8dfd1381bdbd30407010e878f3a11269d5f74a58788505cdca22ea6eab7cfb40dc0e07aba200424ab0d79122a653ad0c7ec9896bdf51ae"
val hex = "455141042f90074d7a5bf30c72cf3a8dfd1381bdbd30407010e878f3a11269d5f74a58788505cdca22ea6eab7cfb40dc0e07aba200424ab0d79122a653ad0c7ec9896bdf51ae"
val scriptPubKey = ScriptPubKey(hex)
val addr = P2SHAddress.encodeScriptPubKeyToAddress(scriptPubKey,MainNet)
addr must be (BitcoinAddress("3P14159f73E4gFr7JterCCQh9QjiTjiZrG"))

View file

@ -72,21 +72,26 @@ class CompactSizeUIntTest extends FlatSpec with MustMatchers {
it must "parse a variable length integer the same from a tx input and a script sig" in {
CompactSizeUInt.parseCompactSizeUInt(TestUtil.txInput.head.scriptSignature) must be (TestUtil.txInput.head.scriptSigCompactSizeUInt)
CompactSizeUInt.parseCompactSizeUInt(TestUtil.txInput.head.scriptSignature.bytes) must be (TestUtil.txInput.head.scriptSignature.compactSizeUInt)
}
it must "parse multiple variable length integers correctly for a multi input tx" in {
CompactSizeUInt.parseCompactSizeUInt(TestUtil.txInputs.head.scriptSignature) must be (TestUtil.txInputs.head.scriptSigCompactSizeUInt)
CompactSizeUInt.parseCompactSizeUInt(TestUtil.txInputs(1).scriptSignature) must be (TestUtil.txInputs(1).scriptSigCompactSizeUInt)
CompactSizeUInt.parseCompactSizeUInt(TestUtil.txInputs.head.scriptSignature.bytes) must be (TestUtil.txInputs.head.scriptSignature.compactSizeUInt)
CompactSizeUInt.parseCompactSizeUInt(TestUtil.txInputs(1).scriptSignature.bytes) must be (TestUtil.txInputs(1).scriptSignature.compactSizeUInt)
}
it must "parse the variable length integer of the empty script" in {
CompactSizeUInt.parseCompactSizeUInt(ScriptSignature.empty) must be (CompactSizeUInt(UInt64.zero,1))
CompactSizeUInt.parseCompactSizeUInt(ScriptSignature.empty) must be (CompactSizeUInt(UInt64.one,1))
}
it must "parse variable length integer of script sig at least 0xffff bytes in length, and greater than 0xffffffff" in {
CompactSizeUInt.parseCompactSizeUInt(ScriptSignature(TestUtil.rawP2shInputScriptLargeSignature * 50)) must be (CompactSizeUInt(UInt64(30300), 3))
CompactSizeUInt.parseCompactSizeUInt(ScriptSignature(TestUtil.rawP2shInputScriptLargeSignature * 120)) must be (CompactSizeUInt(UInt64(72720), 5))
val c = CompactSizeUInt.calculateCompactSizeUInt(_: String)
val s1NoCmpct = TestUtil.rawP2shInputScriptLargeSignature * 50
val s2NoCmpct = TestUtil.rawP2shInputScriptLargeSignature * 120
val s1 = c(s1NoCmpct).hex + s1NoCmpct
val s2 = c(s2NoCmpct).hex + s2NoCmpct
CompactSizeUInt.parseCompactSizeUInt(ScriptSignature(s1)) must be (CompactSizeUInt(UInt64(30453), 3))
CompactSizeUInt.parseCompactSizeUInt(ScriptSignature(s2)) must be (CompactSizeUInt(UInt64(73085), 5))
}
it must "parse 32 bit number and 64 bit number as compactsizeuints" in {

View file

@ -15,9 +15,9 @@ class ChainParamsTest extends FlatSpec with MustMatchers with BitcoinSLogger {
val genesisBlock = MainNetChainParams.genesisBlock
val genesisTransaction = genesisBlock.transactions.head
val expectedGenesisScriptSig = ScriptSignature("04FFFF001D0104455468652054696D65732030332F4A616E2F32303039204368616E63656C6C6F72206F6E206272696E6B206F66207365636F6E64206261696C6F757420666F722062616E6B73".toLowerCase())
val expectedGenesisScriptSig = ScriptSignature("4D04FFFF001D0104455468652054696D65732030332F4A616E2F32303039204368616E63656C6C6F72206F6E206272696E6B206F66207365636F6E64206261696C6F757420666F722062616E6B73".toLowerCase())
val expectedGenesisInput = TransactionInput(expectedGenesisScriptSig)
val expectedGenesisScriptPubKey = ScriptPubKey("4104678AFDB0FE5548271967F1A67130B7105CD6A828E03909A67962E0EA1F61DEB649F6BC3F4CEF38C4F35504E51EC112DE5C384DF7BA0B8D578A4C702B6BF11D5FAC".toLowerCase)
val expectedGenesisScriptPubKey = ScriptPubKey("434104678AFDB0FE5548271967F1A67130B7105CD6A828E03909A67962E0EA1F61DEB649F6BC3F4CEF38C4F35504E51EC112DE5C384DF7BA0B8D578A4C702B6BF11D5FAC".toLowerCase)
val expectedGenesisOutput = TransactionOutput(Satoshis(Int64(5000000000L)),expectedGenesisScriptPubKey)
"ChainParams" must "generate correct block hex for genesis block" in {
val hex = "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e6" +
@ -41,7 +41,7 @@ class ChainParamsTest extends FlatSpec with MustMatchers with BitcoinSLogger {
it must "generate the input correctly for the genesis transaction's input" in {
val input = genesisBlock.transactions.head.inputs.head
input must be (expectedGenesisInput)
input.hex must be ("010000000000000000000000000000000000000000000000000000000000000000FFFFFFFF4D".toLowerCase
input.hex must be ("010000000000000000000000000000000000000000000000000000000000000000FFFFFFFF".toLowerCase
+ expectedGenesisScriptSig.hex + "FFFFFFFF".toLowerCase )
}
@ -54,7 +54,7 @@ class ChainParamsTest extends FlatSpec with MustMatchers with BitcoinSLogger {
val output = genesisTransaction.outputs.head
output.value must be (Satoshis(Int64(5000000000L)))
output.scriptPubKey.hex must be (expectedGenesisScriptPubKey.hex)
output.hex must be ("0100F2052A0100000043".toLowerCase + expectedGenesisScriptPubKey.hex)
output.hex must be ("0100F2052A01000000".toLowerCase + expectedGenesisScriptPubKey.hex)
}
it must "generate the correct txid for the genesis transaction" in {

View file

@ -1,6 +1,7 @@
package org.bitcoins.core.protocol.script
import org.bitcoins.core.crypto.ECPublicKey
import org.bitcoins.core.util.TestUtil
import org.scalatest.{FlatSpec, MustMatchers}
/**
@ -9,10 +10,8 @@ import org.scalatest.{FlatSpec, MustMatchers}
class MultiSignatureScriptPubKeyTest extends FlatSpec with MustMatchers {
"MultiSignatureScriptPubKey" must "derive the amount of required signatures from a multisignature script" in {
val multiSigRawScriptPubKeyHex = "5221025878e270211662a27181cf" +
"4d6ad4d2cf0e69a98a3815c086f587c7e9388d87182103fc85980e3fac1f3d" +
"8a5c3223c3ef5bffc1bd42d2cc42add8c3899cc66e7f1906210215b5bd0508" +
"69166a70a7341b4f216e268b7c6c7504576dcea2cce7d11cc9a35f53ae"
val multiSigRawScriptPubKeyHex = TestUtil.multiSigScriptPubKeyHex
val scriptPubKey = ScriptPubKey(multiSigRawScriptPubKeyHex)
val multiSigScriptPubKey : MultiSignatureScriptPubKey = scriptPubKey match {
case s : MultiSignatureScriptPubKey => s
@ -24,7 +23,7 @@ class MultiSignatureScriptPubKeyTest extends FlatSpec with MustMatchers {
}
it must "derive the public keys encoded inside of a multisignature script" in {
val multiSigRawScriptPubKeyHex = "5221025878e270211662a27181cf" +
val multiSigRawScriptPubKeyHex = "695221025878e270211662a27181cf" +
"4d6ad4d2cf0e69a98a3815c086f587c7e9388d87182103fc85980e3fac1f3d" +
"8a5c3223c3ef5bffc1bd42d2cc42add8c3899cc66e7f1906210215b5bd0508" +
"69166a70a7341b4f216e268b7c6c7504576dcea2cce7d11cc9a35f53ae"
@ -43,7 +42,7 @@ class MultiSignatureScriptPubKeyTest extends FlatSpec with MustMatchers {
}
it must "find the required signatures from a multisignature scriptPubKey using an OP_CHECKMULTISIGVERFIY" in {
val multiSigRawScriptPubKeyHex = "5221025878e270211662a27181cf" +
val multiSigRawScriptPubKeyHex = "695221025878e270211662a27181cf" +
"4d6ad4d2cf0e69a98a3815c086f587c7e9388d87182103fc85980e3fac1f3d" +
"8a5c3223c3ef5bffc1bd42d2cc42add8c3899cc66e7f1906210215b5bd0508" +
"69166a70a7341b4f216e268b7c6c7504576dcea2cce7d11cc9a35f53af"

View file

@ -15,7 +15,7 @@ class P2PKHScriptSignatureTest extends FlatSpec with MustMatchers {
case s : P2PKHScriptSignature => s
case _ => throw new RuntimeException("Must be p2pkh scriptSig")
}
HashType.fromBytes(Seq(TestUtil.p2pkhScriptSig.signatures.head.bytes.last)) must be (SIGHASH_ALL.defaultValue)
HashType.fromBytes(Seq(TestUtil.p2pkhScriptSig.signatures.head.bytes.last)) must be (HashType.sigHashAll)
}
it must "be able to identify the signature in a p2pkh scriptSig" in {

View file

@ -1,17 +1,27 @@
package org.bitcoins.core.protocol.script
import org.bitcoins.core.gen.ScriptGenerators
import org.bitcoins.core.util.BitcoinSLogger
import org.scalacheck.{Prop, Properties}
/**
* Created by chris on 6/24/16.
*/
class P2SHScriptSignatureSpec extends Properties("P2SHScriptSignatureSpec") {
class P2SHScriptSignatureSpec extends Properties("P2SHScriptSignatureSpec") with BitcoinSLogger {
property("Symmetrical serialization") =
Prop.forAll(ScriptGenerators.p2shScriptSignature) { p2shScriptSig =>
logger.info("P2shScriptSig: " + p2shScriptSig)
P2SHScriptSignature(p2shScriptSig.hex) == p2shScriptSig
}
property("place a witness scriptPubKey in a p2shScriptSig, then extract the witScriptPubKey again") =
Prop.forAll(ScriptGenerators.witnessScriptPubKeyV0) { case (witScriptPubKey,privKeys) =>
val p2shScriptSig = P2SHScriptSignature(witScriptPubKey)
p2shScriptSig.redeemScript == witScriptPubKey
p2shScriptSig.scriptSignatureNoRedeemScript == EmptyScriptSignature
}
}

View file

@ -17,9 +17,9 @@ class ScriptPubKeyFactoryTest extends FlatSpec with MustMatchers {
scriptPubKeyFromBytes must be (scriptPubKeyFromHex)
}
it must "create a scriptPubKey from an empty string" in {
val scriptPubKey = ScriptPubKey("")
scriptPubKey.hex must be ("")
it must "create a scriptPubKey from an empty scriptpubkey's hex format" in {
val scriptPubKey = ScriptPubKey("00")
scriptPubKey.hex must be ("00")
}
it must "create a p2pk scriptPubKey from its hexadecimal representation" in {
@ -34,10 +34,7 @@ class ScriptPubKeyFactoryTest extends FlatSpec with MustMatchers {
}
it must "create a multisignature scriptPubKey from a script using OP_CHECKMULTISIGVERIFY" in {
val multiSigRawScriptPubKeyHex = "5221025878e270211662a27181cf" +
"4d6ad4d2cf0e69a98a3815c086f587c7e9388d87182103fc85980e3fac1f3d" +
"8a5c3223c3ef5bffc1bd42d2cc42add8c3899cc66e7f1906210215b5bd0508" +
"69166a70a7341b4f216e268b7c6c7504576dcea2cce7d11cc9a35f53af"
val multiSigRawScriptPubKeyHex = "695221025878e270211662a27181cf4d6ad4d2cf0e69a98a3815c086f587c7e9388d87182103fc85980e3fac1f3d8a5c3223c3ef5bffc1bd42d2cc42add8c3899cc66e7f1906210215b5bd050869166a70a7341b4f216e268b7c6c7504576dcea2cce7d11cc9a35f53af"
val scriptPubKey = ScriptPubKey(multiSigRawScriptPubKeyHex)
val isMultiSigScriptPubKey : Boolean = scriptPubKey match {
case s : MultiSignatureScriptPubKey => true

View file

@ -1,10 +1,11 @@
package org.bitcoins.core.protocol.script
import org.bitcoins.core.crypto.ECPrivateKey
import org.bitcoins.core.gen.CryptoGenerators
import org.bitcoins.core.script.bitwise.OP_EQUALVERIFY
import org.bitcoins.core.script.constant.{ScriptNumber, BytesToPushOntoStack, ScriptConstant, ScriptToken}
import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.crypto.{OP_CHECKSIG, OP_CODESEPARATOR, OP_HASH160}
import org.bitcoins.core.script.locktime.{OP_CHECKSEQUENCEVERIFY, OP_CHECKLOCKTIMEVERIFY}
import org.bitcoins.core.script.locktime.{OP_CHECKLOCKTIMEVERIFY, OP_CHECKSEQUENCEVERIFY}
import org.bitcoins.core.script.stack.{OP_DROP, OP_DUP}
import org.bitcoins.core.util.{CryptoUtil, TestUtil}
import org.scalatest.{FlatSpec, MustMatchers}
@ -22,4 +23,15 @@ class ScriptPubKeyTest extends FlatSpec with MustMatchers {
"ScriptPubKey" must "give the expected asm from creating a scriptPubKey from hex" in {
scriptPubKey.asm must be (expectedAsm)
}
it must "determine if we have a witness program inside of the scriptPubKey" in {
val pubKeyHash = CryptoUtil.sha256Hash160(CryptoGenerators.publicKey.sample.get.bytes)
val witnessProgram = Seq(ScriptConstant(pubKeyHash.bytes))
val asm = OP_0 +: BytesToPushOntoStack(20) +: witnessProgram
val witnessScriptPubKey = WitnessScriptPubKey(asm)
witnessScriptPubKey.isDefined must be (true)
witnessScriptPubKey.get.witnessVersion must be (WitnessVersion0)
witnessScriptPubKey.get.witnessProgram must be (witnessProgram)
}
}

View file

@ -49,8 +49,4 @@ class ScriptSignatureFactoryTest extends FlatSpec with MustMatchers {
}
result must be (true)
}
}

View file

@ -4,9 +4,10 @@ package org.bitcoins.core.protocol.script
import org.bitcoins.core.crypto._
import org.bitcoins.core.number.Int32
import org.bitcoins.core.protocol.script.testprotocol.SignatureHashTestCase
import org.bitcoins.core.protocol.transaction.Transaction
import org.bitcoins.core.script.crypto.{HashType, SIGHASH_ALL, SIGHASH_SINGLE}
import org.bitcoins.core.serializers.script.RawScriptSignatureParser
import org.bitcoins.core.util.{BitcoinSUtil, TestUtil}
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil, TestUtil}
import org.scalatest.{FlatSpec, MustMatchers}
import spray.json._
@ -15,7 +16,7 @@ import scala.io.Source
/**
* Created by chris on 2/17/16.
*/
class ScriptSignatureTest extends FlatSpec with MustMatchers {
class ScriptSignatureTest extends FlatSpec with MustMatchers with BitcoinSLogger {
"ScriptSignature" must "find the digital signature for the transaction inside of a p2pkh script signature" in {
val scriptSig = ScriptSignature(TestUtil.rawScriptSig)
@ -24,7 +25,7 @@ class ScriptSignatureTest extends FlatSpec with MustMatchers {
it must "derive the signature hash type from the signature" in {
HashType(Seq(TestUtil.scriptSig.signatures.head.bytes.last)) must be (SIGHASH_ALL.defaultValue)
HashType(Seq(TestUtil.scriptSig.signatures.head.bytes.last)) must be (HashType.sigHashAll)
}
@ -53,26 +54,26 @@ class ScriptSignatureTest extends FlatSpec with MustMatchers {
))
}
it must "find the hash type for a p2sh script signature" in {
HashType(Seq(TestUtil.p2shInputScript2Of2.signatures.head.bytes.last)) must be (SIGHASH_ALL.defaultValue)
HashType(Seq(TestUtil.p2shInputScript2Of2.signatures.head.bytes.last)) must be (HashType.sigHashAll)
}
it must "find the digital signature and hash type for a SIGHASH_SINGLE" in {
TestUtil.p2shInputScriptSigHashSingle.signatures.head.hex must be ("3045022100dfcfafcea73d83e1c54d444a19fb30d17317f922c19e2ff92dcda65ad09cba24022001e7a805c5672c49b222c5f2f1e67bb01f87215fb69df184e7c16f66c1f87c2903")
HashType(TestUtil.p2shInputScriptSigHashSingle.signatures.head.bytes.last) must be (SIGHASH_SINGLE.defaultValue)
HashType(TestUtil.p2shInputScriptSigHashSingle.signatures.head.bytes.last) must be (HashType.sigHashSingle)
}
it must "find the hash type for the weird occurrence of hash type being 0 on the blockchain" in {
//from this tx https://btc.blockr.io/api/v1/tx/raw/c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73
val hex = "493046022100d23459d03ed7e9511a47d13292d3430a04627de6235b6e51a40f9cd386f2abe3022100e7d25b080f0bb8d8d5f878bba7d54ad2fda650ea8d158a33ee3cbd11768191fd004104b0e2c879e4daf7b9ab68350228c159766676a14f5815084ba166432aab46198d4cca98fa3e9981d0a90b2effc514b76279476550ba3663fdcaff94c38420e9d5"
val hex = "8c493046022100d23459d03ed7e9511a47d13292d3430a04627de6235b6e51a40f9cd386f2abe3022100e7d25b080f0bb8d8d5f878bba7d54ad2fda650ea8d158a33ee3cbd11768191fd004104b0e2c879e4daf7b9ab68350228c159766676a14f5815084ba166432aab46198d4cca98fa3e9981d0a90b2effc514b76279476550ba3663fdcaff94c38420e9d5"
val scriptSig : ScriptSignature = RawScriptSignatureParser.read(hex)
HashType(scriptSig.signatures.head.bytes.last) must be (SIGHASH_ALL(Int32.zero))
}
it must "have an empty script signature" in {
EmptyScriptSignature.hex must be ("")
EmptyScriptSignature.bytes must be (Seq())
EmptyScriptSignature.asm must be (Seq())
EmptyScriptSignature.signatures must be (Seq())
EmptyScriptSignature.hex must be ("00")
EmptyScriptSignature.bytes must be (Seq(0.toByte))
EmptyScriptSignature.asm must be (Nil)
EmptyScriptSignature.signatures must be (Nil)
}
it must "create a p2pkh scriptSig" in {
@ -96,32 +97,39 @@ class ScriptSignatureTest extends FlatSpec with MustMatchers {
it must "read sighash.json and return result" in {
import org.bitcoins.core.protocol.script.testprotocol.SignatureHashTestCaseProtocol._
//["raw_transaction, script, input_index, hashType, signature_hash (result)"],
/* val lines =
/*
val lines =
"""
| [
| ["4ddaa680026ec4d8060640304b86823f1ac760c260cef81d85bd847952863d629a3002b54b0200000008526365636a656aab65457861fc6c24bdc760c8b2e906b6656edaf9ed22b5f50e1fb29ec076ceadd9e8ebcb6b000000000152ffffffff033ff04f00000000000551526a00657a1d900300000000002153af040000000003006a6300000000", "ab526a53acabab", 0, 1055317633, "7f21b62267ed52462e371a917eb3542569a4049b9dfca2de3c75872b39510b26"]
| ["a0aa3126041621a6dea5b800141aa696daf28408959dfb2df96095db9fa425ad3f427f2f6103000000015360290e9c6063fa26912c2e7fb6a0ad80f1c5fea1771d42f12976092e7a85a4229fdb6e890000000001abc109f6e47688ac0e4682988785744602b8c87228fcef0695085edf19088af1a9db126e93000000000665516aac536affffffff8fe53e0806e12dfd05d67ac68f4768fdbe23fc48ace22a5aa8ba04c96d58e2750300000009ac51abac63ab5153650524aa680455ce7b000000000000499e50030000000008636a00ac526563ac5051ee030000000003abacabd2b6fe000000000003516563910fb6b5", "65", 0, -1391424484, "48d6a1bd2cd9eec54eb866fc71209418a950402b5d7e52363bfb75c98e141175"]
| ]
|
""".stripMargin*/
""".stripMargin
*/
val source = Source.fromURL(this.getClass.getResource("/sighash.json"))
val lines = try source.getLines.filterNot(_.isEmpty).map(_.trim) mkString "\n" finally source.close()
val lines = try source.getLines.filterNot(_.isEmpty).map(_.trim).mkString("\n") finally source.close()
val testCases : Seq[SignatureHashTestCase] = lines.parseJson.convertTo[Seq[SignatureHashTestCase]]
for {
testCase <- testCases
} yield {
Transaction(testCase.transaction.hex) must be (testCase.transaction)
logger.info("Test case: " + testCase)
logger.info("Hash type num: " + testCase.hashTypeNum)
logger.info("Hash type: " + testCase.hashType)
val hashForSig = TransactionSignatureSerializer.hashForSignature(testCase.transaction, testCase.inputIndex, testCase.script.asm, testCase.hashType)
//the hash is returned with opposite endianness
logger.info("Expected hash: " + testCase.hash.hex)
val flipHash = BitcoinSUtil.flipEndianness(testCase.hash.hex)
hashForSig must be (DoubleSha256Digest(flipHash))
}
}
it must "create a cltvScriptSig with the correct underlying scriptSig" in {
val cltvScriptPubKey = CLTVScriptPubKey("04e71bbe57b17576a914da88dc82530f0a4d1327dcfe75cc60c44277532c88ac")
val cltvScriptPubKey = CLTVScriptPubKey("2004e71bbe57b17576a914da88dc82530f0a4d1327dcfe75cc60c44277532c88ac")
val pubKey = ECPublicKey("039ba48e162b1f47246f4ce9dc40f197fab7bde11da1b2fe9ac21113959e9f381b")
val sig = ECDigitalSignature("3045022100d71cfe32fa4545c5a0fd665b3701eb458a1bacbba868a05fa703fd1fa4b4f5c502204ee706334f976d0bee9b0f0ff919c1dfe9ba027993bf3e39fc03416ba4255b2401")
CLTVScriptSignature(cltvScriptPubKey, Seq(sig), Seq(pubKey)).scriptSig.isInstanceOf[P2PKHScriptSignature]
CLTVScriptSignature(cltvScriptPubKey, Seq(sig), Seq(pubKey)).scriptSig.isInstanceOf[P2PKHScriptSignature] must be (true)
}
}

View file

@ -15,8 +15,6 @@ class TransactionInputTest extends FlatSpec with MustMatchers {
"TransactionInput" must "define an empty transaction input" in {
EmptyTransactionInput.previousOutput must be (EmptyTransactionOutPoint)
EmptyTransactionInput.scriptSignature must be (EmptyScriptSignature)
EmptyTransactionInput.scriptSigCompactSizeUInt.num must be (UInt64.zero)
EmptyTransactionInput.scriptSigCompactSizeUInt.size must be (1)
EmptyTransactionInput.sequence must be (TransactionConstants.sequence)
}

View file

@ -6,8 +6,8 @@ import org.bitcoins.core.protocol.transaction.testprotocol.CoreTransactionTestCa
import org.bitcoins.core.script.ScriptProgram
import org.bitcoins.core.script.interpreter.ScriptInterpreter
import org.bitcoins.core.script.result.ScriptOk
import org.bitcoins.core.serializers.transaction.RawTransactionParser
import org.bitcoins.core.util.{BitcoinSLogger, TestUtil, BitcoinSUtil}
import org.bitcoins.core.serializers.transaction.RawBaseTransactionParser
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil, TestUtil}
import org.scalatest.{FlatSpec, MustMatchers}
import spray.json._
@ -22,7 +22,7 @@ class TransactionTest extends FlatSpec with MustMatchers with BitcoinSLogger {
"Transaction" must "derive the correct txid from the transaction contents" in {
//https://btc.blockr.io/api/v1/tx/raw/cddda897b0e9322937ee1f4fd5d6147d60f04a0f4d3b461e4f87066ac3918f2a
val tx = RawTransactionParser.read("01000000020df1e23002ddf909aec026b1cf0c3b6b7943c042f22e25dbd0441855e6b39ee900000000fdfd00004730440220028c02f14654a0cc12c7e3229adb09d5d35bebb6ba1057e39adb1b2706607b0d0220564fab12c6da3d5acef332406027a7ff1cbba980175ffd880e1ba1bf40598f6b014830450221009362f8d67b60773745e983d07ba10efbe566127e244b724385b2ca2e47292dda022033def393954c320653843555ddbe7679b35cc1cacfe1dad923977de8cd6cc6d7014c695221025e9adcc3d65c11346c8a6069d6ebf5b51b348d1d6dc4b95e67480c34dc0bc75c21030585b3c80f4964bf0820086feda57c8e49fa1eab925db7c04c985467973df96521037753a5e3e9c4717d3f81706b38a6fb82b5fb89d29e580d7b98a37fea8cdefcad53aeffffffffd11533b0f283fca193e361a91ca7ddfc66592e20fd6eaf5dc0f1ef5fed05818000000000fdfe0000483045022100b4062edd75b5b3117f28ba937ed737b10378f762d7d374afabf667180dedcc62022005d44c793a9d787197e12d5049da5e77a09046014219b31e9c6b89948f648f1701483045022100b3b0c0273fc2c531083701f723e03ea3d9111e4bbca33bdf5b175cec82dcab0802206650462db37f9b4fe78da250a3b339ab11e11d84ace8f1b7394a1f6db0960ba4014c695221025e9adcc3d65c11346c8a6069d6ebf5b51b348d1d6dc4b95e67480c34dc0bc75c21030585b3c80f4964bf0820086feda57c8e49fa1eab925db7c04c985467973df96521037753a5e3e9c4717d3f81706b38a6fb82b5fb89d29e580d7b98a37fea8cdefcad53aeffffffff02500f1e00000000001976a9147ecaa33ef3cd6169517e43188ad3c034db091f5e88ac204e0000000000001976a914321908115d8a138942f98b0b53f86c9a1848501a88ac00000000")
val tx = RawBaseTransactionParser.read("01000000020df1e23002ddf909aec026b1cf0c3b6b7943c042f22e25dbd0441855e6b39ee900000000fdfd00004730440220028c02f14654a0cc12c7e3229adb09d5d35bebb6ba1057e39adb1b2706607b0d0220564fab12c6da3d5acef332406027a7ff1cbba980175ffd880e1ba1bf40598f6b014830450221009362f8d67b60773745e983d07ba10efbe566127e244b724385b2ca2e47292dda022033def393954c320653843555ddbe7679b35cc1cacfe1dad923977de8cd6cc6d7014c695221025e9adcc3d65c11346c8a6069d6ebf5b51b348d1d6dc4b95e67480c34dc0bc75c21030585b3c80f4964bf0820086feda57c8e49fa1eab925db7c04c985467973df96521037753a5e3e9c4717d3f81706b38a6fb82b5fb89d29e580d7b98a37fea8cdefcad53aeffffffffd11533b0f283fca193e361a91ca7ddfc66592e20fd6eaf5dc0f1ef5fed05818000000000fdfe0000483045022100b4062edd75b5b3117f28ba937ed737b10378f762d7d374afabf667180dedcc62022005d44c793a9d787197e12d5049da5e77a09046014219b31e9c6b89948f648f1701483045022100b3b0c0273fc2c531083701f723e03ea3d9111e4bbca33bdf5b175cec82dcab0802206650462db37f9b4fe78da250a3b339ab11e11d84ace8f1b7394a1f6db0960ba4014c695221025e9adcc3d65c11346c8a6069d6ebf5b51b348d1d6dc4b95e67480c34dc0bc75c21030585b3c80f4964bf0820086feda57c8e49fa1eab925db7c04c985467973df96521037753a5e3e9c4717d3f81706b38a6fb82b5fb89d29e580d7b98a37fea8cdefcad53aeffffffff02500f1e00000000001976a9147ecaa33ef3cd6169517e43188ad3c034db091f5e88ac204e0000000000001976a914321908115d8a138942f98b0b53f86c9a1848501a88ac00000000")
tx.txId.hex must be (BitcoinSUtil.flipEndianness("cddda897b0e9322937ee1f4fd5d6147d60f04a0f4d3b461e4f87066ac3918f2a"))
}
@ -53,7 +53,7 @@ class TransactionTest extends FlatSpec with MustMatchers with BitcoinSLogger {
tx.hex must be (rawTx)
(Transaction(tx.hex) == tx) must be (true)
}
it must "read all of the tx_valid.json's contents and return ScriptOk" in {
@ -70,7 +70,7 @@ class TransactionTest extends FlatSpec with MustMatchers with BitcoinSLogger {
| "010000000200010000000000000000000000000000000000000000000000000000000000000000000049483045022100d180fd2eb9140aeb4210c9204d3f358766eb53842b2a9473db687fa24b12a3cc022079781799cd4f038b85135bbe49ec2b57f306b2bb17101b17f71f000fcab2b6fb01ffffffff0002000000000000000000000000000000000000000000000000000000000000000000004847304402205f7530653eea9b38699e476320ab135b74771e1c48b81a5d041e2ca84b9be7a802200ac8d1f40fb026674fe5a5edd3dea715c27baa9baca51ed45ea750ac9dc0a55e81ffffffff010100000000000000015100000000", "P2SH"]
|
|]
""".stripMargin*/
""".stripMargin */
val lines = try source.getLines.filterNot(_.isEmpty).map(_.trim) mkString "\n" finally source.close()
val json = lines.parseJson
val testCasesOpt : Seq[Option[CoreTransactionTestCase]] = json.convertTo[Seq[Option[CoreTransactionTestCase]]]
@ -97,18 +97,16 @@ class TransactionTest extends FlatSpec with MustMatchers with BitcoinSLogger {
}
}
it must "read all of the tx_invalid.json's contents and return a ScriptError" in {
val source = Source.fromURL(getClass.getResource("/tx_invalid.json"))
//use this to represent a single test case from script_valid.json
/* val lines =
"""
|[
[[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]],
"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0151ffffffff010000000000000000015100000000", "P2SH"]
|[[["6ca7ec7b1847f6bdbd737176050e6a08d66ccd55bb94ad24f4018024107a5827", 0, "0x41 0x043b640e983c9690a14c039a2037ecc3467b27a0dcd58f19d76c7bc118d09fec45adc5370a1c5bf8067ca9f5557a4cf885fdb0fe0dcc9c3a7137226106fbc779a5 CHECKSIG VERIFY 1"]],
|"010000000127587a10248001f424ad94bb55cd6cd6086a0e05767173bdbdf647187beca76c000000004948304502201b822ad10d6adc1a341ae8835be3f70a25201bbff31f59cbb9c5353a5f0eca18022100ea7b2f7074e9aa9cf70aa8d0ffee13e6b45dddabf1ab961bda378bcdb778fa4701ffffffff0100f2052a010000001976a914fc50c5907d86fed474ba5ce8b12a66e0a4c139d888ac00000000", "P2SH"]
|]
""".stripMargin*/
val lines = try source.getLines.filterNot(_.isEmpty).map(_.trim) mkString "\n" finally source.close()
@ -140,7 +138,7 @@ class TransactionTest extends FlatSpec with MustMatchers with BitcoinSLogger {
}
withClue(testCase.raw) {
//only one input is required to be false to make the transaction invalid
txInputValidity.contains(false) must be (true)
txInputValidity.exists(_ == false) must be (true)
}
}
}

View file

@ -0,0 +1,16 @@
package org.bitcoins.core.protocol.transaction
import org.bitcoins.core.gen.WitnessGenerators
import org.scalacheck.{Prop, Properties}
/**
* Created by chris on 11/28/16.
*/
class TransactionWitnessSpec extends Properties("TransactionWitnessSpec") {
property("serialization symmetry") = {
Prop.forAll(WitnessGenerators.transactionWitness) { witness =>
TransactionWitness(witness.hex,witness.witnesses.size) == witness
}
}
}

View file

@ -1,10 +1,11 @@
package org.bitcoins.core.script
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.script.constant.{OP_1, OP_0}
import org.bitcoins.core.protocol.script.SigVersionBase
import org.bitcoins.core.script.constant.{OP_0, OP_1}
import org.bitcoins.core.script.flag.ScriptFlagFactory
import org.bitcoins.core.util.TestUtil
import org.scalatest.{MustMatchers, FlatSpec}
import org.scalatest.{FlatSpec, MustMatchers}
/**
* Created by chris on 3/31/16.
@ -50,7 +51,8 @@ class ScriptProgramFactoryTest extends FlatSpec with MustMatchers {
it must "update the script program to the given stack and script" in {
val stack = List(OP_0)
val script = List(OP_1)
val program = ScriptProgram(TestUtil.transaction, TestUtil.scriptPubKey, UInt32.zero, stack,script, ScriptFlagFactory.empty)
val program = ScriptProgram(TestUtil.transaction, TestUtil.scriptPubKey, UInt32.zero,
stack,script, ScriptFlagFactory.empty)
program.stack must be (stack)
program.script must be (script)
}

View file

@ -1,7 +1,8 @@
package org.bitcoins.core.script.crypto
import org.bitcoins.core.currency.CurrencyUnits
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.script.{ScriptPubKey, ScriptSignature}
import org.bitcoins.core.protocol.script.{P2SHScriptSignature, ScriptPubKey, ScriptSignature, SigVersionBase}
import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.script._
import org.bitcoins.core.script.constant._
@ -15,6 +16,7 @@ import org.scalatest.{FlatSpec, MustMatchers}
*/
class CryptoInterpreterTest extends FlatSpec with MustMatchers with CryptoInterpreter with BitcoinSLogger {
val stack = List(ScriptConstant("02218AD6CDC632E7AE7D04472374311CEBBBBF0AB540D2D08C3400BB844C654231".toLowerCase))
"CryptoInterpreter" must "evaluate OP_HASH160 correctly when it is on top of the script stack" in {
val script = List(OP_HASH160)
@ -127,53 +129,22 @@ class CryptoInterpreterTest extends FlatSpec with MustMatchers with CryptoInterp
val script = List(OP_CHECKMULTISIG)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
val programNoFlags = ScriptProgram(program, ScriptFlagFactory.empty)
logger.warn("Running OP_CHECKMULTISIG program")
val newProgram = opCheckMultiSig(programNoFlags)
logger.warn("Ran OP_CHECKMULTISIG program")
newProgram.stack must be (List(OP_TRUE))
newProgram.script.isEmpty must be (true)
newProgram.isInstanceOf[ExecutedScriptProgram] must be (false)
}
it must "evaluate an OP_CHECKMULTISIG for a p2sh transaction" in {
val rawScriptSig = "0047304402205b7d2c2f177ae76cfbbf14d589c113b0b35db753d305d5562dd0b61cbf366cfb02202e56f93c4f08a27f986cd424ffc48a462c3202c4902104d4d0ff98ed28f4bf80014730440220563e5b3b1fc11662a84bc5ea2a32cc3819703254060ba30d639a1aaf2d5068ad0220601c1f47ddc76d93284dd9ed68f7c9974c4a0ea7cbe8a247d6bc3878567a5fca014c6952210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179821038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f515082103363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff464053ae"
val p2shScriptSig = ScriptSignature(rawScriptSig)
val rawScriptPubKey = "a914c9e4a896d149702d0d1695434feddd52e24ad78d87"
val p2shScriptPubKey = ScriptPubKey(rawScriptPubKey)
val (creditingTx,outputIndex) = TransactionTestUtil.buildCreditingTransaction(p2shScriptPubKey)
val (spendingTx,inputIndex) = TransactionTestUtil.buildSpendingTransaction(creditingTx,p2shScriptSig,outputIndex)
val stack = List(ScriptNumber(3),
ScriptConstant("03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640"),
ScriptConstant("038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508"),
ScriptConstant("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"),
ScriptNumber(2),
ScriptConstant("30440220563e5b3b1fc11662a84bc5ea2a32cc3819703254060ba30d639a1aaf2d5068ad0220601c1f47ddc76d93284dd9ed68f7c9974c4a0ea7cbe8a247d6bc3878567a5fca01"),
ScriptConstant("304402205b7d2c2f177ae76cfbbf14d589c113b0b35db753d305d5562dd0b61cbf366cfb02202e56f93c4f08a27f986cd424ffc48a462c3202c4902104d4d0ff98ed28f4bf8001"),
OP_0)
val script = List(OP_CHECKMULTISIG)
val baseProgram = ScriptProgram(spendingTx,creditingTx.outputs(0).scriptPubKey,UInt32.zero,ScriptFlagFactory.empty)
val program = ScriptProgram(baseProgram,stack,script)
val newProgram = opCheckMultiSig(program)
newProgram.stackTopIsTrue must be (true)
newProgram.stack.size must be (1)
newProgram.script.isEmpty must be (true)
}
it must "mark a transaction invalid when the NULLDUMMY flag is set for a OP_CHECKMULTISIG operation & the scriptSig does not begin with OP_0" in {
val flags = Seq(ScriptVerifyNullDummy)
val scriptSig = ScriptSignature.fromAsm(Seq(OP_1))
val input = TransactionInput(EmptyTransactionOutPoint, scriptSig, TransactionConstants.sequence)
val tx = Transaction(TestUtil.transaction,UpdateTransactionInputs(Seq(input)))
val baseProgram = ScriptProgram.toExecutionInProgress(ScriptProgram(tx,TestUtil.scriptPubKey,UInt32.zero,flags))
val baseProgram = ScriptProgram.toExecutionInProgress(ScriptProgram(tx,TestUtil.scriptPubKey,
UInt32.zero,flags))
val stack = Seq(OP_0,OP_0,OP_1)
val script = Seq(OP_CHECKMULTISIG)
val program = ScriptProgram(baseProgram,stack,script)

View file

@ -9,24 +9,24 @@ import org.scalatest.{FlatSpec, MustMatchers}
class HashTypeTest extends FlatSpec with MustMatchers {
"HashType" must "combine hash types with SIGHASH_ANYONECANPAY" in {
SIGHASH_ALL_ANYONECANPAY.defaultValue.num must be (Int32(0x81))
SIGHASH_NONE_ANYONECANPAY.defaultValue.num must be (Int32(0x82))
SIGHASH_SINGLE_ANYONECANPAY.defaultValue.num must be (Int32(0x83))
HashType.sigHashAllAnyoneCanPay.num must be (Int32(0x81))
HashType.sigHashNoneAnyoneCanPay.num must be (Int32(0x82))
HashType.sigHashSingleAnyoneCanPay.num must be (Int32(0x83))
}
it must "find a hash type by its hex value" in {
HashType("00000001") must be (SIGHASH_ALL.defaultValue)
HashType("00000002") must be (SIGHASH_NONE.defaultValue)
HashType("00000003") must be (SIGHASH_SINGLE.defaultValue)
HashType("00000080") must be (SIGHASH_ANYONECANPAY.defaultValue)
HashType("00000001") must be (HashType.sigHashAll)
HashType("00000002") must be (HashType.sigHashNone)
HashType("00000003") must be (HashType.sigHashSingle)
HashType("00000080") must be (HashType.sigHashAnyoneCanPay)
}
it must "find a hash type by its byte value" in {
HashType(0.toByte) must be (SIGHASH_ALL(Int32.zero))
HashType(1.toByte) must be (SIGHASH_ALL(Int32.one))
HashType(2.toByte) must be (SIGHASH_NONE.defaultValue)
HashType(3.toByte) must be (SIGHASH_SINGLE.defaultValue)
HashType(0x80) must be (SIGHASH_ANYONECANPAY.defaultValue)
HashType(2.toByte) must be (HashType.sigHashNone)
HashType(3.toByte) must be (HashType.sigHashSingle)
HashType(0x80) must be (HashType.sigHashAnyoneCanPay)
}
@ -37,8 +37,8 @@ class HashTypeTest extends FlatSpec with MustMatchers {
it must "find hashType for number 1190874345" in {
//1190874345 & 0x80 = 0x80
val num = Int32(1190874345)
HashType(num) must be (SIGHASH_ANYONECANPAY(num))
HashType(num.bytes) must be (SIGHASH_ANYONECANPAY(num.bytes))
HashType(num).isInstanceOf[SIGHASH_ANYONECANPAY] must be (true)
HashType(num.bytes).isInstanceOf[SIGHASH_ANYONECANPAY] must be (true)
}
it must "determine if a given number is of hashType SIGHASH_ALL" in {
@ -46,53 +46,35 @@ class HashTypeTest extends FlatSpec with MustMatchers {
HashType.isSIGHASH_ALL(Int32.one) must be (true)
HashType.isSIGHASH_ALL(Int32(5)) must be (true)
HashType.isSIGHASH_ALL(SIGHASH_NONE.defaultValue.num) must be (false)
HashType.isSIGHASH_ALL(SIGHASH_SINGLE.defaultValue.num) must be (false)
HashType.isSIGHASH_ALL(HashType.sigHashNone.num) must be (false)
HashType.isSIGHASH_ALL(HashType.sigHashSingle.num) must be (false)
}
it must "return the correct byte for a given hashtype" in {
SIGHASH_ALL.byte must be (0x01.toByte)
SIGHASH_NONE.byte must be (0x02.toByte)
SIGHASH_SINGLE.byte must be (0x03.toByte)
SIGHASH_ANYONECANPAY.byte must be (0x80.toByte)
SIGHASH_ALL_ANYONECANPAY.byte must be (0x81.toByte)
SIGHASH_NONE_ANYONECANPAY.byte must be (0x82.toByte)
SIGHASH_SINGLE_ANYONECANPAY.byte must be (0x83.toByte)
SIGHASH_ALL(HashType.sigHashAllByte).byte must be (0x01.toByte)
HashType.sigHashNone.byte must be (0x02.toByte)
HashType.sigHashSingle.byte must be (0x03.toByte)
HashType.sigHashAnyoneCanPay.byte must be (0x80.toByte)
HashType.sigHashAllAnyoneCanPay.byte must be (0x81.toByte)
HashType.sigHashNoneAnyoneCanPay.byte must be (0x82.toByte)
HashType.sigHashSingleAnyoneCanPay.byte must be (0x83.toByte)
}
it must "intercept require statements for each hashType with illegal inputs" in {
intercept[IllegalArgumentException]{
SIGHASH_ALL(Int32(2))
}
intercept[IllegalArgumentException]{
SIGHASH_NONE(Int32(5))
}
intercept[IllegalArgumentException]{
SIGHASH_SINGLE(Int32(10))
}
intercept[IllegalArgumentException]{
SIGHASH_ANYONECANPAY(Int32(50))
}
intercept[IllegalArgumentException]{
SIGHASH_ALL_ANYONECANPAY(Int32(0x80))
}
intercept[IllegalArgumentException]{
SIGHASH_NONE_ANYONECANPAY(Int32(0x80))
}
intercept[IllegalArgumentException]{
SIGHASH_SINGLE_ANYONECANPAY(Int32(0x80))
}
}
it must "find each specific hashType from byte sequence of default value" in {
//tests each hashtypes overriding fromBytes function
SIGHASH_ALL(SIGHASH_ALL.defaultValue.num.bytes).isInstanceOf[SIGHASH_ALL] must be (true)
SIGHASH_NONE(SIGHASH_NONE.defaultValue.num.bytes).isInstanceOf[SIGHASH_NONE] must be (true)
SIGHASH_SINGLE(SIGHASH_SINGLE.defaultValue.num.bytes).isInstanceOf[SIGHASH_SINGLE] must be (true)
SIGHASH_ANYONECANPAY(SIGHASH_ANYONECANPAY.defaultValue.num.bytes).isInstanceOf[SIGHASH_ANYONECANPAY] must be (true)
SIGHASH_ALL_ANYONECANPAY(SIGHASH_ALL_ANYONECANPAY.defaultValue.num.bytes).isInstanceOf[SIGHASH_ALL_ANYONECANPAY] must be (true)
SIGHASH_NONE_ANYONECANPAY(SIGHASH_NONE_ANYONECANPAY.defaultValue.num.bytes).isInstanceOf[SIGHASH_NONE_ANYONECANPAY] must be (true)
SIGHASH_SINGLE_ANYONECANPAY(SIGHASH_SINGLE_ANYONECANPAY.defaultValue.num.bytes).isInstanceOf[SIGHASH_SINGLE_ANYONECANPAY] must be (true)
HashType(HashType.sigHashAll.num.bytes) must be (HashType.sigHashAll)
HashType(HashType.sigHashNone.num.bytes) must be (HashType.sigHashNone)
HashType(HashType.sigHashSingle.num.bytes) must be (HashType.sigHashSingle)
HashType(HashType.sigHashAnyoneCanPay.num.bytes) must be (HashType.sigHashAnyoneCanPay)
HashType(HashType.sigHashAllAnyoneCanPay.num.bytes) must be (HashType.sigHashAllAnyoneCanPay)
HashType(HashType.sigHashNoneAnyoneCanPay.num.bytes) must be (HashType.sigHashNoneAnyoneCanPay)
HashType(HashType.sigHashSingleAnyoneCanPay.num.bytes) must be (HashType.sigHashSingleAnyoneCanPay)
}
it must "find a hashtype with only an integer" in {

View file

@ -1,11 +1,13 @@
package org.bitcoins.core.script.interpreter
import org.bitcoins.core.crypto.{TransactionSignatureComponent, ECPrivateKey}
import org.bitcoins.core.crypto.{ECPrivateKey, TransactionSignatureComponent, TransactionSignatureSerializer}
import org.bitcoins.core.currency.CurrencyUnits
import org.bitcoins.core.gen.TransactionGenerators
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.policy.Policy
import org.bitcoins.core.protocol.script.{P2SHScriptSignature, CLTVScriptSignature, CLTVScriptPubKey, ScriptPubKey}
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction.WitnessTransaction
import org.bitcoins.core.script.ScriptProgram
import org.bitcoins.core.script.flag.ScriptFlagFactory
import org.bitcoins.core.script.interpreter.testprotocol.CoreTestCaseProtocol._
@ -29,13 +31,7 @@ class ScriptInterpreterTest extends FlatSpec with MustMatchers with ScriptInterp
//use this to represent a single test case from script_valid.json
/* val lines =
"""
| [[
| "0x47 0x304402204710a85181663b32d25c70ec2bbd14adff5ddfff6cb50d09e155ef5f541fc86c0220056b0cc949be9386ecc5f6c2ac0493269031dbb185781db90171b54ac127790281",
| "0x41 0x048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf CHECKSIG",
| "",
| "OK",
| "P2PK anyonecanpay"
|]]
| [["0 0x09 0x300602010102010101 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG,NULLFAIL", "NULLFAIL", "BIP66-compliant but not NULLFAIL-compliant"]]
""".stripMargin*/
val lines = try source.getLines.filterNot(_.isEmpty).map(_.trim) mkString "\n" finally source.close()
val json = lines.parseJson
@ -43,18 +39,25 @@ class ScriptInterpreterTest extends FlatSpec with MustMatchers with ScriptInterp
val testCases : Seq[CoreTestCase] = testCasesOpt.flatten
for {
testCase <- testCases
(creditingTx,outputIndex) = TransactionTestUtil.buildCreditingTransaction(testCase.scriptPubKey)
(tx,inputIndex) = TransactionTestUtil.buildSpendingTransaction(creditingTx,testCase.scriptSig,outputIndex)
(creditingTx,outputIndex) = TransactionTestUtil.buildCreditingTransaction(testCase.scriptPubKey, testCase.witness.map(_._2))
(tx,inputIndex) = TransactionTestUtil.buildSpendingTransaction(creditingTx,testCase.scriptSig,outputIndex, testCase.witness)
} yield {
logger.info("Raw test case: " + testCase.raw)
logger.info("Parsed ScriptSig: " + testCase.scriptSig)
logger.info("Parsed ScriptPubKey: " + testCase.scriptPubKey)
logger.info("Parsed tx: " + tx.hex)
logger.info("Flags: " + testCase.flags)
logger.info("Comments: " + testCase.comments)
val scriptPubKey = ScriptPubKey.fromAsm(testCase.scriptPubKey.asm)
val flags = ScriptFlagFactory.fromList(testCase.flags)
val witness = testCase.witness
logger.info("Flags after parsing: " + flags)
val program = ScriptProgram(tx,scriptPubKey,inputIndex,flags)
logger.info("Witness after parsing: " + witness)
val program = witness match {
case Some((w, amount)) => ScriptProgram(tx.asInstanceOf[WitnessTransaction], scriptPubKey,
inputIndex, flags, amount)
case None => ScriptProgram(tx, scriptPubKey, inputIndex, flags)
}
withClue(testCase.raw) {
ScriptInterpreter.run(program) must equal (testCase.expectedResult)
}

View file

@ -1,12 +1,14 @@
package org.bitcoins.core.script.interpreter.testprotocol
import org.bitcoins.core.protocol.script.{ScriptPubKey, ScriptSignature}
import org.bitcoins.core.currency.CurrencyUnit
import org.bitcoins.core.protocol.script.{ScriptPubKey, ScriptSignature, ScriptWitness}
import org.bitcoins.core.script.constant.ScriptToken
import org.bitcoins.core.script.result.ScriptResult
/**
* Created by chris on 1/18/16.
* This represents a core test case for valid and invalid scripts
* the scripts can be seen in the ../script_valid.json and ../script_invalid.json
* the scripts can be seen in the script_tests.json file
* files.
*/
trait CoreTestCase {
@ -16,8 +18,9 @@ trait CoreTestCase {
def expectedResult : ScriptResult
def comments : String
def raw : String
def witness: Option[(ScriptWitness, CurrencyUnit)]
}
case class CoreTestCaseImpl(scriptSig : ScriptSignature,
scriptPubKey: ScriptPubKey, flags : String, expectedResult : ScriptResult,
comments : String, raw : String) extends CoreTestCase
comments : String, raw : String, witness: Option[(ScriptWitness, CurrencyUnit)]) extends CoreTestCase

View file

@ -1,8 +1,10 @@
package org.bitcoins.core.script.locktime
import org.bitcoins.core.currency.CurrencyUnits
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.policy.Policy
import org.bitcoins.core.protocol.script.SigVersionBase
import org.bitcoins.core.protocol.transaction.{Transaction, TransactionConstants, TransactionInput, UpdateTransactionInputs}
import org.bitcoins.core.script.result._
import org.bitcoins.core.script.{ExecutedScriptProgram, ExecutionInProgressScriptProgram, PreExecutionScriptProgram, ScriptProgram}
@ -78,7 +80,8 @@ class LockTimeInterpreterTest extends FlatSpec with MustMatchers with LockTimeIn
val txAdjustedSequenceNumber = Transaction(TestUtil.transaction,
UpdateTransactionInputs(Seq(txInputAdjustedSequenceNumber)))
val adjustedLockTimeTx = Transaction(txAdjustedSequenceNumber,UInt32.zero)
val baseProgram = ScriptProgram.toExecutionInProgress(ScriptProgram(adjustedLockTimeTx,TestUtil.testProgram.txSignatureComponent.scriptPubKey,
val baseProgram = ScriptProgram.toExecutionInProgress(ScriptProgram(adjustedLockTimeTx,
TestUtil.testProgram.txSignatureComponent.scriptPubKey,
TestUtil.testProgram.txSignatureComponent.inputIndex,TestUtil.testProgram.flags))
val program = ScriptProgram(baseProgram,stack,script)
val newProgram = opCheckLockTimeVerify(program)
@ -95,7 +98,8 @@ class LockTimeInterpreterTest extends FlatSpec with MustMatchers with LockTimeIn
val txAdjustedSequenceNumber = Transaction(TestUtil.transaction,
UpdateTransactionInputs(Seq(txInputAdjustedSequenceNumber)))
val adjustedLockTimeTx = Transaction(txAdjustedSequenceNumber,UInt32.zero)
val baseProgram = ScriptProgram.toExecutionInProgress(ScriptProgram(adjustedLockTimeTx,TestUtil.testProgram.txSignatureComponent.scriptPubKey,
val baseProgram = ScriptProgram.toExecutionInProgress(ScriptProgram(adjustedLockTimeTx,
TestUtil.testProgram.txSignatureComponent.scriptPubKey,
TestUtil.testProgram.txSignatureComponent.inputIndex,TestUtil.testProgram.flags))
val program = ScriptProgram(baseProgram,stack,script)
val newProgram = opCheckLockTimeVerify(program)
@ -110,8 +114,10 @@ class LockTimeInterpreterTest extends FlatSpec with MustMatchers with LockTimeIn
val txInputAdjustedSequenceNumber = TransactionInput(TestUtil.transaction.inputs(0),UInt32.zero)
val txAdjustedSequenceNumber = Transaction(TestUtil.transaction,UpdateTransactionInputs(Seq(txInputAdjustedSequenceNumber)))
val adjustedLockTimeTx = Transaction(txAdjustedSequenceNumber,UInt32(500000000))
val baseProgram : PreExecutionScriptProgram = ScriptProgram(adjustedLockTimeTx,TestUtil.testProgram.txSignatureComponent.scriptPubKey,
TestUtil.testProgram.txSignatureComponent.inputIndex,TestUtil.testProgram.flags)
val baseProgram : PreExecutionScriptProgram = ScriptProgram(adjustedLockTimeTx,
TestUtil.testProgram.txSignatureComponent.scriptPubKey,
TestUtil.testProgram.txSignatureComponent.inputIndex,TestUtil.testProgram.flags
)
val program = ScriptProgram(baseProgram,stack,script)
val newProgram = opCheckLockTimeVerify(program)
//if an error is hit, the newProgram will be an instance of ExecutedScriptProgram

View file

@ -12,23 +12,15 @@ import org.scalatest.{FlatSpec, MustMatchers}
/**
* Created by chris on 1/12/16.
*/
class RawScriptPubKeyParserTest extends FlatSpec with MustMatchers with RawScriptPubKeyParser {
class RawScriptPubKeyParserTest extends FlatSpec with MustMatchers {
"RawScriptPubKeyParser" must "parse a hex string into a scriptPubKey" in {
val scriptPubKey : ScriptPubKey = read(TestUtil.rawScriptPubKey)
scriptPubKey.asm must be (Seq(OP_DUP,OP_HASH160, BytesToPushOntoStack(20),
ScriptConstant("cbc20a7664f2f69e5355aa427045bc15e7c6c772"),OP_EQUALVERIFY,OP_CHECKSIG))
}
it must "read then write the scriptPubKey and get the original scriptPubKey" in {
val scriptPubKey : ScriptPubKey = read(TestUtil.rawScriptPubKey)
write(scriptPubKey) must be (TestUtil.rawScriptPubKey)
"RawScriptPubKeyParser" must "read then write the scriptPubKey and get the original scriptPubKey" in {
val scriptPubKey : ScriptPubKey = RawScriptPubKeyParser.read(TestUtil.rawScriptPubKey)
RawScriptPubKeyParser.write(scriptPubKey) must be (TestUtil.rawScriptPubKey)
}
it must "read a raw scriptPubKey and give us the expected asm" in {
val scriptPubKey = read(TestUtil.rawP2PKHScriptPubKey)
val scriptPubKey = RawScriptPubKeyParser.read(TestUtil.rawP2PKHScriptPubKey)
val expectedAsm : Seq[ScriptToken] =
List(OP_DUP, OP_HASH160, BytesToPushOntoStack(20), ScriptConstant("31a420903c05a0a7de2de40c9f02ebedbacdc172"),
OP_EQUALVERIFY, OP_CHECKSIG)
@ -39,18 +31,17 @@ class RawScriptPubKeyParserTest extends FlatSpec with MustMatchers with RawScrip
//from b30d3148927f620f5b1228ba941c211fdabdae75d0ba0b688a58accbf018f3cc
//output is index 1
val rawScriptPubKey = "17a914af575bd77c5ce7eba3bd9ce6f89774713ae62c7987"
val scriptPubKey = read(rawScriptPubKey)
write(scriptPubKey) must be (rawScriptPubKey)
val scriptPubKey = RawScriptPubKeyParser.read(rawScriptPubKey)
RawScriptPubKeyParser.write(scriptPubKey) must be (rawScriptPubKey)
}
it must "read and write the scriptPubKey that pushes using a PUSHDATA1 that is negative when read as signed" in {
val rawScriptPubKey = "0x4c 0xae 0x606563686f2022553246736447566b58312b5a536e587574356542793066794778625456415675534a6c376a6a334878416945325364667657734f53474f36633338584d7439435c6e543249584967306a486956304f376e775236644546673d3d22203e20743b206f70656e73736c20656e63202d7061737320706173733a5b314a564d7751432d707269766b65792d6865785d202d64202d6165732d3235362d636263202d61202d696e207460 DROP DUP HASH160 0x14 0xbfd7436b6265aa9de506f8a994f881ff08cc2872 EQUALVERIFY CHECKSIG"
val asm = ScriptParser.fromString(rawScriptPubKey)
println("Asm: " + asm)
val scriptPubKey = ScriptPubKey.fromAsm(asm)
val actualRawScriptPubKey = RawScriptPubKeyParser.write(scriptPubKey)
//the actual hex representation is from a bitcoin core test case inside of tx_valid.json
actualRawScriptPubKey must be ("4cae606563686f2022553246736447566b58312b5a536e587574356542793066794778625456415675534a6c376a6a334878416945325364667657734f53474f36633338584d7439435c6e543249584967306a486956304f376e775236644546673d3d22203e20743b206f70656e73736c20656e63202d7061737320706173733a5b314a564d7751432d707269766b65792d6865785d202d64202d6165732d3235362d636263202d61202d696e2074607576a914bfd7436b6265aa9de506f8a994f881ff08cc287288ac")
actualRawScriptPubKey must be ("ca4cae606563686f2022553246736447566b58312b5a536e587574356542793066794778625456415675534a6c376a6a334878416945325364667657734f53474f36633338584d7439435c6e543249584967306a486956304f376e775236644546673d3d22203e20743b206f70656e73736c20656e63202d7061737320706173733a5b314a564d7751432d707269766b65792d6865785d202d64202d6165732d3235362d636263202d61202d696e2074607576a914bfd7436b6265aa9de506f8a994f881ff08cc287288ac")
}

View file

@ -13,7 +13,7 @@ class RawScriptSignatureParserTest extends FlatSpec with MustMatchers with RawSc
//from bitcoin developer examples
//https://bitcoin.org/en/developer-reference#raw-transaction-format
val rawScriptSig = "494830450221008949f0cb400094ad2b5eb399d59d01c14d73d8fe6e96df1a7150deb388ab8935022079656090d7f6bac4c9a94e0aad311a4268e082a725f8aeae0573fb12ff866a5f01"
val rawScriptSig = "4a494830450221008949f0cb400094ad2b5eb399d59d01c14d73d8fe6e96df1a7150deb388ab8935022079656090d7f6bac4c9a94e0aad311a4268e082a725f8aeae0573fb12ff866a5f01"
"RawScriptSignatureParser" must "write a raw script sig" in {
val scriptSig = read(rawScriptSig)
@ -23,7 +23,7 @@ class RawScriptSignatureParserTest extends FlatSpec with MustMatchers with RawSc
it must "read then write a raw script sig" in {
//from this tx
//https://tbtc.blockr.io/api/v1/tx/raw/bdc221db675c06dbee2ae75d33e31cad4e2555efea10c337ff32c8cdf97f8e74
val rawScriptSig = "483045022100ad8e961fe3c22b2647d92b078f4c0cf81b3106ea5bf8b900ab8646aa4430216f022071d4edc2b5588be20ac4c2d07edd8ed069e10b2402d3dce2d3b835ccd075f283014104fa79182bbc26c708b5d9f36b8635947d4a834ea356cf612ede08395c295f962e0b1dc2557aba34188640e51a58ed547f2c89c8265cd0c04ff890d8435648746e"
val rawScriptSig = TestUtil.rawScriptSig
val scriptSig = RawScriptSignatureParser.read(rawScriptSig)
RawScriptSignatureParser.write(scriptSig) must be (rawScriptSig)
}
@ -65,7 +65,7 @@ class RawScriptSignatureParserTest extends FlatSpec with MustMatchers with RawSc
it must "parse hex to a p2pkh scriptSig and then write that p2pkh scriptSig to hex" in {
//from b30d3148927f620f5b1228ba941c211fdabdae75d0ba0b688a58accbf018f3cc
val rawScriptSig = "4730440220048e15422cf62349dc586ffb8c749d40280781edd5064ff27a5910ff5cf225a802206a82685dbc2cf195d158c29309939d5a3cd41a889db6f766f3809fff35722305012103dcfc9882c1b3ae4e03fb6cac08bdb39e284e81d70c7aa8b27612457b2774509b"
val rawScriptSig = "6a4730440220048e15422cf62349dc586ffb8c749d40280781edd5064ff27a5910ff5cf225a802206a82685dbc2cf195d158c29309939d5a3cd41a889db6f766f3809fff35722305012103dcfc9882c1b3ae4e03fb6cac08bdb39e284e81d70c7aa8b27612457b2774509b"
val scriptSig = RawScriptSignatureParser.read(rawScriptSig)

View file

@ -50,22 +50,22 @@ class ScriptParserTest extends FlatSpec with MustMatchers with ScriptParser with
}
it must "parse a p2pkh output script from a byte array to script tokens" in {
val bytes : Seq[Byte] = decodeHex(TestUtil.p2pkhOutputScript)
val bytes : Seq[Byte] = decodeHex(TestUtil.p2pkhOutputScript).tail
fromBytes(bytes) must be (TestUtil.p2pkhOutputScriptAsm)
}
it must "parse a p2pkh input script from a byte array to script tokens" in {
val bytes = decodeHex(TestUtil.p2pkhInputScript)
val bytes = decodeHex(TestUtil.p2pkhInputScript).tail
fromBytes(bytes) must be (TestUtil.p2pkhInputScriptAsm)
}
it must "parse a p2sh input script from a byte array into script tokens" in {
val bytes = decodeHex(TestUtil.rawP2shInputScript)
val bytes = decodeHex(TestUtil.rawP2shInputScript).tail
fromBytes(bytes) must be (TestUtil.p2shInputScriptAsm)
}
it must "parse a p2sh outputscript from a byte array into script tokens" in {
val bytes = decodeHex(TestUtil.p2shOutputScript)
val bytes = decodeHex(TestUtil.p2shOutputScript).tail
fromBytes(bytes) must be (TestUtil.p2shOutputScriptAsm)
}

View file

@ -1,17 +1,17 @@
package org.bitcoins.core.serializers.transaction
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.transaction.{TransactionConstants, Transaction}
import org.bitcoins.core.protocol.transaction.{Transaction, TransactionConstants}
import org.bitcoins.core.util.{BitcoinSUtil, TestUtil}
import org.scalatest.{FlatSpec, MustMatchers}
/**
* Created by chris on 1/14/16.
*/
class RawTransactionParserTest extends FlatSpec with MustMatchers {
class RawBaseTransactionParserTest extends FlatSpec with MustMatchers {
"RawTransactionParser" must "parse a raw transaction" in {
val tx : Transaction = RawTransactionParser.read(TestUtil.rawTransaction)
"RawBaseTransactionParser" must "parse a raw transaction" in {
val tx : Transaction = RawBaseTransactionParser.read(TestUtil.rawTransaction)
tx.version must be (UInt32.one)
tx.inputs.size must be (2)
tx.outputs.size must be (2)
@ -21,7 +21,7 @@ class RawTransactionParserTest extends FlatSpec with MustMatchers {
it must "parse a transaction correctly with a locktime" in {
//txid bdc221db675c06dbee2ae75d33e31cad4e2555efea10c337ff32c8cdf97f8e74
val rawTx = "0100000002fc37adbd036fb51b3f4f6f70474270939d6ff8c4ea697639f2b57dd6359e3070010000008b483045022100ad8e961fe3c22b2647d92b078f4c0cf81b3106ea5bf8b900ab8646aa4430216f022071d4edc2b5588be20ac4c2d07edd8ed069e10b2402d3dce2d3b835ccd075f283014104fa79182bbc26c708b5d9f36b8635947d4a834ea356cf612ede08395c295f962e0b1dc2557aba34188640e51a58ed547f2c89c8265cd0c04ff890d8435648746e0000000036219231b3043efdfb9405bbc2610baa73e340dddfe9c2a07b09bd3785ca6330000000008b483045022100cb097f8720d0c4665e8771fff5181b30584fd9e7d437fae21b440c94fe76d56902206f9b539ae26ec9688c54272d6a3309d93f17fb9835f382fff1ebeead84af2763014104fa79182bbc26c708b5d9f36b8635947d4a834ea356cf612ede08395c295f962e0b1dc2557aba34188640e51a58ed547f2c89c8265cd0c04ff890d8435648746effffffff02905f0100000000001976a914a45bc47d00c3d2b0d0ea37cbf74b94cd1986ea7988aca0860100000000001976a914a45bc47d00c3d2b0d0ea37cbf74b94cd1986ea7988ac77d3a655"
val tx : Transaction = RawTransactionParser.read(rawTx)
val tx : Transaction = RawBaseTransactionParser.read(rawTx)
tx.txId.hex must be (BitcoinSUtil.flipEndianness("bdc221db675c06dbee2ae75d33e31cad4e2555efea10c337ff32c8cdf97f8e74"))
tx.lockTime must be (UInt32(1436996471))
}
@ -29,8 +29,8 @@ class RawTransactionParserTest extends FlatSpec with MustMatchers {
it must "write a transaction with a locktime" in {
//txid bdc221db675c06dbee2ae75d33e31cad4e2555efea10c337ff32c8cdf97f8e74
val rawTxWithLockTime = "0100000002fc37adbd036fb51b3f4f6f70474270939d6ff8c4ea697639f2b57dd6359e3070010000008b483045022100ad8e961fe3c22b2647d92b078f4c0cf81b3106ea5bf8b900ab8646aa4430216f022071d4edc2b5588be20ac4c2d07edd8ed069e10b2402d3dce2d3b835ccd075f283014104fa79182bbc26c708b5d9f36b8635947d4a834ea356cf612ede08395c295f962e0b1dc2557aba34188640e51a58ed547f2c89c8265cd0c04ff890d8435648746e0000000036219231b3043efdfb9405bbc2610baa73e340dddfe9c2a07b09bd3785ca6330000000008b483045022100cb097f8720d0c4665e8771fff5181b30584fd9e7d437fae21b440c94fe76d56902206f9b539ae26ec9688c54272d6a3309d93f17fb9835f382fff1ebeead84af2763014104fa79182bbc26c708b5d9f36b8635947d4a834ea356cf612ede08395c295f962e0b1dc2557aba34188640e51a58ed547f2c89c8265cd0c04ff890d8435648746effffffff02905f0100000000001976a914a45bc47d00c3d2b0d0ea37cbf74b94cd1986ea7988aca0860100000000001976a914a45bc47d00c3d2b0d0ea37cbf74b94cd1986ea7988ac77d3a655"
val tx = RawTransactionParser.read(rawTxWithLockTime)
val serializedTx = RawTransactionParser.write(tx)
val tx = RawBaseTransactionParser.read(rawTxWithLockTime)
val serializedTx = RawBaseTransactionParser.write(tx)
serializedTx must be (rawTxWithLockTime)
}
@ -38,23 +38,23 @@ class RawTransactionParserTest extends FlatSpec with MustMatchers {
//https://btc.blockr.io/api/v1/tx/raw/cddda897b0e9322937ee1f4fd5d6147d60f04a0f4d3b461e4f87066ac3918f2a
val rawTx = "01000000020df1e23002ddf909aec026b1cf0c3b6b7943c042f22e25dbd0441855e6b39ee900000000fdfd00004730440220028c02f14654a0cc12c7e3229adb09d5d35bebb6ba1057e39adb1b2706607b0d0220564fab12c6da3d5acef332406027a7ff1cbba980175ffd880e1ba1bf40598f6b014830450221009362f8d67b60773745e983d07ba10efbe566127e244b724385b2ca2e47292dda022033def393954c320653843555ddbe7679b35cc1cacfe1dad923977de8cd6cc6d7014c695221025e9adcc3d65c11346c8a6069d6ebf5b51b348d1d6dc4b95e67480c34dc0bc75c21030585b3c80f4964bf0820086feda57c8e49fa1eab925db7c04c985467973df96521037753a5e3e9c4717d3f81706b38a6fb82b5fb89d29e580d7b98a37fea8cdefcad53aeffffffffd11533b0f283fca193e361a91ca7ddfc66592e20fd6eaf5dc0f1ef5fed05818000000000fdfe0000483045022100b4062edd75b5b3117f28ba937ed737b10378f762d7d374afabf667180dedcc62022005d44c793a9d787197e12d5049da5e77a09046014219b31e9c6b89948f648f1701483045022100b3b0c0273fc2c531083701f723e03ea3d9111e4bbca33bdf5b175cec82dcab0802206650462db37f9b4fe78da250a3b339ab11e11d84ace8f1b7394a1f6db0960ba4014c695221025e9adcc3d65c11346c8a6069d6ebf5b51b348d1d6dc4b95e67480c34dc0bc75c21030585b3c80f4964bf0820086feda57c8e49fa1eab925db7c04c985467973df96521037753a5e3e9c4717d3f81706b38a6fb82b5fb89d29e580d7b98a37fea8cdefcad53aeffffffff02500f1e00000000001976a9147ecaa33ef3cd6169517e43188ad3c034db091f5e88ac204e0000000000001976a914321908115d8a138942f98b0b53f86c9a1848501a88ac00000000"
val tx = RawTransactionParser.read(rawTx)
val serializedTx = RawTransactionParser.write(tx)
val tx = RawBaseTransactionParser.read(rawTx)
val serializedTx = RawBaseTransactionParser.write(tx)
serializedTx must be (rawTx)
}
it must "read then write a simple raw transaction with one input and two outputs" in {
val rawTx = TestUtil.simpleRawTransaction
val tx = RawTransactionParser.read(rawTx)
val serializedTx = RawTransactionParser.write(tx)
val tx = RawBaseTransactionParser.read(rawTx)
val serializedTx = RawBaseTransactionParser.write(tx)
serializedTx must be (rawTx)
}
it must "parse a transaction with one input and two outputs" in {
val tx = RawTransactionParser.read(TestUtil.parentSimpleRawTransaction)
val tx = RawBaseTransactionParser.read(TestUtil.parentSimpleRawTransaction)
tx.inputs.size must be (1)
tx.inputs.head.scriptSignature.hex must be ("4730440220048e15422cf62349dc586ffb8c749d40280781edd5064ff27a5910ff5cf225a802206a82685dbc2cf195d158c29309939d5a3cd41a889db6f766f3809fff35722305012103dcfc9882c1b3ae4e03fb6cac08bdb39e284e81d70c7aa8b27612457b2774509b")
tx.inputs.head.scriptSignature.hex must be ("6a4730440220048e15422cf62349dc586ffb8c749d40280781edd5064ff27a5910ff5cf225a802206a82685dbc2cf195d158c29309939d5a3cd41a889db6f766f3809fff35722305012103dcfc9882c1b3ae4e03fb6cac08bdb39e284e81d70c7aa8b27612457b2774509b")
tx.inputs.head.previousOutput.vout must be (UInt32.one)
tx.inputs.head.previousOutput.txId.hex must be (BitcoinSUtil.flipEndianness("65bd23d3fb0ac9d3ee0195aae8d033d6689dacf71907902b27a7ad6f6441a7cd"))
tx.inputs.head.sequence must be (TransactionConstants.sequence)

View file

@ -37,23 +37,22 @@ class RawTransactionInputParserTest extends FlatSpec with MustMatchers with RawT
val secondInput = txInputs(1)
firstInput.previousOutput.txId.hex must be (BitcoinSUtil.flipEndianness("0140d0ed6c9feeb68ea727723a82bbaf0d143fc1d3810265d4dca7ebe6e380df"))
firstInput.previousOutput.vout must be (UInt32(27))
firstInput.scriptSignature.hex must be ("473044022040f91c48f4011bf2e2edb6621bfa8fb802241de939cb86f1872c99c580ef0fe402204fc27388bc525e1b655b5f5b35f9d601d28602432dd5672f29e0a47f5b8bbb26012102c114f376c98d12a0540c3a81ab99bb1c5234245c05e8239d09f48229f9ebf011")
firstInput.scriptSignature.hex must be ("6a473044022040f91c48f4011bf2e2edb6621bfa8fb802241de939cb86f1872c99c580ef0fe402204fc27388bc525e1b655b5f5b35f9d601d28602432dd5672f29e0a47f5b8bbb26012102c114f376c98d12a0540c3a81ab99bb1c5234245c05e8239d09f48229f9ebf011")
secondInput.previousOutput.txId.hex must be (BitcoinSUtil.flipEndianness("0140d0ed6c9feeb68ea727723a82bbaf0d143fc1d3810265d4dca7ebe6e380df"))
secondInput.previousOutput.vout must be (UInt32(52))
secondInput.scriptSignature.hex must be ("483045022100cf317c320d078c5b884c44e7488825dab5bcdf3f88c66314ac925770cd8773a7022033fde60d33cc2842ea73fce5d9cf4f8da6fadf414a75b7085efdcd300407f438012102605c23537b27b80157c770cd23e066cd11db3800d3066a38b9b592fc08ae9c70")
secondInput.scriptSignature.hex must be ("6b483045022100cf317c320d078c5b884c44e7488825dab5bcdf3f88c66314ac925770cd8773a7022033fde60d33cc2842ea73fce5d9cf4f8da6fadf414a75b7085efdcd300407f438012102605c23537b27b80157c770cd23e066cd11db3800d3066a38b9b592fc08ae9c70")
}
it must "find the correct size for an input" in {
val txInput : TransactionInput = RawTransactionInputParser.read(rawTxInput).head
txInput.size must be (BitcoinSUtil.decodeHex(rawTxInput).size - 1)
it must "find the correct size for an input" in {
val txInput : TransactionInput = RawTransactionInputParser.read(rawTxInput).head
txInput.size must be (BitcoinSUtil.decodeHex(rawTxInput).size - 1)
}
}
it must "write a single input" in {
val txInputs = RawTransactionInputParser.read(rawTxInput)
val serializedInputs = RawTransactionInputParser.write(txInputs)
serializedInputs must be (rawTxInput)
}
it must "write a single input" in {
val txInputs = RawTransactionInputParser.read(rawTxInput)
val serializedInputs = RawTransactionInputParser.write(txInputs)
serializedInputs must be (rawTxInput)
}
it must "write a single input not in a sequence" in {
val txInputs = RawTransactionInputParser.read(rawTxInput)
@ -76,11 +75,11 @@ class RawTransactionInputParserTest extends FlatSpec with MustMatchers with RawT
val rawTxInputs = "02" +
"fc37adbd036fb51b3f4f6f70474270939d6ff8c4ea697639f2b57dd6359e3070010000008b483045022100ad8e961fe3c22b2647d92b078f4c0cf81b3106ea5bf8b900ab8646aa4430216f022071d4edc2b5588be20ac4c2d07edd8ed069e10b2402d3dce2d3b835ccd075f283014104fa79182bbc26c708b5d9f36b8635947d4a834ea356cf612ede08395c295f962e0b1dc2557aba34188640e51a58ed547f2c89c8265cd0c04ff890d8435648746e0000000036219231b3043efdfb9405bbc2610baa73e340dddfe9c2a07b09bd3785ca6330000000008b483045022100cb097f8720d0c4665e8771fff5181b30584fd9e7d437fae21b440c94fe76d56902206f9b539ae26ec9688c54272d6a3309d93f17fb9835f382fff1ebeead84af2763014104fa79182bbc26c708b5d9f36b8635947d4a834ea356cf612ede08395c295f962e0b1dc2557aba34188640e51a58ed547f2c89c8265cd0c04ff890d8435648746effffffff"
val txInputs = RawTransactionInputParser.read(rawTxInputs)
txInputs.head.scriptSignature.hex must be ("483045022100ad8e961fe3c22b2647d92b078f4c0cf81b3106ea5bf8b900ab8646aa4430216f022071d4edc2b5588be20ac4c2d07edd8ed069e10b2402d3dce2d3b835ccd075f283014104fa79182bbc26c708b5d9f36b8635947d4a834ea356cf612ede08395c295f962e0b1dc2557aba34188640e51a58ed547f2c89c8265cd0c04ff890d8435648746e")
txInputs.head.scriptSignature.hex must be ("8b483045022100ad8e961fe3c22b2647d92b078f4c0cf81b3106ea5bf8b900ab8646aa4430216f022071d4edc2b5588be20ac4c2d07edd8ed069e10b2402d3dce2d3b835ccd075f283014104fa79182bbc26c708b5d9f36b8635947d4a834ea356cf612ede08395c295f962e0b1dc2557aba34188640e51a58ed547f2c89c8265cd0c04ff890d8435648746e")
txInputs(1).previousOutput.txId.hex must be (BitcoinSUtil.flipEndianness("3063ca8537bd097ba0c2e9dfdd40e373aa0b61c2bb0594fbfd3e04b331922136"))
txInputs(1).previousOutput.vout must be (UInt32.zero)
txInputs(1).scriptSignature.hex must be ("483045022100cb097f8720d0c4665e8771fff5181b30584fd9e7d437fae21b440c94fe76d56902206f9b539ae26ec9688c54272d6a3309d93f17fb9835f382fff1ebeead84af2763014104fa79182bbc26c708b5d9f36b8635947d4a834ea356cf612ede08395c295f962e0b1dc2557aba34188640e51a58ed547f2c89c8265cd0c04ff890d8435648746e")
txInputs(1).scriptSignature.hex must be ("8b483045022100cb097f8720d0c4665e8771fff5181b30584fd9e7d437fae21b440c94fe76d56902206f9b539ae26ec9688c54272d6a3309d93f17fb9835f382fff1ebeead84af2763014104fa79182bbc26c708b5d9f36b8635947d4a834ea356cf612ede08395c295f962e0b1dc2557aba34188640e51a58ed547f2c89c8265cd0c04ff890d8435648746e")
val serializedTx = RawTransactionInputParser.write(txInputs)
serializedTx must be (rawTxInputs)
@ -96,8 +95,8 @@ class RawTransactionInputParserTest extends FlatSpec with MustMatchers with RawT
txInput.size must be (1)
txInput.head.previousOutput.txId.hex must be (BitcoinSUtil.flipEndianness("e99eb3e6551844d0db252ef242c043796b3b0ccfb126c0ae09f9dd0230e2f10d"))
txInput.head.previousOutput.vout must be (UInt32.zero)
txInput.head.scriptSignature.hex must be ("004730440220028c02f14654a0cc12c7e3229adb09d5d35bebb6ba1057e39adb1b2706607b0d0220564fab12c6da3d5acef332406027a7ff1cbba980175ffd880e1ba1bf40598f6b014830450221009362f8d67b60773745e983d07ba10efbe566127e244b724385b2ca2e47292dda022033def393954c320653843555ddbe7679b35cc1cacfe1dad923977de8cd6cc6d7014c695221025e9adcc3d65c11346c8a6069d6ebf5b51b348d1d6dc4b95e67480c34dc0bc75c21030585b3c80f4964bf0820086feda57c8e49fa1eab925db7c04c985467973df96521037753a5e3e9c4717d3f81706b38a6fb82b5fb89d29e580d7b98a37fea8cdefcad53ae")
txInput.head.scriptSigCompactSizeUInt.num must be (UInt64(txInput.head.scriptSignature.size))
txInput.head.scriptSignature.hex must be ("fdfd00004730440220028c02f14654a0cc12c7e3229adb09d5d35bebb6ba1057e39adb1b2706607b0d0220564fab12c6da3d5acef332406027a7ff1cbba980175ffd880e1ba1bf40598f6b014830450221009362f8d67b60773745e983d07ba10efbe566127e244b724385b2ca2e47292dda022033def393954c320653843555ddbe7679b35cc1cacfe1dad923977de8cd6cc6d7014c695221025e9adcc3d65c11346c8a6069d6ebf5b51b348d1d6dc4b95e67480c34dc0bc75c21030585b3c80f4964bf0820086feda57c8e49fa1eab925db7c04c985467973df96521037753a5e3e9c4717d3f81706b38a6fb82b5fb89d29e580d7b98a37fea8cdefcad53ae")
txInput.head.scriptSignature.compactSizeUInt.num must be (UInt64(txInput.head.scriptSignature.asm.flatMap(_.bytes).size))
txInput.head.sequence must be (UInt32(4294967295L))
RawTransactionInputParser.write(txInput) must be (rawTxInput)
@ -106,7 +105,7 @@ class RawTransactionInputParserTest extends FlatSpec with MustMatchers with RawT
val txInput2 = RawTransactionInputParser.read(rawTxInput2).head
txInput2.previousOutput.txId.hex must be (BitcoinSUtil.flipEndianness("808105ed5feff1c05daf6efd202e5966fcdda71ca961e393a1fc83f2b03315d1"))
txInput2.previousOutput.vout must be (UInt32.zero)
txInput2.scriptSignature.hex must be ("00483045022100b4062edd75b5b3117f28ba937ed737b10378f762d7d374afabf667180dedcc62022005d44c793a9d787197e12d5049da5e77a09046014219b31e9c6b89948f648f1701483045022100b3b0c0273fc2c531083701f723e03ea3d9111e4bbca33bdf5b175cec82dcab0802206650462db37f9b4fe78da250a3b339ab11e11d84ace8f1b7394a1f6db0960ba4014c695221025e9adcc3d65c11346c8a6069d6ebf5b51b348d1d6dc4b95e67480c34dc0bc75c21030585b3c80f4964bf0820086feda57c8e49fa1eab925db7c04c985467973df96521037753a5e3e9c4717d3f81706b38a6fb82b5fb89d29e580d7b98a37fea8cdefcad53ae")
txInput2.scriptSignature.hex must be ("fdfe0000483045022100b4062edd75b5b3117f28ba937ed737b10378f762d7d374afabf667180dedcc62022005d44c793a9d787197e12d5049da5e77a09046014219b31e9c6b89948f648f1701483045022100b3b0c0273fc2c531083701f723e03ea3d9111e4bbca33bdf5b175cec82dcab0802206650462db37f9b4fe78da250a3b339ab11e11d84ace8f1b7394a1f6db0960ba4014c695221025e9adcc3d65c11346c8a6069d6ebf5b51b348d1d6dc4b95e67480c34dc0bc75c21030585b3c80f4964bf0820086feda57c8e49fa1eab925db7c04c985467973df96521037753a5e3e9c4717d3f81706b38a6fb82b5fb89d29e580d7b98a37fea8cdefcad53ae")
}
@ -136,12 +135,12 @@ class RawTransactionInputParserTest extends FlatSpec with MustMatchers with RawT
firstInput.previousOutput.txId.hex must be (BitcoinSUtil.flipEndianness("e99eb3e6551844d0db252ef242c043796b3b0ccfb126c0ae09f9dd0230e2f10d"))
firstInput.previousOutput.vout must be (UInt32.zero)
firstInput.scriptSignature.hex must be ("004730440220028c02f14654a0cc12c7e3229adb09d5d35bebb6ba1057e39adb1b2706607b0d0220564fab12c6da3d5acef332406027a7ff1cbba980175ffd880e1ba1bf40598f6b014830450221009362f8d67b60773745e983d07ba10efbe566127e244b724385b2ca2e47292dda022033def393954c320653843555ddbe7679b35cc1cacfe1dad923977de8cd6cc6d7014c695221025e9adcc3d65c11346c8a6069d6ebf5b51b348d1d6dc4b95e67480c34dc0bc75c21030585b3c80f4964bf0820086feda57c8e49fa1eab925db7c04c985467973df96521037753a5e3e9c4717d3f81706b38a6fb82b5fb89d29e580d7b98a37fea8cdefcad53ae")
firstInput.scriptSignature.hex must be ("fdfd00004730440220028c02f14654a0cc12c7e3229adb09d5d35bebb6ba1057e39adb1b2706607b0d0220564fab12c6da3d5acef332406027a7ff1cbba980175ffd880e1ba1bf40598f6b014830450221009362f8d67b60773745e983d07ba10efbe566127e244b724385b2ca2e47292dda022033def393954c320653843555ddbe7679b35cc1cacfe1dad923977de8cd6cc6d7014c695221025e9adcc3d65c11346c8a6069d6ebf5b51b348d1d6dc4b95e67480c34dc0bc75c21030585b3c80f4964bf0820086feda57c8e49fa1eab925db7c04c985467973df96521037753a5e3e9c4717d3f81706b38a6fb82b5fb89d29e580d7b98a37fea8cdefcad53ae")
firstInput.sequence must be (UInt32(4294967295L))
txInputs(1).previousOutput.txId.hex must be (BitcoinSUtil.flipEndianness("808105ed5feff1c05daf6efd202e5966fcdda71ca961e393a1fc83f2b03315d1"))
txInputs(1).previousOutput.vout must be (UInt32.zero)
txInputs(1).scriptSignature.hex must be ("00483045022100b4062edd75b5b3117f28ba937ed737b10378f762d7d374afabf667180dedcc62022005d44c793a9d787197e12d5049da5e77a09046014219b31e9c6b89948f648f1701483045022100b3b0c0273fc2c531083701f723e03ea3d9111e4bbca33bdf5b175cec82dcab0802206650462db37f9b4fe78da250a3b339ab11e11d84ace8f1b7394a1f6db0960ba4014c695221025e9adcc3d65c11346c8a6069d6ebf5b51b348d1d6dc4b95e67480c34dc0bc75c21030585b3c80f4964bf0820086feda57c8e49fa1eab925db7c04c985467973df96521037753a5e3e9c4717d3f81706b38a6fb82b5fb89d29e580d7b98a37fea8cdefcad53ae")
txInputs(1).scriptSignature.hex must be ("fdfe0000483045022100b4062edd75b5b3117f28ba937ed737b10378f762d7d374afabf667180dedcc62022005d44c793a9d787197e12d5049da5e77a09046014219b31e9c6b89948f648f1701483045022100b3b0c0273fc2c531083701f723e03ea3d9111e4bbca33bdf5b175cec82dcab0802206650462db37f9b4fe78da250a3b339ab11e11d84ace8f1b7394a1f6db0960ba4014c695221025e9adcc3d65c11346c8a6069d6ebf5b51b348d1d6dc4b95e67480c34dc0bc75c21030585b3c80f4964bf0820086feda57c8e49fa1eab925db7c04c985467973df96521037753a5e3e9c4717d3f81706b38a6fb82b5fb89d29e580d7b98a37fea8cdefcad53ae")
txInputs(1).sequence must be (UInt32(4294967295L))
RawTransactionInputParser.write(txInputs) must be (rawTxInputs)
@ -156,7 +155,7 @@ class RawTransactionInputParserTest extends FlatSpec with MustMatchers with RawT
val inputs = RawTransactionInputParser.read(rawTxInput)
inputs.head.scriptSignature.hex must be ("4730440220048e15422cf62349dc586ffb8c749d40280781edd5064ff27a5910ff5cf225a802206a82685dbc2cf195d158c29309939d5a3cd41a889db6f766f3809fff35722305012103dcfc9882c1b3ae4e03fb6cac08bdb39e284e81d70c7aa8b27612457b2774509b")
inputs.head.scriptSignature.hex must be ("6a4730440220048e15422cf62349dc586ffb8c749d40280781edd5064ff27a5910ff5cf225a802206a82685dbc2cf195d158c29309939d5a3cd41a889db6f766f3809fff35722305012103dcfc9882c1b3ae4e03fb6cac08bdb39e284e81d70c7aa8b27612457b2774509b")
inputs.head.previousOutput.vout must be (UInt32.one)
inputs.head.previousOutput.txId.hex must be (BitcoinSUtil.flipEndianness("65bd23d3fb0ac9d3ee0195aae8d033d6689dacf71907902b27a7ad6f6441a7cd"))
inputs.head.sequence must be (TransactionConstants.sequence)
@ -169,12 +168,12 @@ class RawTransactionInputParserTest extends FlatSpec with MustMatchers with RawT
RawTransactionInputParser.write(input) must be (rawInput)
}
it must "read and write an input with everything that is empty besides the sequence number" in {
/* it must "read and write an input with everything that is empty besides the sequence number" in {
val rawInput = "0100010000000000000000000000000000000000000000000000000000000000000000000000ffff0000"
val input = RawTransactionInputParser.read(rawInput)
input.head.sequence must be (UInt32(65535))
RawTransactionInputParser.write(input) must be (rawInput)
}
}*/
it must "read and write an input with a large vout index in the outpoint" in {
val rawInput = "01b32667dc69ce8030bffb7d7cf9a87c985da6552f71ac39363d043ff9d75d230411d2346448473045022100f9649ac255ce97a132233f896a70babd1f4d8eaeaa4108165be97309817ad5bb02205bf70f31b4bb605cb9d1ce1a860d40bc92e2b5fca7be335edfea874570619d6da6ab6f7e"
@ -186,11 +185,8 @@ class RawTransactionInputParserTest extends FlatSpec with MustMatchers with RawT
it must "write then read this list of inputs with large sequence numbers" in {
val input = List(TransactionInput(TransactionOutPoint(
DoubleSha256Digest("ba3dd35a9b55c48114590d9bce52686ffd4b52f9c3f530bf65ad89d2b6109703"),UInt32(1311080620)),
P2PKScriptSignature("46304402204a77315e14decd47650f5ecc2c84a411c6ae7049e01637ebbd8f63eaab007c2e0220425cff64ca35a6fe426459eb4b7596cea75fdb057c843518046568f6ecba81b3"),UInt32(30250565)))
P2PKScriptSignature("4746304402204a77315e14decd47650f5ecc2c84a411c6ae7049e01637ebbd8f63eaab007c2e0220425cff64ca35a6fe426459eb4b7596cea75fdb057c843518046568f6ecba81b3"),UInt32(30250565)))
val hex = RawTransactionInputParser.write(input)
logger.info("Input hex: " + hex)
RawTransactionInputParser.read(hex) must be (input)
}

View file

@ -1,11 +1,13 @@
package org.bitcoins.core.util
import org.bitcoins.core.crypto.ECPublicKey
import org.bitcoins.core.script.bitwise.{OP_OR, OP_EQUALVERIFY}
import org.bitcoins.core.crypto.{ECPrivateKey, ECPublicKey}
import org.bitcoins.core.script.bitwise.{OP_EQUALVERIFY, OP_OR}
import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.crypto._
import org.bitcoins.core.script.flag.ScriptVerifyWitnessPubKeyType
import org.bitcoins.core.script.locktime.OP_CHECKLOCKTIMEVERIFY
import org.bitcoins.core.script.reserved.{OP_NOP, OP_RESERVED}
import org.bitcoins.core.script.result.ScriptErrorWitnessPubKeyType
import org.bitcoins.core.script.stack.OP_DUP
import org.scalatest.{FlatSpec, MustMatchers}
@ -19,11 +21,11 @@ class BitcoinScriptUtilTest extends FlatSpec with MustMatchers {
val expectedHex = TestUtil.rawP2PKHScriptPubKey
"BitcoinScriptUtil" must "give us the correct hexadecimal value of an asm script" in {
BitcoinScriptUtil.asmToHex(asm) must be (expectedHex)
BitcoinScriptUtil.asmToHex(asm) must be (asm.flatMap(_.hex).mkString)
}
it must "give us the correct byte representation of an asm script" in {
BitcoinScriptUtil.asmToBytes(asm) must be (BitcoinSUtil.decodeHex(expectedHex))
BitcoinScriptUtil.asmToBytes(asm) must be (asm.flatMap(_.bytes))
}
it must "filter out all of the push operations in a scriptSig" in {
@ -198,4 +200,25 @@ class BitcoinScriptUtilTest extends FlatSpec with MustMatchers {
BitcoinScriptUtil.minimalScriptNumberRepresentation(ScriptNumber(-1)) must be (OP_1NEGATE)
BitcoinScriptUtil.minimalScriptNumberRepresentation(ScriptNumber(-2)) must be (ScriptNumber(-2))
}
it must "determine if a segwit pubkey is compressed" in {
val key = ECPrivateKey(false)
val pubKey = key.publicKey
val flags = Seq(ScriptVerifyWitnessPubKeyType)
BitcoinScriptUtil.isValidPubKeyEncoding(pubKey,flags) must be (Some(ScriptErrorWitnessPubKeyType))
val key2 = ECPrivateKey(true)
val pubKey2 = key2.publicKey
BitcoinScriptUtil.isValidPubKeyEncoding(pubKey2,flags) must be (None)
}
it must "remove the signatures from a p2sh scriptSig" in {
val p2shScriptSig = TestUtil.p2sh2Of3ScriptSig
val signatures = p2shScriptSig.signatures
val asmWithoutSigs = BitcoinScriptUtil.removeSignaturesFromScript(signatures,p2shScriptSig.asm)
val sigExists = signatures.map(sig => asmWithoutSigs.exists(_ == ScriptConstant(sig.hex)))
sigExists.exists(_ == true) must be (false)
}
}

View file

@ -1,17 +1,17 @@
package org.bitcoins.core.util
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.serializers.script.{RawScriptSignatureParser, RawScriptPubKeyParser}
import org.bitcoins.core.serializers.transaction.{RawTransactionInputParser, RawTransactionParser}
import org.bitcoins.core.policy.Policy
import org.bitcoins.core.protocol.BitcoinAddress
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction.{EmptyTransaction, Transaction}
import org.bitcoins.core.protocol.{BitcoinAddress}
import org.bitcoins.core.script.{ExecutionInProgressScriptProgram, PreExecutionScriptProgram, ExecutedScriptProgram, ScriptProgram}
import org.bitcoins.core.protocol.transaction.Transaction
import org.bitcoins.core.script.bitwise.{OP_EQUAL, OP_EQUALVERIFY}
import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.crypto.{OP_CHECKMULTISIG, OP_CHECKSIG, OP_HASH160}
import org.bitcoins.core.script.stack.OP_DUP
import org.bitcoins.core.script.{PreExecutionScriptProgram, ScriptProgram}
import org.bitcoins.core.serializers.script.RawScriptPubKeyParser
import org.bitcoins.core.serializers.transaction.RawTransactionInputParser
/**
* Created by chris on 12/2/15.
@ -23,7 +23,7 @@ object TestUtil {
def bitcoinAddress = BitcoinAddress("1C4kYhyLftmkn48YarSoLupxHfYFo8kp64")
def multiSigAddress = BitcoinAddress("342ftSRCvFHfCeFFBuz4xwbeqnDw6BGUey")
val p2pkhInputScript = "473044022016ffdbb7c57634903c5e018fcfc48d59f4e37dc4bc3bbc9ba4e6ee39150bca030220119c2241a931819bc1a75d3596e4029d803d1cd6de123bf8a1a1a2c3665e1fac012102af7dad03e682fcd0427b5c24140c220ac9d8abe286c15f8cf5bf77eed19c3652"
val p2pkhInputScript = "69473044022016ffdbb7c57634903c5e018fcfc48d59f4e37dc4bc3bbc9ba4e6ee39150bca030220119c2241a931819bc1a75d3596e4029d803d1cd6de123bf8a1a1a2c3665e1fac012102af7dad03e682fcd0427b5c24140c220ac9d8abe286c15f8cf5bf77eed19c3652"
def p2pkhScriptSig = ScriptSignature(p2pkhInputScript)
val p2pkhInputScriptNotParsedAsm =
@ -34,7 +34,7 @@ object TestUtil {
BytesToPushOntoStack(33),
ScriptConstant("02af7dad03e682fcd0427b5c24140c220ac9d8abe286c15f8cf5bf77eed19c3652"))
val p2pkhOutputScript = "76a914e2e7c1ab3f807151e832dd1accb3d4f5d7d19b4b88ac"
val p2pkhOutputScript = "1976a914e2e7c1ab3f807151e832dd1accb3d4f5d7d19b4b88ac"
val p2pkhOutputScriptNotParsedAsm = "OP_DUP OP_HASH160 e2e7c1ab3f807151e832dd1accb3d4f5d7d19b4b OP_EQUALVERIFY OP_CHECKSIG"
val p2pkhOutputScriptAsm = List(OP_DUP,OP_HASH160,BytesToPushOntoStack(20), ScriptConstant("e2e7c1ab3f807151e832dd1accb3d4f5d7d19b4b"),OP_EQUALVERIFY, OP_CHECKSIG)
@ -43,7 +43,7 @@ object TestUtil {
val p2shInputScriptNotParsedAsm =
"0 304402207df6dd8dad22d49c3c83d8031733c32a53719278eb7985d3b35b375d776f84f102207054f9209a1e87d55feafc90aa04c33008e5bae9191da22aeaa16efde96f41f001 512102b022902a0fdd71e831c37e4136c2754a59887be0618fb75336d7ab67e2982ff551ae"
val rawP2shInputScript = "0047304402207df6dd8dad22d49c3c83d8031733c32a53719278eb7985d3b35b375d776f84f102207054f9209a1e87d55feafc90aa04c33008e5bae9191da22aeaa16efde96f41f00125512102b022902a0fdd71e831c37e4136c2754a59887be0618fb75336d7ab67e2982ff551ae"
val rawP2shInputScript = "6f0047304402207df6dd8dad22d49c3c83d8031733c32a53719278eb7985d3b35b375d776f84f102207054f9209a1e87d55feafc90aa04c33008e5bae9191da22aeaa16efde96f41f00125512102b022902a0fdd71e831c37e4136c2754a59887be0618fb75336d7ab67e2982ff551ae"
val p2shInputScript = ScriptSignature(rawP2shInputScript)
val p2shInputScriptAsm = List(
OP_0,
@ -54,19 +54,19 @@ object TestUtil {
)
val p2shOutputScript = "a914eda8ae08b5c9f973f49543e90a7c292367b3337c87"
val p2shOutputScript = "17a914eda8ae08b5c9f973f49543e90a7c292367b3337c87"
val p2shOutputScriptNotParsedAsm = "OP_HASH160 eda8ae08b5c9f973f49543e90a7c292367b3337c OP_EQUAL"
val p2shOutputScriptAsm = List(OP_HASH160, BytesToPushOntoStack(20), ScriptConstant("eda8ae08b5c9f973f49543e90a7c292367b3337c"), OP_EQUAL)
//https://btc.blockr.io/api/v1/tx/raw/791fe035d312dcf9196b48649a5c9a027198f623c0a5f5bd4cc311b8864dd0cf
val rawP2shInputScriptSigHashSingle = "00483045022100dfcfafcea73d83e1c54d444a19fb30d17317f922c19e2ff92dcda65ad09cba24022001e7a805c5672c49b222c5f2f1e67bb01f87215fb69df184e7c16f66c1f87c290347304402204a657ab8358a2edb8fd5ed8a45f846989a43655d2e8f80566b385b8f5a70dab402207362f870ce40f942437d43b6b99343419b14fb18fa69bee801d696a39b3410b8034c695221023927b5cd7facefa7b85d02f73d1e1632b3aaf8dd15d4f9f359e37e39f05611962103d2c0e82979b8aba4591fe39cffbf255b3b9c67b3d24f94de79c5013420c67b802103ec010970aae2e3d75eef0b44eaa31d7a0d13392513cd0614ff1c136b3b1020df53ae"
val rawP2shInputScriptSigHashSingle = "fdfd0000483045022100dfcfafcea73d83e1c54d444a19fb30d17317f922c19e2ff92dcda65ad09cba24022001e7a805c5672c49b222c5f2f1e67bb01f87215fb69df184e7c16f66c1f87c290347304402204a657ab8358a2edb8fd5ed8a45f846989a43655d2e8f80566b385b8f5a70dab402207362f870ce40f942437d43b6b99343419b14fb18fa69bee801d696a39b3410b8034c695221023927b5cd7facefa7b85d02f73d1e1632b3aaf8dd15d4f9f359e37e39f05611962103d2c0e82979b8aba4591fe39cffbf255b3b9c67b3d24f94de79c5013420c67b802103ec010970aae2e3d75eef0b44eaa31d7a0d13392513cd0614ff1c136b3b1020df53ae"
def p2shInputScriptSigHashSingle = ScriptSignature(rawP2shInputScriptSigHashSingle)
//p2sh script for a 2 of 2
//https://tbtc.blockr.io/api/v1/tx/raw/2f18c646a2b2ee8ee1f295bb5a0f5cc51c5e820a123a14b0c0e170f9777518bb
val rawP2shInputScript2Of2 = "0047304402207d764cb90c9fd84b74d33a47cf3a0ffead9ded98333776becd6acd32c4426dac02203905a0d064e7f53d07793e86136571b6e4f700c1cfb888174e84d78638335b8101483045022100906aaca39f022acd8b7a38fd2f92aca9e9f35cfeaee69a6f13e1d083ae18222602204c9ed96fc6c4de56fd85c679fc59c16ee1ccc80c42563b86174e1a506fc007c8014752210369d26ebd086523384a0f89f293d4c327a65fa73332d8efd1097cb35231295b832102480863e5c4a4e9763f5380c44fcfe6a3b7787397076cf9ea1049303a9d34f72152ae"
val rawP2shInputScript2Of2 = "da0047304402207d764cb90c9fd84b74d33a47cf3a0ffead9ded98333776becd6acd32c4426dac02203905a0d064e7f53d07793e86136571b6e4f700c1cfb888174e84d78638335b8101483045022100906aaca39f022acd8b7a38fd2f92aca9e9f35cfeaee69a6f13e1d083ae18222602204c9ed96fc6c4de56fd85c679fc59c16ee1ccc80c42563b86174e1a506fc007c8014752210369d26ebd086523384a0f89f293d4c327a65fa73332d8efd1097cb35231295b832102480863e5c4a4e9763f5380c44fcfe6a3b7787397076cf9ea1049303a9d34f72152ae"
val rawP2SH2Of2Tx = "0100000001d69b8ece3059c429a83707cde2db9d8a76897b5d418c4a784a5f52d40063518f00000000da0047304402207d764cb90c9fd84b74d33a47cf3a0ffead9ded98333776becd6acd32c4426dac02203905a0d064e7f53d07793e86136571b6e4f700c1cfb888174e84d78638335b8101483045022100906aaca39f022acd8b7a38fd2f92aca9e9f35cfeaee69a6f13e1d083ae18222602204c9ed96fc6c4de56fd85c679fc59c16ee1ccc80c42563b86174e1a506fc007c8014752210369d26ebd086523384a0f89f293d4c327a65fa73332d8efd1097cb35231295b832102480863e5c4a4e9763f5380c44fcfe6a3b7787397076cf9ea1049303a9d34f72152ae0000000001b7e4ca00000000001976a914c59529a317559cfa818ddd625b7b980435b333dc88acb6917056"
def p2shInputScript2Of2 = ScriptSignature(rawP2shInputScript2Of2)
def p2sh2Of2Tx = Transaction(rawP2SH2Of2Tx)
@ -83,11 +83,10 @@ object TestUtil {
def p2sh2Of2CreditingTx = Transaction(rawP2SH2Of2CreditingTx)
//p2sh input with large amount of signatures
//https://tbtc.blockr.io/api/v1/tx/raw/5d254a872c9197c683ea9111fb5c0e2e0f49280a89961c45b9fea76834d335fe
val rawP2shInputScriptLargeSignature = "00483045022100a077d4fe9a81411ecb796c254d8b4e0bc73ff86a42288bc3b3ecfa1ef26c00dd02202389bf96cf38c14c3a6ccb8c688339f3fd880b724322862547a8ee3b547a9df90147304402207c0692464998e7f3869f8501cdd25bbcd9d32b6fd34ae8aeae643b422a8dfd42022057eb16f8ca1f34e88babc9f8beb4c2521eb5c4dea41f8902a70d045f1c132a4401473044022024233923253c73569f4b34723a5495698bc124b099c5542a5997d13fba7d18a802203c317bddc070276c6f6c79cb3415413e608af30e4759e31b0d53eab3ca0acd4e014830450221009b9f0d8b945717d2fca3685093d547a3928d122b8894903ed51e2248303213bc022008b376422c9f2cd713b9d10b5b106d1c56c5893dcc01ae300253ed2234bdb63f014730440220257b57cb09386d82c4328461f8fe200c2f381d6b635e2a2f4ea40c8d945e9ec102201ec67d58d51a309af4d8896e9147a42944e9f9833a456f733ea5fa6954ed2fed01" +
"4cf155210269992fb441ae56968e5b77d46a3e53b69f136444ae65a94041fc937bdb28d93321021df31471281d4478df85bfce08a10aab82601dca949a79950f8ddf7002bd915a2102174c82021492c2c6dfcbfa4187d10d38bed06afb7fdcd72c880179fddd641ea121033f96e43d72c33327b6a4631ccaa6ea07f0b106c88b9dc71c9000bb6044d5e88a210313d8748790f2a86fb524579b46ce3c68fedd58d2a738716249a9f7d5458a15c221030b632eeb079eb83648886122a04c7bf6d98ab5dfb94cf353ee3e9382a4c2fab02102fb54a7fcaa73c307cfd70f3fa66a2e4247a71858ca731396343ad30c7c4009ce57ae"
val rawP2shInputScriptLargeSignature = "fd5e0200483045022100a077d4fe9a81411ecb796c254d8b4e0bc73ff86a42288bc3b3ecfa1ef26c00dd02202389bf96cf38c14c3a6ccb8c688339f3fd880b724322862547a8ee3b547a9df90147304402207c0692464998e7f3869f8501cdd25bbcd9d32b6fd34ae8aeae643b422a8dfd42022057eb16f8ca1f34e88babc9f8beb4c2521eb5c4dea41f8902a70d045f1c132a4401473044022024233923253c73569f4b34723a5495698bc124b099c5542a5997d13fba7d18a802203c317bddc070276c6f6c79cb3415413e608af30e4759e31b0d53eab3ca0acd4e014830450221009b9f0d8b945717d2fca3685093d547a3928d122b8894903ed51e2248303213bc022008b376422c9f2cd713b9d10b5b106d1c56c5893dcc01ae300253ed2234bdb63f014730440220257b57cb09386d82c4328461f8fe200c2f381d6b635e2a2f4ea40c8d945e9ec102201ec67d58d51a309af4d8896e9147a42944e9f9833a456f733ea5fa6954ed2fed014cf155210269992fb441ae56968e5b77d46a3e53b69f136444ae65a94041fc937bdb28d93321021df31471281d4478df85bfce08a10aab82601dca949a79950f8ddf7002bd915a2102174c82021492c2c6dfcbfa4187d10d38bed06afb7fdcd72c880179fddd641ea121033f96e43d72c33327b6a4631ccaa6ea07f0b106c88b9dc71c9000bb6044d5e88a210313d8748790f2a86fb524579b46ce3c68fedd58d2a738716249a9f7d5458a15c221030b632eeb079eb83648886122a04c7bf6d98ab5dfb94cf353ee3e9382a4c2fab02102fb54a7fcaa73c307cfd70f3fa66a2e4247a71858ca731396343ad30c7c4009ce57ae"
def p2shInputScriptLargeSignature = ScriptSignature(rawP2shInputScriptLargeSignature)
def rawP2sh2Of3ScriptSig = "004730440220028c02f14654a0cc12c7e3229adb09d5d35bebb6ba1057e39adb1b2706607b0d0220564fab12c6da3d5acef332406027a7ff1cbba980175ffd880e1ba1bf40598f6b014830450221009362f8d67b60773745e983d07ba10efbe566127e244b724385b2ca2e47292dda022033def393954c320653843555ddbe7679b35cc1cacfe1dad923977de8cd6cc6d7014c695221025e9adcc3d65c11346c8a6069d6ebf5b51b348d1d6dc4b95e67480c34dc0bc75c21030585b3c80f4964bf0820086feda57c8e49fa1eab925db7c04c985467973df96521037753a5e3e9c4717d3f81706b38a6fb82b5fb89d29e580d7b98a37fea8cdefcad53ae"
def rawP2sh2Of3ScriptSig = "fdfd00004730440220028c02f14654a0cc12c7e3229adb09d5d35bebb6ba1057e39adb1b2706607b0d0220564fab12c6da3d5acef332406027a7ff1cbba980175ffd880e1ba1bf40598f6b014830450221009362f8d67b60773745e983d07ba10efbe566127e244b724385b2ca2e47292dda022033def393954c320653843555ddbe7679b35cc1cacfe1dad923977de8cd6cc6d7014c695221025e9adcc3d65c11346c8a6069d6ebf5b51b348d1d6dc4b95e67480c34dc0bc75c21030585b3c80f4964bf0820086feda57c8e49fa1eab925db7c04c985467973df96521037753a5e3e9c4717d3f81706b38a6fb82b5fb89d29e580d7b98a37fea8cdefcad53ae"
def p2sh2Of3ScriptSig : P2SHScriptSignature = ScriptSignature(rawP2sh2Of3ScriptSig) match {
case p2sh : P2SHScriptSignature => p2sh
case _ : ScriptSignature => throw new RuntimeException
@ -129,21 +128,21 @@ object TestUtil {
def parentSimpleTransaction = Transaction(parentSimpleRawTransaction)
//scriptPubKey taken from https://bitcoin.org/en/developer-reference#raw-transaction-format
val rawScriptPubKey = "76a914cbc20a7664f2f69e5355aa427045bc15e7c6c77288ac"
def rawScriptPubKey = rawP2PKHScriptPubKey
def scriptPubKey = RawScriptPubKeyParser.read(rawScriptPubKey)
//from b30d3148927f620f5b1228ba941c211fdabdae75d0ba0b688a58accbf018f3cc
//ouptut is index 0
val rawP2PKHScriptPubKey = "76a91431a420903c05a0a7de2de40c9f02ebedbacdc17288ac"
val rawP2PKHScriptPubKey = "1976a91431a420903c05a0a7de2de40c9f02ebedbacdc17288ac"
def p2pkhScriptPubKey = ScriptPubKey(rawP2PKHScriptPubKey)
val rawP2SHScriptPubKey = "a9145780b80be32e117f675d6e0ada13ba799bf248e987"
def p2shScriptPubKey = ScriptPubKey(rawP2SHScriptPubKey)
//https://tbtc.blockr.io/api/v1/tx/raw/bdc221db675c06dbee2ae75d33e31cad4e2555efea10c337ff32c8cdf97f8e74
val rawScriptSig = "483045022100ad8e961fe3c22b2647d92b078f4c0cf81b3106ea5bf8b900ab8646aa4430216f022071d4edc2b5588be20ac4c2d07edd8ed069e10b2402d3dce2d3b835ccd075f283014104fa79182bbc26c708b5d9f36b8635947d4a834ea356cf612ede08395c295f962e0b1dc2557aba34188640e51a58ed547f2c89c8265cd0c04ff890d8435648746e"
val rawScriptSig = "8b483045022100ad8e961fe3c22b2647d92b078f4c0cf81b3106ea5bf8b900ab8646aa4430216f022071d4edc2b5588be20ac4c2d07edd8ed069e10b2402d3dce2d3b835ccd075f283014104fa79182bbc26c708b5d9f36b8635947d4a834ea356cf612ede08395c295f962e0b1dc2557aba34188640e51a58ed547f2c89c8265cd0c04ff890d8435648746e"
def scriptSig = ScriptSignature(rawScriptSig)
def testProgram : ScriptProgram = ScriptProgram(TransactionTestUtil.testTransaction,
EmptyScriptPubKey,UInt32.zero,List(),Policy.standardScriptVerifyFlags)
EmptyScriptPubKey,UInt32.zero,Policy.standardScriptVerifyFlags)
def testProgramPreExecution = testProgram match {
case p : PreExecutionScriptProgram => p
@ -154,10 +153,10 @@ object TestUtil {
val rawP2PKScriptSig = "47304402200a5c6163f07b8d3b013c4d1d6dba25e780b39658d79ba37af7057a3b7f15ffa102201fd9b4eaa9943f734928b99a83592c2e7bf342ea2680f6a2bb705167966b742001"
val rawP2PKScriptSig = "4847304402200a5c6163f07b8d3b013c4d1d6dba25e780b39658d79ba37af7057a3b7f15ffa102201fd9b4eaa9943f734928b99a83592c2e7bf342ea2680f6a2bb705167966b742001"
def p2pkScriptSig = ScriptSignature(rawP2PKScriptSig)
val rawP2PKScriptPubKey = "410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac"
val rawP2PKScriptPubKey = "43410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac"
def p2pkScriptPubKey = ScriptPubKey(rawP2PKScriptPubKey)
@ -165,7 +164,7 @@ object TestUtil {
* This is a script sig that doesn't have a signature strictly der encoded
* Zero-length R is correctly encoded
*/
def rawScriptSigNotStrictDerEncoded = "173014020002107777777777777777777777777777777701"
def rawScriptSigNotStrictDerEncoded = "173014020002107777777777777777777777777777777701"
def scriptSigNotStrictDerEncoded = ScriptSignature(rawScriptSigNotStrictDerEncoded)
def p2pkhScriptSigNotStrictDerEncoded = ScriptSignature.fromAsm(List(BytesToPushOntoStack(71),
@ -173,8 +172,6 @@ object TestUtil {
BytesToPushOntoStack(33),
ScriptConstant("02af7dad03e682fcd0427b5c24140c220ac9d8abe286c15f8cf5bf77eed19c3652")))
def multiSigScriptPubKeyHex = "695221025878e270211662a27181cf4d6ad4d2cf0e69a98a3815c086f587c7e9388d87182103fc85980e3fac1f3d8a5c3223c3ef5bffc1bd42d2cc42add8c3899cc66e7f1906210215b5bd050869166a70a7341b4f216e268b7c6c7504576dcea2cce7d11cc9a35f53ae"
}

View file

@ -1,7 +1,7 @@
package org.bitcoins.core.util
import org.bitcoins.core.crypto.{ECPrivateKey, ECPublicKey}
import org.bitcoins.core.currency.CurrencyUnits
import org.bitcoins.core.currency.{CurrencyUnit, CurrencyUnits}
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction._
@ -20,14 +20,11 @@ trait TransactionTestUtil extends BitcoinSLogger {
/**
* First input of this raw tx is a spending a multisignature output
* the first input is signed for this tx
* @return
*/
def rawSignedMultiSignatureTx = "0100000001d324b34c80c2e611b23c92ed1be31729b2856ae439d54b237a296d618425e912010000009300483045022100f5d203c0b36027ce61cd72ecd09b9629de029cd5cb34155c459f55999d7a08df02206db673c84556c202e5a5a354eca2bb6effeffff2fa040d34ecdbe642dc2219c001483045022100f0e0c53f1ebddb97407e801d90e5131f40dcab071168322454237b49f3bf74ca022069e2545cf9e2e7dc2c708be403f356c3d436fd498b68ef5f0c9138299547f14701ffffffff0140420f00000000001976a914edc96705498831b16782d439fa93164bc5c8db6f88ac00000000"
/**
* First input of this raw tx is a spending a multisignature output
* the first input is signed for this tx
* @return
*/
* the first input is signed for this tx */
def signedMultiSignatureTx = Transaction(rawSignedMultiSignatureTx)
/**
@ -36,7 +33,7 @@ trait TransactionTestUtil extends BitcoinSLogger {
* @param scriptPubKey
* @return the transaction and the output index of the scriptPubKey
*/
def buildCreditingTransaction(scriptPubKey : ScriptPubKey) : (Transaction,UInt32) = {
def buildCreditingTransaction(scriptPubKey : ScriptPubKey, amount: Option[CurrencyUnit] = None) : (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
@ -44,9 +41,9 @@ trait TransactionTestUtil extends BitcoinSLogger {
val outpoint = EmptyTransactionOutPoint
val scriptSignature = ScriptSignature("0000")
val scriptSignature = ScriptSignature("020000")
val input = TransactionInput(outpoint,scriptSignature,TransactionConstants.sequence)
val output = TransactionOutput(CurrencyUnits.zero,scriptPubKey)
val output = TransactionOutput(amount.getOrElse(CurrencyUnits.zero),scriptPubKey)
val tx = Transaction(TransactionConstants.version,Seq(input),Seq(output),TransactionConstants.lockTime)
(tx,UInt32.zero)
@ -59,24 +56,39 @@ trait TransactionTestUtil extends BitcoinSLogger {
* @param outputIndex
* @return the built spending transaction and the input index for the script signature
*/
def buildSpendingTransaction(creditingTx : Transaction,scriptSignature : ScriptSignature, outputIndex : UInt32) : (Transaction,UInt32) = {
def buildSpendingTransaction(creditingTx : Transaction,scriptSignature : ScriptSignature, outputIndex : UInt32,
witness: Option[(ScriptWitness,CurrencyUnit)] = None) : (Transaction,UInt32) = {
/*
CMutableTransaction txSpend;
txSpend.nVersion = 1;
txSpend.nLockTime = 0;
txSpend.vin.resize(1);
txSpend.vout.resize(1);
txSpend.wit.vtxinwit.resize(1);
txSpend.wit.vtxinwit[0].scriptWitness = scriptWitness;
txSpend.vin[0].prevout.hash = txCredit.GetHash();
txSpend.vin[0].prevout.n = 0;
txSpend.vin[0].scriptSig = scriptSig;
txSpend.vin[0].nSequence = std::numeric_limits<unsigned int>::max();
txSpend.vin[0].nSequence = CTxIn::SEQUENCE_FINAL;
txSpend.vout[0].scriptPubKey = CScript();
txSpend.vout[0].nValue = 0;*/
txSpend.vout[0].nValue = txCredit.vout[0].nValue;
*/
val outpoint = TransactionOutPoint(creditingTx.txId,outputIndex)
val input = TransactionInput(outpoint,scriptSignature,TransactionConstants.sequence)
val output = TransactionOutput(CurrencyUnits.zero,EmptyScriptPubKey)
val tx = Transaction(TransactionConstants.version,Seq(input),Seq(output),TransactionConstants.lockTime)
val tx = witness match {
case Some((scriptWitness,amount)) =>
val txWitness = TransactionWitness(Seq(scriptWitness))
val output = TransactionOutput(amount,EmptyScriptPubKey)
WitnessTransaction(TransactionConstants.version,Seq(input), Seq(output),
TransactionConstants.lockTime, txWitness)
case None =>
val output = TransactionOutput(CurrencyUnits.zero,EmptyScriptPubKey)
Transaction(TransactionConstants.version,Seq(input),Seq(output),TransactionConstants.lockTime)
}
/* val expectedHex = "01000000019ce5586f04dd407719ab7e2ed3583583b9022f29652702cfac5ed082013461fe000000004847304402200a5c6163f07b8d3b013c4d1d6dba25e780b39658d79ba37af7057a3b7f15ffa102201fd9b4eaa9943f734928b99a83592c2e7bf342ea2680f6a2bb705167966b742001ffffffff0100000000000000000000000000"
require(tx.hex == expectedHex,"\nExpected hex: " + expectedHex + "\nActual hex: " + tx.hex)*/
(tx,UInt32.zero)
@ -105,15 +117,12 @@ trait TransactionTestUtil extends BitcoinSLogger {
def signedMultiSignatureTransaction : (Transaction, Int, ScriptPubKey, Seq[ECPublicKey]) = {
val key1 = ECPrivateKey.fromWIFToPrivateKey("cVLwRLTvz3BxDAWkvS3yzT9pUcTCup7kQnfT2smRjvmmm1wAP6QT")
val key2 = ECPrivateKey.fromWIFToPrivateKey("cTine92s8GLpVqvebi8rYce3FrUYq78ZGQffBYCS1HmDPJdSTxUo")
def key3 = ECPrivateKey.fromWIFToPrivateKey("cVHwXSPRZmL9adctwBwmn4oTZdZMbaCsR5XF6VznqMgcvt1FDDxg")
val key3 = ECPrivateKey.fromWIFToPrivateKey("cVHwXSPRZmL9adctwBwmn4oTZdZMbaCsR5XF6VznqMgcvt1FDDxg")
(signedMultiSignatureTx,0,multiSignatureScriptPubKey, Seq(key1.publicKey,key2.publicKey,key3.publicKey))
}
/**
* Returns a p2sh transaction with its corresponding crediting output
* @return
*/
/** Returns a p2sh transaction with its corresponding crediting output */
def p2shTransactionWithSpendingInputAndCreditingOutput : (Transaction, TransactionInput, UInt32, TransactionOutput) = {
val creditingTx = TestUtil.p2sh2Of2CreditingTx
val spendingTx = TestUtil.p2sh2Of2Tx