From c0614c0478e426e6dc7ce5cffeaaaebc132b81b1 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 27 Jul 2016 11:32:27 -0700 Subject: [PATCH] lnwallet: add basic tests for cooperative channel closure This commit adds a basic test for cooperative channel closure. The current test ensures correctness of the cooperative closure procedure initiated by either the channel initiator, or the channel responder. --- htlcswitch.go | 2 +- lnwallet/channel.go | 19 +++++++++++++----- lnwallet/channel_test.go | 43 +++++++++++++++++++++++++++++++++++++++- server.go | 2 +- 4 files changed, 58 insertions(+), 8 deletions(-) diff --git a/htlcswitch.go b/htlcswitch.go index 64b58a6c5..44e4b0e7b 100644 --- a/htlcswitch.go +++ b/htlcswitch.go @@ -173,7 +173,7 @@ out: continue } - hswcLog.Tracef("Sending %v to %x", amt, dest) + hswcLog.Tracef("Sending %v to %x", amt, dest[:]) // TODO(roasbeef): peer downstream should set chanPoint wireMsg.ChannelPoint = link.chanPoint diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 1ce5ee560..8f2dbca40 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -1275,7 +1275,7 @@ func (lc *LightningChannel) ForceClose() error { // settle any inflight. func (lc *LightningChannel) InitCooperativeClose() ([]byte, *wire.ShaHash, error) { lc.Lock() - defer lc.Unlock() // TODO(roasbeef): coarser graiend locking + defer lc.Unlock() // If we're already closing the channel, then ignore this request. if lc.status == channelClosing || lc.status == channelClosed { @@ -1317,7 +1317,7 @@ func (lc *LightningChannel) InitCooperativeClose() ([]byte, *wire.ShaHash, error // a signed+valid closure transaction to the network. func (lc *LightningChannel) CompleteCooperativeClose(remoteSig []byte) (*wire.MsgTx, error) { lc.Lock() - defer lc.Unlock() // TODO(roasbeef): coarser graiend locking + defer lc.Unlock() // If we're already closing the channel, then ignore this request. if lc.status == channelClosing || lc.status == channelClosed { @@ -1339,9 +1339,9 @@ func (lc *LightningChannel) CompleteCooperativeClose(remoteSig []byte) (*wire.Ms // the 2-of-2 multi-sig needed to redeem the funding output. redeemScript := lc.channelState.FundingRedeemScript hashCache := txscript.NewTxSigHashes(closeTx) + capacity := int64(lc.channelState.Capacity) closeSig, err := txscript.RawTxInWitnessSignature(closeTx, - hashCache, 0, int64(lc.channelState.Capacity), - redeemScript, txscript.SigHashAll, + hashCache, 0, capacity, redeemScript, txscript.SigHashAll, lc.channelState.OurMultiSigKey) if err != nil { return nil, err @@ -1355,7 +1355,16 @@ func (lc *LightningChannel) CompleteCooperativeClose(remoteSig []byte) (*wire.Ms theirKey, remoteSig) closeTx.TxIn[0].Witness = witness - // TODO(roasbeef): VALIDATE + // Validate the finalized transaction to ensure the output script is + // properly met, and that the remote peer supplied a valid signature. + vm, err := txscript.NewEngine(lc.fundingP2WSH, closeTx, 0, + txscript.StandardVerifyFlags, nil, hashCache, capacity) + if err != nil { + return nil, err + } + if err := vm.Execute(); err != nil { + return nil, err + } return closeTx, nil } diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index c973e3891..8a778b01b 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -442,5 +442,46 @@ func TestSimpleAddSettleWorkflow(t *testing.T) { } func TestCooperativeChannelClosure(t *testing.T) { - // * add validation of their sig + // Create a test channel which will be used for the duration of this + // unittest. The channel will be funded evenly with Alice having 5 BTC, + // and Bob having 5 BTC. + aliceChannel, bobChannel, cleanUp, err := createTestChannels() + if err != nil { + t.Fatalf("unable to create test channels: %v", err) + } + defer cleanUp() + + // First we test the channel initiator requesting a cooperative close. + sig, txid, err := aliceChannel.InitCooperativeClose() + if err != nil { + t.Fatalf("unable to initiate alice cooperative close: %v", err) + } + closeTx, err := bobChannel.CompleteCooperativeClose(sig) + if err != nil { + t.Fatalf("unable to complete alice cooperative close: %v", err) + } + bobCloseSha := closeTx.TxSha() + if !bobCloseSha.IsEqual(txid) { + t.Fatalf("alice's transactions doesn't match: %x vs %x", + bobCloseSha[:], txid[:]) + } + + aliceChannel.status = channelOpen + bobChannel.status = channelOpen + + // Next we test the channel recipient requesting a cooperative closure. + // First we test the channel initiator requesting a cooperative close. + sig, txid, err = bobChannel.InitCooperativeClose() + if err != nil { + t.Fatalf("unable to initiate bob cooperative close: %v", err) + } + closeTx, err = aliceChannel.CompleteCooperativeClose(sig) + if err != nil { + t.Fatalf("unable to complete bob cooperative close: %v", err) + } + aliceCloseSha := closeTx.TxSha() + if !aliceCloseSha.IsEqual(txid) { + t.Fatalf("bob's closure transactions don't match: %x vs %x", + aliceCloseSha[:], txid[:]) + } } diff --git a/server.go b/server.go index 27264a0e7..1b39e17dc 100644 --- a/server.go +++ b/server.go @@ -228,7 +228,7 @@ type openChanResp struct { chanPoint *wire.OutPoint } -// peerManager handles any requests to modify the server's internal state of +// queryHandler handles any requests to modify the server's internal state of // all active peers, or query/mutate the server's global state. Additionally, // any queries directed at peers will be handled by this goroutine. //