mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-04 09:48:19 +01:00
itest: refactor testRevokedCloseRetributionRemoteHodl
This commit is contained in:
parent
f590a96372
commit
211aa4c6f2
3 changed files with 78 additions and 187 deletions
|
@ -300,4 +300,8 @@ var allTestCasesTemp = []*lntemp.TestCase{
|
||||||
"remote output",
|
"remote output",
|
||||||
TestFunc: testRevokedCloseRetributionZeroValueRemoteOutput,
|
TestFunc: testRevokedCloseRetributionZeroValueRemoteOutput,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "revoked uncooperative close retribution remote hodl",
|
||||||
|
TestFunc: testRevokedCloseRetributionRemoteHodl,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -301,9 +301,7 @@ func testRevokedCloseRetributionZeroValueRemoteOutput(ht *lntemp.HarnessTest) {
|
||||||
// testRevokedCloseRetributionRemoteHodl tests that Dave properly responds to a
|
// testRevokedCloseRetributionRemoteHodl tests that Dave properly responds to a
|
||||||
// channel breach made by the remote party, specifically in the case that the
|
// channel breach made by the remote party, specifically in the case that the
|
||||||
// remote party breaches before settling extended HTLCs.
|
// remote party breaches before settling extended HTLCs.
|
||||||
func testRevokedCloseRetributionRemoteHodl(net *lntest.NetworkHarness,
|
func testRevokedCloseRetributionRemoteHodl(ht *lntemp.HarnessTest) {
|
||||||
t *harnessTest) {
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
chanAmt = funding.MaxBtcFundingAmount
|
chanAmt = funding.MaxBtcFundingAmount
|
||||||
pushAmt = 200000
|
pushAmt = 200000
|
||||||
|
@ -314,34 +312,31 @@ func testRevokedCloseRetributionRemoteHodl(net *lntest.NetworkHarness,
|
||||||
// Since this test will result in the counterparty being left in a
|
// Since this test will result in the counterparty being left in a
|
||||||
// weird state, we will introduce another node into our test network:
|
// weird state, we will introduce another node into our test network:
|
||||||
// Carol.
|
// Carol.
|
||||||
carol := net.NewNode(t.t, "Carol", []string{"--hodl.exit-settle"})
|
carol := ht.NewNode("Carol", []string{"--hodl.exit-settle"})
|
||||||
defer shutdownAndAssert(net, t, carol)
|
|
||||||
|
|
||||||
// We'll also create a new node Dave, who will have a channel with
|
// We'll also create a new node Dave, who will have a channel with
|
||||||
// Carol, and also use similar settings so we can broadcast a commit
|
// Carol, and also use similar settings so we can broadcast a commit
|
||||||
// with active HTLCs. Dave will be the breached party. We set
|
// with active HTLCs. Dave will be the breached party. We set
|
||||||
// --nolisten to ensure Carol won't be able to connect to him and
|
// --nolisten to ensure Carol won't be able to connect to him and
|
||||||
// trigger the channel data protection logic automatically.
|
// trigger the channel data protection logic automatically.
|
||||||
dave := net.NewNode(
|
dave := ht.NewNode(
|
||||||
t.t, "Dave",
|
"Dave",
|
||||||
[]string{"--hodl.exit-settle", "--nolisten"},
|
[]string{"--hodl.exit-settle", "--nolisten"},
|
||||||
)
|
)
|
||||||
defer shutdownAndAssert(net, t, dave)
|
|
||||||
|
|
||||||
// We must let Dave communicate with Carol before they are able to open
|
// We must let Dave communicate with Carol before they are able to open
|
||||||
// channel, so we connect Dave and Carol,
|
// channel, so we connect Dave and Carol,
|
||||||
net.ConnectNodes(t.t, dave, carol)
|
ht.ConnectNodes(dave, carol)
|
||||||
|
|
||||||
// Before we make a channel, we'll load up Dave with some coins sent
|
// Before we make a channel, we'll load up Dave with some coins sent
|
||||||
// directly from the miner.
|
// directly from the miner.
|
||||||
net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, dave)
|
ht.FundCoins(btcutil.SatoshiPerBitcoin, dave)
|
||||||
|
|
||||||
// In order to test Dave's response to an uncooperative channel closure
|
// In order to test Dave's response to an uncooperative channel closure
|
||||||
// by Carol, we'll first open up a channel between them with a
|
// by Carol, we'll first open up a channel between them with a
|
||||||
// funding.MaxBtcFundingAmount (2^24) satoshis value.
|
// funding.MaxBtcFundingAmount (2^24) satoshis value.
|
||||||
chanPoint := openChannelAndAssert(
|
chanPoint := ht.OpenChannel(
|
||||||
t, net, dave, carol,
|
dave, carol, lntemp.OpenChannelParams{
|
||||||
lntest.OpenChannelParams{
|
|
||||||
Amt: chanAmt,
|
Amt: chanAmt,
|
||||||
PushAmt: pushAmt,
|
PushAmt: pushAmt,
|
||||||
},
|
},
|
||||||
|
@ -349,206 +344,122 @@ func testRevokedCloseRetributionRemoteHodl(net *lntest.NetworkHarness,
|
||||||
|
|
||||||
// With the channel open, we'll create a few invoices for Carol that
|
// With the channel open, we'll create a few invoices for Carol that
|
||||||
// Dave will pay to in order to advance the state of the channel.
|
// Dave will pay to in order to advance the state of the channel.
|
||||||
carolPayReqs, _, _, err := createPayReqs(
|
carolPayReqs, _, _ := ht.CreatePayReqs(carol, paymentAmt, numInvoices)
|
||||||
carol, paymentAmt, numInvoices,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to create pay reqs: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// We'll introduce a closure to validate that Carol's current balance
|
// We'll introduce an closure to validate that Carol's current
|
||||||
// matches the given expected amount.
|
|
||||||
checkCarolBalance := func(expectedAmt int64) {
|
|
||||||
carolChan, err := getChanInfo(carol)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to get carol's channel info: %v", err)
|
|
||||||
}
|
|
||||||
if carolChan.LocalBalance != expectedAmt {
|
|
||||||
t.Fatalf("carol's balance is incorrect, "+
|
|
||||||
"got %v, expected %v", carolChan.LocalBalance,
|
|
||||||
expectedAmt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We'll introduce another closure to validate that Carol's current
|
|
||||||
// number of updates is at least as large as the provided minimum
|
// number of updates is at least as large as the provided minimum
|
||||||
// number.
|
// number.
|
||||||
checkCarolNumUpdatesAtLeast := func(minimum uint64) {
|
checkCarolNumUpdatesAtLeast := func(carolChan *lnrpc.Channel,
|
||||||
carolChan, err := getChanInfo(carol)
|
minimum int) {
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to get carol's channel info: %v", err)
|
|
||||||
}
|
|
||||||
if carolChan.NumUpdates < minimum {
|
|
||||||
t.Fatalf("carol's numupdates is incorrect, want %v "+
|
|
||||||
"to be at least %v", carolChan.NumUpdates,
|
|
||||||
minimum)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for Dave to receive the channel edge from the funding manager.
|
require.GreaterOrEqual(ht, int(carolChan.NumUpdates), minimum,
|
||||||
err = dave.WaitForNetworkChannelOpen(chanPoint)
|
"carol's numupdates is incorrect")
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("dave didn't see the dave->carol channel before "+
|
|
||||||
"timeout: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that carol's balance starts with the amount we pushed to her.
|
// Ensure that carol's balance starts with the amount we pushed to her.
|
||||||
checkCarolBalance(pushAmt)
|
ht.AssertChannelLocalBalance(carol, chanPoint, pushAmt)
|
||||||
|
|
||||||
// Send payments from Dave to Carol using 3 of Carol's payment hashes
|
// Send payments from Dave to Carol using 3 of Carol's payment hashes
|
||||||
// generated above.
|
// generated above.
|
||||||
err = completePaymentRequests(
|
ht.CompletePaymentRequestsNoWait(
|
||||||
dave, dave.RouterClient, carolPayReqs[:numInvoices/2], false,
|
dave, carolPayReqs[:numInvoices/2], chanPoint,
|
||||||
)
|
)
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to send payments: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// At this point, we'll also send over a set of HTLC's from Carol to
|
// At this point, we'll also send over a set of HTLC's from Carol to
|
||||||
// Dave. This ensures that the final revoked transaction has HTLC's in
|
// Dave. This ensures that the final revoked transaction has HTLC's in
|
||||||
// both directions.
|
// both directions.
|
||||||
davePayReqs, _, _, err := createPayReqs(
|
davePayReqs, _, _ := ht.CreatePayReqs(dave, paymentAmt, numInvoices)
|
||||||
dave, paymentAmt, numInvoices,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to create pay reqs: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send payments from Carol to Dave using 3 of Dave's payment hashes
|
// Send payments from Carol to Dave using 3 of Dave's payment hashes
|
||||||
// generated above.
|
// generated above.
|
||||||
err = completePaymentRequests(
|
ht.CompletePaymentRequestsNoWait(
|
||||||
carol, carol.RouterClient, davePayReqs[:numInvoices/2], false,
|
carol, davePayReqs[:numInvoices/2], chanPoint,
|
||||||
)
|
)
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to send payments: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next query for Carol's channel state, as we sent 3 payments of 10k
|
// Next query for Carol's channel state, as we sent 3 payments of 10k
|
||||||
// satoshis each, however Carol should now see her balance as being
|
// satoshis each, however Carol should now see her balance as being
|
||||||
// equal to the push amount in satoshis since she has not settled.
|
// equal to the push amount in satoshis since she has not settled.
|
||||||
carolChan, err := getChanInfo(carol)
|
|
||||||
if err != nil {
|
// Ensure that carol's balance still reflects the original amount we
|
||||||
t.Fatalf("unable to get carol's channel info: %v", err)
|
// pushed to her, minus the HTLCs she just sent to Dave.
|
||||||
}
|
carolChan := ht.AssertChannelLocalBalance(
|
||||||
|
carol, chanPoint, pushAmt-3*paymentAmt,
|
||||||
|
)
|
||||||
|
|
||||||
// Grab Carol's current commitment height (update number), we'll later
|
// Grab Carol's current commitment height (update number), we'll later
|
||||||
// revert her to this state after additional updates to force her to
|
// revert her to this state after additional updates to force her to
|
||||||
// broadcast this soon to be revoked state.
|
// broadcast this soon to be revoked state.
|
||||||
carolStateNumPreCopy := carolChan.NumUpdates
|
carolStateNumPreCopy := int(carolChan.NumUpdates)
|
||||||
|
|
||||||
// Ensure that carol's balance still reflects the original amount we
|
|
||||||
// pushed to her, minus the HTLCs she just sent to Dave.
|
|
||||||
checkCarolBalance(pushAmt - 3*paymentAmt)
|
|
||||||
|
|
||||||
// Since Carol has not settled, she should only see at least one update
|
// Since Carol has not settled, she should only see at least one update
|
||||||
// to her channel.
|
// to her channel.
|
||||||
checkCarolNumUpdatesAtLeast(1)
|
checkCarolNumUpdatesAtLeast(carolChan, 1)
|
||||||
|
|
||||||
// With the temporary file created, copy Carol's current state into the
|
// With the temporary file created, copy Carol's current state into the
|
||||||
// temporary file we created above. Later after more updates, we'll
|
// temporary file we created above. Later after more updates, we'll
|
||||||
// restore this state.
|
// restore this state.
|
||||||
if err := net.BackupDb(carol); err != nil {
|
ht.BackupDB(carol)
|
||||||
t.Fatalf("unable to copy database files: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reconnect the peers after the restart that was needed for the db
|
// Reconnect the peers after the restart that was needed for the db
|
||||||
// backup.
|
// backup.
|
||||||
net.EnsureConnected(t.t, dave, carol)
|
ht.EnsureConnected(dave, carol)
|
||||||
|
|
||||||
// Finally, send payments from Dave to Carol, consuming Carol's
|
// Finally, send payments from Dave to Carol, consuming Carol's
|
||||||
// remaining payment hashes.
|
// remaining payment hashes.
|
||||||
err = completePaymentRequests(
|
ht.CompletePaymentRequestsNoWait(
|
||||||
dave, dave.RouterClient, carolPayReqs[numInvoices/2:], false,
|
dave, carolPayReqs[numInvoices/2:], chanPoint,
|
||||||
)
|
)
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to send payments: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that carol's balance still shows the amount we originally
|
// Ensure that carol's balance still shows the amount we originally
|
||||||
// pushed to her (minus the HTLCs she sent to Bob), and that at least
|
// pushed to her (minus the HTLCs she sent to Bob), and that at least
|
||||||
// one more update has occurred.
|
// one more update has occurred.
|
||||||
time.Sleep(500 * time.Millisecond)
|
carolChan = ht.AssertChannelLocalBalance(
|
||||||
checkCarolBalance(pushAmt - 3*paymentAmt)
|
carol, chanPoint, pushAmt-3*paymentAmt,
|
||||||
checkCarolNumUpdatesAtLeast(carolStateNumPreCopy + 1)
|
)
|
||||||
|
checkCarolNumUpdatesAtLeast(carolChan, carolStateNumPreCopy+1)
|
||||||
|
|
||||||
// Suspend Dave, such that Carol won't reconnect at startup, triggering
|
// Suspend Dave, such that Carol won't reconnect at startup, triggering
|
||||||
// the data loss protection.
|
// the data loss protection.
|
||||||
restartDave, err := net.SuspendNode(dave)
|
restartDave := ht.SuspendNode(dave)
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to suspend Dave: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we shutdown Carol, copying over the her temporary database state
|
// Now we shutdown Carol, copying over the her temporary database state
|
||||||
// which has the *prior* channel state over her current most up to date
|
// which has the *prior* channel state over her current most up to date
|
||||||
// state. With this, we essentially force Carol to travel back in time
|
// state. With this, we essentially force Carol to travel back in time
|
||||||
// within the channel's history.
|
// within the channel's history.
|
||||||
if err = net.RestartNode(carol, func() error {
|
ht.RestartNodeAndRestoreDB(carol)
|
||||||
return net.RestoreDb(carol)
|
|
||||||
}); err != nil {
|
|
||||||
t.Fatalf("unable to restart node: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(200 * time.Millisecond)
|
|
||||||
|
|
||||||
// Ensure that Carol's view of the channel is consistent with the state
|
// Ensure that Carol's view of the channel is consistent with the state
|
||||||
// of the channel just before it was snapshotted.
|
// of the channel just before it was snapshotted.
|
||||||
checkCarolBalance(pushAmt - 3*paymentAmt)
|
carolChan = ht.AssertChannelLocalBalance(
|
||||||
checkCarolNumUpdatesAtLeast(1)
|
carol, chanPoint, pushAmt-3*paymentAmt,
|
||||||
|
)
|
||||||
|
checkCarolNumUpdatesAtLeast(carolChan, 1)
|
||||||
|
|
||||||
// Now query for Carol's channel state, it should show that she's at a
|
// Now query for Carol's channel state, it should show that she's at a
|
||||||
// state number in the past, *not* the latest state.
|
// state number in the past, *not* the latest state.
|
||||||
carolChan, err = getChanInfo(carol)
|
ht.AssertChannelCommitHeight(carol, chanPoint, carolStateNumPreCopy)
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to get carol chan info: %v", err)
|
|
||||||
}
|
|
||||||
if carolChan.NumUpdates != carolStateNumPreCopy {
|
|
||||||
t.Fatalf("db copy failed: %v", carolChan.NumUpdates)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now force Carol to execute a *force* channel closure by unilaterally
|
// Now force Carol to execute a *force* channel closure by unilaterally
|
||||||
// broadcasting her current channel state. This is actually the
|
// broadcasting her current channel state. This is actually the
|
||||||
// commitment transaction of a prior *revoked* state, so she'll soon
|
// commitment transaction of a prior *revoked* state, so she'll soon
|
||||||
// feel the wrath of Dave's retribution.
|
// feel the wrath of Dave's retribution.
|
||||||
force := true
|
closeUpdates, closeTxID := ht.CloseChannelAssertPending(
|
||||||
closeUpdates, closeTxID, err := net.CloseChannel(
|
carol, chanPoint, true,
|
||||||
carol, chanPoint, force,
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to close channel: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Query the mempool for the breaching closing transaction, this should
|
|
||||||
// be broadcast by Carol when she force closes the channel above.
|
|
||||||
txid, err := waitForTxInMempool(net.Miner.Client, minerMempoolTimeout)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to find Carol's force close tx in mempool: %v",
|
|
||||||
err)
|
|
||||||
}
|
|
||||||
if *txid != *closeTxID {
|
|
||||||
t.Fatalf("expected closeTx(%v) in mempool, instead found %v",
|
|
||||||
closeTxID, txid)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate a single block to mine the breach transaction.
|
// Generate a single block to mine the breach transaction.
|
||||||
block := mineBlocks(t, net, 1, 1)[0]
|
block := ht.MineBlocksAndAssertNumTxes(1, 1)[0]
|
||||||
|
|
||||||
// We resurrect Dave to ensure he will be exacting justice after his
|
// We resurrect Dave to ensure he will be exacting justice after his
|
||||||
// node restarts.
|
// node restarts.
|
||||||
if err := restartDave(); err != nil {
|
require.NoError(ht, restartDave(), "unable to restart Dave's node")
|
||||||
t.Fatalf("unable to stop Dave's node: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, wait for the final close status update, then ensure that
|
// Finally, wait for the final close status update, then ensure that
|
||||||
// the closing transaction was included in the block.
|
// the closing transaction was included in the block.
|
||||||
breachTXID, err := net.WaitForChannelClose(closeUpdates)
|
breachTXID := ht.WaitForChannelCloseEvent(closeUpdates)
|
||||||
if err != nil {
|
require.Equal(ht, closeTxID[:], breachTXID[:],
|
||||||
t.Fatalf("error while waiting for channel close: %v", err)
|
"expected breach ID to be equal to close ID")
|
||||||
}
|
ht.Miner.AssertTxInBlock(block, breachTXID)
|
||||||
if *breachTXID != *closeTxID {
|
|
||||||
t.Fatalf("expected breach ID(%v) to be equal to close ID (%v)",
|
|
||||||
breachTXID, closeTxID)
|
|
||||||
}
|
|
||||||
assertTxInBlock(t, block, breachTXID)
|
|
||||||
|
|
||||||
// Query the mempool for Dave's justice transaction, this should be
|
// Query the mempool for Dave's justice transaction, this should be
|
||||||
// broadcast as Carol's contract breaching transaction gets confirmed
|
// broadcast as Carol's contract breaching transaction gets confirmed
|
||||||
|
@ -556,24 +467,15 @@ func testRevokedCloseRetributionRemoteHodl(net *lntest.NetworkHarness,
|
||||||
// outputs to the second level before Dave broadcasts his justice tx,
|
// outputs to the second level before Dave broadcasts his justice tx,
|
||||||
// we'll search through the mempool for a tx that matches the number of
|
// we'll search through the mempool for a tx that matches the number of
|
||||||
// expected inputs in the justice tx.
|
// expected inputs in the justice tx.
|
||||||
var predErr error
|
|
||||||
var justiceTxid *chainhash.Hash
|
var justiceTxid *chainhash.Hash
|
||||||
errNotFound := errors.New("justice tx not found")
|
errNotFound := errors.New("justice tx not found")
|
||||||
findJusticeTx := func() (*chainhash.Hash, error) {
|
findJusticeTx := func() (*chainhash.Hash, error) {
|
||||||
mempool, err := net.Miner.Client.GetRawMempool()
|
mempool := ht.Miner.GetRawMempool()
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to get mempool from "+
|
|
||||||
"miner: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, txid := range mempool {
|
for _, txid := range mempool {
|
||||||
// Check that the justice tx has the appropriate number
|
// Check that the justice tx has the appropriate number
|
||||||
// of inputs.
|
// of inputs.
|
||||||
tx, err := net.Miner.Client.GetRawTransaction(txid)
|
tx := ht.Miner.GetRawTransaction(txid)
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to query for "+
|
|
||||||
"txs: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
exNumInputs := 2 + numInvoices
|
exNumInputs := 2 + numInvoices
|
||||||
if len(tx.MsgTx().TxIn) == exNumInputs {
|
if len(tx.MsgTx().TxIn) == exNumInputs {
|
||||||
|
@ -583,17 +485,17 @@ func testRevokedCloseRetributionRemoteHodl(net *lntest.NetworkHarness,
|
||||||
return nil, errNotFound
|
return nil, errNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
err = wait.Predicate(func() bool {
|
err := wait.NoError(func() error {
|
||||||
txid, err := findJusticeTx()
|
txid, err := findJusticeTx()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
predErr = err
|
return err
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
justiceTxid = txid
|
justiceTxid = txid
|
||||||
return true
|
|
||||||
|
return nil
|
||||||
}, defaultTimeout)
|
}, defaultTimeout)
|
||||||
if err != nil && predErr == errNotFound {
|
|
||||||
|
if err != nil && errors.Is(err, errNotFound) {
|
||||||
// If Dave is unable to broadcast his justice tx on first
|
// If Dave is unable to broadcast his justice tx on first
|
||||||
// attempt because of the second layer transactions, he will
|
// attempt because of the second layer transactions, he will
|
||||||
// wait until the next block epoch before trying again. Because
|
// wait until the next block epoch before trying again. Because
|
||||||
|
@ -602,35 +504,28 @@ func testRevokedCloseRetributionRemoteHodl(net *lntest.NetworkHarness,
|
||||||
// transactions will be in the mempool at this point, we pass 0
|
// transactions will be in the mempool at this point, we pass 0
|
||||||
// as the last argument, indicating we don't care what's in the
|
// as the last argument, indicating we don't care what's in the
|
||||||
// mempool.
|
// mempool.
|
||||||
mineBlocks(t, net, 1, 0)
|
ht.MineBlocks(1)
|
||||||
err = wait.Predicate(func() bool {
|
err = wait.NoError(func() error {
|
||||||
txid, err := findJusticeTx()
|
txid, err := findJusticeTx()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
predErr = err
|
return err
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
justiceTxid = txid
|
justiceTxid = txid
|
||||||
return true
|
|
||||||
|
return nil
|
||||||
}, defaultTimeout)
|
}, defaultTimeout)
|
||||||
}
|
}
|
||||||
if err != nil {
|
require.NoError(ht, err, "timeout finding justice tx")
|
||||||
t.Fatalf(predErr.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
justiceTx, err := net.Miner.Client.GetRawTransaction(justiceTxid)
|
justiceTx := ht.Miner.GetRawTransaction(justiceTxid)
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to query for justice tx: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// isSecondLevelSpend checks that the passed secondLevelTxid is a
|
// isSecondLevelSpend checks that the passed secondLevelTxid is a
|
||||||
// potentitial second level spend spending from the commit tx.
|
// potentitial second level spend spending from the commit tx.
|
||||||
isSecondLevelSpend := func(commitTxid, secondLevelTxid *chainhash.Hash) bool {
|
isSecondLevelSpend := func(commitTxid,
|
||||||
secondLevel, err := net.Miner.Client.GetRawTransaction(
|
secondLevelTxid *chainhash.Hash) bool {
|
||||||
secondLevelTxid)
|
|
||||||
if err != nil {
|
secondLevel := ht.Miner.GetRawTransaction(secondLevelTxid)
|
||||||
t.Fatalf("unable to query for tx: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A second level spend should have only one input, and one
|
// A second level spend should have only one input, and one
|
||||||
// output.
|
// output.
|
||||||
|
@ -661,27 +556,23 @@ func testRevokedCloseRetributionRemoteHodl(net *lntest.NetworkHarness,
|
||||||
if isSecondLevelSpend(breachTXID, &txIn.PreviousOutPoint.Hash) {
|
if isSecondLevelSpend(breachTXID, &txIn.PreviousOutPoint.Hash) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
t.Fatalf("justice tx not spending commitment utxo "+
|
require.Fail(ht, "justice tx not spending commitment utxo "+
|
||||||
"instead is: %v", txIn.PreviousOutPoint)
|
"instead is: %v", txIn.PreviousOutPoint)
|
||||||
}
|
}
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
|
|
||||||
// We restart Dave here to ensure that he persists he retribution state
|
// We restart Dave here to ensure that he persists he retribution state
|
||||||
// and successfully continues exacting retribution after restarting. At
|
// and successfully continues exacting retribution after restarting. At
|
||||||
// this point, Dave has broadcast the justice transaction, but it
|
// this point, Dave has broadcast the justice transaction, but it
|
||||||
// hasn't been confirmed yet; when Dave restarts, he should start
|
// hasn't been confirmed yet; when Dave restarts, he should start
|
||||||
// waiting for the justice transaction to confirm again.
|
// waiting for the justice transaction to confirm again.
|
||||||
if err := net.RestartNode(dave, nil); err != nil {
|
ht.RestartNode(dave)
|
||||||
t.Fatalf("unable to restart Dave's node: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now mine a block, this transaction should include Dave's justice
|
// Now mine a block, this transaction should include Dave's justice
|
||||||
// transaction which was just accepted into the mempool.
|
// transaction which was just accepted into the mempool.
|
||||||
block = mineBlocks(t, net, 1, 1)[0]
|
ht.MineBlocksAndAssertNumTxes(1, 1)
|
||||||
assertTxInBlock(t, block, justiceTxid)
|
|
||||||
|
|
||||||
// Dave should have no open channels.
|
// Dave should have no open channels.
|
||||||
assertNodeNumChannels(t, dave, 0)
|
ht.AssertNodeNumChannels(dave, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// testRevokedCloseRetributionAltruistWatchtower establishes a channel between
|
// testRevokedCloseRetributionAltruistWatchtower establishes a channel between
|
||||||
|
|
|
@ -72,10 +72,6 @@ var allTestCases = []*testCase{
|
||||||
name: "switch offline delivery outgoing offline",
|
name: "switch offline delivery outgoing offline",
|
||||||
test: testSwitchOfflineDeliveryOutgoingOffline,
|
test: testSwitchOfflineDeliveryOutgoingOffline,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "revoked uncooperative close retribution remote hodl",
|
|
||||||
test: testRevokedCloseRetributionRemoteHodl,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "revoked uncooperative close retribution altruist watchtower",
|
name: "revoked uncooperative close retribution altruist watchtower",
|
||||||
test: testRevokedCloseRetributionAltruistWatchtower,
|
test: testRevokedCloseRetributionAltruistWatchtower,
|
||||||
|
|
Loading…
Add table
Reference in a new issue