Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] multi: add new draft key send mode for spontaneous payments #2455

Closed
wants to merge 14 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion cmd/lncli/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -2077,6 +2077,10 @@ var sendPaymentCommand = cli.Command{
Name: "force, f",
Usage: "will skip payment request confirmation",
},
cli.BoolFlag{
Name: "key_send",
Usage: "will generate a pre-image and encode it in the sphinx packet, a dest must be set",
},
},
Action: sendPayment,
}
Expand Down Expand Up @@ -2215,11 +2219,12 @@ func sendPayment(ctx *cli.Context) error {
Dest: destNode,
Amt: amount,
FeeLimit: feeLimit,
KeySend: ctx.Bool("key_send"),
}

if ctx.Bool("debug_send") && (ctx.IsSet("payment_hash") || args.Present()) {
return fmt.Errorf("do not provide a payment hash with debug send")
} else if !ctx.Bool("debug_send") {
} else if !ctx.Bool("debug_send") && !ctx.Bool("key_send") {
var rHash []byte

switch {
Expand Down
2 changes: 1 addition & 1 deletion contractcourt/htlc_incoming_contest_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
// identical to HTLC resolution in the link.
event, err := h.Registry.NotifyExitHopHtlc(
h.payHash, h.htlcAmt, h.htlcExpiry, currentHeight,
hodlChan,
hodlChan, nil,
)
switch err {
case channeldb.ErrInvoiceNotFound:
Expand Down
3 changes: 2 additions & 1 deletion contractcourt/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ type Registry interface {
// the resolution is sent on the passed in hodlChan later.
NotifyExitHopHtlc(payHash lntypes.Hash, paidAmount lnwire.MilliSatoshi,
expiry uint32, currentHeight int32,
hodlChan chan<- interface{}) (*invoices.HodlEvent, error)
hodlChan chan<- interface{},
destEOB []byte) (*invoices.HodlEvent, error)

// HodlUnsubscribeAll unsubscribes from all hodl events.
HodlUnsubscribeAll(subscriber chan<- interface{})
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,5 @@ require (
replace github.com/lightningnetwork/lnd/ticker => ./ticker

replace github.com/lightningnetwork/lnd/queue => ./queue

replace github.com/lightningnetwork/lightning-onion v0.0.0-20190430041606-751fb4dd8b72 => github.com/roasbeef/lightning-onion v0.0.0-20190111024907-b16e0e746d70d6f19176fe321d8c392e788f6168
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/roasbeef/lightning-onion v0.0.0-20190111024907-b16e0e746d70d6f19176fe321d8c392e788f6168 h1:CRdukCpvAHhnzm7stsLDSi88h26wcTCIUxFSDSso6Vg=
github.com/roasbeef/lightning-onion v0.0.0-20190111024907-b16e0e746d70d6f19176fe321d8c392e788f6168/go.mod h1:Sooe/CoCqa85JxqHV+IBR2HW+6t2Cv+36awSmoccswM=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af h1:gu+uRPtBe88sKxUCEXRoeCvVG90TJmwhiqRpvdhQFng=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/tv42/zbase32 v0.0.0-20160707012821-501572607d02 h1:tcJ6OjwOMvExLlzrAVZute09ocAGa7KqOON60++Gz4E=
Expand Down
3 changes: 2 additions & 1 deletion htlcswitch/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ type InvoiceDatabase interface {
// the resolution is sent on the passed in hodlChan later.
NotifyExitHopHtlc(payHash lntypes.Hash, paidAmount lnwire.MilliSatoshi,
expiry uint32, currentHeight int32,
hodlChan chan<- interface{}) (*invoices.HodlEvent, error)
hodlChan chan<- interface{},
packetEOB []byte) (*invoices.HodlEvent, error)

// CancelInvoice attempts to cancel the invoice corresponding to the
// passed payment hash.
Expand Down
13 changes: 13 additions & 0 deletions htlcswitch/iterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ type HopIterator interface {
// information given to it by the prior hop.
ForwardingInstructions() ForwardingInfo

// ExtraOnionBlob returns any parsed EOB data if any. If data is
// present, then the Type field will be anything other than
// sphinx.EOBEmpty.
ExtraOnionBlob() []byte

// EncodeNextHop encodes the onion packet destined for the next hop
// into the passed io.Writer.
EncodeNextHop(w io.Writer) error
Expand Down Expand Up @@ -159,6 +164,14 @@ func (r *sphinxHopIterator) ForwardingInstructions() ForwardingInfo {
}
}

// ExtraOnionBlob returns any parsed EOB data if any. If data is present, then
// the Type field will be anything other than sphinx.EOBEmpty.
//
// NOTE: Part of the HopIterator interface.
func (r *sphinxHopIterator) ExtraOnionBlob() []byte {
return r.processedPacket.ExtraOnionBlob
}

// ExtractErrorEncrypter decodes and returns the ErrorEncrypter for this hop,
// along with a failure code to signal if the decoding was successful. The
// ErrorEncrypter is used to encrypt errors back to the sender in the event that
Expand Down
8 changes: 4 additions & 4 deletions htlcswitch/link.go
Original file line number Diff line number Diff line change
Expand Up @@ -2565,6 +2565,7 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg,
case exitHop:
updated, err := l.processExitHop(
pd, obfuscator, fwdInfo, heightNow,
chanIterator.ExtraOnionBlob(),
)
if err != nil {
l.fail(LinkFailureError{code: ErrInternalError},
Expand Down Expand Up @@ -2737,8 +2738,8 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg,
// processExitHop handles an htlc for which this link is the exit hop. It
// returns a boolean indicating whether the commitment tx needs an update.
func (l *channelLink) processExitHop(pd *lnwallet.PaymentDescriptor,
obfuscator ErrorEncrypter, fwdInfo ForwardingInfo, heightNow uint32) (
bool, error) {
obfuscator ErrorEncrypter, fwdInfo ForwardingInfo, heightNow uint32,
packetEOB []byte) (bool, error) {

// If hodl.ExitSettle is requested, we will not validate the final hop's
// ADD, nor will we settle the corresponding invoice or respond with the
Expand Down Expand Up @@ -2783,10 +2784,9 @@ func (l *channelLink) processExitHop(pd *lnwallet.PaymentDescriptor,
// after this, this code will be re-executed after restart. We will
// receive back a resolution event.
invoiceHash := lntypes.Hash(pd.RHash)

event, err := l.cfg.Registry.NotifyExitHopHtlc(
invoiceHash, pd.Amount, pd.Timeout, int32(heightNow),
l.hodlQueue.ChanIn(),
l.hodlQueue.ChanIn(), packetEOB,
)

switch err {
Expand Down
9 changes: 7 additions & 2 deletions htlcswitch/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,10 @@ func (r *mockHopIterator) ForwardingInstructions() ForwardingInfo {
return h
}

func (r *mockHopIterator) ExtraOnionBlob() []byte {
return []byte{}
}

func (r *mockHopIterator) ExtractErrorEncrypter(
extracter ErrorEncrypterExtracter) (ErrorEncrypter, lnwire.FailCode) {

Expand Down Expand Up @@ -787,10 +791,11 @@ func (i *mockInvoiceRegistry) SettleHodlInvoice(preimage lntypes.Preimage) error

func (i *mockInvoiceRegistry) NotifyExitHopHtlc(rhash lntypes.Hash,
amt lnwire.MilliSatoshi, expiry uint32, currentHeight int32,
hodlChan chan<- interface{}) (*invoices.HodlEvent, error) {
hodlChan chan<- interface{},
packetEOB []byte) (*invoices.HodlEvent, error) {

event, err := i.registry.NotifyExitHopHtlc(
rhash, amt, expiry, currentHeight, hodlChan,
rhash, amt, expiry, currentHeight, hodlChan, packetEOB,
)
if err != nil {
return nil, err
Expand Down
45 changes: 44 additions & 1 deletion invoices/invoiceregistry.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/queue"
"github.com/lightningnetwork/lnd/tlv"
"github.com/lightningnetwork/lnd/watchtower/wtwire"
)

var (
Expand Down Expand Up @@ -563,7 +565,7 @@ func (i *InvoiceRegistry) checkHtlcParameters(invoice *channeldb.Invoice,
// prevent deadlock.
func (i *InvoiceRegistry) NotifyExitHopHtlc(rHash lntypes.Hash,
amtPaid lnwire.MilliSatoshi, expiry uint32, currentHeight int32,
hodlChan chan<- interface{}) (*HodlEvent, error) {
hodlChan chan<- interface{}, packetEOB []byte) (*HodlEvent, error) {

i.Lock()
defer i.Unlock()
Expand All @@ -572,6 +574,7 @@ func (i *InvoiceRegistry) NotifyExitHopHtlc(rHash lntypes.Hash,
log.Debugf("Invoice(%x): %v, amt=%v, expiry=%v",
rHash[:], s, amtPaid, expiry)
}

// First check the in-memory debug invoice index to see if this is an
// existing invoice added for debugging.
if invoice, ok := i.debugInvoices[rHash]; ok {
Expand All @@ -585,6 +588,46 @@ func (i *InvoiceRegistry) NotifyExitHopHtlc(rHash lntypes.Hash,
}, nil
}

// If any EOB data was provided, then we'll attempt to decode the TLV
// information included, as it may modify when/how we settle the HTLC.
if len(packetEOB) != 0 {
log.Infof("TLV packet other: %x", packetEOB)

// Atm, the only TLV field that we care about is the one that
// denotes a pre-image has been sent to us in the encrypted EOB, so
// we'll attempt to parse that out.
var (
preImage [32]byte
blankPreImage [32]byte
)
tlvStream := tlv.NewStream(
wtwire.WriteElement, wtwire.ReadElement,
tlv.MakePrimitiveRecord(PreimageTLV, &preImage),
)
err := tlvStream.Decode(
bytes.NewReader(packetEOB), tlv.ParseModeRetain,
)
if err != nil {
return nil, err
}

// If something was acutally specified for this tag, then we'll
// check to see if it matches up with the payment hash.
paymentSecret := lntypes.Preimage(preImage)
if preImage != blankPreImage && paymentSecret.Matches(rHash) {

debugLog("settled")

// TODO(roasbeef): record settled spontaneous payment
// in DB

return &HodlEvent{
Hash: rHash,
Preimage: &paymentSecret,
}, nil
}
}

// If this isn't a debug invoice, then we'll attempt to settle an
// invoice matching this rHash on disk (if one exists).
invoice, err := i.cdb.AcceptOrSettleInvoice(
Expand Down
6 changes: 6 additions & 0 deletions invoices/tlv_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package invoices

const (
// PreimageTLV...
PreimageTLV = 128
)
18 changes: 9 additions & 9 deletions lnrpc/invoicesrpc/invoices.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading