783 lines
20 KiB
Go
783 lines
20 KiB
Go
// Copyright (c) 2018 The btcsuite developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Package psbt 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"
|
|
)
|
|
|
|
// psbtMagicLength is the length of the magic bytes used to signal the start of
|
|
// a serialized PSBT packet.
|
|
const psbtMagicLength = 5
|
|
|
|
var (
|
|
// psbtMagic is the separator
|
|
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
|
|
|
|
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")
|
|
)
|
|
|
|
// 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
|
|
}
|