Making WitnessV0TransactionSignatureComponent contain a WitnessTransaction

This commit is contained in:
Chris Stewart 2016-11-29 14:33:29 -06:00
parent 3c1cbf7ac1
commit f4beb545cc
8 changed files with 56 additions and 105 deletions

View File

@ -80,9 +80,16 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
}
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 +100,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

View File

@ -3,7 +3,7 @@ package org.bitcoins.core.crypto
import org.bitcoins.core.currency.CurrencyUnit
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction.Transaction
import org.bitcoins.core.protocol.transaction.{Transaction, WitnessTransaction}
import org.bitcoins.core.script.flag.ScriptFlag
/**
@ -43,6 +43,9 @@ sealed trait BaseTransactionSignatureComponent extends TransactionSignatureCompo
* [[https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki]]
*/
sealed trait WitnessV0TransactionSignatureComponent extends TransactionSignatureComponent {
override def transaction: WitnessTransaction
def witness: ScriptWitness
/** The amount of [[CurrencyUnit]] this input is spending */
@ -60,11 +63,11 @@ object TransactionSignatureComponent {
private case class BaseTransactionSignatureComponentImpl(transaction : Transaction, inputIndex : UInt32,
scriptPubKey : ScriptPubKey, flags : Seq[ScriptFlag]) extends BaseTransactionSignatureComponent
private case class WitnessV0TransactionSignatureComponentImpl(transaction : Transaction, inputIndex : UInt32,
private case class WitnessV0TransactionSignatureComponentImpl(transaction : WitnessTransaction, inputIndex : UInt32,
scriptPubKey : ScriptPubKey, flags : Seq[ScriptFlag],
witness: ScriptWitness, amount: CurrencyUnit) extends WitnessV0TransactionSignatureComponent
def apply(transaction : Transaction, inputIndex : UInt32, scriptPubKey : ScriptPubKey,
def apply(transaction : WitnessTransaction, inputIndex : UInt32, scriptPubKey : ScriptPubKey,
flags : Seq[ScriptFlag], witness : ScriptWitness,
amount: CurrencyUnit) : WitnessV0TransactionSignatureComponent = {
WitnessV0TransactionSignatureComponentImpl(transaction,inputIndex, scriptPubKey, flags, witness, amount)

View File

@ -156,7 +156,7 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper with Bit
def serializeForSignature(spendingTx: Transaction, inputIndex: UInt32, script: Seq[ScriptToken], hashType: HashType,
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])
@ -202,7 +202,7 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper with Bit
* [[https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki]]
* NOTE: This covers the amount of [[CurrencyUnit]] we are spending in the output
* */
def hashForSignature(spendingTx: Transaction, inputIndex: UInt32, script: Seq[ScriptToken], hashType: HashType,
def hashForSignature(spendingTx: WitnessTransaction, inputIndex: UInt32, script: Seq[ScriptToken], hashType: HashType,
amount: CurrencyUnit): DoubleSha256Digest = {
val serialization = serializeForSignature(spendingTx,inputIndex,script,hashType,amount)
@ -216,10 +216,14 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper with Bit
*/
def hashForSignature(txSignatureComponent: TransactionSignatureComponent, hashType: HashType): DoubleSha256Digest = txSignatureComponent match {
case b: BaseTransactionSignatureComponent =>
hashForSignature(b.transaction,b.inputIndex,
val hash = hashForSignature(b.transaction,b.inputIndex,
b.scriptPubKey.asm,hashType)
logger.info("btx signature hash: " + hash)
hash
case w : WitnessV0TransactionSignatureComponent =>
hashForSignature(w.transaction,w.inputIndex,w.scriptPubKey.asm, hashType, w.amount)
val hash = hashForSignature(w.transaction,w.inputIndex,w.scriptPubKey.asm, hashType, w.amount)
logger.info("wtx signature hash: " + hash)
hash
}

View File

@ -17,8 +17,6 @@ sealed trait ScriptSignature extends NetworkElement with BitcoinSLogger {
* 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.fromHex(hex)
@ -30,8 +28,6 @@ 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]
@ -92,12 +88,7 @@ object P2PKHScriptSignature extends Factory[P2PKHScriptSignature] {
/**
* 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)
@ -106,12 +97,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
@ -127,27 +113,15 @@ object P2PKHScriptSignature extends Factory[P2PKHScriptSignature] {
*/
sealed trait P2SHScriptSignature extends ScriptSignature {
/**
* The redeemScript represents the conditions that must be satisfied to spend the output
*
* @return
*/
/** The redeemScript represents the conditions that must be satisfied to spend the output */
def redeemScript : ScriptPubKey = ScriptPubKey(asm.last.bytes)
/**
* Returns the script signature of this p2shScriptSig with no serialized redeemScript
*
* @return
*/
/** Returns the script signature of this p2shScriptSig with no serialized redeemScript */
def scriptSignatureNoRedeemScript = ScriptSignature.fromAsm(splitAtRedeemScript(asm)._1)
/**
* 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])
@ -155,11 +129,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)
@ -170,11 +140,7 @@ 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))
@ -202,12 +168,7 @@ object P2SHScriptSignature extends Factory[P2SHScriptSignature] with BitcoinSLog
P2SHScriptSignatureImpl(hex)
}
/**
* 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 _ => false
@ -236,12 +197,7 @@ 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))
redeemScript
@ -322,18 +278,10 @@ 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))
}
@ -360,12 +308,7 @@ object P2PKScriptSignature extends Factory[P2PKScriptSignature] {
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
@ -468,21 +411,12 @@ case object EmptyScriptSignature extends ScriptSignature {
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)) =>

View File

@ -5,7 +5,7 @@ import org.bitcoins.core.crypto.{BaseTransactionSignatureComponent, TransactionS
import org.bitcoins.core.currency.CurrencyUnit
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.script.{ScriptPubKey, ScriptWitness, SignatureVersion}
import org.bitcoins.core.protocol.transaction.Transaction
import org.bitcoins.core.protocol.transaction.{Transaction, WitnessTransaction}
import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.flag.ScriptFlag
import org.bitcoins.core.script.result._
@ -244,7 +244,7 @@ object ScriptProgram {
/**
* 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 [[ScriptSignature]]
* 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
@ -253,7 +253,7 @@ object ScriptProgram {
* @param amount the amount of [[CurrencyUnit]] we are spending in this input
* @return the script program representing all of this information
*/
def apply(transaction: Transaction, scriptPubKey : ScriptPubKey, inputIndex : UInt32,
def apply(transaction: WitnessTransaction, scriptPubKey : ScriptPubKey, inputIndex : UInt32,
flags : Seq[ScriptFlag], witness: ScriptWitness,
amount: CurrencyUnit) : PreExecutionScriptProgram = {
val script = transaction.inputs(inputIndex.toInt).scriptSignature.asm
@ -266,14 +266,14 @@ object ScriptProgram {
ScriptProgram(p,stack,script)
}
def apply(transaction: Transaction, scriptPubKey : ScriptPubKey, inputIndex : UInt32, script : Seq[ScriptToken],
def apply(transaction: WitnessTransaction, scriptPubKey : ScriptPubKey, inputIndex : UInt32, script : Seq[ScriptToken],
flags : Seq[ScriptFlag], witness: ScriptWitness, amount: CurrencyUnit) : PreExecutionScriptProgram = {
val txSignatureComponent = TransactionSignatureComponent(transaction,inputIndex,scriptPubKey,flags,
witness, amount)
PreExecutionScriptProgramImpl(txSignatureComponent,Nil,script.toList,script.toList,Nil,flags)
}
def apply(transaction: Transaction, scriptPubKey : ScriptPubKey, inputIndex : UInt32, stack : Seq[ScriptToken],
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,witness, amount)
@ -311,7 +311,7 @@ object ScriptProgram {
}
/** Creates a fresh [[PreExecutionScriptProgram]] */
def apply(transaction: Transaction, scriptPubKey: ScriptPubKey, inputIndex: UInt32, stack: Seq[ScriptToken],
def apply(transaction: WitnessTransaction, scriptPubKey: ScriptPubKey, inputIndex: UInt32, stack: Seq[ScriptToken],
script: Seq[ScriptToken], originalScript: Seq[ScriptToken], altStack: Seq[ScriptToken],
flags: Seq[ScriptFlag], witness: ScriptWitness, sigVersion: SignatureVersion,
amount: CurrencyUnit): PreExecutionScriptProgram = {
@ -321,11 +321,11 @@ object ScriptProgram {
}
/** Creates a fresh instance of [[PreExecutionScriptProgram]] */
def apply(txSigComponent: TransactionSignatureComponent, stack: Seq[ScriptToken], script: Seq[ScriptToken],
def apply(txSigComponent: WitnessV0TransactionSignatureComponent, stack: Seq[ScriptToken], script: Seq[ScriptToken],
originalScript: Seq[ScriptToken], altStack: Seq[ScriptToken], flags: Seq[ScriptFlag],
witness: ScriptWitness, amount: CurrencyUnit): PreExecutionScriptProgram = {
ScriptProgram(txSigComponent.transaction, txSigComponent.scriptPubKey, txSigComponent.inputIndex, stack,
script,originalScript, altStack,flags, witness, txSigComponent.sigVersion, amount)
ScriptProgram(txSigComponent.transaction, txSigComponent.scriptPubKey, txSigComponent.inputIndex, stack,
script,originalScript, altStack,flags, witness, txSigComponent.sigVersion, amount)
}
/** Creates a fresh instance of [[org.bitcoins.core.script.PreExecutionScriptProgram]] */

View File

@ -157,7 +157,9 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
val either: Either[(Seq[ScriptToken], ScriptPubKey),ScriptError] = witnessVersion.rebuild(scriptWitness, witnessProgram)
either match {
case Left((stack,scriptPubKey)) =>
val newProgram = ScriptProgram(witnessTxSigComponent,stack,scriptPubKey.asm)
val w = witnessTxSigComponent
val newProgram = ScriptProgram(w.transaction,scriptPubKey,w.inputIndex,stack,scriptPubKey.asm, scriptPubKey.asm,Nil,
w.flags,w.witness,w.sigVersion,w.amount)
val evaluated = loop(newProgram,0)
logger.info("Stack after evaluating witness: " + evaluated.stack)
if (evaluated.stack.size != 1) ScriptProgram(evaluated,ScriptErrorEvalFalse)

View File

@ -449,12 +449,12 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with
val expected = "01000000ff78d7a91d1d9f2defd4b9d7e17c8b2182565453e83ceaacc78dd2ee095681f13bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e7066504490491d88b9f0dc24d271f0f67179bce5914afe1ac0f83f6cd205f8b807436d6f0000000043410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac0100000000000000ffffffffe5d196bfb21caca9dbd654cafb3b4dc0c4882c8927d2eb300d9539dd0b9342280000000001000000"
val hex = "0100000000010190491d88b9f0dc24d271f0f67179bce5914afe1ac0f83f6cd205f8b807436d6f0000000000ffffffff010100000000000000000247304402200d461c140cfdfcf36b94961db57ae8c18d1cb80e9d95a9e47ac22470c1bf125502201c8dc1cbfef6a3ef90acbbb992ca22fe9466ee6f9d4898eda277a7ac3ab4b2510143410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac00000000"
val spendingTx = Transaction(hex)
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 witnessRedeemScript = ScriptPubKey("410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac")
val amount = Satoshis(Int64(1))
val witnessStack = Seq("304402200d461c140cfdfcf36b94961db57ae8c18d1cb80e9d95a9e47ac22470c1bf125502201c8dc1cbfef6a3ef90acbbb992ca22fe9466ee6f9d4898eda277a7ac3ab4b25101",

View File

@ -1,12 +1,13 @@
package org.bitcoins.core.script.interpreter
import org.bitcoins.core.crypto.{ECPrivateKey, TransactionSignatureComponent}
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._
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._
@ -64,10 +65,11 @@ class ScriptInterpreterTest extends FlatSpec with MustMatchers with ScriptInterp
logger.info("Flags after parsing: " + flags)
logger.info("Witness after parsing: " + witness)
val program = witness match {
case Some((w, amount)) => ScriptProgram(tx, scriptPubKey, inputIndex, flags, w, amount)
case Some((w, amount)) => ScriptProgram(tx.asInstanceOf[WitnessTransaction], scriptPubKey, inputIndex, flags, w, amount)
case None => ScriptProgram(tx, scriptPubKey, inputIndex, flags)
}
tx.hex must be ("0100000000010190491d88b9f0dc24d271f0f67179bce5914afe1ac0f83f6cd205f8b807436d6f0000000000ffffffff010100000000000000000247304402200d461c140cfdfcf36b94961db57ae8c18d1cb80e9d95a9e47ac22470c1bf125502201c8dc1cbfef6a3ef90acbbb992ca22fe9466ee6f9d4898eda277a7ac3ab4b2510143410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac00000000")
withClue(testCase.raw) {
ScriptInterpreter.run(program) must equal (testCase.expectedResult)
}