2020-01-16 02:36:48 +01:00
|
|
|
package psbt
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"io"
|
|
|
|
"sort"
|
|
|
|
|
2021-09-10 22:30:39 +02:00
|
|
|
"github.com/lbryio/lbcd/wire"
|
2020-01-16 02:36:48 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|