zpay32: add new 'c' field to payreqs for specifying final cltv delta
This commit is contained in:
parent
de7e339281
commit
c54e6fc803
@ -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))
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user