mirror of
https://github.com/ACINQ/eclair.git
synced 2025-02-23 14:40:34 +01:00
added a bunch of tests to NORMAL state
This commit is contained in:
parent
51bb812fb5
commit
c79b101291
13 changed files with 484 additions and 59 deletions
|
@ -12,6 +12,7 @@ import lightning.open_channel.anchor_offer.{WILL_CREATE_ANCHOR, WONT_CREATE_ANCH
|
|||
|
||||
import scala.util.{Failure, Success, Try}
|
||||
import scala.concurrent.duration._
|
||||
import scala.util.control.NonFatal
|
||||
|
||||
/**
|
||||
* Created by PM on 20/08/2015.
|
||||
|
@ -45,7 +46,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, val params: OurChann
|
|||
888 888 Y8888 888 888
|
||||
8888888 888 Y888 8888888 888
|
||||
*/
|
||||
when(OPEN_WAIT_FOR_OPEN_NOANCHOR) {
|
||||
when(OPEN_WAIT_FOR_OPEN_NOANCHOR)(handleExceptions {
|
||||
case Event(open_channel(delay, theirRevocationHash, theirNextRevocationHash, commitKey, finalKey, WILL_CREATE_ANCHOR, minDepth, initialFeeRate), DATA_OPEN_WAIT_FOR_OPEN(ourParams)) =>
|
||||
val theirParams = TheirChannelParams(delay, commitKey, finalKey, minDepth, initialFeeRate)
|
||||
goto(OPEN_WAIT_FOR_ANCHOR) using DATA_OPEN_WAIT_FOR_ANCHOR(ourParams, theirParams, theirRevocationHash, theirNextRevocationHash)
|
||||
|
@ -55,9 +56,9 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, val params: OurChann
|
|||
goto(CLOSED)
|
||||
|
||||
case Event(CMD_CLOSE(_), _) => goto(CLOSED)
|
||||
}
|
||||
})
|
||||
|
||||
when(OPEN_WAIT_FOR_OPEN_WITHANCHOR) {
|
||||
when(OPEN_WAIT_FOR_OPEN_WITHANCHOR)(handleExceptions {
|
||||
case Event(open_channel(delay, theirRevocationHash, theirNextRevocationHash, commitKey, finalKey, WONT_CREATE_ANCHOR, minDepth, initialFeeRate), DATA_OPEN_WAIT_FOR_OPEN(ourParams)) =>
|
||||
val theirParams = TheirChannelParams(delay, commitKey, finalKey, minDepth, initialFeeRate)
|
||||
log.debug(s"their params: $theirParams")
|
||||
|
@ -76,9 +77,9 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, val params: OurChann
|
|||
goto(CLOSED)
|
||||
|
||||
case Event(CMD_CLOSE(_), _) => goto(CLOSED)
|
||||
}
|
||||
})
|
||||
|
||||
when(OPEN_WAIT_FOR_ANCHOR) {
|
||||
when(OPEN_WAIT_FOR_ANCHOR)(handleExceptions {
|
||||
case Event(open_anchor(anchorTxHash, anchorOutputIndex, anchorAmount), DATA_OPEN_WAIT_FOR_ANCHOR(ourParams, theirParams, theirRevocationHash, theirNextRevocationHash)) =>
|
||||
val anchorTxid = anchorTxHash.reverse //see https://github.com/ElementsProject/lightning/issues/17
|
||||
|
||||
|
@ -109,9 +110,9 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, val params: OurChann
|
|||
goto(CLOSED)
|
||||
|
||||
case Event(CMD_CLOSE(_), _) => goto(CLOSED)
|
||||
}
|
||||
})
|
||||
|
||||
when(OPEN_WAIT_FOR_COMMIT_SIG) {
|
||||
when(OPEN_WAIT_FOR_COMMIT_SIG)(handleExceptions {
|
||||
case Event(open_commit_sig(theirSig), DATA_OPEN_WAIT_FOR_COMMIT_SIG(ourParams, theirParams, anchorTx, anchorOutputIndex, theirCommitment, theirNextRevocationHash)) =>
|
||||
val anchorAmount = anchorTx.txOut(anchorOutputIndex).amount
|
||||
val theirSpec = theirCommitment.spec
|
||||
|
@ -145,9 +146,9 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, val params: OurChann
|
|||
goto(CLOSED)
|
||||
|
||||
case Event(CMD_CLOSE(_), _) => goto(CLOSED)
|
||||
}
|
||||
})
|
||||
|
||||
when(OPEN_WAITING_THEIRANCHOR) {
|
||||
when(OPEN_WAITING_THEIRANCHOR)(handleExceptions {
|
||||
case Event(msg@open_complete(blockId_opt), d: DATA_OPEN_WAITING) =>
|
||||
log.info(s"received their open_complete, deferring message")
|
||||
stay using d.copy(deferred = Some(msg))
|
||||
|
@ -174,9 +175,9 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, val params: OurChann
|
|||
goto(CLOSED)
|
||||
|
||||
case Event(CMD_CLOSE(_), _) => goto(CLOSED)
|
||||
}
|
||||
})
|
||||
|
||||
when(OPEN_WAITING_OURANCHOR) {
|
||||
when(OPEN_WAITING_OURANCHOR)(handleExceptions {
|
||||
case Event(msg@open_complete(blockId_opt), d: DATA_OPEN_WAITING) =>
|
||||
log.info(s"received their open_complete, deferring message")
|
||||
stay using d.copy(deferred = Some(msg))
|
||||
|
@ -209,10 +210,9 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, val params: OurChann
|
|||
blockchain ! Publish(d.commitments.ourCommit.publishableTx)
|
||||
blockchain ! WatchConfirmed(self, d.commitments.ourCommit.publishableTx.txid, d.commitments.ourParams.minDepth, BITCOIN_CLOSE_DONE)
|
||||
goto(CLOSING) using DATA_CLOSING(d.commitments, ourCommitPublished = Some(d.commitments.ourCommit.publishableTx))
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
when(OPEN_WAIT_FOR_COMPLETE_THEIRANCHOR) {
|
||||
when(OPEN_WAIT_FOR_COMPLETE_THEIRANCHOR)(handleExceptions {
|
||||
case Event(open_complete(blockid_opt), d: DATA_NORMAL) =>
|
||||
Register.create_alias(theirNodeId, d.commitments.anchorId)
|
||||
goto(NORMAL)
|
||||
|
@ -228,9 +228,9 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, val params: OurChann
|
|||
goto(CLOSED)
|
||||
|
||||
case Event(CMD_CLOSE(_), _) => goto(CLOSED)
|
||||
}
|
||||
})
|
||||
|
||||
when(OPEN_WAIT_FOR_COMPLETE_OURANCHOR) {
|
||||
when(OPEN_WAIT_FOR_COMPLETE_OURANCHOR)(handleExceptions {
|
||||
case Event(open_complete(blockid_opt), d: DATA_NORMAL) =>
|
||||
Register.create_alias(theirNodeId, d.commitments.anchorId)
|
||||
goto(NORMAL)
|
||||
|
@ -252,7 +252,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, val params: OurChann
|
|||
blockchain ! Publish(d.commitments.ourCommit.publishableTx)
|
||||
blockchain ! WatchConfirmed(self, d.commitments.ourCommit.publishableTx.txid, d.commitments.ourParams.minDepth, BITCOIN_CLOSE_DONE)
|
||||
goto(CLOSING) using DATA_CLOSING(d.commitments, ourCommitPublished = Some(d.commitments.ourCommit.publishableTx))
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
/*
|
||||
|
@ -267,25 +267,45 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, val params: OurChann
|
|||
*/
|
||||
|
||||
when(NORMAL) {
|
||||
|
||||
case Event(CMD_ADD_HTLC(amount, rHash, expiry, nodeIds, origin, id_opt), d@DATA_NORMAL(commitments, htlcIdx, _)) =>
|
||||
// TODO: should we take pending htlcs into account?
|
||||
// TODO: assert(commitment.state.commit_changes(staged).us.pay_msat >= amount, "insufficient funds!")
|
||||
// TODO: nodeIds are ignored
|
||||
val id: Long = id_opt.getOrElse(htlcIdx + 1)
|
||||
val htlc = update_add_htlc(id, amount, rHash, expiry, routing(ByteString.EMPTY))
|
||||
them ! htlc
|
||||
stay using d.copy(htlcIdx = htlc.id, commitments = commitments.addOurProposal(htlc))
|
||||
// TODO : this should probably be done in Commitments.scala
|
||||
// our available funds as seen by them, including all pending changes
|
||||
val reduced = reduce(commitments.theirCommit.spec, commitments.theirChanges.acked, commitments.ourChanges.acked ++ commitments.ourChanges.signed ++ commitments.ourChanges.proposed)
|
||||
// the pending htlcs that we sent to them (seen as IN from their pov) have already been deduced from our balance
|
||||
val available = reduced.amount_them_msat + reduced.htlcs.filter(_.direction == OUT).map(-_.amountMsat).sum
|
||||
if (amount > available) {
|
||||
sender ! s"insufficient funds (available=$available msat)"
|
||||
stay
|
||||
} else {
|
||||
// TODO: nodeIds are ignored
|
||||
val id: Long = id_opt.getOrElse(htlcIdx + 1)
|
||||
val htlc = update_add_htlc(id, amount, rHash, expiry, routing(ByteString.EMPTY))
|
||||
them ! htlc
|
||||
sender ! "ok"
|
||||
stay using d.copy(htlcIdx = htlc.id, commitments = commitments.addOurProposal(htlc))
|
||||
}
|
||||
|
||||
case Event(htlc@update_add_htlc(htlcId, amount, rHash, expiry, nodeIds), d@DATA_NORMAL(commitments, _, _)) =>
|
||||
// TODO: should we take pending htlcs into account?
|
||||
// assert(commitment.state.commit_changes(staged).them.pay_msat >= amount, "insufficient funds!") // TODO : we should fail the channel
|
||||
// TODO: nodeIds are ignored
|
||||
stay using d.copy(commitments = commitments.addTheirProposal(htlc))
|
||||
// TODO : this should probably be done in Commitments.scala
|
||||
// their available funds as seen by us, including all pending changes
|
||||
val reduced = reduce(commitments.ourCommit.spec, commitments.ourChanges.acked, commitments.theirChanges.acked ++ commitments.theirChanges.proposed)
|
||||
// the pending htlcs that they sent to us (seen as IN from our pov) have already been deduced from their balance
|
||||
val available = reduced.amount_them_msat + reduced.htlcs.filter(_.direction == OUT).map(-_.amountMsat).sum
|
||||
if (amount > available) {
|
||||
log.error("they sent an htlc but had insufficient funds")
|
||||
them ! error(Some("Insufficient funds"))
|
||||
blockchain ! Publish(d.commitments.ourCommit.publishableTx)
|
||||
blockchain ! WatchConfirmed(self, d.commitments.ourCommit.publishableTx.txid, d.commitments.ourParams.minDepth, BITCOIN_CLOSE_DONE)
|
||||
goto(CLOSING) using DATA_CLOSING(d.commitments, ourCommitPublished = Some(d.commitments.ourCommit.publishableTx))
|
||||
} else {
|
||||
// TODO: nodeIds are ignored
|
||||
stay using d.copy(commitments = commitments.addTheirProposal(htlc))
|
||||
}
|
||||
|
||||
case Event(CMD_FULFILL_HTLC(id, r), d: DATA_NORMAL) =>
|
||||
val (commitments1, fullfill) = Commitments.sendFulfill(d.commitments, CMD_FULFILL_HTLC(id, r))
|
||||
them ! fullfill
|
||||
sender ! "ok"
|
||||
stay using d.copy(commitments = commitments1)
|
||||
|
||||
case Event(fulfill@update_fulfill_htlc(id, r), d: DATA_NORMAL) =>
|
||||
|
@ -300,9 +320,27 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, val params: OurChann
|
|||
stay using d.copy(commitments = Commitments.receiveFail(d.commitments, fail))
|
||||
|
||||
case Event(CMD_SIGN, d: DATA_NORMAL) =>
|
||||
val (commitments1, commit) = Commitments.sendCommit(d.commitments)
|
||||
them ! commit
|
||||
stay using d.copy(commitments = commitments1)
|
||||
if (d.commitments.theirNextCommitInfo.isLeft) {
|
||||
sender ! "cannot sign until next revocation hash is received"
|
||||
stay
|
||||
} /*else if (d.commitments.ourChanges.proposed.isEmpty) {
|
||||
//TODO : check this
|
||||
sender ! "cannot sign when there are no changes"
|
||||
stay
|
||||
}*/ else {
|
||||
Try(Commitments.sendCommit(d.commitments)) match {
|
||||
case Success((commitments1, commit)) =>
|
||||
them ! commit
|
||||
sender ! "ok"
|
||||
stay using d.copy(commitments = commitments1)
|
||||
case Failure(cause) =>
|
||||
log.error(cause, "")
|
||||
them ! error(Some("Bad signature"))
|
||||
blockchain ! Publish(d.commitments.ourCommit.publishableTx)
|
||||
blockchain ! WatchConfirmed(self, d.commitments.ourCommit.publishableTx.txid, d.commitments.ourParams.minDepth, BITCOIN_CLOSE_DONE)
|
||||
goto(CLOSING) using DATA_CLOSING(d.commitments, ourCommitPublished = Some(d.commitments.ourCommit.publishableTx))
|
||||
}
|
||||
}
|
||||
|
||||
case Event(msg@update_commit(theirSig), d: DATA_NORMAL) =>
|
||||
Try(Commitments.receiveCommit(d.commitments, msg)) match {
|
||||
|
@ -310,7 +348,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, val params: OurChann
|
|||
them ! revocation
|
||||
stay using d.copy(commitments = commitments1)
|
||||
case Failure(cause) =>
|
||||
log.error(cause, "received a bad signature")
|
||||
log.error(cause, "")
|
||||
them ! error(Some("Bad signature"))
|
||||
blockchain ! Publish(d.commitments.ourCommit.publishableTx)
|
||||
blockchain ! WatchConfirmed(self, d.commitments.ourCommit.publishableTx.txid, d.commitments.ourParams.minDepth, BITCOIN_CLOSE_DONE)
|
||||
|
@ -320,8 +358,16 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, val params: OurChann
|
|||
case Event(msg@update_revocation(revocationPreimage, nextRevocationHash), d: DATA_NORMAL) =>
|
||||
// we received a revocation because we sent a signature
|
||||
// => all our changes have been acked
|
||||
// TODO: check preimage
|
||||
stay using d.copy(commitments = Commitments.receiveRevocation(d.commitments, msg))
|
||||
Try(Commitments.receiveRevocation(d.commitments, msg)) match {
|
||||
case Success(commitments1) =>
|
||||
stay using d.copy(commitments = commitments1)
|
||||
case Failure(cause) =>
|
||||
log.error(cause, "")
|
||||
them ! error(Some("Bad revocation"))
|
||||
blockchain ! Publish(d.commitments.ourCommit.publishableTx)
|
||||
blockchain ! WatchConfirmed(self, d.commitments.ourCommit.publishableTx.txid, d.commitments.ourParams.minDepth, BITCOIN_CLOSE_DONE)
|
||||
goto(CLOSING) using DATA_CLOSING(d.commitments, ourCommitPublished = Some(d.commitments.ourCommit.publishableTx))
|
||||
}
|
||||
|
||||
case Event(theirClearing@close_clearing(theirScriptPubKey), d@DATA_NORMAL(commitments, _, ourClearingOpt)) =>
|
||||
val ourClearing: close_clearing = ourClearingOpt.getOrElse {
|
||||
|
@ -411,7 +457,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, val params: OurChann
|
|||
stay using d.copy(commitments = commitments1)
|
||||
}
|
||||
case Failure(cause) =>
|
||||
log.error(cause, "received a bad signature")
|
||||
log.error(cause, "")
|
||||
them ! error(Some("Bad signature"))
|
||||
blockchain ! Publish(d.commitments.ourCommit.publishableTx)
|
||||
blockchain ! WatchConfirmed(self, d.commitments.ourCommit.publishableTx.txid, d.commitments.ourParams.minDepth, BITCOIN_CLOSE_DONE)
|
||||
|
@ -479,7 +525,9 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, val params: OurChann
|
|||
stay()
|
||||
|
||||
case Event(close_signature(theirCloseFee, theirSig), d: DATA_CLOSING) =>
|
||||
throw new RuntimeException(s"unexpected closing fee: $theirCloseFee ours is ${d.ourSignature.map(_.closeFee)}")
|
||||
throw new RuntimeException(s"unexpected closing fee: $theirCloseFee ours is ${
|
||||
d.ourSignature.map(_.closeFee)
|
||||
}")
|
||||
|
||||
case Event(BITCOIN_CLOSE_DONE, _) => goto(CLOSED)
|
||||
|
||||
|
@ -528,6 +576,25 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, val params: OurChann
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* This helper function runs the state's default event handlers, and react to exceptions by unilaterally closing the channel
|
||||
*/
|
||||
def handleExceptions(s: StateFunction): StateFunction = {
|
||||
case event =>
|
||||
try {
|
||||
s(event)
|
||||
} catch {
|
||||
case t: Throwable => event.stateData match {
|
||||
case d: HasCommitments =>
|
||||
blockchain ! Publish(d.commitments.ourCommit.publishableTx)
|
||||
blockchain ! WatchConfirmed(self, d.commitments.ourCommit.publishableTx.txid, d.commitments.ourParams.minDepth, BITCOIN_CLOSE_DONE)
|
||||
goto(CLOSING) using DATA_CLOSING(d.commitments, ourCommitPublished = Some(d.commitments.ourCommit.publishableTx))
|
||||
case _ =>
|
||||
goto(CLOSED)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -82,14 +82,14 @@ sealed trait Command
|
|||
/**
|
||||
*
|
||||
*
|
||||
* @param amount
|
||||
* @param amountMsat
|
||||
* @param rHash
|
||||
* @param expiry
|
||||
* @param nodeIds
|
||||
* @param originChannelId
|
||||
* @param id should only be provided in tests otherwise it will be assigned automatically
|
||||
*/
|
||||
final case class CMD_ADD_HTLC(amount: Int, rHash: sha256_hash, expiry: locktime, nodeIds: Seq[String] = Seq.empty[String], originChannelId: Option[BinaryData] = None, id: Option[Long] = None) extends Command
|
||||
final case class CMD_ADD_HTLC(amountMsat: Int, rHash: sha256_hash, expiry: locktime, nodeIds: Seq[String] = Seq.empty[String], originChannelId: Option[BinaryData] = None, id: Option[Long] = None) extends Command
|
||||
final case class CMD_FULFILL_HTLC(id: Long, r: sha256_hash) extends Command
|
||||
final case class CMD_FAIL_HTLC(id: Long, reason: String) extends Command
|
||||
case object CMD_SIGN extends Command
|
||||
|
@ -143,23 +143,27 @@ case class TheirCommit(index: Long, spec: CommitmentSpec, theirRevocationHash: s
|
|||
|
||||
final case class ClosingData(ourScriptPubKey: BinaryData, theirScriptPubKey: Option[BinaryData])
|
||||
|
||||
trait HasCommitments {
|
||||
def commitments: Commitments
|
||||
}
|
||||
|
||||
final case class DATA_OPEN_WAIT_FOR_OPEN (ourParams: OurChannelParams) extends Data
|
||||
final case class DATA_OPEN_WITH_ANCHOR_WAIT_FOR_ANCHOR(ourParams: OurChannelParams, theirParams: TheirChannelParams, theirRevocationHash: BinaryData, theirNextRevocationHash: sha256_hash) extends Data
|
||||
final case class DATA_OPEN_WAIT_FOR_ANCHOR (ourParams: OurChannelParams, theirParams: TheirChannelParams, theirRevocationHash: sha256_hash, theirNextRevocationHash: sha256_hash) extends Data
|
||||
final case class DATA_OPEN_WAIT_FOR_COMMIT_SIG (ourParams: OurChannelParams, theirParams: TheirChannelParams, anchorTx: Transaction, anchorOutputIndex: Int, initialCommitment: TheirCommit, theirNextRevocationHash: sha256_hash) extends Data
|
||||
final case class DATA_OPEN_WAITING (commitments: Commitments, deferred: Option[open_complete]) extends Data
|
||||
final case class DATA_OPEN_WAITING (commitments: Commitments, deferred: Option[open_complete]) extends Data with HasCommitments
|
||||
final case class DATA_NORMAL (commitments: Commitments, htlcIdx: Long,
|
||||
ourClearing: Option[close_clearing]) extends Data
|
||||
ourClearing: Option[close_clearing]) extends Data with HasCommitments
|
||||
final case class DATA_CLEARING (commitments: Commitments, htlcIdx: Long,
|
||||
ourClearing: close_clearing, theirClearing: close_clearing) extends Data
|
||||
ourClearing: close_clearing, theirClearing: close_clearing) extends Data with HasCommitments
|
||||
final case class DATA_NEGOCIATING (commitments: Commitments, htlcIdx: Long,
|
||||
ourClearing: close_clearing, theirClearing: close_clearing, ourSignature: close_signature) extends Data
|
||||
ourClearing: close_clearing, theirClearing: close_clearing, ourSignature: close_signature) extends Data with HasCommitments
|
||||
final case class DATA_CLOSING (commitments: Commitments,
|
||||
ourSignature: Option[close_signature] = None,
|
||||
mutualClosePublished: Option[Transaction] = None,
|
||||
ourCommitPublished: Option[Transaction] = None,
|
||||
theirCommitPublished: Option[Transaction] = None,
|
||||
revokedPublished: Seq[Transaction] = Seq()) extends Data {
|
||||
revokedPublished: Seq[Transaction] = Seq()) extends Data with HasCommitments {
|
||||
assert(mutualClosePublished.isDefined || ourCommitPublished.isDefined || theirCommitPublished.isDefined || revokedPublished.size > 0, "there should be at least one tx published in this state")
|
||||
}
|
||||
|
||||
|
|
|
@ -113,6 +113,10 @@ object Commitments {
|
|||
// we will reply to this sig with our old revocation hash preimage (at index) and our next revocation hash (at index + 1)
|
||||
// and will increment our index
|
||||
|
||||
// check that there have been changes
|
||||
/*if (commitments.theirChanges.proposed.isEmpty)
|
||||
throw new RuntimeException("cannot sign if there are no changes")*/
|
||||
|
||||
// check that their signature is valid
|
||||
val spec = Helpers.reduce(ourCommit.spec, ourChanges.acked, theirChanges.acked ++ theirChanges.proposed)
|
||||
val ourNextRevocationHash = Helpers.revocationHash(ourParams.shaSeed, ourCommit.index + 1)
|
||||
|
@ -121,7 +125,7 @@ object Commitments {
|
|||
val signedTx = Helpers.addSigs(ourParams, theirParams, anchorOutput.amount, ourTx, ourSig, commit.sig)
|
||||
Helpers.checksig(ourParams, theirParams, anchorOutput, signedTx).get
|
||||
|
||||
// we will send our revocation preimage+ our next revocation hash
|
||||
// we will send our revocation preimage + our next revocation hash
|
||||
val ourRevocationPreimage = Helpers.revocationPreimage(ourParams.shaSeed, ourCommit.index)
|
||||
val ourNextRevocationHash1 = Helpers.revocationHash(ourParams.shaSeed, ourCommit.index + 2)
|
||||
val revocation = update_revocation(ourRevocationPreimage, ourNextRevocationHash1)
|
||||
|
@ -138,8 +142,9 @@ object Commitments {
|
|||
import commitments._
|
||||
// we receive a revocation because we just sent them a sig for their next commit tx
|
||||
theirNextCommitInfo match {
|
||||
case Left(theirNextCommit) if BinaryData(Crypto.sha256(revocation.revocationPreimage)) != BinaryData(theirCommit.theirRevocationHash) =>
|
||||
throw new RuntimeException("invalid preimage")
|
||||
case Left(theirNextCommit) =>
|
||||
assert(BinaryData(Crypto.sha256(revocation.revocationPreimage)) == BinaryData(theirCommit.theirRevocationHash), "invalid preimage")
|
||||
commitments.copy(
|
||||
ourChanges = ourChanges.copy(signed = Nil, acked = ourChanges.acked ++ ourChanges.signed),
|
||||
theirCommit = theirNextCommit,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package fr.acinq.eclair.channel.simulator
|
||||
package fr.acinq.eclair.channel.simulator.states.a
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import akka.testkit.{TestFSMRef, TestKit, TestProbe}
|
||||
|
@ -6,8 +6,8 @@ import fr.acinq.eclair.TestConstants.{Alice, Bob}
|
|||
import fr.acinq.eclair.channel._
|
||||
import lightning.{error, open_channel}
|
||||
import org.junit.runner.RunWith
|
||||
import org.scalatest.{BeforeAndAfterAll, fixture}
|
||||
import org.scalatest.junit.JUnitRunner
|
||||
import org.scalatest.{BeforeAndAfterAll, fixture}
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package fr.acinq.eclair.channel.simulator
|
||||
package fr.acinq.eclair.channel.simulator.states.a
|
||||
|
||||
import akka.actor.{ActorRef, ActorSystem}
|
||||
import akka.testkit.{TestActorRef, TestFSMRef, TestKit, TestProbe}
|
|
@ -1,4 +1,4 @@
|
|||
package fr.acinq.eclair.channel.simulator
|
||||
package fr.acinq.eclair.channel.simulator.states.b
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import akka.testkit.{TestActorRef, TestFSMRef, TestKit, TestProbe}
|
|
@ -1,11 +1,11 @@
|
|||
package fr.acinq.eclair.channel.simulator
|
||||
package fr.acinq.eclair.channel.simulator.states.b
|
||||
|
||||
import akka.actor.{ActorRef, ActorSystem}
|
||||
import akka.testkit.{TestActorRef, TestFSMRef, TestKit, TestProbe}
|
||||
import fr.acinq.eclair.TestBitcoinClient
|
||||
import fr.acinq.eclair.TestConstants.{Alice, Bob}
|
||||
import fr.acinq.eclair.blockchain._
|
||||
import fr.acinq.eclair.channel.{OPEN_WAITING_OURANCHOR, OPEN_WAIT_FOR_OPEN_WITHANCHOR, _}
|
||||
import fr.acinq.eclair.channel.{OPEN_WAITING_OURANCHOR, _}
|
||||
import lightning._
|
||||
import org.junit.runner.RunWith
|
||||
import org.scalatest.junit.JUnitRunner
|
|
@ -1,4 +1,4 @@
|
|||
package fr.acinq.eclair.channel.simulator
|
||||
package fr.acinq.eclair.channel.simulator.states.c
|
||||
|
||||
import akka.actor.{ActorRef, ActorSystem}
|
||||
import akka.testkit.{TestActorRef, TestFSMRef, TestKit, TestProbe}
|
|
@ -1,4 +1,4 @@
|
|||
package fr.acinq.eclair.channel.simulator
|
||||
package fr.acinq.eclair.channel.simulator.states.c
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import akka.testkit.{TestActorRef, TestFSMRef, TestKit, TestProbe}
|
|
@ -1,11 +1,11 @@
|
|||
package fr.acinq.eclair.channel.simulator
|
||||
package fr.acinq.eclair.channel.simulator.states.d
|
||||
|
||||
import akka.actor.{ActorRef, ActorSystem}
|
||||
import akka.testkit.{TestActorRef, TestFSMRef, TestKit, TestProbe}
|
||||
import fr.acinq.eclair.TestBitcoinClient
|
||||
import fr.acinq.eclair.TestConstants.{Alice, Bob}
|
||||
import fr.acinq.eclair.blockchain._
|
||||
import fr.acinq.eclair.channel.{OPEN_WAITING_OURANCHOR, OPEN_WAIT_FOR_COMPLETE_OURANCHOR, _}
|
||||
import fr.acinq.eclair.channel.{OPEN_WAIT_FOR_COMPLETE_OURANCHOR, _}
|
||||
import lightning._
|
||||
import org.junit.runner.RunWith
|
||||
import org.scalatest.junit.JUnitRunner
|
|
@ -1,11 +1,11 @@
|
|||
package fr.acinq.eclair.channel.simulator
|
||||
package fr.acinq.eclair.channel.simulator.states.d
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import akka.testkit.{TestActorRef, TestFSMRef, TestKit, TestProbe}
|
||||
import fr.acinq.eclair.TestBitcoinClient
|
||||
import fr.acinq.eclair.TestConstants.{Alice, Bob}
|
||||
import fr.acinq.eclair.blockchain.{PollingWatcher, WatchConfirmed, WatchLost, WatchSpent}
|
||||
import fr.acinq.eclair.channel.{BITCOIN_ANCHOR_DEPTHOK, OPEN_WAITING_THEIRANCHOR, OPEN_WAIT_FOR_COMPLETE_THEIRANCHOR, _}
|
||||
import fr.acinq.eclair.channel.{BITCOIN_ANCHOR_DEPTHOK, OPEN_WAIT_FOR_COMPLETE_THEIRANCHOR, _}
|
||||
import lightning._
|
||||
import org.junit.runner.RunWith
|
||||
import org.scalatest.junit.JUnitRunner
|
||||
|
@ -52,7 +52,7 @@ class OpenWaitForCompleteTheirAnchorStateSpec extends TestKit(ActorSystem("test"
|
|||
|
||||
test("recv open_complete") { case (bob, alice2bob, bob2alice, bob2blockchain) =>
|
||||
within(30 seconds) {
|
||||
val msg = alice2bob.expectMsgType[open_complete]
|
||||
alice2bob.expectMsgType[open_complete]
|
||||
alice2bob.forward(bob)
|
||||
awaitCond(bob.stateName == NORMAL)
|
||||
}
|
|
@ -0,0 +1,345 @@
|
|||
package fr.acinq.eclair.channel.simulator.states.e
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import akka.testkit.{TestActorRef, TestFSMRef, TestKit, TestProbe}
|
||||
import com.google.protobuf.ByteString
|
||||
import fr.acinq.bitcoin.Crypto
|
||||
import fr.acinq.eclair._
|
||||
import fr.acinq.eclair.TestBitcoinClient
|
||||
import fr.acinq.eclair.TestConstants.{Alice, Bob}
|
||||
import fr.acinq.eclair.blockchain._
|
||||
import fr.acinq.eclair.channel.{BITCOIN_ANCHOR_DEPTHOK, _}
|
||||
import lightning._
|
||||
import lightning.locktime.Locktime.Blocks
|
||||
import org.junit.runner.RunWith
|
||||
import org.scalatest.junit.JUnitRunner
|
||||
import org.scalatest.{BeforeAndAfterAll, fixture}
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* Created by PM on 05/07/2016.
|
||||
*/
|
||||
@RunWith(classOf[JUnitRunner])
|
||||
class NormalStateSpec extends TestKit(ActorSystem("test")) with fixture.FunSuiteLike with BeforeAndAfterAll {
|
||||
|
||||
type FixtureParam = Tuple6[TestFSMRef[State, Data, Channel], TestFSMRef[State, Data, Channel], TestProbe, TestProbe, TestProbe, TestProbe]
|
||||
|
||||
override def withFixture(test: OneArgTest) = {
|
||||
val alice2bob = TestProbe()
|
||||
val bob2alice = TestProbe()
|
||||
val alice2blockchain = TestProbe()
|
||||
val blockchainA = TestActorRef(new PollingWatcher(new TestBitcoinClient()))
|
||||
val bob2blockchain = TestProbe()
|
||||
val alice: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(alice2bob.ref, alice2blockchain.ref, Alice.channelParams, "B"))
|
||||
val bob: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(bob2alice.ref, bob2blockchain.ref, Bob.channelParams, "A"))
|
||||
alice2bob.expectMsgType[open_channel]
|
||||
alice2bob.forward(bob)
|
||||
bob2alice.expectMsgType[open_channel]
|
||||
bob2alice.forward(alice)
|
||||
alice2blockchain.expectMsgType[MakeAnchor]
|
||||
alice2blockchain.forward(blockchainA)
|
||||
alice2bob.expectMsgType[open_anchor]
|
||||
alice2bob.forward(bob)
|
||||
bob2alice.expectMsgType[open_commit_sig]
|
||||
bob2alice.forward(alice)
|
||||
alice2blockchain.expectMsgType[WatchConfirmed]
|
||||
alice2blockchain.forward(blockchainA)
|
||||
alice2blockchain.expectMsgType[WatchSpent]
|
||||
alice2blockchain.forward(blockchainA)
|
||||
alice2blockchain.expectMsgType[Publish]
|
||||
alice2blockchain.forward(blockchainA)
|
||||
bob2blockchain.expectMsgType[WatchConfirmed]
|
||||
bob2blockchain.expectMsgType[WatchSpent]
|
||||
bob ! BITCOIN_ANCHOR_DEPTHOK
|
||||
bob2blockchain.expectMsgType[WatchLost]
|
||||
bob2alice.expectMsgType[open_complete]
|
||||
bob2alice.forward(alice)
|
||||
alice2blockchain.expectMsgType[WatchLost]
|
||||
alice2blockchain.forward(blockchainA)
|
||||
alice2bob.expectMsgType[open_complete]
|
||||
alice2bob.forward(bob)
|
||||
awaitCond(alice.stateName == NORMAL)
|
||||
awaitCond(bob.stateName == NORMAL)
|
||||
// note : alice is funder and bob is fundee, so alice has all the money
|
||||
test((alice, bob, alice2bob, bob2alice, alice2blockchain, bob2blockchain))
|
||||
}
|
||||
|
||||
override def afterAll {
|
||||
TestKit.shutdownActorSystem(system)
|
||||
}
|
||||
|
||||
test("recv CMD_ADD_HTLC") { case (alice, _, alice2bob, _, _, _) =>
|
||||
within(30 seconds) {
|
||||
val h = sha256_hash(1, 2, 3, 4)
|
||||
alice ! CMD_ADD_HTLC(500000, h, locktime(Blocks(3)))
|
||||
val htlc = alice2bob.expectMsgType[update_add_htlc]
|
||||
assert(htlc.id == 1 && htlc.rHash == h)
|
||||
awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].htlcIdx == 1 && alice.stateData.asInstanceOf[DATA_NORMAL].commitments.ourChanges.proposed == htlc :: Nil)
|
||||
}
|
||||
}
|
||||
|
||||
test("recv CMD_ADD_HTLC (insufficient funds)") { case (alice, _, alice2bob, _, _, _) =>
|
||||
within(30 seconds) {
|
||||
val sender = TestProbe()
|
||||
sender.send(alice, CMD_ADD_HTLC(Int.MaxValue, sha256_hash(1, 1, 1, 1), locktime(Blocks(3))))
|
||||
sender.expectMsg("insufficient funds (available=1000000000 msat)")
|
||||
}
|
||||
}
|
||||
|
||||
test("recv CMD_ADD_HTLC (insufficient funds w/ pending htlcs 1/2)") { case (alice, _, alice2bob, _, _, _) =>
|
||||
within(30 seconds) {
|
||||
val sender = TestProbe()
|
||||
sender.send(alice, CMD_ADD_HTLC(500000000, sha256_hash(1, 1, 1, 1), locktime(Blocks(3))))
|
||||
sender.expectMsg("ok")
|
||||
sender.send(alice, CMD_ADD_HTLC(500000000, sha256_hash(2, 2, 2, 2), locktime(Blocks(3))))
|
||||
sender.expectMsg("ok")
|
||||
sender.send(alice, CMD_ADD_HTLC(500000000, sha256_hash(3, 3, 3, 3), locktime(Blocks(3))))
|
||||
sender.expectMsg("insufficient funds (available=0 msat)")
|
||||
}
|
||||
}
|
||||
|
||||
test("recv CMD_ADD_HTLC (insufficient funds w/ pending htlcs 2/2)") { case (alice, _, alice2bob, _, _, _) =>
|
||||
within(30 seconds) {
|
||||
val sender = TestProbe()
|
||||
sender.send(alice, CMD_ADD_HTLC(300000000, sha256_hash(1, 1, 1, 1), locktime(Blocks(3))))
|
||||
sender.expectMsg("ok")
|
||||
sender.send(alice, CMD_ADD_HTLC(300000000, sha256_hash(2, 2, 2, 2), locktime(Blocks(3))))
|
||||
sender.expectMsg("ok")
|
||||
sender.send(alice, CMD_ADD_HTLC(500000000, sha256_hash(3, 3, 3, 3), locktime(Blocks(3))))
|
||||
sender.expectMsg("insufficient funds (available=400000000 msat)")
|
||||
}
|
||||
}
|
||||
|
||||
test("recv update_add_htlc") { case (_, bob, alice2bob, _, _,_) =>
|
||||
within(30 seconds) {
|
||||
val initialData = bob.stateData.asInstanceOf[DATA_NORMAL]
|
||||
val htlc = update_add_htlc(42, 150, sha256_hash(1, 2, 3, 4), locktime(Blocks(3)), routing(ByteString.EMPTY))
|
||||
bob ! htlc
|
||||
awaitCond(bob.stateData == initialData.copy(commitments = initialData.commitments.copy(theirChanges = initialData.commitments.theirChanges.copy(proposed = initialData.commitments.theirChanges.proposed :+ htlc))))
|
||||
}
|
||||
}
|
||||
|
||||
test("recv update_add_htlc (insufficient funds)") { case (_, bob, alice2bob, bob2alice, _, bob2blockchain) =>
|
||||
within(30 seconds) {
|
||||
val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.ourCommit.publishableTx
|
||||
val htlc = update_add_htlc(42, Int.MaxValue, sha256_hash(1, 2, 3, 4), locktime(Blocks(3)), routing(ByteString.EMPTY))
|
||||
alice2bob.forward(bob, htlc)
|
||||
bob2alice.expectMsgType[error]
|
||||
awaitCond(bob.stateName == CLOSING)
|
||||
bob2blockchain.expectMsg(Publish(tx))
|
||||
bob2blockchain.expectMsgType[WatchConfirmed]
|
||||
}
|
||||
}
|
||||
|
||||
test("recv update_add_htlc (insufficient funds w/ pending htlcs 1/2)") { case (_, bob, alice2bob, bob2alice, _, bob2blockchain) =>
|
||||
within(30 seconds) {
|
||||
val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.ourCommit.publishableTx
|
||||
alice2bob.forward(bob, update_add_htlc(42, 500000000, sha256_hash(1, 1, 1, 1), locktime(Blocks(3)), routing(ByteString.EMPTY)))
|
||||
alice2bob.forward(bob, update_add_htlc(43, 500000000, sha256_hash(2, 2, 2, 2), locktime(Blocks(3)), routing(ByteString.EMPTY)))
|
||||
alice2bob.forward(bob, update_add_htlc(44, 500000000, sha256_hash(3, 3, 3, 3), locktime(Blocks(3)), routing(ByteString.EMPTY)))
|
||||
bob2alice.expectMsgType[error]
|
||||
awaitCond(bob.stateName == CLOSING)
|
||||
bob2blockchain.expectMsg(Publish(tx))
|
||||
bob2blockchain.expectMsgType[WatchConfirmed]
|
||||
}
|
||||
}
|
||||
|
||||
test("recv update_add_htlc (insufficient funds w/ pending htlcs 2/2)") { case (_, bob, alice2bob, bob2alice, _, bob2blockchain) =>
|
||||
within(30 seconds) {
|
||||
val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.ourCommit.publishableTx
|
||||
alice2bob.forward(bob, update_add_htlc(42, 300000000, sha256_hash(1, 1, 1, 1), locktime(Blocks(3)), routing(ByteString.EMPTY)))
|
||||
alice2bob.forward(bob, update_add_htlc(43, 300000000, sha256_hash(2, 2, 2, 2), locktime(Blocks(3)), routing(ByteString.EMPTY)))
|
||||
alice2bob.forward(bob, update_add_htlc(44, 500000000, sha256_hash(3, 3, 3, 3), locktime(Blocks(3)), routing(ByteString.EMPTY)))
|
||||
bob2alice.expectMsgType[error]
|
||||
awaitCond(bob.stateName == CLOSING)
|
||||
bob2blockchain.expectMsg(Publish(tx))
|
||||
bob2blockchain.expectMsgType[WatchConfirmed]
|
||||
}
|
||||
}
|
||||
|
||||
test("recv CMD_SIGN") { case (alice, bob, alice2bob, bob2alice, _, _) =>
|
||||
within(30 seconds) {
|
||||
val sender = TestProbe()
|
||||
val r = sha256_hash(1, 2, 3, 4)
|
||||
val h: sha256_hash = Crypto.sha256(r)
|
||||
sender.send(alice, CMD_ADD_HTLC(500000, h, locktime(Blocks(3))))
|
||||
sender.expectMsg("ok")
|
||||
alice2bob.expectMsgType[update_add_htlc]
|
||||
sender.send(alice, CMD_SIGN)
|
||||
sender.expectMsg("ok")
|
||||
alice2bob.expectMsgType[update_commit]
|
||||
awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.theirNextCommitInfo.isLeft)
|
||||
}
|
||||
}
|
||||
|
||||
/*test("recv CMD_SIGN (no changes)") { case (alice, bob, alice2bob, bob2alice, alice2blockchain, _) =>
|
||||
within(30 seconds) {
|
||||
val sender = TestProbe()
|
||||
sender.send(alice, CMD_SIGN)
|
||||
sender.expectMsg("cannot sign when there are no changes")
|
||||
}
|
||||
}*/
|
||||
|
||||
test("recv CMD_SIGN (while waiting for update_revocation)") { case (alice, bob, alice2bob, bob2alice, alice2blockchain, _) =>
|
||||
within(30 seconds) {
|
||||
val tx = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.ourCommit.publishableTx
|
||||
val sender = TestProbe()
|
||||
val r = sha256_hash(1, 2, 3, 4)
|
||||
val h: sha256_hash = Crypto.sha256(r)
|
||||
sender.send(alice, CMD_ADD_HTLC(500000, h, locktime(Blocks(3))))
|
||||
sender.expectMsg("ok")
|
||||
alice2bob.expectMsgType[update_add_htlc]
|
||||
awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.theirNextCommitInfo.isRight)
|
||||
sender.send(alice, CMD_SIGN)
|
||||
sender.expectMsg("ok")
|
||||
alice2bob.expectMsgType[update_commit]
|
||||
awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.theirNextCommitInfo.isLeft)
|
||||
sender.send(alice, CMD_SIGN)
|
||||
sender.expectMsg("cannot sign until next revocation hash is received")
|
||||
}
|
||||
}
|
||||
|
||||
test("recv update_commit") { case (alice, bob, alice2bob, bob2alice, _, _) =>
|
||||
within(30 seconds) {
|
||||
val sender = TestProbe()
|
||||
val r = sha256_hash(1, 2, 3, 4)
|
||||
val h: sha256_hash = Crypto.sha256(r)
|
||||
|
||||
sender.send(alice, CMD_ADD_HTLC(500000, h, locktime(Blocks(3))))
|
||||
sender.expectMsg("ok")
|
||||
val htlc = alice2bob.expectMsgType[update_add_htlc]
|
||||
alice2bob.forward(bob)
|
||||
awaitCond(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.theirChanges.proposed == htlc :: Nil)
|
||||
val initialState = bob.stateData.asInstanceOf[DATA_NORMAL]
|
||||
|
||||
sender.send(alice, CMD_SIGN)
|
||||
sender.expectMsg("ok")
|
||||
alice2bob.expectMsgType[update_commit]
|
||||
alice2bob.forward(bob)
|
||||
|
||||
bob2alice.expectMsgType[update_revocation]
|
||||
awaitCond(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.ourCommit.spec.htlcs.exists(h => h.id == htlc.id && h.direction == IN))
|
||||
assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.ourCommit.spec.amount_us_msat == initialState.commitments.ourCommit.spec.amount_us_msat)
|
||||
}
|
||||
}
|
||||
|
||||
/*test("recv update_commit (no changes)") { case (alice, bob, alice2bob, bob2alice, _, bob2blockchain) =>
|
||||
within(30 seconds) {
|
||||
val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.ourCommit.publishableTx
|
||||
val sender = TestProbe()
|
||||
// signature is invalid but it doesn't matter
|
||||
sender.send(bob, update_commit(signature(0, 0, 0, 0, 0, 0, 0, 0)))
|
||||
bob2alice.expectMsgType[error]
|
||||
awaitCond(bob.stateName == CLOSING)
|
||||
bob2blockchain.expectMsg(Publish(tx))
|
||||
bob2blockchain.expectMsgType[WatchConfirmed]
|
||||
}
|
||||
}*/
|
||||
|
||||
test("recv update_commit (invalid signature)") { case (alice, bob, alice2bob, bob2alice, _, bob2blockchain) =>
|
||||
within(30 seconds) {
|
||||
val sender = TestProbe()
|
||||
val r = sha256_hash(1, 2, 3, 4)
|
||||
val h: sha256_hash = Crypto.sha256(r)
|
||||
|
||||
sender.send(alice, CMD_ADD_HTLC(500000, h, locktime(Blocks(3))))
|
||||
sender.expectMsg("ok")
|
||||
val htlc = alice2bob.expectMsgType[update_add_htlc]
|
||||
alice2bob.forward(bob)
|
||||
awaitCond(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.theirChanges.proposed == htlc :: Nil)
|
||||
val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.ourCommit.publishableTx
|
||||
|
||||
sender.send(bob, update_commit(signature(0, 0, 0, 0, 0, 0, 0, 0)))
|
||||
bob2alice.expectMsgType[error]
|
||||
awaitCond(bob.stateName == CLOSING)
|
||||
bob2blockchain.expectMsg(Publish(tx))
|
||||
bob2blockchain.expectMsgType[WatchConfirmed]
|
||||
}
|
||||
}
|
||||
|
||||
test("recv update_revocation") { case (alice, bob, alice2bob, bob2alice, _, bob2blockchain) =>
|
||||
within(30 seconds) {
|
||||
val sender = TestProbe()
|
||||
val r = sha256_hash(1, 2, 3, 4)
|
||||
val h: sha256_hash = Crypto.sha256(r)
|
||||
|
||||
sender.send(alice, CMD_ADD_HTLC(500000, h, locktime(Blocks(3))))
|
||||
sender.expectMsg("ok")
|
||||
val htlc = alice2bob.expectMsgType[update_add_htlc]
|
||||
alice2bob.forward(bob)
|
||||
awaitCond(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.theirChanges.proposed == htlc :: Nil)
|
||||
val initialState = bob.stateData.asInstanceOf[DATA_NORMAL]
|
||||
|
||||
sender.send(alice, CMD_SIGN)
|
||||
sender.expectMsg("ok")
|
||||
alice2bob.expectMsgType[update_commit]
|
||||
alice2bob.forward(bob)
|
||||
|
||||
awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.theirNextCommitInfo.isLeft)
|
||||
bob2alice.expectMsgType[update_revocation]
|
||||
bob2alice.forward(alice)
|
||||
awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.theirNextCommitInfo.isRight)
|
||||
}
|
||||
}
|
||||
|
||||
test("recv update_revocation (invalid preimage") { case (alice, bob, alice2bob, bob2alice, alice2blockchain, _) =>
|
||||
within(30 seconds) {
|
||||
val tx = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.ourCommit.publishableTx
|
||||
val sender = TestProbe()
|
||||
val r = sha256_hash(1, 2, 3, 4)
|
||||
val h: sha256_hash = Crypto.sha256(r)
|
||||
|
||||
sender.send(alice, CMD_ADD_HTLC(500000, h, locktime(Blocks(3))))
|
||||
sender.expectMsg("ok")
|
||||
val htlc = alice2bob.expectMsgType[update_add_htlc]
|
||||
alice2bob.forward(bob)
|
||||
awaitCond(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.theirChanges.proposed == htlc :: Nil)
|
||||
|
||||
sender.send(alice, CMD_SIGN)
|
||||
sender.expectMsg("ok")
|
||||
alice2bob.expectMsgType[update_commit]
|
||||
alice2bob.forward(bob)
|
||||
|
||||
bob2alice.expectMsgType[update_revocation]
|
||||
sender.send(alice, update_revocation(sha256_hash(0, 0, 0, 0), sha256_hash(1, 1, 1, 1)))
|
||||
alice2bob.expectMsgType[error]
|
||||
awaitCond(alice.stateName == CLOSING)
|
||||
alice2blockchain.expectMsg(Publish(tx))
|
||||
alice2blockchain.expectMsgType[WatchConfirmed]
|
||||
}
|
||||
}
|
||||
|
||||
test("recv update_revocation (unexpectedly") { case (alice, bob, alice2bob, bob2alice, alice2blockchain, _) =>
|
||||
within(30 seconds) {
|
||||
val tx = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.ourCommit.publishableTx
|
||||
val sender = TestProbe()
|
||||
awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.theirNextCommitInfo.isRight)
|
||||
sender.send(alice, update_revocation(sha256_hash(0, 0, 0, 0), sha256_hash(1, 1, 1, 1)))
|
||||
alice2bob.expectMsgType[error]
|
||||
awaitCond(alice.stateName == CLOSING)
|
||||
alice2blockchain.expectMsg(Publish(tx))
|
||||
alice2blockchain.expectMsgType[WatchConfirmed]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*test("recv CMD_FULFILL_HTLC") { case (alice, bob, alice2bob, bob2alice, _) =>
|
||||
within(30 seconds) {
|
||||
val sender = TestProbe()
|
||||
val r = sha256_hash(1, 2, 3, 4)
|
||||
val h: sha256_hash = Crypto.sha256(r)
|
||||
sender.send(alice, CMD_ADD_HTLC(500000, h, locktime(Blocks(3))))
|
||||
sender.expectMsg("ok")
|
||||
val htlc = alice2bob.expectMsgType[update_add_htlc]
|
||||
alice2bob.forward(bob)
|
||||
awaitCond(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.theirChanges.proposed == htlc :: Nil)
|
||||
val initialState = bob.stateData.asInstanceOf[DATA_NORMAL]
|
||||
sender.send(bob, CMD_FULFILL_HTLC(htlc.id, r))
|
||||
sender.expectMsg("ok")
|
||||
val fulfill = bob2alice.expectMsgType[update_fulfill_htlc]
|
||||
awaitCond(bob.stateData == initialState.copy(commitments = initialState.commitments.copy(ourChanges = initialState.commitments.ourChanges.copy(initialState.commitments.ourChanges.proposed :+ fulfill))))
|
||||
}
|
||||
}*/
|
||||
|
||||
}
|
|
@ -110,6 +110,7 @@ class SynchronizationPipe(latch: CountDownLatch) extends Actor with ActorLogging
|
|||
import scala.io.Source
|
||||
val script = Source.fromFile(file).getLines().filterNot(_.startsWith("#")).toList
|
||||
exec(script, a, b)
|
||||
case "ok" => {}
|
||||
case msg if sender() == a =>
|
||||
log.info(s"a -> b $msg")
|
||||
b forward msg
|
||||
|
@ -120,11 +121,14 @@ class SynchronizationPipe(latch: CountDownLatch) extends Actor with ActorLogging
|
|||
}
|
||||
|
||||
def wait(a: ActorRef, b: ActorRef, script: List[String]): Receive = {
|
||||
case "ok" => {}
|
||||
case msg if sender() == a && script.head.startsWith("B:recv") =>
|
||||
log.info(s"a -> b $msg")
|
||||
b forward msg
|
||||
unstashAll()
|
||||
exec(script.drop(1), a, b)
|
||||
case msg if sender() == b && script.head.startsWith("A:recv") =>
|
||||
log.info(s"b -> a $msg")
|
||||
a forward msg
|
||||
unstashAll()
|
||||
exec(script.drop(1), a, b)
|
||||
|
|
Loading…
Add table
Reference in a new issue