Rework WitnessVersion.rebuild() to take WitnessScriptPubKey as a parameter (#4393)

* Rework WitnessVersion.rebuild() to take WitnessScriptPubKey as a parameter rather than the raw asm

* Fix bug when calculating witness program length

* Fix witnessProgram bug in ScriptInterpreter.rebuildV0()

* remove invariant, fix type
This commit is contained in:
Chris Stewart 2022-06-14 20:08:13 -05:00 committed by GitHub
parent ad8802164c
commit acd1d227cf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 23 additions and 17 deletions

View file

@ -20,7 +20,7 @@ sealed trait WitnessVersion {
*/
def rebuild(
scriptWitness: ScriptWitness,
witnessProgram: Seq[ScriptToken]): Either[ScriptError, ScriptPubKey]
witnessSPK: WitnessScriptPubKey): Either[ScriptError, ScriptPubKey]
def version: ScriptNumberOperation
}
@ -30,7 +30,8 @@ case object WitnessVersion0 extends WitnessVersion {
/** Rebuilds a witness version 0 SPK program, see BIP141 */
override def rebuild(
scriptWitness: ScriptWitness,
witnessProgram: Seq[ScriptToken]): Either[ScriptError, ScriptPubKey] = {
witnessSPK: WitnessScriptPubKey): Either[ScriptError, ScriptPubKey] = {
val witnessProgram = witnessSPK.witnessProgram
val programBytes = BytesUtil.toByteVector(witnessProgram)
programBytes.size match {
case 20 =>
@ -68,8 +69,9 @@ case object WitnessVersion1 extends WitnessVersion {
override def rebuild(
scriptWitness: ScriptWitness,
witnessProgram: Seq[ScriptToken]): Either[ScriptError, ScriptPubKey] = {
throw new UnsupportedOperationException("Taproot is not yet supported")
witnessSPK: WitnessScriptPubKey): Either[ScriptError, ScriptPubKey] = {
throw new UnsupportedOperationException(
s"Taproot is not yet supported $scriptWitness $witnessSPK")
}
override def version: ScriptNumberOperation = OP_1
@ -85,8 +87,9 @@ case class UnassignedWitness(version: ScriptNumberOperation)
override def rebuild(
scriptWitness: ScriptWitness,
witnessProgram: Seq[ScriptToken]): Either[ScriptError, ScriptPubKey] =
witnessSPK: WitnessScriptPubKey): Either[ScriptError, ScriptPubKey] = {
Left(ScriptErrorDiscourageUpgradeableWitnessProgram)
}
}
object WitnessVersion {

View file

@ -335,9 +335,7 @@ sealed abstract class ScriptInterpreter {
case w: WitnessTxSigComponent =>
val scriptSig =
scriptPubKeyExecutedProgram.txSignatureComponent.scriptSignature
val (witnessVersion, witnessProgram) =
(witnessScriptPubKey.witnessVersion,
witnessScriptPubKey.witnessProgram)
val witnessVersion = witnessScriptPubKey.witnessVersion
val witness = w.witness
//scriptsig must be empty if we have raw p2wsh
//if script pubkey is a P2SHScriptPubKey then we have P2SH(P2WSH)
@ -348,7 +346,10 @@ sealed abstract class ScriptInterpreter {
scriptPubKeyExecutedProgram.failExecution(ScriptErrorPushSize)
)
} else {
verifyWitnessProgram(witnessVersion, witness, witnessProgram, w)
verifyWitnessProgram(witnessVersion,
witness,
witnessScriptPubKey,
w)
}
case (_, _) =>
Success(
@ -389,7 +390,7 @@ sealed abstract class ScriptInterpreter {
private def verifyWitnessProgram(
witnessVersion: WitnessVersion,
scriptWitness: ScriptWitness,
witnessProgram: Seq[ScriptToken],
witnessSPK: WitnessScriptPubKey,
wTxSigComponent: WitnessTxSigComponent): Try[ExecutedScriptProgram] = {
/** Helper function to run the post segwit execution checks */
@ -405,9 +406,12 @@ sealed abstract class ScriptInterpreter {
else evaluated
}
def rebuildV0(witness: ScriptWitness, program: Seq[ScriptToken]): Either[
def rebuildV0(
witness: ScriptWitness,
witnessSPKV0: WitnessScriptPubKey): Either[
ScriptError,
(Seq[ScriptToken], ScriptPubKey)] = {
val program = witnessSPKV0.witnessProgram
val programBytes = BytesUtil.toByteVector(program)
programBytes.size match {
case 20 =>
@ -416,7 +420,7 @@ sealed abstract class ScriptInterpreter {
Left(ScriptErrorWitnessProgramMisMatch)
} else {
for {
rebuilt <- WitnessVersion0.rebuild(witness, program)
rebuilt <- WitnessVersion0.rebuild(witness, witnessSPKV0)
r <- Right((witness.stack.map(ScriptConstant(_)), rebuilt))
} yield r
}
@ -425,7 +429,7 @@ sealed abstract class ScriptInterpreter {
if (scriptWitness.stack.isEmpty)
Left(ScriptErrorWitnessProgramWitnessEmpty)
else {
WitnessVersion0.rebuild(witness, program) match {
WitnessVersion0.rebuild(witness, witnessSPKV0) match {
case Right(rebuilt) =>
Right((witness.stack.tail.map(ScriptConstant(_)), rebuilt))
case Left(err) => Left(err)
@ -440,7 +444,7 @@ sealed abstract class ScriptInterpreter {
witnessVersion match {
case WitnessVersion0 =>
val either: Either[ScriptError, (Seq[ScriptToken], ScriptPubKey)] =
rebuildV0(scriptWitness, witnessProgram)
rebuildV0(scriptWitness, witnessSPK)
either match {
case Right((stack, scriptPubKey)) =>
val newWTxSigComponent =

View file

@ -440,8 +440,7 @@ trait BitcoinScriptUtil {
case w: WitnessScriptPubKey =>
txSignatureComponent match {
case wtxSigComponent: WitnessTxSigComponent =>
val scriptE = w.witnessVersion.rebuild(wtxSigComponent.witness,
w.witnessProgram)
val scriptE = w.witnessVersion.rebuild(wtxSigComponent.witness, w)
parseScriptEither(scriptE)
case rWTxSigComponent: WitnessTxSigComponentRebuilt =>
rWTxSigComponent.scriptPubKey.asm
@ -492,7 +491,7 @@ trait BitcoinScriptUtil {
case w: WitnessScriptPubKey =>
val wtx = spendingTransaction.asInstanceOf[WitnessTransaction]
val scriptT =
w.witnessVersion.rebuild(wtx.witness.witnesses(idx), w.witnessProgram)
w.witnessVersion.rebuild(wtx.witness.witnesses(idx), w)
parseScriptEither(scriptT)
case _: P2PKHScriptPubKey | _: P2PKScriptPubKey |