mirror of
https://github.com/ACINQ/eclair.git
synced 2025-02-22 22:25:26 +01:00
added CMD_CLOSE/Shutdown tests to NORMAL
This commit is contained in:
parent
e04a7d47c3
commit
3ddefdb124
7 changed files with 183 additions and 86 deletions
|
@ -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"
|
||||
case Event(CMD_CLOSE(ourScriptPubKey_opt), d: DATA_NORMAL) if d.localShutdown.isDefined =>
|
||||
handleCommandError(sender, new RuntimeException("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(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)
|
||||
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, commitments, localShutdown, remoteShutdown, closingSigned)
|
||||
} else {
|
||||
goto(SHUTDOWN) using DATA_SHUTDOWN(channelId, params, commitments, localShutdown, remoteShutdown, downstreams)
|
||||
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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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,12 +261,18 @@ 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 (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)
|
||||
|
@ -388,12 +395,18 @@ 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 (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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue