e17c9730c4
Implements: PSBT struct, roles: creator, updater, signer, extractor. Passing test vectors.
312 lines
10 KiB
Go
312 lines
10 KiB
Go
// Copyright (c) 2018 The btcsuite developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package psbt
|
|
|
|
// The Updater requires provision of a single PSBT and is able
|
|
// to add data to both input and output sections.
|
|
// It can be called repeatedly to add more data.
|
|
// It also allows addition of signatures via the addPartialSignature
|
|
// function; this is called internally to the package in the Sign()
|
|
// function of Updater, located in signer.go
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha256"
|
|
|
|
"github.com/btcsuite/btcd/txscript"
|
|
"github.com/btcsuite/btcd/wire"
|
|
"github.com/btcsuite/btcutil"
|
|
)
|
|
|
|
// Updater encapsulates the role 'Updater'
|
|
// as specified in BIP174; it accepts Psbt structs
|
|
// and has methods to add fields to the inputs and outputs.
|
|
type Updater struct {
|
|
Upsbt *Psbt
|
|
}
|
|
|
|
// NewUpdater returns a new instance of Updater,
|
|
// if the passed Psbt struct is in a valid form,
|
|
// else an error.
|
|
func NewUpdater(p *Psbt) (*Updater, error) {
|
|
if err := p.SanityCheck(); err != nil {
|
|
return nil, err
|
|
}
|
|
return &Updater{Upsbt: p}, nil
|
|
|
|
}
|
|
|
|
// AddInNonWitnessUtxo adds the utxo information for an input which
|
|
// is non-witness. This requires provision of a full transaction
|
|
// (which is the source of the corresponding prevOut), and the input
|
|
// index. If addition of this key-value pair to the Psbt fails, an
|
|
// error is returned.
|
|
func (p *Updater) AddInNonWitnessUtxo(tx *wire.MsgTx, inIndex int) error {
|
|
if inIndex > len(p.Upsbt.Inputs)-1 {
|
|
return ErrInvalidPrevOutNonWitnessTransaction
|
|
}
|
|
p.Upsbt.Inputs[inIndex].NonWitnessUtxo = tx
|
|
if err := p.Upsbt.SanityCheck(); err != nil {
|
|
return ErrInvalidPsbtFormat
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AddInWitnessUtxo adds the utxo information for an input which
|
|
// is witness. This requires provision of a full transaction *output*
|
|
// (which is the source of the corresponding prevOut); not the full
|
|
// transaction because BIP143 means the output information is sufficient,
|
|
// and the input index. If addition of this key-value pair to the Psbt fails,
|
|
// an error is returned.
|
|
func (p *Updater) AddInWitnessUtxo(txout *wire.TxOut, inIndex int) error {
|
|
if inIndex > len(p.Upsbt.Inputs)-1 {
|
|
return ErrInvalidPsbtFormat
|
|
}
|
|
p.Upsbt.Inputs[inIndex].WitnessUtxo = txout
|
|
if err := p.Upsbt.SanityCheck(); err != nil {
|
|
return ErrInvalidPsbtFormat
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// addPartialSignature allows the Updater role to insert fields
|
|
// of type partial signature into a Psbt, consisting of both
|
|
// the pubkey (as keydata) and the ECDSA signature (as value).
|
|
// Note that the Signer role is encapsulated in this function;
|
|
// signatures are only allowed to be added that follow the sanity-check
|
|
// on signing rules explained in the BIP under `Signer`; if the rules are not
|
|
// satisfied, an ErrInvalidSignatureForInput is returned.
|
|
// NOTE this function does *not* validate the ECDSA signature itself.
|
|
func (p *Updater) addPartialSignature(inIndex int, sig []byte,
|
|
pubkey []byte) error {
|
|
|
|
partialSig := PartialSig{PubKey: pubkey, Signature: sig}
|
|
//First validate the passed (sig, pub):
|
|
if !partialSig.checkValid() {
|
|
return ErrInvalidPsbtFormat
|
|
}
|
|
|
|
pInput := p.Upsbt.Inputs[inIndex]
|
|
|
|
// First check; don't add duplicates
|
|
for _, x := range pInput.PartialSigs {
|
|
if bytes.Equal(x.PubKey, partialSig.PubKey) {
|
|
return ErrDuplicateKey
|
|
}
|
|
}
|
|
|
|
// Sanity checks
|
|
if pInput.NonWitnessUtxo != nil {
|
|
if len(p.Upsbt.UnsignedTx.TxIn) < inIndex+1 {
|
|
return ErrInvalidPrevOutNonWitnessTransaction
|
|
}
|
|
if pInput.NonWitnessUtxo.TxHash() !=
|
|
p.Upsbt.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Hash {
|
|
return ErrInvalidSignatureForInput
|
|
}
|
|
if pInput.RedeemScript != nil {
|
|
// To validate that the redeem script matches, we must pull out the
|
|
// scriptPubKey of the corresponding output and compare that with
|
|
// the P2SH scriptPubKey that is generated by redeemScript:
|
|
outIndex := p.Upsbt.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Index
|
|
scriptPubKey := pInput.NonWitnessUtxo.TxOut[outIndex].PkScript
|
|
scriptHash := btcutil.Hash160(pInput.RedeemScript)
|
|
scriptHashScript, err := txscript.NewScriptBuilder().AddOp(
|
|
txscript.OP_HASH160).AddData(scriptHash).AddOp(
|
|
txscript.OP_EQUAL).Script()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !bytes.Equal(scriptHashScript, scriptPubKey) {
|
|
return ErrInvalidSignatureForInput
|
|
}
|
|
}
|
|
} else if pInput.WitnessUtxo != nil {
|
|
scriptPubKey := pInput.WitnessUtxo.PkScript
|
|
var script []byte
|
|
if pInput.RedeemScript != nil {
|
|
scriptHash := btcutil.Hash160(pInput.RedeemScript)
|
|
scriptHashScript, err := txscript.NewScriptBuilder().AddOp(
|
|
txscript.OP_HASH160).AddData(scriptHash).AddOp(
|
|
txscript.OP_EQUAL).Script()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !bytes.Equal(scriptHashScript, scriptPubKey) {
|
|
return ErrInvalidSignatureForInput
|
|
}
|
|
script = pInput.RedeemScript
|
|
} else {
|
|
script = scriptPubKey
|
|
}
|
|
// If a witnessScript field is present, this is a P2WSH,
|
|
// whether nested or not (that is handled by the assignment to
|
|
// `script` above); in that case, sanity check that `script`
|
|
// is the p2wsh of witnessScript. Contrariwise, if no witnessScript
|
|
// field is present, this will be signed as p2wkh.
|
|
if pInput.WitnessScript != nil {
|
|
witnessScriptHash := sha256.Sum256(pInput.WitnessScript)
|
|
witnessScriptHashScript, err := txscript.NewScriptBuilder().AddOp(
|
|
txscript.OP_0).AddData(witnessScriptHash[:]).Script()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !bytes.Equal(script, witnessScriptHashScript[:]) {
|
|
return ErrInvalidSignatureForInput
|
|
}
|
|
} else { // p2wkh
|
|
pubkeyHash := btcutil.Hash160(pubkey)
|
|
pubkeyHashScript, err := txscript.NewScriptBuilder().AddOp(
|
|
txscript.OP_0).AddData(pubkeyHash).Script()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !bytes.Equal(pubkeyHashScript, script) {
|
|
return ErrInvalidSignatureForInput
|
|
}
|
|
}
|
|
} else {
|
|
// attaching signature without utxo field is not allowed
|
|
return ErrInvalidPsbtFormat
|
|
}
|
|
|
|
p.Upsbt.Inputs[inIndex].PartialSigs = append(
|
|
p.Upsbt.Inputs[inIndex].PartialSigs, &partialSig)
|
|
|
|
if err := p.Upsbt.SanityCheck(); err != nil {
|
|
return err
|
|
}
|
|
// Addition of a non-duplicate-key partial signature
|
|
// cannot violate sanity-check rules.
|
|
return nil
|
|
}
|
|
|
|
// AddInSighashType adds the sighash type information for an input.
|
|
// The sighash type is passed as a 32 bit unsigned integer, along with the
|
|
// index for the input. An error is returned if addition of this key-value pair
|
|
// to the Psbt fails.
|
|
func (p *Updater) AddInSighashType(sighashType txscript.SigHashType,
|
|
inIndex int) error {
|
|
p.Upsbt.Inputs[inIndex].SighashType = sighashType
|
|
if err := p.Upsbt.SanityCheck(); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AddInRedeemScript adds the redeem script information for an input.
|
|
// The redeem script is passed serialized, as a byte slice, along with the
|
|
// index of the input. An error is returned if addition of this key-value pair
|
|
// to the Psbt fails.
|
|
func (p *Updater) AddInRedeemScript(redeemScript []byte,
|
|
inIndex int) error {
|
|
p.Upsbt.Inputs[inIndex].RedeemScript = redeemScript
|
|
if err := p.Upsbt.SanityCheck(); err != nil {
|
|
return ErrInvalidPsbtFormat
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AddInWitnessScript adds the witness script information for an input.
|
|
// The witness script is passed serialized, as a byte slice, along with the
|
|
// index of the input. An error is returned if addition of this key-value pair
|
|
// to the Psbt fails.
|
|
func (p *Updater) AddInWitnessScript(witnessScript []byte,
|
|
inIndex int) error {
|
|
p.Upsbt.Inputs[inIndex].WitnessScript = witnessScript
|
|
if err := p.Upsbt.SanityCheck(); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AddInBip32Derivation takes a master key fingerprint
|
|
// as defined in BIP32, a BIP32 path as a slice of uint32 values,
|
|
// and a serialized pubkey as a byte slice, along with the
|
|
// integer index of the input, and inserts this data into that input.
|
|
// NOTE that this can be called multiple times for the same input.
|
|
// An error is returned if addition of this key-value pair
|
|
// to the Psbt fails.
|
|
func (p *Updater) AddInBip32Derivation(masterKeyFingerprint uint32,
|
|
bip32Path []uint32, pubKeyData []byte, inIndex int) error {
|
|
bip32Derivation := Bip32Derivation{
|
|
PubKey: pubKeyData,
|
|
MasterKeyFingerprint: masterKeyFingerprint,
|
|
Bip32Path: bip32Path,
|
|
}
|
|
|
|
if !bip32Derivation.checkValid() {
|
|
return ErrInvalidPsbtFormat
|
|
}
|
|
|
|
// Don't allow duplicate keys
|
|
for _, x := range p.Upsbt.Inputs[inIndex].Bip32Derivation {
|
|
if bytes.Equal(x.PubKey, bip32Derivation.PubKey) {
|
|
return ErrDuplicateKey
|
|
}
|
|
}
|
|
|
|
p.Upsbt.Inputs[inIndex].Bip32Derivation = append(
|
|
p.Upsbt.Inputs[inIndex].Bip32Derivation, &bip32Derivation)
|
|
if err := p.Upsbt.SanityCheck(); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AddOutBip32Derivation takes a master key fingerprint
|
|
// as defined in BIP32, a BIP32 path as a slice of uint32 values,
|
|
// and a serialized pubkey as a byte slice, along with the
|
|
// integer index of the output, and inserts this data into that output.
|
|
// NOTE that this can be called multiple times for the same output.
|
|
// An error is returned if addition of this key-value pair
|
|
// to the Psbt fails.
|
|
func (p *Updater) AddOutBip32Derivation(masterKeyFingerprint uint32,
|
|
bip32Path []uint32, pubKeyData []byte, outIndex int) error {
|
|
bip32Derivation := Bip32Derivation{
|
|
PubKey: pubKeyData,
|
|
MasterKeyFingerprint: masterKeyFingerprint,
|
|
Bip32Path: bip32Path,
|
|
}
|
|
if !bip32Derivation.checkValid() {
|
|
return ErrInvalidPsbtFormat
|
|
}
|
|
|
|
// Don't allow duplicate keys
|
|
for _, x := range p.Upsbt.Outputs[outIndex].Bip32Derivation {
|
|
if bytes.Equal(x.PubKey, bip32Derivation.PubKey) {
|
|
return ErrDuplicateKey
|
|
}
|
|
}
|
|
|
|
p.Upsbt.Outputs[outIndex].Bip32Derivation = append(
|
|
p.Upsbt.Outputs[outIndex].Bip32Derivation, &bip32Derivation)
|
|
if err := p.Upsbt.SanityCheck(); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AddOutRedeemScript takes a redeem script as a byte slice
|
|
// and appends it to the output at index outIndex.
|
|
func (p *Updater) AddOutRedeemScript(redeemScript []byte,
|
|
outIndex int) error {
|
|
p.Upsbt.Outputs[outIndex].RedeemScript = redeemScript
|
|
if err := p.Upsbt.SanityCheck(); err != nil {
|
|
return ErrInvalidPsbtFormat
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AddOutWitnessScript takes a witness script as a byte slice
|
|
// and appends it to the output at index outIndex.
|
|
func (p *Updater) AddOutWitnessScript(witnessScript []byte,
|
|
outIndex int) error {
|
|
p.Upsbt.Outputs[outIndex].WitnessScript = witnessScript
|
|
if err := p.Upsbt.SanityCheck(); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|