mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-01-19 05:43:51 +01:00
Refactor WitnessVersion.rebuild() to be Either[ScriptError,ScriptPubKey] to make the taproot implemtation easier (#4382)
This commit is contained in:
parent
ab215e26df
commit
b021649ac4
@ -2,11 +2,10 @@ package org.bitcoins.core.protocol.script
|
||||
|
||||
import org.bitcoins.core.protocol.CompactSizeUInt
|
||||
import org.bitcoins.core.script.constant._
|
||||
import org.bitcoins.core.script.result._
|
||||
import org.bitcoins.core.util.BytesUtil
|
||||
import org.bitcoins.crypto.{CryptoUtil, Sha256Digest, Sha256Hash160Digest}
|
||||
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
/** Created by chris on 11/10/16.
|
||||
* The version of the [[org.bitcoins.core.protocol.script.WitnessScriptPubKey WitnessScriptPubKey]],
|
||||
* this indicates how a [[org.bitcoins.core.protocol.script.ScriptWitness ScriptWitness]] is rebuilt.
|
||||
@ -16,11 +15,12 @@ sealed trait WitnessVersion {
|
||||
|
||||
/** Rebuilds the full script from the given witness and [[org.bitcoins.core.protocol.script.ScriptPubKey ScriptPubKey]]
|
||||
* Either returns the [[org.bitcoins.core.protocol.script.ScriptPubKey ScriptPubKey]]
|
||||
* it needs to be executed against or the failure that was encountered while rebuilding the witness
|
||||
* it needs to be executed against or the [[ScriptError]] that was encountered when
|
||||
* building the rebuild the scriptpubkey from the witness
|
||||
*/
|
||||
def rebuild(
|
||||
scriptWitness: ScriptWitness,
|
||||
witnessProgram: Seq[ScriptToken]): Try[ScriptPubKey]
|
||||
witnessProgram: Seq[ScriptToken]): Either[ScriptError, ScriptPubKey]
|
||||
|
||||
def version: ScriptNumberOperation
|
||||
}
|
||||
@ -30,38 +30,34 @@ case object WitnessVersion0 extends WitnessVersion {
|
||||
/** Rebuilds a witness version 0 SPK program, see BIP141 */
|
||||
override def rebuild(
|
||||
scriptWitness: ScriptWitness,
|
||||
witnessProgram: Seq[ScriptToken]): Try[ScriptPubKey] = {
|
||||
witnessProgram: Seq[ScriptToken]): Either[ScriptError, ScriptPubKey] = {
|
||||
val programBytes = BytesUtil.toByteVector(witnessProgram)
|
||||
programBytes.size match {
|
||||
case 20 =>
|
||||
//p2wpkh
|
||||
val hash = Sha256Hash160Digest(programBytes)
|
||||
Success(P2PKHScriptPubKey(hash))
|
||||
Right(P2PKHScriptPubKey(hash))
|
||||
case 32 =>
|
||||
//p2wsh
|
||||
if (scriptWitness.stack.isEmpty)
|
||||
Failure(
|
||||
new IllegalArgumentException(
|
||||
"P2WSH cannot be rebuilt without redeem script"))
|
||||
else {
|
||||
if (scriptWitness.stack.isEmpty) {
|
||||
Left(ScriptErrorWitnessProgramWitnessEmpty)
|
||||
} else {
|
||||
//need to check if the hashes match
|
||||
val stackTop = scriptWitness.stack.head
|
||||
val stackHash = CryptoUtil.sha256(stackTop)
|
||||
val witnessHash = Sha256Digest(witnessProgram.head.bytes)
|
||||
if (stackHash != witnessHash) {
|
||||
Failure(new IllegalArgumentException(
|
||||
s"Witness hash $witnessHash did not match stack hash $stackHash"))
|
||||
Left(ScriptErrorWitnessProgramMisMatch)
|
||||
} else {
|
||||
val compactSizeUInt =
|
||||
CompactSizeUInt.calculateCompactSizeUInt(stackTop)
|
||||
val scriptPubKey = ScriptPubKey(compactSizeUInt.bytes ++ stackTop)
|
||||
Success(scriptPubKey)
|
||||
Right(scriptPubKey)
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
//witness version 0 programs need to be 20 bytes or 32 bytes in size
|
||||
Failure(new IllegalArgumentException(
|
||||
s"Witness program had invalid length (${programBytes.length}) for version 0, must be 20 or 30: $witnessProgram"))
|
||||
Left(ScriptErrorWitnessProgramWrongLength)
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,7 +68,7 @@ case object WitnessVersion1 extends WitnessVersion {
|
||||
|
||||
override def rebuild(
|
||||
scriptWitness: ScriptWitness,
|
||||
witnessProgram: Seq[ScriptToken]): Try[ScriptPubKey] = {
|
||||
witnessProgram: Seq[ScriptToken]): Either[ScriptError, ScriptPubKey] = {
|
||||
throw new UnsupportedOperationException("Taproot is not yet supported")
|
||||
}
|
||||
|
||||
@ -89,10 +85,8 @@ case class UnassignedWitness(version: ScriptNumberOperation)
|
||||
|
||||
override def rebuild(
|
||||
scriptWitness: ScriptWitness,
|
||||
witnessProgram: Seq[ScriptToken]): Try[ScriptPubKey] =
|
||||
Failure(
|
||||
new UnsupportedOperationException(
|
||||
s"Rebuilding is not defined for version $version yet."))
|
||||
witnessProgram: Seq[ScriptToken]): Either[ScriptError, ScriptPubKey] =
|
||||
Left(ScriptErrorDiscourageUpgradeableWitnessProgram)
|
||||
}
|
||||
|
||||
object WitnessVersion {
|
||||
|
@ -415,9 +415,10 @@ sealed abstract class ScriptInterpreter {
|
||||
if (witness.stack.size != 2) {
|
||||
Left(ScriptErrorWitnessProgramMisMatch)
|
||||
} else {
|
||||
Right(
|
||||
(witness.stack.map(ScriptConstant(_)),
|
||||
WitnessVersion0.rebuild(witness, program).get))
|
||||
for {
|
||||
rebuilt <- WitnessVersion0.rebuild(witness, program)
|
||||
r <- Right((witness.stack.map(ScriptConstant(_)), rebuilt))
|
||||
} yield r
|
||||
}
|
||||
case 32 =>
|
||||
//p2wsh
|
||||
@ -425,10 +426,9 @@ sealed abstract class ScriptInterpreter {
|
||||
Left(ScriptErrorWitnessProgramWitnessEmpty)
|
||||
else {
|
||||
WitnessVersion0.rebuild(witness, program) match {
|
||||
case Success(rebuilt) =>
|
||||
case Right(rebuilt) =>
|
||||
Right((witness.stack.tail.map(ScriptConstant(_)), rebuilt))
|
||||
case Failure(_) =>
|
||||
Left(ScriptErrorWitnessProgramMisMatch)
|
||||
case Left(err) => Left(err)
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
|
@ -440,9 +440,9 @@ trait BitcoinScriptUtil {
|
||||
case w: WitnessScriptPubKey =>
|
||||
txSignatureComponent match {
|
||||
case wtxSigComponent: WitnessTxSigComponent =>
|
||||
val scriptT = w.witnessVersion.rebuild(wtxSigComponent.witness,
|
||||
val scriptE = w.witnessVersion.rebuild(wtxSigComponent.witness,
|
||||
w.witnessProgram)
|
||||
parseScriptTry(scriptT)
|
||||
parseScriptEither(scriptE)
|
||||
case rWTxSigComponent: WitnessTxSigComponentRebuilt =>
|
||||
rWTxSigComponent.scriptPubKey.asm
|
||||
case _: BaseTxSigComponent =>
|
||||
@ -493,7 +493,7 @@ trait BitcoinScriptUtil {
|
||||
val wtx = spendingTransaction.asInstanceOf[WitnessTransaction]
|
||||
val scriptT =
|
||||
w.witnessVersion.rebuild(wtx.witness.witnesses(idx), w.witnessProgram)
|
||||
parseScriptTry(scriptT)
|
||||
parseScriptEither(scriptT)
|
||||
|
||||
case _: P2PKHScriptPubKey | _: P2PKScriptPubKey |
|
||||
_: P2PKWithTimeoutScriptPubKey | _: MultiSignatureScriptPubKey |
|
||||
@ -553,10 +553,13 @@ trait BitcoinScriptUtil {
|
||||
} else program.originalScript
|
||||
}
|
||||
|
||||
private def parseScriptTry(scriptT: Try[ScriptPubKey]): Seq[ScriptToken] =
|
||||
private def parseScriptEither(
|
||||
scriptT: Either[ScriptError, ScriptPubKey]): Seq[ScriptToken] =
|
||||
scriptT match {
|
||||
case Success(scriptPubKey) => scriptPubKey.asm
|
||||
case Failure(err) => throw err
|
||||
case Right(scriptPubKey) => scriptPubKey.asm
|
||||
case Left(err) =>
|
||||
throw new IllegalArgumentException(
|
||||
s"Could not parse witness script, got err=$err")
|
||||
}
|
||||
|
||||
/** Casts the given script token to a boolean value
|
||||
|
Loading…
Reference in New Issue
Block a user