psbt: create new enum type for return values of Sign method
This commit is contained in:
parent
3385fba2f2
commit
e2fd54d981
1 changed files with 81 additions and 61 deletions
152
psbt/signer.go
152
psbt/signer.go
|
@ -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)
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// nonWitnessToWitness extracts the TxOut from the existing
|
err := u.addPartialSignature(inIndex, sig, pubKey)
|
||||||
// NonWitnessUtxo field in the given PSBT input and sets it as type
|
if err != nil {
|
||||||
// witness by replacing the NonWitnessUtxo field with a WitnessUtxo
|
return SignInvalid, err
|
||||||
// field. See https://github.com/bitcoin/bitcoin/pull/14197
|
}
|
||||||
func nonWitnessToWitness(p *Psbt, inIndex int) error {
|
}
|
||||||
|
|
||||||
|
return SignSuccesful, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// nonWitnessToWitness extracts the TxOut from the existing NonWitnessUtxo
|
||||||
|
// field in the given PSBT input and sets it as type witness by replacing the
|
||||||
|
// NonWitnessUtxo field with a WitnessUtxo field. See
|
||||||
|
// https://github.com/bitcoin/bitcoin/pull/14197.
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue