multi: introduce new traffic shaper method.
We introduce a new specific fail resolution error when the external HTLC interceptor denies the incoming HTLC. Moreover we introduce a new traffic shaper method which moves the implementation of asset HTLC to the external layers. Moreover itests are adopted to reflect this new change.
This commit is contained in:
8 files changed
+37
-248
No files matched your search
@@ -406,14 +406,6 @@ var allTestCases = []*lntest.TestCase{
|
||||
Name: "forward interceptor",
|
||||
TestFunc: testForwardInterceptorBasic,
|
||||
},
|
||||
{
|
||||
Name: "forward interceptor modified htlc",
|
||||
TestFunc: testForwardInterceptorModifiedHtlc,
|
||||
},
|
||||
{
|
||||
Name: "forward interceptor wire records",
|
||||
TestFunc: testForwardInterceptorWireRecords,
|
||||
},
|
||||
{
|
||||
Name: "forward interceptor restart",
|
||||
TestFunc: testForwardInterceptorRestart,
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/lightningnetwork/lnd/chainreg"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
|
||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||
"github.com/lightningnetwork/lnd/lntest"
|
||||
"github.com/lightningnetwork/lnd/lntest/node"
|
||||
@@ -346,223 +345,6 @@ func testForwardInterceptorBasic(ht *lntest.HarnessTest) {
|
||||
}
|
||||
}
|
||||
|
||||
// testForwardInterceptorModifiedHtlc tests that the interceptor can modify the
|
||||
// amount and custom records of an intercepted HTLC and resume it.
|
||||
func testForwardInterceptorModifiedHtlc(ht *lntest.HarnessTest) {
|
||||
const chanAmt = btcutil.Amount(300000)
|
||||
p := lntest.OpenChannelParams{Amt: chanAmt}
|
||||
|
||||
// Initialize the test context with 3 connected nodes.
|
||||
cfgs := [][]string{nil, nil, nil}
|
||||
|
||||
// Open and wait for channels.
|
||||
_, nodes := ht.CreateSimpleNetwork(cfgs, p)
|
||||
alice, bob, carol := nodes[0], nodes[1], nodes[2]
|
||||
|
||||
// Init the scenario.
|
||||
ts := &interceptorTestScenario{
|
||||
ht: ht,
|
||||
alice: alice,
|
||||
bob: bob,
|
||||
carol: carol,
|
||||
}
|
||||
|
||||
// Connect an interceptor to Bob's node.
|
||||
bobInterceptor, cancelBobInterceptor := bob.RPC.HtlcInterceptor()
|
||||
|
||||
// We're going to modify the payment amount and want Carol to accept the
|
||||
// payment, so we set up an invoice acceptor on Dave.
|
||||
carolAcceptor, carolCancel := carol.RPC.InvoiceHtlcModifier()
|
||||
defer carolCancel()
|
||||
|
||||
// Prepare the test cases.
|
||||
invoiceValueAmtMsat := int64(20_000_000)
|
||||
req := &lnrpc.Invoice{ValueMsat: invoiceValueAmtMsat}
|
||||
addResponse := carol.RPC.AddInvoice(req)
|
||||
invoice := carol.RPC.LookupInvoice(addResponse.RHash)
|
||||
tc := &interceptorTestCase{
|
||||
amountMsat: invoiceValueAmtMsat,
|
||||
invoice: invoice,
|
||||
payAddr: invoice.PaymentAddr,
|
||||
}
|
||||
|
||||
// We initiate a payment from Alice.
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
// Signal that all the payments have been sent.
|
||||
defer close(done)
|
||||
|
||||
ts.sendPaymentAndAssertAction(tc)
|
||||
}()
|
||||
|
||||
// We start the htlc interceptor with a simple implementation that saves
|
||||
// all intercepted packets. These packets are held to simulate a
|
||||
// pending payment.
|
||||
packet := ht.ReceiveHtlcInterceptor(bobInterceptor)
|
||||
|
||||
// Resume the intercepted HTLC with a modified amount and custom
|
||||
// records.
|
||||
customRecords := make(map[uint64][]byte)
|
||||
|
||||
// Add custom records entry.
|
||||
crKey := uint64(65537)
|
||||
crValue := []byte("custom-records-test-value")
|
||||
customRecords[crKey] = crValue
|
||||
|
||||
// Modify the amount of the HTLC, so we send out less than the original
|
||||
// amount.
|
||||
const modifyAmount = 5_000_000
|
||||
newOutAmountMsat := packet.OutgoingAmountMsat - modifyAmount
|
||||
err := bobInterceptor.Send(&routerrpc.ForwardHtlcInterceptResponse{
|
||||
IncomingCircuitKey: packet.IncomingCircuitKey,
|
||||
OutAmountMsat: newOutAmountMsat,
|
||||
OutWireCustomRecords: customRecords,
|
||||
Action: actionResumeModify,
|
||||
})
|
||||
require.NoError(ht, err, "failed to send request")
|
||||
|
||||
invoicePacket := ht.ReceiveInvoiceHtlcModification(carolAcceptor)
|
||||
require.EqualValues(
|
||||
ht, newOutAmountMsat, invoicePacket.ExitHtlcAmt,
|
||||
)
|
||||
amtPaid := newOutAmountMsat + modifyAmount
|
||||
err = carolAcceptor.Send(&invoicesrpc.HtlcModifyResponse{
|
||||
CircuitKey: invoicePacket.ExitHtlcCircuitKey,
|
||||
AmtPaid: &amtPaid,
|
||||
})
|
||||
require.NoError(ht, err, "carol acceptor response")
|
||||
|
||||
// Cancel the context, which will disconnect Bob's interceptor.
|
||||
cancelBobInterceptor()
|
||||
|
||||
// Make sure all goroutines are finished.
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(defaultTimeout):
|
||||
require.Fail(ht, "timeout waiting for sending payment")
|
||||
}
|
||||
|
||||
// Assert that the payment was successful.
|
||||
var preimage lntypes.Preimage
|
||||
copy(preimage[:], invoice.RPreimage)
|
||||
ht.AssertPaymentStatus(alice, preimage, lnrpc.Payment_SUCCEEDED)
|
||||
}
|
||||
|
||||
// testForwardInterceptorWireRecords tests that the interceptor can read any
|
||||
// wire custom records provided by the sender of a payment as part of the
|
||||
// update_add_htlc message.
|
||||
func testForwardInterceptorWireRecords(ht *lntest.HarnessTest) {
|
||||
const chanAmt = btcutil.Amount(300000)
|
||||
p := lntest.OpenChannelParams{Amt: chanAmt}
|
||||
|
||||
// Initialize the test context with 4 connected nodes.
|
||||
cfgs := [][]string{nil, nil, nil, nil}
|
||||
|
||||
// Open and wait for channels.
|
||||
_, nodes := ht.CreateSimpleNetwork(cfgs, p)
|
||||
alice, bob, carol, dave := nodes[0], nodes[1], nodes[2], nodes[3]
|
||||
|
||||
// Connect an interceptor to Bob's node.
|
||||
bobInterceptor, cancelBobInterceptor := bob.RPC.HtlcInterceptor()
|
||||
defer cancelBobInterceptor()
|
||||
|
||||
// Also connect an interceptor on Carol's node to check whether we're
|
||||
// relaying the TLVs send in update_add_htlc over Alice -> Bob on the
|
||||
// Bob -> Carol link.
|
||||
carolInterceptor, cancelCarolInterceptor := carol.RPC.HtlcInterceptor()
|
||||
defer cancelCarolInterceptor()
|
||||
|
||||
// We're going to modify the payment amount and want Dave to accept the
|
||||
// payment, so we set up an invoice acceptor on Dave.
|
||||
daveAcceptor, daveCancel := dave.RPC.InvoiceHtlcModifier()
|
||||
defer daveCancel()
|
||||
|
||||
req := &lnrpc.Invoice{ValueMsat: 20_000_000}
|
||||
addResponse := dave.RPC.AddInvoice(req)
|
||||
invoice := dave.RPC.LookupInvoice(addResponse.RHash)
|
||||
|
||||
customRecords := map[uint64][]byte{
|
||||
65537: []byte("test"),
|
||||
}
|
||||
sendReq := &routerrpc.SendPaymentRequest{
|
||||
PaymentRequest: invoice.PaymentRequest,
|
||||
TimeoutSeconds: int32(wait.PaymentTimeout.Seconds()),
|
||||
FeeLimitMsat: noFeeLimitMsat,
|
||||
FirstHopCustomRecords: customRecords,
|
||||
}
|
||||
ht.SendPaymentAssertInflight(alice, sendReq)
|
||||
|
||||
// We start the htlc interceptor with a simple implementation that saves
|
||||
// all intercepted packets. These packets are held to simulate a
|
||||
// pending payment.
|
||||
packet := ht.ReceiveHtlcInterceptor(bobInterceptor)
|
||||
require.Equal(ht, lntest.CustomRecordsWithUnendorsed(
|
||||
customRecords,
|
||||
), packet.InWireCustomRecords)
|
||||
|
||||
// Just resume the payment on Bob.
|
||||
err := bobInterceptor.Send(&routerrpc.ForwardHtlcInterceptResponse{
|
||||
IncomingCircuitKey: packet.IncomingCircuitKey,
|
||||
Action: actionResume,
|
||||
})
|
||||
require.NoError(ht, err, "failed to send request")
|
||||
|
||||
// Assert that the Alice -> Bob custom records in update_add_htlc are
|
||||
// not propagated on the Bob -> Carol link, just an endorsement signal.
|
||||
packet = ht.ReceiveHtlcInterceptor(carolInterceptor)
|
||||
require.Equal(ht, lntest.CustomRecordsWithUnendorsed(nil),
|
||||
packet.InWireCustomRecords)
|
||||
|
||||
// We're going to tell Carol to forward 5k sats less to Dave. We need to
|
||||
// set custom records on the HTLC as well, to make sure the HTLC isn't
|
||||
// rejected outright and actually gets to the invoice acceptor.
|
||||
const modifyAmount = 5_000_000
|
||||
newOutAmountMsat := packet.OutgoingAmountMsat - modifyAmount
|
||||
err = carolInterceptor.Send(&routerrpc.ForwardHtlcInterceptResponse{
|
||||
IncomingCircuitKey: packet.IncomingCircuitKey,
|
||||
OutAmountMsat: newOutAmountMsat,
|
||||
OutWireCustomRecords: customRecords,
|
||||
Action: actionResumeModify,
|
||||
})
|
||||
require.NoError(ht, err, "carol interceptor response")
|
||||
|
||||
// The payment should get to Dave, and we should be able to intercept
|
||||
// and modify it, telling Dave to accept it.
|
||||
invoicePacket := ht.ReceiveInvoiceHtlcModification(daveAcceptor)
|
||||
require.EqualValues(
|
||||
ht, newOutAmountMsat, invoicePacket.ExitHtlcAmt,
|
||||
)
|
||||
amtPaid := newOutAmountMsat + modifyAmount
|
||||
err = daveAcceptor.Send(&invoicesrpc.HtlcModifyResponse{
|
||||
CircuitKey: invoicePacket.ExitHtlcCircuitKey,
|
||||
AmtPaid: &amtPaid,
|
||||
})
|
||||
require.NoError(ht, err, "dave acceptor response")
|
||||
|
||||
// Assert that the payment was successful.
|
||||
var preimage lntypes.Preimage
|
||||
copy(preimage[:], invoice.RPreimage)
|
||||
ht.AssertPaymentStatus(
|
||||
alice, preimage, lnrpc.Payment_SUCCEEDED,
|
||||
func(p *lnrpc.Payment) error {
|
||||
recordsEqual := reflect.DeepEqual(
|
||||
p.FirstHopCustomRecords,
|
||||
lntest.CustomRecordsWithUnendorsed(
|
||||
customRecords,
|
||||
),
|
||||
)
|
||||
if !recordsEqual {
|
||||
return fmt.Errorf("expected custom records to "+
|
||||
"be equal, got %v expected %v",
|
||||
p.FirstHopCustomRecords,
|
||||
sendReq.FirstHopCustomRecords)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// testForwardInterceptorRestart tests that the interceptor can read any wire
|
||||
// custom records provided by the sender of a payment as part of the
|
||||
// update_add_htlc message and that those records are persisted correctly and
|
||||
|
||||
Reference in new issue
Block a user