1
0
Fork 0
mirror of https://github.com/ACINQ/eclair.git synced 2025-02-22 14:22:39 +01:00

added CMD_CLOSE/Shutdown tests to NORMAL

This commit is contained in:
pm47 2017-01-11 15:03:01 +01:00
parent e04a7d47c3
commit 3ddefdb124
7 changed files with 183 additions and 86 deletions

View file

@ -78,7 +78,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
feeratePerKw = localParams.feeratePerKw,
toSelfDelay = localParams.toSelfDelay,
maxAcceptedHtlcs = localParams.maxAcceptedHtlcs,
fundingPubkey = localParams.fundingPrivkey.toPoint,
fundingPubkey = localParams.fundingPrivKey.toPoint,
revocationBasepoint = localParams.revocationSecret.toPoint,
paymentBasepoint = localParams.paymentKey.toPoint,
delayedPaymentBasepoint = localParams.delayedPaymentKey.toPoint,
@ -104,7 +104,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
htlcMinimumMsat = localParams.htlcMinimumMsat,
toSelfDelay = localParams.toSelfDelay,
maxAcceptedHtlcs = localParams.maxAcceptedHtlcs,
fundingPubkey = localParams.fundingPrivkey.toPoint,
fundingPubkey = localParams.fundingPrivKey.toPoint,
revocationBasepoint = localParams.revocationSecret.toPoint,
paymentBasepoint = localParams.paymentKey.toPoint,
delayedPaymentBasepoint = localParams.delayedPaymentKey.toPoint,
@ -117,7 +117,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
feeratePerKw = open.feeratePerKw,
toSelfDelay = open.toSelfDelay,
maxAcceptedHtlcs = open.maxAcceptedHtlcs,
fundingPubkey = open.fundingPubkey,
fundingPubKey = open.fundingPubkey,
revocationBasepoint = open.revocationBasepoint,
paymentBasepoint = open.paymentBasepoint,
delayedPaymentBasepoint = open.delayedPaymentBasepoint)
@ -149,7 +149,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
feeratePerKw = localParams.feeratePerKw, // funder gets to choose the first feerate
toSelfDelay = accept.toSelfDelay,
maxAcceptedHtlcs = accept.maxAcceptedHtlcs,
fundingPubkey = accept.fundingPubkey,
fundingPubKey = accept.fundingPubkey,
revocationBasepoint = accept.revocationBasepoint,
paymentBasepoint = accept.paymentBasepoint,
delayedPaymentBasepoint = accept.delayedPaymentBasepoint
@ -161,8 +161,8 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
fundingSatoshis = fundingSatoshis,
minimumDepth = accept.minimumDepth,
autoSignInterval = autoSignInterval)
val localFundingPubkey = params.localParams.fundingPrivkey.toPoint
blockchain ! MakeFundingTx(localFundingPubkey, remoteParams.fundingPubkey, Satoshi(params.fundingSatoshis))
val localFundingPubkey = params.localParams.fundingPrivKey.toPoint
blockchain ! MakeFundingTx(localFundingPubkey, remoteParams.fundingPubKey, Satoshi(params.fundingSatoshis))
goto(WAIT_FOR_FUNDING_CREATED_INTERNAL) using DATA_WAIT_FOR_FUNDING_INTERNAL(temporaryChannelId, params, pushMsat, accept.firstPerCommitmentPoint)
case Event(CMD_CLOSE(_), _) => goto(CLOSED)
@ -180,7 +180,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
// let's create the first commitment tx that spends the yet uncommitted funding tx
val (localSpec, localCommitTx, remoteSpec, remoteCommitTx) = Funding.makeFirstCommitTxs(params, pushMsat, fundingTx.hash, fundingTxOutputIndex, remoteFirstPerCommitmentPoint)
val localSigOfRemoteTx = Transactions.sign(remoteCommitTx, localParams.fundingPrivkey) // signature of their initial commitment tx that pays them pushMsat
val localSigOfRemoteTx = Transactions.sign(remoteCommitTx, localParams.fundingPrivKey) // signature of their initial commitment tx that pays them pushMsat
them ! FundingCreated(
temporaryChannelId = temporaryChannelId,
txid = fundingTx.hash,
@ -202,8 +202,8 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
val (localSpec, localCommitTx, remoteSpec, remoteCommitTx) = Funding.makeFirstCommitTxs(params, pushMsat, fundingTxHash, fundingTxOutputIndex, remoteFirstPerCommitmentPoint)
// check remote signature validity
val localSigOfLocalTx = Transactions.sign(localCommitTx, localParams.fundingPrivkey)
val signedLocalCommitTx = Transactions.addSigs(localCommitTx, params.localParams.fundingPrivkey.toPoint, params.remoteParams.fundingPubkey, localSigOfLocalTx, remoteSig)
val localSigOfLocalTx = Transactions.sign(localCommitTx, localParams.fundingPrivKey)
val signedLocalCommitTx = Transactions.addSigs(localCommitTx, params.localParams.fundingPrivKey.toPoint, params.remoteParams.fundingPubKey, localSigOfLocalTx, remoteSig)
Transactions.checkSpendable(signedLocalCommitTx) match {
case Failure(cause) =>
log.error(cause, "their FundingCreated message contains an invalid signature")
@ -212,7 +212,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
goto(CLOSED)
case Success(_) =>
log.info(s"signing remote tx: $remoteCommitTx")
val localSigOfRemoteTx = Transactions.sign(remoteCommitTx, localParams.fundingPrivkey)
val localSigOfRemoteTx = Transactions.sign(remoteCommitTx, localParams.fundingPrivKey)
them ! FundingSigned(
temporaryChannelId = temporaryChannelId,
signature = localSigOfRemoteTx
@ -243,8 +243,8 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
when(WAIT_FOR_FUNDING_SIGNED)(handleExceptions {
case Event(FundingSigned(_, remoteSig), DATA_WAIT_FOR_FUNDING_SIGNED(temporaryChannelId, params, fundingTx, localSpec, localCommitTx, remoteCommit)) =>
// we make sure that their sig checks out and that our first commit tx is spendable
val localSigOfLocalTx = Transactions.sign(localCommitTx, localParams.fundingPrivkey)
val signedLocalCommitTx = Transactions.addSigs(localCommitTx, params.localParams.fundingPrivkey.toPoint, params.remoteParams.fundingPubkey, localSigOfLocalTx, remoteSig)
val localSigOfLocalTx = Transactions.sign(localCommitTx, localParams.fundingPrivKey)
val signedLocalCommitTx = Transactions.addSigs(localCommitTx, params.localParams.fundingPrivKey.toPoint, params.remoteParams.fundingPubKey, localSigOfLocalTx, remoteSig)
Transactions.checkSpendable(signedLocalCommitTx) match {
case Failure(cause) =>
log.error(cause, "their FundingSigned message contains an invalid signature")
@ -343,7 +343,6 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
case Event(c: CMD_ADD_HTLC, d: DATA_NORMAL) if d.localShutdown.isDefined =>
handleCommandError(sender, new RuntimeException("cannot send new htlcs, closing in progress"))
stay
case Event(c@CMD_ADD_HTLC(amountMsat, rHash, expiry, route, origin, id_opt, do_commit), d@DATA_NORMAL(channelId, params, commitments, _, downstreams)) =>
Try(Commitments.sendAdd(commitments, c)) match {
@ -437,31 +436,45 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
case Failure(cause) => handleLocalError(cause, d)
}
case Event(CMD_CLOSE(ourScriptPubKey_opt), d: DATA_NORMAL) =>
if (d.localShutdown.isDefined) {
sender ! "closing already in progress"
stay
} else {
val defaultScriptPubkey: BinaryData = Script.write(Script.pay2wpkh(d.params.localParams.finalPrivKey.toPoint))
val ourScriptPubKey = ourScriptPubKey_opt.getOrElse(defaultScriptPubkey)
val localShutdown = Shutdown(d.channelId, ourScriptPubKey)
them ! localShutdown
stay using d.copy(localShutdown = Some(localShutdown))
}
case Event(CMD_CLOSE(ourScriptPubKey_opt), d: DATA_NORMAL) if d.localShutdown.isDefined =>
handleCommandError(sender, new RuntimeException("closing already in progress"))
stay
case Event(remoteShutdown@Shutdown(_, remoteScriptPubkey), d@DATA_NORMAL(channelId, params, commitments, ourShutdownOpt, downstreams)) =>
val localShutdown: Shutdown = ourShutdownOpt.getOrElse {
case Event(c@CMD_CLOSE(ourScriptPubKey_opt), d: DATA_NORMAL) if Commitments.localHasChanges(d.commitments) =>
// TODO: simplistic behavior, we could maybe sign+close
handleCommandError(sender, new RuntimeException("cannot close when there are pending changes"))
stay
case Event(CMD_CLOSE(ourScriptPubKey_opt), d: DATA_NORMAL) =>
val ourScriptPubKey = ourScriptPubKey_opt.getOrElse(Script.write(Script.pay2wpkh(d.params.localParams.finalPrivKey.toPoint)))
val localShutdown = Shutdown(d.channelId, ourScriptPubKey)
handleCommandSuccess(sender, localShutdown, d.copy(localShutdown = Some(localShutdown)))
case Event(remoteShutdown@Shutdown(_, remoteScriptPubKey), d@DATA_NORMAL(channelId, params, commitments, ourShutdownOpt, downstreams)) if commitments.remoteChanges.proposed.size > 0 =>
handleLocalError(new RuntimeException("it is illegal to send a shutdown while having unsigned changes"), d)
case Event(remoteShutdown@Shutdown(_, remoteScriptPubKey), d@DATA_NORMAL(channelId, params, commitments, ourShutdownOpt, downstreams)) =>
Try(ourShutdownOpt.map(s => (s, commitments)).getOrElse {
// first if we have pending changes, we need to commit them
val commitments2 = if (Commitments.localHasChanges(commitments)) {
val (commitments1, commit) = Commitments.sendCommit(d.commitments)
them ! commit
commitments1
} else commitments
val defaultScriptPubkey: BinaryData = Script.write(Script.pay2wpkh(params.localParams.finalPrivKey.toPoint))
val c = Shutdown(channelId, defaultScriptPubkey)
them ! c
c
}
if (commitments.hasNoPendingHtlcs) {
val closingSigned = Closing.makeFirstClosingTx(params, commitments, localShutdown.scriptPubKey, remoteShutdown.scriptPubKey)
them ! closingSigned
goto(NEGOTIATING) using DATA_NEGOTIATING(channelId, params, commitments, localShutdown, remoteShutdown, closingSigned)
} else {
goto(SHUTDOWN) using DATA_SHUTDOWN(channelId, params, commitments, localShutdown, remoteShutdown, downstreams)
val shutdown = Shutdown(channelId, defaultScriptPubkey)
them ! shutdown
(shutdown, commitments2)
}) match {
case Success((localShutdown, commitments3))
if (commitments3.remoteNextCommitInfo.isRight && commitments3.localCommit.spec.htlcs.size == 0 && commitments3.localCommit.spec.htlcs.size == 0)
|| (commitments3.remoteNextCommitInfo.isLeft && commitments3.localCommit.spec.htlcs.size == 0 && commitments3.remoteNextCommitInfo.left.get.spec.htlcs.size == 0) =>
val closingSigned = Closing.makeFirstClosingTx(params, commitments3, localShutdown.scriptPubKey, remoteShutdown.scriptPubKey)
them ! closingSigned
goto(NEGOTIATING) using DATA_NEGOTIATING(channelId, params, commitments3, localShutdown, remoteShutdown, closingSigned)
case Success((localShutdown, commitments3)) =>
goto(SHUTDOWN) using DATA_SHUTDOWN(channelId, params, commitments3, localShutdown, remoteShutdown, downstreams)
case Failure(cause) => handleLocalError(cause, d)
}
case Event((BITCOIN_FUNDING_SPENT, tx: Transaction), d: DATA_NORMAL) if tx.txid == d.commitments.remoteCommit.txid => handleRemoteSpentCurrent(tx, d)
@ -580,7 +593,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
val nextClosingFee = Closing.nextClosingFee(Satoshi(d.localClosingSigned.feeSatoshis), Satoshi(remoteClosingFee))
val (_, closingSigned) = Closing.makeClosingTx(d.params, d.commitments, d.localShutdown.scriptPubKey, d.remoteShutdown.scriptPubKey, nextClosingFee)
them ! closingSigned
if (nextClosingFee == remoteClosingFee) {
if (nextClosingFee == Satoshi(remoteClosingFee)) {
publishMutualClosing(signedClosingTx)
goto(CLOSING) using DATA_CLOSING(d.commitments, ourSignature = Some(closingSigned), mutualClosePublished = Some(signedClosingTx))
} else {
@ -729,10 +742,14 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
downstream ! CMD_FULFILL_HTLC(origin.htlc_id, fulfill.paymentPreimage)
downstream ! CMD_SIGN
case (None, Left(fail)) =>
log.info(s"we were the origin payer for htlc #${htlc.id}")
log.info(s"we were the origin payer for htlc #${
htlc.id
}")
context.system.eventStream.publish(PaymentFailed(self, htlc.paymentHash, fail.reason.toStringUtf8))
case (None, Right(fulfill)) =>
log.info(s"we were the origin payer for htlc #${htlc.id}")
log.info(s"we were the origin payer for htlc #${
htlc.id
}")
context.system.eventStream.publish(PaymentSent(self, htlc.paymentHash))
}
}
@ -761,7 +778,9 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
}
def publishMutualClosing(mutualClosing: Transaction) = {
log.info(s"closingTxId=${mutualClosing.txid}")
log.info(s"closingTxId=${
mutualClosing.txid
}")
blockchain ! Publish(mutualClosing)
blockchain ! WatchConfirmed(self, mutualClosing.txid, 3, BITCOIN_CLOSE_DONE) // hardcoded mindepth
}
@ -787,7 +806,9 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
}
def handleRemoteSpentCurrent(tx: Transaction, d: HasCommitments) = {
log.warning(s"they published their current commit in txid=${tx.txid}")
log.warning(s"they published their current commit in txid=${
tx.txid
}")
assert(tx.txid == d.commitments.remoteCommit.txid)
blockchain ! WatchConfirmed(self, tx.txid, 3, BITCOIN_SPEND_THEIRS_DONE) // TODO hardcoded mindepth
@ -808,10 +829,14 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
}
def handleRemoteSpentOther(tx: Transaction, d: HasCommitments) = {
log.warning(s"funding tx spent in txid=${tx.txid}")
log.warning(s"funding tx spent in txid=${
tx.txid
}")
d.commitments.txDb.get(tx.txid) match {
case Some(spendingTx) =>
log.warning(s"txid=${tx.txid} was a revoked commitment, publishing the punishment tx")
log.warning(s"txid=${
tx.txid
} was a revoked commitment, publishing the punishment tx")
them ! Error(0, "Anchor has been spent".getBytes)
blockchain ! Publish(spendingTx)
blockchain ! WatchConfirmed(self, spendingTx.txid, 3, BITCOIN_STEAL_DONE)
@ -823,14 +848,18 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
goto(CLOSING) using nextData
case None =>
// the published tx was neither their current commitment nor a revoked one
log.error(s"couldn't identify txid=${tx.txid}!")
log.error(s"couldn't identify txid=${
tx.txid
}!")
goto(ERR_INFORMATION_LEAK)
}
}
def handleInformationLeak(d: HasCommitments) = {
// this is never supposed to happen !!
log.error(s"our funding tx ${d.commitments.anchorId} was spent !!")
log.error(s"our funding tx ${
d.commitments.anchorId
} was spent !!")
// TODO! channel id
them ! Error(0, "Anchor has been spent".getBytes)
blockchain ! Publish(d.commitments.localCommit.publishableTxs._1.tx)

View file

@ -155,7 +155,7 @@ final case class LocalParams(dustLimitSatoshis: Long,
feeratePerKw: Long,
toSelfDelay: Int,
maxAcceptedHtlcs: Int,
fundingPrivkey: Scalar,
fundingPrivKey: Scalar,
revocationSecret: Scalar,
paymentKey: Scalar,
delayedPaymentKey: Scalar,
@ -170,7 +170,7 @@ final case class RemoteParams(dustLimitSatoshis: Long,
feeratePerKw: Long,
toSelfDelay: Int,
maxAcceptedHtlcs: Int,
fundingPubkey: Point,
fundingPubKey: Point,
revocationBasepoint: Point,
paymentBasepoint: Point,
delayedPaymentBasepoint: Point)

View file

@ -138,7 +138,7 @@ object Commitments {
// remote commitment will includes all local changes + remote acked changes
val spec = CommitmentSpec.reduce(remoteCommit.spec, remoteChanges.acked, localChanges.proposed)
val (remoteCommitTx, htlcTimeoutTxs, htlcSuccessTxs) = makeRemoteTxs(remoteCommit.index + 1, localParams, remoteParams, commitInput, remoteNextPerCommitmentPoint, spec)
val sig = Transactions.sign(remoteCommitTx, localParams.fundingPrivkey)
val sig = Transactions.sign(remoteCommitTx, localParams.fundingPrivKey)
val sortedHtlcTxs: Seq[TransactionWithInputInfo] = (htlcTimeoutTxs ++ htlcSuccessTxs).sortBy(_.input.outPoint.index)
val paymentKey = Generators.derivePrivKey(localParams.paymentKey, remoteNextPerCommitmentPoint)
@ -182,12 +182,12 @@ object Commitments {
val localPerCommitmentPoint = Generators.perCommitPoint(localParams.shaSeed, commitments.localCommit.index.toInt + 1)
// TODO: Long or Int??
val (localCommitTx, htlcTimeoutTxs, htlcSuccessTxs) = makeLocalTxs(localCommit.index + 1, localParams, remoteParams, commitInput, localPerCommitmentPoint, spec)
val sig = Transactions.sign(localCommitTx, localParams.fundingPrivkey)
val sig = Transactions.sign(localCommitTx, localParams.fundingPrivKey)
// TODO: should we have optional sig? (original comment: this tx will NOT be signed if our output is empty)
// no need to compute htlc sigs if commit sig doesn't check out
val signedCommitTx = Transactions.addSigs(localCommitTx, localParams.fundingPrivkey.toPoint, remoteParams.fundingPubkey, sig, commit.signature)
val signedCommitTx = Transactions.addSigs(localCommitTx, localParams.fundingPrivKey.toPoint, remoteParams.fundingPubKey, sig, commit.signature)
if (Transactions.checkSpendable(signedCommitTx).isFailure) throw new RuntimeException("invalid sig")
val sortedHtlcTxs: Seq[TransactionWithInputInfo] = (htlcTimeoutTxs ++ htlcSuccessTxs).sortBy(_.input.outPoint.index)

View file

@ -42,7 +42,7 @@ object Helpers {
val localSpec = CommitmentSpec(Set.empty[Htlc], feeRatePerKw = params.localParams.feeratePerKw, toLocalMsat = toLocalMsat, toRemoteMsat = toRemoteMsat)
val remoteSpec = CommitmentSpec(Set.empty[Htlc], feeRatePerKw = params.remoteParams.feeratePerKw, toLocalMsat = toRemoteMsat, toRemoteMsat = toLocalMsat)
val commitmentInput = makeFundingInputInfo(fundingTxHash, fundingTxOutputIndex, Satoshi(params.fundingSatoshis), params.localParams.fundingPrivkey.toPoint, params.remoteParams.fundingPubkey)
val commitmentInput = makeFundingInputInfo(fundingTxHash, fundingTxOutputIndex, Satoshi(params.fundingSatoshis), params.localParams.fundingPrivKey.toPoint, params.remoteParams.fundingPubKey)
val localPerCommitmentPoint = Generators.perCommitPoint(params.localParams.shaSeed, 0)
val (localCommitTx, _, _) = Commitments.makeLocalTxs(0, params.localParams, params.remoteParams, commitmentInput, localPerCommitmentPoint, localSpec)
val (remoteCommitTx, _, _) = Commitments.makeRemoteTxs(0, params.localParams, params.remoteParams, commitmentInput, remoteFirstPerCommitmentPoint, remoteSpec)
@ -61,7 +61,7 @@ object Helpers {
val closingFee = Satoshi(20000)
// TODO: check commitments.localCommit.spec == commitments.remoteCommit.spec
val closingTx = Transactions.makeClosingTx(commitments.commitInput, localScriptPubkey, remoteScriptPubkey, commitments.localParams.isFunder, dustLimitSatoshis, closingFee, commitments.localCommit.spec)
val localClosingSig = Transactions.sign(closingTx, params.localParams.fundingPrivkey)
val localClosingSig = Transactions.sign(closingTx, params.localParams.fundingPrivKey)
val closingSigned = ClosingSigned(commitments.channelId, closingFee.amount, localClosingSig)
closingSigned
}
@ -71,7 +71,7 @@ object Helpers {
val dustLimitSatoshis = Satoshi(Math.max(commitments.localParams.dustLimitSatoshis, commitments.remoteParams.dustLimitSatoshis))
// TODO: check commitments.localCommit.spec == commitments.remoteCommit.spec
val closingTx = Transactions.makeClosingTx(commitments.commitInput, localScriptPubkey, remoteScriptPubkey, commitments.localParams.isFunder, dustLimitSatoshis, closingFee, commitments.localCommit.spec)
val localClosingSig = Transactions.sign(closingTx, params.localParams.fundingPrivkey)
val localClosingSig = Transactions.sign(closingTx, params.localParams.fundingPrivKey)
val closingSigned = ClosingSigned(commitments.channelId, closingFee.amount, localClosingSig)
(closingTx, closingSigned)
}
@ -80,8 +80,8 @@ object Helpers {
// TODO: check that
val dustLimitSatoshis = Satoshi(Math.max(commitments.localParams.dustLimitSatoshis, commitments.remoteParams.dustLimitSatoshis))
val closingTx = Transactions.makeClosingTx(commitments.commitInput, localScriptPubkey, remoteScriptPubkey, commitments.localParams.isFunder, dustLimitSatoshis, closingFee, commitments.localCommit.spec)
val localClosingSig = Transactions.sign(closingTx, params.localParams.fundingPrivkey)
val signedClosingTx = Transactions.addSigs(closingTx, commitments.localParams.fundingPrivkey.toPoint, commitments.remoteParams.fundingPubkey, localClosingSig, remoteClosingSig)
val localClosingSig = Transactions.sign(closingTx, params.localParams.fundingPrivKey)
val signedClosingTx = Transactions.addSigs(closingTx, commitments.localParams.fundingPrivKey.toPoint, commitments.remoteParams.fundingPubKey, localClosingSig, remoteClosingSig)
val closingSigned = ClosingSigned(commitments.channelId, closingFee.amount, localClosingSig)
Transactions.checkSpendable(signedClosingTx).map(x => signedClosingTx.tx)
}

View file

@ -46,7 +46,7 @@ class Register(blockchain: ActorRef, paymentHandler: ActorRef) extends Actor wit
feeratePerKw = 10000,
toSelfDelay = 144,
maxAcceptedHtlcs = 100,
fundingPrivkey = generateKey(0),
fundingPrivKey = generateKey(0),
revocationSecret = generateKey(1),
paymentKey = generateKey(2),
delayedPaymentKey = generateKey(3),

View file

@ -20,7 +20,7 @@ object TestConstants {
feeratePerKw = 10000,
toSelfDelay = 144,
maxAcceptedHtlcs = 100,
fundingPrivkey = Scalar(Array.fill[Byte](32)(1) :+ 1.toByte),
fundingPrivKey = Scalar(Array.fill[Byte](32)(1) :+ 1.toByte),
revocationSecret = Scalar(Array.fill[Byte](32)(2) :+ 1.toByte),
paymentKey = Scalar(Array.fill[Byte](32)(3) :+ 1.toByte),
delayedPaymentKey = Scalar(Array.fill[Byte](32)(4) :+ 1.toByte),
@ -39,7 +39,7 @@ object TestConstants {
feeratePerKw = 10000,
toSelfDelay = 144,
maxAcceptedHtlcs = 100,
fundingPrivkey = Scalar(Array.fill[Byte](32)(11) :+ 1.toByte),
fundingPrivKey = Scalar(Array.fill[Byte](32)(11) :+ 1.toByte),
revocationSecret = Scalar(Array.fill[Byte](32)(12) :+ 1.toByte),
paymentKey = Scalar(Array.fill[Byte](32)(13) :+ 1.toByte),
delayedPaymentKey = Scalar(Array.fill[Byte](32)(14) :+ 1.toByte),

View file

@ -3,13 +3,13 @@ package fr.acinq.eclair.channel.states.e
import akka.actor.Props
import akka.testkit.{TestFSMRef, TestProbe}
import fr.acinq.bitcoin.{BinaryData, Crypto, Satoshi, Script, ScriptFlags, Transaction, TxOut}
import fr.acinq.eclair.{TestBitcoinClient, TestConstants}
import fr.acinq.eclair.TestConstants.{Alice, Bob}
import fr.acinq.eclair.blockchain._
import fr.acinq.eclair.channel.states.{StateSpecBaseClass, StateTestsHelperMethods}
import fr.acinq.eclair.channel.{BITCOIN_FUNDING_DEPTHOK, Data, State, _}
import fr.acinq.eclair.transactions.{IN, OUT}
import fr.acinq.eclair.wire.{AcceptChannel, ClosingSigned, CommitSig, Error, FundingCreated, FundingLocked, FundingSigned, OpenChannel, RevokeAndAck, Shutdown, UpdateAddHtlc, UpdateFailHtlc, UpdateFulfillHtlc}
import fr.acinq.eclair.{TestBitcoinClient, TestConstants}
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
@ -117,6 +117,7 @@ class NormalStateSpec extends StateSpecBaseClass with StateTestsHelperMethods {
within(30 seconds) {
val sender = TestProbe()
sender.send(alice, CMD_CLOSE(None))
sender.expectMsg("ok")
alice2bob.expectMsgType[Shutdown]
awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].localShutdown.isDefined)
@ -260,13 +261,19 @@ class NormalStateSpec extends StateSpecBaseClass with StateTestsHelperMethods {
within(30 seconds) {
val sender = TestProbe()
val (r1, htlc1) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) // a->b (regular)
val (r2, htlc2) = addHtlc(8000000, alice, bob, alice2bob, bob2alice) // a->b (regular)
val (r3, htlc3) = addHtlc(300000, bob, alice, bob2alice, alice2bob) // b->a (dust)
val (r4, htlc4) = addHtlc(1000000, alice, bob, alice2bob, bob2alice) // a->b (regular)
val (r5, htlc5) = addHtlc(50000000, bob, alice, bob2alice, alice2bob) // b->a (regular)
val (r6, htlc6) = addHtlc(500000, alice, bob, alice2bob, bob2alice) // a->b (dust)
val (r7, htlc7) = addHtlc(4000000, bob, alice, bob2alice, alice2bob) // b->a (regular)
val (r1, htlc1) = addHtlc(50000000, alice, bob, alice2bob, bob2alice)
// a->b (regular)
val (r2, htlc2) = addHtlc(8000000, alice, bob, alice2bob, bob2alice)
// a->b (regular)
val (r3, htlc3) = addHtlc(300000, bob, alice, bob2alice, alice2bob)
// b->a (dust)
val (r4, htlc4) = addHtlc(1000000, alice, bob, alice2bob, bob2alice)
// a->b (regular)
val (r5, htlc5) = addHtlc(50000000, bob, alice, bob2alice, alice2bob)
// b->a (regular)
val (r6, htlc6) = addHtlc(500000, alice, bob, alice2bob, bob2alice)
// a->b (dust)
val (r7, htlc7) = addHtlc(4000000, bob, alice, bob2alice, alice2bob) // b->a (regular)
sender.send(alice, CMD_SIGN)
sender.expectMsg("ok")
@ -388,13 +395,19 @@ class NormalStateSpec extends StateSpecBaseClass with StateTestsHelperMethods {
test("recv RevokeAndAck (multiple htlcs in both directions)") { case (alice, bob, alice2bob, bob2alice, _, bob2blockchain) =>
within(30 seconds) {
val sender = TestProbe()
val (r1, htlc1) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) // a->b (regular)
val (r2, htlc2) = addHtlc(8000000, alice, bob, alice2bob, bob2alice) // a->b (regular)
val (r3, htlc3) = addHtlc(300000, bob, alice, bob2alice, alice2bob) // b->a (dust)
val (r4, htlc4) = addHtlc(1000000, alice, bob, alice2bob, bob2alice) // a->b (regular)
val (r5, htlc5) = addHtlc(50000000, bob, alice, bob2alice, alice2bob) // b->a (regular)
val (r6, htlc6) = addHtlc(500000, alice, bob, alice2bob, bob2alice) // a->b (dust)
val (r7, htlc7) = addHtlc(4000000, bob, alice, bob2alice, alice2bob) // b->a (regular)
val (r1, htlc1) = addHtlc(50000000, alice, bob, alice2bob, bob2alice)
// a->b (regular)
val (r2, htlc2) = addHtlc(8000000, alice, bob, alice2bob, bob2alice)
// a->b (regular)
val (r3, htlc3) = addHtlc(300000, bob, alice, bob2alice, alice2bob)
// b->a (dust)
val (r4, htlc4) = addHtlc(1000000, alice, bob, alice2bob, bob2alice)
// a->b (regular)
val (r5, htlc5) = addHtlc(50000000, bob, alice, bob2alice, alice2bob)
// b->a (regular)
val (r6, htlc6) = addHtlc(500000, alice, bob, alice2bob, bob2alice)
// a->b (dust)
val (r7, htlc7) = addHtlc(4000000, bob, alice, bob2alice, alice2bob) // b->a (regular)
sender.send(alice, CMD_SIGN)
sender.expectMsg("ok")
@ -640,6 +653,29 @@ class NormalStateSpec extends StateSpecBaseClass with StateTestsHelperMethods {
val sender = TestProbe()
awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].localShutdown.isEmpty)
sender.send(alice, CMD_CLOSE(None))
sender.expectMsg("ok")
alice2bob.expectMsgType[Shutdown]
awaitCond(alice.stateName == NORMAL)
awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].localShutdown.isDefined)
}
}
test("recv CMD_CLOSE (with unacked sent htlcs)") { case (alice, bob, alice2bob, bob2alice, _, _) =>
within(30 seconds) {
val sender = TestProbe()
val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice)
sender.send(alice, CMD_CLOSE(None))
sender.expectMsg("cannot close when there are pending changes")
}
}
test("recv CMD_CLOSE (with signed sent htlcs)") { case (alice, bob, alice2bob, bob2alice, _, _) =>
within(30 seconds) {
val sender = TestProbe()
val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice)
sign(alice, bob, alice2bob, bob2alice)
sender.send(alice, CMD_CLOSE(None))
sender.expectMsg("ok")
alice2bob.expectMsgType[Shutdown]
awaitCond(alice.stateName == NORMAL)
awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].localShutdown.isDefined)
@ -651,6 +687,7 @@ class NormalStateSpec extends StateSpecBaseClass with StateTestsHelperMethods {
val sender = TestProbe()
awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].localShutdown.isEmpty)
sender.send(alice, CMD_CLOSE(None))
sender.expectMsg("ok")
alice2bob.expectMsgType[Shutdown]
awaitCond(alice.stateName == NORMAL)
awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].localShutdown.isDefined)
@ -659,6 +696,21 @@ class NormalStateSpec extends StateSpecBaseClass with StateTestsHelperMethods {
}
}
test("recv CMD_CLOSE (while waiting for a RevokeAndAck)") { case (alice, bob, alice2bob, bob2alice, alice2blockchain, _) =>
within(30 seconds) {
val sender = TestProbe()
val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice)
sender.send(alice, CMD_SIGN)
sender.expectMsg("ok")
alice2bob.expectMsgType[CommitSig]
// actual test begins
sender.send(alice, CMD_CLOSE(None))
sender.expectMsg("ok")
alice2bob.expectMsgType[Shutdown]
awaitCond(alice.stateName == NORMAL)
}
}
test("recv Shutdown (no pending htlcs)") { case (alice, _, alice2bob, _, alice2blockchain, _) =>
within(30 seconds) {
val sender = TestProbe()
@ -669,31 +721,30 @@ class NormalStateSpec extends StateSpecBaseClass with StateTestsHelperMethods {
}
}
/**
* see https://github.com/ElementsProject/lightning/issues/29
*/
ignore("recv Shutdown (with unacked received htlcs)") { case (alice, bob, alice2bob, bob2alice, alice2blockchain, _) =>
test("recv Shutdown (with unacked sent htlcs)") { case (alice, bob, alice2bob, bob2alice, alice2blockchain, _) =>
within(30 seconds) {
val sender = TestProbe()
val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice)
sender.send(bob, CMD_CLOSE(None))
bob2alice.expectMsgType[Shutdown]
// actual test begins
sender.send(alice, Shutdown(0, "00" * 25))
bob2alice.forward(alice)
alice2bob.expectMsgType[CommitSig]
alice2bob.expectMsgType[Shutdown]
awaitCond(alice.stateName == SHUTDOWN)
}
}
/**
* see https://github.com/ElementsProject/lightning/issues/29
*/
ignore("recv Shutdown (with unacked sent htlcs)") { case (alice, bob, alice2bob, bob2alice, alice2blockchain, _) =>
test("recv Shutdown (with unacked received htlcs)") { case (alice, bob, alice2bob, bob2alice, _, bob2blockchain) =>
within(30 seconds) {
val sender = TestProbe()
val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice)
// actual test begins
sender.send(alice, Shutdown(0, "00" * 25))
alice2bob.expectMsgType[Shutdown]
awaitCond(alice.stateName == SHUTDOWN)
sender.send(bob, Shutdown(0, Script.write(Script.pay2wpkh(TestConstants.Alice.channelParams.finalPrivKey.toPoint))))
bob2alice.expectMsgType[Error]
bob2blockchain.expectMsgType[Publish]
bob2blockchain.expectMsgType[WatchConfirmed]
awaitCond(bob.stateName == CLOSING)
}
}
@ -702,9 +753,26 @@ class NormalStateSpec extends StateSpecBaseClass with StateTestsHelperMethods {
val sender = TestProbe()
val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice)
sign(alice, bob, alice2bob, bob2alice)
sign(bob, alice, bob2alice, alice2bob)
// actual test begins
sender.send(alice, Shutdown(0, "00" * 25))
sender.send(bob, Shutdown(0, Script.write(Script.pay2wpkh(TestConstants.Alice.channelParams.finalPrivKey.toPoint))))
bob2alice.expectMsgType[Shutdown]
awaitCond(bob.stateName == SHUTDOWN)
}
}
test("recv Shutdown (while waiting for a RevokeAndAck)") { case (alice, bob, alice2bob, bob2alice, alice2blockchain, _) =>
within(30 seconds) {
val sender = TestProbe()
val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice)
sender.send(alice, CMD_SIGN)
sender.expectMsg("ok")
alice2bob.expectMsgType[CommitSig]
sender.send(bob, CMD_CLOSE(None))
bob2alice.expectMsgType[Shutdown]
// actual test begins
bob2alice.forward(alice)
alice2bob.expectMsgType[Shutdown]
awaitCond(alice.stateName == SHUTDOWN)
}