lntest/test: move multi-hop payments test into own file
This commit is contained in:
parent
e229717025
commit
578f6be0a1
289
lntest/itest/lnd_multi-hop-payments.go
Normal file
289
lntest/itest/lnd_multi-hop-payments.go
Normal file
@ -0,0 +1,289 @@
|
||||
// +build rpctest
|
||||
|
||||
package itest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightningnetwork/lnd"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/lntest"
|
||||
)
|
||||
|
||||
func testMultiHopPayments(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
ctxb := context.Background()
|
||||
|
||||
const chanAmt = btcutil.Amount(100000)
|
||||
var networkChans []*lnrpc.ChannelPoint
|
||||
|
||||
// Open a channel with 100k satoshis between Alice and Bob with Alice
|
||||
// being the sole funder of the channel.
|
||||
ctxt, _ := context.WithTimeout(ctxb, channelOpenTimeout)
|
||||
chanPointAlice := openChannelAndAssert(
|
||||
ctxt, t, net, net.Alice, net.Bob,
|
||||
lntest.OpenChannelParams{
|
||||
Amt: chanAmt,
|
||||
},
|
||||
)
|
||||
networkChans = append(networkChans, chanPointAlice)
|
||||
|
||||
aliceChanTXID, err := lnd.GetChanPointFundingTxid(chanPointAlice)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get txid: %v", err)
|
||||
}
|
||||
aliceFundPoint := wire.OutPoint{
|
||||
Hash: *aliceChanTXID,
|
||||
Index: chanPointAlice.OutputIndex,
|
||||
}
|
||||
|
||||
// As preliminary setup, we'll create two new nodes: Carol and Dave,
|
||||
// such that we now have a 4 node, 3 channel topology. Dave will make a
|
||||
// channel with Alice, and Carol with Dave. After this setup, the
|
||||
// network topology should now look like:
|
||||
// Carol -> Dave -> Alice -> Bob
|
||||
//
|
||||
// First, we'll create Dave and establish a channel to Alice. Dave will
|
||||
// be running an older node that requires the legacy onion payload.
|
||||
daveArgs := []string{"--protocol.legacyonion"}
|
||||
dave, err := net.NewNode("Dave", daveArgs)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create new nodes: %v", err)
|
||||
}
|
||||
defer shutdownAndAssert(net, t, dave)
|
||||
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
if err := net.ConnectNodes(ctxt, dave, net.Alice); err != nil {
|
||||
t.Fatalf("unable to connect dave to alice: %v", err)
|
||||
}
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
err = net.SendCoins(ctxt, btcutil.SatoshiPerBitcoin, dave)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to send coins to dave: %v", err)
|
||||
}
|
||||
ctxt, _ = context.WithTimeout(ctxb, channelOpenTimeout)
|
||||
chanPointDave := openChannelAndAssert(
|
||||
ctxt, t, net, dave, net.Alice,
|
||||
lntest.OpenChannelParams{
|
||||
Amt: chanAmt,
|
||||
},
|
||||
)
|
||||
networkChans = append(networkChans, chanPointDave)
|
||||
daveChanTXID, err := lnd.GetChanPointFundingTxid(chanPointDave)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get txid: %v", err)
|
||||
}
|
||||
daveFundPoint := wire.OutPoint{
|
||||
Hash: *daveChanTXID,
|
||||
Index: chanPointDave.OutputIndex,
|
||||
}
|
||||
|
||||
// Next, we'll create Carol and establish a channel to from her to
|
||||
// Dave.
|
||||
carol, err := net.NewNode("Carol", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create new nodes: %v", err)
|
||||
}
|
||||
defer shutdownAndAssert(net, t, carol)
|
||||
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
if err := net.ConnectNodes(ctxt, carol, dave); err != nil {
|
||||
t.Fatalf("unable to connect carol to dave: %v", err)
|
||||
}
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
err = net.SendCoins(ctxt, btcutil.SatoshiPerBitcoin, carol)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to send coins to carol: %v", err)
|
||||
}
|
||||
ctxt, _ = context.WithTimeout(ctxb, channelOpenTimeout)
|
||||
chanPointCarol := openChannelAndAssert(
|
||||
ctxt, t, net, carol, dave,
|
||||
lntest.OpenChannelParams{
|
||||
Amt: chanAmt,
|
||||
},
|
||||
)
|
||||
networkChans = append(networkChans, chanPointCarol)
|
||||
|
||||
carolChanTXID, err := lnd.GetChanPointFundingTxid(chanPointCarol)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get txid: %v", err)
|
||||
}
|
||||
carolFundPoint := wire.OutPoint{
|
||||
Hash: *carolChanTXID,
|
||||
Index: chanPointCarol.OutputIndex,
|
||||
}
|
||||
|
||||
// Wait for all nodes to have seen all channels.
|
||||
nodes := []*lntest.HarnessNode{net.Alice, net.Bob, carol, dave}
|
||||
nodeNames := []string{"Alice", "Bob", "Carol", "Dave"}
|
||||
for _, chanPoint := range networkChans {
|
||||
for i, node := range nodes {
|
||||
txid, err := lnd.GetChanPointFundingTxid(chanPoint)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get txid: %v", err)
|
||||
}
|
||||
point := wire.OutPoint{
|
||||
Hash: *txid,
|
||||
Index: chanPoint.OutputIndex,
|
||||
}
|
||||
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
err = node.WaitForNetworkChannelOpen(ctxt, chanPoint)
|
||||
if err != nil {
|
||||
t.Fatalf("%s(%d): timeout waiting for "+
|
||||
"channel(%s) open: %v", nodeNames[i],
|
||||
node.NodeID, point, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create 5 invoices for Bob, which expect a payment from Carol for 1k
|
||||
// satoshis with a different preimage each time.
|
||||
const numPayments = 5
|
||||
const paymentAmt = 1000
|
||||
payReqs, _, _, err := createPayReqs(
|
||||
net.Bob, paymentAmt, numPayments,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create pay reqs: %v", err)
|
||||
}
|
||||
|
||||
// We'll wait for all parties to recognize the new channels within the
|
||||
// network.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
err = dave.WaitForNetworkChannelOpen(ctxt, chanPointDave)
|
||||
if err != nil {
|
||||
t.Fatalf("dave didn't advertise his channel: %v", err)
|
||||
}
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
err = carol.WaitForNetworkChannelOpen(ctxt, chanPointCarol)
|
||||
if err != nil {
|
||||
t.Fatalf("carol didn't advertise her channel in time: %v",
|
||||
err)
|
||||
}
|
||||
|
||||
time.Sleep(time.Millisecond * 50)
|
||||
|
||||
// Set the fee policies of the Alice -> Bob and the Dave -> Alice
|
||||
// channel edges to relatively large non default values. This makes it
|
||||
// possible to pick up more subtle fee calculation errors.
|
||||
maxHtlc := uint64(calculateMaxHtlc(chanAmt))
|
||||
updateChannelPolicy(
|
||||
t, net.Alice, chanPointAlice, 1000, 100000,
|
||||
lnd.DefaultBitcoinTimeLockDelta, maxHtlc, carol,
|
||||
)
|
||||
|
||||
updateChannelPolicy(
|
||||
t, dave, chanPointDave, 5000, 150000,
|
||||
lnd.DefaultBitcoinTimeLockDelta, maxHtlc, carol,
|
||||
)
|
||||
|
||||
// Using Carol as the source, pay to the 5 invoices from Bob created
|
||||
// above.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
err = completePaymentRequests(ctxt, carol, payReqs, true)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to send payments: %v", err)
|
||||
}
|
||||
|
||||
// When asserting the amount of satoshis moved, we'll factor in the
|
||||
// default base fee, as we didn't modify the fee structure when
|
||||
// creating the seed nodes in the network.
|
||||
const baseFee = 1
|
||||
|
||||
// At this point all the channels within our proto network should be
|
||||
// shifted by 5k satoshis in the direction of Bob, the sink within the
|
||||
// payment flow generated above. The order of asserts corresponds to
|
||||
// increasing of time is needed to embed the HTLC in commitment
|
||||
// transaction, in channel Carol->David->Alice->Bob, order is Bob,
|
||||
// Alice, David, Carol.
|
||||
|
||||
// The final node bob expects to get paid five times 1000 sat.
|
||||
expectedAmountPaidAtoB := int64(5 * 1000)
|
||||
|
||||
assertAmountPaid(t, "Alice(local) => Bob(remote)", net.Bob,
|
||||
aliceFundPoint, int64(0), expectedAmountPaidAtoB)
|
||||
assertAmountPaid(t, "Alice(local) => Bob(remote)", net.Alice,
|
||||
aliceFundPoint, expectedAmountPaidAtoB, int64(0))
|
||||
|
||||
// To forward a payment of 1000 sat, Alice is charging a fee of
|
||||
// 1 sat + 10% = 101 sat.
|
||||
const expectedFeeAlice = 5 * 101
|
||||
|
||||
// Dave needs to pay what Alice pays plus Alice's fee.
|
||||
expectedAmountPaidDtoA := expectedAmountPaidAtoB + expectedFeeAlice
|
||||
|
||||
assertAmountPaid(t, "Dave(local) => Alice(remote)", net.Alice,
|
||||
daveFundPoint, int64(0), expectedAmountPaidDtoA)
|
||||
assertAmountPaid(t, "Dave(local) => Alice(remote)", dave,
|
||||
daveFundPoint, expectedAmountPaidDtoA, int64(0))
|
||||
|
||||
// To forward a payment of 1101 sat, Dave is charging a fee of
|
||||
// 5 sat + 15% = 170.15 sat. This is rounded down in rpcserver to 170.
|
||||
const expectedFeeDave = 5 * 170
|
||||
|
||||
// Carol needs to pay what Dave pays plus Dave's fee.
|
||||
expectedAmountPaidCtoD := expectedAmountPaidDtoA + expectedFeeDave
|
||||
|
||||
assertAmountPaid(t, "Carol(local) => Dave(remote)", dave,
|
||||
carolFundPoint, int64(0), expectedAmountPaidCtoD)
|
||||
assertAmountPaid(t, "Carol(local) => Dave(remote)", carol,
|
||||
carolFundPoint, expectedAmountPaidCtoD, int64(0))
|
||||
|
||||
// Now that we know all the balances have been settled out properly,
|
||||
// we'll ensure that our internal record keeping for completed circuits
|
||||
// was properly updated.
|
||||
|
||||
// First, check that the FeeReport response shows the proper fees
|
||||
// accrued over each time range. Dave should've earned 170 satoshi for
|
||||
// each of the forwarded payments.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
feeReport, err := dave.FeeReport(ctxt, &lnrpc.FeeReportRequest{})
|
||||
if err != nil {
|
||||
t.Fatalf("unable to query for fee report: %v", err)
|
||||
}
|
||||
|
||||
if feeReport.DayFeeSum != uint64(expectedFeeDave) {
|
||||
t.Fatalf("fee mismatch: expected %v, got %v", expectedFeeDave,
|
||||
feeReport.DayFeeSum)
|
||||
}
|
||||
if feeReport.WeekFeeSum != uint64(expectedFeeDave) {
|
||||
t.Fatalf("fee mismatch: expected %v, got %v", expectedFeeDave,
|
||||
feeReport.WeekFeeSum)
|
||||
}
|
||||
if feeReport.MonthFeeSum != uint64(expectedFeeDave) {
|
||||
t.Fatalf("fee mismatch: expected %v, got %v", expectedFeeDave,
|
||||
feeReport.MonthFeeSum)
|
||||
}
|
||||
|
||||
// Next, ensure that if we issue the vanilla query for the forwarding
|
||||
// history, it returns 5 values, and each entry is formatted properly.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
fwdingHistory, err := dave.ForwardingHistory(
|
||||
ctxt, &lnrpc.ForwardingHistoryRequest{},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to query for fee report: %v", err)
|
||||
}
|
||||
if len(fwdingHistory.ForwardingEvents) != 5 {
|
||||
t.Fatalf("wrong number of forwarding event: expected %v, "+
|
||||
"got %v", 5, len(fwdingHistory.ForwardingEvents))
|
||||
}
|
||||
expectedForwardingFee := uint64(expectedFeeDave / numPayments)
|
||||
for _, event := range fwdingHistory.ForwardingEvents {
|
||||
// Each event should show a fee of 170 satoshi.
|
||||
if event.Fee != expectedForwardingFee {
|
||||
t.Fatalf("fee mismatch: expected %v, got %v",
|
||||
expectedForwardingFee, event.Fee)
|
||||
}
|
||||
}
|
||||
|
||||
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout)
|
||||
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPointAlice, false)
|
||||
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout)
|
||||
closeChannelAndAssert(ctxt, t, net, dave, chanPointDave, false)
|
||||
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout)
|
||||
closeChannelAndAssert(ctxt, t, net, carol, chanPointCarol, false)
|
||||
}
|
@ -4504,281 +4504,6 @@ func updateChannelPolicy(t *harnessTest, node *lntest.HarnessNode,
|
||||
)
|
||||
}
|
||||
|
||||
func testMultiHopPayments(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
ctxb := context.Background()
|
||||
|
||||
const chanAmt = btcutil.Amount(100000)
|
||||
var networkChans []*lnrpc.ChannelPoint
|
||||
|
||||
// Open a channel with 100k satoshis between Alice and Bob with Alice
|
||||
// being the sole funder of the channel.
|
||||
ctxt, _ := context.WithTimeout(ctxb, channelOpenTimeout)
|
||||
chanPointAlice := openChannelAndAssert(
|
||||
ctxt, t, net, net.Alice, net.Bob,
|
||||
lntest.OpenChannelParams{
|
||||
Amt: chanAmt,
|
||||
},
|
||||
)
|
||||
networkChans = append(networkChans, chanPointAlice)
|
||||
|
||||
aliceChanTXID, err := lnd.GetChanPointFundingTxid(chanPointAlice)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get txid: %v", err)
|
||||
}
|
||||
aliceFundPoint := wire.OutPoint{
|
||||
Hash: *aliceChanTXID,
|
||||
Index: chanPointAlice.OutputIndex,
|
||||
}
|
||||
|
||||
// As preliminary setup, we'll create two new nodes: Carol and Dave,
|
||||
// such that we now have a 4 node, 3 channel topology. Dave will make a
|
||||
// channel with Alice, and Carol with Dave. After this setup, the
|
||||
// network topology should now look like:
|
||||
// Carol -> Dave -> Alice -> Bob
|
||||
//
|
||||
// First, we'll create Dave and establish a channel to Alice. Dave will
|
||||
// be running an older node that requires the legacy onion payload.
|
||||
daveArgs := []string{"--protocol.legacyonion"}
|
||||
dave, err := net.NewNode("Dave", daveArgs)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create new nodes: %v", err)
|
||||
}
|
||||
defer shutdownAndAssert(net, t, dave)
|
||||
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
if err := net.ConnectNodes(ctxt, dave, net.Alice); err != nil {
|
||||
t.Fatalf("unable to connect dave to alice: %v", err)
|
||||
}
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
err = net.SendCoins(ctxt, btcutil.SatoshiPerBitcoin, dave)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to send coins to dave: %v", err)
|
||||
}
|
||||
ctxt, _ = context.WithTimeout(ctxb, channelOpenTimeout)
|
||||
chanPointDave := openChannelAndAssert(
|
||||
ctxt, t, net, dave, net.Alice,
|
||||
lntest.OpenChannelParams{
|
||||
Amt: chanAmt,
|
||||
},
|
||||
)
|
||||
networkChans = append(networkChans, chanPointDave)
|
||||
daveChanTXID, err := lnd.GetChanPointFundingTxid(chanPointDave)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get txid: %v", err)
|
||||
}
|
||||
daveFundPoint := wire.OutPoint{
|
||||
Hash: *daveChanTXID,
|
||||
Index: chanPointDave.OutputIndex,
|
||||
}
|
||||
|
||||
// Next, we'll create Carol and establish a channel to from her to
|
||||
// Dave.
|
||||
carol, err := net.NewNode("Carol", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create new nodes: %v", err)
|
||||
}
|
||||
defer shutdownAndAssert(net, t, carol)
|
||||
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
if err := net.ConnectNodes(ctxt, carol, dave); err != nil {
|
||||
t.Fatalf("unable to connect carol to dave: %v", err)
|
||||
}
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
err = net.SendCoins(ctxt, btcutil.SatoshiPerBitcoin, carol)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to send coins to carol: %v", err)
|
||||
}
|
||||
ctxt, _ = context.WithTimeout(ctxb, channelOpenTimeout)
|
||||
chanPointCarol := openChannelAndAssert(
|
||||
ctxt, t, net, carol, dave,
|
||||
lntest.OpenChannelParams{
|
||||
Amt: chanAmt,
|
||||
},
|
||||
)
|
||||
networkChans = append(networkChans, chanPointCarol)
|
||||
|
||||
carolChanTXID, err := lnd.GetChanPointFundingTxid(chanPointCarol)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get txid: %v", err)
|
||||
}
|
||||
carolFundPoint := wire.OutPoint{
|
||||
Hash: *carolChanTXID,
|
||||
Index: chanPointCarol.OutputIndex,
|
||||
}
|
||||
|
||||
// Wait for all nodes to have seen all channels.
|
||||
nodes := []*lntest.HarnessNode{net.Alice, net.Bob, carol, dave}
|
||||
nodeNames := []string{"Alice", "Bob", "Carol", "Dave"}
|
||||
for _, chanPoint := range networkChans {
|
||||
for i, node := range nodes {
|
||||
txid, err := lnd.GetChanPointFundingTxid(chanPoint)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get txid: %v", err)
|
||||
}
|
||||
point := wire.OutPoint{
|
||||
Hash: *txid,
|
||||
Index: chanPoint.OutputIndex,
|
||||
}
|
||||
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
err = node.WaitForNetworkChannelOpen(ctxt, chanPoint)
|
||||
if err != nil {
|
||||
t.Fatalf("%s(%d): timeout waiting for "+
|
||||
"channel(%s) open: %v", nodeNames[i],
|
||||
node.NodeID, point, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create 5 invoices for Bob, which expect a payment from Carol for 1k
|
||||
// satoshis with a different preimage each time.
|
||||
const numPayments = 5
|
||||
const paymentAmt = 1000
|
||||
payReqs, _, _, err := createPayReqs(
|
||||
net.Bob, paymentAmt, numPayments,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create pay reqs: %v", err)
|
||||
}
|
||||
|
||||
// We'll wait for all parties to recognize the new channels within the
|
||||
// network.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
err = dave.WaitForNetworkChannelOpen(ctxt, chanPointDave)
|
||||
if err != nil {
|
||||
t.Fatalf("dave didn't advertise his channel: %v", err)
|
||||
}
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
err = carol.WaitForNetworkChannelOpen(ctxt, chanPointCarol)
|
||||
if err != nil {
|
||||
t.Fatalf("carol didn't advertise her channel in time: %v",
|
||||
err)
|
||||
}
|
||||
|
||||
time.Sleep(time.Millisecond * 50)
|
||||
|
||||
// Set the fee policies of the Alice -> Bob and the Dave -> Alice
|
||||
// channel edges to relatively large non default values. This makes it
|
||||
// possible to pick up more subtle fee calculation errors.
|
||||
maxHtlc := uint64(calculateMaxHtlc(chanAmt))
|
||||
updateChannelPolicy(
|
||||
t, net.Alice, chanPointAlice, 1000, 100000,
|
||||
lnd.DefaultBitcoinTimeLockDelta, maxHtlc, carol,
|
||||
)
|
||||
|
||||
updateChannelPolicy(
|
||||
t, dave, chanPointDave, 5000, 150000,
|
||||
lnd.DefaultBitcoinTimeLockDelta, maxHtlc, carol,
|
||||
)
|
||||
|
||||
// Using Carol as the source, pay to the 5 invoices from Bob created
|
||||
// above.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
err = completePaymentRequests(ctxt, carol, payReqs, true)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to send payments: %v", err)
|
||||
}
|
||||
|
||||
// When asserting the amount of satoshis moved, we'll factor in the
|
||||
// default base fee, as we didn't modify the fee structure when
|
||||
// creating the seed nodes in the network.
|
||||
const baseFee = 1
|
||||
|
||||
// At this point all the channels within our proto network should be
|
||||
// shifted by 5k satoshis in the direction of Bob, the sink within the
|
||||
// payment flow generated above. The order of asserts corresponds to
|
||||
// increasing of time is needed to embed the HTLC in commitment
|
||||
// transaction, in channel Carol->David->Alice->Bob, order is Bob,
|
||||
// Alice, David, Carol.
|
||||
|
||||
// The final node bob expects to get paid five times 1000 sat.
|
||||
expectedAmountPaidAtoB := int64(5 * 1000)
|
||||
|
||||
assertAmountPaid(t, "Alice(local) => Bob(remote)", net.Bob,
|
||||
aliceFundPoint, int64(0), expectedAmountPaidAtoB)
|
||||
assertAmountPaid(t, "Alice(local) => Bob(remote)", net.Alice,
|
||||
aliceFundPoint, expectedAmountPaidAtoB, int64(0))
|
||||
|
||||
// To forward a payment of 1000 sat, Alice is charging a fee of
|
||||
// 1 sat + 10% = 101 sat.
|
||||
const expectedFeeAlice = 5 * 101
|
||||
|
||||
// Dave needs to pay what Alice pays plus Alice's fee.
|
||||
expectedAmountPaidDtoA := expectedAmountPaidAtoB + expectedFeeAlice
|
||||
|
||||
assertAmountPaid(t, "Dave(local) => Alice(remote)", net.Alice,
|
||||
daveFundPoint, int64(0), expectedAmountPaidDtoA)
|
||||
assertAmountPaid(t, "Dave(local) => Alice(remote)", dave,
|
||||
daveFundPoint, expectedAmountPaidDtoA, int64(0))
|
||||
|
||||
// To forward a payment of 1101 sat, Dave is charging a fee of
|
||||
// 5 sat + 15% = 170.15 sat. This is rounded down in rpcserver to 170.
|
||||
const expectedFeeDave = 5 * 170
|
||||
|
||||
// Carol needs to pay what Dave pays plus Dave's fee.
|
||||
expectedAmountPaidCtoD := expectedAmountPaidDtoA + expectedFeeDave
|
||||
|
||||
assertAmountPaid(t, "Carol(local) => Dave(remote)", dave,
|
||||
carolFundPoint, int64(0), expectedAmountPaidCtoD)
|
||||
assertAmountPaid(t, "Carol(local) => Dave(remote)", carol,
|
||||
carolFundPoint, expectedAmountPaidCtoD, int64(0))
|
||||
|
||||
// Now that we know all the balances have been settled out properly,
|
||||
// we'll ensure that our internal record keeping for completed circuits
|
||||
// was properly updated.
|
||||
|
||||
// First, check that the FeeReport response shows the proper fees
|
||||
// accrued over each time range. Dave should've earned 170 satoshi for
|
||||
// each of the forwarded payments.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
feeReport, err := dave.FeeReport(ctxt, &lnrpc.FeeReportRequest{})
|
||||
if err != nil {
|
||||
t.Fatalf("unable to query for fee report: %v", err)
|
||||
}
|
||||
|
||||
if feeReport.DayFeeSum != uint64(expectedFeeDave) {
|
||||
t.Fatalf("fee mismatch: expected %v, got %v", expectedFeeDave,
|
||||
feeReport.DayFeeSum)
|
||||
}
|
||||
if feeReport.WeekFeeSum != uint64(expectedFeeDave) {
|
||||
t.Fatalf("fee mismatch: expected %v, got %v", expectedFeeDave,
|
||||
feeReport.WeekFeeSum)
|
||||
}
|
||||
if feeReport.MonthFeeSum != uint64(expectedFeeDave) {
|
||||
t.Fatalf("fee mismatch: expected %v, got %v", expectedFeeDave,
|
||||
feeReport.MonthFeeSum)
|
||||
}
|
||||
|
||||
// Next, ensure that if we issue the vanilla query for the forwarding
|
||||
// history, it returns 5 values, and each entry is formatted properly.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
fwdingHistory, err := dave.ForwardingHistory(
|
||||
ctxt, &lnrpc.ForwardingHistoryRequest{},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to query for fee report: %v", err)
|
||||
}
|
||||
if len(fwdingHistory.ForwardingEvents) != 5 {
|
||||
t.Fatalf("wrong number of forwarding event: expected %v, "+
|
||||
"got %v", 5, len(fwdingHistory.ForwardingEvents))
|
||||
}
|
||||
expectedForwardingFee := uint64(expectedFeeDave / numPayments)
|
||||
for _, event := range fwdingHistory.ForwardingEvents {
|
||||
// Each event should show a fee of 170 satoshi.
|
||||
if event.Fee != expectedForwardingFee {
|
||||
t.Fatalf("fee mismatch: expected %v, got %v",
|
||||
expectedForwardingFee, event.Fee)
|
||||
}
|
||||
}
|
||||
|
||||
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout)
|
||||
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPointAlice, false)
|
||||
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout)
|
||||
closeChannelAndAssert(ctxt, t, net, dave, chanPointDave, false)
|
||||
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout)
|
||||
closeChannelAndAssert(ctxt, t, net, carol, chanPointCarol, false)
|
||||
}
|
||||
|
||||
type singleHopSendToRouteCase struct {
|
||||
name string
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user