lnd_test: modify tests to work with on-chain spend registrations

This commit modifies the integration tests to work with the recent
 changes to the ChannelArbitrator, where it will only act on commitments
 that has been confirmed. Main changes involving when to look for
 transactions in the mempool and in blocks, and using the new RPC for
 getting channels in the "waiting close" phase when they are waiting for
 the commitment to confirm.
This commit is contained in:
Johan T. Halseth committed 2018-04-25 09:37:26 +02:00
1 parent 028d1b7e26
commit 8d5a33e349
1 file changed
+360 -171
+360 -171
View File
@@ -1109,8 +1109,17 @@ func testDisconnectingTargetPeer(net *lntest.NetworkHarness, t *harnessTest) {
// Disconnect Alice-peer from Bob-peer without getting error // Disconnect Alice-peer from Bob-peer without getting error
// about existing channels. // about existing channels.
var predErr error
err = lntest.WaitPredicate(func() bool {
if err := net.DisconnectNodes(ctxt, net.Alice, net.Bob); err != nil { if err := net.DisconnectNodes(ctxt, net.Alice, net.Bob); err != nil {
t.Fatalf("unable to disconnect Bob's peer from Alice's: err %v", err) predErr = err
return false
}
return true
}, time.Second*15)
if err != nil {
t.Fatalf("unable to disconnect Bob's peer from Alice's: err %v",
predErr)
} }
// Check zero peer connections. // Check zero peer connections.
@@ -1353,6 +1362,27 @@ func findForceClosedChannel(t *harnessTest,
return forceClose return forceClose
} }
// findWaitingCloseChannel searches a pending channel response for a particular
// channel, returning the waiting close channel upon success.
func findWaitingCloseChannel(t *harnessTest,
pendingChanResp *lnrpc.PendingChannelsResponse,
op *wire.OutPoint) *lnrpc.PendingChannelsResponse_WaitingCloseChannel {
var found bool
var waitingClose *lnrpc.PendingChannelsResponse_WaitingCloseChannel
for _, waitingClose = range pendingChanResp.WaitingCloseChannels {
if waitingClose.Channel.ChannelPoint == op.String() {
found = true
break
}
}
if !found {
t.Fatalf("channel not marked as waiting close")
}
return waitingClose
}
func assertCommitmentMaturity(t *harnessTest, func assertCommitmentMaturity(t *harnessTest,
forceClose *lnrpc.PendingChannelsResponse_ForceClosedChannel, forceClose *lnrpc.PendingChannelsResponse_ForceClosedChannel,
maturityHeight uint32, blocksTilMaturity int32) { maturityHeight uint32, blocksTilMaturity int32) {
@@ -1394,6 +1424,18 @@ func assertNumForceClosedChannels(t *harnessTest,
} }
} }
// assertNumWaitingCloseChannels checks that a pending channel response has the
// expected number of channels waiting for closing tx to confirm.
func assertNumWaitingCloseChannels(t *harnessTest,
pendingChanResp *lnrpc.PendingChannelsResponse, expectedNumChans int) {
if len(pendingChanResp.WaitingCloseChannels) != expectedNumChans {
t.Fatalf("expected to find %d channels waiting closure, got %d",
expectedNumChans,
len(pendingChanResp.WaitingCloseChannels))
}
}
// assertPendingHtlcStageAndMaturity uniformly tests all pending htlc's // assertPendingHtlcStageAndMaturity uniformly tests all pending htlc's
// belonging to a force closed channel, testing for the expected stage number, // belonging to a force closed channel, testing for the expected stage number,
// blocks till maturity, and the maturity height. // blocks till maturity, and the maturity height.
@@ -1574,13 +1616,13 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) {
} }
// Now that the channel has been force closed, it should show up in the // Now that the channel has been force closed, it should show up in the
// PendingChannels RPC under the force close section. // PendingChannels RPC under the waiting close section.
pendingChansRequest := &lnrpc.PendingChannelsRequest{} pendingChansRequest := &lnrpc.PendingChannelsRequest{}
pendingChanResp, err := net.Alice.PendingChannels(ctxb, pendingChansRequest) pendingChanResp, err := net.Alice.PendingChannels(ctxb, pendingChansRequest)
if err != nil { if err != nil {
t.Fatalf("unable to query for pending channels: %v", err) t.Fatalf("unable to query for pending channels: %v", err)
} }
assertNumForceClosedChannels(t, pendingChanResp, 1) assertNumWaitingCloseChannels(t, pendingChanResp, 1)
// Compute the outpoint of the channel, which we will use repeatedly to // Compute the outpoint of the channel, which we will use repeatedly to
// locate the pending channel information in the rpc responses. // locate the pending channel information in the rpc responses.
@@ -1597,21 +1639,12 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) {
Index: chanPoint.OutputIndex, Index: chanPoint.OutputIndex,
} }
forceClose := findForceClosedChannel(t, pendingChanResp, &op) waitingClose := findWaitingCloseChannel(t, pendingChanResp, &op)
// Immediately after force closing, all of the funds should be in limbo, // Immediately after force closing, all of the funds should be in limbo.
// and the pending channels response should not indicate that any funds if waitingClose.LimboBalance == 0 {
// have been recovered.
if forceClose.LimboBalance == 0 {
t.Fatalf("all funds should still be in limbo") t.Fatalf("all funds should still be in limbo")
} }
if forceClose.RecoveredBalance != 0 {
t.Fatalf("no funds should yet be shown as recovered")
}
// The commitment transaction has not been confirmed, so we expect to
// see a maturity height and blocks til maturity of 0.
assertCommitmentMaturity(t, forceClose, 0, 0)
// The several restarts in this test are intended to ensure that when a // The several restarts in this test are intended to ensure that when a
// channel is force-closed, the UTXO nursery has persisted the state of // channel is force-closed, the UTXO nursery has persisted the state of
@@ -1639,13 +1672,15 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) {
duration := time.Millisecond * 300 duration := time.Millisecond * 300
time.Sleep(duration) time.Sleep(duration)
// Now that the commitment has been confirmed, the channel should be
// marked as force closed.
pendingChanResp, err = net.Alice.PendingChannels(ctxb, pendingChansRequest) pendingChanResp, err = net.Alice.PendingChannels(ctxb, pendingChansRequest)
if err != nil { if err != nil {
t.Fatalf("unable to query for pending channels: %v", err) t.Fatalf("unable to query for pending channels: %v", err)
} }
assertNumForceClosedChannels(t, pendingChanResp, 1) assertNumForceClosedChannels(t, pendingChanResp, 1)
forceClose = findForceClosedChannel(t, pendingChanResp, &op) forceClose := findForceClosedChannel(t, pendingChanResp, &op)
// Now that the channel has been force closed, it should now have the // Now that the channel has been force closed, it should now have the
// height and number of blocks to confirm populated. // height and number of blocks to confirm populated.
@@ -4224,11 +4259,24 @@ func testRevokedCloseRetributionZeroValueRemoteOutput(net *lntest.NetworkHarness
// commitment transaction of a prior *revoked* state, so he'll soon // commitment transaction of a prior *revoked* state, so he'll soon
// feel the wrath of Alice's retribution. // feel the wrath of Alice's retribution.
force := true force := true
closeUpdates, _, err := net.CloseChannel(ctxb, carol, chanPoint, force) closeUpdates, closeTxId, err := net.CloseChannel(ctxb, carol,
chanPoint, force)
if err != nil { if err != nil {
t.Fatalf("unable to close channel: %v", err) 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.Node, 20*time.Second)
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)
}
// Finally, generate a single block, wait for the final close status // Finally, generate a single block, wait for the final close status
// update, then ensure that the closing transaction was included in the // update, then ensure that the closing transaction was included in the
// block. // block.
@@ -4545,17 +4593,22 @@ func testRevokedCloseRetributionRemoteHodl(net *lntest.NetworkHarness,
// 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 force := true
closeUpdates, _, err := net.CloseChannel(ctxb, carol, chanPoint, force) closeUpdates, closeTxId, err := net.CloseChannel(ctxb, carol,
chanPoint, force)
if err != nil { if err != nil {
t.Fatalf("unable to close channel: %v", err) t.Fatalf("unable to close channel: %v", err)
} }
// Query the mempool for Dave's justice transaction, this should be // Query the mempool for the breaching closing transaction, this should
// broadcast as Carol's contract breaching transaction gets confirmed // be broadcast by Carol when she force closes the channel above.
// above. txid, err := waitForTxInMempool(net.Miner.Node, 20*time.Second)
_, err = waitForTxInMempool(net.Miner.Node, 5*time.Second)
if err != nil { if err != nil {
t.Fatalf("unable to find Dave's justice tx in mempool: %v", err) 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)
} }
time.Sleep(200 * time.Millisecond) time.Sleep(200 * time.Millisecond)
@@ -4577,14 +4630,101 @@ func testRevokedCloseRetributionRemoteHodl(net *lntest.NetworkHarness,
if err != nil { if err != nil {
t.Fatalf("error while waiting for channel close: %v", err) t.Fatalf("error while waiting for channel close: %v", err)
} }
if *breachTXID != *closeTxId {
t.Fatalf("expected breach ID(%v) to be equal to close ID (%v)",
breachTXID, closeTxId)
}
assertTxInBlock(t, block, breachTXID) 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
// above. // above. Since Carol might have had the time to take some of the HTLC
justiceTXID, err := waitForTxInMempool(net.Miner.Node, 5*time.Second) // outputs to the second level before Alice broadcasts her justice tx,
// we'll search through the mempool for a tx that matches the number of
// expected inputs in the justice tx.
// TODO(halseth): change to deterministic check if/when only acting on
// confirmed second level spends?
var predErr error
var justiceTxid *chainhash.Hash
err = lntest.WaitPredicate(func() bool {
mempool, err := net.Miner.Node.GetRawMempool()
if err != nil { if err != nil {
t.Fatalf("unable to find Dave's justice tx in mempool: %v", err) t.Fatalf("unable to get mempool from miner: %v", err)
}
for _, txid := range mempool {
// Check that the justice tx has the appropriate number
// of inputs.
tx, err := net.Miner.Node.GetRawTransaction(txid)
if err != nil {
predErr = fmt.Errorf("unable to query for "+
"txs: %v", err)
return false
}
exNumInputs := 2 + numInvoices
if len(tx.MsgTx().TxIn) == exNumInputs {
justiceTxid = txid
return true
}
}
predErr = fmt.Errorf("justice tx not found")
return false
}, time.Second*15)
if err != nil {
t.Fatalf(predErr.Error())
}
justiceTx, err := net.Miner.Node.GetRawTransaction(justiceTxid)
if err != nil {
t.Fatalf("unable to query for justice tx: %v", err)
}
// isSecondLevelSpend checks that the passed secondLevelTxid is a
// potentitial second level spend spending from the commit tx.
isSecondLevelSpend := func(commitTxid, secondLevelTxid *chainhash.Hash) bool {
secondLevel, err := net.Miner.Node.GetRawTransaction(
secondLevelTxid)
if err != nil {
t.Fatalf("unable to query for tx: %v", err)
}
// A second level spend should have only one input, and one
// output.
if len(secondLevel.MsgTx().TxIn) != 1 {
return false
}
if len(secondLevel.MsgTx().TxOut) != 1 {
return false
}
// The sole input should be spending from the commit tx.
txIn := secondLevel.MsgTx().TxIn[0]
if !bytes.Equal(txIn.PreviousOutPoint.Hash[:], commitTxid[:]) {
return false
}
return true
}
// Check that all the inputs of this transaction are spending outputs
// generated by Carol's breach transaction above.
for _, txIn := range justiceTx.MsgTx().TxIn {
if bytes.Equal(txIn.PreviousOutPoint.Hash[:], breachTXID[:]) {
continue
}
// If the justice tx is spending from an output that was not on
// the breach tx, Carol might have had the time to take an
// output to the second level. In that case, check that the
// justice tx is spending this second level output.
if isSecondLevelSpend(breachTXID, &txIn.PreviousOutPoint.Hash) {
continue
}
t.Fatalf("justice tx not spending commitment utxo "+
"instead is: %v", txIn.PreviousOutPoint)
} }
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
@@ -4597,36 +4737,12 @@ func testRevokedCloseRetributionRemoteHodl(net *lntest.NetworkHarness,
t.Fatalf("unable to restart Dave's node: %v", err) t.Fatalf("unable to restart Dave's node: %v", err)
} }
// Query for the mempool transaction found above. Then assert that (1)
// the justice tx has the appropriate number of inputs, and (2) all the
// inputs of this transaction are spending outputs generated by Carol's
// breach transaction above, and also the HTLCs from Carol to Dave.
justiceTx, err := net.Miner.Node.GetRawTransaction(justiceTXID)
if err != nil {
t.Fatalf("unable to query for justice tx: %v", err)
}
exNumInputs := 2 + numInvoices
if len(justiceTx.MsgTx().TxIn) != exNumInputs {
t.Fatalf("justice tx should have exactly 2 commitment inputs"+
"and %v htlc inputs, expected %v in total, got %v",
numInvoices/2, exNumInputs,
len(justiceTx.MsgTx().TxIn))
}
// 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)[0] block = mineBlocks(t, net, 1)[0]
assertTxInBlock(t, block, justiceTxid)
// The block should have exactly *two* transactions, one of which is // Dave should have no open channels.
// the justice transaction.
if len(block.Transactions) != 2 {
t.Fatalf("transaction wasn't mined")
}
justiceSha := block.Transactions[1].TxHash()
if !bytes.Equal(justiceTx.Hash()[:], justiceSha[:]) {
t.Fatalf("justice tx wasn't mined")
}
assertNodeNumChannels(t, ctxb, dave, 0) assertNodeNumChannels(t, ctxb, dave, 0)
} }
@@ -6026,6 +6142,9 @@ func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest) {
}, },
) )
// Mine a block to confirm the closing transaction.
mineBlocks(t, net, 1)
// At this point, Bob should have cancelled backwards the dust HTLC // At this point, Bob should have cancelled backwards the dust HTLC
// that we sent earlier. This means Alice should now only have a single // that we sent earlier. This means Alice should now only have a single
// HTLC on her channel. // HTLC on her channel.
@@ -6056,7 +6175,7 @@ func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest) {
// The second layer HTLC timeout transaction should now have been // The second layer HTLC timeout transaction should now have been
// broadcast on-chain. // broadcast on-chain.
_, err = waitForTxInMempool(net.Miner.Node, time.Second*10) secondLayerHash, err := waitForTxInMempool(net.Miner.Node, time.Second*10)
if err != nil { if err != nil {
t.Fatalf("unable to find bob's second layer transaction") t.Fatalf("unable to find bob's second layer transaction")
} }
@@ -6083,13 +6202,12 @@ func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest) {
} }
// Now we'll mine an additional block. // Now we'll mine an additional block.
if _, err := net.Miner.Node.Generate(1); err != nil { block := mineBlocks(t, net, 1)[0]
t.Fatalf("unable to generate blocks: %v", err)
}
// The block should have confirmed Bob's second layer sweeping // The block should have confirmed Bob's second layer sweeping
// transaction. Therefore, at this point, there should be no active // transaction. Therefore, at this point, there should be no active
// HTLC's on the commitment transaction from Alice -> Bob. // HTLC's on the commitment transaction from Alice -> Bob.
assertTxInBlock(t, block, secondLayerHash)
nodes = []*lntest.HarnessNode{net.Alice} nodes = []*lntest.HarnessNode{net.Alice}
err = lntest.WaitPredicate(func() bool { err = lntest.WaitPredicate(func() bool {
return assertNumActiveHtlcs(nodes, 0) return assertNumActiveHtlcs(nodes, 0)
@@ -6222,12 +6340,11 @@ func testMultiHopReceiverChainClaim(net *lntest.NetworkHarness, t *harnessTest)
// At this point, Carol should broadcast her active commitment // At this point, Carol should broadcast her active commitment
// transaction in order to go to the chain and sweep her HTLC. // transaction in order to go to the chain and sweep her HTLC.
// Additionally, Carol's should have broadcast her second layer sweep txids, err := waitForNTxsInMempool(net.Miner.Node, 1, time.Second*20)
// transaction for the HTLC as well.
txids, err := waitForNTxsInMempool(net.Miner.Node, 2, time.Second*15)
if err != nil { if err != nil {
t.Fatalf("transactions not found in mempool: %v", err) t.Fatalf("expected transaction not found in mempool: %v", err)
} }
txidHash, err := getChanPointFundingTxid(bobChanPoint) txidHash, err := getChanPointFundingTxid(bobChanPoint)
if err != nil { if err != nil {
t.Fatalf("unable to get txid: %v", err) t.Fatalf("unable to get txid: %v", err)
@@ -6243,42 +6360,53 @@ func testMultiHopReceiverChainClaim(net *lntest.NetworkHarness, t *harnessTest)
Index: bobChanPoint.OutputIndex, Index: bobChanPoint.OutputIndex,
} }
tx1, err := net.Miner.Node.GetRawTransaction(txids[0]) // The commitment transaction should be spending from the funding
// transaction.
commitHash := txids[0]
tx, err := net.Miner.Node.GetRawTransaction(commitHash)
if err != nil { if err != nil {
t.Fatalf("unable to get txn: %v", err) t.Fatalf("unable to get txn: %v", err)
} }
tx1Hash := tx1.MsgTx().TxHash() commitTx := tx.MsgTx()
tx2, err := net.Miner.Node.GetRawTransaction(txids[1])
if err != nil {
t.Fatalf("unable to get txn: %v", err)
}
tx2Hash := tx2.MsgTx().TxHash()
// Of the two transactions, one should be spending from the funding if commitTx.TxIn[0].PreviousOutPoint != carolFundingPoint {
// transaction, and the second transaction should then be spending from t.Fatalf("commit transaction not spending from expected "+
"outpoint: %v", spew.Sdump(commitTx))
}
// Confirm the commitment.
mineBlocks(t, net, 1)
// After the force close transaction is mined, Carol should broadcast
// her second level HTLC transaction. Bob will broadcast a sweep tx to
// sweep his output in the channel with Carol. When Bob notices Carol's
// second level transaction in the mempool, he will extract the
// preimage and settle the HTLC back off-chain.
secondLevelHashes, err := waitForNTxsInMempool(net.Miner.Node, 2,
time.Second*15)
if err != nil {
t.Fatalf("transactions not found in mempool: %v", err)
}
// Carol's second level transaction should be spending from
// the commitment transaction. // the commitment transaction.
var commitHash *chainhash.Hash var secondLevelHash *chainhash.Hash
if tx1.MsgTx().TxIn[0].PreviousOutPoint == carolFundingPoint { for _, txid := range secondLevelHashes {
commitHash = &tx1Hash tx, err := net.Miner.Node.GetRawTransaction(txid)
if tx2.MsgTx().TxIn[0].PreviousOutPoint.Hash != *commitHash { if err != nil {
t.Fatalf("second transaction not spending commit tx: %v", t.Fatalf("unable to get txn: %v", err)
spew.Sdump(tx2)) }
if tx.MsgTx().TxIn[0].PreviousOutPoint.Hash == *commitHash {
secondLevelHash = txid
} }
} }
if tx2.MsgTx().TxIn[0].PreviousOutPoint == carolFundingPoint { if secondLevelHash == nil {
commitHash = &tx2Hash t.Fatalf("Carol's second level tx not found")
if tx1.MsgTx().TxIn[0].PreviousOutPoint.Hash != *commitHash {
t.Fatalf("second transaction not spending commit tx: %v",
spew.Sdump(tx1))
}
}
if commitHash == nil {
t.Fatalf("commit tx not found in mempool")
} }
// We'll now mine an additional block which should confirm both the // We'll now mine an additional block which should confirm both the
// second layer transaction as well as the commitment transaction // second layer transactions.
// itself.
if _, err := net.Miner.Node.Generate(1); err != nil { if _, err := net.Miner.Node.Generate(1); err != nil {
t.Fatalf("unable to generate block: %v", err) t.Fatalf("unable to generate block: %v", err)
} }
@@ -6438,17 +6566,32 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
// At this point, Bob should have a pending force close channel as he // At this point, Bob should have a pending force close channel as he
// just went to chain. // just went to chain.
pendingChansRequest := &lnrpc.PendingChannelsRequest{} pendingChansRequest := &lnrpc.PendingChannelsRequest{}
pendingChanResp, err := net.Bob.PendingChannels(ctxb, pendingChansRequest) err = lntest.WaitPredicate(func() bool {
pendingChanResp, err := net.Bob.PendingChannels(ctxb,
pendingChansRequest)
if err != nil { if err != nil {
t.Fatalf("unable to query for pending channels: %v", err) predErr = fmt.Errorf("unable to query for pending "+
"channels: %v", err)
return false
} }
if len(pendingChanResp.PendingForceClosingChannels) == 0 { if len(pendingChanResp.PendingForceClosingChannels) == 0 {
t.Fatalf("bob should have pending for close chan but doesn't") predErr = fmt.Errorf("bob should have pending for " +
"close chan but doesn't")
return false
} }
forceCloseChan := pendingChanResp.PendingForceClosingChannels[0] forceCloseChan := pendingChanResp.PendingForceClosingChannels[0]
if forceCloseChan.LimboBalance == 0 { if forceCloseChan.LimboBalance == 0 {
t.Fatalf("bob should have nonzero limbo balance instead "+ predErr = fmt.Errorf("bob should have nonzero limbo "+
"has: %v", forceCloseChan.LimboBalance) "balance instead has: %v",
forceCloseChan.LimboBalance)
return false
}
return true
}, time.Second*15)
if err != nil {
t.Fatalf(predErr.Error())
} }
// We'll now mine enough blocks for the HTLC to expire. After this, Bob // We'll now mine enough blocks for the HTLC to expire. After this, Bob
@@ -6470,12 +6613,12 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
} }
if len(pendingChanResp.PendingForceClosingChannels) == 0 { if len(pendingChanResp.PendingForceClosingChannels) == 0 {
predErr = fmt.Errorf("bob should have pending for " + predErr = fmt.Errorf("bob should have pending force " +
"close chan but doesn't") "close chan but doesn't")
return false return false
} }
forceCloseChan = pendingChanResp.PendingForceClosingChannels[0] forceCloseChan := pendingChanResp.PendingForceClosingChannels[0]
if len(forceCloseChan.PendingHtlcs) != 1 { if len(forceCloseChan.PendingHtlcs) != 1 {
predErr = fmt.Errorf("bob should have pending htlc " + predErr = fmt.Errorf("bob should have pending htlc " +
"but doesn't") "but doesn't")
@@ -6534,7 +6677,7 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
return false return false
} }
forceCloseChan = pendingChanResp.PendingForceClosingChannels[0] forceCloseChan := pendingChanResp.PendingForceClosingChannels[0]
if len(forceCloseChan.PendingHtlcs) != 1 { if len(forceCloseChan.PendingHtlcs) != 1 {
predErr = fmt.Errorf("bob should have pending htlc " + predErr = fmt.Errorf("bob should have pending htlc " +
"but doesn't") "but doesn't")
@@ -6570,7 +6713,7 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
// At this point, Bob should no longer show any channels as pending // At this point, Bob should no longer show any channels as pending
// close. // close.
err = lntest.WaitPredicate(func() bool { err = lntest.WaitPredicate(func() bool {
pendingChanResp, err = net.Bob.PendingChannels( pendingChanResp, err := net.Bob.PendingChannels(
ctxb, pendingChansRequest, ctxb, pendingChansRequest,
) )
if err != nil { if err != nil {
@@ -6665,12 +6808,25 @@ func testMultHopRemoteForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
// At this point, Bob should have a pending force close channel as // At this point, Bob should have a pending force close channel as
// Carol has gone directly to chain. // Carol has gone directly to chain.
pendingChansRequest := &lnrpc.PendingChannelsRequest{} pendingChansRequest := &lnrpc.PendingChannelsRequest{}
pendingChanResp, err := net.Bob.PendingChannels(ctxb, pendingChansRequest) err = lntest.WaitPredicate(func() bool {
pendingChanResp, err := net.Bob.PendingChannels(
ctxb, pendingChansRequest,
)
if err != nil { if err != nil {
t.Fatalf("unable to query for pending channels: %v", err) predErr = fmt.Errorf("unable to query for "+
"pending channels: %v", err)
return false
} }
if len(pendingChanResp.PendingForceClosingChannels) == 0 { if len(pendingChanResp.PendingForceClosingChannels) == 0 {
t.Fatalf("bob should have pending for close chan but doesn't") predErr = fmt.Errorf("bob should have pending " +
"force close channels but doesn't")
return false
}
return true
}, time.Second*15)
if err != nil {
t.Fatalf(predErr.Error())
} }
// Next, we'll mine enough blocks for the HTLC to expire. At this // Next, we'll mine enough blocks for the HTLC to expire. At this
@@ -6750,7 +6906,7 @@ func testMultHopRemoteForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
// commitment, he doesn't have to wait for any CSV delays. As a result, // commitment, he doesn't have to wait for any CSV delays. As a result,
// he should show no additional pending transactions. // he should show no additional pending transactions.
err = lntest.WaitPredicate(func() bool { err = lntest.WaitPredicate(func() bool {
pendingChanResp, err = net.Bob.PendingChannels( pendingChanResp, err := net.Bob.PendingChannels(
ctxb, pendingChansRequest, ctxb, pendingChansRequest,
) )
if err != nil { if err != nil {
@@ -6846,9 +7002,8 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest)
t.Fatalf("unable to generate blocks") t.Fatalf("unable to generate blocks")
} }
// Carol's commitment transaction should now be in the mempool. She // Carol's commitment transaction should now be in the mempool.
// should also have broadcast her second level HTLC transaction. txids, err := waitForNTxsInMempool(net.Miner.Node, 1, time.Second*15)
txids, err := waitForNTxsInMempool(net.Miner.Node, 2, time.Second*15)
if err != nil { if err != nil {
t.Fatalf("transactions not found in mempool: %v", err) t.Fatalf("transactions not found in mempool: %v", err)
} }
@@ -6865,50 +7020,52 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest)
Index: bobChanPoint.OutputIndex, Index: bobChanPoint.OutputIndex,
} }
// Of the two transactions, one should be spending from the funding // The tx should be spending from the funding transaction,
// transaction, and the second transaction should then be spending from commitHash := txids[0]
// the commitment transaction. tx1, err := net.Miner.Node.GetRawTransaction(commitHash)
var commitHash *chainhash.Hash
tx1, err := net.Miner.Node.GetRawTransaction(txids[0])
if err != nil { if err != nil {
t.Fatalf("unable to get txn: %v", err) t.Fatalf("unable to get txn: %v", err)
} }
tx1Hash := tx1.MsgTx().TxHash() if tx1.MsgTx().TxIn[0].PreviousOutPoint != carolFundingPoint {
tx2, err := net.Miner.Node.GetRawTransaction(txids[1]) t.Fatalf("commit transaction not spending fundingtx: %v",
if err != nil {
t.Fatalf("unable to get txn: %v", err)
}
tx2Hash := tx2.MsgTx().TxHash()
if tx1.MsgTx().TxIn[0].PreviousOutPoint == carolFundingPoint {
commitHash = &tx1Hash
if tx2.MsgTx().TxIn[0].PreviousOutPoint.Hash != *commitHash {
t.Fatalf("second transaction not spending commit tx: %v",
spew.Sdump(tx2))
}
}
if tx2.MsgTx().TxIn[0].PreviousOutPoint == carolFundingPoint {
commitHash = &tx2Hash
if tx1.MsgTx().TxIn[0].PreviousOutPoint.Hash != *commitHash {
t.Fatalf("second transaction not spending commit tx: %v",
spew.Sdump(tx1)) spew.Sdump(tx1))
} }
}
if commitHash == nil {
t.Fatalf("commit tx not found in mempool")
}
// We'll now mine a block which should confirm both the second layer // Mine a block that should confirm the commit tx.
// transaction as well as the commitment transaction. block := mineBlocks(t, net, 1)[0]
if _, err := net.Miner.Node.Generate(1); err != nil { if len(block.Transactions) != 2 {
t.Fatalf("unable to generate block: %v", err) t.Fatalf("expected 2 transactions in block, got %v",
len(block.Transactions))
} }
assertTxInBlock(t, block, commitHash)
// At this point, Bob should detect that Carol has revealed the // After the force close transacion is mined, Carol should broadcast
// preimage on-chain. As a result, he should now attempt to broadcast // her second level HTLC transacion. Bob will braodcast a sweep tx to
// his second-layer claim transaction to claim the output. // sweep his output in the channel with Carol. When Bob notices Carol's
_, err = waitForTxInMempool(net.Miner.Node, time.Second*10) // second level transaction in the mempool, he will extract the
// preimage and broadcast a second level tx to claim the HTLC in his
// (already closed) channel with Alice.
secondLevelHashes, err := waitForNTxsInMempool(net.Miner.Node, 3,
time.Second*20)
if err != nil { if err != nil {
t.Fatalf("unable to find bob's sweeping transaction") t.Fatalf("transactions not found in mempool: %v", err)
}
// Carol's second level transaction should be spending from
// the commitment transaction.
var secondLevelHash *chainhash.Hash
for _, txid := range secondLevelHashes {
tx, err := net.Miner.Node.GetRawTransaction(txid)
if err != nil {
t.Fatalf("unable to get txn: %v", err)
}
if tx.MsgTx().TxIn[0].PreviousOutPoint.Hash == *commitHash {
secondLevelHash = txid
}
}
if secondLevelHash == nil {
t.Fatalf("Carol's second level tx not found")
} }
// At this point, Bob should have broadcast his second layer success // At this point, Bob should have broadcast his second layer success
@@ -6955,6 +7112,15 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest)
t.Fatalf("bob didn't hand off time-locked HTLC: %v", predErr) t.Fatalf("bob didn't hand off time-locked HTLC: %v", predErr)
} }
// We'll now mine a block which should confirm the two second layer
// transactions and the commit sweep.
block = mineBlocks(t, net, 1)[0]
if len(block.Transactions) != 4 {
t.Fatalf("expected 4 transactions in block, got %v",
len(block.Transactions))
}
assertTxInBlock(t, block, secondLevelHash)
// If we then mine 4 additional blocks, Bob should pull the output // If we then mine 4 additional blocks, Bob should pull the output
// destined for him. // destined for him.
if _, err := net.Miner.Node.Generate(defaultCSV); err != nil { if _, err := net.Miner.Node.Generate(defaultCSV); err != nil {
@@ -7007,6 +7173,8 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest
timeout := time.Duration(time.Second * 15) timeout := time.Duration(time.Second * 15)
ctxb := context.Background() ctxb := context.Background()
defaultCSV := uint32(4)
// First, we'll create a three hop network: Alice -> Bob -> Carol, with // First, we'll create a three hop network: Alice -> Bob -> Carol, with
// Carol refusing to actually settle or directly cancel any HTLC's // Carol refusing to actually settle or directly cancel any HTLC's
// self. // self.
@@ -7065,9 +7233,8 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest
t.Fatalf("unable to generate blocks") t.Fatalf("unable to generate blocks")
} }
// Carol's commitment transaction should now be in the mempool. She // Carol's commitment transaction should now be in the mempool.
// should also have broadcast her second level HTLC transaction. txids, err := waitForNTxsInMempool(net.Miner.Node, 1, time.Second*15)
txids, err := waitForNTxsInMempool(net.Miner.Node, 2, time.Second*15)
if err != nil { if err != nil {
t.Fatalf("transactions not found in mempool: %v", err) t.Fatalf("transactions not found in mempool: %v", err)
} }
@@ -7084,48 +7251,70 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest
Index: bobChanPoint.OutputIndex, Index: bobChanPoint.OutputIndex,
} }
// Of the two transactions, one should be spending from the funding // The transaction should be spending from the funding transaction
// transaction, and the second transaction should then be spending from commitHash := txids[0]
// the commitment transaction. tx1, err := net.Miner.Node.GetRawTransaction(commitHash)
var commitHash *chainhash.Hash
tx1, err := net.Miner.Node.GetRawTransaction(txids[0])
if err != nil { if err != nil {
t.Fatalf("unable to get txn: %v", err) t.Fatalf("unable to get txn: %v", err)
} }
tx1Hash := tx1.MsgTx().TxHash() if tx1.MsgTx().TxIn[0].PreviousOutPoint != carolFundingPoint {
tx2, err := net.Miner.Node.GetRawTransaction(txids[1]) t.Fatalf("commit transaction not spending fundingtx: %v",
if err != nil {
t.Fatalf("unable to get txn: %v", err)
}
tx2Hash := tx2.MsgTx().TxHash()
if tx1.MsgTx().TxIn[0].PreviousOutPoint == carolFundingPoint {
commitHash = &tx1Hash
if tx2.MsgTx().TxIn[0].PreviousOutPoint.Hash != *commitHash {
t.Fatalf("second transaction not spending commit tx: %v",
spew.Sdump(tx2))
}
}
if tx2.MsgTx().TxIn[0].PreviousOutPoint == carolFundingPoint {
commitHash = &tx2Hash
if tx1.MsgTx().TxIn[0].PreviousOutPoint.Hash != *commitHash {
t.Fatalf("second transaction not spending commit tx: %v",
spew.Sdump(tx1)) spew.Sdump(tx1))
} }
// Mine a block, which should contain the commitment.
block := mineBlocks(t, net, 1)[0]
if len(block.Transactions) != 2 {
t.Fatalf("expected 2 transactions in block, got %v",
len(block.Transactions))
} }
if commitHash == nil { assertTxInBlock(t, block, commitHash)
t.Fatalf("commit tx not found in mempool")
// After the force close transacion is mined, Carol should broadcast
// her second level HTLC transacion. Bob will braodcast a sweep tx to
// sweep his output in the channel with Carol. When Bob notices Carol's
// second level transaction in the mempool, he will extract the
// preimage and broadcast a second level tx to claim the HTLC in his
// (already closed) channel with Alice.
secondLevelHashes, err := waitForNTxsInMempool(net.Miner.Node, 3,
time.Second*20)
if err != nil {
t.Fatalf("transactions not found in mempool: %v", err)
} }
// We'll now mine a block which should confirm both the second layer // Carol's second level transaction should be spending from
// transaction as well as the commitment transaction. // the commitment transaction.
if _, err := net.Miner.Node.Generate(1); err != nil { var secondLevelHash *chainhash.Hash
for _, txid := range secondLevelHashes {
tx, err := net.Miner.Node.GetRawTransaction(txid)
if err != nil {
t.Fatalf("unable to get txn: %v", err)
}
if tx.MsgTx().TxIn[0].PreviousOutPoint.Hash == *commitHash {
secondLevelHash = txid
}
}
if secondLevelHash == nil {
t.Fatalf("Carol's second level tx not found")
}
// We'll now mine a block which should confirm the two second layer
// transactions and the commit sweep.
block = mineBlocks(t, net, 1)[0]
if len(block.Transactions) != 4 {
t.Fatalf("expected 4 transactions in block, got %v",
len(block.Transactions))
}
assertTxInBlock(t, block, secondLevelHash)
// If we then mine 4 additional blocks, Bob should pull the output
// destined for him.
if _, err := net.Miner.Node.Generate(defaultCSV); err != nil {
t.Fatalf("unable to generate block: %v", err) t.Fatalf("unable to generate block: %v", err)
} }
// With the block mined above, Bob should detect that Carol is _, err = waitForNTxsInMempool(net.Miner.Node, 1, time.Second*15)
// attempting to sweep the HTLC on-chain, and should obtain the
// preimage.
_, err = waitForNTxsInMempool(net.Miner.Node, 2, time.Second*15)
if err != nil { if err != nil {
t.Fatalf("unable to find bob's sweeping transaction") t.Fatalf("unable to find bob's sweeping transaction")
} }