PSBT BIP 174 implementation (#126)
Implements: PSBT struct, roles: creator, updater, signer, extractor. Passing test vectors.
This commit is contained in:
parent
9e5f4b9a99
commit
e17c9730c4
7 changed files with 3274 additions and 0 deletions
70
psbt/creator.go
Normal file
70
psbt/creator.go
Normal 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
66
psbt/extractor.go
Normal 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
485
psbt/finalizer.go
Normal 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
987
psbt/psbt.go
Normal 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
1222
psbt/psbt_test.go
Normal file
File diff suppressed because it is too large
Load diff
132
psbt/signer.go
Normal file
132
psbt/signer.go
Normal 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
312
psbt/updater.go
Normal 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
|
||||
}
|
Loading…
Reference in a new issue