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