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:
parent
4e1980b41f
commit
4665ce8d4b
@ -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 {
|
||||
|
@ -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,38 +199,39 @@ 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?
|
||||
// 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(
|
||||
temporaryChannelId = temporaryChannelId,
|
||||
signature = localSigOfRemoteTx
|
||||
)
|
||||
|
||||
// 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
|
||||
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(
|
||||
temporaryChannelId = temporaryChannelId,
|
||||
signature = localSigOfRemoteTx
|
||||
)
|
||||
// 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 ! WatchConfirmed(self, fundingTxid, params.minimumDepth.toInt, BITCOIN_FUNDING_DEPTHOK)
|
||||
|
||||
// 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 ! 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)
|
||||
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)
|
||||
val commitments = Commitments(params.localParams, params.remoteParams,
|
||||
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 =>
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
})
|
||||
|
@ -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 ?
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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))))))
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user