Merge pull request #767 from sangaman/master
zpay32: handle segwit prefixes > 2 chars
This commit is contained in:
commit
00ea46d9ae
10
rpcserver.go
10
rpcserver.go
@ -1693,7 +1693,8 @@ func (r *rpcServer) SendPayment(paymentStream lnrpc.Lightning_SendPaymentServer)
|
|||||||
// attempt to decode it, populating the
|
// attempt to decode it, populating the
|
||||||
// payment accordingly.
|
// payment accordingly.
|
||||||
if nextPayment.PaymentRequest != "" {
|
if nextPayment.PaymentRequest != "" {
|
||||||
payReq, err := zpay32.Decode(nextPayment.PaymentRequest)
|
payReq, err := zpay32.Decode(nextPayment.PaymentRequest,
|
||||||
|
activeNetParams.Params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
select {
|
select {
|
||||||
case errChan <- err:
|
case errChan <- err:
|
||||||
@ -1882,7 +1883,8 @@ func (r *rpcServer) SendPaymentSync(ctx context.Context,
|
|||||||
// If the proto request has an encoded payment request, then we we'll
|
// If the proto request has an encoded payment request, then we we'll
|
||||||
// use that solely to dispatch the payment.
|
// use that solely to dispatch the payment.
|
||||||
if nextPayment.PaymentRequest != "" {
|
if nextPayment.PaymentRequest != "" {
|
||||||
payReq, err := zpay32.Decode(nextPayment.PaymentRequest)
|
payReq, err := zpay32.Decode(nextPayment.PaymentRequest,
|
||||||
|
activeNetParams.Params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -2150,7 +2152,7 @@ func (r *rpcServer) AddInvoice(ctx context.Context,
|
|||||||
// createRPCInvoice creates an *lnrpc.Invoice from the *channeldb.Invoice.
|
// createRPCInvoice creates an *lnrpc.Invoice from the *channeldb.Invoice.
|
||||||
func createRPCInvoice(invoice *channeldb.Invoice) (*lnrpc.Invoice, error) {
|
func createRPCInvoice(invoice *channeldb.Invoice) (*lnrpc.Invoice, error) {
|
||||||
paymentRequest := string(invoice.PaymentRequest)
|
paymentRequest := string(invoice.PaymentRequest)
|
||||||
decoded, err := zpay32.Decode(paymentRequest)
|
decoded, err := zpay32.Decode(paymentRequest, activeNetParams.Params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to decode payment request: %v",
|
return nil, fmt.Errorf("unable to decode payment request: %v",
|
||||||
err)
|
err)
|
||||||
@ -2978,7 +2980,7 @@ func (r *rpcServer) DecodePayReq(ctx context.Context,
|
|||||||
// Fist we'll attempt to decode the payment request string, if the
|
// Fist we'll attempt to decode the payment request string, if the
|
||||||
// request is invalid or the checksum doesn't match, then we'll exit
|
// request is invalid or the checksum doesn't match, then we'll exit
|
||||||
// here with an error.
|
// here with an error.
|
||||||
payReq, err := zpay32.Decode(req.PayReq)
|
payReq, err := zpay32.Decode(req.PayReq, activeNetParams.Params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -265,9 +265,9 @@ func NewInvoice(net *chaincfg.Params, paymentHash [32]byte,
|
|||||||
return invoice, nil
|
return invoice, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode parses the provided encoded invoice, and returns a decoded Invoice in
|
// Decode parses the provided encoded invoice and returns a decoded Invoice if
|
||||||
// case it is valid by BOLT-0011.
|
// it is valid by BOLT-0011 and matches the provided active network.
|
||||||
func Decode(invoice string) (*Invoice, error) {
|
func Decode(invoice string, net *chaincfg.Params) (*Invoice, error) {
|
||||||
decodedInvoice := Invoice{}
|
decodedInvoice := Invoice{}
|
||||||
|
|
||||||
// Decode the invoice using the modified bech32 decoder.
|
// Decode the invoice using the modified bech32 decoder.
|
||||||
@ -276,9 +276,9 @@ func Decode(invoice string) (*Invoice, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// We expect the human-readable part to at least have ln + two chars
|
// We expect the human-readable part to at least have ln + one char
|
||||||
// encoding the network.
|
// encoding the network.
|
||||||
if len(hrp) < 4 {
|
if len(hrp) < 3 {
|
||||||
return nil, fmt.Errorf("hrp too short")
|
return nil, fmt.Errorf("hrp too short")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,24 +288,17 @@ func Decode(invoice string) (*Invoice, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The next characters should be a valid prefix for a segwit BIP173
|
// The next characters should be a valid prefix for a segwit BIP173
|
||||||
// address. This will also determine which network this invoice is
|
// address that match the active network.
|
||||||
// meant for.
|
if !strings.HasPrefix(hrp[2:], net.Bech32HRPSegwit) {
|
||||||
var net *chaincfg.Params
|
|
||||||
if strings.HasPrefix(hrp[2:], chaincfg.MainNetParams.Bech32HRPSegwit) {
|
|
||||||
net = &chaincfg.MainNetParams
|
|
||||||
} else if strings.HasPrefix(hrp[2:], chaincfg.TestNet3Params.Bech32HRPSegwit) {
|
|
||||||
net = &chaincfg.TestNet3Params
|
|
||||||
} else if strings.HasPrefix(hrp[2:], chaincfg.SimNetParams.Bech32HRPSegwit) {
|
|
||||||
net = &chaincfg.SimNetParams
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("unknown network")
|
return nil, fmt.Errorf("unknown network")
|
||||||
}
|
}
|
||||||
decodedInvoice.Net = net
|
decodedInvoice.Net = net
|
||||||
|
|
||||||
// Optionally, if there's anything left of the HRP, it encodes the
|
// Optionally, if there's anything left of the HRP after ln + the segwit
|
||||||
// payment amount.
|
// prefix, we try to decode this as the payment amount.
|
||||||
if len(hrp) > 4 {
|
var netPrefixLength = len(net.Bech32HRPSegwit) + 2
|
||||||
amount, err := decodeAmount(hrp[4:])
|
if len(hrp) > netPrefixLength {
|
||||||
|
amount, err := decodeAmount(hrp[netPrefixLength:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -573,11 +566,7 @@ func parseData(invoice *Invoice, data []byte, net *chaincfg.Params) error {
|
|||||||
|
|
||||||
// The rest are tagged parts.
|
// The rest are tagged parts.
|
||||||
tagData := data[7:]
|
tagData := data[7:]
|
||||||
if err := parseTaggedFields(invoice, tagData, net); err != nil {
|
return parseTaggedFields(invoice, tagData, net)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseTimestamp converts a 35-bit timestamp (encoded in base32) to uint64.
|
// parseTimestamp converts a 35-bit timestamp (encoded in base32) to uint64.
|
||||||
|
@ -15,7 +15,10 @@ import (
|
|||||||
"github.com/roasbeef/btcd/btcec"
|
"github.com/roasbeef/btcd/btcec"
|
||||||
"github.com/roasbeef/btcd/chaincfg"
|
"github.com/roasbeef/btcd/chaincfg"
|
||||||
"github.com/roasbeef/btcd/chaincfg/chainhash"
|
"github.com/roasbeef/btcd/chaincfg/chainhash"
|
||||||
|
"github.com/roasbeef/btcd/wire"
|
||||||
"github.com/roasbeef/btcutil"
|
"github.com/roasbeef/btcutil"
|
||||||
|
|
||||||
|
litecoinCfg "github.com/ltcsuite/ltcd/chaincfg"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -90,11 +93,24 @@ var (
|
|||||||
// Must be initialized in init().
|
// Must be initialized in init().
|
||||||
testPaymentHash [32]byte
|
testPaymentHash [32]byte
|
||||||
testDescriptionHash [32]byte
|
testDescriptionHash [32]byte
|
||||||
|
|
||||||
|
ltcTestNetParams chaincfg.Params
|
||||||
|
ltcMainNetParams chaincfg.Params
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
copy(testPaymentHash[:], testPaymentHashSlice[:])
|
copy(testPaymentHash[:], testPaymentHashSlice[:])
|
||||||
copy(testDescriptionHash[:], testDescriptionHashSlice[:])
|
copy(testDescriptionHash[:], testDescriptionHashSlice[:])
|
||||||
|
|
||||||
|
// Initialize litecoin testnet and mainnet params by applying key fields
|
||||||
|
// to copies of bitcoin params.
|
||||||
|
// TODO(sangaman): create an interface for chaincfg.params
|
||||||
|
ltcTestNetParams = chaincfg.TestNet3Params
|
||||||
|
ltcTestNetParams.Net = wire.BitcoinNet(litecoinCfg.TestNet4Params.Net)
|
||||||
|
ltcTestNetParams.Bech32HRPSegwit = litecoinCfg.TestNet4Params.Bech32HRPSegwit
|
||||||
|
ltcMainNetParams = chaincfg.MainNetParams
|
||||||
|
ltcMainNetParams.Net = wire.BitcoinNet(litecoinCfg.MainNetParams.Net)
|
||||||
|
ltcMainNetParams.Bech32HRPSegwit = litecoinCfg.MainNetParams.Bech32HRPSegwit
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestDecodeEncode tests that an encoded invoice gets decoded into the expected
|
// TestDecodeEncode tests that an encoded invoice gets decoded into the expected
|
||||||
@ -123,7 +139,7 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
valid: false,
|
valid: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
encodedInvoice: "lnb1asdsaddnv4wudz", // hrp too short
|
encodedInvoice: "ln1asdsaddnv4wudz", // hrp too short
|
||||||
valid: false,
|
valid: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -515,10 +531,64 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
return i
|
return i
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// Decode a mainnet invoice while expecting active net to be testnet
|
||||||
|
encodedInvoice: "lnbc241pveeq09pp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdqqnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66jd3m5klcwhq68vdsmx2rjgxeay5v0tkt2v5sjaky4eqahe4fx3k9sqavvce3capfuwv8rvjng57jrtfajn5dkpqv8yelsewtljwmmycq62k443",
|
||||||
|
valid: false,
|
||||||
|
decodedInvoice: func() *Invoice {
|
||||||
|
return &Invoice{
|
||||||
|
Net: &chaincfg.TestNet3Params,
|
||||||
|
MilliSat: &testMillisat24BTC,
|
||||||
|
Timestamp: time.Unix(1503429093, 0),
|
||||||
|
PaymentHash: &testPaymentHash,
|
||||||
|
Destination: testPubKey,
|
||||||
|
Description: &testEmptyString,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
skipEncoding: true, // Skip encoding since we were given the wrong net
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Decode a litecoin testnet invoice
|
||||||
|
encodedInvoice: "lntltc241pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66m2eq2fx9uctzkmj30meaghyskkgsd6geap5qg9j2ae444z24a4p8xg3a6g73p8l7d689vtrlgzj0wyx2h6atq8dfty7wmkt4frx9g9sp730h5a",
|
||||||
|
valid: true,
|
||||||
|
decodedInvoice: func() *Invoice {
|
||||||
|
return &Invoice{
|
||||||
|
// TODO(sangaman): create an interface for chaincfg.params
|
||||||
|
Net: <cTestNetParams,
|
||||||
|
MilliSat: &testMillisat24BTC,
|
||||||
|
Timestamp: time.Unix(1496314658, 0),
|
||||||
|
PaymentHash: &testPaymentHash,
|
||||||
|
DescriptionHash: &testDescriptionHash,
|
||||||
|
Destination: testPubKey,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Decode a litecoin mainnet invoice
|
||||||
|
encodedInvoice: "lnltc241pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66859t2d55efrxdlgqg9hdqskfstdmyssdw4fjc8qdl522ct885pqk7acn2aczh0jeht0xhuhnkmm3h0qsrxedlwm9x86787zzn4qwwwcpjkl3t2",
|
||||||
|
valid: true,
|
||||||
|
decodedInvoice: func() *Invoice {
|
||||||
|
return &Invoice{
|
||||||
|
Net: <cMainNetParams,
|
||||||
|
MilliSat: &testMillisat24BTC,
|
||||||
|
Timestamp: time.Unix(1496314658, 0),
|
||||||
|
PaymentHash: &testPaymentHash,
|
||||||
|
DescriptionHash: &testDescriptionHash,
|
||||||
|
Destination: testPubKey,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
invoice, err := Decode(test.encodedInvoice)
|
var decodedInvoice *Invoice
|
||||||
|
net := &chaincfg.MainNetParams
|
||||||
|
if test.decodedInvoice != nil {
|
||||||
|
decodedInvoice = test.decodedInvoice()
|
||||||
|
net = decodedInvoice.Net
|
||||||
|
}
|
||||||
|
|
||||||
|
invoice, err := Decode(test.encodedInvoice, net)
|
||||||
if (err == nil) != test.valid {
|
if (err == nil) != test.valid {
|
||||||
t.Errorf("Decoding test %d failed: %v", i, err)
|
t.Errorf("Decoding test %d failed: %v", i, err)
|
||||||
return
|
return
|
||||||
@ -535,11 +605,6 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var decodedInvoice *Invoice
|
|
||||||
if test.decodedInvoice != nil {
|
|
||||||
decodedInvoice = test.decodedInvoice()
|
|
||||||
}
|
|
||||||
|
|
||||||
if test.beforeEncoding != nil {
|
if test.beforeEncoding != nil {
|
||||||
test.beforeEncoding(decodedInvoice)
|
test.beforeEncoding(decodedInvoice)
|
||||||
}
|
}
|
||||||
@ -621,6 +686,30 @@ func TestNewInvoice(t *testing.T) {
|
|||||||
valid: true,
|
valid: true,
|
||||||
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfpp3qjmp7lwpagxun9pygexvgpjdc4jdj85fr9yq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqafqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzqj9n4evl6mr5aj9f58zp6fyjzup6ywn3x6sk8akg5v4tgn2q8g4fhx05wf6juaxu9760yp46454gpg5mtzgerlzezqcqvjnhjh8z3g2qqdhhwkj",
|
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfpp3qjmp7lwpagxun9pygexvgpjdc4jdj85fr9yq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqafqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzqj9n4evl6mr5aj9f58zp6fyjzup6ywn3x6sk8akg5v4tgn2q8g4fhx05wf6juaxu9760yp46454gpg5mtzgerlzezqcqvjnhjh8z3g2qqdhhwkj",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// Create a litecoin testnet invoice
|
||||||
|
newInvoice: func() (*Invoice, error) {
|
||||||
|
return NewInvoice(<cTestNetParams,
|
||||||
|
testPaymentHash, time.Unix(1496314658, 0),
|
||||||
|
Amount(testMillisat24BTC),
|
||||||
|
DescriptionHash(testDescriptionHash),
|
||||||
|
Destination(testPubKey))
|
||||||
|
},
|
||||||
|
valid: true,
|
||||||
|
encodedInvoice: "lntltc241pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66m2eq2fx9uctzkmj30meaghyskkgsd6geap5qg9j2ae444z24a4p8xg3a6g73p8l7d689vtrlgzj0wyx2h6atq8dfty7wmkt4frx9g9sp730h5a",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Create a litecoin mainnet invoice
|
||||||
|
newInvoice: func() (*Invoice, error) {
|
||||||
|
return NewInvoice(<cMainNetParams,
|
||||||
|
testPaymentHash, time.Unix(1496314658, 0),
|
||||||
|
Amount(testMillisat24BTC),
|
||||||
|
DescriptionHash(testDescriptionHash),
|
||||||
|
Destination(testPubKey))
|
||||||
|
},
|
||||||
|
valid: true,
|
||||||
|
encodedInvoice: "lnltc241pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66859t2d55efrxdlgqg9hdqskfstdmyssdw4fjc8qdl522ct885pqk7acn2aczh0jeht0xhuhnkmm3h0qsrxedlwm9x86787zzn4qwwwcpjkl3t2",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
@ -689,11 +778,7 @@ func compareInvoices(expected, actual *Invoice) error {
|
|||||||
expected.FallbackAddr, actual.FallbackAddr)
|
expected.FallbackAddr, actual.FallbackAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := compareRoutingInfos(expected.RoutingInfo, actual.RoutingInfo); err != nil {
|
return compareRoutingInfos(expected.RoutingInfo, actual.RoutingInfo)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func comparePubkeys(a, b *btcec.PublicKey) bool {
|
func comparePubkeys(a, b *btcec.PublicKey) bool {
|
||||||
|
Loading…
Reference in New Issue
Block a user