contractcourt/contract_resolvers: make htlcOutgoingContestResolver act on conf
This commit is contained in:
parent
b3e17aa67b
commit
9fffe23696
@ -792,7 +792,7 @@ func (h *htlcOutgoingContestResolver) Resolve() (ContractResolver, error) {
|
|||||||
// the remote party sweeps with the pre-image, we'll be notified.
|
// the remote party sweeps with the pre-image, we'll be notified.
|
||||||
spendNtfn, err := h.Notifier.RegisterSpendNtfn(
|
spendNtfn, err := h.Notifier.RegisterSpendNtfn(
|
||||||
&outPointToWatch,
|
&outPointToWatch,
|
||||||
h.broadcastHeight, true,
|
h.broadcastHeight, false,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
270
lnd_test.go
270
lnd_test.go
@ -8276,7 +8276,8 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest)
|
|||||||
// At this point, Bob decides that he wants to exit the channel
|
// At this point, Bob decides that he wants to exit the channel
|
||||||
// immediately, so he force closes his commitment transaction.
|
// immediately, so he force closes his commitment transaction.
|
||||||
ctxt, _ := context.WithTimeout(ctxb, timeout)
|
ctxt, _ := context.WithTimeout(ctxb, timeout)
|
||||||
closeChannelAndAssert(ctxt, t, net, net.Bob, aliceChanPoint, true)
|
bobForceClose := closeChannelAndAssert(ctxt, t, net, net.Bob,
|
||||||
|
aliceChanPoint, true)
|
||||||
|
|
||||||
// We'll now mine enough blocks so Carol decides that she needs to go
|
// We'll now mine enough blocks so Carol decides that she needs to go
|
||||||
// on-chain to claim the HTLC as Bob has been inactive.
|
// on-chain to claim the HTLC as Bob has been inactive.
|
||||||
@ -8324,31 +8325,58 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest)
|
|||||||
|
|
||||||
// After the force close transacion is mined, Carol should broadcast
|
// After the force close transacion is mined, Carol should broadcast
|
||||||
// her second level HTLC transacion. Bob will braodcast a sweep tx to
|
// 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
|
// sweep his output in the channel with Carol. He can do this
|
||||||
// second level transaction in the mempool, he will extract the
|
// immediately, as the output is not timelocked since Carol was the one
|
||||||
// preimage and broadcast a second level tx to claim the HTLC in his
|
// force closing.
|
||||||
// (already closed) channel with Alice.
|
commitSpends, err := waitForNTxsInMempool(net.Miner.Node, 2,
|
||||||
secondLevelHashes, err := waitForNTxsInMempool(net.Miner.Node, 3,
|
|
||||||
time.Second*20)
|
time.Second*20)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("transactions not found in mempool: %v", err)
|
t.Fatalf("transactions not found in mempool: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carol's second level transaction should be spending from
|
// Both Carol's second level transaction and Bob's sweep should be
|
||||||
// the commitment transaction.
|
// spending from the commitment transaction.
|
||||||
var secondLevelHash *chainhash.Hash
|
for _, txid := range commitSpends {
|
||||||
for _, txid := range secondLevelHashes {
|
|
||||||
tx, err := net.Miner.Node.GetRawTransaction(txid)
|
tx, err := net.Miner.Node.GetRawTransaction(txid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to get txn: %v", err)
|
t.Fatalf("unable to get txn: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if tx.MsgTx().TxIn[0].PreviousOutPoint.Hash == *commitHash {
|
if tx.MsgTx().TxIn[0].PreviousOutPoint.Hash != *commitHash {
|
||||||
secondLevelHash = txid
|
t.Fatalf("tx did not spend from commitment tx")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if secondLevelHash == nil {
|
|
||||||
t.Fatalf("Carol's second level tx not found")
|
// Mine a block to confirm the two transactions (+ the coinbase).
|
||||||
|
block = mineBlocks(t, net, 1)[0]
|
||||||
|
if len(block.Transactions) != 3 {
|
||||||
|
t.Fatalf("expected 3 transactions in block, got %v",
|
||||||
|
len(block.Transactions))
|
||||||
|
}
|
||||||
|
for _, txid := range commitSpends {
|
||||||
|
assertTxInBlock(t, block, txid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep track of the second level tx maturity.
|
||||||
|
carolSecondLevelCSV := defaultCSV
|
||||||
|
|
||||||
|
// When Bob notices Carol's second level transaction in the block, he
|
||||||
|
// will extract the preimage and broadcast a second level tx to claim
|
||||||
|
// the HTLC in his (already closed) channel with Alice.
|
||||||
|
bobSecondLvlTx, err := waitForTxInMempool(net.Miner.Node,
|
||||||
|
time.Second*20)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("transactions not found in mempool: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// It should spend from the commitment in the channel with Alice.
|
||||||
|
tx, err := net.Miner.Node.GetRawTransaction(bobSecondLvlTx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to get txn: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tx.MsgTx().TxIn[0].PreviousOutPoint.Hash != *bobForceClose {
|
||||||
|
t.Fatalf("tx did not spend from bob's force close tx")
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point, Bob should have broadcast his second layer success
|
// At this point, Bob should have broadcast his second layer success
|
||||||
@ -8388,41 +8416,63 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest)
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}, time.Second*15)
|
}, time.Second*15)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
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
|
// We'll now mine a block which should confirm Bob's second layer
|
||||||
// transactions and the commit sweep.
|
// transaction.
|
||||||
block = mineBlocks(t, net, 1)[0]
|
block = mineBlocks(t, net, 1)[0]
|
||||||
if len(block.Transactions) != 4 {
|
if len(block.Transactions) != 2 {
|
||||||
t.Fatalf("expected 4 transactions in block, got %v",
|
t.Fatalf("expected 2 transactions in block, got %v",
|
||||||
len(block.Transactions))
|
len(block.Transactions))
|
||||||
}
|
}
|
||||||
assertTxInBlock(t, block, secondLevelHash)
|
assertTxInBlock(t, block, bobSecondLvlTx)
|
||||||
|
|
||||||
// If we then mine 4 additional blocks, Bob and Carol should sweep the
|
// Keep track of Bob's second level maturity, and decrement our track
|
||||||
// outputs destined for them.
|
// of Carol's.
|
||||||
if _, err := net.Miner.Node.Generate(defaultCSV); err != nil {
|
bobSecondLevelCSV := defaultCSV
|
||||||
|
carolSecondLevelCSV--
|
||||||
|
|
||||||
|
// If we then mine 3 additional blocks, Carol's second level tx should
|
||||||
|
// mature, and she can pull the funds from it with a sweep tx.
|
||||||
|
if _, err := net.Miner.Node.Generate(carolSecondLevelCSV); err != nil {
|
||||||
t.Fatalf("unable to generate block: %v", err)
|
t.Fatalf("unable to generate block: %v", err)
|
||||||
}
|
}
|
||||||
|
bobSecondLevelCSV -= carolSecondLevelCSV
|
||||||
|
|
||||||
sweepTxs, err := waitForNTxsInMempool(net.Miner.Node, 2, time.Second*10)
|
carolSweep, err := waitForTxInMempool(net.Miner.Node, time.Second*10)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to find sweeping transactions: %v", err)
|
t.Fatalf("unable to find Carol's sweeping transaction: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point, Bob should detect that he has no pending channels
|
// Mining one additional block, Bob's second level tx is mature, and he
|
||||||
// anymore, as this just resolved it by the confirmation of the sweep
|
// can sweep the output.
|
||||||
// transaction we detected above.
|
block = mineBlocks(t, net, bobSecondLevelCSV)[0]
|
||||||
block = mineBlocks(t, net, 1)[0]
|
assertTxInBlock(t, block, carolSweep)
|
||||||
for _, sweepTx := range sweepTxs {
|
|
||||||
assertTxInBlock(t, block, sweepTx)
|
bobSweep, err := waitForTxInMempool(net.Miner.Node, time.Second*10)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to find bob's sweeping transaction")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure it spends from the second level tx.
|
||||||
|
tx, err = net.Miner.Node.GetRawTransaction(bobSweep)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to get txn: %v", err)
|
||||||
|
}
|
||||||
|
if tx.MsgTx().TxIn[0].PreviousOutPoint.Hash != *bobSecondLvlTx {
|
||||||
|
t.Fatalf("tx did not spend from bob's second level tx")
|
||||||
|
}
|
||||||
|
|
||||||
|
// When we mine one additional block, that will confirm Bob's sweep.
|
||||||
|
// Now Bob should have no pending channels anymore, as this just
|
||||||
|
// resolved it by the confirmation of the sweep transaction.
|
||||||
|
block = mineBlocks(t, net, 1)[0]
|
||||||
|
assertTxInBlock(t, block, bobSweep)
|
||||||
|
|
||||||
err = lntest.WaitPredicate(func() bool {
|
err = lntest.WaitPredicate(func() bool {
|
||||||
pendingChanResp, err := net.Bob.PendingChannels(
|
pendingChanResp, err := net.Bob.PendingChannels(
|
||||||
ctxb, pendingChansRequest,
|
ctxb, pendingChansRequest,
|
||||||
@ -8437,7 +8487,54 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest)
|
|||||||
"but shouldn't: %v", spew.Sdump(pendingChanResp))
|
"but shouldn't: %v", spew.Sdump(pendingChanResp))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
req := &lnrpc.ListChannelsRequest{}
|
||||||
|
chanInfo, err := net.Bob.ListChannels(ctxb, req)
|
||||||
|
if err != nil {
|
||||||
|
predErr = fmt.Errorf("unable to query for open "+
|
||||||
|
"channels: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(chanInfo.Channels) != 0 {
|
||||||
|
predErr = fmt.Errorf("Bob should have no open "+
|
||||||
|
"channels, instead he has %v",
|
||||||
|
len(chanInfo.Channels))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}, time.Second*15)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(predErr.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also Carol should have no channels left (open nor pending).
|
||||||
|
err = lntest.WaitPredicate(func() bool {
|
||||||
|
pendingChanResp, err := carol.PendingChannels(
|
||||||
|
ctxb, pendingChansRequest,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
predErr = fmt.Errorf("unable to query for pending "+
|
||||||
|
"channels: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(pendingChanResp.PendingForceClosingChannels) != 0 {
|
||||||
|
predErr = fmt.Errorf("bob carol has pending channels "+
|
||||||
|
"but shouldn't: %v", spew.Sdump(pendingChanResp))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &lnrpc.ListChannelsRequest{}
|
||||||
|
chanInfo, err := carol.ListChannels(ctxb, req)
|
||||||
|
if err != nil {
|
||||||
|
predErr = fmt.Errorf("unable to query for open "+
|
||||||
|
"channels: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(chanInfo.Channels) != 0 {
|
||||||
|
predErr = fmt.Errorf("carol should have no open "+
|
||||||
|
"channels, instead she has %v",
|
||||||
|
len(chanInfo.Channels))
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}, time.Second*15)
|
}, time.Second*15)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -8506,7 +8603,8 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest
|
|||||||
// immediately force close the channel by broadcast her commitment
|
// immediately force close the channel by broadcast her commitment
|
||||||
// transaction.
|
// transaction.
|
||||||
ctxt, _ := context.WithTimeout(ctxb, timeout)
|
ctxt, _ := context.WithTimeout(ctxb, timeout)
|
||||||
closeChannelAndAssert(ctxt, t, net, net.Alice, aliceChanPoint, true)
|
aliceForceClose := closeChannelAndAssert(ctxt, t, net, net.Alice,
|
||||||
|
aliceChanPoint, true)
|
||||||
|
|
||||||
// We'll now mine enough blocks so Carol decides that she needs to go
|
// We'll now mine enough blocks so Carol decides that she needs to go
|
||||||
// on-chain to claim the HTLC as Bob has been inactive.
|
// on-chain to claim the HTLC as Bob has been inactive.
|
||||||
@ -8555,58 +8653,68 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest
|
|||||||
|
|
||||||
// After the force close transacion is mined, Carol should broadcast
|
// After the force close transacion is mined, Carol should broadcast
|
||||||
// her second level HTLC transacion. Bob will braodcast a sweep tx to
|
// 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
|
// sweep his output in the channel with Carol. He can do this
|
||||||
// second level transaction in the mempool, he will extract the
|
// immediately, as the output is not timelocked since Carol was the one
|
||||||
// preimage and broadcast a second level tx to claim the HTLC in his
|
// force closing.
|
||||||
// (already closed) channel with Alice.
|
commitSpends, err := waitForNTxsInMempool(net.Miner.Node, 2,
|
||||||
secondLevelHashes, err := waitForNTxsInMempool(net.Miner.Node, 3,
|
|
||||||
time.Second*20)
|
time.Second*20)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("transactions not found in mempool: %v", err)
|
t.Fatalf("transactions not found in mempool: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carol's second level transaction should be spending from
|
// Both Carol's second level transaction and Bob's sweep should be
|
||||||
// the commitment transaction.
|
// spending from the commitment transaction.
|
||||||
var secondLevelHash *chainhash.Hash
|
for _, txid := range commitSpends {
|
||||||
for _, txid := range secondLevelHashes {
|
|
||||||
tx, err := net.Miner.Node.GetRawTransaction(txid)
|
tx, err := net.Miner.Node.GetRawTransaction(txid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to get txn: %v", err)
|
t.Fatalf("unable to get txn: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if tx.MsgTx().TxIn[0].PreviousOutPoint.Hash == *commitHash {
|
if tx.MsgTx().TxIn[0].PreviousOutPoint.Hash != *commitHash {
|
||||||
secondLevelHash = txid
|
t.Fatalf("tx did not spend from commitment tx")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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
|
// Mine a block to confirm the two transactions (+ coinbase).
|
||||||
// transactions and the commit sweep.
|
|
||||||
block = mineBlocks(t, net, 1)[0]
|
block = mineBlocks(t, net, 1)[0]
|
||||||
if len(block.Transactions) != 4 {
|
if len(block.Transactions) != 3 {
|
||||||
t.Fatalf("expected 4 transactions in block, got %v",
|
t.Fatalf("expected 3 transactions in block, got %v",
|
||||||
len(block.Transactions))
|
len(block.Transactions))
|
||||||
}
|
}
|
||||||
assertTxInBlock(t, block, secondLevelHash)
|
for _, txid := range commitSpends {
|
||||||
|
assertTxInBlock(t, block, txid)
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = waitForNTxsInMempool(net.Miner.Node, 1, time.Second*15)
|
// Keep track of the second level tx maturity.
|
||||||
|
carolSecondLevelCSV := defaultCSV
|
||||||
|
|
||||||
|
// When Bob notices Carol's second level transaction in the block, he
|
||||||
|
// will extract the preimage and broadcast a sweep tx to directly claim
|
||||||
|
// the HTLC in his (already closed) channel with Alice.
|
||||||
|
bobHtlcSweep, err := waitForTxInMempool(net.Miner.Node,
|
||||||
|
time.Second*20)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to find bob's sweeping transaction: %v", err)
|
t.Fatalf("transactions not found in mempool: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll now mine another block, this should confirm the sweep
|
// It should spend from the commitment in the channel with Alice.
|
||||||
// transaction that Bob broadcast in the prior stage.
|
tx, err := net.Miner.Node.GetRawTransaction(bobHtlcSweep)
|
||||||
if _, err := net.Miner.Node.Generate(1); err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to generate block: %v", err)
|
t.Fatalf("unable to get txn: %v", err)
|
||||||
}
|
}
|
||||||
|
if tx.MsgTx().TxIn[0].PreviousOutPoint.Hash != *aliceForceClose {
|
||||||
|
t.Fatalf("tx did not spend from alice's force close tx")
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll now mine a block which should confirm Bob's HTLC sweep
|
||||||
|
// transaction.
|
||||||
|
block = mineBlocks(t, net, 1)[0]
|
||||||
|
if len(block.Transactions) != 2 {
|
||||||
|
t.Fatalf("expected 2 transactions in block, got %v",
|
||||||
|
len(block.Transactions))
|
||||||
|
}
|
||||||
|
assertTxInBlock(t, block, bobHtlcSweep)
|
||||||
|
carolSecondLevelCSV--
|
||||||
|
|
||||||
// Now that the sweeping transaction has been confirmed, Bob should now
|
// Now that the sweeping transaction has been confirmed, Bob should now
|
||||||
// recognize that all contracts have been fully resolved, and show no
|
// recognize that all contracts have been fully resolved, and show no
|
||||||
@ -8632,6 +8740,44 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(predErr.Error())
|
t.Fatalf(predErr.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we then mine 3 additional blocks, Carol's second level tx will
|
||||||
|
// mature, and she should pull the funds.
|
||||||
|
if _, err := net.Miner.Node.Generate(carolSecondLevelCSV); err != nil {
|
||||||
|
t.Fatalf("unable to generate block: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
carolSweep, err := waitForTxInMempool(net.Miner.Node, time.Second*10)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to find Carol's sweeping transaction: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// When Carol's sweep gets confirmed, she should have no more pending
|
||||||
|
// channels.
|
||||||
|
block = mineBlocks(t, net, 1)[0]
|
||||||
|
assertTxInBlock(t, block, carolSweep)
|
||||||
|
|
||||||
|
pendingChansRequest = &lnrpc.PendingChannelsRequest{}
|
||||||
|
err = lntest.WaitPredicate(func() bool {
|
||||||
|
pendingChanResp, err := carol.PendingChannels(
|
||||||
|
ctxb, pendingChansRequest,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
predErr = fmt.Errorf("unable to query for pending "+
|
||||||
|
"channels: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(pendingChanResp.PendingForceClosingChannels) != 0 {
|
||||||
|
predErr = fmt.Errorf("carol still has pending channels "+
|
||||||
|
"but shouldn't: %v", spew.Sdump(pendingChanResp))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}, time.Second*15)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(predErr.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// testSwitchCircuitPersistence creates a multihop network to ensure the sender
|
// testSwitchCircuitPersistence creates a multihop network to ensure the sender
|
||||||
|
Loading…
Reference in New Issue
Block a user