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

completed rewiring of OPEN->NORMAL

This commit is contained in:
pm47 2016-12-08 14:57:50 +01:00
parent 4e1980b41f
commit 4665ce8d4b
12 changed files with 123 additions and 89 deletions

View File

@ -35,7 +35,7 @@ class PeerWatcher(client: ExtendedBitcoinClient, blockCount: Long)(implicit ec:
case NewBlock(block) =>
client.getBlockCount.map(count => context.system.eventStream.publish(CurrentBlockCount(count)))
// TODO : beware of the herd effect
// TODO: beware of the herd effect
watches.collect {
case w@WatchConfirmed(channel, txId, minDepth, event) =>
client.getTxConfirmations(txId.toString).map(_ match {

View File

@ -1,7 +1,7 @@
package fr.acinq.eclair.channel
import akka.actor.{ActorRef, FSM, LoggingFSM, Props}
import fr.acinq.bitcoin.{OutPoint, _}
import fr.acinq.bitcoin._
import fr.acinq.eclair._
import fr.acinq.eclair.blockchain._
import fr.acinq.eclair.channel.Helpers.Closing._
@ -94,8 +94,8 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
when(WAIT_FOR_OPEN_CHANNEL)(handleExceptions {
case Event(open: OpenChannel, DATA_WAIT_FOR_OPEN_CHANNEL(localParams, autoSignInterval)) =>
// TODO : here we should check if remote parameters suit us
// TODO : maybe also check uniqueness of temporary channel id
// TODO: here we should check if remote parameters suit us
// TODO: maybe also check uniqueness of temporary channel id
val minimumDepth = Globals.default_mindepth
val firstPerCommitmentPoint: BinaryData = Generators.perCommitPoint(localParams.shaSeed, 0)
them ! AcceptChannel(temporaryChannelId = Platform.currentTime,
@ -141,14 +141,14 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
when(WAIT_FOR_ACCEPT_CHANNEL)(handleExceptions {
case Event(accept: AcceptChannel, DATA_WAIT_FOR_ACCEPT_CHANNEL(temporaryChannelId, localParams, fundingSatoshis, pushMsat, autoSignInterval)) =>
// TODO : here we should check if remote parameters suit us
// TODO : check equality of temporaryChannelId? or should be done upstream
// TODO: here we should check if remote parameters suit us
// TODO: check equality of temporaryChannelId? or should be done upstream
val remoteParams = RemoteParams(
dustLimitSatoshis = accept.dustLimitSatoshis,
maxHtlcValueInFlightMsat = accept.maxHtlcValueInFlightMsat,
channelReserveSatoshis = accept.channelReserveSatoshis,
htlcMinimumMsat = accept.htlcMinimumMsat,
feeratePerKw = localParams.feeratePerKw, // TODO : this should probably be in AcceptChannel packet
feeratePerKw = localParams.feeratePerKw, // funder gets to choose the first feerate
toSelfDelay = accept.toSelfDelay,
maxAcceptedHtlcs = accept.maxAcceptedHtlcs,
fundingPubkey = accept.fundingPubkey,
@ -178,10 +178,10 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
case Event((fundingTx: Transaction, fundingTxOutputIndex: Int), DATA_WAIT_FOR_FUNDING_INTERNAL(temporaryChannelId, params, pushMsat, remoteFirstPerCommitmentPoint)) =>
// our wallet provided us with a funding tx
log.info(s"anchor txid=${fundingTx.txid}")
// let's create the first commitment tx that spends the yet uncommitted funding tx
val remoteSpec = CommitmentSpec(Set.empty[Htlc], feeRate = params.remoteParams.feeratePerKw, to_local_msat = pushMsat, to_remote_msat = params.fundingSatoshis * 1000 - pushMsat)
val commitmentInput = Funding.inputFromFundingTx(fundingTx.hash, fundingTxOutputIndex)
val remoteTx = CommitmentSpec.makeRemoteTxTemplate(params.localParams, params.remoteParams, commitmentInput :: Nil, remoteFirstPerCommitmentPoint, remoteSpec).makeTx
val (localSpec, localTx, remoteSpec, remoteTx, fundingTxOutput) = Funding.makeFirstCommitmentTx(funder = true, params, pushMsat, fundingTx.hash, fundingTxOutputIndex, remoteFirstPerCommitmentPoint)
val localSigOfRemoteTx = Signature.sign(params.localParams, params.remoteParams, Satoshi(params.fundingSatoshis), remoteTx) // signature of their initial commitment tx that pays them pushMsat
them ! FundingCreated(
temporaryChannelId = temporaryChannelId,
@ -189,7 +189,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
outputIndex = fundingTxOutputIndex,
signature = localSigOfRemoteTx
)
goto(WAIT_FOR_FUNDING_SIGNED) using DATA_WAIT_FOR_FUNDING_SIGNED(temporaryChannelId, params, pushMsat, fundingTx, fundingTxOutputIndex, RemoteCommit(0, remoteSpec, fundingTx.hash, remoteFirstPerCommitmentPoint))
goto(WAIT_FOR_FUNDING_SIGNED) using DATA_WAIT_FOR_FUNDING_SIGNED(temporaryChannelId, params, fundingTx, fundingTxOutputIndex, fundingTxOutput, localSpec, localTx, RemoteCommit(0, remoteSpec, fundingTx.hash, remoteFirstPerCommitmentPoint))
case Event(CMD_CLOSE(_), _) => goto(CLOSED)
@ -199,16 +199,18 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
})
when(WAIT_FOR_FUNDING_CREATED)(handleExceptions {
case Event(FundingCreated(_, fundingTxHash, fundingTxOutputIndex, remoteSignature), DATA_WAIT_FOR_FUNDING_CREATED(temporaryChannelId, params, pushMsat, remoteFirstPerCommitmentPoint)) =>
case Event(FundingCreated(_, fundingTxHash, fundingTxOutputIndex, remoteSig), DATA_WAIT_FOR_FUNDING_CREATED(temporaryChannelId, params, pushMsat, remoteFirstPerCommitmentPoint)) =>
// they fund the channel with their anchor tx, so the money is theirs (but we are paid pushMsat)
val localSpec = CommitmentSpec(Set.empty[Htlc], feeRate = params.localParams.feeratePerKw, to_remote_msat = params.fundingSatoshis * 1000 - pushMsat, to_local_msat = pushMsat)
val remoteSpec = CommitmentSpec(Set.empty[Htlc], feeRate = params.remoteParams.feeratePerKw, to_remote_msat = pushMsat, to_local_msat = params.fundingSatoshis * 1000 - pushMsat)
val (localSpec, localTx, remoteSpec, remoteTx, fundingTxOutput) = Funding.makeFirstCommitmentTx(funder = false, params, pushMsat, fundingTxHash, fundingTxOutputIndex, remoteFirstPerCommitmentPoint)
// TODO check remote signature validity?
// build and sign remote commit tx
val commitmentInput = Funding.inputFromFundingTx(fundingTxHash, fundingTxOutputIndex)
val remoteTx = CommitmentSpec.makeRemoteTxTemplate(params.localParams, params.remoteParams, commitmentInput :: Nil, remoteFirstPerCommitmentPoint, remoteSpec).makeTx
// check remote signature validity
signAndCheckSig(params.localParams, params.remoteParams, fundingTxOutput, localTx, remoteSig) match {
case Failure(cause) =>
log.error(cause, "their FundingCreated message contains an invalid signature")
them ! Error(temporaryChannelId, cause.getMessage.getBytes)
// we haven't anything at stake yet, we can just stop
goto(CLOSED)
case Success(signedTx) =>
log.info(s"signing remote tx: $remoteTx")
val localSigOfRemoteTx = Signature.sign(params.localParams, params.remoteParams, Satoshi(params.fundingSatoshis), remoteTx) // signature of their initial commitment tx that pays them pushMsat
them ! FundingSigned(
@ -218,19 +220,18 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
// watch the funding transaction
val fundingTxid = fundingTxHash.reverse //see https://github.com/ElementsProject/lightning/issues/17
blockchain ! WatchSpent(self, fundingTxid, fundingTxOutputIndex, 0, BITCOIN_FUNDING_SPENT) // TODO : should we wait for an acknowledgment from the watcher?
blockchain ! WatchSpent(self, fundingTxid, fundingTxOutputIndex, 0, BITCOIN_FUNDING_SPENT) // TODO: should we wait for an acknowledgment from the watcher?
blockchain ! WatchConfirmed(self, fundingTxid, params.minimumDepth.toInt, BITCOIN_FUNDING_DEPTHOK)
val ourRevocationPubkey: BinaryData = ???
// Helpers.revocationHash(ourParams.shaSeed, 0)
val ourTx: Transaction = ??? // makeOurTx(ourParams, theirParams, TxIn(OutPoint(fundingTxHash, fundingTxOutputIndex), Array.emptyByteArray, 0xffffffffL) :: Nil, ourRevocationHash, localSpec)
val commitments = Commitments(params.localParams, params.remoteParams,
LocalCommit(0, localSpec, ourTx), RemoteCommit(0, remoteSpec, remoteTx.txid, ???),
LocalChanges(Nil, Nil, Nil), RemoteChanges(Nil, Nil), 0L,
Right(???), ???, ShaChain.init, new BasicTxDb)
LocalCommit(0, localSpec, signedTx), RemoteCommit(0, remoteSpec, remoteTx.txid, remoteFirstPerCommitmentPoint),
LocalChanges(Nil, Nil, Nil), RemoteChanges(Nil, Nil),
localCurrentHtlcId = 0L,
remoteNextCommitInfo = Right(BinaryData("")), // we will receive their next per-commitment point in the next message, so we temporarily put an empty byte array
fundingTxOutput, ShaChain.init, new BasicTxDb)
context.system.eventStream.publish(ChannelIdAssigned(self, commitments.anchorId, Satoshi(params.fundingSatoshis)))
goto(WAIT_FOR_FUNDING_LOCKED) using DATA_WAIT_FOR_FUNDING_LOCKED(temporaryChannelId, params, commitments, None)
}
case Event(CMD_CLOSE(_), _) => goto(CLOSED)
@ -240,33 +241,26 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
})
when(WAIT_FOR_FUNDING_SIGNED)(handleExceptions {
case Event(FundingSigned(_, theirSig), DATA_WAIT_FOR_FUNDING_SIGNED(temporaryChannelId, params, pushMsat, anchorTx, anchorOutputIndex, remoteCommit)) =>
val anchorAmount = anchorTx.txOut(anchorOutputIndex).amount
val remoteSpec = remoteCommit.spec
// we build our commitment tx, sign it and check that it is spendable using the counterparty's sig
val localRevocationHash = Commitments.revocationHash(params.localParams.shaSeed, 0L)
val localSpec = CommitmentSpec(Set.empty[Htlc], feeRate = params.localParams.feeratePerKw, to_local_msat = anchorAmount.toLong * 1000 - pushMsat, to_remote_msat = pushMsat)
val localTx = CommitmentSpec.makeLocalTxTemplate(params.localParams, params.remoteParams, TxIn(OutPoint(anchorTx, anchorOutputIndex), Array.emptyByteArray, 0xffffffffL) :: Nil, localRevocationHash, localSpec).makeTx
case Event(FundingSigned(_, remoteSig), DATA_WAIT_FOR_FUNDING_SIGNED(temporaryChannelId, params, fundingTx, fundingTxOutputIndex, fundingTxOutput, localSpec, localTx, remoteCommit)) =>
// we make sure that their sig checks out and that our first commit tx is spendable
log.info(s"checking our tx: $localTx")
val ourSig = sign(params.localParams, params.remoteParams, anchorAmount, localTx)
val signedTx: Transaction = ???
//addSigs(ourParams, theirParams, anchorAmount, ourTx, ourSig, theirSig)
val anchorOutput: TxOut = ??? //anchorTx.txOut(anchorOutputIndex)
checksig(params.localParams, params.remoteParams, anchorOutput, signedTx) match {
signAndCheckSig(params.localParams, params.remoteParams, fundingTxOutput, localTx, remoteSig) match {
case Failure(cause) =>
log.error(cause, "their FundingSigned message contains an invalid signature")
them ! Error(temporaryChannelId, cause.getMessage.getBytes)
// we haven't published anything yet, we can just stop
goto(CLOSED)
case Success(_) =>
blockchain ! WatchConfirmed(self, anchorTx.txid, params.minimumDepth, BITCOIN_FUNDING_DEPTHOK)
blockchain ! WatchSpent(self, anchorTx.txid, anchorOutputIndex, 0, BITCOIN_FUNDING_SPENT)
blockchain ! Publish(anchorTx)
case Success(signedTx) =>
blockchain ! WatchConfirmed(self, fundingTx.txid, params.minimumDepth, BITCOIN_FUNDING_DEPTHOK)
blockchain ! WatchSpent(self, fundingTx.txid, fundingTxOutputIndex, 0, BITCOIN_FUNDING_SPENT)
blockchain ! Publish(fundingTx)
val commitments = Commitments(params.localParams, params.remoteParams,
LocalCommit(0, localSpec, signedTx), remoteCommit,
LocalChanges(Nil, Nil, Nil), RemoteChanges(Nil, Nil), 0L,
Right(???), anchorOutput, ShaChain.init, new BasicTxDb)
context.system.eventStream.publish(ChannelIdAssigned(self, commitments.anchorId, anchorAmount))
LocalChanges(Nil, Nil, Nil), RemoteChanges(Nil, Nil),
localCurrentHtlcId = 0L,
remoteNextCommitInfo = Right(BinaryData("")), // we will receive their next per-commitment point in the next message, so we temporarily put an empty byte array
fundingTxOutput, ShaChain.init, new BasicTxDb)
context.system.eventStream.publish(ChannelIdAssigned(self, commitments.anchorId, Satoshi(params.fundingSatoshis)))
context.system.eventStream.publish(ChannelSignatureReceived(self, commitments))
goto(WAIT_FOR_FUNDING_LOCKED) using DATA_WAIT_FOR_FUNDING_LOCKED(temporaryChannelId, params, commitments, None)
}
@ -286,9 +280,10 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
case Event(BITCOIN_FUNDING_DEPTHOK, d@DATA_WAIT_FOR_FUNDING_LOCKED(temporaryChannelId, params, commitments, deferred)) =>
val channelId = 0L
blockchain ! WatchLost(self, commitments.anchorId, params.minimumDepth, BITCOIN_FUNDING_LOST)
them ! FundingLocked(channelId, 0L, "00" * 64, "00" * 64, ???) // TODO
val nextPerCommitmentPoint: BinaryData = Generators.perCommitPoint(localParams.shaSeed, 1)
them ! FundingLocked(channelId, 0L, "00" * 64, "00" * 64, nextPerCommitmentPoint) // TODO: routing announcements disabled
deferred.map(self ! _)
//TODO htlcIdx should not be 0 when resuming connection
// TODO: htlcIdx should not be 0 when resuming connection
goto(WAIT_FOR_FUNDING_LOCKED) using DATA_NORMAL(channelId, params, commitments, None, Map())
case Event(BITCOIN_FUNDING_TIMEOUT, _) =>
@ -317,6 +312,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
when(WAIT_FOR_FUNDING_LOCKED)(handleExceptions {
case Event(FundingLocked(temporaryChannelId, channelId, _, _, nextPerCommitmentPoint), d: DATA_NORMAL) =>
// TODO: check channelId matches ours
Register.create_alias(theirNodeId, d.commitments.anchorId)
goto(NORMAL)
@ -402,7 +398,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
}
case Event(CMD_SIGN, d: DATA_NORMAL) if d.commitments.remoteNextCommitInfo.isLeft =>
//TODO : this is a temporary fix
// TODO: this is a temporary fix
log.info(s"already in the process of signing, delaying CMD_SIGN")
context.system.scheduler.scheduleOnce(100 milliseconds, self, CMD_SIGN)
stay
@ -517,7 +513,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
}
case Event(CMD_SIGN, d: DATA_SHUTDOWN) if d.commitments.remoteNextCommitInfo.isLeft =>
//TODO : this is a temporary fix
// TODO: this is a temporary fix
log.info(s"already in the process of signing, delaying CMD_SIGN")
context.system.scheduler.scheduleOnce(100 milliseconds, self, CMD_SIGN)
stay
@ -534,7 +530,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
}
case Event(msg@CommitSig(_, theirSig, theirHtlcSigs), d@DATA_SHUTDOWN(channelId, params, commitments, ourShutdown, theirShutdown, _)) =>
// TODO : we might have to propagate htlcs upstream depending on the outcome of https://github.com/ElementsProject/lightning/issues/29
// TODO: we might have to propagate htlcs upstream depending on the outcome of https://github.com/ElementsProject/lightning/issues/29
Try(Commitments.receiveCommit(d.commitments, msg)) match {
case Success((commitments1, revocation)) if commitments1.hasNoPendingHtlcs =>
them ! revocation
@ -717,11 +713,11 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
case Success(upstream) =>
log.info(s"forwarding htlc #${add.id} to upstream=$upstream")
val upstream_route = route(rest)
// TODO : we should decrement expiry !!
// TODO: we should decrement expiry !!
upstream ! CMD_ADD_HTLC(amountMsat, add.rHash, add.expiry, upstream_route, Some(Origin(anchorId, add.id)))
upstream ! CMD_SIGN
case Failure(t: Throwable) =>
// TODO : send "fail route error"
// TODO: send "fail route error"
log.warning(s"couldn't resolve upstream node, htlc #${add.id} will timeout", t)
}
case route_step(amount, Next.End(true)) +: rest =>

View File

@ -1,6 +1,7 @@
package fr.acinq.eclair.channel
import fr.acinq.bitcoin.{BinaryData, Crypto, Satoshi, Transaction}
import fr.acinq.bitcoin.{BinaryData, Transaction, TxOut}
import fr.acinq.eclair.transactions.CommitmentSpec
import fr.acinq.eclair.wire.{ClosingSigned, FundingLocked, Shutdown}
import lightning.{route, route_step}
@ -122,7 +123,7 @@ final case class DATA_WAIT_FOR_OPEN_CHANNEL(localParams: LocalParams, autoSignIn
final case class DATA_WAIT_FOR_ACCEPT_CHANNEL(temporaryChannelId: Long, localParams: LocalParams, fundingSatoshis: Long, pushMsat: Long, autoSignInterval: Option[FiniteDuration]) extends Data
final case class DATA_WAIT_FOR_FUNDING_INTERNAL(temporaryChannelId: Long, params: ChannelParams, pushMsat: Long, remoteFirstPerCommitmentPoint: BinaryData) extends Data
final case class DATA_WAIT_FOR_FUNDING_CREATED(temporaryChannelId: Long, params: ChannelParams, pushMsat: Long, remoteFirstPerCommitmentPoint: BinaryData) extends Data
final case class DATA_WAIT_FOR_FUNDING_SIGNED(temporaryChannelId: Long, params: ChannelParams, pushMsat: Long, anchorTx: Transaction, anchorOutputIndex: Int, remoteCommit: RemoteCommit) extends Data
final case class DATA_WAIT_FOR_FUNDING_SIGNED(temporaryChannelId: Long, params: ChannelParams, fundingTx: Transaction, fundingTxOutputIndex: Int, fundingTxOutput: TxOut, localSpec: CommitmentSpec, localTx: Transaction, remoteCommit: RemoteCommit) extends Data
final case class DATA_WAIT_FOR_FUNDING_LOCKED(temporaryChannelId: Long, params: ChannelParams, commitments: Commitments, deferred: Option[FundingLocked]) extends Data with HasCommitments
final case class DATA_NORMAL(channelId: Long, params: ChannelParams, commitments: Commitments, ourShutdown: Option[Shutdown], downstreams: Map[Long, Option[Origin]]) extends Data with HasCommitments
final case class DATA_SHUTDOWN(channelId: Long, params: ChannelParams, commitments: Commitments,

View File

@ -13,7 +13,7 @@ case class LocalChanges(proposed: List[UpdateMessage], signed: List[UpdateMessag
case class RemoteChanges(proposed: List[UpdateMessage], acked: List[UpdateMessage])
case class Changes(ourChanges: LocalChanges, theirChanges: RemoteChanges)
case class LocalCommit(index: Long, spec: CommitmentSpec, publishableTx: Transaction)
case class RemoteCommit(index: Long, spec: CommitmentSpec, txid: BinaryData, theirRevocationHash: BinaryData)
case class RemoteCommit(index: Long, spec: CommitmentSpec, txid: BinaryData, remotePerCommitmentPoint: BinaryData)
// @formatter:on
/**
@ -101,7 +101,7 @@ object Commitments {
commitments.remoteCommit.spec.htlcs.collectFirst { case u: Htlc if u.add.id == fulfill.id => u.add } match {
case Some(htlc) if htlc.paymentHash == sha256(fulfill.paymentPreimage) => (addRemoteProposal(commitments, fulfill), htlc)
case Some(htlc) => throw new RuntimeException(s"invalid htlc preimage for htlc id=${fulfill.id}")
case None => throw new RuntimeException(s"unknown htlc id=${fulfill.id}") // TODO : we should fail the channel
case None => throw new RuntimeException(s"unknown htlc id=${fulfill.id}") // TODO: we should fail the channel
}
}
@ -119,7 +119,7 @@ object Commitments {
def receiveFail(commitments: Commitments, fail: UpdateFailHtlc): (Commitments, UpdateAddHtlc) = {
commitments.remoteCommit.spec.htlcs.collectFirst { case u: Htlc if u.add.id == fail.id => u.add } match {
case Some(htlc) => (addRemoteProposal(commitments, fail), htlc)
case None => throw new RuntimeException(s"unknown htlc id=${fail.id}") // TODO : we should fail the channel
case None => throw new RuntimeException(s"unknown htlc id=${fail.id}") // TODO: we should fail the channel
}
}
@ -134,13 +134,13 @@ object Commitments {
def sendCommit(commitments: Commitments): (Commitments, CommitSig) = {
import commitments._
commitments.remoteNextCommitInfo match {
case Right(remoteNextRevocationHash) if !localHasChanges(commitments) =>
case Right(remoteNextPerCommitmentPoint) if !localHasChanges(commitments) =>
throw new RuntimeException("cannot sign when there are no changes")
case Right(theirNextRevocationHash) =>
case Right(remoteNextPerCommitmentPoint) =>
// sign all our proposals + their acked proposals
// their commitment now includes all our changes + their acked changes
val spec = CommitmentSpec.reduce(remoteCommit.spec, remoteChanges.acked, localChanges.proposed)
val theirTxTemplate = CommitmentSpec.makeRemoteTxTemplate(localParams, remoteParams, localCommit.publishableTx.txIn, theirNextRevocationHash, spec)
val theirTxTemplate = CommitmentSpec.makeRemoteTxTemplate(localParams, remoteParams, localCommit.publishableTx.txIn, remoteNextPerCommitmentPoint, spec)
val theirTx = theirTxTemplate.makeTx
// don't sign if they don't get paid
val commit: CommitSig = ???
@ -151,7 +151,7 @@ object Commitments {
CommitSig(None)
}*/
val commitments1 = commitments.copy(
remoteNextCommitInfo = Left(RemoteCommit(remoteCommit.index + 1, spec, theirTx.txid, theirNextRevocationHash)),
remoteNextCommitInfo = Left(RemoteCommit(remoteCommit.index + 1, spec, theirTx.txid, remoteNextPerCommitmentPoint)),
localChanges = localChanges.copy(proposed = Nil, signed = localChanges.proposed),
remoteChanges = remoteChanges.copy(acked = Nil))
(commitments1, commit)
@ -214,11 +214,11 @@ object Commitments {
import commitments._
// we receive a revocation because we just sent them a sig for their next commit tx
remoteNextCommitInfo match {
case Left(theirNextCommit) if BinaryData(Crypto.sha256(revocation.perCommitmentSecret)) != BinaryData(remoteCommit.theirRevocationHash) =>
case Left(theirNextCommit) if BinaryData(Crypto.sha256(revocation.perCommitmentSecret)) != BinaryData(remoteCommit.remotePerCommitmentPoint) =>
throw new RuntimeException("invalid preimage")
case Left(theirNextCommit) =>
// this is their revoked commit tx
val theirTxTemplate = CommitmentSpec.makeRemoteTxTemplate(localParams, remoteParams, localCommit.publishableTx.txIn, remoteCommit.theirRevocationHash, remoteCommit.spec)
val theirTxTemplate = CommitmentSpec.makeRemoteTxTemplate(localParams, remoteParams, localCommit.publishableTx.txIn, remoteCommit.remotePerCommitmentPoint, remoteCommit.spec)
val theirTx = theirTxTemplate.makeTx
val punishTx: Transaction = ??? //Helpers.claimRevokedCommitTx(theirTxTemplate, revocation.revocationPreimage, localParams.finalPrivKey)
Transaction.correctlySpends(punishTx, Seq(theirTx), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)
@ -243,10 +243,10 @@ object Commitments {
commitments.remoteNextCommitInfo match {
case Left(theirNextCommit) =>
CommitmentSpec.makeRemoteTxTemplate(commitments.localParams, commitments.remoteParams, commitments.localCommit.publishableTx.txIn,
theirNextCommit.theirRevocationHash, theirNextCommit.spec)
theirNextCommit.remotePerCommitmentPoint, theirNextCommit.spec)
case Right(revocationHash) =>
CommitmentSpec.makeRemoteTxTemplate(commitments.localParams, commitments.remoteParams, commitments.localCommit.publishableTx.txIn,
commitments.remoteCommit.theirRevocationHash, commitments.remoteCommit.spec)
commitments.remoteCommit.remotePerCommitmentPoint, commitments.remoteCommit.spec)
}
}
}

View File

@ -1,6 +1,7 @@
package fr.acinq.eclair.channel
import fr.acinq.bitcoin.{OutPoint, _}
import fr.acinq.eclair.crypto.Generators
import fr.acinq.eclair.transactions.OldScripts._
import fr.acinq.eclair.transactions._
import fr.acinq.eclair.wire.UpdateFulfillHtlc
@ -22,6 +23,35 @@ object Helpers {
*/
def inputFromFundingTx(fundingTxId: BinaryData, fundingTxOutputIndex: Int): TxIn = TxIn(OutPoint(fundingTxId, fundingTxOutputIndex), Array.emptyByteArray, 0xffffffffL)
/**
* Creates both sides's first commitment transaction
* @param funder
* @param params
* @param pushMsat
* @param fundingTxHash
* @param fundingTxOutputIndex
* @param remoteFirstPerCommitmentPoint
* @return (localSpec, localTx, remoteSpec, remoteTx, fundingTxOutput)
*/
def makeFirstCommitmentTx(funder: Boolean, params: ChannelParams, pushMsat: Long, fundingTxHash: BinaryData, fundingTxOutputIndex: Int, remoteFirstPerCommitmentPoint: BinaryData): (CommitmentSpec, Transaction, CommitmentSpec, Transaction, TxOut) = {
val toLocalMsat = if (funder) params.fundingSatoshis * 1000 - pushMsat else pushMsat
val toRemoteMsat = if (funder) pushMsat else params.fundingSatoshis * 1000 - pushMsat
// local and remote feerate are the same at this point (funder gets to choose the initial feerate)
val localSpec = CommitmentSpec(Set.empty[Htlc], feeRate = params.localParams.feeratePerKw, to_local_msat = toLocalMsat, to_remote_msat = toRemoteMsat)
val remoteSpec = CommitmentSpec(Set.empty[Htlc], feeRate = params.remoteParams.feeratePerKw, to_local_msat = toRemoteMsat, to_remote_msat = toLocalMsat)
val commitmentInput = Funding.inputFromFundingTx(fundingTxHash, fundingTxOutputIndex)
val localPerCommitmentPoint: BinaryData = Generators.perCommitPoint(params.localParams.shaSeed, 0)
val localTx = CommitmentSpec.makeLocalTxTemplate(params.localParams, params.remoteParams, commitmentInput :: Nil, localPerCommitmentPoint, localSpec).makeTx
val remoteTx = CommitmentSpec.makeRemoteTxTemplate(params.localParams, params.remoteParams, commitmentInput :: Nil, remoteFirstPerCommitmentPoint, remoteSpec).makeTx
val localFundingPubkey = Crypto.publicKeyFromPrivateKey(params.localParams.fundingPrivkey)
val fundingTxOutput = TxOut(Satoshi(params.fundingSatoshis), publicKeyScript = OldScripts.anchorPubkeyScript(localFundingPubkey, params.remoteParams.fundingPubkey))
(localSpec, localTx, remoteSpec, remoteTx, fundingTxOutput)
}
}
object Closing {

View File

@ -139,7 +139,7 @@ class AuthHandler(them: ActorRef, blockchain: ActorRef, paymentHandler: ActorRef
log.debug(s"received chunk=${BinaryData(chunk)}")
val decryptor1 = Decryptor.add(decryptor, chunk)
decryptor1.bodies.map(plaintext => {
// TODO : redo this
// TODO: redo this
val msg = lightningMessageCodec.decode(BitVector(plaintext.data)).toOption.get.value
self ! msg
})

View File

@ -24,7 +24,7 @@ class Client(remote: InetSocketAddress, amount: Satoshi, register: ActorRef) ext
log.info(s"connected to $remote")
val connection = sender()
register ! CreateChannel(connection, Some(amount))
// TODO : kill this actor ?
// TODO: kill this actor ?
}
}

View File

@ -24,7 +24,7 @@ class LocalPaymentHandler extends Actor with ActorLogging {
override def receive: Receive = run(Map())
//TODO: store this map on file ?
// TODO: store this map on file ?
def run(h2r: Map[BinaryData, BinaryData]): Receive = {
case 'genh =>
val r = generateR()

View File

@ -100,7 +100,7 @@ object PaymentLifecycle {
def buildRoute(finalAmountMsat: Int, nodeIds: Seq[BinaryData]): lightning.route = {
// TODO : use actual fee parameters that are specific to each node
// TODO: use actual fee parameters that are specific to each node
def fee(amountMsat: Int) = nodeFee(Globals.base_fee, Globals.proportional_fee, amountMsat).toInt
var amountMsat = finalAmountMsat

View File

@ -10,20 +10,27 @@ import scala.util.Try
*/
object Signature {
def sign(localParams: LocalParams, RemoteParams: RemoteParams, anchorAmount: Satoshi, tx: Transaction): BinaryData = ???
def sign(localParams: LocalParams, remoteParams: RemoteParams, fundingSatoshis: Satoshi, tx: Transaction): BinaryData = ???
//bin2signature(Transaction.signInput(tx, 0, multiSig2of2(ourParams.commitPubKey, theirParams.commitPubKey), SIGHASH_ALL, anchorAmount, 1, ourParams.commitPrivKey))
// Transaction.signInput(tx, 0, OldScripts.multiSig2of2(ourParams.commitPubKey, theirParams.commitPubKey), SIGHASH_ALL, anchorAmount, 1, ourParams.commitPrivKey)
def addSigs(localParams: LocalParams, RemoteParams: RemoteParams, anchorAmount: Satoshi, tx: Transaction, ourSig: BinaryData, theirSig: BinaryData): Transaction = ???
def addSigs(localParams: LocalParams, remoteParams: RemoteParams, fundingSatoshis: Satoshi, tx: Transaction, localSig: BinaryData, remoteSig: BinaryData): Transaction = ???
/*{
// TODO : Transaction.sign(...) should handle multisig
// TODO: Transaction.sign(...) should handle multisig
val ourSig = Transaction.signInput(tx, 0, multiSig2of2(ourParams.commitPubKey, theirParams.commitPubKey), SIGHASH_ALL, anchorAmount, 1, ourParams.commitPrivKey)
val witness = witness2of2(theirSig, ourSig, theirParams.commitPubKey, ourParams.commitPubKey)
tx.updateWitness(0, witness)
}*/
def checksig(localParams: LocalParams, RemoteParams: RemoteParams, anchorOutput: TxOut, tx: Transaction): Try[Unit] =
def checksig(localParams: LocalParams, remoteParams: RemoteParams, anchorOutput: TxOut, tx: Transaction): Try[Unit] =
Try(Transaction.correctlySpends(tx, Map(tx.txIn(0).outPoint -> anchorOutput), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS))
def signAndCheckSig(localParams: LocalParams, remoteParams: RemoteParams, anchorOutput: TxOut, tx: Transaction, remoteSig: BinaryData): Try[Transaction] = {
val localSig = sign(localParams, remoteParams, anchorOutput.amount, tx)
val signedTx = addSigs(localParams, remoteParams, anchorOutput.amount, tx, localSig, remoteSig)
checksig(localParams, remoteParams, anchorOutput, signedTx).map(_ => signedTx)
}
}

View File

@ -35,7 +35,7 @@ class ThroughputSpec extends FunSuite {
override def receive: Receive = ???
//TODO: store this map on file ?
// TODO: store this map on file ?
def run(h2r: Map[BinaryData, BinaryData]): Receive = {
case ('add, tgt: ActorRef) =>
val r = generateR()

View File

@ -694,7 +694,7 @@ class NormalStateSpec extends StateSpecBaseClass with StateTestsHelperMethods {
assert(revokedTx.txOut.size == 6)
// the punishment tx consumes all output but ours (which already goes to our final key)
assert(punishTx.txIn.size == 5)
// TODO : when changefee is implemented we should set fee = 0 and check against 304 000
// TODO: when changefee is implemented we should set fee = 0 and check against 304 000
assert(punishTx.txOut == Seq(TxOut(Satoshi(301670), Script.write(OldScripts.pay2wpkh(Crypto.publicKeyFromPrivateKey(Alice.channelParams.finalPrivKey))))))
}
}