Spending Info ADT use (#840)

* Replaced TxBuilder signAndAddInput match with UTXOSpendingInfo match, tests don't pass

* Fixed tests!
This commit is contained in:
Nadav Kohen 2019-10-30 15:00:41 -05:00 committed by Chris Stewart
parent 04edc40b7e
commit 310ccbb838
5 changed files with 283 additions and 188 deletions

View file

@ -20,6 +20,10 @@ class UTXOSpendingInfoTest extends BitcoinSAsyncTest {
ScriptGenerators.scriptPubKey.map(_._1).sample.get
}
def randomNonWitnessSPK: NonWitnessScriptPubKey = {
ScriptGenerators.nonWitnessScriptPubKey.map(_._1).sample.get
}
def randomWitnessSPK: WitnessScriptPubKeyV0 = {
ScriptGenerators.witnessScriptPubKeyV0.map(_._1).sample.get
}
@ -34,12 +38,12 @@ class UTXOSpendingInfoTest extends BitcoinSAsyncTest {
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
assertThrows[IllegalArgumentException] {
P2SHSpendingInfo(outPoint = outPoint,
amount = CurrencyUnits.zero,
scriptPubKey = p2sh,
signers = Seq(privKey),
hashType = HashType.sigHashAll,
redeemScript = randomSPK)
P2SHNoNestSpendingInfo(outPoint = outPoint,
amount = CurrencyUnits.zero,
scriptPubKey = p2sh,
signers = Seq(privKey),
hashType = HashType.sigHashAll,
redeemScript = randomNonWitnessSPK)
}
}

View file

@ -26,19 +26,22 @@ import scala.util.{Failure, Success, Try}
*/
sealed abstract class ScriptPubKey extends Script
sealed trait NonWitnessScriptPubKey extends ScriptPubKey
/**
* Represents a
* [[https://bitcoin.org/en/developer-guide#pay-to-public-key-hash-p2pkh pay-to-pubkey hash script pubkey]]
*
* Format: `OP_DUP OP_HASH160 <PubKeyHash> OP_EQUALVERIFY OP_CHECKSIG`
*/
sealed trait P2PKHScriptPubKey extends ScriptPubKey {
sealed trait P2PKHScriptPubKey extends NonWitnessScriptPubKey {
def pubKeyHash: Sha256Hash160Digest =
Sha256Hash160Digest(asm(asm.length - 3).bytes)
}
object P2PKHScriptPubKey extends ScriptFactory[P2PKHScriptPubKey] {
private case class P2PKHScriptPubKeyImpl(
override val asm: Vector[ScriptToken])
extends P2PKHScriptPubKey {
@ -89,7 +92,7 @@ object P2PKHScriptPubKey extends ScriptFactory[P2PKHScriptPubKey] {
* https://bitcoin.org/en/developer-guide#multisig
* Format: <m> <A pubkey> [B pubkey] [C pubkey...] <n> OP_CHECKMULTISIG
*/
sealed trait MultiSignatureScriptPubKey extends ScriptPubKey {
sealed trait MultiSignatureScriptPubKey extends NonWitnessScriptPubKey {
/** Returns the amount of required signatures for this multisignature script pubkey output */
def requiredSigs: Int = {
@ -259,7 +262,7 @@ object MultiSignatureScriptPubKey
* Represents a [[https://bitcoin.org/en/developer-guide#pay-to-script-hash-p2sh pay-to-scripthash public key]]
* Format: `OP_HASH160 <Hash160(redeemScript)> OP_EQUAL`
*/
sealed trait P2SHScriptPubKey extends ScriptPubKey {
sealed trait P2SHScriptPubKey extends NonWitnessScriptPubKey {
/** The hash of the script for which this scriptPubKey is being created from */
def scriptHash: Sha256Hash160Digest =
@ -267,6 +270,7 @@ sealed trait P2SHScriptPubKey extends ScriptPubKey {
}
object P2SHScriptPubKey extends ScriptFactory[P2SHScriptPubKey] {
private case class P2SHScriptPubKeyImpl(override val asm: Vector[ScriptToken])
extends P2SHScriptPubKey {
override def toString = "P2SHScriptPubKeyImpl(" + hex + ")"
@ -309,7 +313,7 @@ object P2SHScriptPubKey extends ScriptFactory[P2SHScriptPubKey] {
* Represents a [[https://bitcoin.org/en/developer-guide#pubkey pay to public key script public key]]
* Format: `<pubkey> OP_CHECKSIG`
*/
sealed trait P2PKScriptPubKey extends ScriptPubKey {
sealed trait P2PKScriptPubKey extends NonWitnessScriptPubKey {
def publicKey: ECPublicKey =
ECPublicKey(BitcoinScriptUtil.filterPushOps(asm).head.bytes)
@ -346,7 +350,7 @@ object P2PKScriptPubKey extends ScriptFactory[P2PKScriptPubKey] {
}
sealed trait LockTimeScriptPubKey extends ScriptPubKey {
sealed trait LockTimeScriptPubKey extends NonWitnessScriptPubKey {
/** Determines the nested `ScriptPubKey` inside the `LockTimeScriptPubKey` */
def nestedScriptPubKey: ScriptPubKey = {
@ -398,6 +402,7 @@ object LockTimeScriptPubKey extends ScriptFactory[LockTimeScriptPubKey] {
sealed trait CLTVScriptPubKey extends LockTimeScriptPubKey
object CLTVScriptPubKey extends ScriptFactory[CLTVScriptPubKey] {
private case class CLTVScriptPubKeyImpl(override val asm: Vector[ScriptToken])
extends CLTVScriptPubKey {
override def toString = "CLTVScriptPubKeyImpl(" + hex + ")"
@ -483,6 +488,7 @@ object CLTVScriptPubKey extends ScriptFactory[CLTVScriptPubKey] {
sealed trait CSVScriptPubKey extends LockTimeScriptPubKey
object CSVScriptPubKey extends ScriptFactory[CSVScriptPubKey] {
private case class CSVScriptPubKeyImpl(override val asm: Vector[ScriptToken])
extends CSVScriptPubKey {
override def toString = "CSVScriptPubKeyImpl(" + hex + ")"
@ -545,9 +551,10 @@ object CSVScriptPubKey extends ScriptFactory[CSVScriptPubKey] {
}
sealed trait NonStandardScriptPubKey extends ScriptPubKey
sealed trait NonStandardScriptPubKey extends NonWitnessScriptPubKey
object NonStandardScriptPubKey extends ScriptFactory[NonStandardScriptPubKey] {
private case class NonStandardScriptPubKeyImpl(
override val asm: Vector[ScriptToken])
extends NonStandardScriptPubKey {
@ -565,7 +572,7 @@ object NonStandardScriptPubKey extends ScriptFactory[NonStandardScriptPubKey] {
}
/** Represents the empty ScriptPubKey */
case object EmptyScriptPubKey extends ScriptPubKey {
case object EmptyScriptPubKey extends NonWitnessScriptPubKey {
override def asm: Seq[ScriptToken] = Vector.empty
}
@ -705,6 +712,7 @@ sealed abstract class P2WPKHWitnessSPKV0 extends WitnessScriptPubKeyV0 {
}
object P2WPKHWitnessSPKV0 extends ScriptFactory[P2WPKHWitnessSPKV0] {
private case class P2WPKHWitnessSPKV0Impl(
override val asm: Vector[ScriptToken])
extends P2WPKHWitnessSPKV0
@ -749,6 +757,7 @@ sealed abstract class P2WSHWitnessSPKV0 extends WitnessScriptPubKeyV0 {
}
object P2WSHWitnessSPKV0 extends ScriptFactory[P2WSHWitnessSPKV0] {
private case class P2WSHWitnessSPKV0Impl(
override val asm: Vector[ScriptToken])
extends P2WSHWitnessSPKV0
@ -790,6 +799,7 @@ sealed trait UnassignedWitnessScriptPubKey extends WitnessScriptPubKey {
object UnassignedWitnessScriptPubKey
extends ScriptFactory[UnassignedWitnessScriptPubKey] {
private case class UnassignedWitnessScriptPubKeyImpl(
override val asm: Vector[ScriptToken])
extends UnassignedWitnessScriptPubKey {
@ -815,7 +825,7 @@ object UnassignedWitnessScriptPubKey
* See BIP141 for more info
* [[https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#commitment-structure]]
*/
sealed trait WitnessCommitment extends ScriptPubKey {
sealed trait WitnessCommitment extends NonWitnessScriptPubKey {
/** The commitment to the
* [[org.bitcoins.core.protocol.transaction.WitnessTransaction WitnessTransaction]]s in the
@ -825,6 +835,7 @@ sealed trait WitnessCommitment extends ScriptPubKey {
}
object WitnessCommitment extends ScriptFactory[WitnessCommitment] {
private case class WitnessCommitmentImpl(
override val asm: Vector[ScriptToken])
extends WitnessCommitment {

View file

@ -19,7 +19,19 @@ import org.bitcoins.core.script.locktime.LockTimeInterpreter
import org.bitcoins.core.util.BitcoinSLogger
import org.bitcoins.core.wallet.fee.FeeUnit
import org.bitcoins.core.wallet.signer._
import org.bitcoins.core.wallet.utxo.{BitcoinUTXOSpendingInfo, UTXOSpendingInfo}
import org.bitcoins.core.wallet.utxo.{
BitcoinUTXOSpendingInfo,
LockTimeSpendingInfo,
MultiSignatureSpendingInfo,
P2PKHSpendingInfo,
P2PKSpendingInfo,
P2SHNestedSegwitV0UTXOSpendingInfo,
P2SHNoNestSpendingInfo,
P2SHSpendingInfo,
SegwitV0NativeUTXOSpendingInfo,
UTXOSpendingInfo,
UnassignedSegwitNativeUTXOSpendingInfo
}
import scala.annotation.tailrec
import scala.concurrent.{ExecutionContext, Future}
@ -244,167 +256,147 @@ sealed abstract class BitcoinTxBuilder extends TxBuilder {
unsignedTx: Transaction,
dummySignatures: Boolean)(
implicit ec: ExecutionContext): Future[Transaction] = {
val outpoint = utxo.outPoint
val output = utxo.output
val signers = utxo.signers
val redeemScriptOpt = utxo.redeemScriptOpt
val scriptWitnessOpt = utxo.scriptWitnessOpt
val hashType = utxo.hashType
val idx =
unsignedTx.inputs.zipWithIndex.find(_._1.previousOutput == outpoint)
unsignedTx.inputs.zipWithIndex.find(_._1.previousOutput == utxo.outPoint)
if (idx.isEmpty) {
Future.fromTry(TxBuilderError.MissingOutPoint)
} else {
val inputIndex = UInt32(idx.get._2)
val oldInput = unsignedTx.inputs(inputIndex.toInt)
output.scriptPubKey match {
case _: P2PKScriptPubKey =>
utxo match {
case p2pkInfo: P2PKSpendingInfo =>
P2PKSigner
.sign(utxo, unsignedTx, dummySignatures)
.sign(p2pkInfo, unsignedTx, dummySignatures)
.map(_.transaction)
case _: P2PKHScriptPubKey =>
case p2pkhInfo: P2PKHSpendingInfo =>
P2PKHSigner
.sign(utxo, unsignedTx, dummySignatures)
.sign(p2pkhInfo, unsignedTx, dummySignatures)
.map(_.transaction)
case _: MultiSignatureScriptPubKey =>
case multiSigInfo: MultiSignatureSpendingInfo =>
MultiSigSigner
.sign(utxo, unsignedTx, dummySignatures)
.sign(multiSigInfo, unsignedTx, dummySignatures)
.map(_.transaction)
case _: LockTimeScriptPubKey =>
case lockTimeInfo: LockTimeSpendingInfo =>
LockTimeSigner
.sign(utxo, unsignedTx, dummySignatures)
.sign(lockTimeInfo, unsignedTx, dummySignatures)
.map(_.transaction)
case p2sh: P2SHScriptPubKey =>
redeemScriptOpt match {
case Some(redeemScript) =>
if (p2sh != P2SHScriptPubKey(redeemScript)) {
Future.fromTry(TxBuilderError.WrongRedeemScript)
} else {
val signedTxF: Future[Transaction] = redeemScript match {
case p2wpkh: P2WPKHWitnessSPKV0 =>
val uwtx = WitnessTransaction.toWitnessTx(unsignedTx)
//breaks an abstraction inside of all of the signers
//won't be able to be handled properly until gemini stuff
//is open sourced
signP2SHP2WPKH(unsignedTx = uwtx,
inputIndex = inputIndex,
output = output,
p2wpkh = p2wpkh,
utxo = utxo,
hashType = hashType,
dummySignatures = dummySignatures)
case _: P2PKScriptPubKey | _: P2PKHScriptPubKey |
_: MultiSignatureScriptPubKey | _: LockTimeScriptPubKey |
_: NonStandardScriptPubKey | _: WitnessCommitment |
_: UnassignedWitnessScriptPubKey | _: P2WSHWitnessSPKV0 |
EmptyScriptPubKey =>
val input = TransactionInput(outpoint,
EmptyScriptSignature,
oldInput.sequence)
val updatedTx =
unsignedTx.updateInput(inputIndex.toInt, input)
val updatedOutput =
TransactionOutput(output.value, redeemScript)
val updatedUTXOInfo = BitcoinUTXOSpendingInfo(
outpoint,
updatedOutput,
signers,
None,
scriptWitnessOpt,
hashType)
val signedTxEither = signAndAddInput(updatedUTXOInfo,
updatedTx,
dummySignatures)
signedTxEither.map { signedTx =>
val i = signedTx.inputs(inputIndex.toInt)
val p2sh =
P2SHScriptSignature(i.scriptSignature, redeemScript)
val signedInput =
TransactionInput(i.previousOutput, p2sh, i.sequence)
val signedInputs =
signedTx.inputs.updated(inputIndex.toInt, signedInput)
signedTx match {
case btx: BaseTransaction =>
BaseTransaction(btx.version,
signedInputs,
btx.outputs,
btx.lockTime)
case wtx: WitnessTransaction =>
WitnessTransaction(wtx.version,
signedInputs,
wtx.outputs,
wtx.lockTime,
wtx.witness)
}
}
case _: P2SHScriptPubKey =>
Future.fromTry(TxBuilderError.NestedP2SHSPK)
}
signedTxF
}
case None => Future.fromTry(TxBuilderError.NoRedeemScript)
case segWitInfo @ SegwitV0NativeUTXOSpendingInfo(_,
_,
witnessSPK,
_,
_,
_) =>
witnessSPK match {
case _: P2WPKHWitnessSPKV0 =>
P2WPKHSigner
.sign(segWitInfo, unsignedTx, dummySignatures)
.map(_.transaction)
case _: P2WSHWitnessSPKV0 =>
P2WSHSigner
.sign(segWitInfo, unsignedTx, dummySignatures)
.map(_.transaction)
}
case p2shInfo: P2SHNoNestSpendingInfo =>
signAndAddInputForP2SH(p2shInfo,
unsignedTx,
oldInput.sequence,
inputIndex.toInt,
dummySignatures)
case p2shNestedSegwitInfo @ P2SHNestedSegwitV0UTXOSpendingInfo(
_,
amount,
scriptPubKey,
_,
hashType,
redeemWitnessScript,
_) =>
redeemWitnessScript match {
case _: P2WSHWitnessSPKV0 =>
signAndAddInputForP2SH(p2shNestedSegwitInfo,
unsignedTx,
oldInput.sequence,
inputIndex.toInt,
dummySignatures)
case p2wpkh: P2WPKHWitnessSPKV0 =>
val uwtx = WitnessTransaction.toWitnessTx(unsignedTx)
case _: P2WPKHWitnessSPKV0 =>
P2WPKHSigner
.sign(utxo, unsignedTx, dummySignatures)
.map(_.transaction)
case p2wshSPK: P2WSHWitnessSPKV0 =>
val p2wshScriptWitF = scriptWitnessOpt match {
case Some(EmptyScriptWitness | _: P2WPKHWitnessV0) =>
Future.fromTry(TxBuilderError.WrongWitness)
case Some(x: P2WSHWitnessV0) => Future.successful(x)
case None => Future.fromTry(TxBuilderError.NoWitness)
//breaks an abstraction inside of all of the signers
//won't be able to be handled properly until gemini stuff
//is open sourced
signP2SHP2WPKH(
unsignedTx = uwtx,
inputIndex = inputIndex,
output = TransactionOutput(amount, scriptPubKey),
p2wpkh = p2wpkh,
utxo = utxo,
hashType = hashType,
dummySignatures = dummySignatures
)
}
val redeemScriptF = p2wshScriptWitF.map(_.redeemScript)
val validatedRedeemScriptF = redeemScriptF.flatMap { redeemScript =>
if (P2WSHWitnessSPKV0(redeemScript) != p2wshSPK) {
Future.fromTry(TxBuilderError.WrongWitness)
} else {
Future.successful(redeemScript)
}
}
val sigComponentF = validatedRedeemScriptF.flatMap {
case _: P2PKScriptPubKey | _: P2PKHScriptPubKey |
_: MultiSignatureScriptPubKey | _: LockTimeScriptPubKey =>
P2WSHSigner.sign(utxo, unsignedTx, dummySignatures)
case _: P2WPKHWitnessSPKV0 | _: P2WSHWitnessSPKV0 =>
Future.fromTry(TxBuilderError.NestedWitnessSPK)
case _: P2SHScriptPubKey =>
Future.fromTry(TxBuilderError.NestedP2SHSPK)
case _: NonStandardScriptPubKey | _: WitnessCommitment |
EmptyScriptPubKey | _: UnassignedWitnessScriptPubKey =>
Future.fromTry(TxBuilderError.NoSigner)
}
sigComponentF.map(_.transaction)
case _: NonStandardScriptPubKey | _: WitnessCommitment |
EmptyScriptPubKey | _: UnassignedWitnessScriptPubKey =>
case _: UnassignedSegwitNativeUTXOSpendingInfo =>
Future.fromTry(TxBuilderError.NoSigner)
}
}
}
private def signAndAddInputForP2SH(
spendingInfo: P2SHSpendingInfo,
unsignedTx: Transaction,
sequence: UInt32,
inputIndex: Int,
dummySignatures: Boolean)(
implicit ec: ExecutionContext): Future[Transaction] = {
require(!spendingInfo.redeemScript.isInstanceOf[P2WPKHWitnessSPKV0],
"Should call signP2SHP2WPKH")
val outpoint = spendingInfo.outPoint
val redeemScript = spendingInfo.redeemScript
val input =
TransactionInput(outpoint, EmptyScriptSignature, sequence)
val updatedTx =
unsignedTx.updateInput(inputIndex, input)
val updatedOutput =
TransactionOutput(spendingInfo.amount, redeemScript)
val updatedUTXOInfo = BitcoinUTXOSpendingInfo(outpoint,
updatedOutput,
spendingInfo.signers,
None,
spendingInfo.scriptWitnessOpt,
spendingInfo.hashType)
val signedTxEither =
signAndAddInput(updatedUTXOInfo, updatedTx, dummySignatures)
signedTxEither.map { signedTx =>
val i = signedTx.inputs(inputIndex.toInt)
val p2sh =
P2SHScriptSignature(i.scriptSignature, redeemScript)
val signedInput =
TransactionInput(i.previousOutput, p2sh, i.sequence)
val signedInputs =
signedTx.inputs.updated(inputIndex.toInt, signedInput)
signedTx match {
case btx: BaseTransaction =>
BaseTransaction(btx.version, signedInputs, btx.outputs, btx.lockTime)
case wtx: WitnessTransaction =>
WitnessTransaction(wtx.version,
signedInputs,
wtx.outputs,
wtx.lockTime,
wtx.witness)
}
}
}
/**
* Returns a valid sequence number for the given [[ScriptNumber]]
* A transaction needs a valid sequence number to spend a OP_CHECKSEQUENCEVERIFY script.

View file

@ -3,12 +3,12 @@ package org.bitcoins.core.wallet.utxo
import org.bitcoins.core.crypto.Sign
import org.bitcoins.core.currency.CurrencyUnit
import org.bitcoins.core.protocol.script.{
CLTVScriptPubKey,
CSVScriptPubKey,
EmptyScriptPubKey,
EmptyScriptWitness,
LockTimeScriptPubKey,
MultiSignatureScriptPubKey,
NonStandardScriptPubKey,
NonWitnessScriptPubKey,
P2PKHScriptPubKey,
P2PKScriptPubKey,
P2SHScriptPubKey,
@ -60,6 +60,7 @@ sealed abstract class UTXOSpendingInfo {
}
sealed trait BitcoinUTXOSpendingInfo extends UTXOSpendingInfo {
protected def isValidScriptWitness(
spk: WitnessScriptPubKeyV0,
scriptWitness: ScriptWitnessV0): Boolean = {
@ -73,7 +74,18 @@ sealed trait BitcoinUTXOSpendingInfo extends UTXOSpendingInfo {
case p2wsh: P2WSHWitnessSPKV0 =>
scriptWitness match {
case witness: P2WSHWitnessV0 =>
CryptoUtil.sha256(witness.redeemScript.asmBytes) == p2wsh.scriptHash
val hashMatch = CryptoUtil.sha256(witness.redeemScript.asmBytes) == p2wsh.scriptHash
val noIllegalNesting = witness.redeemScript match {
case _: P2PKScriptPubKey | _: P2PKHScriptPubKey |
_: MultiSignatureScriptPubKey | _: LockTimeScriptPubKey |
_: NonStandardScriptPubKey | _: WitnessCommitment |
EmptyScriptPubKey | _: UnassignedWitnessScriptPubKey =>
true
case _: P2WPKHWitnessSPKV0 | _: P2WSHWitnessSPKV0 |
_: P2SHScriptPubKey =>
false
}
hashMatch && noIllegalNesting
case _: ScriptWitnessV0 => false
}
}
@ -82,7 +94,6 @@ sealed trait BitcoinUTXOSpendingInfo extends UTXOSpendingInfo {
object BitcoinUTXOSpendingInfo {
// TODO: Get rid of this and force all callers to directly call a subclass apply method
def apply(
outPoint: TransactionOutPoint,
output: TransactionOutput,
@ -116,13 +127,16 @@ object BitcoinUTXOSpendingInfo {
witnessOpt.getOrElse(throw new IllegalArgumentException(
"Script Witness must be defined for (nested) Segwit input"))
)
case _: ScriptPubKey =>
P2SHSpendingInfo(outPoint,
output.value,
p2sh,
signers,
hashType,
redeemScript)
case nonWitnessSPK: NonWitnessScriptPubKey =>
P2SHNoNestSpendingInfo(outPoint,
output.value,
p2sh,
signers,
hashType,
nonWitnessSPK)
case _: UnassignedWitnessScriptPubKey =>
throw new UnsupportedOperationException(
s"Unsupported ScriptPubKey ${output.scriptPubKey}")
}
}
case wspk: WitnessScriptPubKeyV0 =>
@ -152,19 +166,29 @@ object BitcoinUTXOSpendingInfo {
signers,
hashType,
scriptWitnessOpt.getOrElse(EmptyScriptWitness))
case _: P2PKScriptPubKey | _: P2PKHScriptPubKey |
_: MultiSignatureScriptPubKey | _: NonStandardScriptPubKey |
_: CLTVScriptPubKey | _: CSVScriptPubKey | _: WitnessCommitment |
case p2pk: P2PKScriptPubKey =>
P2PKSpendingInfo(outPoint, output.value, p2pk, signers.head, hashType)
case p2pkh: P2PKHScriptPubKey =>
P2PKHSpendingInfo(outPoint, output.value, p2pkh, signers.head, hashType)
case multisig: MultiSignatureScriptPubKey =>
MultiSignatureSpendingInfo(outPoint,
output.value,
multisig,
signers.toVector,
hashType)
case locktime: LockTimeScriptPubKey =>
LockTimeSpendingInfo(outPoint,
output.value,
locktime,
signers.toVector,
hashType)
case _: NonStandardScriptPubKey | _: WitnessCommitment |
EmptyScriptPubKey =>
RawScriptUTXOSpendingInfo(outPoint,
output.value,
output.scriptPubKey,
signers,
hashType)
throw new UnsupportedOperationException(
s"Currently unsupported ScriptPubKey ${output.scriptPubKey}")
}
}
// TODO: Get rid of this and force all matches to match on the ADT
def unapply(info: BitcoinUTXOSpendingInfo): Option[
(
TransactionOutPoint,
@ -185,18 +209,60 @@ object BitcoinUTXOSpendingInfo {
/** This represents the information needed to be spend scripts like
* [[org.bitcoins.core.protocol.script.P2PKHScriptPubKey p2pkh]] or [[org.bitcoins.core.protocol.script.P2PKScriptPubKey p2pk]]
* scripts. Basically there is no nesting that requires a redeem script here*/
case class RawScriptUTXOSpendingInfo(
outPoint: TransactionOutPoint,
amount: CurrencyUnit,
scriptPubKey: ScriptPubKey,
signers: Seq[Sign],
hashType: HashType)
extends BitcoinUTXOSpendingInfo {
sealed trait RawScriptUTXOSpendingInfo extends BitcoinUTXOSpendingInfo {
override val outPoint: TransactionOutPoint
override val amount: CurrencyUnit
override val scriptPubKey: ScriptPubKey
override val signers: Seq[Sign]
override val hashType: HashType
override val redeemScriptOpt: Option[ScriptPubKey] = None
override val scriptWitnessOpt: Option[ScriptWitnessV0] = None
}
case class P2PKSpendingInfo(
outPoint: TransactionOutPoint,
amount: CurrencyUnit,
scriptPubKey: P2PKScriptPubKey,
signer: Sign,
hashType: HashType)
extends RawScriptUTXOSpendingInfo {
require(scriptPubKey.publicKey == signer.publicKey,
"Signer pubkey must match ScriptPubKey")
override val signers: Vector[Sign] = Vector(signer)
}
case class P2PKHSpendingInfo(
outPoint: TransactionOutPoint,
amount: CurrencyUnit,
scriptPubKey: P2PKHScriptPubKey,
signer: Sign,
hashType: HashType)
extends RawScriptUTXOSpendingInfo {
require(scriptPubKey == P2PKHScriptPubKey(signer.publicKey),
"Signer pubkey must match ScriptPubKey")
override val signers: Vector[Sign] = Vector(signer)
}
case class MultiSignatureSpendingInfo(
outPoint: TransactionOutPoint,
amount: CurrencyUnit,
scriptPubKey: MultiSignatureScriptPubKey,
signers: Vector[Sign],
hashType: HashType
) extends RawScriptUTXOSpendingInfo
case class LockTimeSpendingInfo(
outPoint: TransactionOutPoint,
amount: CurrencyUnit,
scriptPubKey: LockTimeScriptPubKey,
signers: Vector[Sign],
hashType: HashType
) extends RawScriptUTXOSpendingInfo
/** This is the case where we are spending a [[org.bitcoins.core.protocol.script.WitnessScriptPubKeyV0 witness v0 script]] */
case class SegwitV0NativeUTXOSpendingInfo(
outPoint: TransactionOutPoint,
@ -229,21 +295,31 @@ case class UnassignedSegwitNativeUTXOSpendingInfo(
override val scriptWitnessOpt: Option[ScriptWitness] = Some(scriptWitness)
}
sealed trait P2SHSpendingInfo extends BitcoinUTXOSpendingInfo {
override def outPoint: TransactionOutPoint
override def amount: CurrencyUnit
override def scriptPubKey: P2SHScriptPubKey
override def signers: Seq[Sign]
override def hashType: HashType
def redeemScript: ScriptPubKey
}
/** This is the case were we are attempting to spend a [[org.bitcoins.core.protocol.script.P2SHScriptPubKey p2sh spk]] */
case class P2SHSpendingInfo(
case class P2SHNoNestSpendingInfo(
outPoint: TransactionOutPoint,
amount: CurrencyUnit,
scriptPubKey: P2SHScriptPubKey,
signers: Seq[Sign],
hashType: HashType,
redeemScript: ScriptPubKey)
extends BitcoinUTXOSpendingInfo {
redeemScript: NonWitnessScriptPubKey)
extends P2SHSpendingInfo {
require(
P2SHScriptPubKey(redeemScript) == output.scriptPubKey,
s"Given redeem script did not match hash in output script, " +
s"got=${P2SHScriptPubKey(redeemScript).scriptHash.hex}, " +
s"expected=${scriptPubKey.scriptHash.hex}"
)
require(!redeemScript.isInstanceOf[P2SHScriptPubKey], "Illegal P2SH nesting")
override val redeemScriptOpt: Option[ScriptPubKey] = Some(redeemScript)
@ -261,7 +337,7 @@ case class P2SHNestedSegwitV0UTXOSpendingInfo(
hashType: HashType,
redeemScript: WitnessScriptPubKeyV0,
scriptWitness: ScriptWitnessV0)
extends BitcoinUTXOSpendingInfo {
extends P2SHSpendingInfo {
require(
P2SHScriptPubKey(redeemScript) == output.scriptPubKey,
s"Given redeem script did not match hash in output script, " +

View file

@ -139,7 +139,7 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
p2sh = P2SHScriptPubKey(randomScriptPubKey)
} yield (p2sh, privKeys)
def emptyScriptPubKey: Gen[(ScriptPubKey, Seq[ECPrivateKey])] =
def emptyScriptPubKey: Gen[(EmptyScriptPubKey.type, Seq[ECPrivateKey])] =
(EmptyScriptPubKey, Nil)
/** Creates a basic version 0 P2WPKH scriptpubkey */
@ -239,6 +239,19 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
)
}
def nonWitnessScriptPubKey: Gen[(NonWitnessScriptPubKey, Seq[ECPrivateKey])] = {
Gen.oneOf(
p2pkScriptPubKey.map(privKeyToSeq),
p2pkhScriptPubKey.map(privKeyToSeq),
multiSigScriptPubKey,
emptyScriptPubKey,
cltvScriptPubKey,
csvScriptPubKey,
p2shScriptPubKey,
witnessCommitment
)
}
/** Generates an arbitrary `ScriptSignature` */
def scriptSignature: Gen[ScriptSignature] = {
Gen.oneOf(
@ -645,9 +658,8 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger {
} yield (scriptSig, scriptPubKey, Seq(privateKey))
/** Simply converts one private key in the generator to a sequence of private keys */
private def privKeyToSeq(tuple: (ScriptPubKey, ECPrivateKey)): (
ScriptPubKey,
Seq[ECPrivateKey]) = {
private def privKeyToSeq[T](
tuple: (T, ECPrivateKey)): (T, Seq[ECPrivateKey]) = {
val (s, key) = tuple
(s, Seq(key))
}