Merge pull request #155 from Roasbeef/psbt-refactor

psbt: refactor new PSBT library to match code style of project
This commit is contained in:
Olaoluwa Osuntokun 2020-01-16 15:10:25 -08:00 committed by GitHub
commit 02a4fd9de1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 2004 additions and 1307 deletions

11
go.mod Normal file
View file

@ -0,0 +1,11 @@
module github.com/btcsuite/btcutil
go 1.13
require (
github.com/aead/siphash v1.0.1
github.com/btcsuite/btcd v0.20.1-beta
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d
)

54
go.sum Normal file
View file

@ -0,0 +1,54 @@
github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd h1:qdGvebPBDuYDPGi1WCPjy1tGyMpmDK8IEapSsszn7HE=
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723 h1:ZA/jbKoGcVAnER6pCHPEkGdZOV7U1oLUedErBHCUMs0=
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495 h1:6IyqGr3fnd0tM3YxipK27TUskaOVUjU2nG45yzwcQKY=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 h1:FOOIBWrEkLgmlgGfMuZT83xIwfPDxEI2OHu6xUmJMFE=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d h1:2+ZP7EfsZV7Vvmx3TIqSlSzATMkTAKqM14YGFPoSKjI=
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

77
psbt/bip32.go Normal file
View file

@ -0,0 +1,77 @@
package psbt
import (
"bytes"
"encoding/binary"
)
// Bip32Derivation encapsulates the data for the input and output
// Bip32Derivation key-value fields.
//
// TODO(roasbeef): use hdkeychain here instead?
type Bip32Derivation struct {
// PubKey is the raw pubkey serialized in compressed format.
PubKey []byte
// MasterKeyFingerprint is the finger print of the master pubkey.
MasterKeyFingerprint uint32
// Bip32Path is the BIP 32 path with child index as a distinct integer.
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 for the Bip32Derivation struct.
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])
var paths []uint32
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 {
var masterKeyBytes [4]byte
binary.LittleEndian.PutUint32(masterKeyBytes[:], masterKeyFingerprint)
derivationPath := make([]byte, 0, 4+4*len(bip32Path))
derivationPath = append(derivationPath, masterKeyBytes[:]...)
for _, path := range bip32Path {
var pathbytes [4]byte
binary.LittleEndian.PutUint32(pathbytes[:], path)
derivationPath = append(derivationPath, pathbytes[:]...)
}
return derivationPath
}

View file

@ -4,44 +4,38 @@
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
// MinTxVersion is the lowest transaction version that we'll permit.
const MinTxVersion = 1
// New on provision of an input and output 'skeleton' for the transaction, a
// new partially populated PBST packet. The populated packet will include the
// unsigned transaction, and the set of known inputs and outputs contained
// within the unsigned transaction. 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. Referencing
// the PSBT BIP, this function serves the roles of teh Creator.
func New(inputs []*wire.OutPoint,
outputs []*wire.TxOut, version int32, nLockTime uint32,
nSequences []uint32) (*Packet, 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.
//
// Ensure that the version of the transaction is greater then our
// minimum allowed transaction version. There must be one sequence
// number per input.
if version < MinTxVersion || len(nSequences) != len(inputs) {
return nil, ErrInvalidPsbtFormat
}
// 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 := wire.NewMsgTx(version)
unsignedTx.LockTime = nLockTime
for i, in := range inputs {
unsignedTx.AddTxIn(&wire.TxIn{
PreviousOutPoint: *in,
@ -57,14 +51,13 @@ func (c *Creator) CreatePsbt(inputs []*wire.OutPoint,
// transaction; the unknown list can be nil.
pInputs := make([]PInput, len(unsignedTx.TxIn))
pOutputs := make([]POutput, len(unsignedTx.TxOut))
c.Cpsbt = &Psbt{
// This new Psbt is "raw" and contains no key-value fields, so sanity
// checking with c.Cpsbt.SanityCheck() is not required.
return &Packet{
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
}, nil
}

View file

@ -12,44 +12,63 @@ package psbt
import (
"bytes"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
)
// Extract takes a finalized psbt and outputs a network serialization
func Extract(p *Psbt) ([]byte, error) {
// Extract takes a finalized psbt.Packet and outputs a finalized transaction
// instance. Note that if the PSBT is in-complete, then an error
// ErrIncompletePSBT will be returned. As the extracted transaction has been
// fully finalized, it will be ready for network broadcast once returned.
func Extract(p *Packet) (*wire.MsgTx, error) {
// If the packet isn't complete, then we'll return an error as it
// doesn't have all the required witness data.
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 {
// First, we'll make a copy of the underlying unsigned transaction (the
// initial template) so we don't mutate it during our activates below.
finalTx := p.UnsignedTx.Copy()
// For each input, we'll now populate any relevant witness and
// sigScript data.
for i, tin := range finalTx.TxIn {
// We'll grab the corresponding internal packet input which
// matches this materialized transaction input and emplace that
// final sigScript (if present).
pInput := p.Inputs[i]
if pInput.FinalScriptSig != nil {
tin.SignatureScript = pInput.FinalScriptSig
}
// Similarly, if there's a final witness, then we'll also need
// to extract that as well, parsing the lower-level transaction
// encoding.
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)
// In order to set the witness, need to re-deserialize
// the field as encoded within the PSBT packet. For
// each input, the witness is encoded as a stack with
// one or more items.
witnessReader := bytes.NewReader(
pInput.FinalScriptWitness,
)
// First we extract the number of witness elements
// encoded in the above witnessReader.
witCount, err := wire.ReadVarInt(witnessReader, 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)
// Now that we know how may inputs we'll need, we'll
// construct a packing slice, then read out each input
// (with a varint prefix) from the witnessReader.
tin.Witness = make(wire.TxWitness, 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")
wit, err := wire.ReadVarBytes(
witnessReader, 0, txscript.MaxScriptSize, "witness",
)
if err != nil {
return nil, err
}
@ -57,10 +76,6 @@ func Extract(p *Psbt) ([]byte, error) {
}
}
}
var networkSerializedTx bytes.Buffer
err = newTx.Serialize(&networkSerializedTx)
if err != nil {
return nil, err
}
return networkSerializedTx.Bytes(), nil
return finalTx, nil
}

View file

@ -6,197 +6,54 @@ 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
// uses it to construct valid final sigScript 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 {
// 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 *Packet, 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]
// isFinalizableWitnessInput returns true if the target input is a witness UTXO
// that can be finalized.
func isFinalizableWitnessInput(pInput *PInput) bool {
pkScript := pInput.WitnessUtxo.PkScript
// 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 {
switch {
// If this is a native witness output, then we require both
// the witness script, but not a redeem script.
case txscript.IsWitnessProgram(pkScript):
if txscript.IsPayToWitnessScriptHash(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 {
// A P2WKH output on the other hand doesn't need
// neither a witnessScript or redeemScript.
if pInput.WitnessScript != nil ||
pInput.RedeemScript != nil {
return false
}
}
} else if txscript.IsPayToScriptHash(pInput.WitnessUtxo.PkScript) {
// For nested P2SH inputs, we verify that a witness script is known.
case txscript.IsPayToScriptHash(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 this is a nested P2SH input, then it must also have a
// witness script, while we don't need one for P2WKH.
if txscript.IsPayToWitnessScriptHash(pInput.RedeemScript) {
if pInput.WitnessScript == nil {
return false
@ -209,11 +66,26 @@ func isFinalizable(p *Psbt, inIndex int) bool {
// unrecognized type
return false
}
// If this isn't a nested nested P2SH output or a native witness
// output, then we can't finalize this input as we don't understand it.
default:
return false
}
} else if pInput.NonWitnessUtxo != nil {
return true
}
// isFinalizableLegacyInput returns true of the passed input a legacy input
// (non-witness) that can be finalized.
func isFinalizableLegacyInput(p *Packet, pInput *PInput, inIndex int) bool {
// If the input has a witness, then it's invalid.
if pInput.WitnessScript != nil {
return false
}
// Otherwise, we'll verify that we only have a RedeemScript if the prev
// output script is P2SH.
outIndex := p.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Index
if txscript.IsPayToScriptHash(pInput.NonWitnessUtxo.TxOut[outIndex].PkScript) {
if pInput.RedeemScript == nil {
@ -224,262 +96,367 @@ func isFinalizable(p *Psbt, inIndex int) bool {
return false
}
}
} else {
// one of witness and nonwitness utxo must be present
return true
}
// isFinalizable checks whether the structure of the entry for the input of the
// psbt.Packet at index inIndex contains sufficient information to finalize
// this input.
func isFinalizable(p *Packet, inIndex int) bool {
pInput := p.Inputs[inIndex]
// The input cannot be finalized without any signatures
if pInput.PartialSigs == nil {
return false
}
// For an input to be finalized, we'll one of two possible top-level
// UTXOs present. Each UTXO type has a distinct set of requirements to
// be considered finalized.
switch {
// A witness input must be either native P2WSH or nested P2SH with all
// relevant sigScript or witness data populated.
case pInput.WitnessUtxo != nil:
if !isFinalizableWitnessInput(&pInput) {
return false
}
case pInput.NonWitnessUtxo != nil:
if !isFinalizableLegacyInput(p, &pInput, inIndex) {
return false
}
// If neither a known UTXO type isn't present at all, then we'll
// return false as we need one of them.
default:
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) {
// 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 *Packet, 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 {
if err := Finalize(p, inIndex); 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 {
// MaybeFinalizeAll attempts to finalize all inputs of the psbt.Packet that are
// not already finalized, and returns an error if it fails to do so.
func MaybeFinalizeAll(p *Packet) 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
// Finalize assumes that the provided psbt.Packet 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 sigScript 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 *Packet, inIndex int) error {
pInput := p.Inputs[inIndex]
if pInput.WitnessUtxo != nil {
err = FinalizeWitness(p, inIndex)
if err != nil {
// Depending on the UTXO type, we either attempt to finalize it as a
// witness or legacy UTXO.
switch {
case pInput.WitnessUtxo != nil:
if err := finalizeWitnessInput(p, inIndex); err != nil {
return err
}
} else if pInput.NonWitnessUtxo != nil {
err = FinalizeNonWitness(p, inIndex)
if err != nil {
case pInput.NonWitnessUtxo != nil:
if err := finalizeNonWitnessInput(p, inIndex); err != nil {
return err
}
} else {
default:
return ErrInvalidPsbtFormat
}
if err = p.SanityCheck(); err != nil {
// Before returning we sanity check the PSBT to ensure we don't extract
// an invalid transaction or produce an invalid intermediate state.
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 {
// checkFinalScriptSigWitness checks whether a given input in the psbt.Packet
// struct already has the fields 07 (FinalInScriptSig) or 08 (FinalInWitness).
// If so, it returns true. It does not modify the Psbt.
func checkFinalScriptSigWitness(p *Packet, 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 {
// finalizeNonWitnessInput attempts to create a PsbtInFinalScriptSig field for
// the 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 finalizeNonWitnessInput(p *Packet, inIndex int) error {
// If this input has already been finalized, then we'll return an error
// as we can't proceed.
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
// Our goal here is to construct a sigScript 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
var sigScript []byte
pInput := p.Inputs[inIndex]
containsRedeemScript := pInput.RedeemScript != nil
var pubKeys [][]byte
var sigs [][]byte
var (
pubKeys [][]byte
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)
}
// We have failed to identify at least 1 (sig, pub) pair in the PSBT,
// which indicates it was not ready to be finalized. As a result, we
// can't proceed.
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 this input doesn't need a redeem script (P2PKH), then we'll
// construct a simple sigScript that's just the signature then the
// pubkey (OP_CHECKSIG).
var err error
if !containsRedeemScript {
// p2pkh - insist on one sig/pub and build scriptSig
// At this point, we should only have a single signature and
// pubkey.
if len(sigs) != 1 || len(pubKeys) != 1 {
return ErrNotFinalizable
}
// In this case, our sigScript is just: <sig> <pubkey>.
builder := txscript.NewScriptBuilder()
builder.AddData(sigs[0]).AddData(pubKeys[0])
scriptSig, err = builder.Script()
sigScript, 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)
// 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.
// At this point, we assume that this is a mult-sig input, so
// we construct our sigScript which looks something like this
// (mind the extra element for the extra multi-sig pop):
// * <nil> <sigs...> <redeemScript>
//
// TODO(waxwing): 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()
sigScript, 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)
// At this point, a sigScript 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.
newInput.FinalScriptSig = sigScript
// 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 {
// finalizeWitnessInput 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 finalizeWitnessInput(p *Packet, inIndex int) error {
// If this input has already been finalized, then we'll return an error
// as we can't proceed.
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
// Depending on the actual output type, we'll either populate a
// serializedWitness or a witness as well asa sigScript.
var (
sigScript []byte
serializedWitness []byte
)
pInput := p.Inputs[inIndex]
containsRedeemScript := pInput.RedeemScript != nil
cointainsWitnessScript := pInput.WitnessScript != nil
var pubKeys [][]byte
var sigs [][]byte
// First we'll validate and collect the pubkey+sig pairs from the set
// of partial signatures.
var (
pubKeys [][]byte
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 at this point, we don't have any pubkey+sig pairs, then we bail
// as we can't proceed.
if len(sigs) == 0 || len(pubKeys) == 0 {
return ErrNotFinalizable
}
containsRedeemScript := pInput.RedeemScript != nil
cointainsWitnessScript := pInput.WitnessScript != nil
// If there's no redeem script, then we assume that this is native
// segwit input.
var err error
if !containsRedeemScript {
if len(pubKeys) == 1 && len(sigs) == 1 && !cointainsWitnessScript {
// p2wkh case
witness, err = writePKHWitness(sigs[0], pubKeys[0])
// If we have only a sigley pubkey+sig pair, and no witness
// script, then we assume this is a P2WKH input.
if len(pubKeys) == 1 && len(sigs) == 1 &&
!cointainsWitnessScript {
serializedWitness, 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)
// Otherwise, we must have a witnessScript field, so
// we'll generate a valid multi-sig witness.
//
// NOTE: We tacitly assume multisig.
//
// TODO(roasbeef): need to add custom finalize for
// non-multisig P2WSH outputs (HTLCs, delay outputs,
// etc).
if !cointainsWitnessScript {
return ErrNotFinalizable
}
witness, err = getMultisigScriptWitness(pInput.WitnessScript,
pubKeys, sigs)
serializedWitness, 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
// Otherwise, we assume that this is a p2wsh multi-sig output,
// which is nested in a p2sh, or a p2wkh nested in a p2sh.
//
// In this case, we'll take the redeem script (the witness
// program in this case), and push it on the stack within the
// sigScript.
builder := txscript.NewScriptBuilder()
builder.AddData(pInput.RedeemScript)
scriptSig, err = builder.Script()
sigScript, err = builder.Script()
if err != nil {
return err
}
// If don't have a witness script, then we assume this is a
// nested p2wkh output.
if !cointainsWitnessScript {
// Assumed p2sh-p2wkh
// Here the witness is just (sig, pub) as for p2pkh case
// 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])
serializedWitness, 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)
// Otherwise, we assume that this is a p2wsh multi-sig,
// so we generate the proper witness.
serializedWitness, 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)
// At this point, a witness has been constructed, and a sigScript (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
if len(sigScript) > 0 {
newInput.FinalScriptSig = sigScript
}
newInput.FinalScriptWitness = witness
// overwrite the entry in the input list at the correct index
newInput.FinalScriptWitness = serializedWitness
// Finally, we overwrite the entry in the input list at the correct
// index.
p.Inputs[inIndex] = *newInput
return nil
}

9
psbt/go.mod Normal file
View file

@ -0,0 +1,9 @@
module github.com/btcsuite/btcutil/psbt
go 1.13
require (
github.com/btcsuite/btcd v0.20.1-beta
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d
github.com/davecgh/go-spew v1.1.1
)

36
psbt/go.sum Normal file
View file

@ -0,0 +1,36 @@
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/btcutil v0.0.0-20191219182022-e17c9730c422 h1:EqnrgSSg0SFWRlEZLExgjtuUR/IPnuQ6qw6nwRda4Uk=
github.com/btcsuite/btcutil v0.0.0-20191219182022-e17c9730c422/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44 h1:9lP3x0pW80sDI6t1UMSLA4to18W7R7imwAI/sWS9S8Q=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

361
psbt/partial_input.go Normal file
View file

@ -0,0 +1,361 @@
package psbt
import (
"bytes"
"encoding/binary"
"io"
"sort"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
)
// 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
}
// deserialize attempts to deserialize a new PInput from the passed io.Reader.
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 InputType(keyint) {
case NonWitnessUtxoType:
if pi.NonWitnessUtxo != nil {
return ErrDuplicateKey
}
if keydata != nil {
return ErrInvalidKeydata
}
tx := wire.NewMsgTx(2)
err := tx.Deserialize(bytes.NewReader(value))
if err != nil {
return err
}
pi.NonWitnessUtxo = tx
case WitnessUtxoType:
if pi.WitnessUtxo != nil {
return ErrDuplicateKey
}
if keydata != nil {
return ErrInvalidKeydata
}
txout, err := readTxOut(value)
if err != nil {
return err
}
pi.WitnessUtxo = txout
case PartialSigType:
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 SighashType:
if pi.SighashType != 0 {
return ErrDuplicateKey
}
if keydata != nil {
return ErrInvalidKeydata
}
shtype := txscript.SigHashType(
binary.LittleEndian.Uint32(value),
)
pi.SighashType = shtype
case RedeemScriptInputType:
if pi.RedeemScript != nil {
return ErrDuplicateKey
}
if keydata != nil {
return ErrInvalidKeydata
}
pi.RedeemScript = value
case WitnessScriptInputType:
if pi.WitnessScript != nil {
return ErrDuplicateKey
}
if keydata != nil {
return ErrInvalidKeydata
}
pi.WitnessScript = value
case Bip32DerivationInputType:
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 FinalScriptSigType:
if pi.FinalScriptSig != nil {
return ErrDuplicateKey
}
if keydata != nil {
return ErrInvalidKeydata
}
pi.FinalScriptSig = value
case FinalScriptWitnessType:
if pi.FinalScriptWitness != nil {
return ErrDuplicateKey
}
if keydata != nil {
return ErrInvalidKeydata
}
pi.FinalScriptWitness = value
default:
// A fall through case for any proprietary types.
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
}
// serialize attempts to serialize the target PInput into the passed io.Writer.
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, uint8(NonWitnessUtxoType), 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, uint8(WitnessUtxoType), 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, uint8(PartialSigType), 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, uint8(SighashType), nil, shtBytes[:],
)
if err != nil {
return err
}
}
if pi.RedeemScript != nil {
err := serializeKVPairWithType(
w, uint8(RedeemScriptInputType), nil,
pi.RedeemScript,
)
if err != nil {
return err
}
}
if pi.WitnessScript != nil {
err := serializeKVPairWithType(
w, uint8(WitnessScriptInputType), nil,
pi.WitnessScript,
)
if err != nil {
return err
}
}
sort.Sort(Bip32Sorter(pi.Bip32Derivation))
for _, kd := range pi.Bip32Derivation {
err := serializeKVPairWithType(
w,
uint8(Bip32DerivationInputType), kd.PubKey,
SerializeBIP32Derivation(
kd.MasterKeyFingerprint, kd.Bip32Path,
),
)
if err != nil {
return err
}
}
}
if pi.FinalScriptSig != nil {
err := serializeKVPairWithType(
w, uint8(FinalScriptSigType), nil, pi.FinalScriptSig,
)
if err != nil {
return err
}
}
if pi.FinalScriptWitness != nil {
err := serializeKVPairWithType(
w, uint8(FinalScriptWitnessType), 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
}

139
psbt/partial_output.go Normal file
View file

@ -0,0 +1,139 @@
package psbt
import (
"bytes"
"io"
"sort"
"github.com/btcsuite/btcd/wire"
)
// 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,
}
}
// deserialize attempts to recode a new POutput from the passed io.Reader.
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 OutputType(keyint) {
case RedeemScriptOutputType:
if po.RedeemScript != nil {
return ErrDuplicateKey
}
if keydata != nil {
return ErrInvalidKeydata
}
po.RedeemScript = value
case WitnessScriptOutputType:
if po.WitnessScript != nil {
return ErrDuplicateKey
}
if keydata != nil {
return ErrInvalidKeydata
}
po.WitnessScript = value
case Bip32DerivationOutputType:
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
}
// serialize attempts to write out the target POutput into the passed
// io.Writer.
func (po *POutput) serialize(w io.Writer) error {
if po.RedeemScript != nil {
err := serializeKVPairWithType(
w, uint8(RedeemScriptOutputType), nil, po.RedeemScript,
)
if err != nil {
return err
}
}
if po.WitnessScript != nil {
err := serializeKVPairWithType(
w, uint8(WitnessScriptOutputType), nil, po.WitnessScript,
)
if err != nil {
return err
}
}
sort.Sort(Bip32Sorter(po.Bip32Derivation))
for _, kd := range po.Bip32Derivation {
err := serializeKVPairWithType(w,
uint8(Bip32DerivationOutputType),
kd.PubKey,
SerializeBIP32Derivation(
kd.MasterKeyFingerprint,
kd.Bip32Path,
),
)
if err != nil {
return err
}
}
return nil
}

58
psbt/partialsig.go Normal file
View file

@ -0,0 +1,58 @@
package psbt
import (
"bytes"
"github.com/btcsuite/btcd/btcec"
)
// 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 for PartialSig.
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
}
// checkValid checks that both the pbukey and sig are valid. See the methods
// (PartialSig, validatePubkey, validateSignature) for more details.
//
// TODO(waxwing): update for Schnorr will be needed here if/when that
// activates.
func (ps *PartialSig) checkValid() bool {
return validatePubkey(ps.PubKey) && validateSignature(ps.Signature)
}

File diff suppressed because it is too large Load diff

View file

@ -25,7 +25,8 @@ import (
// extracting it. Returned are: an unsigned transaction serialization, a list
// of scriptSigs, one per input, and a list of witnesses, one per input.
func createPsbtFromSignedTx(serializedSignedTx []byte) (
*Psbt, [][]byte, []wire.TxWitness, error) {
*Packet, [][]byte, []wire.TxWitness, error) {
tx := wire.NewMsgTx(2)
err := tx.Deserialize(bytes.NewReader(serializedSignedTx))
if err != nil {
@ -34,6 +35,7 @@ func createPsbtFromSignedTx(serializedSignedTx []byte) (
scriptSigs := make([][]byte, 0, len(tx.TxIn))
witnesses := make([]wire.TxWitness, 0, len(tx.TxIn))
tx2 := tx.Copy()
// Blank out signature info in inputs
for i, tin := range tx2.TxIn {
tin.SignatureScript = nil
@ -42,10 +44,10 @@ func createPsbtFromSignedTx(serializedSignedTx []byte) (
witnesses = append(witnesses, tx.TxIn[i].Witness)
}
// Outputs always contain (value, scriptPubkey so don't need amending)
// Now tx2 is tx with all signing data stripped out
unsignedPsbt, err := NewPsbtFromUnsignedTx(tx2)
// Outputs always contain: (value, scriptPubkey) so don't need
// amending. Now tx2 is tx with all signing data stripped out
unsignedPsbt, err := NewFromUnsignedTx(tx2)
if err != nil {
return nil, nil, nil, err
}
@ -116,16 +118,24 @@ func TestReadValidPsbtAndReserialize(t *testing.T) {
if err != nil {
t.Fatalf("Unable to decode hex: %v", err)
}
testPsbt, err := NewPsbt(PsbtBytes, false)
testPsbt, err := NewFromRawBytes(
bytes.NewReader(PsbtBytes), false,
)
if err != nil {
t.Fatalf("unable to parse psbt: %v", err)
}
t.Logf("Successfully parsed test, got transaction: %v",
spew.Sdump(testPsbt.UnsignedTx))
raw, err := testPsbt.Serialize()
var b bytes.Buffer
err = testPsbt.Serialize(&b)
if err != nil {
t.Fatalf("Unable to serialize created Psbt: %v", err)
}
raw := b.Bytes()
if !bytes.Equal(raw, PsbtBytes) {
t.Fatalf("Serialized PSBT didn't match: %v",
hex.EncodeToString(raw))
@ -139,11 +149,13 @@ func TestReadInvalidPsbt(t *testing.T) {
if err != nil {
t.Fatalf("Unable to decode hex: %v", err)
}
_, err = NewPsbt(PsbtBytes, false)
_, err = NewFromRawBytes(bytes.NewReader(PsbtBytes), false)
if err == nil {
t.Fatalf("Incorrectly validated psbt: %v",
hex.EncodeToString(PsbtBytes))
}
t.Logf("Correctly got error: %v", err)
}
}
@ -165,14 +177,16 @@ func TestSanityCheck(t *testing.T) {
if err != nil {
t.Fatalf("Unable to decode hex: %v", err)
}
psbt1, err := NewPsbt(psbtraw1, false)
psbt1, err := NewFromRawBytes(bytes.NewReader(psbtraw1), false)
if err != nil {
t.Fatalf("Unable to create Psbt struct: %v", err)
}
// Add a non-witness utxo field to input2 using raw insertion function,
// so that it becomes invalid, then NewUpdater should fail:
// so that it becomes invalid, then NewUpdater should fail.
nonWitnessUtxoRaw, err := hex.DecodeString(
CUTestHexData["NonWitnessUtxo"])
CUTestHexData["NonWitnessUtxo"],
)
if err != nil {
t.Fatalf("Unable to decode hex: %v", err)
}
@ -183,22 +197,26 @@ func TestSanityCheck(t *testing.T) {
}
inputs1 := &psbt1.Inputs[1]
inputs1.NonWitnessUtxo = nonWitnessUtxo
// The PSBT is now in an inconsistent state; Updater creation should fail
// The PSBT is now in an inconsistent state; Updater creation should
// fail.
updater, err := NewUpdater(psbt1)
if err == nil {
t.Fatalf("Failed to identify invalid PSBT state ( " +
"witness, non-witness fields)")
}
// Overwrite back with the correct psbt
psbtraw1, err = hex.DecodeString(validPsbtHex[1])
if err != nil {
t.Fatalf("Unable to decode hex: %v", err)
}
psbt1, err = NewPsbt(psbtraw1, false)
psbt1, err = NewFromRawBytes(bytes.NewReader(psbtraw1), false)
updater, err = NewUpdater(psbt1)
if err != nil {
t.Fatalf("Unable to create Updater: %v", err)
}
// Create a fake non-witness utxo field to overlap with
// the existing witness input at index 1.
tx := wire.NewMsgTx(2)
@ -211,10 +229,13 @@ func TestSanityCheck(t *testing.T) {
t.Fatalf("Incorrectly accepted Psbt with conflicting witness " +
"and non-witness utxo entries in the same input.")
}
// Now we try again; this time we try to add a witnessScript
// key-value pair to an input which is non-witness, which should
// also be rejected.
psbt2, err := NewPsbt(psbtraw1, false)
psbt2, err := NewFromRawBytes(
bytes.NewReader(psbtraw1), false,
)
if err != nil {
t.Fatalf("Unable to create Psbt struct: %v", err)
}
@ -232,7 +253,6 @@ func TestSanityCheck(t *testing.T) {
t.Fatalf("Incorrectly accepted adding witness script field " +
"to non-witness utxo")
}
}
// Data for creation and updating tests
@ -319,33 +339,37 @@ func TestPsbtCreator(t *testing.T) {
}
prevOut2 := wire.NewOutPoint(hash2, uint32(1))
inputs := []*wire.OutPoint{prevOut1, prevOut2}
creator := Creator{}
// Check creation fails with invalid sequences:
nSequences := []uint32{wire.MaxTxInSequenceNum}
err = creator.CreatePsbt(inputs, outputs, int32(3), uint32(0), nSequences)
_, err = New(inputs, outputs, int32(3), uint32(0), nSequences)
if err == nil {
t.Fatalf("Did not error when creating transaction with " +
"invalid nSequences")
}
nSequences = append(nSequences, wire.MaxTxInSequenceNum)
// Check creation fails with invalid version
err = creator.CreatePsbt(inputs, outputs, int32(3), uint32(0), nSequences)
_, err = New(inputs, outputs, int32(0), uint32(0), nSequences)
if err == nil {
t.Fatalf("Did not error when creating transaction with " +
"invalid version (3)")
}
// Use valid data to create:
creator.CreatePsbt(inputs, outputs, int32(2), uint32(0), nSequences)
rawCreated, err := creator.Cpsbt.Serialize()
cPsbt, err := New(inputs, outputs, int32(2), uint32(0), nSequences)
var b bytes.Buffer
err = cPsbt.Serialize(&b)
if err != nil {
t.Fatalf("Unable to serialize created Psbt: %v", err)
}
if CUTestHexData["COPsbtHex"] != hex.EncodeToString(rawCreated) {
if CUTestHexData["COPsbtHex"] != hex.EncodeToString(b.Bytes()) {
t.Fatalf("Failed to create expected psbt, instead got: %v",
hex.EncodeToString(rawCreated))
hex.EncodeToString(b.Bytes()))
}
// Now simulate passing the created PSBT to an Updater
updater, err := NewUpdater(creator.Cpsbt)
updater, err := NewUpdater(cPsbt)
if err != nil {
t.Fatalf("Unable to create Updater object")
}
@ -375,11 +399,13 @@ func TestPsbtCreator(t *testing.T) {
t.Fatalf("Unable to add Witness Utxo to inputs: %v", err)
}
rawUpdated, err := updater.Upsbt.Serialize()
b.Reset()
err = updater.Upsbt.Serialize(&b)
if err != nil {
t.Fatalf("Unable to serialize updated Psbt: %v", err)
}
if CUTestHexData["UOPsbtHex"] != hex.EncodeToString(rawUpdated) {
if CUTestHexData["UOPsbtHex"] != hex.EncodeToString(b.Bytes()) {
t.Fatal("Failed to create valid updated PSBT after utxos")
}
input1RedeemScript, err := hex.DecodeString(CUTestHexData["Input1RedeemScript"])
@ -406,11 +432,13 @@ func TestPsbtCreator(t *testing.T) {
if err != nil {
t.Fatalf("Unable to add witness script: %v", err)
}
rawUpdated, err = updater.Upsbt.Serialize()
b.Reset()
err = updater.Upsbt.Serialize(&b)
if err != nil {
t.Fatalf("Unable to serialize updated Psbt: %v", err)
}
if CUTestHexData["UOPsbtHex2"] != hex.EncodeToString(rawUpdated) {
if CUTestHexData["UOPsbtHex2"] != hex.EncodeToString(b.Bytes()) {
t.Fatal("Failed to create valid updated PSBT after redeem scripts")
}
masterKey, err := hex.DecodeString(CUMasterKeyFingerPrint)
@ -490,11 +518,13 @@ func TestPsbtCreator(t *testing.T) {
if err != nil {
t.Fatal("Failed to add key to second output")
}
rawUpdated, err = updater.Upsbt.Serialize()
b.Reset()
err = updater.Upsbt.Serialize(&b)
if err != nil {
t.Fatalf("Unable to serialize updated Psbt: %v", err)
}
if CUTestHexData["UOPsbtHex3"] != hex.EncodeToString(rawUpdated) {
if CUTestHexData["UOPsbtHex3"] != hex.EncodeToString(b.Bytes()) {
t.Fatal("Failed to create valid updated PSBT after BIP32 derivations")
}
err = updater.AddInSighashType(txscript.SigHashType(1), 0)
@ -505,11 +535,13 @@ func TestPsbtCreator(t *testing.T) {
if err != nil {
t.Fatal("Failed to add sighash type to second input")
}
rawUpdated, err = updater.Upsbt.Serialize()
b.Reset()
err = updater.Upsbt.Serialize(&b)
if err != nil {
t.Fatalf("Unable to serialize updated Psbt: %v", err)
}
if CUTestHexData["UOPsbtHex4"] != hex.EncodeToString(rawUpdated) {
if CUTestHexData["UOPsbtHex4"] != hex.EncodeToString(b.Bytes()) {
t.Fatal("Failed to create valid updated PSBT after sighash types")
}
b644, err := updater.Upsbt.B64Encode()
@ -536,7 +568,10 @@ var signerPsbtData = map[string]string{
}
func TestPsbtSigner(t *testing.T) {
psbt1, err := NewPsbt([]byte(signerPsbtData["signer1PsbtB64"]), true)
psbt1, err := NewFromRawBytes(
bytes.NewReader([]byte(signerPsbtData["signer1PsbtB64"])),
true,
)
if err != nil {
t.Fatalf("Failed to parse PSBT: %v", err)
}
@ -559,11 +594,13 @@ func TestPsbtSigner(t *testing.T) {
if err != nil {
t.Fatalf("Unable to decode hex: %v", err)
}
rawUpdated, err := psbtUpdater1.Upsbt.Serialize()
var b bytes.Buffer
err = psbtUpdater1.Upsbt.Serialize(&b)
if err != nil {
t.Fatalf("Unable to serialize updated Psbt: %v", err)
}
if !bytes.Equal(rawUpdated, signer1Result) {
if !bytes.Equal(b.Bytes(), signer1Result) {
t.Fatalf("Failed to add signatures correctly")
}
}
@ -580,12 +617,15 @@ var finalizerPsbtData = map[string]string{
func TestPsbtExtractor(t *testing.T) {
rawToFinalize, err := base64.StdEncoding.DecodeString(
finalizerPsbtData["finalizeb64"])
finalizerPsbtData["finalizeb64"],
)
if err != nil {
t.Fatalf("Error decoding b64: %v", err)
}
psbt1, err := NewPsbt(rawToFinalize, false)
psbt1, err := NewFromRawBytes(
bytes.NewReader(rawToFinalize), false,
)
if err != nil {
t.Fatalf("Failed to parse PSBT: %v", err)
}
@ -598,7 +638,8 @@ func TestPsbtExtractor(t *testing.T) {
}
finalizer1Result, err := base64.StdEncoding.DecodeString(
finalizerPsbtData["resultb64"])
finalizerPsbtData["resultb64"],
)
if err != nil {
t.Fatalf("Unable to decode b64: %v", err)
}
@ -606,19 +647,26 @@ func TestPsbtExtractor(t *testing.T) {
if err != nil {
t.Fatalf("Unable to decode hex: %v", err)
}
resultToNetwork, err := Extract(psbt1)
tx, err := Extract(psbt1)
if err != nil {
t.Fatalf("Failed to extract: %v", err)
}
rawPsbt1, err := psbt1.Serialize()
var resultToNetwork bytes.Buffer
if err := tx.Serialize(&resultToNetwork); err != nil {
t.Fatalf("unable to serialize: %v", err)
}
var b bytes.Buffer
err = psbt1.Serialize(&b)
if err != nil {
t.Fatalf("Unable to serialize updated Psbt: %v", err)
}
if !bytes.Equal(rawPsbt1, finalizer1Result) {
t.Fatalf("Failed to finalize transaction %x", rawPsbt1)
if !bytes.Equal(b.Bytes(), finalizer1Result) {
t.Fatalf("Failed to finalize transaction: expected %x, "+
"got %x", finalizer1Result, b.Bytes())
}
if !bytes.Equal(finalToNetworkExpected, resultToNetwork) {
t.Fatalf("Failed to network serialize transaction: %x", resultToNetwork)
if !bytes.Equal(finalToNetworkExpected, resultToNetwork.Bytes()) {
t.Fatalf("Failed to network serialize transaction: %x", b.Bytes())
}
}
@ -629,7 +677,7 @@ func TestImportFromCore1(t *testing.T) {
// separately, then finalize and extract, and compare with the network
// serialized tx output from Core.
imported := "cHNidP8BAJwCAAAAAjaoF6eKeGsPiDQxxqqhFDfHWjBtZzRqmaZmvyCVWZ5JAQAAAAD/////RhypNiFfnQSMNpo0SGsgIvDOyMQFAYEHZXD5jp4kCrUAAAAAAP////8CgCcSjAAAAAAXqRQFWy8ScSkkhlGMwfOnx15YwRzApofwX5MDAAAAABepFAt4TyLfGnL9QY6GLYHbpSQj+QclhwAAAAAAAAAAAA=="
psbt1, err := NewPsbt([]byte(imported), true)
psbt1, err := NewFromRawBytes(bytes.NewReader([]byte(imported)), true)
if err != nil {
t.Fatalf("Failed to parse PSBT: %v", err)
}
@ -709,7 +757,7 @@ func TestImportFromCore1(t *testing.T) {
// modifications to the input data and check it fails sanity checks.
// First an invalid tx:
psbtBorkedInput2, _ := NewPsbt([]byte(imported), true)
psbtBorkedInput2, _ := NewFromRawBytes(bytes.NewReader([]byte(imported)), true)
borkedUpdater, err := NewUpdater(psbtBorkedInput2)
if err != nil {
t.Fatalf("NewUpdater failed while trying to create borked "+
@ -759,15 +807,23 @@ func TestImportFromCore1(t *testing.T) {
t.Fatalf("Failed to finalize second input, %v", err)
}
networkSerializedTx, err := Extract(psbt1)
tx, err := Extract(psbt1)
if err != nil {
t.Fatalf("unable to extract tx: %v", err)
}
var networkSerializedTx bytes.Buffer
if err := tx.Serialize(&networkSerializedTx); err != nil {
t.Fatalf("unable to encode tx: %v", err)
}
expectedTx := "0200000000010236a817a78a786b0f883431c6aaa11437c75a306d67346a99a666bf2095599e490100000000ffffffff461ca936215f9d048c369a34486b2022f0cec8c4050181076570f98e9e240ab5000000006a473044022014eb9c4858f71c9f280bc68402aa742a5187f54c56c8eb07c902eb1eb5804e5502203d66656de8386b9b044346d5605f5ae2b200328fb30476f6ac993fc0dbb04559012103b4c79acdf4e7d978bef4019c421e4c6c67044ed49d27322dc90e808d8080e862ffffffff028027128c0000000017a914055b2f1271292486518cc1f3a7c75e58c11cc0a687f05f93030000000017a9140b784f22df1a72fd418e862d81dba52423f90725870247304402200da03ac9890f5d724c42c83c2a62844c08425a274f1a5bca50dcde4126eb20dd02205278897b65cb8e390a0868c9582133c7157b2ad3e81c1c70d8fbd65f51a5658b0121024d6b24f372dd4551277c8df4ecc0655101e11c22894c8e05a3468409c865a72c0000000000"
expectedTxBytes, err := hex.DecodeString(expectedTx)
if err != nil {
t.Fatalf("Unable to decode hex: %v", err)
}
if !bytes.Equal(expectedTxBytes, networkSerializedTx) {
if !bytes.Equal(expectedTxBytes, networkSerializedTx.Bytes()) {
t.Fatalf("The produced network transaction did not match the expected: %x \n %x \n",
networkSerializedTx, expectedTxBytes)
networkSerializedTx.Bytes(), expectedTxBytes)
}
}
@ -787,7 +843,7 @@ func TestImportFromCore2(t *testing.T) {
// the previous example, we cannot here compare with a Core produced
// network serialized final transaction, because of the fake input.
imported := "cHNidP8BAJsCAAAAAkxTQ+rig5QNnUS5nMc+Pccow4IcOJeQRcNNw+7p5ZA5AQAAAAD/////qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqoNAAAAAP////8CAIYOcAAAAAAWABQ1l7nn13RubTwqRQU2BnVV5WlXBWAxMbUAAAAAF6kUkiuXUjfWFgTp6nl/gf9+8zIWR6KHAAAAAAAAAAAA"
psbt1, err := NewPsbt([]byte(imported), true)
psbt1, err := NewFromRawBytes(bytes.NewReader([]byte(imported)), true)
if err != nil {
t.Fatalf("Failed to parse PSBT: %v", err)
}
@ -877,7 +933,7 @@ func TestImportFromCore2(t *testing.T) {
fakevalSerialized := binary.LittleEndian.Uint64(fakeTxOutSerialized[:8])
fakeScriptPubKey := fakeTxOutSerialized[9:]
txFund2Out := wire.NewTxOut(int64(fakevalSerialized), fakeScriptPubKey)
psbt2, err := NewPsbt([]byte(expectedPsbtPartialB64), true)
psbt2, err := NewFromRawBytes(bytes.NewReader([]byte(expectedPsbtPartialB64)), true)
if err != nil {
t.Fatalf("Failed to load partial PSBT: %v", err)
}
@ -984,14 +1040,22 @@ func TestImportFromCore2(t *testing.T) {
if uoutput2.WitnessScript == nil {
t.Fatalf("PSBT should contain outwitnessscript but it does not.")
}
var tx bytes.Buffer
networkSerializedTx, err := Extract(psbt2)
if err != nil {
t.Fatalf("unable to extract tx: %v", err)
}
if err := networkSerializedTx.Serialize(&tx); err != nil {
t.Fatalf("unable to encode tx: %v", err)
}
expectedSerializedTx, err := hex.DecodeString("020000000001024c5343eae283940d9d44b99cc73e3dc728c3821c38979045c34dc3eee9e5903901000000171600147aed39420a8b7ab98a83791327ccb70819d1fbe2ffffffffaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0d000000232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903ffffffff0200860e70000000001600143597b9e7d7746e6d3c2a450536067555e5695705603131b50000000017a914922b975237d61604e9ea797f81ff7ef3321647a287024730440220546d182d00e45ef659c329dce6197dc19e0abc795e2c9279873f5a887998b273022044143113fc3475d04fc8d5113e0bbcb42d80514a9f1a2247e9b2a7878e20d449012102bb3ce35af26f4c826eab3e5fc263ef56871b26686a8a995599b7ee65766131040400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00000000")
if err != nil {
t.Fatalf("Failed to decode hex: %v", err)
}
if !bytes.Equal(expectedSerializedTx, networkSerializedTx) {
t.Fatalf("Failed to create correct network serialized transaction: "+
"%x\n", networkSerializedTx)
if !bytes.Equal(expectedSerializedTx, tx.Bytes()) {
t.Fatalf("Failed to create correct network serialized "+
"transaction: expected %x, got %x",
expectedSerializedTx, tx.Bytes())
}
}
@ -999,7 +1063,7 @@ func TestMaybeFinalizeAll(t *testing.T) {
// The following data is from a 3rd transaction from Core,
// using 3 inputs, all p2wkh.
imported := "cHNidP8BAKQCAAAAAzJyXH13IqBFvvZ7y1VSgUgkMvMoPgP5CfFNqsjQexKQAQAAAAD/////fMdLydu5bsoiHN9cFSaBL0Qnq2KLSKx0RA4b938CAgQAAAAAAP/////yKNgfsDAHr/zFz8R9k8EFI26allfg9DdE8Gzj6tGlegEAAAAA/////wHw9E0OAAAAABYAFDnPCRduiEWmmSc1j30SJ8k9u7PHAAAAAAAAAAAA"
psbt1, err := NewPsbt([]byte(imported), true)
psbt1, err := NewFromRawBytes(bytes.NewReader([]byte(imported)), true)
if err != nil {
t.Fatalf("Failed to parse PSBT: %v", err)
}
@ -1091,7 +1155,7 @@ func TestFromUnsigned(t *testing.T) {
if err != nil {
t.Fatalf("Error: %v", err)
}
psbt1, err := NewPsbtFromUnsignedTx(tx)
psbt1, err := NewFromUnsignedTx(tx)
if err != nil {
t.Fatalf("Error: %v", err)
}
@ -1105,7 +1169,7 @@ func TestFromUnsigned(t *testing.T) {
if encoded != fromCoreB64 {
t.Fatalf("Got incorrect b64: %v", encoded)
}
_, err = NewPsbt([]byte(fromCoreB64), true)
_, err = NewFromRawBytes(bytes.NewReader([]byte(fromCoreB64)), true)
if err != nil {
t.Fatalf("Error: %v", err)
}
@ -1144,7 +1208,7 @@ func TestNonWitnessToWitness(t *testing.T) {
}
// import the PSBT
psbt1, err := NewPsbt([]byte(psbt1B64), true)
psbt1, err := NewFromRawBytes(bytes.NewReader([]byte(psbt1B64)), true)
if err != nil {
t.Fatalf("Failed to create PSBT: %v", err)
}
@ -1212,11 +1276,15 @@ func TestNonWitnessToWitness(t *testing.T) {
}
expectedNetworkSer, _ := hex.DecodeString("020000000001047b4131763e497f79c627665893faec24d0d3614f5296cd848c4da6501d96f93e0100000017160014b3773ea5d2c881d62aa9b86a1e66f5eadcff73a8ffffffff1b69ade1b5fbfac562d6375bb816b943fc6c25d83314281d1c7499ab77b02ba600000000171600142412be29368c0260cb841eecd9b59d7e01174aa1ffffffffcdadb65bec6cdf020e243aa1560086d769bab8700bed546bfe79e70822fd4a820100000000ffffffffbdecf627e9ae012b95a38d1f5ae0a4db3797afadfa6cf65c64c7b1355609e9bf010000006a4730440220290abcaacbd759c4f989762a9ee3468a9231788aab8f50bf65955d8597d8dd3602204d7e394f4419dc5392c6edba6945837458dd750a030ac67a746231903a8eb7db01210388025f50bb51c0469421ed13381f22f9d46a070ec2837e055c49c5876f0d0968ffffffff01f02672530000000017a914430b040b99f36dd63999e38dd99436b6199e1603870247304402201fb93318eda2b247c2bd7d1d8240eecdfa89eed02eeca51939eca44e4275498902201bf841164ad612f3e390f482cf8bcfddce138b37ee590f544f9295766d6b3584012102de369fd7d30c7deac19f4067e65c572463aaa2a99f4af9c772542f079809de710247304402205676877e6162ce40a49ee5a74443cdc1e7915637c42da7b872c2ec2298fd371b02203c1d4a05b1e2a7a588d9ec9b8d4892d2cd59bebe0e777483477a0ec692ebbe6d012102534f23cb88a048b649672967263bd7570312d5d31d066fa7b303970010a77b2b02473044022065d0a349709b8d8043cfd644cf6c196c1f601a22e1b3fdfbf8c0cc2a80fe2f1702207c87d36b666a8862e81ec5df288707f517d2f35ea1548feb82019de2c8de90f701210257d88eaf1e79b72ea0a33ae89b57dae95ea68499bdc6770257e010ab899f0abb0000000000")
serializedtx, err := Extract(psbt1)
tx, err := Extract(psbt1)
if err != nil {
t.Fatalf("Failed to extract: %v", err)
}
if !bytes.Equal(expectedNetworkSer, serializedtx) {
t.Fatalf("Expected serialized transaction was not produced: %x", serializedtx)
var b bytes.Buffer
if err := tx.Serialize(&b); err != nil {
t.Fatalf("unable to encode tx: %v", err)
}
if !bytes.Equal(expectedNetworkSer, b.Bytes()) {
t.Fatalf("Expected serialized transaction was not produced: %x", b.Bytes())
}
}

View file

@ -4,129 +4,149 @@
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.
// 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"
)
// SignOutcome is a enum-like value that expresses the outcome of a call to the
// Sign method.
type SignOutcome int
const (
// SignSuccesful indicates that the partial signature was successfully
// attached.
SignSuccesful = 0
// SignFinalized indicates that this input is already finalized, so the provided
// signature was *not* attached
SignFinalized = 1
// SignInvalid indicates that the provided signature data was not valid. In this case
// an error will also be returned.
SignInvalid = -1
)
// 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).
// 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.
// 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) {
redeemScript []byte, witnessScript []byte) (SignOutcome, error) {
if isFinalized(u.Upsbt, inIndex) {
return 1, nil
return SignFinalized, nil
}
// Add the witnessScript to the PSBT in preparation. If it already
// exists, it will be overwritten.
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
return SignInvalid, err
}
}
// Add the redeemScript to the PSBT in preparation. If it already
// exists, it will be overwritten.
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
return SignInvalid, 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;
// 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 {
switch {
case u.Upsbt.Inputs[inIndex].WitnessScript != nil:
if u.Upsbt.Inputs[inIndex].WitnessUtxo == nil {
err := nonWitnessToWitness(u.Upsbt, inIndex)
if err != nil {
return -1, err
return SignInvalid, err
}
}
err := u.addPartialSignature(inIndex, sig, pubKey)
if err != nil {
return -1, err
return SignInvalid, 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
// Case 2: no witness script, only redeem script; can be legacy p2sh or
// p2sh-wrapped p2wkh.
case u.Upsbt.Inputs[inIndex].RedeemScript != nil:
// 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:
// 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
return SignInvalid, err
}
}
}
// If it is not a valid witness program, we here assume
// that the provided WitnessUtxo/NonWitnessUtxo field was correct.
// 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
return SignInvalid, 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.
// 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.
default:
if u.Upsbt.Inputs[inIndex].WitnessUtxo == nil {
outIndex := u.Upsbt.UnsignedTx.TxIn[inIndex].
PreviousOutPoint.Index
script := u.Upsbt.Inputs[inIndex].NonWitnessUtxo.
TxOut[outIndex].PkScript
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
return SignInvalid, 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 {
err := u.addPartialSignature(inIndex, sig, pubKey)
if err != nil {
return SignInvalid, err
}
}
return SignSuccesful, 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 *Packet, 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}
u := Updater{
Upsbt: p,
}
return u.AddInWitnessUtxo(txout, inIndex)
}

149
psbt/types.go Normal file
View file

@ -0,0 +1,149 @@
package psbt
// GlobalType is the set of types that are used at the global scope level
// within the PSBT.
type GlobalType uint8
const (
// UnsignedTxType is the global scope key that houses the unsigned
// transaction of the PSBT. The value is a transaction in network
// serialization. The scriptSigs and witnesses for each input must be
// empty. The transaction must be in the old serialization format
// (without witnesses). A PSBT must have a transaction, otherwise it is
// invalid.
UnsignedTxType GlobalType = 0
// XpubType houses a global xpub for the entire PSBT packet.
//
// The key ({0x01}|{xpub}) is he 78 byte serialized extended public key
// as defined by BIP 32. Extended public keys are those that can be
// used to derive public keys used in the inputs and outputs of this
// transaction. It should be the public key at the highest hardened
// derivation index so that
// the unhardened child keys used in the transaction can be derived.
//
// The value is the master key fingerprint as defined by BIP 32
// concatenated with the derivation path of the public key. The
// derivation path is represented as 32-bit little endian unsigned
// integer indexes concatenated with each other. The number of 32 bit
// unsigned integer indexes must match the depth provided in the
// extended public key.
XpubType GlobalType = 1
// VersionType houses the global version number of this PSBT. There is
// no key (only contains the byte type), then the value if omitted, is
// assumed to be zero.
VersionType GlobalType = 0xFB
// ProprietaryGlobalType is used to house any proper chary global-scope
// keys within the PSBT.
//
// The key is ({0xFC}|<prefix>|{subtype}|{key data}) a variable length
// identifier prefix, followed by a subtype, followed by the key data
// itself.
//
// The value is any data as defined by the proprietary type user.
ProprietaryGlobalType = 0xFC
)
// InputType is the set of types that are defined for each input included
// within the PSBT.
type InputType uint32
const (
// NonWitnessUtxoType has no key ({0x00}) and houses the transaction in
// network serialization format the current input spends from. This
// should only be present for inputs which spend non-segwit outputs.
// However, if it is unknown whether an input spends a segwit output,
// this type should be used. The entire input transaction is needed in
// order to be able to verify the values of the input (pre-segwit they
// aren't in the signature digest).
NonWitnessUtxoType InputType = 0
// WitnessUtxoType has no key ({0x01}), and houses the entire
// transaction output in network serialization which the current input
// spends from. This should only be present for inputs which spend
// segwit outputs, including P2SH embedded ones (value || script).
WitnessUtxoType InputType = 1
// PartialSigType is used to include a partial signature with key
// ({0x02}|{public key}).
//
// The value is the signature as would be pushed to the stack from a
// scriptSig or witness..
PartialSigType InputType = 2
// SighashType is an empty key ({0x03}).
//
// The value contains the 32-bit unsigned integer specifying the
// sighash type to be used for this input. Signatures for this input
// must use the sighash type, finalizers must fail to finalize inputs
// which have signatures that do not match the specified sighash type.
// Signers who cannot produce signatures with the sighash type must not
// provide a signature.
SighashType InputType = 3
// RedeemScriptInputType is an empty key ({0x40}).
//
// The value is the redeem script of the input if present.
RedeemScriptInputType InputType = 4
// WitnessScriptInputType is an empty key ({0x05}).
//
// The value is the witness script of this input, if it has one.
WitnessScriptInputType InputType = 5
// Bip32DerivationInputType is a type that carries the pubkey along
// with the key ({0x06}|{public key}).
//
// The value is master key fingerprint as defined by BIP 32
// concatenated with the derivation path of the public key. The
// derivation path is represented as 32 bit unsigned integer indexes
// concatenated with each other. Public keys are those that will be
// needed to sign this input.
Bip32DerivationInputType InputType = 6
// FinalScriptSigType is an empty key ({0x07}).
//
// The value contains a fully constructed scriptSig with signatures and
// any other scripts necessary for the input to pass validation.
FinalScriptSigType InputType = 7
// FinalScriptWitnessType is an empty key ({0x08}). The value is a
// fully constructed scriptWitness with signatures and any other
// scripts necessary for the input to pass validation.
FinalScriptWitnessType InputType = 8
// ProprietaryInputType is a custom type for use by devs.
//
// The key ({0xFC}|<prefix>|{subtype}|{key data}), is a Variable length
// identifier prefix, followed by a subtype, followed by the key data
// itself.
//
// The value is any value data as defined by the proprietary type user.
ProprietaryInputType InputType = 0xFC
)
// OutputType is the set of types defined per output within the PSBT.
type OutputType uint32
const (
// RedeemScriptOutputType is an empty key ({0x00}>
//
// The value is the redeemScript for this output if it has one.
RedeemScriptOutputType OutputType = 0
// WitnessScriptOutputType is an empty key ({0x01}).
//
// The value is the witness script of this input, if it has one.
WitnessScriptOutputType OutputType = 1
j // Bip32DerivationOutputType is used to communicate derivation information
// needed to spend this output. The key is ({0x02}|{public key}).
//
// The value is master key fingerprint concatenated with the derivation
// path of the public key. The derivation path is represented as 32-bit
// little endian unsigned integer indexes concatenated with each other.
// Public keys are those needed to spend this output.
Bip32DerivationOutputType OutputType = 2
)

View file

@ -4,12 +4,11 @@
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
// 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"
@ -20,217 +19,259 @@ import (
"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.
// 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
Upsbt *Packet
}
// NewUpdater returns a new instance of Updater,
// if the passed Psbt struct is in a valid form,
// else an error.
func NewUpdater(p *Psbt) (*Updater, error) {
// NewUpdater returns a new instance of Updater, if the passed Psbt struct is
// in a valid form, else an error.
func NewUpdater(p *Packet) (*Updater, 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.
// 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.
// 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.
// 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):
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
// First check; don't add duplicates.
for _, x := range pInput.PartialSigs {
if bytes.Equal(x.PubKey, partialSig.PubKey) {
return ErrDuplicateKey
}
}
// Sanity checks
// Next, we perform a series of additional 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
}
// To validate that the redeem script matches, we must pull out
// the scriptPubKey of the corresponding output and compare
// that with the P2SH scriptPubKey that is generated by
// redeemScript.
if pInput.RedeemScript != nil {
// 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()
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()
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.
// `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()
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
} else {
// Otherwise, this is a p2wkh input.
pubkeyHash := btcutil.Hash160(pubkey)
pubkeyHashScript, err := txscript.NewScriptBuilder().AddOp(
txscript.OP_0).AddData(pubkeyHash).Script()
pubkeyHashScript, err := txscript.NewScriptBuilder().
AddOp(txscript.OP_0).
AddData(pubkeyHash).
Script()
if err != nil {
return err
}
// Validate that we're able to properly reconstruct the
// witness program.
if !bytes.Equal(pubkeyHashScript, script) {
return ErrInvalidSignatureForInput
}
}
} else {
// attaching signature without utxo field is not allowed
// Attaching signature without utxo field is not allowed.
return ErrInvalidPsbtFormat
}
p.Upsbt.Inputs[inIndex].PartialSigs = append(
p.Upsbt.Inputs[inIndex].PartialSigs, &partialSig)
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.
// 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.
// 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.
// 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.
// 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.
// AddInBip32Derivation takes a master key fingerprint as defined in BIP32, a
// BIP32 path as a slice of uint32 values, and a serialized pubkey as a byte
// slice, along with the integer index of the input, and inserts this data into
// that input.
//
// NOTE: This can be called multiple times for the same input. An error is
// returned if addition of this key-value pair to the Psbt fails.
func (p *Updater) AddInBip32Derivation(masterKeyFingerprint uint32,
bip32Path []uint32, pubKeyData []byte, inIndex int) error {
bip32Derivation := Bip32Derivation{
PubKey: pubKeyData,
MasterKeyFingerprint: masterKeyFingerprint,
@ -249,27 +290,32 @@ func (p *Updater) AddInBip32Derivation(masterKeyFingerprint uint32,
}
p.Upsbt.Inputs[inIndex].Bip32Derivation = append(
p.Upsbt.Inputs[inIndex].Bip32Derivation, &bip32Derivation)
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.
// 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
}
@ -282,31 +328,40 @@ func (p *Updater) AddOutBip32Derivation(masterKeyFingerprint uint32,
}
p.Upsbt.Outputs[outIndex].Bip32Derivation = append(
p.Upsbt.Outputs[outIndex].Bip32Derivation, &bip32Derivation)
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.
// 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.
// 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
}

272
psbt/utils.go Normal file
View file

@ -0,0 +1,272 @@
// 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
import (
"bytes"
"encoding/binary"
"errors"
"io"
"sort"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
)
// writeTxWitness is a 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 {
if err := wire.WriteVarInt(w, 0, uint64(len(wit))); 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
witnessItems = [][]byte{sig, pub}
)
if err := writeTxWitness(&buf, witnessItems); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// checkIsMultisigScript is a utility function to check whether 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 the number of sigs provided, doesn't match the number of required
// pubkeys, then we can't proceed as we're not yet final.
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. This
// function is used to ensure 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 this isn't a proper finalized multi-sig script, then we can't
// proceed.
if !checkIsMultiSigScript(expectedPubkeys, sigs, script) {
return nil, ErrUnsupportedScriptType
}
// Arrange the pubkeys and sigs into a slice of format:
// * [[pub,sig], [pub,sig],..]
type sigWithPub struct {
pubKey []byte
sig []byte
}
var pubsSigs []sigWithPub
for i, pub := range expectedPubkeys {
pubsSigs = append(pubsSigs, sigWithPub{
pubKey: pub,
sig: sigs[i],
})
}
// Now that we have the set of (pubkey, sig) pairs, we'll construct a
// position map that we can use to swap the order in the slice above to
// match how things are laid out in the script.
type positionEntry struct {
index int
value sigWithPub
}
var positionMap []positionEntry
// For each pubkey in our pubsSigs slice, we'll now construct a proper
// positionMap entry, based on _where_ in the script the pubkey first
// appears.
for _, p := range pubsSigs {
pos := bytes.Index(script, p.pubKey)
if pos < 0 {
return nil, errors.New("script does not contain pubkeys")
}
positionMap = append(positionMap, positionEntry{
index: pos,
value: p,
})
}
// Now that we have the position map full populated, we'll use the
// index data to properly sort the entries in the map based on where
// they appear in the script.
sort.Slice(positionMap, func(i, j int) bool {
return positionMap[i].index < positionMap[j].index
})
// Finally, we can simply iterate through the position map in order to
// extract the proper signature ordering.
sortedSigs := make([][]byte, 0, len(positionMap))
for _, x := range positionMap {
sortedSigs = append(sortedSigs, x.value.sig)
}
return sortedSigs, nil
}
// getMultisigScriptWitness creates a full psbt serialized Witness field for
// the transaction, given the public keys and signatures to be appended. This
// function will only accept witnessScripts of the 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) {
// First using the script as a guide, we'll properly order the sigs
// according to how their corresponding pubkeys appear in the
// witnessScript.
orderedSigs, err := extractKeyOrderFromScript(
witnessScript, pubKeys, sigs,
)
if err != nil {
return nil, err
}
// Now that we know the proper order, we'll append each of the
// signatures into a new witness stack, then top it off with the
// witness script at the end, prepending the nil as we need the extra
// pop..
witnessElements := make(wire.TxWitness, 0, len(sigs)+2)
witnessElements = append(witnessElements, nil)
for _, os := range orderedSigs {
witnessElements = append(witnessElements, os)
}
witnessElements = append(witnessElements, witnessScript)
// Now that we have the full witness stack, we'll serialize it in the
// expected format, and return the final bytes.
var buf bytes.Buffer
if err = writeTxWitness(&buf, witnessElements); 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(waxwing): 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])
}
// serializeKVpair writes out a kv pair using a varbyte prefix for each.
func serializeKVpair(w io.Writer, key []byte, value []byte) error {
if err := wire.WriteVarBytes(w, 0, key); err != nil {
return err
}
return wire.WriteVarBytes(w, 0, value)
}
// serializeKVPairWithType writes out to the passed writer a type coupled with
// a key.
func serializeKVPairWithType(w io.Writer, kt uint8, keydata []byte,
value []byte) error {
// If the key has no data, then we write a blank slice.
if keydata == nil {
keydata = []byte{}
}
// The final key to be written is: {type} || {keyData}
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. This integer is 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 {
// A separator indicates end of key-value pair list.
return -1, nil, nil
}
// Next, we ready out the designated number of bytes, which may include
// a type, key, and optional data.
keyTypeAndData := make([]byte, count)
if _, err := io.ReadFull(r, keyTypeAndData[:]); err != nil {
return -1, nil, err
}
keyType := int(string(keyTypeAndData)[0])
// Note that the second return value will usually be empty, since most
// keys contain no more than the key type byte.
if len(keyTypeAndData) == 1 {
return keyType, nil, nil
}
// Otherwise, we return the key, along with any data that it may
// contain.
return keyType, keyTypeAndData[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
}