1
0
mirror of https://github.com/ACINQ/eclair.git synced 2024-11-20 10:39:19 +01:00

added logs and slightly changed signature of claimRevokedRemoteCommitTxOutputs

This commit is contained in:
pm47 2017-01-19 17:02:07 +01:00
parent a58befd1ed
commit 22789d9395
3 changed files with 36 additions and 24 deletions

View File

@ -839,7 +839,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
log.warning(s"funding tx spent in txid=${tx.txid}")
Helpers.Closing.claimRevokedRemoteCommitTxOutputs(d.commitments, tx) match {
case Success(claimTxs) =>
case Some(claimTxs) =>
log.warning(s"txid=${tx.txid} was a revoked commitment, publishing the punishment tx")
them ! Error(0, "Funding tx has been spent".getBytes)
@ -861,9 +861,9 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
case _ => DATA_CLOSING(d.commitments, revokedCommitPublished = remoteCommitPublished :: Nil)
}
goto(CLOSING) using nextData
case Failure(t) =>
case None =>
// the published tx was neither their current commitment nor a revoked one
log.error(t, s"couldn't identify txid=${tx.txid}")
log.error(s"couldn't identify txid=${tx.txid}, something very bad is going on!!!")
goto(ERR_INFORMATION_LEAK)
}
}

View File

@ -1,11 +1,12 @@
package fr.acinq.eclair.channel
import fr.acinq.bitcoin.Crypto.{Point, Scalar, sha256}
import fr.acinq.bitcoin.{BinaryData, Crypto, Satoshi}
import fr.acinq.bitcoin.{BinaryData, Crypto, Satoshi, Transaction}
import fr.acinq.eclair.crypto.{Generators, ShaChain}
import fr.acinq.eclair.transactions.Transactions._
import fr.acinq.eclair.transactions._
import fr.acinq.eclair.wire._
import grizzled.slf4j.Logging
// @formatter:off
case class LocalChanges(proposed: List[UpdateMessage], signed: List[UpdateMessage], acked: List[UpdateMessage]) {
@ -43,7 +44,7 @@ case class Commitments(localParams: LocalParams, remoteParams: RemoteParams,
def addRemoteProposal(proposal: UpdateMessage): Commitments = Commitments.addRemoteProposal(this, proposal)
}
object Commitments {
object Commitments extends Logging {
/**
* add a change to our proposed change list
*
@ -230,6 +231,8 @@ object Commitments {
val theirChanges1 = remoteChanges.copy(proposed = Nil, acked = remoteChanges.acked ++ remoteChanges.proposed)
val commitments1 = commitments.copy(localCommit = ourCommit1, localChanges = ourChanges1, remoteChanges = theirChanges1)
logger.debug(s"current commit: index=${ourCommit1.index} htlc_in=${ourCommit1.spec.htlcs.filter(_.direction == IN).size} htlc_out=${ourCommit1.spec.htlcs.filter(_.direction == OUT).size} tx=${Transaction.write(ourCommit1.publishableTxs.commitTx.tx)}")
(commitments1, revocation)
}

View File

@ -187,9 +187,15 @@ object Helpers {
}
/**
* In reaction to the counterparty publishing a revoked commitment tx, we punish them by
* When an unexpected transaction spending the funding tx is detected:
* 1) we find out if the published transaction is one of remote's revoked txs
* 2) and then:
* a) if it is a revoked tx we build a set of transactions that will punish them by stealing all their funds
* b) otherwise there is nothing we can do
*
* @return a list of transactions (one per HTLC that we can claim) if the tx is a revoked commitment, [[None]] otherwise
*/
def claimRevokedRemoteCommitTxOutputs(commitments: Commitments, tx: Transaction): Try[Seq[TransactionWithInputInfo]] = Try {
def claimRevokedRemoteCommitTxOutputs(commitments: Commitments, tx: Transaction): Option[Seq[TransactionWithInputInfo]] = {
import commitments._
require(tx.txIn.size == 1, "commitment tx should have 1 input")
val obscuredTxNumber = Transactions.decodeTxNumber(tx.txIn(0).sequence, tx.lockTime)
@ -197,28 +203,31 @@ object Helpers {
val txnumber = Transactions.obscuredCommitTxNumber(obscuredTxNumber, remoteParams.paymentBasepoint, localParams.paymentKey.toPoint)
require(txnumber <= 0xffffffffffffL, "txnumber must be lesser than 48 bits long")
// now we know what commit number this tx is referring to, we can derive the commitment point from the shachain
val remotePerCommitmentSecret = remotePerCommitmentSecrets.getHash(0xFFFFFFFFFFFFFFFFL - txnumber).map(d => Scalar(d :+ 1.toByte)).getOrElse(throw new RuntimeException(s"cannot get commitment secret for txnumber=$txnumber"))
val remotePerCommitmentPoint = remotePerCommitmentSecret.toPoint
val remoteDelayedPubkey = Generators.derivePubKey(remoteParams.delayedPaymentBasepoint, remotePerCommitmentPoint)
val remoteRevocationPrivkey = Generators.revocationPrivKey(localParams.revocationSecret, remotePerCommitmentSecret)
remotePerCommitmentSecrets.getHash(0xFFFFFFFFFFFFFFFFL - txnumber)
.map(d => Scalar(d :+ 1.toByte))
.map { remotePerCommitmentSecret =>
val remotePerCommitmentPoint = remotePerCommitmentSecret.toPoint
// let's punish remote by stealing its main output
val mainDelayedRevokedTx = {
// TODO: we should use the current fee rate, not the initial fee rate that we get from localParams
val txinfo = Transactions.makeMainPunishmentTx(tx, remoteRevocationPrivkey.toPoint, localParams.defaultFinalScriptPubKey, remoteParams.toSelfDelay, remoteDelayedPubkey, commitments.localParams.feeratePerKw)
val sig = Transactions.sign(txinfo, remoteRevocationPrivkey)
Transactions.addSigs(txinfo, sig)
}
val remoteDelayedPubkey = Generators.derivePubKey(remoteParams.delayedPaymentBasepoint, remotePerCommitmentPoint)
val remoteRevocationPrivkey = Generators.revocationPrivKey(localParams.revocationSecret, remotePerCommitmentSecret)
// TODO: we don't claim htlcs outputs yet
// let's punish remote by stealing its main output
val mainDelayedRevokedTx = {
// TODO: we should use the current fee rate, not the initial fee rate that we get from localParams
val txinfo = Transactions.makeMainPunishmentTx(tx, remoteRevocationPrivkey.toPoint, localParams.defaultFinalScriptPubKey, remoteParams.toSelfDelay, remoteDelayedPubkey, commitments.localParams.feeratePerKw)
val sig = Transactions.sign(txinfo, remoteRevocationPrivkey)
Transactions.addSigs(txinfo, sig)
}
val txes = mainDelayedRevokedTx :: Nil
// TODO: we don't claim htlcs outputs yet
// OPTIONAL: let's check transactions are actually spendable
require(txes.forall(Transactions.checkSpendable(_).isSuccess), "the tx we produced are not spendable!")
val txes = mainDelayedRevokedTx :: Nil
txes
// OPTIONAL: let's check transactions are actually spendable
require(txes.forall(Transactions.checkSpendable(_).isSuccess), "the tx we produced are not spendable!")
txes
}
}
}