Add invariant to WitnessTransaction that says inputs.length == witnes… (#954)

* Add invariant to WitnessTransaction that says inputs.length == witnesses.length

* Remove println

* Address code review
This commit is contained in:
Chris Stewart 2019-12-17 13:42:58 -06:00 committed by GitHub
parent 535fd2d58f
commit 713d76ae67
5 changed files with 39 additions and 28 deletions

View File

@ -205,12 +205,13 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
oldInput.sequence)
val updatedInputs = ubtx.inputs.updated(inputIndex.toInt, updatedInput)
val witness = EmptyWitness.fromInputs(updatedInputs)
val uwtx = WitnessTransaction(ubtx.version,
updatedInputs,
ubtx.outputs,
ubtx.lockTime,
EmptyWitness)
val uwtx = WitnessTransaction(version = ubtx.version,
inputs = updatedInputs,
outputs = ubtx.outputs,
lockTime = ubtx.lockTime,
witness = witness)
val wtxSigComp = {
WitnessTxSigComponentP2SH(transaction = uwtx,

View File

@ -20,7 +20,7 @@ class TransactionWitnessSpec extends BitcoinSUnitTest {
}
it must "be able to resize a witness to the given index" in {
val empty = EmptyWitness
val empty = EmptyWitness.fromN(0)
val pubKey = ECPrivateKey.freshPrivateKey.publicKey
val p2pkh = P2PKHScriptSignature(EmptyDigitalSignature, pubKey)
val scriptWit = P2WPKHWitnessV0.fromP2PKHScriptSig(p2pkh)
@ -31,7 +31,7 @@ class TransactionWitnessSpec extends BitcoinSUnitTest {
it must "fail to update a negative index witness" in {
intercept[IndexOutOfBoundsException] {
EmptyWitness.updated(-1, EmptyScriptWitness)
EmptyWitness.fromN(0).updated(-1, EmptyScriptWitness)
}
}

View File

@ -119,6 +119,10 @@ case object EmptyTransaction extends BaseTransaction {
}
sealed abstract class WitnessTransaction extends Transaction {
require(
inputs.length == witness.witnesses.length,
s"Must have same amount of inputs and witnesses in witness tx, inputs=${inputs.length} witnesses=${witness.witnesses.length}"
)
/** The txId for the witness transaction from satoshi's original serialization */
override def txId: DoubleSha256Digest = {
@ -224,7 +228,7 @@ object WitnessTransaction extends Factory[WitnessTransaction] {
btx.inputs,
btx.outputs,
btx.lockTime,
EmptyWitness)
EmptyWitness.fromInputs(btx.inputs))
case wtx: WitnessTransaction => wtx
}
}

View File

@ -55,10 +55,26 @@ sealed abstract class TransactionWitness extends NetworkElement {
}
}
/** Used to represent a transaction witness pre segwit, see BIP141 for details */
case object EmptyWitness extends TransactionWitness {
override val bytes: ByteVector = ByteVector.low(1)
override val witnesses: Vector[ScriptWitness] = Vector.empty
/** Each input (even if it does not spend a segwit output) needs to have a witness associated with it
* in a [[WitnessTransaction]]. This helper case class is used to "fill in" [[EmptyScriptWitness]] for
* the inputs that do not spend a [[org.bitcoins.core.protocol.script.WitnessScriptPubKeyV0 WitnessScriptPubKeyV0]]
*
* @see https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#specification
* */
case class EmptyWitness(witnesses: Vector[EmptyScriptWitness.type])
extends TransactionWitness
object EmptyWitness {
/** Generates an empty witness with n [[EmptyScriptWitness]] inside it */
def fromN(n: Int): EmptyWitness = {
val wits = Vector.fill(n)(EmptyScriptWitness)
new EmptyWitness(wits)
}
def fromInputs(inputs: Seq[TransactionInput]): EmptyWitness = {
fromN(inputs.length)
}
}
object TransactionWitness {
@ -69,7 +85,8 @@ object TransactionWitness {
if (witnesses.exists(_ != EmptyScriptWitness)) {
TransactionWitnessImpl(witnesses)
} else {
EmptyWitness
//means that everything must be a empty ScriptWitness
EmptyWitness.fromN(witnesses.length)
}
}

View File

@ -113,7 +113,8 @@ sealed abstract class Signer[-SpendingInfo <: UTXOSpendingInfo] {
spendingInfo.scriptWitnessOpt.map(scriptWit =>
TransactionWitness(Vector(scriptWit)))
val transactionWitness =
transactionWitnessOpt.getOrElse(EmptyWitness)
transactionWitnessOpt.getOrElse(
EmptyWitness.fromInputs(btx.inputs))
WitnessTransaction(btx.version,
btx.inputs,
@ -407,11 +408,7 @@ sealed abstract class P2WPKHSigner extends BitcoinSigner[P2WPKHV0SpendingInfo] {
}
case btx: BaseTransaction =>
val wtx = WitnessTransaction(btx.version,
btx.inputs,
btx.outputs,
btx.lockTime,
EmptyWitness)
val wtx = WitnessTransaction.toWitnessTx(btx)
sign(spendingInfoToSatisfy, wtx, isDummySignature)
}
@ -433,15 +430,7 @@ sealed abstract class P2WSHSigner extends BitcoinSigner[P2WSHV0SpendingInfo] {
} else {
val (_, output, inputIndex, _) = relevantInfo(spendingInfo, unsignedTx)
val wtx = unsignedTx match {
case btx: BaseTransaction =>
WitnessTransaction(btx.version,
btx.inputs,
btx.outputs,
btx.lockTime,
EmptyWitness)
case wtx: WitnessTransaction => wtx
}
val wtx = WitnessTransaction.toWitnessTx(unsignedTx)
val signedSigComponentF = BitcoinSigner.sign(
spendingInfo,