zpay32/invoice: parse payment address as type s

This commit is contained in:
Conner Fromknecht 2019-12-05 07:59:17 -08:00
parent 2bf94fa409
commit df72097f2d
No known key found for this signature in database
GPG Key ID: E7D737B67FA592C7
2 changed files with 65 additions and 3 deletions

@ -73,6 +73,11 @@ const (
// supported or required by the receiver. // supported or required by the receiver.
fieldType9 = 5 fieldType9 = 5
// fieldTypeS contains a 32-byte payment address, which is a nonce
// included in the final hop's payload to prevent intermediaries from
// probing the recipient.
fieldTypeS = 16
// maxInvoiceLength is the maximum total length an invoice can have. // maxInvoiceLength is the maximum total length an invoice can have.
// This is chosen to be the maximum number of bytes that can fit into a // This is chosen to be the maximum number of bytes that can fit into a
// single QR code: https://en.wikipedia.org/wiki/QR_code#Storage // single QR code: https://en.wikipedia.org/wiki/QR_code#Storage
@ -126,6 +131,10 @@ type Invoice struct {
// invoice. // invoice.
PaymentHash *[32]byte PaymentHash *[32]byte
// PaymentAddr is the payment address to be used by payments to prevent
// probing of the destination.
PaymentAddr *[32]byte
// Destination is the public key of the target node. This will always // Destination is the public key of the target node. This will always
// be set after decoding, and can optionally be set before encoding to // be set after decoding, and can optionally be set before encoding to
// include the pubkey as an 'n' field. If this is not set before // include the pubkey as an 'n' field. If this is not set before
@ -258,6 +267,14 @@ func Features(features *lnwire.FeatureVector) func(*Invoice) {
} }
} }
// PaymentAddr is a functional option that allows callers of NewInvoice to set
// the desired payment address tht is advertised on the invoice.
func PaymentAddr(addr [32]byte) func(*Invoice) {
return func(i *Invoice) {
i.PaymentAddr = &addr
}
}
// NewInvoice creates a new Invoice object. The last parameter is a set of // NewInvoice creates a new Invoice object. The last parameter is a set of
// variadic arguments for setting optional fields of the invoice. // variadic arguments for setting optional fields of the invoice.
// //
@ -643,6 +660,14 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
} }
invoice.PaymentHash, err = parse32Bytes(base32Data) invoice.PaymentHash, err = parse32Bytes(base32Data)
case fieldTypeS:
if invoice.PaymentAddr != nil {
// We skip the field if we have already seen a
// supported one.
continue
}
invoice.PaymentAddr, err = parse32Bytes(base32Data)
case fieldTypeD: case fieldTypeD:
if invoice.Description != nil { if invoice.Description != nil {
// We skip the field if we have already seen a // We skip the field if we have already seen a
@ -1060,6 +1085,14 @@ func writeTaggedFields(bufferBase32 *bytes.Buffer, invoice *Invoice) error {
return err return err
} }
} }
if invoice.PaymentAddr != nil {
err := writeBytes32(
bufferBase32, fieldTypeS, *invoice.PaymentAddr,
)
if err != nil {
return err
}
}
return nil return nil
} }

@ -28,7 +28,19 @@ var (
testMillisat25mBTC = lnwire.MilliSatoshi(2500000000) testMillisat25mBTC = lnwire.MilliSatoshi(2500000000)
testMillisat20mBTC = lnwire.MilliSatoshi(2000000000) testMillisat20mBTC = lnwire.MilliSatoshi(2000000000)
testPaymentHashSlice, _ = hex.DecodeString("0001020304050607080900010203040506070809000102030405060708090102") testPaymentHash = [32]byte{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09, 0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x01, 0x02,
}
testPaymentAddr = [32]byte{
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x01, 0x02,
0x06, 0x07, 0x08, 0x09, 0x00, 0x01, 0x02, 0x03,
0x08, 0x09, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
}
testEmptyString = "" testEmptyString = ""
testCupOfCoffee = "1 cup coffee" testCupOfCoffee = "1 cup coffee"
@ -94,7 +106,6 @@ var (
} }
// Must be initialized in init(). // Must be initialized in init().
testPaymentHash [32]byte
testDescriptionHash [32]byte testDescriptionHash [32]byte
ltcTestNetParams chaincfg.Params ltcTestNetParams chaincfg.Params
@ -102,7 +113,6 @@ var (
) )
func init() { func init() {
copy(testPaymentHash[:], testPaymentHashSlice[:])
copy(testDescriptionHash[:], testDescriptionHashSlice[:]) copy(testDescriptionHash[:], testDescriptionHashSlice[:])
// Initialize litecoin testnet and mainnet params by applying key fields // Initialize litecoin testnet and mainnet params by applying key fields
@ -587,6 +597,25 @@ func TestDecodeEncode(t *testing.T) {
return i return i
}, },
}, },
{
// Send 2500uBTC for a cup of coffee with a payment
// address.
encodedInvoice: "lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66sp5qszsvpcgpyqsyps8pqysqqgzqvyqjqqpqgpsgpgqqypqxpq9qcrsusq8nx2hdt3st3ankwz23xy9w7udvqq3f0mdlpc6ga5ew3y67u4qkx8vu72ejg5x6tqhyclm28r7r0mg6lx9x3vls9g6glp2qy3y34cpry54xp",
valid: true,
decodedInvoice: func() *Invoice {
i, _ := NewInvoice(
&chaincfg.MainNetParams,
testPaymentHash,
time.Unix(1496314658, 0),
Amount(testMillisat2500uBTC),
Description(testCupOfCoffee),
Destination(testPubKey),
PaymentAddr(testPaymentAddr),
)
return i
},
},
{ {
// Decode a mainnet invoice while expecting active net to be testnet // Decode a mainnet invoice while expecting active net to be testnet
encodedInvoice: "lnbc241pveeq09pp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdqqnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66jd3m5klcwhq68vdsmx2rjgxeay5v0tkt2v5sjaky4eqahe4fx3k9sqavvce3capfuwv8rvjng57jrtfajn5dkpqv8yelsewtljwmmycq62k443", encodedInvoice: "lnbc241pveeq09pp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdqqnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66jd3m5klcwhq68vdsmx2rjgxeay5v0tkt2v5sjaky4eqahe4fx3k9sqavvce3capfuwv8rvjng57jrtfajn5dkpqv8yelsewtljwmmycq62k443",