Merge pull request #155 from Roasbeef/psbt-refactor
psbt: refactor new PSBT library to match code style of project
This commit is contained in:
commit
02a4fd9de1
17 changed files with 2004 additions and 1307 deletions
11
go.mod
Normal file
11
go.mod
Normal 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
54
go.sum
Normal 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
77
psbt/bip32.go
Normal 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
|
||||
}
|
|
@ -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
|
||||
|
||||
// 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.
|
||||
// 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) {
|
||||
|
||||
// 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
|
||||
// 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
|
||||
}
|
||||
unsignedTx := wire.NewMsgTx(Version)
|
||||
unsignedTx.LockTime = nLockTime
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -6,480 +6,457 @@ 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 {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
// if it's p2wkh there should be no redeemScript or witnessScript
|
||||
if pInput.WitnessScript != nil || pInput.RedeemScript != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
} else if txscript.IsPayToScriptHash(pInput.WitnessUtxo.PkScript) {
|
||||
if pInput.RedeemScript == nil {
|
||||
return false
|
||||
}
|
||||
// if it's nested, and it's p2wsh, it must have WitnessScript;
|
||||
// if p2wkh, it must not.
|
||||
if txscript.IsPayToWitnessScriptHash(pInput.RedeemScript) {
|
||||
if pInput.WitnessScript == nil {
|
||||
return false
|
||||
}
|
||||
} else if txscript.IsPayToWitnessPubKeyHash(pInput.RedeemScript) {
|
||||
if pInput.WitnessScript != nil {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
// unrecognized type
|
||||
return false
|
||||
}
|
||||
}
|
||||
} else if pInput.NonWitnessUtxo != nil {
|
||||
if pInput.WitnessScript != nil {
|
||||
return false
|
||||
}
|
||||
outIndex := p.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Index
|
||||
if txscript.IsPayToScriptHash(pInput.NonWitnessUtxo.TxOut[outIndex].PkScript) {
|
||||
if pInput.RedeemScript == nil {
|
||||
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 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 {
|
||||
// one of witness and nonwitness utxo must be present
|
||||
|
||||
// For nested P2SH inputs, we verify that a witness script is known.
|
||||
case txscript.IsPayToScriptHash(pkScript):
|
||||
if pInput.RedeemScript == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
} else if txscript.IsPayToWitnessPubKeyHash(pInput.RedeemScript) {
|
||||
if pInput.WitnessScript != nil {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
// 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
|
||||
}
|
||||
|
||||
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) {
|
||||
// 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 {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
if pInput.RedeemScript != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
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 *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
9
psbt/go.mod
Normal 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
36
psbt/go.sum
Normal 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
361
psbt/partial_input.go
Normal 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
139
psbt/partial_output.go
Normal 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
58
psbt/partialsig.go
Normal 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)
|
||||
}
|
847
psbt/psbt.go
847
psbt/psbt.go
File diff suppressed because it is too large
Load diff
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
142
psbt/signer.go
142
psbt/signer.go
|
@ -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 SignInvalid, err
|
||||
}
|
||||
}
|
||||
return 0, nil
|
||||
|
||||
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 *Psbt, inIndex int) error {
|
||||
// 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
149
psbt/types.go
Normal 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
|
||||
)
|
235
psbt/updater.go
235
psbt/updater.go
|
@ -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
272
psbt/utils.go
Normal 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
|
||||
}
|
Loading…
Reference in a new issue