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