From edef0169f98a8a2b5e087afec754364149ac9ce0 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Sat, 28 Dec 2024 17:25:19 +0100 Subject: [PATCH] psbt: add output silent payment info --- btcutil/psbt/partial_output.go | 23 +++++++++++++++++++++++ btcutil/psbt/silentpayments.go | 32 ++++++++++++++++++++++++++++++++ btcutil/psbt/types.go | 4 ++++ 3 files changed, 59 insertions(+) diff --git a/btcutil/psbt/partial_output.go b/btcutil/psbt/partial_output.go index 86e476457d..9698fcf79f 100644 --- a/btcutil/psbt/partial_output.go +++ b/btcutil/psbt/partial_output.go @@ -17,6 +17,7 @@ type POutput struct { TaprootInternalKey []byte TaprootTapTree []byte TaprootBip32Derivation []*TaprootBip32Derivation + SilentPaymentInfo *SilentPaymentInfo Unknowns []*Unknown } @@ -144,6 +145,18 @@ func (po *POutput) deserialize(r io.Reader) error { po.TaprootBip32Derivation, taprootDerivation, ) + case SilentPaymentV0InfoOutputType: + if po.SilentPaymentInfo != nil { + return ErrDuplicateKey + } + + info, err := ReadSilentPaymentInfo(value) + if err != nil { + return err + } + + po.SilentPaymentInfo = info + default: // A fall through case for any proprietary types. keyCodeAndData := append( @@ -246,6 +259,16 @@ func (po *POutput) serialize(w io.Writer) error { } } + if po.SilentPaymentInfo != nil { + err := serializeKVPairWithType( + w, uint8(SilentPaymentV0InfoOutputType), nil, + SerializeSilentPaymentInfo(po.SilentPaymentInfo), + ) + 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 po.Unknowns { diff --git a/btcutil/psbt/silentpayments.go b/btcutil/psbt/silentpayments.go index d62d85360e..947f01b24f 100644 --- a/btcutil/psbt/silentpayments.go +++ b/btcutil/psbt/silentpayments.go @@ -200,3 +200,35 @@ func SerializeSilentPaymentDLEQ(dleq *SilentPaymentDLEQ) ([]byte, []byte) { return keyData, dleq.Proof } + +// SilentPaymentInfo is the information needed to create a silent payment +// recipient output. +type SilentPaymentInfo struct { + // ScanKey is the silent payment recipient's scan key. + ScanKey []byte + + // SpendKey is the silent payment recipient's spend key. + SpendKey []byte +} + +// ReadSilentPaymentInfo deserializes a silent payment info from the given +// value. +func ReadSilentPaymentInfo(value []byte) (*SilentPaymentInfo, error) { + if len(value) != secp.PubKeyBytesLenCompressed*2 { + return nil, ErrInvalidPsbtFormat + } + + return &SilentPaymentInfo{ + ScanKey: value[:secp.PubKeyBytesLenCompressed], + SpendKey: value[secp.PubKeyBytesLenCompressed:], + }, nil +} + +// SerializeSilentPaymentInfo serializes a silent payment info to value. +func SerializeSilentPaymentInfo(info *SilentPaymentInfo) []byte { + value := make([]byte, 0, secp.PubKeyBytesLenCompressed*2) + value = append(value, info.ScanKey...) + value = append(value, info.SpendKey...) + + return value +} diff --git a/btcutil/psbt/types.go b/btcutil/psbt/types.go index 817eb67835..a2c809e5b6 100644 --- a/btcutil/psbt/types.go +++ b/btcutil/psbt/types.go @@ -208,4 +208,8 @@ const ( // followed by said number of 32-byte leaf hashes. The rest of the value // is then identical to the Bip32DerivationInputType value. TaprootBip32DerivationOutputType OutputType = 7 + + // SilentPaymentV0InfoOutputType is used to house the silent payment + // recipient information for a version 0 silent payment output. + SilentPaymentV0InfoOutputType OutputType = 0x09 )