Added handling of p2sh outputs in TxBuilder -- no support for p2sh(segwit) outputs yet

This commit is contained in:
Chris Stewart 2018-02-09 16:34:24 -06:00
parent 29510ad638
commit fe03a35018
2 changed files with 92 additions and 8 deletions

View File

@ -18,4 +18,14 @@ object TxBuilderError {
*/ */
case object TooManyKeys extends TxBuilderError case object TooManyKeys extends TxBuilderError
/** Means that you are using the wrong [[org.bitcoins.core.wallet.signer.Signer]] to
* sign the given [[org.bitcoins.core.protocol.script.ScriptPubKey]]
*/
case object WrongSigner extends TxBuilderError
/** Can occurr when we are trying to sign a [[org.bitcoins.core.protocol.script.P2SHScriptPubKey]] but
* we do not have a redeem script for that p2sh spk.
*/
case object NoRedeemScript extends TxBuilderError
} }

View File

@ -3,13 +3,14 @@ package org.bitcoins.core.wallet.signer
import org.bitcoins.core.crypto._ import org.bitcoins.core.crypto._
import org.bitcoins.core.number.UInt32 import org.bitcoins.core.number.UInt32
import org.bitcoins.core.policy.Policy import org.bitcoins.core.policy.Policy
import org.bitcoins.core.protocol.script.P2PKHScriptSignature import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction.{Transaction, TransactionInput, TransactionOutPoint, TransactionOutput} import org.bitcoins.core.protocol.transaction.{Transaction, TransactionInput, TransactionOutPoint, TransactionOutput}
import org.bitcoins.core.script.crypto.HashType import org.bitcoins.core.script.crypto.HashType
import org.bitcoins.core.wallet.builder.TxBuilderError import org.bitcoins.core.wallet.builder.TxBuilderError
/** The class used to represent a signing process for a specific [[org.bitcoins.core.protocol.script.ScriptPubKey]] type */ /** The class used to represent a signing process for a specific [[org.bitcoins.core.protocol.script.ScriptPubKey]] type */
sealed abstract class Signer { sealed abstract class Signer {
/** /**
* The method used to sign a bitcoin unspent transaction output * The method used to sign a bitcoin unspent transaction output
* @param privKey the private keys needed to sign the utxo * @param privKey the private keys needed to sign the utxo
@ -22,25 +23,69 @@ sealed abstract class Signer {
def sign(privKey: Seq[ECPrivateKey], output: TransactionOutput, unsignedTx: Transaction, inputIndex: UInt32, hashType: HashType): Either[TxSigComponent, TxBuilderError] def sign(privKey: Seq[ECPrivateKey], output: TransactionOutput, unsignedTx: Transaction, inputIndex: UInt32, hashType: HashType): Either[TxSigComponent, TxBuilderError]
} }
sealed abstract class BitcoinSigner extends Signer sealed abstract class BitcoinSigner extends Signer
sealed abstract class P2PKHSigner extends BitcoinSigner { /** Used to sign a [[org.bitcoins.core.protocol.script.P2PKScriptPubKey]] */
sealed abstract class P2PKSigner extends BitcoinSigner {
/** Helper function to sign a [[org.bitcoins.core.protocol.script.P2PKHScriptPubKey]], this is slightly different
* than the other sign variant because it only takes ONE PRIVATE KEY rather than a Seq[ECPrivateKey].
* Only one private key is required to spend a p2pkh spk/
*/
def sign(privKey: ECPrivateKey, output: TransactionOutput, unsignedTx: Transaction,
inputIndex: UInt32, hashType: HashType): Either[TxSigComponent, TxBuilderError] = {
sign(Seq(privKey), output, unsignedTx, inputIndex, hashType)
}
/** This sign function gives you full customizability of what version/locktime/sequence number are used on the tx */
override def sign(privKeys: Seq[ECPrivateKey], output: TransactionOutput, unsignedTx: Transaction, override def sign(privKeys: Seq[ECPrivateKey], output: TransactionOutput, unsignedTx: Transaction,
inputIndex: UInt32, hashType: HashType): Either[TxSigComponent, TxBuilderError] = { inputIndex: UInt32, hashType: HashType): Either[TxSigComponent, TxBuilderError] = {
val spk = output.scriptPubKey
if (privKeys.size != 1) { if (privKeys.size != 1) {
Right(TxBuilderError.TooManyKeys) Right(TxBuilderError.TooManyKeys)
} else if (!spk.isInstanceOf[P2PKScriptPubKey]) {
Right(TxBuilderError.WrongSigner)
} else { } else {
val privKey = privKeys.head val privKey = privKeys.head
val spk = output.scriptPubKey
val publicKey = privKey.publicKey
val unsignedInput = unsignedTx.inputs(inputIndex.toInt) val unsignedInput = unsignedTx.inputs(inputIndex.toInt)
//val inputIndex = UInt32(unsignedInputs.indexOf(unsignedInput)) //val inputIndex = UInt32(unsignedInputs.indexOf(unsignedInput))
val txSigComponent = TxSigComponent(unsignedTx,inputIndex,spk, Policy.standardFlags) val txSigComponent = TxSigComponent(unsignedTx,inputIndex,spk, Policy.standardFlags)
val signature = TransactionSignatureCreator.createSig(txSigComponent,privKey,hashType) val signature = TransactionSignatureCreator.createSig(txSigComponent,privKey,hashType)
val p2pkScriptSig = P2PKScriptSignature(signature)
val signedInput = TransactionInput(unsignedInput.previousOutput,p2pkScriptSig,unsignedInput.sequence)
val signedInputs = unsignedTx.inputs.updated(inputIndex.toInt,signedInput)
val signedTx = Transaction(unsignedTx.version,signedInputs, unsignedTx.outputs, unsignedTx.lockTime)
Left(TxSigComponent(signedTx,inputIndex,spk,txSigComponent.flags))
}
}
}
object P2PKSigner extends P2PKSigner
/** Used to sign a [[org.bitcoins.core.protocol.script.P2PKHScriptPubKey]] */
sealed abstract class P2PKHSigner extends BitcoinSigner {
/** Helper function to sign a [[org.bitcoins.core.protocol.script.P2PKHScriptPubKey]], this is slightly different
* than the other sign variant because it only takes ONE PRIVATE KEY rather than a Seq[ECPrivateKey].
* Only one private key is required to spend a p2pkh spk/
*/
def sign(privKey: ECPrivateKey, output: TransactionOutput, unsignedTx: Transaction,
inputIndex: UInt32, hashType: HashType): Either[TxSigComponent, TxBuilderError] = {
sign(Seq(privKey), output, unsignedTx, inputIndex, hashType)
}
/** This sign function gives you full customizability of what version/locktime/sequence number are used on the tx */
override def sign(privKeys: Seq[ECPrivateKey], output: TransactionOutput, unsignedTx: Transaction,
inputIndex: UInt32, hashType: HashType): Either[TxSigComponent, TxBuilderError] = {
val spk = output.scriptPubKey
if (privKeys.size != 1) {
Right(TxBuilderError.TooManyKeys)
} else if (!spk.isInstanceOf[P2PKHScriptPubKey]) {
Right(TxBuilderError.WrongSigner)
} else {
val privKey = privKeys.head
val publicKey = privKey.publicKey
val unsignedInput = unsignedTx.inputs(inputIndex.toInt)
val txSigComponent = TxSigComponent(unsignedTx,inputIndex,spk, Policy.standardFlags)
val signature = TransactionSignatureCreator.createSig(txSigComponent,privKey,hashType)
val signedInput = buildP2PKHInput(signature,publicKey,unsignedInput.previousOutput,unsignedInput.sequence) val signedInput = buildP2PKHInput(signature,publicKey,unsignedInput.previousOutput,unsignedInput.sequence)
val signedInputs = unsignedTx.inputs.updated(inputIndex.toInt,signedInput) val signedInputs = unsignedTx.inputs.updated(inputIndex.toInt,signedInput)
val signedTx = Transaction(unsignedTx.version,signedInputs, unsignedTx.outputs, unsignedTx.lockTime) val signedTx = Transaction(unsignedTx.version,signedInputs, unsignedTx.outputs, unsignedTx.lockTime)
@ -48,6 +93,7 @@ sealed abstract class P2PKHSigner extends BitcoinSigner {
} }
} }
private def buildP2PKHInput(signature: ECDigitalSignature, publicKey: ECPublicKey, private def buildP2PKHInput(signature: ECDigitalSignature, publicKey: ECPublicKey,
outPoint: TransactionOutPoint, sequence: UInt32): TransactionInput = { outPoint: TransactionOutPoint, sequence: UInt32): TransactionInput = {
val scriptSig = P2PKHScriptSignature(signature,publicKey) val scriptSig = P2PKHScriptSignature(signature,publicKey)
@ -56,3 +102,31 @@ sealed abstract class P2PKHSigner extends BitcoinSigner {
} }
object P2PKHSigner extends P2PKHSigner object P2PKHSigner extends P2PKHSigner
sealed abstract class MultiSigSigner extends BitcoinSigner {
override def sign(privKeys: Seq[ECPrivateKey], output: TransactionOutput, unsignedTx: Transaction,
inputIndex: UInt32, hashType: HashType): Either[TxSigComponent, TxBuilderError] = {
val spk = output.scriptPubKey
if (!spk.isInstanceOf[MultiSignatureScriptPubKey]) {
Right(TxBuilderError.WrongSigner)
} else {
val multisig = spk.asInstanceOf[MultiSignatureScriptPubKey]
val requiredSigs = multisig.requiredSigs
val emptyDigitalSignatures = privKeys.map(_ => EmptyDigitalSignature)
val emptyScriptSig = MultiSignatureScriptSignature(emptyDigitalSignatures)
val oldInput = unsignedTx.inputs(inputIndex.toInt)
val emptyInput = TransactionInput(oldInput.previousOutput,emptyScriptSig,oldInput.sequence)
val unsignedSpendingTx = Transaction(unsignedTx.version,unsignedTx.inputs.updated(inputIndex.toInt,emptyInput), unsignedTx.outputs, unsignedTx.lockTime)
val txSignatureComponent = TxSigComponent(unsignedSpendingTx,inputIndex,
multisig,Policy.standardFlags)
val sigs = (0 until requiredSigs).map(i => TransactionSignatureCreator.createSig(txSignatureComponent, privKeys(i), hashType))
val signedScriptSig = MultiSignatureScriptSignature(sigs)
val signedInput = TransactionInput(oldInput.previousOutput,signedScriptSig,oldInput.sequence)
val signedTx = Transaction(unsignedTx.version, unsignedTx.inputs.updated(inputIndex.toInt, signedInput),
unsignedTx.outputs,unsignedTx.lockTime)
Left(TxSigComponent(signedTx,inputIndex,multisig,Policy.standardFlags))
}
}
}
object MultiSigSigner extends MultiSigSigner