123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373 |
- // 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"
- sha256 "github.com/minio/sha256-simd"
- "github.com/pkt-cash/pktd/btcutil"
- "github.com/pkt-cash/pktd/btcutil/er"
- "github.com/pkt-cash/pktd/txscript/opcode"
- "github.com/pkt-cash/pktd/txscript/params"
- "github.com/pkt-cash/pktd/txscript/scriptbuilder"
- "github.com/pkt-cash/pktd/wire"
- )
- // 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 *Packet
- }
- // NewUpdater returns a new instance of Updater, if the passed Psbt struct is
- // in a valid form, else an error.
- func NewUpdater(p *Packet) (*Updater, er.R) {
- 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) er.R {
- if inIndex > len(p.Upsbt.Inputs)-1 {
- return ErrInvalidPrevOutNonWitnessTransaction.Default()
- }
- p.Upsbt.Inputs[inIndex].NonWitnessUtxo = tx
- if err := p.Upsbt.SanityCheck(); err != nil {
- return ErrInvalidPsbtFormat.Default()
- }
- 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) er.R {
- if inIndex > len(p.Upsbt.Inputs)-1 {
- return ErrInvalidPsbtFormat.Default()
- }
- p.Upsbt.Inputs[inIndex].WitnessUtxo = txout
- if err := p.Upsbt.SanityCheck(); err != nil {
- return ErrInvalidPsbtFormat.Default()
- }
- 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) er.R {
- partialSig := PartialSig{
- PubKey: pubkey, Signature: sig,
- }
- // First validate the passed (sig, pub).
- if !partialSig.checkValid() {
- return ErrInvalidPsbtFormat.Default()
- }
- 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.Default()
- }
- }
- // Attaching signature without utxo field is not allowed.
- if pInput.WitnessUtxo == nil && pInput.NonWitnessUtxo == nil {
- return ErrInvalidPsbtFormat.Default()
- }
- // Next, we perform a series of additional sanity checks.
- if pInput.NonWitnessUtxo != nil {
- if len(p.Upsbt.UnsignedTx.TxIn) < inIndex+1 {
- return ErrInvalidPrevOutNonWitnessTransaction.Default()
- }
- if pInput.NonWitnessUtxo.TxHash() !=
- p.Upsbt.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Hash {
- return ErrInvalidSignatureForInput.Default()
- }
- // 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.
- if pInput.RedeemScript != nil {
- outIndex := p.Upsbt.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Index
- scriptPubKey := pInput.NonWitnessUtxo.TxOut[outIndex].PkScript
- scriptHash := btcutil.Hash160(pInput.RedeemScript)
- scriptHashScript, err := scriptbuilder.NewScriptBuilder().
- AddOp(opcode.OP_HASH160).
- AddData(scriptHash).
- AddOp(opcode.OP_EQUAL).
- Script()
- if err != nil {
- return err
- }
- if !bytes.Equal(scriptHashScript, scriptPubKey) {
- return ErrInvalidSignatureForInput.Default()
- }
- }
- }
- // It could be that we set both the non-witness and witness UTXO fields
- // in case it's from a wallet that patched the CVE-2020-14199
- // vulnerability. We detect whether the input being spent is actually a
- // witness input and then copy it over to the witness UTXO field in the
- // signer. Run the witness checks as well, even if we might already have
- // checked the script hash. But that should be a negligible performance
- // penalty.
- if pInput.WitnessUtxo != nil {
- scriptPubKey := pInput.WitnessUtxo.PkScript
- var script []byte
- if pInput.RedeemScript != nil {
- scriptHash := btcutil.Hash160(pInput.RedeemScript)
- scriptHashScript, err := scriptbuilder.NewScriptBuilder().
- AddOp(opcode.OP_HASH160).
- AddData(scriptHash).
- AddOp(opcode.OP_EQUAL).
- Script()
- if err != nil {
- return err
- }
- if !bytes.Equal(scriptHashScript, scriptPubKey) {
- return ErrInvalidSignatureForInput.Default()
- }
- 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 := scriptbuilder.NewScriptBuilder().
- AddOp(opcode.OP_0).
- AddData(witnessScriptHash[:]).
- Script()
- if err != nil {
- return err
- }
- if !bytes.Equal(script, witnessScriptHashScript) {
- return ErrInvalidSignatureForInput.Default()
- }
- } else {
- // Otherwise, this is a p2wkh input.
- pubkeyHash := btcutil.Hash160(pubkey)
- pubkeyHashScript, err := scriptbuilder.NewScriptBuilder().
- AddOp(opcode.OP_0).
- AddData(pubkeyHash).
- Script()
- if err != nil {
- return err
- }
- // Validate that we're able to properly reconstruct the
- // witness program.
- if !bytes.Equal(pubkeyHashScript, script) {
- return ErrInvalidSignatureForInput.Default()
- }
- }
- }
- 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 params.SigHashType,
- inIndex int) er.R {
- 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) er.R {
- p.Upsbt.Inputs[inIndex].RedeemScript = redeemScript
- if err := p.Upsbt.SanityCheck(); err != nil {
- return ErrInvalidPsbtFormat.Default()
- }
- 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) er.R {
- 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: 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) er.R {
- bip32Derivation := Bip32Derivation{
- PubKey: pubKeyData,
- MasterKeyFingerprint: masterKeyFingerprint,
- Bip32Path: bip32Path,
- }
- if !bip32Derivation.checkValid() {
- return ErrInvalidPsbtFormat.Default()
- }
- // Don't allow duplicate keys
- for _, x := range p.Upsbt.Inputs[inIndex].Bip32Derivation {
- if bytes.Equal(x.PubKey, bip32Derivation.PubKey) {
- return ErrDuplicateKey.Default()
- }
- }
- 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) er.R {
- bip32Derivation := Bip32Derivation{
- PubKey: pubKeyData,
- MasterKeyFingerprint: masterKeyFingerprint,
- Bip32Path: bip32Path,
- }
- if !bip32Derivation.checkValid() {
- return ErrInvalidPsbtFormat.Default()
- }
- // Don't allow duplicate keys
- for _, x := range p.Upsbt.Outputs[outIndex].Bip32Derivation {
- if bytes.Equal(x.PubKey, bip32Derivation.PubKey) {
- return ErrDuplicateKey.Default()
- }
- }
- 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) er.R {
- p.Upsbt.Outputs[outIndex].RedeemScript = redeemScript
- if err := p.Upsbt.SanityCheck(); err != nil {
- return ErrInvalidPsbtFormat.Default()
- }
- 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) er.R {
- p.Upsbt.Outputs[outIndex].WitnessScript = witnessScript
- if err := p.Upsbt.SanityCheck(); err != nil {
- return err
- }
- return nil
- }
|