psbt: create new enum type for return values of Sign method

This commit is contained in:
Olaoluwa Osuntokun 2020-01-15 17:43:29 -08:00
parent 3385fba2f2
commit e2fd54d981

View file

@ -4,129 +4,149 @@
package psbt package psbt
// signer encapsulates the role 'Signer' // signer encapsulates the role 'Signer' as specified in BIP174; it controls
// as specified in BIP174; it controls the insertion of signatures; // the insertion of signatures; the Sign() function will attempt to insert
// the Sign() function will attempt to insert signatures using // signatures using Updater.addPartialSignature, after first ensuring the Psbt
// Updater.addPartialSignature, after first ensuring the Psbt is in the // is in the correct state.
// correct state.
import ( import (
"github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/txscript"
) )
// SignOutcome is a enum-like value that expresses the outcome of a call to the
// Sign method.
type SignOutcome int
const (
// SignSuccesful indicates that the partial signature was successfully
// attached.
SignSuccesful = 0
// SignFinalized indicates that this input is already finalized, so the provided
// signature was *not* attached
SignFinalized = 1
// SignInvalid indicates that the provided signature data was not valid. In this case
// an error will also be returned.
SignInvalid = -1
)
// Sign allows the caller to sign a PSBT at a particular input; they // Sign allows the caller to sign a PSBT at a particular input; they
// must provide a signature and a pubkey, both as byte slices; they can also // must provide a signature and a pubkey, both as byte slices; they can also
// optionally provide both witnessScript and/or redeemScript, otherwise // optionally provide both witnessScript and/or redeemScript, otherwise these
// these arguments must be set as nil (and in that case, they must already // arguments must be set as nil (and in that case, they must already be present
// be present in the PSBT if required for signing to succeed). // in the PSBT if required for signing to succeed).
// //
// Return value: // This serves as a wrapper around Updater.addPartialSignature; it ensures that
// 0 indicates that the partial signature was successfully attached. // the redeemScript and witnessScript are updated as needed (note that the
// 1 indicates that this input is already finalized, so the provided // Updater is allowed to add redeemScripts and witnessScripts independently,
// signature was *not* attached // before signing), and ensures that the right form of utxo field
// -1 indicates that the provided signature data was not valid. In this // (NonWitnessUtxo or WitnessUtxo) is included in the input so that signature
// case an error will also be returned. // insertion (and then finalization) can take place.
//
// This serves as a wrapper around Updater.addPartialSignature;
// it ensures that the redeemScript and witnessScript are updated as needed
// (note that the Updater is allowed to add redeemScripts and witnessScripts
// independently, before signing), and ensures that the right form of utxo
// field (NonWitnessUtxo or WitnessUtxo) is included in the input so that
// signature insertion (and then finalization) can take place.
func (u *Updater) Sign(inIndex int, sig []byte, pubKey []byte, func (u *Updater) Sign(inIndex int, sig []byte, pubKey []byte,
redeemScript []byte, witnessScript []byte) (int, error) { redeemScript []byte, witnessScript []byte) (SignOutcome, error) {
if isFinalized(u.Upsbt, inIndex) { if isFinalized(u.Upsbt, inIndex) {
return 1, nil return SignFinalized, nil
} }
// Add the witnessScript to the PSBT in preparation. If it already
// exists, it will be overwritten.
if witnessScript != nil { if witnessScript != nil {
// Add the witnessScript to the PSBT in preparation.
// If it already exists, it will be overwritten.
err := u.AddInWitnessScript(witnessScript, inIndex) err := u.AddInWitnessScript(witnessScript, inIndex)
if err != nil { if err != nil {
return -1, err return SignInvalid, err
} }
} }
// Add the redeemScript to the PSBT in preparation. If it already
// exists, it will be overwritten.
if redeemScript != nil { if redeemScript != nil {
// Add the redeemScript to the PSBT in preparation.
// If it already exists, it will be overwritten.
err := u.AddInRedeemScript(redeemScript, inIndex) err := u.AddInRedeemScript(redeemScript, inIndex)
if err != nil { if err != nil {
return -1, err return SignInvalid, err
} }
} }
// At this point, the PSBT must have the requisite // At this point, the PSBT must have the requisite witnessScript or
// witnessScript or redeemScript fields for signing to succeed. // redeemScript fields for signing to succeed.
//
// case 1: if witnessScript is present, it must be of type witness; // Case 1: if witnessScript is present, it must be of type witness;
// if not, signature insertion will of course fail. // if not, signature insertion will of course fail.
if u.Upsbt.Inputs[inIndex].WitnessScript != nil { switch {
case u.Upsbt.Inputs[inIndex].WitnessScript != nil:
if u.Upsbt.Inputs[inIndex].WitnessUtxo == nil { if u.Upsbt.Inputs[inIndex].WitnessUtxo == nil {
err := nonWitnessToWitness(u.Upsbt, inIndex) err := nonWitnessToWitness(u.Upsbt, inIndex)
if err != nil { if err != nil {
return -1, err return SignInvalid, err
} }
} }
err := u.addPartialSignature(inIndex, sig, pubKey) err := u.addPartialSignature(inIndex, sig, pubKey)
if err != nil { if err != nil {
return -1, err return SignInvalid, err
} }
} else if u.Upsbt.Inputs[inIndex].RedeemScript != nil {
// case 2: no witness script, only redeem script; can be legacy // Case 2: no witness script, only redeem script; can be legacy p2sh or
// p2sh or p2sh-wrapped p2wkh // p2sh-wrapped p2wkh.
case u.Upsbt.Inputs[inIndex].RedeemScript != nil:
// We only need to decide if the input is witness, and we don't // We only need to decide if the input is witness, and we don't
// rely on the witnessutxo/nonwitnessutxo in the PSBT, instead // rely on the witnessutxo/nonwitnessutxo in the PSBT, instead
// we check the redeemScript content: // we check the redeemScript content.
if txscript.IsWitnessProgram(redeemScript) { if txscript.IsWitnessProgram(redeemScript) {
if u.Upsbt.Inputs[inIndex].WitnessUtxo == nil { if u.Upsbt.Inputs[inIndex].WitnessUtxo == nil {
err := nonWitnessToWitness(u.Upsbt, inIndex) err := nonWitnessToWitness(u.Upsbt, inIndex)
if err != nil { if err != nil {
return -1, err return SignInvalid, err
} }
} }
} }
// If it is not a valid witness program, we here assume
// that the provided WitnessUtxo/NonWitnessUtxo field was correct. // If it is not a valid witness program, we here assume that
// the provided WitnessUtxo/NonWitnessUtxo field was correct.
err := u.addPartialSignature(inIndex, sig, pubKey) err := u.addPartialSignature(inIndex, sig, pubKey)
if err != nil { if err != nil {
return -1, err return SignInvalid, err
} }
} else {
// case 3: Neither provided only works for native p2wkh, or // Case 3: Neither provided only works for native p2wkh, or non-segwit
// non-segwit non-p2sh. To check if it's segwit, check // non-p2sh. To check if it's segwit, check the scriptPubKey of the
// the scriptPubKey of the output. // output.
default:
if u.Upsbt.Inputs[inIndex].WitnessUtxo == nil { if u.Upsbt.Inputs[inIndex].WitnessUtxo == nil {
outIndex := u.Upsbt.UnsignedTx.TxIn[inIndex]. outIndex := u.Upsbt.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Index
PreviousOutPoint.Index script := u.Upsbt.Inputs[inIndex].NonWitnessUtxo.TxOut[outIndex].PkScript
script := u.Upsbt.Inputs[inIndex].NonWitnessUtxo.
TxOut[outIndex].PkScript
if txscript.IsWitnessProgram(script) { if txscript.IsWitnessProgram(script) {
err := nonWitnessToWitness(u.Upsbt, inIndex) err := nonWitnessToWitness(u.Upsbt, inIndex)
if err != nil { if err != nil {
return -1, err return SignInvalid, err
} }
} }
} }
err := u.addPartialSignature(inIndex, sig, pubKey) err := u.addPartialSignature(inIndex, sig, pubKey)
if err != nil { if err != nil {
return -1, err return SignInvalid, err
} }
} }
return 0, nil
return SignSuccesful, nil
} }
// nonWitnessToWitness extracts the TxOut from the existing // nonWitnessToWitness extracts the TxOut from the existing NonWitnessUtxo
// NonWitnessUtxo field in the given PSBT input and sets it as type // field in the given PSBT input and sets it as type witness by replacing the
// witness by replacing the NonWitnessUtxo field with a WitnessUtxo // NonWitnessUtxo field with a WitnessUtxo field. See
// field. See https://github.com/bitcoin/bitcoin/pull/14197 // https://github.com/bitcoin/bitcoin/pull/14197.
func nonWitnessToWitness(p *Psbt, inIndex int) error { func nonWitnessToWitness(p *Packet, inIndex int) error {
outIndex := p.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Index outIndex := p.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Index
txout := p.Inputs[inIndex].NonWitnessUtxo.TxOut[outIndex] txout := p.Inputs[inIndex].NonWitnessUtxo.TxOut[outIndex]
// Remove the non-witness first, else sanity check will not pass: // Remove the non-witness first, else sanity check will not pass:
p.Inputs[inIndex].NonWitnessUtxo = nil p.Inputs[inIndex].NonWitnessUtxo = nil
u := Updater{Upsbt: p} u := Updater{
Upsbt: p,
}
return u.AddInWitnessUtxo(txout, inIndex) return u.AddInWitnessUtxo(txout, inIndex)
} }