mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-02-24 06:57:51 +01:00
Merge branch 'segwit' of https://github.com/Christewart/bitcoin-s-core into segwit
This commit is contained in:
commit
8b1f4db05f
70 changed files with 2207 additions and 1756 deletions
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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] {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
||||
}
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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 {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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 = {
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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") {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,8 +49,4 @@ class ScriptSignatureFactoryTest extends FlatSpec with MustMatchers {
|
|||
}
|
||||
result must be (true)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue