PSBT BIP 174 implementation (#126)

Implements: PSBT struct, roles: creator, updater, signer, extractor.
Passing test vectors.
This commit is contained in:
Adam Gibson 2019-12-19 18:20:22 +00:00 committed by Olaoluwa Osuntokun
parent 9e5f4b9a99
commit e17c9730c4
7 changed files with 3274 additions and 0 deletions

70
psbt/creator.go Normal file
View file

@ -0,0 +1,70 @@
// 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 Creator has only one function:
// takes a list of inputs and outputs and converts them to
// the simplest Psbt, i.e. one with no data appended to inputs or outputs,
// but only the raw unsigned transaction in the global section.
import (
"github.com/btcsuite/btcd/wire"
)
// Creator holds a reference to a created Psbt struct.
type Creator struct {
Cpsbt *Psbt
}
// CreatePsbt , on provision of an input and output 'skeleton' for
// the transaction, returns a Creator struct.
// Note that we require OutPoints and not TxIn structs, as we will
// only populate the txid:n information, *not* any scriptSig/witness
// information. The values of nLockTime, nSequence (per input) and
// transaction version (must be 1 of 2) must be specified here. Note
// that the default nSequence value is wire.MaxTxInSequenceNum.
func (c *Creator) CreatePsbt(inputs []*wire.OutPoint,
outputs []*wire.TxOut, Version int32, nLockTime uint32,
nSequences []uint32) error {
// Create the new struct; the input and output lists will be empty,
// the unsignedTx object must be constructed and serialized,
// and that serialization should be entered as the only entry for
// the globalKVPairs list.
// Check the version is a valid Bitcoin tx version; the nLockTime
// can be any valid uint32. There must be one sequence number per
// input.
if !(Version == 1 || Version == 2) || len(nSequences) != len(inputs) {
return ErrInvalidPsbtFormat
}
unsignedTx := wire.NewMsgTx(Version)
unsignedTx.LockTime = nLockTime
for i, in := range inputs {
unsignedTx.AddTxIn(&wire.TxIn{
PreviousOutPoint: *in,
Sequence: nSequences[i],
})
}
for _, out := range outputs {
unsignedTx.AddTxOut(out)
}
// The input and output lists are empty, but there is a list of those
// two lists, and each one must be of length matching the unsigned
// transaction; the unknown list can be nil.
pInputs := make([]PInput, len(unsignedTx.TxIn))
pOutputs := make([]POutput, len(unsignedTx.TxOut))
c.Cpsbt = &Psbt{
UnsignedTx: unsignedTx,
Inputs: pInputs,
Outputs: pOutputs,
Unknowns: nil,
}
// This new Psbt is "raw" and contains no key-value fields,
// so sanity checking with c.Cpsbt.SanityCheck() is not required.
return nil
}

66
psbt/extractor.go Normal file
View file

@ -0,0 +1,66 @@
// 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 Extractor requires provision of a single PSBT
// in which all necessary signatures are encoded, and
// uses it to construct a fully valid network serialized
// transaction.
import (
"bytes"
"github.com/btcsuite/btcd/wire"
)
// Extract takes a finalized psbt and outputs a network serialization
func Extract(p *Psbt) ([]byte, error) {
if !p.IsComplete() {
return nil, ErrIncompletePSBT
}
var err error
// We take the existing UnsignedTx field and append SignatureScript
// and Witness as appropriate, then allow MsgTx to do the serialization
// for us.
newTx := p.UnsignedTx.Copy()
for i, tin := range newTx.TxIn {
pInput := p.Inputs[i]
if pInput.FinalScriptSig != nil {
tin.SignatureScript = pInput.FinalScriptSig
}
if pInput.FinalScriptWitness != nil {
// to set the witness, need to re-deserialize the field
// For each input, the witness is encoded as a stack
// with one or more items. Therefore, we first read a
// varint which encodes the number of stack items.
r := bytes.NewReader(pInput.FinalScriptWitness)
witCount, err := wire.ReadVarInt(r, 0)
if err != nil {
return nil, err
}
// Then for witCount number of stack items, each item
// has a varint length prefix, followed by the witness
// item itself.
tin.Witness = make([][]byte, witCount)
for j := uint64(0); j < witCount; j++ {
// the 10000 size limit is as per BIP141 for witness script;
// TODO this constant should be somewhere else in the lib,
// perhaps btcd/wire/common.go ?
wit, err := wire.ReadVarBytes(r, 0, 10000, "witness")
if err != nil {
return nil, err
}
tin.Witness[j] = wit
}
}
}
var networkSerializedTx bytes.Buffer
err = newTx.Serialize(&networkSerializedTx)
if err != nil {
return nil, err
}
return networkSerializedTx.Bytes(), nil
}

485
psbt/finalizer.go Normal file
View file

@ -0,0 +1,485 @@
// 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 Finalizer requires provision of a single PSBT input
// in which all necessary signatures are encoded, and
// uses it to construct valid final scriptSig and scriptWitness
// fields.
// NOTE that p2sh (legacy) and p2wsh currently support only
// multisig and no other custom script.
import (
"bytes"
"errors"
"io"
"sort"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
)
// A utility function due to non-exported witness serialization
// (writeTxWitness encodes the bitcoin protocol encoding for a transaction
// input's witness into w).
func writeTxWitness(w io.Writer, wit [][]byte) error {
err := wire.WriteVarInt(w, 0, uint64(len(wit)))
if err != nil {
return err
}
for _, item := range wit {
err = wire.WriteVarBytes(w, 0, item)
if err != nil {
return err
}
}
return nil
}
// writePKHWitness writes a witness for a p2wkh spending input
func writePKHWitness(sig []byte, pub []byte) ([]byte, error) {
var buf bytes.Buffer
var witnessItems = [][]byte{sig, pub}
err := writeTxWitness(&buf, witnessItems)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// checkIsMultisigScript is a utility function to check wheter
// a given redeemscript fits the standard multisig template used
// in all p2sh based multisig, given a set of pubkeys for redemption.
func checkIsMultiSigScript(pubKeys [][]byte, sigs [][]byte,
script []byte) bool {
// First insist that the script type is multisig
if txscript.GetScriptClass(script) != txscript.MultiSigTy {
return false
}
// Inspect the script to ensure that the number of sigs and
// pubkeys is correct
numSigs, numPubKeys, err := txscript.CalcMultiSigStats(script)
if err != nil {
return false
}
if numPubKeys != len(pubKeys) || numSigs != len(sigs) {
return false
}
return true
}
// extractKeyOrderFromScript is a utility function
// to extract an ordered list of signatures, given
// a serialized script (redeemscript or witness script),
// a list of pubkeys and the signatures corresponding to those
// pubkeys, so that the signatures will be embedded in the final
// scriptSig or scriptWitness in the correct order.
func extractKeyOrderFromScript(script []byte, expectedPubkeys [][]byte,
sigs [][]byte) ([][]byte, error) {
if !checkIsMultiSigScript(expectedPubkeys, sigs, script) {
return nil, ErrUnsupportedScriptType
}
// Arrange the pubkeys and sigs into a slice of format:
// [[pub,sig], [pub,sig],..]
pubsSigs := [][][]byte{}
for i, pub := range expectedPubkeys {
tmp := [][]byte{pub, sigs[i]}
pubsSigs = append(pubsSigs, tmp)
}
type kv struct {
Key int
Value [][]byte
}
var positionMap []kv
for _, p := range pubsSigs {
pos := bytes.Index(script, p[0])
if pos < 0 {
return nil, errors.New("Script does not contain pubkeys")
}
positionMap = append(positionMap, kv{Key: pos, Value: p})
}
sort.Slice(positionMap, func(i, j int) bool {
return positionMap[i].Key < positionMap[j].Key
})
// Build the return array of signatures
sigsNew := [][]byte{}
for _, x := range positionMap {
sigsNew = append(sigsNew, x.Value[1])
}
return sigsNew, nil
}
// getMultisigScriptWitness creates a full Witness field for the transaction,
// given the public keys and signatures to be appended, after checking
// that the witnessScript is of type M of N multisig. This
// is used for both p2wsh and nested p2wsh multisig cases.
func getMultisigScriptWitness(witnessScript []byte, pubKeys [][]byte,
sigs [][]byte) ([]byte, error) {
orderedSigs, err := extractKeyOrderFromScript(witnessScript, pubKeys, sigs)
if err != nil {
return nil, err
}
var buf bytes.Buffer
var witnessItems = [][]byte{
nil}
for _, os := range orderedSigs {
witnessItems = append(witnessItems, os)
}
witnessItems = append(witnessItems, witnessScript)
err = writeTxWitness(&buf, witnessItems)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// checkSigHashFlags compares the sighash flag byte on a signature with the
// value expected according to any PsbtInSighashType field in this section
// of the PSBT, and returns true if they match, false otherwise.
// If no SighashType field exists, it is assumed to be SIGHASH_ALL.
// TODO sighash type not restricted to one byte in future?
func checkSigHashFlags(sig []byte, input *PInput) bool {
expectedSighashType := txscript.SigHashAll
if input.SighashType != 0 {
expectedSighashType = input.SighashType
}
return expectedSighashType == txscript.SigHashType(sig[len(sig)-1])
}
// isFinalized considers this input finalized if it contains
// at least one of the FinalScriptSig or FinalScriptWitness
// are filled (which only occurs in a successful call to Finalize*)
func isFinalized(p *Psbt, inIndex int) bool {
input := p.Inputs[inIndex]
return input.FinalScriptSig != nil || input.FinalScriptWitness != nil
}
// isFinalizable checks whether the structure of the entry
// for the input of the Psbt p at index inIndex contains sufficient
// information to finalize this input. Deduce the template
// from the contents.
func isFinalizable(p *Psbt, inIndex int) bool {
pInput := p.Inputs[inIndex]
// Cannot be finalizable without any signatures
if pInput.PartialSigs == nil {
return false
}
if pInput.WitnessUtxo != nil {
if txscript.IsWitnessProgram(pInput.WitnessUtxo.PkScript) {
if txscript.IsPayToWitnessScriptHash(pInput.WitnessUtxo.PkScript) {
if pInput.WitnessScript == nil || pInput.RedeemScript != nil {
return false
}
} else {
// if it's p2wkh there should be no redeemScript or witnessScript
if pInput.WitnessScript != nil || pInput.RedeemScript != nil {
return false
}
}
} else if txscript.IsPayToScriptHash(pInput.WitnessUtxo.PkScript) {
if pInput.RedeemScript == nil {
return false
}
// if it's nested, and it's p2wsh, it must have WitnessScript;
// if p2wkh, it must not.
if txscript.IsPayToWitnessScriptHash(pInput.RedeemScript) {
if pInput.WitnessScript == nil {
return false
}
} else if txscript.IsPayToWitnessPubKeyHash(pInput.RedeemScript) {
if pInput.WitnessScript != nil {
return false
}
} else {
// unrecognized type
return false
}
}
} else if pInput.NonWitnessUtxo != nil {
if pInput.WitnessScript != nil {
return false
}
outIndex := p.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Index
if txscript.IsPayToScriptHash(pInput.NonWitnessUtxo.TxOut[outIndex].PkScript) {
if pInput.RedeemScript == nil {
return false
}
} else {
if pInput.RedeemScript != nil {
return false
}
}
} else {
// one of witness and nonwitness utxo must be present
return false
}
return true
}
// MaybeFinalize attempts to finalize the input at index inIndex
// in the PSBT p, returning true with no error if it succeeds, OR
// if the input has already been finalized.
func MaybeFinalize(p *Psbt, inIndex int) (bool, error) {
if isFinalized(p, inIndex) {
return true, nil
}
if !isFinalizable(p, inIndex) {
return false, ErrNotFinalizable
}
err := Finalize(p, inIndex)
if err != nil {
return false, err
}
return true, nil
}
// MaybeFinalizeAll attempts to finalize all inputs of the Psbt that
// are not already finalized, and returns an error if it fails to do so.
func MaybeFinalizeAll(p *Psbt) error {
for i := range p.UnsignedTx.TxIn {
success, err := MaybeFinalize(p, i)
if err != nil || !success {
return err
}
}
return nil
}
// Finalize assumes that the provided Psbt struct
// has all partial signatures and redeem scripts/witness scripts
// already prepared for the specified input, and so removes all temporary
// data and replaces them with completed scriptSig and witness
// fields, which are stored in key-types 07 and 08. The witness/
// non-witness utxo fields in the inputs (key-types 00 and 01) are
// left intact as they may be needed for validation (?).
// If there is any invalid or incomplete data, an error is
// returned.
func Finalize(p *Psbt, inIndex int) error {
var err error
pInput := p.Inputs[inIndex]
if pInput.WitnessUtxo != nil {
err = FinalizeWitness(p, inIndex)
if err != nil {
return err
}
} else if pInput.NonWitnessUtxo != nil {
err = FinalizeNonWitness(p, inIndex)
if err != nil {
return err
}
} else {
return ErrInvalidPsbtFormat
}
if err = p.SanityCheck(); err != nil {
return err
}
return nil
}
// checkFinalScriptSigWitness checks whether a given input in the
// Psbt struct already has the fields 07 (FinalInScriptSig) or 08
// (FinalInWitness). If so, it returns true. It does not modify the
// Psbt.
func checkFinalScriptSigWitness(p *Psbt, inIndex int) bool {
pInput := p.Inputs[inIndex]
if pInput.FinalScriptSig != nil {
return true
}
if pInput.FinalScriptWitness != nil {
return true
}
return false
}
// FinalizeNonWitness attempts to create PsbtInFinalScriptSig field
// for input at index inIndex, and removes all other fields except
// for the utxo field, for an input of type non-witness, or returns
// an error.
func FinalizeNonWitness(p *Psbt, inIndex int) error {
if checkFinalScriptSigWitness(p, inIndex) {
return ErrInputAlreadyFinalized
}
// Construct a scriptSig given the pubkey, signature (keytype 02),
// of which there might be multiple, and the redeem script
// field (keytype 04) if present (note, it is not present
// for p2pkh type inputs).
var scriptSig []byte
var err error
pInput := p.Inputs[inIndex]
containsRedeemScript := pInput.RedeemScript != nil
var pubKeys [][]byte
var sigs [][]byte
for _, ps := range pInput.PartialSigs {
pubKeys = append(pubKeys, ps.PubKey)
sigOK := checkSigHashFlags(ps.Signature, &pInput)
if !sigOK {
return ErrInvalidSigHashFlags
}
sigs = append(sigs, ps.Signature)
}
if len(sigs) < 1 || len(pubKeys) < 1 {
// We have failed to identify at least 1 (sig, pub) pair
// in the PSBT, which indicates it was not ready to be finalized.
return ErrNotFinalizable
}
if !containsRedeemScript {
// p2pkh - insist on one sig/pub and build scriptSig
if len(sigs) != 1 || len(pubKeys) != 1 {
return ErrNotFinalizable
}
builder := txscript.NewScriptBuilder()
builder.AddData(sigs[0]).AddData(pubKeys[0])
scriptSig, err = builder.Script()
if err != nil {
return err
}
} else {
// This is assumed p2sh multisig
// Given redeemScript and pubKeys we can decide in what order
// signatures must be appended.
orderedSigs, err := extractKeyOrderFromScript(pInput.RedeemScript,
pubKeys, sigs)
if err != nil {
return err
}
// TODO the below is specific to the multisig case.
builder := txscript.NewScriptBuilder()
builder.AddOp(txscript.OP_FALSE)
for _, os := range orderedSigs {
builder.AddData(os)
}
builder.AddData(pInput.RedeemScript)
scriptSig, err = builder.Script()
if err != nil {
return err
}
}
// At this point, a scriptSig has been constructed.
// Remove all fields other than non-witness utxo (00)
// and finaliscriptsig (07)
newInput := NewPsbtInput(pInput.NonWitnessUtxo, nil)
newInput.FinalScriptSig = scriptSig
// overwrite the entry in the input list at the correct index
// Note that this removes all the other entries in the list for
// this input index.
p.Inputs[inIndex] = *newInput
return nil
}
// FinalizeWitness attempts to create PsbtInFinalScriptSig field
// and PsbtInFinalScriptWitness field for input at index inIndex,
// and removes all other fields except for the utxo field, for an
// input of type witness, or returns an error.
func FinalizeWitness(p *Psbt, inIndex int) error {
if checkFinalScriptSigWitness(p, inIndex) {
return ErrInputAlreadyFinalized
}
// Construct a scriptSig given the redeem script
// field (keytype 04) if present (if not present it's empty
// as per bip141).
// Fill this in in field FinalScriptSig (keytype 07).
// And/or construct a FinalScriptWitness field (keytype 08),
// assuming either p2wkh or p2wsh multisig.
var scriptSig []byte
var witness []byte
var err error
pInput := p.Inputs[inIndex]
containsRedeemScript := pInput.RedeemScript != nil
cointainsWitnessScript := pInput.WitnessScript != nil
var pubKeys [][]byte
var sigs [][]byte
for _, ps := range pInput.PartialSigs {
pubKeys = append(pubKeys, ps.PubKey)
sigOK := checkSigHashFlags(ps.Signature, &pInput)
if !sigOK {
return ErrInvalidSigHashFlags
}
sigs = append(sigs, ps.Signature)
}
if len(sigs) == 0 || len(pubKeys) == 0 {
return ErrNotFinalizable
}
if !containsRedeemScript {
if len(pubKeys) == 1 && len(sigs) == 1 && !cointainsWitnessScript {
// p2wkh case
witness, err = writePKHWitness(sigs[0], pubKeys[0])
if err != nil {
return err
}
} else {
// Otherwise, we must have a witnessScript field,
// to fulfil the requirements of p2wsh
// NOTE (we tacitly assume multisig)
if !cointainsWitnessScript {
return ErrNotFinalizable
}
witness, err = getMultisigScriptWitness(pInput.WitnessScript,
pubKeys, sigs)
if err != nil {
return err
}
}
} else {
// This is currently assumed p2wsh, multisig, nested in p2sh,
// or p2wkh, nested in p2sh.
// The scriptSig is taken from the redeemscript field, but embedded
// in a push
builder := txscript.NewScriptBuilder()
builder.AddData(pInput.RedeemScript)
scriptSig, err = builder.Script()
if err != nil {
return err
}
if !cointainsWitnessScript {
// Assumed p2sh-p2wkh
// Here the witness is just (sig, pub) as for p2pkh case
if len(sigs) != 1 || len(pubKeys) != 1 {
return ErrNotFinalizable
}
witness, err = writePKHWitness(sigs[0], pubKeys[0])
if err != nil {
return err
}
} else {
// Assumed p2sh-p2wsh with multisig.
// To build the witness, we do exactly as for the native p2wsh case.
witness, err = getMultisigScriptWitness(pInput.WitnessScript,
pubKeys, sigs)
if err != nil {
return err
}
}
}
// At this point, a witness has been constructed,
// and a scriptSig (if nested; else it's []).
// Remove all fields other than witness utxo (01)
// and finalscriptsig (07), finalscriptwitness (08)
newInput := NewPsbtInput(nil, pInput.WitnessUtxo)
if len(scriptSig) > 0 {
newInput.FinalScriptSig = scriptSig
}
newInput.FinalScriptWitness = witness
// overwrite the entry in the input list at the correct index
p.Inputs[inIndex] = *newInput
return nil
}

987
psbt/psbt.go Normal file
View file

@ -0,0 +1,987 @@
// 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 is an implementation of Partially Signed Bitcoin
// Transactions (PSBT). The format is defined in BIP 174:
// https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki
package psbt
import (
"bytes"
"encoding/base64"
"encoding/binary"
"errors"
"io"
"sort"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
)
// BIP-174 aka PSBT defined values
// Key types are currently encoded with single bytes
type psbtKeyType = uint8
const psbtMagicLength = 5
var (
psbtMagic = [psbtMagicLength]byte{0x70,
0x73, 0x62, 0x74, 0xff, // = "psbt" + 0xff sep
}
)
// MaxPsbtValueLength is the size of the largest transaction serialization
// that could be passed in a NonWitnessUtxo field. This is definitely
//less than 4M.
const MaxPsbtValueLength = 4000000
// The below are the known key types as per the BIP.
// Unknown types may be accepted but will not be processed.
const (
// Global known key types
PsbtGlobalUnsignedTx psbtKeyType = 0
// TxIn section known key types
PsbtInNonWitnessUtxo psbtKeyType = 0
PsbtInWitnessUtxo psbtKeyType = 1
PsbtInPartialSig psbtKeyType = 2
PsbtInSighashType psbtKeyType = 3
PsbtInRedeemScript psbtKeyType = 4
PsbtInWitnessScript psbtKeyType = 5
PsbtInBip32Derivation psbtKeyType = 6
PsbtInFinalScriptSig psbtKeyType = 7
PsbtInFinalScriptWitness psbtKeyType = 8
// TxOut section known key types
PsbtOutRedeemScript psbtKeyType = 0
PsbtOutWitnessScript psbtKeyType = 1
PsbtOutBip32Derivation psbtKeyType = 2
)
var (
// ErrInvalidPsbtFormat is a generic error for any situation in which a
// provided Psbt serialization does not conform to the rules of BIP174.
ErrInvalidPsbtFormat = errors.New("Invalid PSBT serialization format")
// ErrDuplicateKey indicates that a passed Psbt serialization is invalid
// due to having the same key repeated in the same key-value pair.
ErrDuplicateKey = errors.New("Invalid Psbt due to duplicate key")
// ErrInvalidKeydata indicates that a key-value pair in the PSBT
// serialization contains data in the key which is not valid.
ErrInvalidKeydata = errors.New("Invalid key data")
// ErrInvalidMagicBytes indicates that a passed Psbt serialization is invalid
// due to having incorrect magic bytes.
ErrInvalidMagicBytes = errors.New("Invalid Psbt due to incorrect magic bytes")
// ErrInvalidRawTxSigned indicates that the raw serialized transaction in the
// global section of the passed Psbt serialization is invalid because it
// contains scriptSigs/witnesses (i.e. is fully or partially signed), which
// is not allowed by BIP174.
ErrInvalidRawTxSigned = errors.New("Invalid Psbt, raw transaction must " +
"be unsigned.")
// ErrInvalidPrevOutNonWitnessTransaction indicates that the transaction
// hash (i.e. SHA256^2) of the fully serialized previous transaction
// provided in the NonWitnessUtxo key-value field doesn't match the prevout
// hash in the UnsignedTx field in the PSBT itself.
ErrInvalidPrevOutNonWitnessTransaction = errors.New("Prevout hash does " +
"not match the provided non-witness utxo serialization")
// ErrInvalidSignatureForInput indicates that the signature the user is
// trying to append to the PSBT is invalid, either because it does
// not correspond to the previous transaction hash, or redeem script,
// or witness script.
// NOTE this does not include ECDSA signature checking.
ErrInvalidSignatureForInput = errors.New("Signature does not correspond " +
"to this input")
// ErrInputAlreadyFinalized indicates that the PSBT passed to a Finalizer
// already contains the finalized scriptSig or witness.
ErrInputAlreadyFinalized = errors.New("Cannot finalize PSBT, finalized " +
"scriptSig or scriptWitnes already exists")
// ErrIncompletePSBT indicates that the Extractor object
// was unable to successfully extract the passed Psbt struct because
// it is not complete
ErrIncompletePSBT = errors.New("PSBT cannot be extracted as it is " +
"incomplete")
// ErrNotFinalizable indicates that the PSBT struct does not have
// sufficient data (e.g. signatures) for finalization
ErrNotFinalizable = errors.New("PSBT is not finalizable")
// ErrInvalidSigHashFlags indicates that a signature added to the PSBT
// uses Sighash flags that are not in accordance with the requirement
// according to the entry in PsbtInSighashType, or otherwise not the
// default value (SIGHASH_ALL)
ErrInvalidSigHashFlags = errors.New("Invalid Sighash Flags")
// ErrUnsupportedScriptType indicates that the redeem script or
// scriptwitness given is not supported by this codebase, or is otherwise
// not valid.
ErrUnsupportedScriptType = errors.New("Unsupported script type")
)
func serializeKVpair(w io.Writer, key []byte, value []byte) error {
err := wire.WriteVarBytes(w, 0, key)
if err != nil {
return err
}
err = wire.WriteVarBytes(w, 0, value)
if err != nil {
return err
}
return nil
}
func serializeKVPairWithType(w io.Writer, kt psbtKeyType, keydata []byte,
value []byte) error {
if keydata == nil {
keydata = []byte{}
}
serializedKey := append([]byte{byte(kt)}, keydata...)
return serializeKVpair(w, serializedKey, value)
}
// getKey retrieves a single key - both the key type and the keydata
// (if present) from the stream and returns the key type as an integer,
// or -1 if the key was of zero length, which is used to indicate the
// presence of a separator byte which indicates the end of a given key-
// value pair list, and the keydata as a byte slice or nil if none is
// present.
func getKey(r io.Reader) (int, []byte, error) {
// For the key, we read the varint separately, instead of
// using the available ReadVarBytes, because we have a specific
// treatment of 0x00 here:
count, err := wire.ReadVarInt(r, 0)
if err != nil {
return -1, nil, ErrInvalidPsbtFormat
}
if count == 0 {
// separator indicates end of key-value pair list
return -1, nil, nil
}
// read count bytes, this is the key (including type and any data)
var keyintanddata = make([]byte, count)
if _, err := io.ReadFull(r, keyintanddata[:]); err != nil {
return -1, nil, err
}
keyType := int(string(keyintanddata)[0])
// Note that the second return value will usually be empty,
// since most keys contain no more than the key type byte.
if len(keyintanddata) == 1 {
return keyType, nil, nil
}
return keyType, keyintanddata[1:], nil
}
// readTxOut is a limited version of wire.readTxOut, because
// the latter is not exported.
func readTxOut(txout []byte) (*wire.TxOut, error) {
if len(txout) < 10 {
return nil, ErrInvalidPsbtFormat
}
valueSer := binary.LittleEndian.Uint64(txout[:8])
scriptPubKey := txout[9:]
return wire.NewTxOut(int64(valueSer), scriptPubKey), nil
}
// PartialSig encapsulate a (BTC public key, ECDSA signature)
// pair, note that the fields are stored as byte slices, not
// btcec.PublicKey or btcec.Signature (because manipulations will
// be with the former not the latter, here); compliance with consensus
// serialization is enforced with .checkValid()
type PartialSig struct {
PubKey []byte
Signature []byte
}
// PartialSigSorter implements sort.Interface.
type PartialSigSorter []*PartialSig
func (s PartialSigSorter) Len() int { return len(s) }
func (s PartialSigSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s PartialSigSorter) Less(i, j int) bool {
return bytes.Compare(s[i].PubKey, s[j].PubKey) < 0
}
// validatePubkey checks if pubKey is *any* valid
// pubKey serialization in a Bitcoin context (compressed/uncomp. OK)
func validatePubkey(pubKey []byte) bool {
_, err := btcec.ParsePubKey(pubKey, btcec.S256())
if err != nil {
return false
}
return true
}
// validateSignature checks that the passed byte slice is a valid
// DER-encoded ECDSA signature, including the sighash flag.
// It does *not* of course validate the signature against any message
// or public key.
func validateSignature(sig []byte) bool {
_, err := btcec.ParseDERSignature(sig, btcec.S256())
if err != nil {
return false
}
return true
}
// See above notes (PartialSig, validatePubkey, validateSignature).
// NOTE update for Schnorr will be needed here if/when that activates.
func (ps *PartialSig) checkValid() bool {
return validatePubkey(ps.PubKey) && validateSignature(ps.Signature)
}
// Bip32Derivation encapsulates the data for the input and output
// Bip32Derivation key-value fields.
type Bip32Derivation struct {
PubKey []byte
MasterKeyFingerprint uint32
Bip32Path []uint32
}
// checkValid ensures that the PubKey in the Bip32Derivation
// struct is valid.
func (pb *Bip32Derivation) checkValid() bool {
return validatePubkey(pb.PubKey)
}
// Bip32Sorter implements sort.Interface.
type Bip32Sorter []*Bip32Derivation
func (s Bip32Sorter) Len() int { return len(s) }
func (s Bip32Sorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s Bip32Sorter) Less(i, j int) bool {
return bytes.Compare(s[i].PubKey, s[j].PubKey) < 0
}
// readBip32Derivation deserializes a byte slice containing
// chunks of 4 byte little endian encodings of uint32 values,
// the first of which is the masterkeyfingerprint and the remainder
// of which are the derivation path.
func readBip32Derivation(path []byte) (uint32, []uint32, error) {
if len(path)%4 != 0 || len(path)/4-1 < 1 {
return 0, nil, ErrInvalidPsbtFormat
}
masterKeyInt := binary.LittleEndian.Uint32(path[:4])
paths := make([]uint32, 0)
for i := 4; i < len(path); i += 4 {
paths = append(paths, binary.LittleEndian.Uint32(path[i:i+4]))
}
return masterKeyInt, paths, nil
}
// SerializeBIP32Derivation takes a master key fingerprint
// as defined in BIP32, along with a path specified as a list
// of uint32 values, and returns a bytestring specifying the derivation
// in the format required by BIP174:
// // master key fingerprint (4) || child index (4) || child index (4) || ...
func SerializeBIP32Derivation(masterKeyFingerprint uint32,
bip32Path []uint32) []byte {
derivationPath := make([]byte, 0, 4+4*len(bip32Path))
var masterKeyBytes [4]byte
binary.LittleEndian.PutUint32(masterKeyBytes[:], masterKeyFingerprint)
derivationPath = append(derivationPath, masterKeyBytes[:]...)
for _, path := range bip32Path {
var pathbytes [4]byte
binary.LittleEndian.PutUint32(pathbytes[:], path)
derivationPath = append(derivationPath, pathbytes[:]...)
}
return derivationPath
}
// Unknown is a struct encapsulating a key-value pair for which
// the key type is unknown by this package; these fields are allowed
// in both the 'Global' and the 'Input' section of a PSBT.
type Unknown struct {
Key []byte
Value []byte
}
// PInput is a struct encapsulating all the data that can be attached
// to any specific input of the PSBT.
type PInput struct {
NonWitnessUtxo *wire.MsgTx
WitnessUtxo *wire.TxOut
PartialSigs []*PartialSig
SighashType txscript.SigHashType
RedeemScript []byte
WitnessScript []byte
Bip32Derivation []*Bip32Derivation
FinalScriptSig []byte
FinalScriptWitness []byte
Unknowns []*Unknown
}
// NewPsbtInput creates an instance of PsbtInput given either a
// nonWitnessUtxo or a witnessUtxo.
// NOTE only one of the two arguments should be specified, with the other
// being `nil`; otherwise the created PsbtInput object will fail IsSane()
// checks and will not be usable.
func NewPsbtInput(nonWitnessUtxo *wire.MsgTx,
witnessUtxo *wire.TxOut) *PInput {
return &PInput{
NonWitnessUtxo: nonWitnessUtxo,
WitnessUtxo: witnessUtxo,
PartialSigs: []*PartialSig{},
SighashType: 0,
RedeemScript: nil,
WitnessScript: nil,
Bip32Derivation: []*Bip32Derivation{},
FinalScriptSig: nil,
FinalScriptWitness: nil,
Unknowns: nil,
}
}
// IsSane returns true only if there are no conflicting
// values in the Psbt PInput. It checks that witness and non-witness
// utxo entries do not both exist, and that witnessScript entries are only
// added to witness inputs.
func (pi *PInput) IsSane() bool {
if pi.NonWitnessUtxo != nil && pi.WitnessUtxo != nil {
return false
}
if pi.WitnessUtxo == nil && pi.WitnessScript != nil {
return false
}
if pi.WitnessUtxo == nil && pi.FinalScriptWitness != nil {
return false
}
return true
}
func (pi *PInput) deserialize(r io.Reader) error {
for {
keyint, keydata, err := getKey(r)
if err != nil {
return err
}
if keyint == -1 {
// Reached separator byte
break
}
value, err := wire.ReadVarBytes(r, 0, MaxPsbtValueLength,
"PSBT value")
if err != nil {
return err
}
switch uint8(keyint) {
case PsbtInNonWitnessUtxo:
if pi.NonWitnessUtxo != nil {
return ErrDuplicateKey
}
if keydata != nil {
return ErrInvalidKeydata
}
tx := wire.NewMsgTx(2)
if err := tx.Deserialize(bytes.NewReader(value)); err != nil {
return err
}
pi.NonWitnessUtxo = tx
case PsbtInWitnessUtxo:
if pi.WitnessUtxo != nil {
return ErrDuplicateKey
}
if keydata != nil {
return ErrInvalidKeydata
}
txout, err := readTxOut(value)
if err != nil {
return err
}
pi.WitnessUtxo = txout
case PsbtInPartialSig:
newPartialSig := PartialSig{PubKey: keydata,
Signature: value}
if !newPartialSig.checkValid() {
return ErrInvalidPsbtFormat
}
// Duplicate keys are not allowed
for _, x := range pi.PartialSigs {
if bytes.Equal(x.PubKey, newPartialSig.PubKey) {
return ErrDuplicateKey
}
}
pi.PartialSigs = append(pi.PartialSigs, &newPartialSig)
case PsbtInSighashType:
if pi.SighashType != 0 {
return ErrDuplicateKey
}
if keydata != nil {
return ErrInvalidKeydata
}
shtype := txscript.SigHashType(binary.LittleEndian.Uint32(value))
pi.SighashType = shtype
case PsbtInRedeemScript:
if pi.RedeemScript != nil {
return ErrDuplicateKey
}
if keydata != nil {
return ErrInvalidKeydata
}
pi.RedeemScript = value
case PsbtInWitnessScript:
if pi.WitnessScript != nil {
return ErrDuplicateKey
}
if keydata != nil {
return ErrInvalidKeydata
}
pi.WitnessScript = value
case PsbtInBip32Derivation:
if !validatePubkey(keydata) {
return ErrInvalidPsbtFormat
}
master, derivationPath, err := readBip32Derivation(value)
if err != nil {
return err
}
// Duplicate keys are not allowed
for _, x := range pi.Bip32Derivation {
if bytes.Equal(x.PubKey, keydata) {
return ErrDuplicateKey
}
}
pi.Bip32Derivation = append(pi.Bip32Derivation,
&Bip32Derivation{
PubKey: keydata,
MasterKeyFingerprint: master,
Bip32Path: derivationPath,
})
case PsbtInFinalScriptSig:
if pi.FinalScriptSig != nil {
return ErrDuplicateKey
}
if keydata != nil {
return ErrInvalidKeydata
}
pi.FinalScriptSig = value
case PsbtInFinalScriptWitness:
if pi.FinalScriptWitness != nil {
return ErrDuplicateKey
}
if keydata != nil {
return ErrInvalidKeydata
}
pi.FinalScriptWitness = value
default:
keyintanddata := []byte{byte(keyint)}
keyintanddata = append(keyintanddata, keydata...)
newUnknown := &Unknown{
Key: keyintanddata,
Value: value,
}
// Duplicate key+keydata are not allowed
for _, x := range pi.Unknowns {
if bytes.Equal(x.Key, newUnknown.Key) && bytes.Equal(x.Value,
newUnknown.Value) {
return ErrDuplicateKey
}
}
pi.Unknowns = append(pi.Unknowns, newUnknown)
}
}
return nil
}
func (pi *PInput) serialize(w io.Writer) error {
if !pi.IsSane() {
return ErrInvalidPsbtFormat
}
if pi.NonWitnessUtxo != nil {
var buf bytes.Buffer
err := pi.NonWitnessUtxo.Serialize(&buf)
if err != nil {
return err
}
err = serializeKVPairWithType(w, PsbtInNonWitnessUtxo, nil,
buf.Bytes())
if err != nil {
return err
}
}
if pi.WitnessUtxo != nil {
var buf bytes.Buffer
err := wire.WriteTxOut(&buf, 0, 0, pi.WitnessUtxo)
if err != nil {
return err
}
err = serializeKVPairWithType(w, PsbtInWitnessUtxo, nil, buf.Bytes())
if err != nil {
return err
}
}
if pi.FinalScriptSig == nil && pi.FinalScriptWitness == nil {
sort.Sort(PartialSigSorter(pi.PartialSigs))
for _, ps := range pi.PartialSigs {
err := serializeKVPairWithType(w, PsbtInPartialSig, ps.PubKey,
ps.Signature)
if err != nil {
return err
}
}
if pi.SighashType != 0 {
var shtBytes [4]byte
binary.LittleEndian.PutUint32(shtBytes[:],
uint32(pi.SighashType))
err := serializeKVPairWithType(w, PsbtInSighashType, nil,
shtBytes[:])
if err != nil {
return err
}
}
if pi.RedeemScript != nil {
err := serializeKVPairWithType(w, PsbtInRedeemScript, nil,
pi.RedeemScript)
if err != nil {
return err
}
}
if pi.WitnessScript != nil {
err := serializeKVPairWithType(w, PsbtInWitnessScript, nil,
pi.WitnessScript)
if err != nil {
return err
}
}
sort.Sort(Bip32Sorter(pi.Bip32Derivation))
for _, kd := range pi.Bip32Derivation {
err := serializeKVPairWithType(w, PsbtInBip32Derivation,
kd.PubKey,
SerializeBIP32Derivation(kd.MasterKeyFingerprint,
kd.Bip32Path))
if err != nil {
return err
}
}
}
if pi.FinalScriptSig != nil {
err := serializeKVPairWithType(w, PsbtInFinalScriptSig, nil,
pi.FinalScriptSig)
if err != nil {
return err
}
}
if pi.FinalScriptWitness != nil {
err := serializeKVPairWithType(w, PsbtInFinalScriptWitness, nil,
pi.FinalScriptWitness)
if err != nil {
return err
}
}
// Unknown is a special case; we don't have a key type, only
// a key and a value field
for _, kv := range pi.Unknowns {
err := serializeKVpair(w, kv.Key, kv.Value)
if err != nil {
return err
}
}
return nil
}
// POutput is a struct encapsulating all the data that can be attached
// to any specific output of the PSBT.
type POutput struct {
RedeemScript []byte
WitnessScript []byte
Bip32Derivation []*Bip32Derivation
}
// NewPsbtOutput creates an instance of PsbtOutput; the three parameters
// redeemScript, witnessScript and Bip32Derivation are all allowed to be
// `nil`.
func NewPsbtOutput(redeemScript []byte, witnessScript []byte,
bip32Derivation []*Bip32Derivation) *POutput {
return &POutput{
RedeemScript: redeemScript,
WitnessScript: witnessScript,
Bip32Derivation: bip32Derivation,
}
}
func (po *POutput) deserialize(r io.Reader) error {
for {
keyint, keydata, err := getKey(r)
if err != nil {
return err
}
if keyint == -1 {
// Reached separator byte
break
}
value, err := wire.ReadVarBytes(r, 0, MaxPsbtValueLength,
"PSBT value")
if err != nil {
return err
}
switch uint8(keyint) {
case PsbtOutRedeemScript:
if po.RedeemScript != nil {
return ErrDuplicateKey
}
if keydata != nil {
return ErrInvalidKeydata
}
po.RedeemScript = value
case PsbtOutWitnessScript:
if po.WitnessScript != nil {
return ErrDuplicateKey
}
if keydata != nil {
return ErrInvalidKeydata
}
po.WitnessScript = value
case PsbtOutBip32Derivation:
if !validatePubkey(keydata) {
return ErrInvalidKeydata
}
master, derivationPath, err := readBip32Derivation(value)
if err != nil {
return err
}
// Duplicate keys are not allowed
for _, x := range po.Bip32Derivation {
if bytes.Equal(x.PubKey, keydata) {
return ErrDuplicateKey
}
}
po.Bip32Derivation = append(po.Bip32Derivation,
&Bip32Derivation{
PubKey: keydata,
MasterKeyFingerprint: master,
Bip32Path: derivationPath,
})
default:
// unknown type is allowed for inputs but not outputs
return ErrInvalidPsbtFormat
}
}
return nil
}
func (po *POutput) serialize(w io.Writer) error {
if po.RedeemScript != nil {
err := serializeKVPairWithType(w, PsbtOutRedeemScript, nil,
po.RedeemScript)
if err != nil {
return err
}
}
if po.WitnessScript != nil {
err := serializeKVPairWithType(w, PsbtOutWitnessScript, nil,
po.WitnessScript)
if err != nil {
return err
}
}
sort.Sort(Bip32Sorter(po.Bip32Derivation))
for _, kd := range po.Bip32Derivation {
err := serializeKVPairWithType(w, PsbtOutBip32Derivation,
kd.PubKey,
SerializeBIP32Derivation(kd.MasterKeyFingerprint,
kd.Bip32Path))
if err != nil {
return err
}
}
return nil
}
// Psbt is a set of 1 + N + M key-value pair lists, 1 global,
// defining the unsigned transaction structure with N inputs and M outputs.
// These key-value pairs can contain scripts, signatures,
// key derivations and other transaction-defining data.
type Psbt struct {
UnsignedTx *wire.MsgTx // Deserialization of unsigned tx
Inputs []PInput
Outputs []POutput
Unknowns []Unknown // Data of unknown type at global scope
}
// validateUnsignedTx returns true if the transaction is unsigned.
// Note that more basic sanity requirements,
// such as the presence of inputs and outputs, is implicitly
// checked in the call to MsgTx.Deserialize()
func validateUnsignedTX(tx *wire.MsgTx) bool {
for _, tin := range tx.TxIn {
if len(tin.SignatureScript) != 0 || len(tin.Witness) != 0 {
return false
}
}
return true
}
// NewPsbtFromUnsignedTx creates a new Psbt struct, without
// any signatures (i.e. only the global section is non-empty).
func NewPsbtFromUnsignedTx(tx *wire.MsgTx) (*Psbt, error) {
if !validateUnsignedTX(tx) {
return nil, ErrInvalidRawTxSigned
}
inSlice := make([]PInput, len(tx.TxIn))
outSlice := make([]POutput, len(tx.TxOut))
unknownSlice := make([]Unknown, 0)
retPsbt := Psbt{
UnsignedTx: tx,
Inputs: inSlice,
Outputs: outSlice,
Unknowns: unknownSlice,
}
return &retPsbt, nil
}
// NewPsbt returns a new instance of a Psbt struct created
// by reading from a byte slice. If the format is invalid, an error
// is returned. If the argument b64 is true, the passed byte slice
// is decoded from base64 encoding before processing.
// NOTE To create a Psbt from one's own data, rather than reading
// in a serialization from a counterparty, one should use a psbt.Creator.
func NewPsbt(psbtBytes []byte, b64 bool) (*Psbt, error) {
var err error
if b64 {
decoded := make([]byte, base64.StdEncoding.DecodedLen(len(psbtBytes)))
_, err = base64.StdEncoding.Decode(decoded, psbtBytes)
if err != nil {
return nil, err
}
psbtBytes = decoded
}
r := bytes.NewReader(psbtBytes)
// The Psbt struct does not store the fixed magic bytes,
// but they must be present or the serialization must
// be explicitly rejected.
var magic [5]byte
if _, err = io.ReadFull(r, magic[:]); err != nil {
return nil, err
}
if magic != psbtMagic {
return nil, ErrInvalidMagicBytes
}
// Next we parse the GLOBAL section.
// There is currently only 1 known key type, UnsignedTx.
// We insist this exists first; unknowns are allowed, but
// only after.
keyint, keydata, err := getKey(r)
if err != nil {
return nil, err
}
if uint8(keyint) != PsbtGlobalUnsignedTx || keydata != nil {
return nil, ErrInvalidPsbtFormat
}
value, err := wire.ReadVarBytes(r, 0, MaxPsbtValueLength,
"PSBT value")
if err != nil {
return nil, err
}
// Attempt to deserialize the unsigned transaction.
msgTx := wire.NewMsgTx(2)
err = msgTx.Deserialize(bytes.NewReader(value))
if err != nil {
return nil, err
}
if !validateUnsignedTX(msgTx) {
return nil, ErrInvalidRawTxSigned
}
// parse any unknowns that may be present, break at separator
unknownSlice := make([]Unknown, 0)
for {
keyint, keydata, err := getKey(r)
if err != nil {
return nil, ErrInvalidPsbtFormat
}
if keyint == -1 {
break
}
value, err := wire.ReadVarBytes(r, 0, MaxPsbtValueLength,
"PSBT value")
if err != nil {
return nil, err
}
keyintanddata := []byte{byte(keyint)}
keyintanddata = append(keyintanddata, keydata...)
newUnknown := Unknown{
Key: keyintanddata,
Value: value,
}
unknownSlice = append(unknownSlice, newUnknown)
}
// Next we parse the INPUT section
inSlice := make([]PInput, len(msgTx.TxIn))
for i := range msgTx.TxIn {
input := PInput{}
err = input.deserialize(r)
if err != nil {
return nil, err
}
inSlice[i] = input
}
//Next we parse the OUTPUT section
outSlice := make([]POutput, len(msgTx.TxOut))
for i := range msgTx.TxOut {
output := POutput{}
err = output.deserialize(r)
if err != nil {
return nil, err
}
outSlice[i] = output
}
// Populate the new Psbt object
newPsbt := Psbt{
UnsignedTx: msgTx,
Inputs: inSlice,
Outputs: outSlice,
Unknowns: unknownSlice,
}
// Extended sanity checking is applied here
// to make sure the externally-passed Psbt follows
// all the rules.
if err = newPsbt.SanityCheck(); err != nil {
return nil, err
}
return &newPsbt, nil
}
// Serialize creates a binary serialization of the referenced
// Psbt struct with lexicographical ordering (by key) of the subsections
func (p *Psbt) Serialize() ([]byte, error) {
serPsbt := []byte{}
serPsbt = append(serPsbt, psbtMagic[:]...)
// Create serialization of unsignedtx
serializedTx := bytes.NewBuffer(make([]byte, 0,
p.UnsignedTx.SerializeSize()))
if err := p.UnsignedTx.Serialize(serializedTx); err != nil {
return nil, err
}
var buf bytes.Buffer
err := serializeKVPairWithType(&buf, PsbtGlobalUnsignedTx,
nil, serializedTx.Bytes())
if err != nil {
return nil, err
}
serPsbt = append(serPsbt, buf.Bytes()...)
serPsbt = append(serPsbt, 0x00)
for _, pInput := range p.Inputs {
var buf bytes.Buffer
err := pInput.serialize(&buf)
if err != nil {
return nil, err
}
serPsbt = append(serPsbt, buf.Bytes()...)
serPsbt = append(serPsbt, 0x00)
}
for _, pOutput := range p.Outputs {
var buf bytes.Buffer
err := pOutput.serialize(&buf)
if err != nil {
return nil, err
}
serPsbt = append(serPsbt, buf.Bytes()...)
serPsbt = append(serPsbt, 0x00)
}
return serPsbt, nil
}
// B64Encode returns the base64 encoding of the serialization of
// the current PSBT, or an error if the encoding fails.
func (p *Psbt) B64Encode() (string, error) {
raw, err := p.Serialize()
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(raw), nil
}
// IsComplete returns true only if all of the inputs are
// finalized; this is particularly important in that it decides
// whether the final extraction to a network serialized signed
// transaction will be possible.
func (p *Psbt) IsComplete() bool {
for i := 0; i < len(p.UnsignedTx.TxIn); i++ {
if !isFinalized(p, i) {
return false
}
}
return true
}
// SanityCheck checks conditions on a PSBT to ensure that it obeys the
// rules of BIP174, and returns true if so, false if not.
func (p *Psbt) SanityCheck() error {
if !validateUnsignedTX(p.UnsignedTx) {
return ErrInvalidRawTxSigned
}
for _, tin := range p.Inputs {
if !tin.IsSane() {
return ErrInvalidPsbtFormat
}
}
return nil
}

1222
psbt/psbt_test.go Normal file

File diff suppressed because it is too large Load diff

132
psbt/signer.go Normal file
View file

@ -0,0 +1,132 @@
// 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
// signer encapsulates the role 'Signer'
// as specified in BIP174; it controls the insertion of signatures;
// the Sign() function will attempt to insert signatures using
// Updater.addPartialSignature, after first ensuring the Psbt is in the
// correct state.
import (
"github.com/btcsuite/btcd/txscript"
)
// 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
// optionally provide both witnessScript and/or redeemScript, otherwise
// these arguments must be set as nil (and in that case, they must already
// be present in the PSBT if required for signing to succeed).
//
// Return value:
// 0 indicates that the partial signature was successfully attached.
// 1 indicates that this input is already finalized, so the provided
// signature was *not* attached
// -1 indicates that the provided signature data was not valid. In this
// case an error will also be returned.
//
// 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,
redeemScript []byte, witnessScript []byte) (int, error) {
if isFinalized(u.Upsbt, inIndex) {
return 1, 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)
if err != nil {
return -1, err
}
}
if redeemScript != nil {
// Add the redeemScript to the PSBT in preparation.
// If it already exists, it will be overwritten.
err := u.AddInRedeemScript(redeemScript, inIndex)
if err != nil {
return -1, err
}
}
// At this point, the PSBT must have the requisite
// witnessScript or redeemScript fields for signing to succeed.
// case 1: if witnessScript is present, it must be of type witness;
// if not, signature insertion will of course fail.
if u.Upsbt.Inputs[inIndex].WitnessScript != nil {
if u.Upsbt.Inputs[inIndex].WitnessUtxo == nil {
err := nonWitnessToWitness(u.Upsbt, inIndex)
if err != nil {
return -1, err
}
}
err := u.addPartialSignature(inIndex, sig, pubKey)
if err != nil {
return -1, err
}
} else if u.Upsbt.Inputs[inIndex].RedeemScript != nil {
// case 2: no witness script, only redeem script; can be legacy
// p2sh or p2sh-wrapped p2wkh
// We only need to decide if the input is witness, and we don't
// rely on the witnessutxo/nonwitnessutxo in the PSBT, instead
// we check the redeemScript content:
if txscript.IsWitnessProgram(redeemScript) {
if u.Upsbt.Inputs[inIndex].WitnessUtxo == nil {
err := nonWitnessToWitness(u.Upsbt, inIndex)
if err != nil {
return -1, err
}
}
}
// 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)
if err != nil {
return -1, err
}
} else {
// case 3: Neither provided only works for native p2wkh, or
// non-segwit non-p2sh. To check if it's segwit, check
// the scriptPubKey of the output.
if u.Upsbt.Inputs[inIndex].WitnessUtxo == nil {
outIndex := u.Upsbt.UnsignedTx.TxIn[inIndex].
PreviousOutPoint.Index
script := u.Upsbt.Inputs[inIndex].NonWitnessUtxo.
TxOut[outIndex].PkScript
if txscript.IsWitnessProgram(script) {
err := nonWitnessToWitness(u.Upsbt, inIndex)
if err != nil {
return -1, err
}
}
}
err := u.addPartialSignature(inIndex, sig, pubKey)
if err != nil {
return -1, err
}
}
return 0, 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 *Psbt, inIndex int) error {
outIndex := p.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Index
txout := p.Inputs[inIndex].NonWitnessUtxo.TxOut[outIndex]
// Remove the non-witness first, else sanity check will not pass:
p.Inputs[inIndex].NonWitnessUtxo = nil
u := Updater{Upsbt: p}
return u.AddInWitnessUtxo(txout, inIndex)
}

312
psbt/updater.go Normal file
View file

@ -0,0 +1,312 @@
// 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
}