zpay32: add new 'c' field to payreqs for specifying final cltv delta

This commit is contained in:
Olaoluwa Osuntokun 2017-10-18 22:14:54 -07:00
parent de7e339281
commit c54e6fc803
No known key found for this signature in database
GPG Key ID: 964EA263DD637C21
2 changed files with 84 additions and 5 deletions

View File

@ -8,6 +8,7 @@ import (
"time"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing"
"github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcd/chaincfg"
"github.com/roasbeef/btcd/chaincfg/chainhash"
@ -60,12 +61,14 @@ const (
// fieldTypeR contains extra routing information.
fieldTypeR = 3
// fieldTypeC contains an optional requested final CLTV delta.
fieldTypeC = 24
)
// MessageSigner is passed to the Encode method to provide a signature
// corresponding to the node's pubkey.
type MessageSigner struct {
// SignCompact signs the passed hash with the node's privkey. The
// returned signature should be 65 bytes, where the last 64 are the
// compact signature, and the first one is a header byte. This is the
@ -100,6 +103,18 @@ type Invoice struct {
// and the pubkey will be extracted from the signature during decoding.
Destination *btcec.PublicKey
// minFinalCLTVExpiry is the value that the creator of the invoice
// expects to be used for the
//
// NOTE: This value is optional, and should be set to nil if the
// invoice creator doesn't have a strong requirement on the CLTV expiry
// of the final HTLC extended to it.
//
// This field is un-exported and can only be read by the
// MinFinalCLTVExpiry() method. By forcing callers to read via this
// method, we can easily enforce the default if not specified.
minFinalCLTVExpiry *uint64
// Description is a short description of the purpose of this invoice.
// Optional. Non-nil iff DescriptionHash is nil.
Description *string
@ -162,16 +177,27 @@ func Destination(destination *btcec.PublicKey) func(*Invoice) {
// Description is a functional option that allows callers of NewInvoice to set
// the payment description of the created Invoice.
// Note: Must be used if and only if DescriptionHash is not used.
//
// NOTE: Must be used if and only if DescriptionHash is not used.
func Description(description string) func(*Invoice) {
return func(i *Invoice) {
i.Description = &description
}
}
// CLTVExpiry is an optional value which allows the receiver of the payment to
// specify the delta between the current height and the HTLC extended to the
// receiver.
func CLTVExpiry(delta uint64) func(*Invoice) {
return func(i *Invoice) {
i.minFinalCLTVExpiry = &delta
}
}
// DescriptionHash is a functional option that allows callers of NewInvoice to
// set the payment description hash of the created Invoice.
// Note: Must be used if and only if Description is not used.
//
// NOTE: Must be used if and only if Description is not used.
func DescriptionHash(descriptionHash [32]byte) func(*Invoice) {
return func(i *Invoice) {
i.DescriptionHash = &descriptionHash
@ -207,7 +233,8 @@ func RoutingInfo(routingInfo []ExtraRoutingInfo) func(*Invoice) {
// NewInvoice creates a new Invoice object. The last parameter is a set of
// variadic argumements for setting optional fields of the invoice.
// Note: Either Description or DescriptionHash must be provided for the Invoice
//
// NOTE: Either Description or DescriptionHash must be provided for the Invoice
// to be considered valid.
func NewInvoice(net *chaincfg.Params, paymentHash [32]byte,
timestamp time.Time, options ...func(*Invoice)) (*Invoice, error) {
@ -459,6 +486,19 @@ func (invoice *Invoice) Expiry() time.Duration {
return 3600 * time.Second
}
// MinFinalCLTVExpiry returns the minimum final CLTV expiry delta as specified
// by the creator of the invoice. This value specifies the delta between the
// current height and the expiry height of the HTLC extended in the last hop.
func (invoice *Invoice) MinFinalCLTVExpiry() uint64 {
if invoice.minFinalCLTVExpiry != nil {
fmt.Println("USING SET CLTV")
return *invoice.minFinalCLTVExpiry
}
fmt.Println("USING DEFAULT CLTV")
return routing.DefaultFinalCLTVDelta
}
// validateInvoice does a sanity check of the provided Invoice, making sure it
// has all the necessary fields set for it to be considered valid by BOLT-0011.
func validateInvoice(invoice *Invoice) error {
@ -651,6 +691,18 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
}
dur := time.Duration(exp) * time.Second
invoice.expiry = &dur
case fieldTypeC:
if invoice.minFinalCLTVExpiry != nil {
// We skip the field if we have already seen a
// supported one.
continue
}
expiry, err := base32ToUint64(base32Data)
if err != nil {
return err
}
invoice.minFinalCLTVExpiry = &expiry
case fieldTypeF:
if invoice.FallbackAddr != nil {
// We skip the field if we have already seen a
@ -675,7 +727,7 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
addr, err = btcutil.NewAddressWitnessScriptHash(
witness, net)
default:
return fmt.Errorf("unknow witness "+
return fmt.Errorf("unknown witness "+
"program length: %d", len(witness))
}
if err != nil {
@ -798,6 +850,14 @@ func writeTaggedFields(bufferBase32 *bytes.Buffer, invoice *Invoice) error {
}
}
if invoice.minFinalCLTVExpiry != nil {
finalDelta := uint64ToBase32(uint64(*invoice.minFinalCLTVExpiry))
err := writeTaggedField(bufferBase32, fieldTypeC, finalDelta)
if err != nil {
return err
}
}
if invoice.expiry != nil {
seconds := invoice.expiry.Seconds()
expiry := uint64ToBase32(uint64(seconds))

View File

@ -432,6 +432,25 @@ func TestDecodeEncode(t *testing.T) {
i.Destination = nil
},
},
{
// Send 2500uBTC for a cup of coffee with a custom CLTV
// expiry value.
encodedInvoice: "lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jscqzysnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66ysxkvnxhcvhz48sn72lp77h4fxcur27z0he48u5qvk3sxse9mr9jhkltt962s8arjnzk8rk59yj5nw4p495747gksj30gza0crhzwjcpgxzy00",
valid: true,
decodedInvoice: func() *zpay32.Invoice {
i, _ := zpay32.NewInvoice(
&chaincfg.MainNetParams,
testPaymentHash,
time.Unix(1496314658, 0),
zpay32.Amount(testMillisat2500uBTC),
zpay32.Description(testCupOfCoffee),
zpay32.Destination(testPubKey),
zpay32.CLTVExpiry(144),
)
return i
},
},
}
for i, test := range tests {