zpay32: handle segwit prefixes > 2 chars

This change fixes a bug when an invoice is decoded for a network
whose bech32 segwit prefix is longer than 2 characters. The length
of the Bech32HRPSegwit network parameter is used to determine
where in the human-readable portion of the invoice the amount
begins, rather than assuming it begins after the first four
characters.

Decode() now throws an error when the encoded invoice does
not match the active network.

Changes the minimum hrp length check to >= 3 instead of >= 4.

Also removes a redundant "if ...; err != nil check" that was raising
a warning in invoice.go.
This commit is contained in:
Daniel McNally 2018-02-19 10:18:16 -05:00
parent 8329a31fb0
commit fdbdcf1560

@ -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.