From 01931bf255b7229e829174222832b20c281bc6a6 Mon Sep 17 00:00:00 2001 From: pm47 Date: Fri, 4 Sep 2015 11:43:06 +0200 Subject: [PATCH] completed CMD_CLOSE tests during channel opening --- .../src/main/scala/fr/acinq/eclair/Node.scala | 15 +++ .../fr/acinq/eclair/ChannelOpenSpec.scala | 111 +++++++++++++++++- 2 files changed, 122 insertions(+), 4 deletions(-) diff --git a/eclair-demo/src/main/scala/fr/acinq/eclair/Node.scala b/eclair-demo/src/main/scala/fr/acinq/eclair/Node.scala index 567ae4688..cc664f7e4 100644 --- a/eclair-demo/src/main/scala/fr/acinq/eclair/Node.scala +++ b/eclair-demo/src/main/scala/fr/acinq/eclair/Node.scala @@ -592,6 +592,21 @@ class Node(val blockchain: ActorRef, val commitPrivKey: BinaryData, val finalPri when(WAIT_FOR_UPDATE_COMPLETE_LOWPRIO)(WAIT_FOR_UPDATE_COMPLETE_handler) when(WAIT_FOR_CLOSE_COMPLETE) { + case Event(close_channel_complete(theirSig), DATA_OPEN_WAITING(ourParams, theirParams, Commitment(commitment, state, _, _))) => + val finalTx = makeFinalTx(commitment.txIn, ourParams.finalKey, theirParams.finalKey, state) + val signedFinalTx = sign_our_commitment_tx(ourParams, theirParams, finalTx, theirSig) + val ok = Try(Transaction.correctlySpends(signedFinalTx, Map(signedFinalTx.txIn(0).outPoint -> anchorPubkeyScript(ourParams.commitKey, theirParams.commitKey)), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)).isSuccess + ok match { + case false => + them ! error(Some("Bad signature")) + stay + case true => + them ! close_channel_ack() + blockchain ! Watch(self, signedFinalTx.hash, Final, 1) + blockchain ! Publish(signedFinalTx) + goto(CLOSE_WAIT_CLOSE) + } + case Event(close_channel_complete(theirSig), DATA_NORMAL(ourParams, theirParams, Commitment(commitment, state, _, _))) => val finalTx = makeFinalTx(commitment.txIn, ourParams.finalKey, theirParams.finalKey, state) val signedFinalTx = sign_our_commitment_tx(ourParams, theirParams, finalTx, theirSig) diff --git a/eclair-demo/src/test/scala/fr/acinq/eclair/ChannelOpenSpec.scala b/eclair-demo/src/test/scala/fr/acinq/eclair/ChannelOpenSpec.scala index 12323b0f2..df668313e 100644 --- a/eclair-demo/src/test/scala/fr/acinq/eclair/ChannelOpenSpec.scala +++ b/eclair-demo/src/test/scala/fr/acinq/eclair/ChannelOpenSpec.scala @@ -150,7 +150,7 @@ class ChannelOpenSpec(_system: ActorSystem) extends TestKit(_system) with Implic expectMsg(CLOSED) } - /*"handle CMD_CLOSE in OPEN_WAITING_THEIRANCHOR" in { + "handle CMD_CLOSE in OPEN_WAITING_THEIRANCHOR" in { val node = system.actorOf(Props(new Node(self, bob_commit_priv, bob_final_priv, 2, None))) node ! INPUT_NONE val their_open_channel = expectMsgClass(classOf[open_channel]) @@ -181,11 +181,17 @@ class ChannelOpenSpec(_system: ActorSystem) extends TestKit(_system) with Implic val theirFinalTx = makeFinalTx(ourCommitTx.txIn, theirParams.finalKey, ourParams.finalKey, state.reverse) val ourFinalSigForThem = bin2signature(Transaction.signInput(theirFinalTx, 0, multiSig2of2(ourParams.commitKey, theirParams.commitKey), SIGHASH_ALL, commitPrivKey)) node ! close_channel_complete(ourFinalSigForThem) + expectMsgClass(classOf[close_channel_ack]) + expectMsgClass(classOf[Watch]) + expectMsgClass(classOf[Publish]) + node ! CMD_GETSTATE + expectMsg(CLOSE_WAIT_CLOSE) + node ! BITCOIN_CLOSE_DONE node ! CMD_GETSTATE expectMsg(CLOSED) - }*/ + } - /*"handle CMD_CLOSE in OPEN_WAITING_OURANCHOR" in { + "handle CMD_CLOSE in OPEN_WAITING_OURANCHOR" in { val node = system.actorOf(Props(new Node(self, bob_commit_priv, bob_final_priv, 2, Some(anchorInput)))) node ! INPUT_NONE val their_open_channel = expectMsgClass(classOf[open_channel]) @@ -211,7 +217,104 @@ class ChannelOpenSpec(_system: ActorSystem) extends TestKit(_system) with Implic expectMsgClass(classOf[close_channel]) node ! CMD_GETSTATE expectMsg(WAIT_FOR_CLOSE_COMPLETE) - }*/ + // the only difference between their final tx and ours is the order of the outputs, because state is symmetric + val theirFinalTx = makeFinalTx(ourCommitTx.txIn, theirParams.finalKey, ourParams.finalKey, state.reverse) + val ourFinalSigForThem = bin2signature(Transaction.signInput(theirFinalTx, 0, multiSig2of2(ourParams.commitKey, theirParams.commitKey), SIGHASH_ALL, commitPrivKey)) + node ! close_channel_complete(ourFinalSigForThem) + expectMsgClass(classOf[close_channel_ack]) + expectMsgClass(classOf[Watch]) + expectMsgClass(classOf[Publish]) + node ! CMD_GETSTATE + expectMsg(CLOSE_WAIT_CLOSE) + node ! BITCOIN_CLOSE_DONE + node ! CMD_GETSTATE + expectMsg(CLOSED) + } + + "handle CMD_CLOSE in OPEN_WAIT_FOR_COMPLETE_THEIRANCHOR" in { + val node = system.actorOf(Props(new Node(self, bob_commit_priv, bob_final_priv, 2, None))) + node ! INPUT_NONE + val their_open_channel = expectMsgClass(classOf[open_channel]) + val theirParams = ChannelParams(their_open_channel.delay, their_open_channel.commitKey, their_open_channel.finalKey, their_open_channel.minDepth.get, their_open_channel.commitmentFee) + val theirRevocationHash = their_open_channel.revocationHash + val ourRevocationHashPreimage = sha256_hash(4, 3, 2, 1) + val ourRevocationHash = Crypto.sha256(ourRevocationHashPreimage) + node ! open_channel(ourParams.delay, ourRevocationHash, ourParams.commitKey, ourParams.finalKey, WILL_CREATE_ANCHOR, Some(ourParams.minDepth), ourParams.commitmentFee) + val anchorTx = makeAnchorTx(ourParams.commitKey, theirParams.commitKey, anchorInput.amount, anchorInput.previousTxOutput, anchorInput.signData) + val anchorOutputIndex = 0 + // we fund the channel with the anchor tx, so the money is ours + val state = ChannelState(them = ChannelOneSide(0, 0, Seq()), us = ChannelOneSide(anchorInput.amount - our_commitment_fee, 0, Seq())) + // we build our commitment tx, leaving it unsigned + val ourCommitTx = makeCommitTx(ourParams.finalKey, theirParams.finalKey, theirParams.delay, anchorTx.hash, anchorOutputIndex, Crypto.sha256(ourRevocationHashPreimage), state) + // then we build their commitment tx and sign it + val theirCommitTx = makeCommitTx(theirParams.finalKey, ourParams.finalKey, ourParams.delay, anchorTx.hash, anchorOutputIndex, theirRevocationHash, state.reverse) + val ourSigForThem = bin2signature(Transaction.signInput(theirCommitTx, 0, multiSig2of2(ourParams.commitKey, theirParams.commitKey), SIGHASH_ALL, commitPrivKey)) + node ! open_anchor(anchorTx.hash, 0, anchorInput.amount, ourSigForThem) + expectMsgClass(classOf[open_commit_sig]) + expectMsgClass(classOf[Watch]) + node ! BITCOIN_ANCHOR_DEPTHOK + expectMsgClass(classOf[open_complete]) + node ! CMD_GETSTATE + expectMsg(OPEN_WAIT_FOR_COMPLETE_THEIRANCHOR) + node ! CMD_CLOSE(0) + expectMsgClass(classOf[close_channel]) + node ! CMD_GETSTATE + expectMsg(WAIT_FOR_CLOSE_COMPLETE) + // the only difference between their final tx and ours is the order of the outputs, because state is symmetric + val theirFinalTx = makeFinalTx(ourCommitTx.txIn, theirParams.finalKey, ourParams.finalKey, state.reverse) + val ourFinalSigForThem = bin2signature(Transaction.signInput(theirFinalTx, 0, multiSig2of2(ourParams.commitKey, theirParams.commitKey), SIGHASH_ALL, commitPrivKey)) + node ! close_channel_complete(ourFinalSigForThem) + expectMsgClass(classOf[close_channel_ack]) + expectMsgClass(classOf[Watch]) + expectMsgClass(classOf[Publish]) + node ! CMD_GETSTATE + expectMsg(CLOSE_WAIT_CLOSE) + node ! BITCOIN_CLOSE_DONE + node ! CMD_GETSTATE + expectMsg(CLOSED) + } + + "handle CMD_CLOSE in OPEN_WAIT_FOR_COMPLETE_OURANCHOR" in { + val node = system.actorOf(Props(new Node(self, bob_commit_priv, bob_final_priv, 2, Some(anchorInput)))) + node ! INPUT_NONE + val their_open_channel = expectMsgClass(classOf[open_channel]) + val theirParams = ChannelParams(their_open_channel.delay, their_open_channel.commitKey, their_open_channel.finalKey, their_open_channel.minDepth.get, their_open_channel.commitmentFee) + val theirRevocationHash = their_open_channel.revocationHash + val ourRevocationHashPreimage = sha256_hash(4, 3, 2, 1) + val ourRevocationHash = Crypto.sha256(ourRevocationHashPreimage) + node ! open_channel(ourParams.delay, ourRevocationHash, ourParams.commitKey, ourParams.finalKey, WONT_CREATE_ANCHOR, Some(ourParams.minDepth), ourParams.commitmentFee) + val their_open_anchor = expectMsgClass(classOf[open_anchor]) + // we fund the channel with the anchor tx, so the money is ours + val state = ChannelState(them = ChannelOneSide(their_open_anchor.amount - our_commitment_fee, 0, Seq()), us = ChannelOneSide(0, 0, Seq())) + // we build our commitment tx, leaving it unsigned + val ourCommitTx = makeCommitTx(ourParams.finalKey, theirParams.finalKey, theirParams.delay, their_open_anchor.txid, their_open_anchor.outputIndex, Crypto.sha256(ourRevocationHashPreimage), state) + // then we build their commitment tx and sign it + val theirCommitTx = makeCommitTx(theirParams.finalKey, ourParams.finalKey, ourParams.delay, their_open_anchor.txid, their_open_anchor.outputIndex, theirRevocationHash, state.reverse) + val ourSigForThem = bin2signature(Transaction.signInput(theirCommitTx, 0, multiSig2of2(ourParams.commitKey, theirParams.commitKey), SIGHASH_ALL, commitPrivKey)) + node ! open_commit_sig(ourSigForThem) + expectMsgClass(classOf[Watch]) + expectMsgClass(classOf[Publish]) + node ! BITCOIN_ANCHOR_DEPTHOK + expectMsgClass(classOf[open_complete]) + node ! CMD_GETSTATE + expectMsg(OPEN_WAIT_FOR_COMPLETE_OURANCHOR) + node ! CMD_CLOSE(0) + expectMsgClass(classOf[close_channel]) + node ! CMD_GETSTATE + expectMsg(WAIT_FOR_CLOSE_COMPLETE) + // the only difference between their final tx and ours is the order of the outputs, because state is symmetric + val theirFinalTx = makeFinalTx(ourCommitTx.txIn, theirParams.finalKey, ourParams.finalKey, state.reverse) + val ourFinalSigForThem = bin2signature(Transaction.signInput(theirFinalTx, 0, multiSig2of2(ourParams.commitKey, theirParams.commitKey), SIGHASH_ALL, commitPrivKey)) + node ! close_channel_complete(ourFinalSigForThem) + expectMsgClass(classOf[close_channel_ack]) + expectMsgClass(classOf[Watch]) + expectMsgClass(classOf[Publish]) + node ! CMD_GETSTATE + expectMsg(CLOSE_WAIT_CLOSE) + node ! BITCOIN_CLOSE_DONE + node ! CMD_GETSTATE + expectMsg(CLOSED) + } }