Skip to content

Commit

Permalink
zpay32: add payment metadata field
Browse files Browse the repository at this point in the history
  • Loading branch information
joostjager committed Apr 13, 2022
1 parent 044286a commit 135e27d
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 5 deletions.
15 changes: 15 additions & 0 deletions zpay32/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,15 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
}

invoice.Description, err = parseDescription(base32Data)
case fieldTypeM:
if invoice.Metadata != nil {
// We skip the field if we have already seen a
// supported one.
continue
}

invoice.Metadata, err = parseMetadata(base32Data)

case fieldTypeN:
if invoice.Destination != nil {
// We skip the field if we have already seen a
Expand Down Expand Up @@ -345,6 +354,12 @@ func parseDescription(data []byte) (*string, error) {
return &description, nil
}

// parseMetadata converts the data (encoded in base32) into a byte slice to use
// as the metadata.
func parseMetadata(data []byte) ([]byte, error) {
return bech32.ConvertBits(data, 5, 8, false)
}

// parseDestination converts the data (encoded in base32) into a 33-byte public
// key of the payee node.
func parseDestination(data []byte) (*btcec.PublicKey, error) {
Expand Down
11 changes: 11 additions & 0 deletions zpay32/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,17 @@ func writeTaggedFields(bufferBase32 *bytes.Buffer, invoice *Invoice) error {
}
}

if invoice.Metadata != nil {
base32, err := bech32.ConvertBits(invoice.Metadata, 8, 5, true)
if err != nil {
return err
}
err = writeTaggedField(bufferBase32, fieldTypeM, base32)
if err != nil {
return err
}
}

if invoice.minFinalCLTVExpiry != nil {
finalDelta := uint64ToBase32(*invoice.minFinalCLTVExpiry)
err := writeTaggedField(bufferBase32, fieldTypeC, finalDelta)
Expand Down
15 changes: 15 additions & 0 deletions zpay32/invoice.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ const (
// fieldTypeD contains a short description of the payment.
fieldTypeD = 13

// fieldTypeM contains the payment metadata.
fieldTypeM = 27

// fieldTypeN contains the pubkey of the target node.
fieldTypeN = 19

Expand Down Expand Up @@ -183,6 +186,10 @@ type Invoice struct {
// Features represents an optional field used to signal optional or
// required support for features by the receiver.
Features *lnwire.FeatureVector

// Metadata is additional data that is sent along with the payment to
// the payee.
Metadata []byte
}

// Amount is a functional option that allows callers of NewInvoice to set the
Expand Down Expand Up @@ -273,6 +280,14 @@ func PaymentAddr(addr [32]byte) func(*Invoice) {
}
}

// Metadata is a functional option that allows callers of NewInvoice to set
// the desired payment Metadata tht is advertised on the invoice.
func Metadata(metadata []byte) func(*Invoice) {
return func(i *Invoice) {
i.Metadata = metadata
}
}

// NewInvoice creates a new Invoice object. The last parameter is a set of
// variadic arguments for setting optional fields of the invoice.
//
Expand Down
39 changes: 34 additions & 5 deletions zpay32/invoice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ var (
testMillisat2500uBTC = lnwire.MilliSatoshi(250000000)
testMillisat25mBTC = lnwire.MilliSatoshi(2500000000)
testMillisat20mBTC = lnwire.MilliSatoshi(2000000000)
testMillisat10mBTC = lnwire.MilliSatoshi(1000000000)

testPaymentHash = [32]byte{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
Expand All @@ -49,11 +50,12 @@ var (
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
}

testEmptyString = ""
testCupOfCoffee = "1 cup coffee"
testCoffeeBeans = "coffee beans"
testCupOfNonsense = "ナンセンス 1杯"
testPleaseConsider = "Please consider supporting this project"
testEmptyString = ""
testCupOfCoffee = "1 cup coffee"
testCoffeeBeans = "coffee beans"
testCupOfNonsense = "ナンセンス 1杯"
testPleaseConsider = "Please consider supporting this project"
testPaymentMetadata = "payment metadata inside"

testPrivKeyBytes, _ = hex.DecodeString("e126f68f7eafcc8b74f54d269fe206be715000f94dac067d1c04a8ca3b2db734")
testPrivKey, testPubKey = btcec.PrivKeyFromBytes(testPrivKeyBytes)
Expand Down Expand Up @@ -692,6 +694,33 @@ func TestDecodeEncode(t *testing.T) {
}
},
},
{
// Please send 0.01 BTC with payment metadata 0x01fafaf0.
encodedInvoice: "lnbc10m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp9wpshjmt9de6zqmt9w3skgct5vysxjmnnd9jx2mq8q8a04uqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q2gqqqqqqsgq7hf8he7ecf7n4ffphs6awl9t6676rrclv9ckg3d3ncn7fct63p6s365duk5wrk202cfy3aj5xnnp5gs3vrdvruverwwq7yzhkf5a3xqpd05wjc",
valid: true,
decodedInvoice: func() *Invoice {
return &Invoice{
Net: &chaincfg.MainNetParams,
MilliSat: &testMillisat10mBTC,
Timestamp: time.Unix(1496314658, 0),
PaymentHash: &testPaymentHash,
Description: &testPaymentMetadata,
Destination: testPubKey,
PaymentAddr: &specPaymentAddr,
Features: lnwire.NewFeatureVector(
lnwire.NewRawFeatureVector(8, 14, 48),
lnwire.Features,
),
Metadata: []byte{0x01, 0xfa, 0xfa, 0xf0},
}
},
beforeEncoding: func(i *Invoice) {
// Since this destination pubkey was recovered
// from the signature, we must set it nil before
// encoding to get back the same invoice string.
i.Destination = nil
},
},
}

for i, test := range tests {
Expand Down

0 comments on commit 135e27d

Please sign in to comment.