mirror of
https://github.com/ACINQ/eclair.git
synced 2025-02-24 06:47:46 +01:00
removed deprecated tests
This commit is contained in:
parent
09b9d9dd14
commit
9f1cdac445
10 changed files with 12 additions and 924 deletions
|
@ -1,7 +1,7 @@
|
|||
package fr.acinq.eclair
|
||||
|
||||
import fr.acinq.bitcoin.Crypto
|
||||
import fr.acinq.eclair.channel.{ChannelOneSide, ChannelState, Htlc}
|
||||
import fr.acinq.eclair.channel._
|
||||
import lightning.locktime.Locktime.Blocks
|
||||
import lightning.{locktime, sha256_hash, update_add_htlc}
|
||||
import org.junit.runner.RunWith
|
||||
|
@ -22,14 +22,14 @@ class ChannelStateSpec extends FunSuite {
|
|||
|
||||
val r = sha256_hash(7, 7, 7, 7)
|
||||
val rHash = Crypto.sha256(r)
|
||||
val htlc = Htlc(100000000, rHash, locktime(Blocks(1)), Nil, None)
|
||||
val state_1 = state_0.htlc_send(htlc)
|
||||
val htlc = Htlc(0, 100000000, rHash, locktime(Blocks(1)), Nil, None)
|
||||
val state_1 = state_0.add_htlc(OUT, htlc)
|
||||
assert(state_1 === ChannelState(
|
||||
us = ChannelOneSide(pay_msat = 850000000, fee_msat = 50000000, htlcs_received = Seq()),
|
||||
them = ChannelOneSide(pay_msat = 0, fee_msat = 0, htlcs_received = Seq(htlc))
|
||||
))
|
||||
|
||||
val state_2 = state_1.htlc_fulfill(r)
|
||||
val state_2 = state_1.fulfill_htlc(IN, htlc.id, r)
|
||||
assert(state_2 === ChannelState(
|
||||
us = ChannelOneSide(pay_msat = 875000000, fee_msat = 25000000, htlcs_received = Seq()),
|
||||
them = ChannelOneSide(pay_msat = 75000000, fee_msat = 25000000, htlcs_received = Seq())
|
||||
|
@ -44,14 +44,14 @@ class ChannelStateSpec extends FunSuite {
|
|||
|
||||
val r = sha256_hash(7, 7, 7, 7)
|
||||
val rHash = Crypto.sha256(r)
|
||||
val htlc = Htlc(2000000, rHash, locktime(Blocks(1)), Nil, None)
|
||||
val state_1 = state_0.htlc_receive(htlc)
|
||||
val htlc = Htlc(0, 2000000, rHash, locktime(Blocks(1)), Nil, None)
|
||||
val state_1 = state_0.add_htlc(IN, htlc)
|
||||
assert(state_1 === ChannelState(
|
||||
us = ChannelOneSide(pay_msat = 0, fee_msat = 0, htlcs_received = Seq(htlc)),
|
||||
them = ChannelOneSide(pay_msat = 948000000, fee_msat = 50000000, htlcs_received = Seq())
|
||||
))
|
||||
|
||||
val state_2 = state_1.htlc_fulfill(r)
|
||||
val state_2 = state_1.fulfill_htlc(OUT, htlc.id, r)
|
||||
assert(state_2 === ChannelState(
|
||||
us = ChannelOneSide(pay_msat = 0, fee_msat = 2000000, htlcs_received = Seq()),
|
||||
them = ChannelOneSide(pay_msat = 950000000, fee_msat = 48000000, htlcs_received = Seq())
|
||||
|
|
|
@ -56,18 +56,20 @@ class ProtocolSpec extends FlatSpec {
|
|||
val (anchor, anchorOutputIndex) = makeAnchorTx(Alice.commitPubKey, Bob.commitPubKey, 10, OutPoint(previousTx, 0), key)
|
||||
val ours = open_channel(
|
||||
delay = locktime(Blocks(100)),
|
||||
revocationHash = Alice.H,
|
||||
revocationHash = Alice .H,
|
||||
commitKey = Alice.commitPubKey,
|
||||
finalKey = Alice.finalPubKey,
|
||||
anch = anchor_offer.WILL_CREATE_ANCHOR,
|
||||
commitmentFee = 1)
|
||||
nextRevocationHash = null,
|
||||
initialFeeRate = 1)
|
||||
val theirs = open_channel(
|
||||
delay = locktime(Blocks(100)),
|
||||
revocationHash = Bob.H,
|
||||
commitKey = Bob.commitPubKey,
|
||||
finalKey = Bob.finalPubKey,
|
||||
anch = anchor_offer.WONT_CREATE_ANCHOR,
|
||||
commitmentFee = 1)
|
||||
nextRevocationHash = null,
|
||||
initialFeeRate = 1)
|
||||
|
||||
// we assume that Alice knows Bob's H
|
||||
val openAnchor = open_anchor(anchor.hash, anchorOutputIndex, 10, signature.defaultInstance) // commit sig will be computed later
|
||||
|
|
|
@ -1,271 +0,0 @@
|
|||
package fr.acinq.eclair.channel
|
||||
|
||||
import fr.acinq.bitcoin._
|
||||
import fr.acinq.eclair._
|
||||
import fr.acinq.eclair.channel.Scripts._
|
||||
import fr.acinq.eclair.blockchain.{Publish, WatchConfirmed}
|
||||
import lightning._
|
||||
import org.junit.runner.RunWith
|
||||
import org.scalatest.Ignore
|
||||
import org.scalatest.junit.JUnitRunner
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* Created by PM on 02/09/2015.
|
||||
*/
|
||||
@RunWith(classOf[JUnitRunner])
|
||||
class ChannelOpenSpec extends TestHelper() {
|
||||
|
||||
"Node" must {
|
||||
|
||||
"successfuly open a channel in ANCHOR_NOINPUT mode" in {
|
||||
reachState_NOANCHOR(NORMAL_LOWPRIO)
|
||||
}
|
||||
|
||||
"successfuly open a channel in ANCHOR_WITHINPUT mode" in {
|
||||
reachState_WITHANCHOR(NORMAL_HIGHPRIO)
|
||||
}
|
||||
|
||||
"handle CMD_CLOSE in OPEN_WAIT_FOR_OPEN_NOANCHOR" in {
|
||||
val (node, _) = reachState_NOANCHOR(OPEN_WAIT_FOR_OPEN_NOANCHOR)
|
||||
node ! CMD_CLOSE(0)
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSED)
|
||||
}
|
||||
|
||||
"handle CMD_CLOSE in OPEN_WAIT_FOR_OPEN_WITHANCHOR" in {
|
||||
val (node, _) = reachState_WITHANCHOR(OPEN_WAIT_FOR_OPEN_WITHANCHOR)
|
||||
node ! CMD_CLOSE(0)
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSED)
|
||||
}
|
||||
|
||||
"handle CMD_CLOSE in OPEN_WAIT_FOR_ANCHOR" in {
|
||||
val (node, _) = reachState_NOANCHOR(OPEN_WAIT_FOR_ANCHOR)
|
||||
node ! CMD_CLOSE(0)
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSED)
|
||||
}
|
||||
|
||||
"handle CMD_CLOSE in OPEN_WAIT_FOR_COMMIT_SIG" in {
|
||||
val (node, _) = reachState_WITHANCHOR(OPEN_WAIT_FOR_COMMIT_SIG)
|
||||
node ! CMD_CLOSE(0)
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSED)
|
||||
}
|
||||
|
||||
"handle CMD_CLOSE in OPEN_WAITING_THEIRANCHOR" in {
|
||||
val (node, ChannelDesc(Some(ourParams), Some(theirParams), Some(Commitment(_, ourCommitTx, state, _)))) = reachState_NOANCHOR(OPEN_WAITING_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 closingState = state.reverse.adjust_fees(Globals.closing_fee * 1000, ourParams.anchorAmount.isDefined)
|
||||
val finalTx = makeFinalTx(ourCommitTx.txIn, theirParams.finalPubKey, ourFinalPubKey, closingState)
|
||||
val ourFinalSigForThem = bin2signature(Transaction.signInput(finalTx, 0, multiSig2of2(ourCommitPubKey, theirParams.commitPubKey), SIGHASH_ALL, ourParams.commitPrivKey))
|
||||
node ! close_channel_complete(ourFinalSigForThem)
|
||||
expectMsgClass(classOf[close_channel_ack])
|
||||
expectMsgClass(classOf[Publish])
|
||||
node ! (BITCOIN_ANCHOR_SPENT, finalTx)
|
||||
expectMsgClass(classOf[WatchConfirmed])
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSING)
|
||||
node ! BITCOIN_CLOSE_DONE
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSED)
|
||||
}
|
||||
|
||||
"handle CMD_CLOSE in OPEN_WAITING_THEIRANCHOR (closing tx received early)" in {
|
||||
val (node, ChannelDesc(Some(ourParams), Some(theirParams), Some(Commitment(_, ourCommitTx, state, _)))) = reachState_NOANCHOR(OPEN_WAITING_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 closingState = state.reverse.adjust_fees(Globals.closing_fee * 1000, ourParams.anchorAmount.isDefined)
|
||||
val finalTx = makeFinalTx(ourCommitTx.txIn, theirParams.finalPubKey, ourFinalPubKey, closingState)
|
||||
val ourFinalSigForThem = bin2signature(Transaction.signInput(finalTx, 0, multiSig2of2(ourCommitPubKey, theirParams.commitPubKey), SIGHASH_ALL, ourParams.commitPrivKey))
|
||||
node ! (BITCOIN_ANCHOR_SPENT, finalTx)
|
||||
expectMsgClass(classOf[WatchConfirmed])
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSING)
|
||||
node ! close_channel_complete(ourFinalSigForThem)
|
||||
node ! BITCOIN_CLOSE_DONE
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSED)
|
||||
}
|
||||
|
||||
"handle CMD_CLOSE in OPEN_WAITING_OURANCHOR" in {
|
||||
val (node, ChannelDesc(Some(ourParams), Some(theirParams), Some(Commitment(_, ourCommitTx, state, _)))) = reachState_WITHANCHOR(OPEN_WAITING_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 closingState = state.reverse.adjust_fees(Globals.closing_fee * 1000, ourParams.anchorAmount.isDefined)
|
||||
val finalTx = makeFinalTx(ourCommitTx.txIn, theirParams.finalPubKey, ourFinalPubKey, closingState)
|
||||
val ourFinalSigForThem = bin2signature(Transaction.signInput(finalTx, 0, multiSig2of2(ourCommitPubKey, theirParams.commitPubKey), SIGHASH_ALL, ourParams.commitPrivKey))
|
||||
node ! close_channel_complete(ourFinalSigForThem)
|
||||
expectMsgClass(classOf[close_channel_ack])
|
||||
expectMsgClass(classOf[Publish])
|
||||
node ! (BITCOIN_ANCHOR_SPENT, finalTx)
|
||||
expectMsgClass(classOf[WatchConfirmed])
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSING)
|
||||
node ! BITCOIN_CLOSE_DONE
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSED)
|
||||
}
|
||||
|
||||
"handle CMD_CLOSE in OPEN_WAIT_FOR_COMPLETE_THEIRANCHOR" in {
|
||||
val (node, ChannelDesc(Some(ourParams), Some(theirParams), Some(Commitment(_, ourCommitTx, state, _)))) = reachState_NOANCHOR(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 closingState = state.reverse.adjust_fees(Globals.closing_fee * 1000, ourParams.anchorAmount.isDefined)
|
||||
val finalTx = makeFinalTx(ourCommitTx.txIn, theirParams.finalPubKey, ourFinalPubKey, closingState)
|
||||
val ourFinalSigForThem = bin2signature(Transaction.signInput(finalTx, 0, multiSig2of2(ourCommitPubKey, theirParams.commitPubKey), SIGHASH_ALL, ourParams.commitPrivKey))
|
||||
node ! close_channel_complete(ourFinalSigForThem)
|
||||
expectMsgClass(classOf[close_channel_ack])
|
||||
expectMsgClass(classOf[Publish])
|
||||
node ! (BITCOIN_ANCHOR_SPENT, finalTx)
|
||||
expectMsgClass(classOf[WatchConfirmed])
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSING)
|
||||
node ! BITCOIN_CLOSE_DONE
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSED)
|
||||
}
|
||||
|
||||
"handle CMD_CLOSE in OPEN_WAIT_FOR_COMPLETE_OURANCHOR" in {
|
||||
val (node, ChannelDesc(Some(ourParams), Some(theirParams), Some(Commitment(_, ourCommitTx, state, _)))) = reachState_WITHANCHOR(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 closingState = state.reverse.adjust_fees(Globals.closing_fee * 1000, ourParams.anchorAmount.isDefined)
|
||||
val finalTx = makeFinalTx(ourCommitTx.txIn, theirParams.finalPubKey, ourFinalPubKey, closingState)
|
||||
val ourFinalSigForThem = bin2signature(Transaction.signInput(finalTx, 0, multiSig2of2(ourCommitPubKey, theirParams.commitPubKey), SIGHASH_ALL, ourParams.commitPrivKey))
|
||||
node ! close_channel_complete(ourFinalSigForThem)
|
||||
expectMsgClass(classOf[close_channel_ack])
|
||||
expectMsgClass(classOf[Publish])
|
||||
node ! (BITCOIN_ANCHOR_SPENT, finalTx)
|
||||
expectMsgClass(classOf[WatchConfirmed])
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSING)
|
||||
node ! BITCOIN_CLOSE_DONE
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSED)
|
||||
}
|
||||
|
||||
"handle PKT_CLOSE in OPEN_WAITING_THEIRANCHOR" in {
|
||||
val (node, ChannelDesc(Some(ourParams), Some(theirParams), Some(Commitment(_, ourCommitTx, state, _)))) = reachState_NOANCHOR(OPEN_WAITING_THEIRANCHOR)
|
||||
// the only difference between their final tx and ours is the order of the outputs, because state is symmetric
|
||||
val closingState = state.reverse.adjust_fees(Globals.closing_fee * 1000, ourParams.anchorAmount.isDefined)
|
||||
val finalTx = makeFinalTx(ourCommitTx.txIn, theirParams.finalPubKey, ourFinalPubKey, closingState)
|
||||
val ourFinalSigForThem = bin2signature(Transaction.signInput(finalTx, 0, multiSig2of2(ourCommitPubKey, theirParams.commitPubKey), SIGHASH_ALL, ourParams.commitPrivKey))
|
||||
node ! close_channel(ourFinalSigForThem, 0)
|
||||
expectMsgClass(classOf[Publish])
|
||||
expectMsgClass(classOf[close_channel_complete])
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(WAIT_FOR_CLOSE_ACK)
|
||||
node ! close_channel_ack()
|
||||
node ! (BITCOIN_ANCHOR_SPENT, finalTx) // TODO: this event could be sent before the close_channel_ack
|
||||
expectMsgClass(classOf[WatchConfirmed])
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSING)
|
||||
node ! BITCOIN_CLOSE_DONE
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSED)
|
||||
}
|
||||
|
||||
"handle PKT_CLOSE in OPEN_WAITING_THEIRANCHOR (closing tx received early)" in {
|
||||
val (node, ChannelDesc(Some(ourParams), Some(theirParams), Some(Commitment(_, ourCommitTx, state, _)))) = reachState_NOANCHOR(OPEN_WAITING_THEIRANCHOR)
|
||||
// the only difference between their final tx and ours is the order of the outputs, because state is symmetric
|
||||
val closingState = state.reverse.adjust_fees(Globals.closing_fee * 1000, ourParams.anchorAmount.isDefined)
|
||||
val finalTx = makeFinalTx(ourCommitTx.txIn, theirParams.finalPubKey, ourFinalPubKey, closingState)
|
||||
val ourFinalSigForThem = bin2signature(Transaction.signInput(finalTx, 0, multiSig2of2(ourCommitPubKey, theirParams.commitPubKey), SIGHASH_ALL, ourParams.commitPrivKey))
|
||||
node ! close_channel(ourFinalSigForThem, 0)
|
||||
expectMsgClass(classOf[Publish])
|
||||
expectMsgClass(classOf[close_channel_complete])
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(WAIT_FOR_CLOSE_ACK)
|
||||
node ! (BITCOIN_ANCHOR_SPENT, finalTx)
|
||||
expectMsgClass(classOf[WatchConfirmed])
|
||||
node ! close_channel_ack()
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSING)
|
||||
node ! BITCOIN_CLOSE_DONE
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSED)
|
||||
}
|
||||
|
||||
"handle PKT_CLOSE in OPEN_WAITING_OURANCHOR" in {
|
||||
val (node, ChannelDesc(Some(ourParams), Some(theirParams), Some(Commitment(_, ourCommitTx, state, _)))) = reachState_WITHANCHOR(OPEN_WAITING_OURANCHOR)
|
||||
// the only difference between their final tx and ours is the order of the outputs, because state is symmetric
|
||||
val closingState = state.reverse.adjust_fees(Globals.closing_fee * 1000, ourParams.anchorAmount.isDefined)
|
||||
val finalTx = makeFinalTx(ourCommitTx.txIn, theirParams.finalPubKey, ourFinalPubKey, closingState)
|
||||
val ourFinalSigForThem = bin2signature(Transaction.signInput(finalTx, 0, multiSig2of2(ourCommitPubKey, theirParams.commitPubKey), SIGHASH_ALL, ourParams.commitPrivKey))
|
||||
node ! close_channel(ourFinalSigForThem, 0)
|
||||
expectMsgClass(classOf[Publish])
|
||||
expectMsgClass(classOf[close_channel_complete])
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(WAIT_FOR_CLOSE_ACK)
|
||||
node ! close_channel_ack()
|
||||
node ! (BITCOIN_ANCHOR_SPENT, finalTx)
|
||||
expectMsgClass(classOf[WatchConfirmed])
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSING)
|
||||
node ! BITCOIN_CLOSE_DONE
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSED)
|
||||
}
|
||||
|
||||
"handle PKT_CLOSE in OPEN_WAIT_FOR_COMPLETE_THEIRANCHOR" in {
|
||||
val (node, ChannelDesc(Some(ourParams), Some(theirParams), Some(Commitment(_, ourCommitTx, state, _)))) = reachState_NOANCHOR(OPEN_WAIT_FOR_COMPLETE_THEIRANCHOR)
|
||||
// the only difference between their final tx and ours is the order of the outputs, because state is symmetric
|
||||
val closingState = state.reverse.adjust_fees(Globals.closing_fee * 1000, ourParams.anchorAmount.isDefined)
|
||||
val finalTx = makeFinalTx(ourCommitTx.txIn, theirParams.finalPubKey, ourFinalPubKey, closingState)
|
||||
val ourFinalSigForThem = bin2signature(Transaction.signInput(finalTx, 0, multiSig2of2(ourCommitPubKey, theirParams.commitPubKey), SIGHASH_ALL, ourParams.commitPrivKey))
|
||||
node ! close_channel(ourFinalSigForThem, 0)
|
||||
expectMsgClass(classOf[Publish])
|
||||
expectMsgClass(classOf[close_channel_complete])
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(WAIT_FOR_CLOSE_ACK)
|
||||
node ! close_channel_ack()
|
||||
node ! (BITCOIN_ANCHOR_SPENT, finalTx)
|
||||
expectMsgClass(classOf[WatchConfirmed])
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSING)
|
||||
node ! BITCOIN_CLOSE_DONE
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSED)
|
||||
}
|
||||
|
||||
"handle PKT_CLOSE in OPEN_WAIT_FOR_COMPLETE_OURANCHOR" in {
|
||||
val (node, ChannelDesc(Some(ourParams), Some(theirParams), Some(Commitment(_, ourCommitTx, state, _)))) = reachState_WITHANCHOR(OPEN_WAIT_FOR_COMPLETE_OURANCHOR)
|
||||
// the only difference between their final tx and ours is the order of the outputs, because state is symmetric
|
||||
val closingState = state.reverse.adjust_fees(Globals.closing_fee * 1000, ourParams.anchorAmount.isDefined)
|
||||
val finalTx = makeFinalTx(ourCommitTx.txIn, theirParams.finalPubKey, ourFinalPubKey, closingState)
|
||||
val ourFinalSigForThem = bin2signature(Transaction.signInput(finalTx, 0, multiSig2of2(ourCommitPubKey, theirParams.commitPubKey), SIGHASH_ALL, ourParams.commitPrivKey))
|
||||
node ! close_channel(ourFinalSigForThem, 0)
|
||||
expectMsgClass(classOf[Publish])
|
||||
expectMsgClass(classOf[close_channel_complete])
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(WAIT_FOR_CLOSE_ACK)
|
||||
node ! close_channel_ack()
|
||||
node ! (BITCOIN_ANCHOR_SPENT, finalTx)
|
||||
expectMsgClass(classOf[WatchConfirmed])
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSING)
|
||||
node ! BITCOIN_CLOSE_DONE
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSED)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,152 +0,0 @@
|
|||
package fr.acinq.eclair.channel
|
||||
|
||||
import akka.actor.Actor.Receive
|
||||
import akka.actor.FSM.{CurrentState, SubscribeTransitionCallBack, Transition}
|
||||
import akka.actor._
|
||||
import akka.testkit.{ImplicitSender, TestKit, TestProbe}
|
||||
import fr.acinq.bitcoin._
|
||||
import fr.acinq.eclair._
|
||||
import fr.acinq.eclair.blockchain._
|
||||
import lightning._
|
||||
import lightning.locktime.Locktime.Blocks
|
||||
import org.junit.runner.RunWith
|
||||
import org.scalatest._
|
||||
import org.scalatest.junit.JUnitRunner
|
||||
|
||||
|
||||
@RunWith(classOf[JUnitRunner])
|
||||
class ChannelSpec extends TestKit(ActorSystem("TestSystem")) with WordSpecLike with ShouldMatchers with ImplicitSender {
|
||||
|
||||
val anchorAmount = 100100000L
|
||||
|
||||
// Alice is funder, Bob is not
|
||||
|
||||
object Alice {
|
||||
val (Base58.Prefix.SecretKeyTestnet, commitPrivKey) = Base58Check.decode("cQPmcNr6pwBQPyGfab3SksE9nTCtx9ism9T4dkS9dETNU2KKtJHk")
|
||||
val (Base58.Prefix.SecretKeyTestnet, finalPrivKey) = Base58Check.decode("cUrAtLtV7GGddqdkhUxnbZVDWGJBTducpPoon3eKp9Vnr1zxs6BG")
|
||||
val channelParams = OurChannelParams(locktime(Blocks(10)), commitPrivKey, finalPrivKey, 1, 100000, "alice-seed".getBytes(), Some(anchorAmount))
|
||||
}
|
||||
|
||||
object Bob {
|
||||
val (Base58.Prefix.SecretKeyTestnet, commitPrivKey) = Base58Check.decode("cSUwLtdZ2tht9ZmHhdQue48pfe7tY2GT2TGWJDtjoZgo6FHrubGk")
|
||||
val (Base58.Prefix.SecretKeyTestnet, finalPrivKey) = Base58Check.decode("cPR7ZgXpUaDPA3GwGceMDS5pfnSm955yvks3yELf3wMJwegsdGTg")
|
||||
val channelParams = OurChannelParams(locktime(Blocks(10)), commitPrivKey, finalPrivKey, 2, 100000, "bob-seed".getBytes(), None)
|
||||
}
|
||||
|
||||
"channel" should {
|
||||
"open, propose, accept, fulfill htlcs and close" in {
|
||||
val blockchain = TestProbe("blockchain")
|
||||
blockchain.ignoreMsg {
|
||||
case m: WatchConfirmed => true
|
||||
case m: WatchSpent => true
|
||||
case m: WatchLost => true
|
||||
}
|
||||
val pipe = system.actorOf(Props[ChannelSpec.Pipe])
|
||||
val alice = system.actorOf(Channel.props(pipe, blockchain.ref, Alice.channelParams), "Alice")
|
||||
val bob = system.actorOf(Channel.props(pipe, blockchain.ref, Bob.channelParams), "Bob")
|
||||
|
||||
val monitora = TestProbe()
|
||||
val monitorb = TestProbe()
|
||||
|
||||
alice ! SubscribeTransitionCallBack(monitora.ref)
|
||||
val CurrentState(_, OPEN_WAIT_FOR_OPEN_WITHANCHOR) = monitora.expectMsgClass(classOf[CurrentState[_]])
|
||||
|
||||
bob ! SubscribeTransitionCallBack(monitorb.ref)
|
||||
val CurrentState(_, OPEN_WAIT_FOR_OPEN_NOANCHOR) = monitorb.expectMsgClass(classOf[CurrentState[_]])
|
||||
|
||||
pipe ! alice
|
||||
pipe ! bob
|
||||
|
||||
def waitForAliceTransition = monitora.expectMsgClass(classOf[Transition[_]])
|
||||
def waitForBobTransition = monitorb.expectMsgClass(classOf[Transition[_]])
|
||||
|
||||
val MakeAnchor(_, _, amount) = blockchain.expectMsgClass(classOf[MakeAnchor])
|
||||
val anchorTx = Transaction(version = 1,
|
||||
txIn = Seq.empty[TxIn],
|
||||
txOut = TxOut(amount, Scripts.anchorPubkeyScript(Alice.channelParams.commitPubKey, Bob.channelParams.commitPubKey)) :: Nil,
|
||||
lockTime = 0
|
||||
)
|
||||
blockchain.reply((anchorTx, 0))
|
||||
blockchain.expectMsgClass(classOf[Publish])
|
||||
|
||||
val Transition(_, OPEN_WAIT_FOR_OPEN_WITHANCHOR, OPEN_WAIT_FOR_COMMIT_SIG) = waitForAliceTransition
|
||||
val Transition(_, OPEN_WAIT_FOR_OPEN_NOANCHOR, OPEN_WAIT_FOR_ANCHOR) = waitForBobTransition
|
||||
|
||||
val Transition(_, OPEN_WAIT_FOR_COMMIT_SIG, OPEN_WAITING_OURANCHOR) = waitForAliceTransition
|
||||
val Transition(_, OPEN_WAIT_FOR_ANCHOR, OPEN_WAITING_THEIRANCHOR) = waitForBobTransition
|
||||
|
||||
blockchain.send(alice, BITCOIN_ANCHOR_DEPTHOK)
|
||||
|
||||
blockchain.send(bob, BITCOIN_ANCHOR_DEPTHOK)
|
||||
|
||||
val Transition(_, OPEN_WAITING_OURANCHOR, OPEN_WAIT_FOR_COMPLETE_OURANCHOR) = waitForAliceTransition
|
||||
val Transition(_, OPEN_WAITING_THEIRANCHOR, OPEN_WAIT_FOR_COMPLETE_THEIRANCHOR) = waitForBobTransition
|
||||
|
||||
val Transition(_, OPEN_WAIT_FOR_COMPLETE_OURANCHOR, NORMAL_HIGHPRIO) = waitForAliceTransition
|
||||
val Transition(_, OPEN_WAIT_FOR_COMPLETE_THEIRANCHOR, NORMAL_LOWPRIO) = waitForBobTransition
|
||||
|
||||
val R: BinaryData = "0102030405060708010203040506070801020304050607080102030405060708"
|
||||
val H = Crypto.sha256(R)
|
||||
alice ! CMD_SEND_HTLC_UPDATE(60000000, H, locktime(Blocks(4)))
|
||||
|
||||
val Transition(_, NORMAL_HIGHPRIO, WAIT_FOR_HTLC_ACCEPT_HIGHPRIO) = waitForAliceTransition
|
||||
val Transition(_, NORMAL_LOWPRIO, WAIT_FOR_UPDATE_SIG_LOWPRIO) = waitForBobTransition
|
||||
|
||||
val Transition(_, WAIT_FOR_HTLC_ACCEPT_HIGHPRIO, WAIT_FOR_UPDATE_COMPLETE_HIGHPRIO) = waitForAliceTransition
|
||||
val Transition(_, WAIT_FOR_UPDATE_COMPLETE_HIGHPRIO, NORMAL_LOWPRIO) = waitForAliceTransition
|
||||
val Transition(_, WAIT_FOR_UPDATE_SIG_LOWPRIO, NORMAL_HIGHPRIO) = waitForBobTransition
|
||||
|
||||
bob ! CMD_SEND_HTLC_FULFILL(R)
|
||||
|
||||
val Transition(_, NORMAL_LOWPRIO, WAIT_FOR_UPDATE_SIG_LOWPRIO) = waitForAliceTransition
|
||||
val Transition(_, NORMAL_HIGHPRIO, WAIT_FOR_HTLC_ACCEPT_HIGHPRIO) = waitForBobTransition
|
||||
|
||||
val Transition(_, WAIT_FOR_UPDATE_SIG_LOWPRIO, NORMAL_HIGHPRIO) = waitForAliceTransition
|
||||
val Transition(_, WAIT_FOR_HTLC_ACCEPT_HIGHPRIO, WAIT_FOR_UPDATE_COMPLETE_HIGHPRIO) = waitForBobTransition
|
||||
val Transition(_, WAIT_FOR_UPDATE_COMPLETE_HIGHPRIO, NORMAL_LOWPRIO) = waitForBobTransition
|
||||
|
||||
alice ! CMD_CLOSE(10000)
|
||||
val Transition(_, NORMAL_HIGHPRIO, WAIT_FOR_CLOSE_COMPLETE) = waitForAliceTransition
|
||||
val Transition(_, NORMAL_LOWPRIO, WAIT_FOR_CLOSE_ACK) = waitForBobTransition
|
||||
|
||||
val Publish(closingTx1) = blockchain.expectMsgClass(classOf[Publish])
|
||||
val Publish(closingTx2) = blockchain.expectMsgClass(classOf[Publish])
|
||||
|
||||
val Transition(_, WAIT_FOR_CLOSE_COMPLETE, CLOSING) = waitForAliceTransition
|
||||
val Transition(_, WAIT_FOR_CLOSE_ACK, CLOSING) = waitForBobTransition
|
||||
|
||||
blockchain.send(alice, (BITCOIN_ANCHOR_SPENT, closingTx1))
|
||||
blockchain.send(bob, (BITCOIN_ANCHOR_SPENT, closingTx1))
|
||||
|
||||
blockchain.send(alice, BITCOIN_CLOSE_DONE)
|
||||
blockchain.send(bob, BITCOIN_CLOSE_DONE)
|
||||
|
||||
val Transition(_, CLOSING, CLOSED) = waitForAliceTransition
|
||||
val Transition(_, CLOSING, CLOSED) = waitForBobTransition
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object ChannelSpec {
|
||||
// handle a bi-directional path between 2 actors
|
||||
// used to avoid the chicken-and-egg problem of:
|
||||
// a = new Channel(b)
|
||||
// b = new Channel(a)
|
||||
class Pipe extends Actor with Stash {
|
||||
|
||||
override def unhandled(message: Any): Unit = stash()
|
||||
|
||||
def receive = {
|
||||
case a: ActorRef => context become receive1(a)
|
||||
}
|
||||
def receive1(a: ActorRef): Receive = {
|
||||
case b: ActorRef =>
|
||||
unstashAll()
|
||||
context become receive2(a, b)
|
||||
}
|
||||
def receive2(a: ActorRef, b: ActorRef): Receive = {
|
||||
case msg if sender() == a => b forward msg
|
||||
case msg if sender() == b => a forward msg
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
package fr.acinq.eclair.channel
|
||||
|
||||
import fr.acinq.eclair._
|
||||
import org.junit.runner.RunWith
|
||||
import org.scalatest.junit.JUnitRunner
|
||||
|
||||
/**
|
||||
* Created by PM on 05/01/2016.
|
||||
*/
|
||||
@RunWith(classOf[JUnitRunner])
|
||||
class CornerCasesSpec extends TestHelper {
|
||||
|
||||
"Node" must {
|
||||
|
||||
/*"handle a reorg where we were waiting for a close on the old chain to be buried enough and they commited a revoked tx on the new main chain" in {
|
||||
val (node, ChannelDesc(Some(ourParams), Some(theirParams), Some(commitment))) = reachState_WITHANCHOR(NORMAL_HIGHPRIO)
|
||||
// the only difference between their final tx and ours is the order of the outputs, because state is symmetric
|
||||
val theirFinalTx = makeFinalTx(commitment.tx.txIn, theirParams.finalPubKey, ourFinalPubKey, commitment.state.reverse)
|
||||
val ourFinalSigForThem = bin2signature(Transaction.signInput(theirFinalTx, 0, multiSig2of2(ourCommitPubKey, theirParams.commitPubKey), SIGHASH_ALL, ourParams.commitPrivKey))
|
||||
node ! close_channel(ourFinalSigForThem, 0)
|
||||
expectMsgClass(classOf[Watch])
|
||||
expectMsgClass(classOf[Publish])
|
||||
expectMsgClass(classOf[close_channel_complete])
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(WAIT_FOR_CLOSE_ACK)
|
||||
node ! close_channel_ack()
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSE_WAIT_CLOSE)
|
||||
|
||||
// reorg ! they spent a revoked tx
|
||||
node ! BITCOIN_ANCHOR_OTHERSPEND
|
||||
|
||||
// we steal and close
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSE_WAIT_STEAL_CLOSE)
|
||||
node ! BITCOIN_STEAL_DONE
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSED)
|
||||
}*/
|
||||
}
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
package fr.acinq.eclair.channel
|
||||
|
||||
import fr.acinq.bitcoin.{Crypto, ScriptFlags, Transaction}
|
||||
import fr.acinq.eclair._
|
||||
import fr.acinq.eclair.channel.Scripts._
|
||||
import fr.acinq.eclair.crypto.ShaChain
|
||||
import lightning._
|
||||
import lightning.locktime.Locktime.Blocks
|
||||
import lightning.update_decline_htlc.Reason.{CannotRoute, InsufficientFunds}
|
||||
import org.junit.runner.RunWith
|
||||
import org.scalatest.junit.JUnitRunner
|
||||
|
||||
/**
|
||||
* Created by PM on 11/09/2015.
|
||||
*/
|
||||
@RunWith(classOf[JUnitRunner])
|
||||
class HTLCFailureSpec extends TestHelper {
|
||||
|
||||
"Node" must {
|
||||
|
||||
"handle an update_decline_htlc when in WAIT_FOR_HTLC_ACCEPT_HIGHPRIO" in {
|
||||
val (node, ChannelDesc(Some(ourParams), Some(theirParams), Some(previousCommitment))) = reachState_WITHANCHOR(NORMAL_HIGHPRIO)
|
||||
val their_r = sha256_hash(1, 2, 1, 2)
|
||||
val their_rHash = Crypto.sha256(their_r)
|
||||
node ! CMD_SEND_HTLC_UPDATE(60000000, their_rHash, locktime(Blocks(4)))
|
||||
expectMsgClass(classOf[update_add_htlc])
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(WAIT_FOR_HTLC_ACCEPT_HIGHPRIO)
|
||||
|
||||
node ! update_decline_htlc(CannotRoute(true))
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(NORMAL_HIGHPRIO)
|
||||
}
|
||||
|
||||
"handle an update_decline_htlc when in WAIT_FOR_HTLC_ACCEPT_LOWPRIO" in {
|
||||
val (node, ChannelDesc(Some(ourParams), Some(theirParams), Some(previousCommitment))) = reachState_NOANCHOR(NORMAL_LOWPRIO)
|
||||
val their_r = sha256_hash(1, 2, 1, 2)
|
||||
val their_rHash = Crypto.sha256(their_r)
|
||||
node ! CMD_SEND_HTLC_UPDATE(60000000, their_rHash, locktime(Blocks(4)))
|
||||
expectMsgClass(classOf[update_add_htlc])
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(WAIT_FOR_HTLC_ACCEPT_LOWPRIO)
|
||||
|
||||
node ! update_decline_htlc(InsufficientFunds(funding(None)))
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(NORMAL_LOWPRIO)
|
||||
}
|
||||
|
||||
"handle an update_routefail_htlc when in NORMAL_HIGHPRIO" in {
|
||||
val (node, channelDesc0) = reachState_NOANCHOR(NORMAL_LOWPRIO)
|
||||
|
||||
val (channelDesc1@ChannelDesc(Some(ourParams), Some(theirParams), Some(previousCommitment)), r) = send_htlc(node, channelDesc0, 40000000)
|
||||
val state1 = previousCommitment.state
|
||||
val their_rHash = Crypto.sha256(r)
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(NORMAL_HIGHPRIO)
|
||||
|
||||
val state2 = state1.htlc_remove(their_rHash)
|
||||
val ourRevocationHash2 = Crypto.sha256(ShaChain.shaChainFromSeed(ourParams.shaSeed, 2))
|
||||
node ! update_routefail_htlc(ourRevocationHash2, their_rHash)
|
||||
|
||||
val update_accept(theirSig2, theirRevocationHash2) = expectMsgClass(classOf[update_accept])
|
||||
val (ourCommitTx2, ourSigForThem2) = sign_their_commitment_tx(ourParams, theirParams, previousCommitment.tx.txIn, state2, ourRevocationHash2, theirRevocationHash2)
|
||||
val signedCommitTx2 = sign_our_commitment_tx(ourParams, theirParams, ourCommitTx2, theirSig2)
|
||||
Transaction.correctlySpends(signedCommitTx2, Map(previousCommitment.tx.txIn(0).outPoint -> anchorPubkeyScript(ourCommitPubKey, theirParams.commitPubKey)), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(WAIT_FOR_UPDATE_SIG_HIGHPRIO)
|
||||
|
||||
node ! update_signature(ourSigForThem2, ShaChain.shaChainFromSeed(ourParams.shaSeed, 1))
|
||||
val update_complete(theirRevocationPreimage1) = expectMsgClass(classOf[update_complete])
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(NORMAL_LOWPRIO)
|
||||
}
|
||||
|
||||
"handle an update_routefail_htlc when in WAIT_FOR_HTLC_ACCEPT_LOWPRIO" in {
|
||||
// TODO
|
||||
}
|
||||
|
||||
"handle an update_timedout_htlc when in NORMAL_HIGHPRIO" in {
|
||||
// TODO
|
||||
}
|
||||
|
||||
"handle an update_timedout_htlc when in NORMAL_LOWPRIO" in {
|
||||
// TODO
|
||||
}
|
||||
|
||||
"handle an update_timedout_htlc when in WAIT_FOR_HTLC_ACCEPT_LOWPRIO" in {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
package fr.acinq.eclair.channel
|
||||
|
||||
import org.junit.runner.RunWith
|
||||
import org.scalatest.junit.JUnitRunner
|
||||
|
||||
/**
|
||||
* Created by PM on 08/09/2015.
|
||||
*/
|
||||
@RunWith(classOf[JUnitRunner])
|
||||
class HTLCUpdateSpec extends TestHelper {
|
||||
|
||||
"Node" must {
|
||||
|
||||
"successfully receive an htlc in NORMAL_LOWPRIO" in {
|
||||
val (node, channelDesc0) = reachState_NOANCHOR(NORMAL_LOWPRIO)
|
||||
|
||||
val (channelDesc1, r) = send_htlc(node, channelDesc0, 40000000)
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(NORMAL_HIGHPRIO)
|
||||
|
||||
val channelDesc2 = send_fulfill_htlc(node, channelDesc1, r)
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(NORMAL_LOWPRIO)
|
||||
}
|
||||
|
||||
"successfully send an htlc in NORMAL_HIGHPRIO" in {
|
||||
val (node, channelDesc0) = reachState_WITHANCHOR(NORMAL_HIGHPRIO)
|
||||
|
||||
val (channelDesc1, r) = receive_htlc(node, channelDesc0, 40000000)
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(NORMAL_LOWPRIO)
|
||||
|
||||
val channelDesc2 = receive_fulfill_htlc(node, channelDesc1, r)
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(NORMAL_HIGHPRIO)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,245 +0,0 @@
|
|||
package fr.acinq.eclair.channel
|
||||
|
||||
import akka.actor.{ActorRef, ActorSystem, Props}
|
||||
import akka.testkit.{ImplicitSender, TestKit}
|
||||
import com.google.protobuf.ByteString
|
||||
import fr.acinq.bitcoin._
|
||||
import fr.acinq.eclair._
|
||||
import fr.acinq.eclair.blockchain._
|
||||
import fr.acinq.eclair.crypto.ShaChain
|
||||
import fr.acinq.eclair.channel.Scripts._
|
||||
import lightning._
|
||||
import lightning.locktime.Locktime.Blocks
|
||||
import lightning.open_channel.anchor_offer.{WILL_CREATE_ANCHOR, WONT_CREATE_ANCHOR}
|
||||
import org.bouncycastle.util.encoders.Hex
|
||||
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
|
||||
|
||||
import scala.util.Random
|
||||
|
||||
/**
|
||||
* Created by PM on 08/09/2015.
|
||||
*/
|
||||
abstract class TestHelper(_system: ActorSystem) extends TestKit(_system) with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll {
|
||||
|
||||
def this() = this(ActorSystem("MySpec"))
|
||||
|
||||
override def afterAll {
|
||||
TestKit.shutdownActorSystem(system)
|
||||
}
|
||||
|
||||
val previousTxOutput = OutPoint(Hex.decode("7727730d21428276a4d6b0e16f3a3e6f3a07a07dc67151e6a88d4a8c3e8edb24").reverse, 1)
|
||||
val signData = SignData("76a914e093fbc19866b98e0fbc25d79d0ad0f0375170af88ac", Base58Check.decode("cU1YgK56oUKAtV6XXHZeJQjEx1KGXkZS1pGiKpyW4mUyKYFJwWFg")._2)
|
||||
|
||||
val our_commitkey_priv = Base58Check.decode("cQPmcNr6pwBQPyGfab3SksE9nTCtx9ism9T4dkS9dETNU2KKtJHk")._2
|
||||
val our_finalkey_priv = Base58Check.decode("cUrAtLtV7GGddqdkhUxnbZVDWGJBTducpPoon3eKp9Vnr1zxs6BG")._2
|
||||
val bob_commit_priv = Base58Check.decode("cSUwLtdZ2tht9ZmHhdQue48pfe7tY2GT2TGWJDtjoZgo6FHrubGk")._2
|
||||
val bob_final_priv = Base58Check.decode("cPR7ZgXpUaDPA3GwGceMDS5pfnSm955yvks3yELf3wMJwegsdGTg")._2
|
||||
|
||||
val anchorAmount = 100100000L
|
||||
|
||||
val ourParams = OurChannelParams(locktime(Blocks(10)), our_commitkey_priv, our_finalkey_priv, 1, 100000, "alice-seed".getBytes(), Some(anchorAmount))
|
||||
val bob_params = OurChannelParams(locktime(Blocks(10)), bob_commit_priv, bob_final_priv, 2, 100000, "bob-seed".getBytes(), None)
|
||||
|
||||
val ourCommitPubKey = bitcoin_pubkey(ByteString.copyFrom(Crypto.publicKeyFromPrivateKey(our_commitkey_priv.key.toByteArray)))
|
||||
val ourFinalPubKey = bitcoin_pubkey(ByteString.copyFrom(Crypto.publicKeyFromPrivateKey(our_finalkey_priv.key.toByteArray)))
|
||||
|
||||
case class ChannelDesc(ourParams: Option[OurChannelParams] = None, theirParams: Option[TheirChannelParams] = None, ourCommitment: Option[Commitment] = None)
|
||||
|
||||
def reachState_NOANCHOR(targetState: State): (ActorRef, ChannelDesc) = {
|
||||
var channelDesc = ChannelDesc()
|
||||
val node = system.actorOf(Channel.props(self, self, bob_params))
|
||||
val their_open_channel = expectMsgClass(classOf[open_channel])
|
||||
val theirParams = TheirChannelParams(their_open_channel.delay, their_open_channel.commitKey, their_open_channel.finalKey, their_open_channel.minDepth, their_open_channel.commitmentFee)
|
||||
channelDesc = channelDesc.copy(theirParams = Some(theirParams))
|
||||
val theirRevocationHash = their_open_channel.revocationHash
|
||||
val ourRevocationHash = Crypto.sha256(ShaChain.shaChainFromSeed(ourParams.shaSeed, 0))
|
||||
node ! CMD_GETSTATE // node is in OPEN_WAIT_FOR_OPEN_NOANCHOR
|
||||
if (expectMsgClass(classOf[State]) == targetState) return (node, channelDesc)
|
||||
channelDesc = channelDesc.copy(ourParams = Some(ourParams.copy(anchorAmount = None)))
|
||||
node ! open_channel(ourParams.delay, ourRevocationHash, ourCommitPubKey, ourFinalPubKey, WILL_CREATE_ANCHOR, Some(ourParams.minDepth), ourParams.commitmentFee)
|
||||
val (anchorTx, anchorOutputIndex) = makeAnchorTx(ourCommitPubKey, theirParams.commitPubKey, anchorAmount, previousTxOutput, signData)
|
||||
// we fund the channel with the anchor tx, so the money is ours
|
||||
val state = ChannelState(them = ChannelOneSide(0, 0, Seq()), us = ChannelOneSide(ourParams.anchorAmount.get * 1000 - ourParams.commitmentFee * 1000, ourParams.commitmentFee * 1000, Seq()))
|
||||
// we build our commitment tx, leaving it unsigned
|
||||
val ourCommitTx = makeCommitTx(ourFinalPubKey, theirParams.finalPubKey, ourParams.delay, anchorTx.hash, anchorOutputIndex, ourRevocationHash, state)
|
||||
channelDesc = channelDesc.copy(ourCommitment = Some(Commitment(0, ourCommitTx, state, theirRevocationHash)))
|
||||
// then we build their commitment tx and sign it
|
||||
val theirCommitTx = makeCommitTx(theirParams.finalPubKey, ourFinalPubKey, theirParams.delay, anchorTx.hash, anchorOutputIndex, theirRevocationHash, state.reverse)
|
||||
val ourSigForThem = bin2signature(Transaction.signInput(theirCommitTx, 0, multiSig2of2(ourCommitPubKey, theirParams.commitPubKey), SIGHASH_ALL, ourParams.commitPrivKey))
|
||||
node ! CMD_GETSTATE // node is in OPEN_WAIT_FOR_ANCHOR
|
||||
if (expectMsgClass(classOf[State]) == targetState) return (node, channelDesc)
|
||||
node ! open_anchor(anchorTx.hash, 0, anchorAmount, ourSigForThem)
|
||||
expectMsgClass(classOf[open_commit_sig])
|
||||
expectMsgClass(classOf[WatchConfirmed])
|
||||
expectMsgClass(classOf[WatchSpent])
|
||||
node ! CMD_GETSTATE // node is in OPEN_WAITING_THEIRANCHOR
|
||||
if (expectMsgClass(classOf[State]) == targetState) return (node, channelDesc)
|
||||
node ! BITCOIN_ANCHOR_DEPTHOK
|
||||
expectMsgClass(classOf[WatchLost])
|
||||
expectMsgClass(classOf[open_complete])
|
||||
node ! CMD_GETSTATE // node is in OPEN_WAIT_FOR_COMPLETE_THEIRANCHOR
|
||||
if (expectMsgClass(classOf[State]) == targetState) return (node, channelDesc)
|
||||
node ! open_complete(None)
|
||||
node ! CMD_GETSTATE // node is in NORMAL_LOWPRIO
|
||||
if (expectMsgClass(classOf[State]) == targetState) return (node, channelDesc)
|
||||
???
|
||||
}
|
||||
|
||||
def reachState_WITHANCHOR(targetState: State): (ActorRef, ChannelDesc) = {
|
||||
var channelDesc = ChannelDesc()
|
||||
val node = system.actorOf(Channel.props(self, self, bob_params.copy(anchorAmount = Some(anchorAmount))))
|
||||
//node ! INPUT_NONE
|
||||
val their_open_channel = expectMsgClass(classOf[open_channel])
|
||||
val theirParams = TheirChannelParams(their_open_channel.delay, their_open_channel.commitKey, their_open_channel.finalKey, their_open_channel.minDepth, their_open_channel.commitmentFee)
|
||||
channelDesc = channelDesc.copy(theirParams = Some(theirParams))
|
||||
val theirRevocationHash = their_open_channel.revocationHash
|
||||
val ourRevocationHash = Crypto.sha256(ShaChain.shaChainFromSeed(ourParams.shaSeed, 0))
|
||||
node ! CMD_GETSTATE // node is in OPEN_WAIT_FOR_OPEN_WITHANCHOR
|
||||
if (expectMsgClass(classOf[State]) == targetState) return (node, channelDesc)
|
||||
channelDesc = channelDesc.copy(ourParams = Some(ourParams))
|
||||
node ! open_channel(ourParams.delay, ourRevocationHash, ourCommitPubKey, ourFinalPubKey, WONT_CREATE_ANCHOR, Some(ourParams.minDepth), ourParams.commitmentFee)
|
||||
val MakeAnchor(_, _, amount) = expectMsgClass(classOf[MakeAnchor])
|
||||
val anchorTx = Transaction(version = 1,
|
||||
txIn = Seq.empty[TxIn],
|
||||
txOut = TxOut(amount, Scripts.anchorPubkeyScript(ourCommitPubKey, theirParams.commitPubKey)) :: Nil,
|
||||
lockTime = 0
|
||||
)
|
||||
node ! (anchorTx, 0)
|
||||
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 * 1000 - ourParams.commitmentFee * 1000, ourParams.commitmentFee * 1000, Seq()), us = ChannelOneSide(0, 0, Seq()))
|
||||
// we build our commitment tx, leaving it unsigned
|
||||
val ourCommitTx = makeCommitTx(ourFinalPubKey, theirParams.finalPubKey, theirParams.delay, their_open_anchor.txid, their_open_anchor.outputIndex, ourRevocationHash, state)
|
||||
channelDesc = channelDesc.copy(ourCommitment = Some(Commitment(0, ourCommitTx, state, theirRevocationHash)))
|
||||
// then we build their commitment tx and sign it
|
||||
val theirCommitTx = makeCommitTx(theirParams.finalPubKey, ourFinalPubKey, ourParams.delay, their_open_anchor.txid, their_open_anchor.outputIndex, theirRevocationHash, state.reverse)
|
||||
val ourSigForThem = bin2signature(Transaction.signInput(theirCommitTx, 0, multiSig2of2(ourCommitPubKey, theirParams.commitPubKey), SIGHASH_ALL, ourParams.commitPrivKey))
|
||||
node ! CMD_GETSTATE // node is in OPEN_WAIT_FOR_COMMIT_SIG
|
||||
if (expectMsgClass(classOf[State]) == targetState) return (node, channelDesc)
|
||||
node ! open_commit_sig(ourSigForThem)
|
||||
expectMsgClass(classOf[WatchConfirmed])
|
||||
expectMsgClass(classOf[WatchSpent])
|
||||
expectMsgClass(classOf[Publish])
|
||||
node ! CMD_GETSTATE // node is in OPEN_WAITING_OURANCHOR
|
||||
if (expectMsgClass(classOf[State]) == targetState) return (node, channelDesc)
|
||||
node ! BITCOIN_ANCHOR_DEPTHOK
|
||||
expectMsgClass(classOf[WatchLost])
|
||||
expectMsgClass(classOf[open_complete])
|
||||
node ! CMD_GETSTATE // node is in OPEN_WAIT_FOR_COMPLETE_OURANCHOR
|
||||
if (expectMsgClass(classOf[State]) == targetState) return (node, channelDesc)
|
||||
node ! open_complete(None)
|
||||
node ! CMD_GETSTATE // node is in NORMAL_HIGHPRIO
|
||||
if (expectMsgClass(classOf[State]) == targetState) return (node, channelDesc)
|
||||
???
|
||||
}
|
||||
|
||||
val random = new Random()
|
||||
|
||||
def random_r = sha256_hash(random.nextLong(), random.nextLong(), random.nextLong(), random.nextLong())
|
||||
|
||||
def send_htlc(node: ActorRef, channelDesc: ChannelDesc, amount: Int): (ChannelDesc, sha256_hash) = {
|
||||
val ChannelDesc(Some(ourParams), Some(theirParams), Some(previousCommitment)) = channelDesc
|
||||
val ourRevocationHash1 = Crypto.sha256(ShaChain.shaChainFromSeed(ourParams.shaSeed, previousCommitment.index + 1))
|
||||
val r = random_r
|
||||
val rHash = Crypto.sha256(r)
|
||||
val htlc = update_add_htlc(ourRevocationHash1, amount, rHash, locktime(Blocks(4)))
|
||||
val state1 = previousCommitment.state.htlc_send(Htlc(amount, rHash, htlc.expiry, Nil, None))
|
||||
|
||||
node ! htlc
|
||||
val update_accept(theirSig1, theirRevocationHash1) = expectMsgClass(classOf[update_accept])
|
||||
val (ourCommitTx1, ourSigForThem1) = sign_their_commitment_tx(ourParams, theirParams, previousCommitment.tx.txIn, state1, ourRevocationHash1, theirRevocationHash1)
|
||||
val signedCommitTx1 = sign_our_commitment_tx(ourParams, theirParams, ourCommitTx1, theirSig1)
|
||||
Transaction.correctlySpends(signedCommitTx1, Map(previousCommitment.tx.txIn(0).outPoint -> anchorPubkeyScript(ourCommitPubKey, theirParams.commitPubKey)), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)
|
||||
|
||||
node ! update_signature(ourSigForThem1, ShaChain.shaChainFromSeed(ourParams.shaSeed, previousCommitment.index))
|
||||
val update_complete(theirRevocationPreimage0) = expectMsgClass(classOf[update_complete])
|
||||
assert(Crypto.sha256(theirRevocationPreimage0).deep === sha2562bin(previousCommitment.theirRevocationHash).data.toArray.deep)
|
||||
|
||||
(channelDesc.copy(ourCommitment = Some(Commitment(previousCommitment.index + 1, signedCommitTx1, state1, theirRevocationHash1))), r)
|
||||
}
|
||||
|
||||
def send_fulfill_htlc(node: ActorRef, channelDesc: ChannelDesc, r: sha256_hash): ChannelDesc = {
|
||||
val ChannelDesc(Some(ourParams), Some(theirParams), Some(previousCommitment)) = channelDesc
|
||||
|
||||
val ourRevocationHash2 = Crypto.sha256(ShaChain.shaChainFromSeed(ourParams.shaSeed, previousCommitment.index + 1))
|
||||
val state2 = previousCommitment.state.htlc_fulfill(r)
|
||||
|
||||
node ! update_fulfill_htlc(ourRevocationHash2, r)
|
||||
val update_accept(theirSig2, theirRevocationHash2) = expectMsgClass(classOf[update_accept])
|
||||
val (ourCommitTx2, ourSigForThem2) = sign_their_commitment_tx(ourParams, theirParams, previousCommitment.tx.txIn, state2, ourRevocationHash2, theirRevocationHash2)
|
||||
val signedCommitTx2 = sign_our_commitment_tx(ourParams, theirParams, ourCommitTx2, theirSig2)
|
||||
Transaction.correctlySpends(signedCommitTx2, Map(previousCommitment.tx.txIn(0).outPoint -> anchorPubkeyScript(ourCommitPubKey, theirParams.commitPubKey)), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)
|
||||
|
||||
node ! update_signature(ourSigForThem2, ShaChain.shaChainFromSeed(ourParams.shaSeed, previousCommitment.index))
|
||||
val update_complete(theirRevocationPreimage1) = expectMsgClass(classOf[update_complete])
|
||||
assert(Crypto.sha256(theirRevocationPreimage1).deep === sha2562bin(previousCommitment.theirRevocationHash).data.toArray.deep)
|
||||
|
||||
channelDesc.copy(ourCommitment = Some(Commitment(previousCommitment.index + 1, signedCommitTx2, state2, theirRevocationHash2)))
|
||||
}
|
||||
|
||||
def receive_htlc(node: ActorRef, channelDesc: ChannelDesc, amount: Int): (ChannelDesc, sha256_hash) = {
|
||||
val ChannelDesc(Some(ourParams), Some(theirParams), Some(previousCommitment)) = channelDesc
|
||||
val state0 = previousCommitment.state
|
||||
val theirRevocationHash0 = previousCommitment.theirRevocationHash
|
||||
|
||||
val their_r = random_r
|
||||
val their_rHash = Crypto.sha256(their_r)
|
||||
node ! CMD_SEND_HTLC_UPDATE(amount, their_rHash, locktime(Blocks(4)))
|
||||
|
||||
val htlc@update_add_htlc(theirRevocationHash1, amount_, rHash, expiry, _) = expectMsgClass(classOf[update_add_htlc])
|
||||
assert(amount === amount_)
|
||||
val state1 = state0.htlc_receive(Htlc(htlc.amountMsat, rHash, htlc.expiry, Nil, None))
|
||||
val ourRevocationHash1 = Crypto.sha256(ShaChain.shaChainFromSeed(ourParams.shaSeed, previousCommitment.index + 1))
|
||||
val (ourCommitTx1, ourSigForThem1) = sign_their_commitment_tx(ourParams, theirParams, previousCommitment.tx.txIn, state1, ourRevocationHash1, theirRevocationHash1)
|
||||
|
||||
node ! update_accept(ourSigForThem1, ourRevocationHash1)
|
||||
val update_signature(theirSig1, theirRevocationPreimage0) = expectMsgClass(classOf[update_signature])
|
||||
assert(Crypto.sha256(theirRevocationPreimage0).deep === sha2562bin(theirRevocationHash0).data.toArray.deep)
|
||||
val signedCommitTx1 = sign_our_commitment_tx(ourParams, theirParams, ourCommitTx1, theirSig1)
|
||||
Transaction.correctlySpends(signedCommitTx1, Map(previousCommitment.tx.txIn(0).outPoint -> anchorPubkeyScript(ourCommitPubKey, theirParams.commitPubKey)), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)
|
||||
|
||||
node ! update_complete(ShaChain.shaChainFromSeed(ourParams.shaSeed, previousCommitment.index))
|
||||
|
||||
(channelDesc.copy(ourCommitment = Some(Commitment(previousCommitment.index + 1, signedCommitTx1, state1, theirRevocationHash1))), their_r)
|
||||
}
|
||||
|
||||
def receive_fulfill_htlc(node: ActorRef, channelDesc: ChannelDesc, r: sha256_hash): ChannelDesc = {
|
||||
val ChannelDesc(Some(ourParams), Some(theirParams), Some(previousCommitment)) = channelDesc
|
||||
val state1 = previousCommitment.state
|
||||
val theirRevocationHash1 = previousCommitment.theirRevocationHash
|
||||
|
||||
node ! CMD_SEND_HTLC_FULFILL(r)
|
||||
val update_fulfill_htlc(theirRevocationHash2, r_) = expectMsgClass(classOf[update_fulfill_htlc])
|
||||
assert(r === r_)
|
||||
val state2 = previousCommitment.state.htlc_fulfill(r)
|
||||
val ourRevocationHash2 = Crypto.sha256(ShaChain.shaChainFromSeed(ourParams.shaSeed, previousCommitment.index + 1))
|
||||
val (ourCommitTx2, ourSigForThem2) = sign_their_commitment_tx(ourParams, theirParams, previousCommitment.tx.txIn, state2, ourRevocationHash2, theirRevocationHash2)
|
||||
|
||||
node ! update_accept(ourSigForThem2, ourRevocationHash2)
|
||||
val update_signature(theirSig2, theirRevocationPreimage1) = expectMsgClass(classOf[update_signature])
|
||||
assert(Crypto.sha256(theirRevocationPreimage1).deep === sha2562bin(previousCommitment.theirRevocationHash).data.toArray.deep)
|
||||
val signedCommitTx2 = sign_our_commitment_tx(ourParams, theirParams, ourCommitTx2, theirSig2)
|
||||
Transaction.correctlySpends(signedCommitTx2, Map(previousCommitment.tx.txIn(0).outPoint -> anchorPubkeyScript(ourCommitPubKey, theirParams.commitPubKey)), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)
|
||||
|
||||
node ! update_complete(ShaChain.shaChainFromSeed(ourParams.shaSeed, previousCommitment.index))
|
||||
|
||||
channelDesc.copy(ourCommitment = Some(Commitment(previousCommitment.index + 1, signedCommitTx2, state2, theirRevocationHash2)))
|
||||
}
|
||||
|
||||
|
||||
def sign_their_commitment_tx(ourParams: OurChannelParams, theirParams: TheirChannelParams, inputs: Seq[TxIn], newState: ChannelState, ourRevocationHash: sha256_hash, theirRevocationHash: sha256_hash): (Transaction, signature) = {
|
||||
// we build our side of the new commitment tx
|
||||
val ourCommitTx = makeCommitTx(inputs, ourFinalPubKey, theirParams.finalPubKey, theirParams.delay, ourRevocationHash, newState)
|
||||
// we build their commitment tx and sign it
|
||||
val theirCommitTx = makeCommitTx(inputs, theirParams.finalPubKey, ourFinalPubKey, ourParams.delay, theirRevocationHash, newState.reverse)
|
||||
val ourSigForThem = bin2signature(Transaction.signInput(theirCommitTx, 0, multiSig2of2(ourCommitPubKey, theirParams.commitPubKey), SIGHASH_ALL, ourParams.commitPrivKey))
|
||||
(ourCommitTx, ourSigForThem)
|
||||
}
|
||||
|
||||
def sign_our_commitment_tx(ourParams: OurChannelParams, theirParams: TheirChannelParams, ourCommitTx: Transaction, theirSig: signature): Transaction = {
|
||||
// TODO : Transaction.sign(...) should handle multisig
|
||||
val ourSig = Transaction.signInput(ourCommitTx, 0, multiSig2of2(ourCommitPubKey, theirParams.commitPubKey), SIGHASH_ALL, ourParams.commitPrivKey)
|
||||
ourCommitTx.updateSigScript(0, sigScript2of2(theirSig, ourSig, theirParams.commitPubKey, ourCommitPubKey))
|
||||
}
|
||||
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
package fr.acinq.eclair.channel
|
||||
|
||||
import fr.acinq.bitcoin.{BinaryData, Transaction, Crypto}
|
||||
import fr.acinq.eclair._
|
||||
import fr.acinq.eclair.crypto.ShaChain
|
||||
import lightning._
|
||||
import lightning.locktime.Locktime.Blocks
|
||||
import org.junit.runner.RunWith
|
||||
import org.scalatest.junit.JUnitRunner
|
||||
|
||||
/**
|
||||
* Created by PM on 08/09/2015.
|
||||
*/
|
||||
@RunWith(classOf[JUnitRunner])
|
||||
class TheirCommitSpec extends TestHelper {
|
||||
|
||||
"Node" must {
|
||||
|
||||
"handle the case where they spend the current commitment tx when in NORMAL_HIGHPRIO" in {
|
||||
val (node, ChannelDesc(Some(ourParams), Some(theirParams), Some(previousCommitment))) = reachState_NOANCHOR(NORMAL_LOWPRIO)
|
||||
// we do an htlc so that both parties have a strictly positive balance, which also inverts priority
|
||||
val ourRevocationHash = Crypto.sha256(ShaChain.shaChainFromSeed(ourParams.shaSeed, 1))
|
||||
val r = sha256_hash(1, 2, 1, 2)
|
||||
val rHash = Crypto.sha256(r)
|
||||
val htlc = update_add_htlc(ourRevocationHash, 40000000, rHash, locktime(Blocks(4)))
|
||||
val newState = previousCommitment.state.htlc_send(Htlc(htlc.amountMsat, rHash, htlc.expiry, Nil, None))
|
||||
node ! htlc
|
||||
val update_accept(theirSig, theirRevocationHash) = expectMsgClass(classOf[update_accept])
|
||||
val (ourCommitTx, ourSigForThem) = sign_their_commitment_tx(ourParams, theirParams, previousCommitment.tx.txIn, newState, ourRevocationHash, theirRevocationHash)
|
||||
println(new BinaryData(Transaction.write(ourCommitTx)))
|
||||
node ! update_signature(ourSigForThem, ShaChain.shaChainFromSeed(ourParams.shaSeed, 0))
|
||||
val update_complete(theirRevocationPreimage) = expectMsgClass(classOf[update_complete])
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(NORMAL_HIGHPRIO)
|
||||
node ! (BITCOIN_ANCHOR_SPENT, ourCommitTx)
|
||||
expectMsgClass(classOf[error])
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSING)
|
||||
node ! CMD_GETSTATEDATA
|
||||
val closingData = expectMsgClass(classOf[DATA_CLOSING])
|
||||
assert(closingData.theirCommitPublished == Some(ourCommitTx))
|
||||
// TODO : test not finished !
|
||||
}
|
||||
|
||||
"handle the case where they spend a revoked commitment tx when in NORMAL_HIGHPRIO" in {
|
||||
val (node, ChannelDesc(Some(ourParams), Some(theirParams), Some(previousCommitment))) = reachState_NOANCHOR(NORMAL_LOWPRIO)
|
||||
// we do an htlc so that both parties have a positive balance, which also inverts priority
|
||||
val ourRevocationHash = Crypto.sha256(ShaChain.shaChainFromSeed(ourParams.shaSeed, 1))
|
||||
val r = sha256_hash(1, 2, 1, 2)
|
||||
val rHash = Crypto.sha256(r)
|
||||
val htlc = update_add_htlc(ourRevocationHash, 40000000, rHash, locktime(Blocks(4)))
|
||||
val newState = previousCommitment.state.htlc_send(Htlc(htlc.amountMsat, rHash, htlc.expiry, Nil, None))
|
||||
node ! htlc
|
||||
val update_accept(theirSig, theirRevocationHash) = expectMsgClass(classOf[update_accept])
|
||||
val (ourCommitTx, ourSigForThem) = sign_their_commitment_tx(ourParams, theirParams, previousCommitment.tx.txIn, newState, ourRevocationHash, theirRevocationHash)
|
||||
node ! update_signature(ourSigForThem, ShaChain.shaChainFromSeed(ourParams.shaSeed, 0))
|
||||
val update_complete(theirRevocationPreimage) = expectMsgClass(classOf[update_complete])
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(NORMAL_HIGHPRIO)
|
||||
node ! (BITCOIN_ANCHOR_SPENT, previousCommitment.tx)
|
||||
expectMsgClass(classOf[error])
|
||||
node ! CMD_GETSTATE
|
||||
expectMsg(CLOSING)
|
||||
node ! CMD_GETSTATEDATA
|
||||
val closingData = expectMsgClass(classOf[DATA_CLOSING])
|
||||
assert(closingData.revokedPublished == List(previousCommitment.tx))
|
||||
// TODO : test not finished !
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -5,8 +5,6 @@ import javax.crypto.Cipher
|
|||
import javax.crypto.spec.{SecretKeySpec, IvParameterSpec}
|
||||
|
||||
import fr.acinq.bitcoin.BinaryData
|
||||
import fr.acinq.eclair.channel.{NORMAL_LOWPRIO, TestHelper}
|
||||
import fr.acinq.eclair.crypto.LightningCrypto
|
||||
import fr.acinq.eclair.io.AuthHandler
|
||||
import AuthHandler.Secrets
|
||||
import lightning.pkt
|
||||
|
|
Loading…
Add table
Reference in a new issue