mirror of
https://github.com/ACINQ/eclair.git
synced 2025-03-13 19:37:35 +01:00
Merge branch 'master' into android
This commit is contained in:
commit
ba2321688a
35 changed files with 373 additions and 235 deletions
|
@ -32,12 +32,12 @@ object Features {
|
|||
val CHANNEL_RANGE_QUERIES_BIT_MANDATORY = 6
|
||||
val CHANNEL_RANGE_QUERIES_BIT_OPTIONAL = 7
|
||||
|
||||
val VARIABLE_LENGTH_ONION_MANDATORY = 8
|
||||
val VARIABLE_LENGTH_ONION_OPTIONAL = 9
|
||||
|
||||
val CHANNEL_RANGE_QUERIES_EX_BIT_MANDATORY = 10
|
||||
val CHANNEL_RANGE_QUERIES_EX_BIT_OPTIONAL = 11
|
||||
|
||||
val VARIABLE_LENGTH_ONION_MANDATORY = 8
|
||||
val VARIABLE_LENGTH_ONION_OPTIONAL = 9
|
||||
|
||||
// Note that BitVector indexes from left to right whereas the specification indexes from right to left.
|
||||
// This is why we have to reverse the bits to check if a feature is set.
|
||||
|
||||
|
|
|
@ -57,8 +57,8 @@ object JsonSerializers {
|
|||
implicit val remoteCommitsReadWriter: ReadWriter[RemoteCommit] = macroRW
|
||||
implicit val commitSgReadWriter: ReadWriter[CommitSig] = macroRW
|
||||
implicit val waitingForRevocationReadWriter: ReadWriter[WaitingForRevocation] = macroRW
|
||||
implicit val localOriginReadWriter: ReadWriter[fr.acinq.eclair.payment.Local] = macroRW
|
||||
implicit val relayedOriginReadWriter: ReadWriter[fr.acinq.eclair.payment.Relayed] = macroRW
|
||||
implicit val localOriginReadWriter: ReadWriter[fr.acinq.eclair.payment.Origin.Local] = macroRW
|
||||
implicit val relayedOriginReadWriter: ReadWriter[fr.acinq.eclair.payment.Origin.Relayed] = macroRW
|
||||
implicit val paymentOriginReadWriter: ReadWriter[Origin] = ReadWriter.merge(localOriginReadWriter, relayedOriginReadWriter)
|
||||
implicit val remoteChangesReadWriter: ReadWriter[RemoteChanges] = macroRW
|
||||
|
||||
|
|
|
@ -129,6 +129,15 @@ object NodeParams {
|
|||
}
|
||||
|
||||
def makeNodeParams(config: Config, keyManager: KeyManager, torAddress_opt: Option[NodeAddress], database: Databases, blockCount: AtomicLong, feeEstimator: FeeEstimator): NodeParams = {
|
||||
// check configuration for keys that have been renamed in v0.3.2
|
||||
val deprecatedKeyPaths = Map(
|
||||
"default-feerates" -> "on-chain-fees.default-feerates",
|
||||
"max-feerate-mismatch" -> "on-chain-fees.max-feerate-mismatch",
|
||||
"update-fee_min-diff-ratio" -> "on-chain-fees.update-fee-min-diff-ratio"
|
||||
)
|
||||
deprecatedKeyPaths.foreach {
|
||||
case (old, new_) => require(!config.hasPath(old), s"configuration key '$old' has been replaced by '$new_'")
|
||||
}
|
||||
|
||||
val chain = config.getString("chain")
|
||||
val chainHash = makeChainHash(chain)
|
||||
|
|
|
@ -38,8 +38,8 @@ import scala.util.{Failure, Success, Try}
|
|||
|
||||
|
||||
/**
|
||||
* Created by PM on 20/08/2015.
|
||||
*/
|
||||
* Created by PM on 20/08/2015.
|
||||
*/
|
||||
|
||||
object Channel {
|
||||
def props(nodeParams: NodeParams, wallet: EclairWallet, remoteNodeId: PublicKey, blockchain: ActorRef, router: ActorRef, relayer: ActorRef, origin_opt: Option[ActorRef]) = Props(new Channel(nodeParams, wallet, remoteNodeId, blockchain, router, relayer, origin_opt))
|
||||
|
@ -1325,7 +1325,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId
|
|||
// for our outgoing payments, let's send events if we know that they will settle on chain
|
||||
Closing
|
||||
.onchainOutgoingHtlcs(d.commitments.localCommit, d.commitments.remoteCommit, d.commitments.remoteNextCommitInfo.left.toOption.map(_.nextRemoteCommit), tx)
|
||||
.map(add => (add, d.commitments.originChannels.get(add.id).collect { case Local(id, _) => id })) // we resolve the payment id if this was a local payment
|
||||
.map(add => (add, d.commitments.originChannels.get(add.id).collect { case Origin.Local(id, _) => id })) // we resolve the payment id if this was a local payment
|
||||
.collect { case (add, Some(id)) => context.system.eventStream.publish(PaymentSettlingOnChain(id, amount = add.amountMsat, add.paymentHash)) }
|
||||
// we update the channel data
|
||||
val d1 = d.copy(localCommitPublished = localCommitPublished1, remoteCommitPublished = remoteCommitPublished1, nextRemoteCommitPublished = nextRemoteCommitPublished1, futureRemoteCommitPublished = futureRemoteCommitPublished1, revokedCommitPublished = revokedCommitPublished1)
|
||||
|
@ -1721,8 +1721,8 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId
|
|||
*/
|
||||
|
||||
/**
|
||||
* This function is used to return feedback to user at channel opening
|
||||
*/
|
||||
* This function is used to return feedback to user at channel opening
|
||||
*/
|
||||
def replyToUser(message: Either[Channel.ChannelError, String]): Unit = {
|
||||
val m = message match {
|
||||
case Left(LocalError(t)) => Status.Failure(t)
|
||||
|
@ -1746,17 +1746,18 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId
|
|||
}
|
||||
|
||||
/**
|
||||
* This is used to check for the commitment fees when the channel is not operational but we have something at stake
|
||||
* @param c the new feerates
|
||||
* @param d the channel commtiments
|
||||
* @return
|
||||
*/
|
||||
* This is used to check for the commitment fees when the channel is not operational but we have something at stake
|
||||
*
|
||||
* @param c the new feerates
|
||||
* @param d the channel commtiments
|
||||
* @return
|
||||
*/
|
||||
def handleOfflineFeerate(c: CurrentFeerates, d: HasCommitments) = {
|
||||
val networkFeeratePerKw = c.feeratesPerKw.feePerBlock(target = nodeParams.onChainFeeConf.feeTargets.commitmentBlockTarget)
|
||||
val currentFeeratePerKw = d.commitments.localCommit.spec.feeratePerKw
|
||||
// if the fees are too high we risk to not be able to confirm our current commitment
|
||||
if(networkFeeratePerKw > currentFeeratePerKw && Helpers.isFeeDiffTooHigh(currentFeeratePerKw, networkFeeratePerKw, nodeParams.onChainFeeConf.maxFeerateMismatch)){
|
||||
if(nodeParams.onChainFeeConf.closeOnOfflineMismatch) {
|
||||
if (networkFeeratePerKw > currentFeeratePerKw && Helpers.isFeeDiffTooHigh(currentFeeratePerKw, networkFeeratePerKw, nodeParams.onChainFeeConf.maxFeerateMismatch)) {
|
||||
if (nodeParams.onChainFeeConf.closeOnOfflineMismatch) {
|
||||
log.warning(s"closing OFFLINE ${d.channelId} due to fee mismatch: currentFeeratePerKw=$currentFeeratePerKw networkFeeratePerKw=$networkFeeratePerKw")
|
||||
handleLocalError(FeerateTooDifferent(d.channelId, localFeeratePerKw = currentFeeratePerKw, remoteFeeratePerKw = networkFeeratePerKw), d, Some(c))
|
||||
} else {
|
||||
|
@ -1783,9 +1784,9 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId
|
|||
}
|
||||
|
||||
/**
|
||||
* When we are funder, we use this function to detect when our funding tx has been double-spent (by another transaction
|
||||
* that we made for some reason). If the funding tx has been double spent we can forget about the channel.
|
||||
*/
|
||||
* When we are funder, we use this function to detect when our funding tx has been double-spent (by another transaction
|
||||
* that we made for some reason). If the funding tx has been double spent we can forget about the channel.
|
||||
*/
|
||||
def checkDoubleSpent(fundingTx: Transaction): Unit = {
|
||||
log.debug(s"checking status of funding tx txid=${fundingTx.txid}")
|
||||
wallet.doubleSpent(fundingTx).onComplete {
|
||||
|
@ -1981,8 +1982,8 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId
|
|||
}
|
||||
|
||||
/**
|
||||
* This helper method will publish txes only if they haven't yet reached minDepth
|
||||
*/
|
||||
* This helper method will publish txes only if they haven't yet reached minDepth
|
||||
*/
|
||||
def publishIfNeeded(txes: Iterable[Transaction], irrevocablySpent: Map[OutPoint, ByteVector32]): Unit = {
|
||||
val (skip, process) = txes.partition(Closing.inputsAlreadySpent(_, irrevocablySpent))
|
||||
process.foreach { tx =>
|
||||
|
@ -1993,8 +1994,8 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId
|
|||
}
|
||||
|
||||
/**
|
||||
* This helper method will watch txes only if they haven't yet reached minDepth
|
||||
*/
|
||||
* This helper method will watch txes only if they haven't yet reached minDepth
|
||||
*/
|
||||
def watchConfirmedIfNeeded(txes: Iterable[Transaction], irrevocablySpent: Map[OutPoint, ByteVector32]): Unit = {
|
||||
val (skip, process) = txes.partition(Closing.inputsAlreadySpent(_, irrevocablySpent))
|
||||
process.foreach(tx => blockchain ! WatchConfirmed(self, tx, nodeParams.minDepthBlocks, BITCOIN_TX_CONFIRMED(tx)))
|
||||
|
@ -2002,8 +2003,8 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId
|
|||
}
|
||||
|
||||
/**
|
||||
* This helper method will watch txes only if the utxo they spend hasn't already been irrevocably spent
|
||||
*/
|
||||
* This helper method will watch txes only if the utxo they spend hasn't already been irrevocably spent
|
||||
*/
|
||||
def watchSpentIfNeeded(parentTx: Transaction, txes: Iterable[Transaction], irrevocablySpent: Map[OutPoint, ByteVector32]): Unit = {
|
||||
val (skip, process) = txes.partition(Closing.inputsAlreadySpent(_, irrevocablySpent))
|
||||
process.foreach(tx => blockchain ! WatchSpent(self, parentTx, tx.txIn.head.outPoint.index.toInt, BITCOIN_OUTPUT_SPENT))
|
||||
|
@ -2227,8 +2228,8 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId
|
|||
}
|
||||
|
||||
/**
|
||||
* This helper function runs the state's default event handlers, and react to exceptions by unilaterally closing the channel
|
||||
*/
|
||||
* This helper function runs the state's default event handlers, and react to exceptions by unilaterally closing the channel
|
||||
*/
|
||||
def handleExceptions(s: StateFunction): StateFunction = {
|
||||
case event if s.isDefinedAt(event) =>
|
||||
try {
|
||||
|
@ -2239,8 +2240,8 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId
|
|||
}
|
||||
|
||||
def origin(c: CMD_ADD_HTLC): Origin = c.upstream match {
|
||||
case Left(id) => Local(id, Some(sender)) // we were the origin of the payment
|
||||
case Right(u) => Relayed(u.channelId, u.id, u.amountMsat, c.amount) // this is a relayed payment
|
||||
case Upstream.Local(id) => Origin.Local(id, Some(sender)) // we were the origin of the payment
|
||||
case Upstream.Relayed(u) => Origin.Relayed(u.channelId, u.id, u.amountMsat, c.amount) // this is a relayed payment to an outgoing channel
|
||||
}
|
||||
|
||||
def feePaid(fee: Satoshi, tx: Transaction, desc: String, channelId: ByteVector32): Unit = {
|
||||
|
@ -2276,9 +2277,9 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId
|
|||
}
|
||||
|
||||
/**
|
||||
* This method allows performing actions during the transition, e.g. after a call to [[MyState.storing]]. This is
|
||||
* particularly useful to publish transactions only after we are sure that the state has been persisted.
|
||||
*/
|
||||
* This method allows performing actions during the transition, e.g. after a call to [[MyState.storing]]. This is
|
||||
* particularly useful to publish transactions only after we are sure that the state has been persisted.
|
||||
*/
|
||||
def calling(f: => Unit): FSM.State[fr.acinq.eclair.channel.State, Data] = {
|
||||
f
|
||||
state
|
||||
|
|
|
@ -27,7 +27,6 @@ import fr.acinq.eclair.wire.{AcceptChannel, ChannelAnnouncement, ChannelReestabl
|
|||
import fr.acinq.eclair.{CltvExpiry, CltvExpiryDelta, MilliSatoshi, ShortChannelId, UInt64}
|
||||
import scodec.bits.{BitVector, ByteVector}
|
||||
|
||||
|
||||
/**
|
||||
* Created by PM on 20/05/2016.
|
||||
*/
|
||||
|
@ -105,8 +104,16 @@ case class BITCOIN_PARENT_TX_CONFIRMED(childTx: Transaction) extends BitcoinEven
|
|||
"Y8888P" "Y88888P" 888 888 888 888 d88P 888 888 Y888 8888888P" "Y8888P"
|
||||
*/
|
||||
|
||||
sealed trait Upstream
|
||||
object Upstream {
|
||||
/** Our node is the origin of the payment. */
|
||||
final case class Local(id: UUID) extends Upstream
|
||||
/** Our node forwarded a single incoming HTLC to an outgoing channel. */
|
||||
final case class Relayed(add: UpdateAddHtlc) extends Upstream
|
||||
}
|
||||
|
||||
sealed trait Command
|
||||
final case class CMD_ADD_HTLC(amount: MilliSatoshi, paymentHash: ByteVector32, cltvExpiry: CltvExpiry, onion: OnionRoutingPacket, upstream: Either[UUID, UpdateAddHtlc], commit: Boolean = false, previousFailures: Seq[AddHtlcFailed] = Seq.empty) extends Command
|
||||
final case class CMD_ADD_HTLC(amount: MilliSatoshi, paymentHash: ByteVector32, cltvExpiry: CltvExpiry, onion: OnionRoutingPacket, upstream: Upstream, commit: Boolean = false, previousFailures: Seq[AddHtlcFailed] = Seq.empty) extends Command
|
||||
final case class CMD_FULFILL_HTLC(id: Long, r: ByteVector32, commit: Boolean = false) extends Command
|
||||
final case class CMD_FAIL_HTLC(id: Long, reason: Either[ByteVector, FailureMessage], commit: Boolean = false) extends Command
|
||||
final case class CMD_FAIL_MALFORMED_HTLC(id: Long, onionHash: ByteVector32, failureCode: Int, commit: Boolean = false) extends Command
|
||||
|
|
|
@ -188,7 +188,8 @@ object Commitments {
|
|||
}
|
||||
}
|
||||
|
||||
val htlcValueInFlight = outgoingHtlcs.map(_.add.amountMsat).sum
|
||||
// NB: we need the `toSeq` because otherwise duplicate amountMsat would be removed (since outgoingHtlcs is a Set).
|
||||
val htlcValueInFlight = outgoingHtlcs.toSeq.map(_.add.amountMsat).sum
|
||||
if (commitments1.remoteParams.maxHtlcValueInFlightMsat < htlcValueInFlight) {
|
||||
// TODO: this should be a specific UPDATE error
|
||||
return Left(HtlcValueTooHighInFlight(commitments.channelId, maximum = commitments1.remoteParams.maxHtlcValueInFlightMsat, actual = htlcValueInFlight))
|
||||
|
@ -229,7 +230,8 @@ object Commitments {
|
|||
}
|
||||
}
|
||||
|
||||
val htlcValueInFlight = incomingHtlcs.map(_.add.amountMsat).sum
|
||||
// NB: we need the `toSeq` because otherwise duplicate amountMsat would be removed (since incomingHtlcs is a Set).
|
||||
val htlcValueInFlight = incomingHtlcs.toSeq.map(_.add.amountMsat).sum
|
||||
if (commitments1.localParams.maxHtlcValueInFlightMsat < htlcValueInFlight) {
|
||||
throw HtlcValueTooHighInFlight(commitments.channelId, maximum = commitments1.localParams.maxHtlcValueInFlightMsat, actual = htlcValueInFlight)
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ class SqliteAuditDb(sqlite: Connection) extends AuditDb with Logging {
|
|||
}
|
||||
|
||||
def migration23(statement: Statement) = {
|
||||
statement.executeUpdate("CREATE TABLE IF NOT EXISTS channel_errors (channel_id BLOB NOT NULL, node_id BLOB NOT NULL, error_name STRING NOT NULL, error_message STRING NOT NULL, is_fatal INTEGER NOT NULL, timestamp INTEGER NOT NULL)")
|
||||
statement.executeUpdate("CREATE TABLE IF NOT EXISTS channel_errors (channel_id BLOB NOT NULL, node_id BLOB NOT NULL, error_name TEXT NOT NULL, error_message TEXT NOT NULL, is_fatal INTEGER NOT NULL, timestamp INTEGER NOT NULL)")
|
||||
statement.executeUpdate("CREATE INDEX IF NOT EXISTS channel_errors_timestamp_idx ON channel_errors(timestamp)")
|
||||
}
|
||||
|
||||
|
@ -66,8 +66,8 @@ class SqliteAuditDb(sqlite: Connection) extends AuditDb with Logging {
|
|||
statement.executeUpdate("CREATE TABLE IF NOT EXISTS received (amount_msat INTEGER NOT NULL, payment_hash BLOB NOT NULL, from_channel_id BLOB NOT NULL, timestamp INTEGER NOT NULL)")
|
||||
statement.executeUpdate("CREATE TABLE IF NOT EXISTS relayed (amount_in_msat INTEGER NOT NULL, amount_out_msat INTEGER NOT NULL, payment_hash BLOB NOT NULL, from_channel_id BLOB NOT NULL, to_channel_id BLOB NOT NULL, timestamp INTEGER NOT NULL)")
|
||||
statement.executeUpdate("CREATE TABLE IF NOT EXISTS network_fees (channel_id BLOB NOT NULL, node_id BLOB NOT NULL, tx_id BLOB NOT NULL, fee_sat INTEGER NOT NULL, tx_type TEXT NOT NULL, timestamp INTEGER NOT NULL)")
|
||||
statement.executeUpdate("CREATE TABLE IF NOT EXISTS channel_events (channel_id BLOB NOT NULL, node_id BLOB NOT NULL, capacity_sat INTEGER NOT NULL, is_funder BOOLEAN NOT NULL, is_private BOOLEAN NOT NULL, event STRING NOT NULL, timestamp INTEGER NOT NULL)")
|
||||
statement.executeUpdate("CREATE TABLE IF NOT EXISTS channel_errors (channel_id BLOB NOT NULL, node_id BLOB NOT NULL, error_name STRING NOT NULL, error_message STRING NOT NULL, is_fatal INTEGER NOT NULL, timestamp INTEGER NOT NULL)")
|
||||
statement.executeUpdate("CREATE TABLE IF NOT EXISTS channel_events (channel_id BLOB NOT NULL, node_id BLOB NOT NULL, capacity_sat INTEGER NOT NULL, is_funder BOOLEAN NOT NULL, is_private BOOLEAN NOT NULL, event TEXT NOT NULL, timestamp INTEGER NOT NULL)")
|
||||
statement.executeUpdate("CREATE TABLE IF NOT EXISTS channel_errors (channel_id BLOB NOT NULL, node_id BLOB NOT NULL, error_name TEXT NOT NULL, error_message TEXT NOT NULL, is_fatal INTEGER NOT NULL, timestamp INTEGER NOT NULL)")
|
||||
|
||||
statement.executeUpdate("CREATE INDEX IF NOT EXISTS balance_updated_idx ON balance_updated(timestamp)")
|
||||
statement.executeUpdate("CREATE INDEX IF NOT EXISTS sent_timestamp_idx ON sent(timestamp)")
|
||||
|
|
|
@ -32,7 +32,6 @@ import scodec.bits.ByteVector
|
|||
import scala.collection.immutable.SortedMap
|
||||
|
||||
class SqliteNetworkDb(sqlite: Connection, chainHash: ByteVector32) extends NetworkDb with Logging {
|
||||
|
||||
import SqliteUtils._
|
||||
import SqliteUtils.ExtendedResultSet._
|
||||
|
||||
|
|
|
@ -534,17 +534,17 @@ class Peer(val nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: A
|
|||
|
||||
onTransition {
|
||||
case _ -> CONNECTED =>
|
||||
//Metrics.connectedPeers.increment()
|
||||
Metrics.connectedPeers.increment()
|
||||
context.system.eventStream.publish(PeerConnected(self, remoteNodeId))
|
||||
case CONNECTED -> DISCONNECTED =>
|
||||
//Metrics.connectedPeers.decrement()
|
||||
Metrics.connectedPeers.decrement()
|
||||
context.system.eventStream.publish(PeerDisconnected(self, remoteNodeId))
|
||||
}
|
||||
|
||||
onTermination {
|
||||
case StopEvent(_, CONNECTED, d: ConnectedData) =>
|
||||
// the transition handler won't be fired if we go directly from CONNECTED to closed
|
||||
//Metrics.connectedPeers.decrement()
|
||||
Metrics.connectedPeers.decrement()
|
||||
context.system.eventStream.publish(PeerDisconnected(self, remoteNodeId))
|
||||
}
|
||||
|
||||
|
@ -654,9 +654,9 @@ object Peer {
|
|||
// @formatter:on
|
||||
|
||||
object Metrics {
|
||||
// val peers = Kamon.rangeSampler("peers.count").withoutTags()
|
||||
// val connectedPeers = Kamon.rangeSampler("peers.connected.count").withoutTags()
|
||||
// val channels = Kamon.rangeSampler("channels.count").withoutTags()
|
||||
val peers = Kamon.rangeSampler("peers.count").withoutTags()
|
||||
val connectedPeers = Kamon.rangeSampler("peers.connected.count").withoutTags()
|
||||
val channels = Kamon.rangeSampler("channels.count").withoutTags()
|
||||
}
|
||||
|
||||
def makeChannelParams(nodeParams: NodeParams, defaultFinalScriptPubKey: ByteVector, isFunder: Boolean, fundingAmount: Satoshi): LocalParams = {
|
||||
|
|
|
@ -27,18 +27,17 @@ import fr.acinq.eclair.channel.Helpers.Closing
|
|||
import fr.acinq.eclair.channel.{HasCommitments, _}
|
||||
import fr.acinq.eclair.db.PendingRelayDb
|
||||
import fr.acinq.eclair.payment.Relayer.RelayPayload
|
||||
import fr.acinq.eclair.payment.{Relayed, Relayer}
|
||||
import fr.acinq.eclair.payment.{Origin, Relayer}
|
||||
import fr.acinq.eclair.router.Rebroadcast
|
||||
import fr.acinq.eclair.transactions.{IN, OUT}
|
||||
import fr.acinq.eclair.wire.{TemporaryNodeFailure, UpdateAddHtlc}
|
||||
import grizzled.slf4j.Logging
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import scala.util.Success
|
||||
|
||||
/**
|
||||
* Ties network connections to peers.
|
||||
* Created by PM on 14/02/2017.
|
||||
*/
|
||||
* Ties network connections to peers.
|
||||
* Created by PM on 14/02/2017.
|
||||
*/
|
||||
class Switchboard(nodeParams: NodeParams, authenticator: ActorRef, watcher: ActorRef, router: ActorRef, relayer: ActorRef, wallet: EclairWallet) extends Actor with ActorLogging {
|
||||
|
||||
import Switchboard._
|
||||
|
@ -93,7 +92,7 @@ class Switchboard(nodeParams: NodeParams, authenticator: ActorRef, watcher: Acto
|
|||
case d: Peer.Disconnect =>
|
||||
getPeer(d.nodeId) match {
|
||||
case Some(peer) => peer forward d
|
||||
case None => sender ! Status.Failure(new RuntimeException("peer not found"))
|
||||
case None => sender ! Status.Failure(new RuntimeException("peer not found"))
|
||||
}
|
||||
|
||||
case o: Peer.OpenChannel =>
|
||||
|
@ -112,25 +111,18 @@ class Switchboard(nodeParams: NodeParams, authenticator: ActorRef, watcher: Acto
|
|||
}
|
||||
|
||||
/**
|
||||
* Retrieves a peer based on its public key.
|
||||
*
|
||||
* NB: Internally akka uses a TreeMap to store the binding, so this lookup is O(log(N)) where N is the number of
|
||||
* peers. We could make it O(1) by using our own HashMap, but it creates other problems when we need to remove an
|
||||
* existing peer. This seems like a reasonable trade-off because we only make this call once per connection, and N
|
||||
* should never be very big anyway.
|
||||
*
|
||||
* @param remoteNodeId
|
||||
* @return
|
||||
*/
|
||||
* Retrieves a peer based on its public key.
|
||||
*
|
||||
* NB: Internally akka uses a TreeMap to store the binding, so this lookup is O(log(N)) where N is the number of
|
||||
* peers. We could make it O(1) by using our own HashMap, but it creates other problems when we need to remove an
|
||||
* existing peer. This seems like a reasonable trade-off because we only make this call once per connection, and N
|
||||
* should never be very big anyway.
|
||||
*/
|
||||
def getPeer(remoteNodeId: PublicKey): Option[ActorRef] = context.child(peerActorName(remoteNodeId))
|
||||
|
||||
/**
|
||||
*
|
||||
* @param remoteNodeId
|
||||
* @param previousKnownAddress only to be set if we know for sure that this ip worked in the past
|
||||
* @param offlineChannels
|
||||
* @return
|
||||
*/
|
||||
* @param previousKnownAddress only to be set if we know for sure that this ip worked in the past
|
||||
*/
|
||||
def createOrGetPeer(remoteNodeId: PublicKey, previousKnownAddress: Option[InetSocketAddress], offlineChannels: Set[HasCommitments]) = {
|
||||
getPeer(remoteNodeId) match {
|
||||
case Some(peer) => peer
|
||||
|
@ -155,14 +147,14 @@ object Switchboard extends Logging {
|
|||
def peerActorName(remoteNodeId: PublicKey): String = s"peer-$remoteNodeId"
|
||||
|
||||
/**
|
||||
* If we have stopped eclair while it was forwarding HTLCs, it is possible that we are in a state were an incoming HTLC
|
||||
* was committed by both sides, but we didn't have time to send and/or sign the corresponding HTLC to the downstream node.
|
||||
*
|
||||
* In that case, if we do nothing, the incoming HTLC will eventually expire and we won't lose money, but the channel will
|
||||
* get closed, which is a major inconvenience.
|
||||
*
|
||||
* This check will detect this and will allow us to fast-fail HTLCs and thus preserve channels.
|
||||
*/
|
||||
* If we have stopped eclair while it was forwarding HTLCs, it is possible that we are in a state were an incoming HTLC
|
||||
* was committed by both sides, but we didn't have time to send and/or sign the corresponding HTLC to the downstream node.
|
||||
*
|
||||
* In that case, if we do nothing, the incoming HTLC will eventually expire and we won't lose money, but the channel will
|
||||
* get closed, which is a major inconvenience.
|
||||
*
|
||||
* This check will detect this and will allow us to fast-fail HTLCs and thus preserve channels.
|
||||
*/
|
||||
def checkBrokenHtlcsLink(channels: Seq[HasCommitments], privateKey: PrivateKey, features: ByteVector): Seq[UpdateAddHtlc] = {
|
||||
|
||||
// We are interested in incoming HTLCs, that have been *cross-signed* (otherwise they wouldn't have been relayed).
|
||||
|
@ -178,7 +170,7 @@ object Switchboard extends Logging {
|
|||
// Here we do it differently because we need the origin information.
|
||||
val relayed_out = channels
|
||||
.flatMap(_.commitments.originChannels.values)
|
||||
.collect { case r: Relayed => r }
|
||||
.collect { case r: Origin.Relayed => r }
|
||||
.toSet
|
||||
|
||||
val htlcs_broken = htlcs_in.filterNot(htlc_in => relayed_out.exists(r => r.originChannelId == htlc_in.channelId && r.originHtlcId == htlc_in.id))
|
||||
|
@ -189,17 +181,17 @@ object Switchboard extends Logging {
|
|||
}
|
||||
|
||||
/**
|
||||
* We store [[CMD_FULFILL_HTLC]]/[[CMD_FAIL_HTLC]]/[[CMD_FAIL_MALFORMED_HTLC]]
|
||||
* in a database (see [[fr.acinq.eclair.payment.CommandBuffer]]) because we
|
||||
* don't want to lose preimages, or to forget to fail incoming htlcs, which
|
||||
* would lead to unwanted channel closings.
|
||||
*
|
||||
* Because of the way our watcher works, in a scenario where a downstream
|
||||
* channel has gone to the blockchain, it may send several times the same
|
||||
* command, and the upstream channel may have disappeared in the meantime.
|
||||
*
|
||||
* That's why we need to periodically clean up the pending relay db.
|
||||
*/
|
||||
* We store [[CMD_FULFILL_HTLC]]/[[CMD_FAIL_HTLC]]/[[CMD_FAIL_MALFORMED_HTLC]]
|
||||
* in a database (see [[fr.acinq.eclair.payment.CommandBuffer]]) because we
|
||||
* don't want to lose preimages, or to forget to fail incoming htlcs, which
|
||||
* would lead to unwanted channel closings.
|
||||
*
|
||||
* Because of the way our watcher works, in a scenario where a downstream
|
||||
* channel has gone to the blockchain, it may send several times the same
|
||||
* command, and the upstream channel may have disappeared in the meantime.
|
||||
*
|
||||
* That's why we need to periodically clean up the pending relay db.
|
||||
*/
|
||||
def cleanupRelayDb(channels: Seq[HasCommitments], relayDb: PendingRelayDb): Int = {
|
||||
|
||||
// We are interested in incoming HTLCs, that have been *cross-signed* (otherwise they wouldn't have been relayed).
|
||||
|
@ -239,14 +231,14 @@ class HtlcReaper extends Actor with ActorLogging {
|
|||
val acked = htlcs
|
||||
.filter(_.channelId == data.channelId) // only consider htlcs related to this channel
|
||||
.filter {
|
||||
case htlc if Commitments.getHtlcCrossSigned(data.commitments, IN, htlc.id).isDefined =>
|
||||
// this htlc is cross signed in the current commitment, we can fail it
|
||||
log.info(s"failing broken htlc=$htlc")
|
||||
channel ! CMD_FAIL_HTLC(htlc.id, Right(TemporaryNodeFailure), commit = true)
|
||||
false // the channel may very well be disconnected before we sign (=ack) the fail, so we keep it for now
|
||||
case _ =>
|
||||
true // the htlc has already been failed, we can forget about it now
|
||||
}
|
||||
case htlc if Commitments.getHtlcCrossSigned(data.commitments, IN, htlc.id).isDefined =>
|
||||
// this htlc is cross signed in the current commitment, we can fail it
|
||||
log.info(s"failing broken htlc=$htlc")
|
||||
channel ! CMD_FAIL_HTLC(htlc.id, Right(TemporaryNodeFailure), commit = true)
|
||||
false // the channel may very well be disconnected before we sign (=ack) the fail, so we keep it for now
|
||||
case _ =>
|
||||
true // the htlc has already been failed, we can forget about it now
|
||||
}
|
||||
acked.foreach(htlc => log.info(s"forgetting htlc id=${htlc.id} channelId=${htlc.channelId}"))
|
||||
context become main(htlcs diff acked)
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ import fr.acinq.bitcoin.ByteVector32
|
|||
import fr.acinq.bitcoin.Crypto.PublicKey
|
||||
import fr.acinq.eclair._
|
||||
import fr.acinq.eclair.blockchain.WatchEventSpentBasic
|
||||
import fr.acinq.eclair.channel.{BITCOIN_FUNDING_EXTERNAL_CHANNEL_SPENT, CMD_ADD_HTLC, Register}
|
||||
import fr.acinq.eclair.channel.{BITCOIN_FUNDING_EXTERNAL_CHANNEL_SPENT, CMD_ADD_HTLC, Register, Upstream}
|
||||
import fr.acinq.eclair.crypto.{Sphinx, TransportHandler}
|
||||
import fr.acinq.eclair.db.{OutgoingPayment, OutgoingPaymentStatus, PaymentsDb}
|
||||
import fr.acinq.eclair.payment.PaymentInitiator.SendPaymentRequest
|
||||
|
@ -140,8 +140,17 @@ class PaymentLifecycle(nodeParams: NodeParams, progressHandler: PaymentProgressH
|
|||
}
|
||||
// in any case, we forward the update to the router
|
||||
router ! failureMessage.update
|
||||
// we also update assisted routes, because they take precedence over the router's routing table
|
||||
val assistedRoutes1 = c.assistedRoutes.map(_.map {
|
||||
case extraHop: ExtraHop if extraHop.shortChannelId == failureMessage.update.shortChannelId => extraHop.copy(
|
||||
cltvExpiryDelta = failureMessage.update.cltvExpiryDelta,
|
||||
feeBase = failureMessage.update.feeBaseMsat,
|
||||
feeProportionalMillionths = failureMessage.update.feeProportionalMillionths
|
||||
)
|
||||
case extraHop => extraHop
|
||||
})
|
||||
// let's try again, router will have updated its state
|
||||
router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.finalPayload.amount, c.assistedRoutes, ignoreNodes, ignoreChannels, c.routeParams)
|
||||
router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.finalPayload.amount, assistedRoutes1, ignoreNodes, ignoreChannels, c.routeParams)
|
||||
} else {
|
||||
// this node is fishy, it gave us a bad sig!! let's filter it out
|
||||
log.warning(s"got bad signature from node=$nodeId update=${failureMessage.update}")
|
||||
|
@ -300,7 +309,7 @@ object PaymentLifecycle {
|
|||
val nodes = hops.map(_.nextNodeId)
|
||||
// BOLT 2 requires that associatedData == paymentHash
|
||||
val onion = buildOnion(nodes, payloads, paymentHash)
|
||||
CMD_ADD_HTLC(firstAmount, paymentHash, firstExpiry, onion.packet, upstream = Left(id), commit = true) -> onion.sharedSecrets
|
||||
CMD_ADD_HTLC(firstAmount, paymentHash, firstExpiry, onion.packet, Upstream.Local(id), commit = true) -> onion.sharedSecrets
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -24,6 +24,7 @@ import fr.acinq.bitcoin.ByteVector32
|
|||
import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey}
|
||||
import fr.acinq.eclair.channel._
|
||||
import fr.acinq.eclair.crypto.Sphinx
|
||||
import fr.acinq.eclair.payment.Origin.{Relayed, Local}
|
||||
import fr.acinq.eclair.router.Announcements
|
||||
import fr.acinq.eclair.wire._
|
||||
import fr.acinq.eclair.{CltvExpiryDelta, Features, LongToBtcAmount, MilliSatoshi, NodeParams, ShortChannelId, UInt64, nodeFee}
|
||||
|
@ -35,8 +36,12 @@ import scala.collection.mutable
|
|||
|
||||
// @formatter:off
|
||||
sealed trait Origin
|
||||
case class Local(id: UUID, sender: Option[ActorRef]) extends Origin // we don't persist reference to local actors
|
||||
case class Relayed(originChannelId: ByteVector32, originHtlcId: Long, amountIn: MilliSatoshi, amountOut: MilliSatoshi) extends Origin
|
||||
object Origin {
|
||||
/** Our node is the origin of the payment. */
|
||||
case class Local(id: UUID, sender: Option[ActorRef]) extends Origin // we don't persist reference to local actors
|
||||
/** Our node forwarded a single incoming HTLC to an outgoing channel. */
|
||||
case class Relayed(originChannelId: ByteVector32, originHtlcId: Long, amountIn: MilliSatoshi, amountOut: MilliSatoshi) extends Origin
|
||||
}
|
||||
|
||||
sealed trait ForwardMessage
|
||||
case class ForwardAdd(add: UpdateAddHtlc, previousFailures: Seq[AddHtlcFailed] = Seq.empty) extends ForwardMessage
|
||||
|
@ -125,7 +130,7 @@ class Relayer(nodeParams: NodeParams, register: ActorRef, paymentHandler: ActorR
|
|||
commandBuffer ! CommandBuffer.CommandSend(add.channelId, add.id, cmdFail)
|
||||
}
|
||||
|
||||
case Status.Failure(Register.ForwardShortIdFailure(Register.ForwardShortId(shortChannelId, CMD_ADD_HTLC(_, _, _, _, Right(add), _, _)))) =>
|
||||
case Status.Failure(Register.ForwardShortIdFailure(Register.ForwardShortId(shortChannelId, CMD_ADD_HTLC(_, _, _, _, Upstream.Relayed(add), _, _)))) =>
|
||||
log.warning(s"couldn't resolve downstream channel $shortChannelId, failing htlc #${add.id}")
|
||||
val cmdFail = CMD_FAIL_HTLC(add.id, Right(UnknownNextPeer), commit = true)
|
||||
commandBuffer ! CommandBuffer.CommandSend(add.channelId, add.id, cmdFail)
|
||||
|
@ -143,11 +148,10 @@ class Relayer(nodeParams: NodeParams, register: ActorRef, paymentHandler: ActorR
|
|||
sender ! Status.Failure(addFailed)
|
||||
case Relayed(originChannelId, originHtlcId, _, _) =>
|
||||
addFailed.originalCommand match {
|
||||
case Some(cmd) =>
|
||||
case Some(CMD_ADD_HTLC(_, _, _, _, Upstream.Relayed(add), _, previousFailures)) =>
|
||||
log.info(s"retrying htlc #$originHtlcId paymentHash=$paymentHash from channelId=$originChannelId")
|
||||
// NB: cmd.upstream.right is defined since this is a relayed payment
|
||||
self ! ForwardAdd(cmd.upstream.right.get, cmd.previousFailures :+ addFailed)
|
||||
case None =>
|
||||
self ! ForwardAdd(add, previousFailures :+ addFailed)
|
||||
case _ =>
|
||||
val failure = translateError(addFailed)
|
||||
val cmdFail = CMD_FAIL_HTLC(originHtlcId, Right(failure), commit = true)
|
||||
log.info(s"rejecting htlc #$originHtlcId paymentHash=$paymentHash from channelId=$originChannelId reason=${cmdFail.reason}")
|
||||
|
@ -377,7 +381,7 @@ object Relayer extends Logging {
|
|||
case Some(channelUpdate) if relayPayload.relayFeeMsat < nodeFee(channelUpdate.feeBaseMsat, channelUpdate.feeProportionalMillionths, payload.amountToForward) =>
|
||||
RelayFailure(CMD_FAIL_HTLC(add.id, Right(FeeInsufficient(add.amountMsat, channelUpdate)), commit = true))
|
||||
case Some(channelUpdate) =>
|
||||
RelaySuccess(channelUpdate.shortChannelId, CMD_ADD_HTLC(payload.amountToForward, add.paymentHash, payload.outgoingCltv, nextPacket, upstream = Right(add), commit = true, previousFailures = previousFailures))
|
||||
RelaySuccess(channelUpdate.shortChannelId, CMD_ADD_HTLC(payload.amountToForward, add.paymentHash, payload.outgoingCltv, nextPacket, Upstream.Relayed(add), commit = true, previousFailures = previousFailures))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -250,6 +250,9 @@ class Router(val nodeParams: NodeParams, watcher: ActorRef, initialized: Option[
|
|||
case Event(GetRoutingState, d: Data) =>
|
||||
stay // ignored on Android
|
||||
|
||||
case Event(GetNetworkStats, d: Data) =>
|
||||
stay // ignored on Android
|
||||
|
||||
case Event(WatchEventSpentBasic(BITCOIN_FUNDING_EXTERNAL_CHANNEL_SPENT(shortChannelId)), d) if d.channels.contains(shortChannelId) =>
|
||||
val lostChannel = d.channels(shortChannelId).ann
|
||||
log.info("funding tx of channelId={} has been spent", shortChannelId)
|
||||
|
@ -281,6 +284,10 @@ class Router(val nodeParams: NodeParams, watcher: ActorRef, initialized: Option[
|
|||
log.info("re-computing network statistics")
|
||||
stay using d.copy(stats = NetworkStats(d.channels.values.toSeq))
|
||||
|
||||
case Event(TickComputeNetworkStats, d) if d.channels.nonEmpty =>
|
||||
log.info("re-computing network statistics")
|
||||
stay using d.copy(stats = NetworkStats(d.channels.values.toSeq))
|
||||
|
||||
case Event(TickPruneStaleChannels, d) =>
|
||||
// first we select channels that we will prune
|
||||
val staleChannels = getStaleChannels(d.channels.values, nodeParams.currentBlockHeight)
|
||||
|
@ -451,6 +458,7 @@ class Router(val nodeParams: NodeParams, watcher: ActorRef, initialized: Option[
|
|||
|
||||
case Event(PeerRoutingMessage(transport, remoteNodeId, routingMessage@ReplyChannelRange(chainHash, _, _, _, shortChannelIds, _)), d) =>
|
||||
sender ! TransportHandler.ReadAck(routingMessage)
|
||||
|
||||
Kamon.runWithContextEntry(remoteNodeIdKey, remoteNodeId.toString) {
|
||||
Kamon.runWithSpan(Kamon.spanBuilder("reply-channel-range").start(), finishSpan = true) {
|
||||
|
||||
|
@ -469,7 +477,7 @@ class Router(val nodeParams: NodeParams, watcher: ActorRef, initialized: Option[
|
|||
val timestamps_opt = routingMessage.timestamps_opt.map(_.timestamps).getOrElse(List.empty[ReplyChannelRangeTlv.Timestamps])
|
||||
val checksums_opt = routingMessage.checksums_opt.map(_.checksums).getOrElse(List.empty[ReplyChannelRangeTlv.Checksums])
|
||||
|
||||
val shortChannelIdAndFlags = {
|
||||
val shortChannelIdAndFlags = Kamon.runWithSpan(Kamon.spanBuilder("compute-flags").start(), finishSpan = true) {
|
||||
loop(shortChannelIds.array, timestamps_opt, checksums_opt)
|
||||
}
|
||||
|
||||
|
|
|
@ -20,8 +20,8 @@ import fr.acinq.eclair.MilliSatoshi
|
|||
import fr.acinq.eclair.wire._
|
||||
|
||||
/**
|
||||
* Created by PM on 07/12/2016.
|
||||
*/
|
||||
* Created by PM on 07/12/2016.
|
||||
*/
|
||||
|
||||
// @formatter:off
|
||||
sealed trait Direction { def opposite: Direction }
|
||||
|
@ -36,10 +36,10 @@ final case class CommitmentSpec(htlcs: Set[DirectedHtlc], feeratePerKw: Long, to
|
|||
}
|
||||
|
||||
object CommitmentSpec {
|
||||
def removeHtlc(changes: List[UpdateMessage], id: Long): List[UpdateMessage] = changes.filterNot(_ match {
|
||||
case u: UpdateAddHtlc if u.id == id => true
|
||||
def removeHtlc(changes: List[UpdateMessage], id: Long): List[UpdateMessage] = changes.filterNot {
|
||||
case u: UpdateAddHtlc => u.id == id
|
||||
case _ => false
|
||||
})
|
||||
}
|
||||
|
||||
def addHtlc(spec: CommitmentSpec, direction: Direction, update: UpdateAddHtlc): CommitmentSpec = {
|
||||
val htlc = DirectedHtlc(direction, update)
|
||||
|
@ -54,7 +54,7 @@ object CommitmentSpec {
|
|||
spec.htlcs.find(htlc => htlc.direction != direction && htlc.add.id == htlcId) match {
|
||||
case Some(htlc) if direction == OUT => spec.copy(toLocal = spec.toLocal + htlc.add.amountMsat, htlcs = spec.htlcs - htlc)
|
||||
case Some(htlc) if direction == IN => spec.copy(toRemote = spec.toRemote + htlc.add.amountMsat, htlcs = spec.htlcs - htlc)
|
||||
case None => throw new RuntimeException(s"cannot find htlc id=${htlcId}")
|
||||
case None => throw new RuntimeException(s"cannot find htlc id=$htlcId")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,7 @@ object CommitmentSpec {
|
|||
spec.htlcs.find(htlc => htlc.direction != direction && htlc.add.id == htlcId) match {
|
||||
case Some(htlc) if direction == OUT => spec.copy(toRemote = spec.toRemote + htlc.add.amountMsat, htlcs = spec.htlcs - htlc)
|
||||
case Some(htlc) if direction == IN => spec.copy(toLocal = spec.toLocal + htlc.add.amountMsat, htlcs = spec.htlcs - htlc)
|
||||
case None => throw new RuntimeException(s"cannot find htlc id=${htlcId}")
|
||||
case None => throw new RuntimeException(s"cannot find htlc id=$htlcId")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,8 @@ import fr.acinq.bitcoin.DeterministicWallet.{ExtendedPrivateKey, KeyPath}
|
|||
import fr.acinq.bitcoin.{ByteVector32, ByteVector64, Crypto, OutPoint, Transaction, TxOut}
|
||||
import fr.acinq.eclair.channel._
|
||||
import fr.acinq.eclair.crypto.ShaChain
|
||||
import fr.acinq.eclair.payment.{Local, Origin, Relayed}
|
||||
import fr.acinq.eclair.payment.Origin
|
||||
import fr.acinq.eclair.payment.Origin.{Relayed, Local}
|
||||
import fr.acinq.eclair.transactions.Transactions._
|
||||
import fr.acinq.eclair.transactions._
|
||||
import fr.acinq.eclair.wire.CommonCodecs._
|
||||
|
@ -37,8 +38,8 @@ import scala.compat.Platform
|
|||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* Created by PM on 02/06/2017.
|
||||
*/
|
||||
* Created by PM on 02/06/2017.
|
||||
*/
|
||||
object ChannelCodecs extends Logging {
|
||||
|
||||
val keyPathCodec: Codec[KeyPath] = ("path" | listOfN(uint16, uint32)).xmap[KeyPath](l => new KeyPath(l), keyPath => keyPath.path.toList).as[KeyPath]
|
||||
|
@ -53,8 +54,8 @@ object ChannelCodecs extends Logging {
|
|||
val channelVersionCodec: Codec[ChannelVersion] = discriminatorWithDefault[ChannelVersion](
|
||||
discriminator = discriminated[ChannelVersion].by(byte)
|
||||
.typecase(0x01, bits(ChannelVersion.LENGTH_BITS).as[ChannelVersion])
|
||||
// NB: 0x02 and 0x03 are *reserved* for backward compatibility reasons
|
||||
,
|
||||
// NB: 0x02 and 0x03 are *reserved* for backward compatibility reasons
|
||||
,
|
||||
fallback = provide(ChannelVersion.ZEROES) // README: DO NOT CHANGE THIS !! old channels don't have a channel version
|
||||
// field and don't support additional features which is why all bits are set to 0.
|
||||
)
|
||||
|
@ -213,7 +214,7 @@ object ChannelCodecs extends Logging {
|
|||
)
|
||||
|
||||
val commitmentsCodec: Codec[Commitments] = (
|
||||
("channelVersion" | channelVersionCodec) ::
|
||||
("channelVersion" | channelVersionCodec) ::
|
||||
("localParams" | localParamsCodec) ::
|
||||
("remoteParams" | remoteParamsCodec) ::
|
||||
("channelFlags" | byte) ::
|
||||
|
@ -324,7 +325,7 @@ object ChannelCodecs extends Logging {
|
|||
|
||||
// this is a decode-only codec compatible with versions 818199e and below, with placeholders for new fields
|
||||
val DATA_CLOSING_COMPAT_06_Codec: Codec[DATA_CLOSING] = (
|
||||
("commitments" | commitmentsCodec) ::
|
||||
("commitments" | commitmentsCodec) ::
|
||||
("fundingTx" | provide[Option[Transaction]](None)) ::
|
||||
("waitingSince" | provide(Platform.currentTime.milliseconds.toSeconds)) ::
|
||||
("mutualCloseProposed" | listOfN(uint16, txCodec)) ::
|
||||
|
@ -336,7 +337,7 @@ object ChannelCodecs extends Logging {
|
|||
("revokedCommitPublished" | listOfN(uint16, revokedCommitPublishedCodec))).as[DATA_CLOSING].decodeOnly
|
||||
|
||||
val DATA_CLOSING_Codec: Codec[DATA_CLOSING] = (
|
||||
("commitments" | commitmentsCodec) ::
|
||||
("commitments" | commitmentsCodec) ::
|
||||
("fundingTx" | optional(bool, txCodec)) ::
|
||||
("waitingSince" | int64) ::
|
||||
("mutualCloseProposed" | listOfN(uint16, txCodec)) ::
|
||||
|
@ -353,16 +354,16 @@ object ChannelCodecs extends Logging {
|
|||
|
||||
|
||||
/**
|
||||
* Order matters!!
|
||||
*
|
||||
* We use the fact that the discriminated codec encodes using the first suitable codec it finds in the list to handle
|
||||
* database migration.
|
||||
*
|
||||
* For example, a data encoded with type 01 will be decoded using [[DATA_WAIT_FOR_FUNDING_CONFIRMED_COMPAT_01_Codec]] and
|
||||
* encoded to a type 08 using [[DATA_WAIT_FOR_FUNDING_CONFIRMED_Codec]].
|
||||
*
|
||||
* More info here: https://github.com/scodec/scodec/issues/122
|
||||
*/
|
||||
* Order matters!!
|
||||
*
|
||||
* We use the fact that the discriminated codec encodes using the first suitable codec it finds in the list to handle
|
||||
* database migration.
|
||||
*
|
||||
* For example, a data encoded with type 01 will be decoded using [[DATA_WAIT_FOR_FUNDING_CONFIRMED_COMPAT_01_Codec]] and
|
||||
* encoded to a type 08 using [[DATA_WAIT_FOR_FUNDING_CONFIRMED_Codec]].
|
||||
*
|
||||
* More info here: https://github.com/scodec/scodec/issues/122
|
||||
*/
|
||||
val stateDataCodec: Codec[HasCommitments] = ("version" | constant(0x00)) ~> discriminated[HasCommitments].by(uint16)
|
||||
.typecase(0x10, DATA_NORMAL_Codec)
|
||||
.typecase(0x09, DATA_CLOSING_Codec)
|
||||
|
|
|
@ -20,6 +20,8 @@ object Kamon {
|
|||
|
||||
def increment() = this
|
||||
|
||||
def decrement() = this
|
||||
|
||||
def record(a: Long) = this
|
||||
}
|
||||
|
||||
|
@ -31,6 +33,8 @@ object Kamon {
|
|||
|
||||
def histogram(name: String) = Mock
|
||||
|
||||
def rangeSampler(name: String) = Mock
|
||||
|
||||
def runWithContextEntry[T, K](key: Context.Key[K], value: K)(f: => T): T = f
|
||||
|
||||
def runWithSpan[T](span: Any, finishSpan: Boolean)(f: => T): T = f
|
||||
|
|
|
@ -18,15 +18,28 @@ package fr.acinq.eclair
|
|||
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import com.typesafe.config.{ConfigFactory, ConfigValue}
|
||||
import fr.acinq.bitcoin.Block
|
||||
import fr.acinq.eclair.crypto.LocalKeyManager
|
||||
import org.scalatest.FunSuite
|
||||
|
||||
import scala.collection.JavaConversions._
|
||||
import scala.util.Try
|
||||
|
||||
class StartupSpec extends FunSuite {
|
||||
|
||||
test("check configuration") {
|
||||
val blockCount = new AtomicLong(0)
|
||||
val keyManager = new LocalKeyManager(seed = randomBytes32, chainHash = Block.TestnetGenesisBlock.hash)
|
||||
val conf = ConfigFactory.load().getConfig("eclair")
|
||||
assert(Try(NodeParams.makeNodeParams(conf, keyManager, None, TestConstants.inMemoryDb(), blockCount, new TestConstants.TestFeeEstimator)).isSuccess)
|
||||
|
||||
val conf1 = conf.withFallback(ConfigFactory.parseMap(Map("max-feerate-mismatch" -> 42)))
|
||||
intercept[RuntimeException] {
|
||||
NodeParams.makeNodeParams(conf1, keyManager, None, TestConstants.inMemoryDb(), blockCount, new TestConstants.TestFeeEstimator)
|
||||
}
|
||||
}
|
||||
|
||||
test("NodeParams should fail if the alias is illegal (over 32 bytes)") {
|
||||
|
||||
val threeBytesUTFChar = '\u20AC' // €
|
||||
|
|
|
@ -20,7 +20,7 @@ import java.util.UUID
|
|||
|
||||
import fr.acinq.eclair.channel.Commitments._
|
||||
import fr.acinq.eclair.channel.states.StateTestsHelperMethods
|
||||
import fr.acinq.eclair.payment.Local
|
||||
import fr.acinq.eclair.payment.Origin.Local
|
||||
import fr.acinq.eclair.wire.IncorrectOrUnknownPaymentDetails
|
||||
import fr.acinq.eclair.{TestkitBaseClass, _}
|
||||
import org.scalatest.Outcome
|
||||
|
|
|
@ -69,7 +69,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
|
||||
val sender = TestProbe()
|
||||
val h = randomBytes32
|
||||
val add = CMD_ADD_HTLC(50000000 msat, h, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))
|
||||
val add = CMD_ADD_HTLC(50000000 msat, h, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
|
||||
sender.send(alice, add)
|
||||
sender.expectMsg("ok")
|
||||
val htlc = alice2bob.expectMsgType[UpdateAddHtlc]
|
||||
|
@ -78,7 +78,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
commitments = initialState.commitments.copy(
|
||||
localNextHtlcId = 1,
|
||||
localChanges = initialState.commitments.localChanges.copy(proposed = htlc :: Nil),
|
||||
originChannels = Map(0L -> Local(add.upstream.left.get, Some(sender.ref)))
|
||||
originChannels = Map(0L -> Origin.Local(add.upstream.asInstanceOf[Upstream.Local].id, Some(sender.ref)))
|
||||
)))
|
||||
}
|
||||
|
||||
|
@ -87,7 +87,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
val sender = TestProbe()
|
||||
val h = randomBytes32
|
||||
for (i <- 0 until 10) {
|
||||
sender.send(alice, CMD_ADD_HTLC(50000000 msat, h, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())))
|
||||
sender.send(alice, CMD_ADD_HTLC(50000000 msat, h, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID())))
|
||||
sender.expectMsg("ok")
|
||||
val htlc = alice2bob.expectMsgType[UpdateAddHtlc]
|
||||
assert(htlc.id == i && htlc.paymentHash == h)
|
||||
|
@ -100,7 +100,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
val sender = TestProbe()
|
||||
val h = randomBytes32
|
||||
val originHtlc = UpdateAddHtlc(channelId = randomBytes32, id = 5656, amountMsat = 50000000 msat, cltvExpiry = CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), paymentHash = h, onionRoutingPacket = TestConstants.emptyOnionPacket)
|
||||
val cmd = CMD_ADD_HTLC(originHtlc.amountMsat - 10000.msat, h, originHtlc.cltvExpiry - CltvExpiryDelta(7), TestConstants.emptyOnionPacket, upstream = Right(originHtlc))
|
||||
val cmd = CMD_ADD_HTLC(originHtlc.amountMsat - 10000.msat, h, originHtlc.cltvExpiry - CltvExpiryDelta(7), TestConstants.emptyOnionPacket, Upstream.Relayed(originHtlc))
|
||||
sender.send(alice, cmd)
|
||||
sender.expectMsg("ok")
|
||||
val htlc = alice2bob.expectMsgType[UpdateAddHtlc]
|
||||
|
@ -109,7 +109,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
commitments = initialState.commitments.copy(
|
||||
localNextHtlcId = 1,
|
||||
localChanges = initialState.commitments.localChanges.copy(proposed = htlc :: Nil),
|
||||
originChannels = Map(0L -> Relayed(originHtlc.channelId, originHtlc.id, originHtlc.amountMsat, htlc.amountMsat))
|
||||
originChannels = Map(0L -> Origin.Relayed(originHtlc.channelId, originHtlc.id, originHtlc.amountMsat, htlc.amountMsat))
|
||||
)))
|
||||
}
|
||||
|
||||
|
@ -118,10 +118,10 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
val sender = TestProbe()
|
||||
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
|
||||
val expiryTooSmall = CltvExpiry(currentBlockHeight + 3)
|
||||
val add = CMD_ADD_HTLC(500000000 msat, randomBytes32, expiryTooSmall, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))
|
||||
val add = CMD_ADD_HTLC(500000000 msat, randomBytes32, expiryTooSmall, TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
|
||||
sender.send(alice, add)
|
||||
val error = ExpiryTooSmall(channelId(alice), Channel.MIN_CLTV_EXPIRY_DELTA.toCltvExpiry(currentBlockHeight), expiryTooSmall, currentBlockHeight)
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add))))
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Origin.Local(add.upstream.asInstanceOf[Upstream.Local].id, Some(sender.ref)), Some(initialState.channelUpdate), Some(add))))
|
||||
alice2bob.expectNoMsg(200 millis)
|
||||
}
|
||||
|
||||
|
@ -130,10 +130,10 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
val sender = TestProbe()
|
||||
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
|
||||
val expiryTooBig = (Channel.MAX_CLTV_EXPIRY_DELTA + 1).toCltvExpiry(currentBlockHeight)
|
||||
val add = CMD_ADD_HTLC(500000000 msat, randomBytes32, expiryTooBig, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))
|
||||
val add = CMD_ADD_HTLC(500000000 msat, randomBytes32, expiryTooBig, TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
|
||||
sender.send(alice, add)
|
||||
val error = ExpiryTooBig(channelId(alice), maximum = Channel.MAX_CLTV_EXPIRY_DELTA.toCltvExpiry(currentBlockHeight), actual = expiryTooBig, blockCount = currentBlockHeight)
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add))))
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Origin.Local(add.upstream.asInstanceOf[Upstream.Local].id, Some(sender.ref)), Some(initialState.channelUpdate), Some(add))))
|
||||
alice2bob.expectNoMsg(200 millis)
|
||||
}
|
||||
|
||||
|
@ -141,10 +141,10 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
import f._
|
||||
val sender = TestProbe()
|
||||
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
|
||||
val add = CMD_ADD_HTLC(50 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))
|
||||
val add = CMD_ADD_HTLC(50 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
|
||||
sender.send(alice, add)
|
||||
val error = HtlcValueTooSmall(channelId(alice), 1000 msat, 50 msat)
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add))))
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Origin.Local(add.upstream.asInstanceOf[Upstream.Local].id, Some(sender.ref)), Some(initialState.channelUpdate), Some(add))))
|
||||
alice2bob.expectNoMsg(200 millis)
|
||||
}
|
||||
|
||||
|
@ -153,7 +153,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
val sender = TestProbe()
|
||||
// channel starts with all funds on alice's side, alice sends some funds to bob, but not enough to make it go above reserve
|
||||
val h = randomBytes32
|
||||
val add = CMD_ADD_HTLC(50000000 msat, h, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))
|
||||
val add = CMD_ADD_HTLC(50000000 msat, h, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
|
||||
sender.send(alice, add)
|
||||
sender.expectMsg("ok")
|
||||
}
|
||||
|
@ -162,10 +162,10 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
import f._
|
||||
val sender = TestProbe()
|
||||
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
|
||||
val add = CMD_ADD_HTLC(MilliSatoshi(Int.MaxValue), randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))
|
||||
val add = CMD_ADD_HTLC(MilliSatoshi(Int.MaxValue), randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
|
||||
sender.send(alice, add)
|
||||
val error = InsufficientFunds(channelId(alice), amount = MilliSatoshi(Int.MaxValue), missing = 1376443 sat, reserve = 20000 sat, fees = 8960 sat)
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add))))
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Origin.Local(add.upstream.asInstanceOf[Upstream.Local].id, Some(sender.ref)), Some(initialState.channelUpdate), Some(add))))
|
||||
alice2bob.expectNoMsg(200 millis)
|
||||
}
|
||||
|
||||
|
@ -173,11 +173,11 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
import f._
|
||||
val sender = TestProbe()
|
||||
val initialState = bob.stateData.asInstanceOf[DATA_NORMAL]
|
||||
val add = CMD_ADD_HTLC(initialState.commitments.availableBalanceForSend + 1.msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))
|
||||
val add = CMD_ADD_HTLC(initialState.commitments.availableBalanceForSend + 1.msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
|
||||
sender.send(bob, add)
|
||||
|
||||
val error = InsufficientFunds(channelId(alice), amount = add.amount, missing = 0 sat, reserve = 10000 sat, fees = 0 sat)
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add))))
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Origin.Local(add.upstream.asInstanceOf[Upstream.Local].id, Some(sender.ref)), Some(initialState.channelUpdate), Some(add))))
|
||||
alice2bob.expectNoMsg(200 millis)
|
||||
}
|
||||
|
||||
|
@ -190,29 +190,29 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
|
||||
// actual test begins
|
||||
// at this point alice has the minimal amount to sustain a channel (29000 sat ~= alice reserve + commit fee)
|
||||
val add = CMD_ADD_HTLC(120000000 msat, randomBytes32, CltvExpiry(400144), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))
|
||||
val add = CMD_ADD_HTLC(120000000 msat, randomBytes32, CltvExpiry(400144), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
|
||||
sender.send(bob, add)
|
||||
val error = RemoteCannotAffordFeesForNewHtlc(channelId(bob), add.amount, missing = 1680 sat, 10000 sat, 10680 sat)
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(bob), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(bob.stateData.asInstanceOf[DATA_NORMAL].channelUpdate), Some(add))))
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(bob), add.paymentHash, error, Origin.Local(add.upstream.asInstanceOf[Upstream.Local].id, Some(sender.ref)), Some(bob.stateData.asInstanceOf[DATA_NORMAL].channelUpdate), Some(add))))
|
||||
}
|
||||
|
||||
test("recv CMD_ADD_HTLC (insufficient funds w/ pending htlcs and 0 balance)") { f =>
|
||||
import f._
|
||||
val sender = TestProbe()
|
||||
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
|
||||
sender.send(alice, CMD_ADD_HTLC(500000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())))
|
||||
sender.send(alice, CMD_ADD_HTLC(500000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID())))
|
||||
sender.expectMsg("ok")
|
||||
alice2bob.expectMsgType[UpdateAddHtlc]
|
||||
sender.send(alice, CMD_ADD_HTLC(200000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())))
|
||||
sender.send(alice, CMD_ADD_HTLC(200000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID())))
|
||||
sender.expectMsg("ok")
|
||||
alice2bob.expectMsgType[UpdateAddHtlc]
|
||||
sender.send(alice, CMD_ADD_HTLC(67600000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())))
|
||||
sender.send(alice, CMD_ADD_HTLC(67600000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID())))
|
||||
sender.expectMsg("ok")
|
||||
alice2bob.expectMsgType[UpdateAddHtlc]
|
||||
val add = CMD_ADD_HTLC(1000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))
|
||||
val add = CMD_ADD_HTLC(1000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
|
||||
sender.send(alice, add)
|
||||
val error = InsufficientFunds(channelId(alice), amount = 1000000 msat, missing = 1000 sat, reserve = 20000 sat, fees = 12400 sat)
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add))))
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Origin.Local(add.upstream.asInstanceOf[Upstream.Local].id, Some(sender.ref)), Some(initialState.channelUpdate), Some(add))))
|
||||
alice2bob.expectNoMsg(200 millis)
|
||||
}
|
||||
|
||||
|
@ -220,16 +220,16 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
import f._
|
||||
val sender = TestProbe()
|
||||
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
|
||||
sender.send(alice, CMD_ADD_HTLC(300000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())))
|
||||
sender.send(alice, CMD_ADD_HTLC(300000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID())))
|
||||
sender.expectMsg("ok")
|
||||
alice2bob.expectMsgType[UpdateAddHtlc]
|
||||
sender.send(alice, CMD_ADD_HTLC(300000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())))
|
||||
sender.send(alice, CMD_ADD_HTLC(300000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID())))
|
||||
sender.expectMsg("ok")
|
||||
alice2bob.expectMsgType[UpdateAddHtlc]
|
||||
val add = CMD_ADD_HTLC(500000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))
|
||||
val add = CMD_ADD_HTLC(500000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
|
||||
sender.send(alice, add)
|
||||
val error = InsufficientFunds(channelId(alice), amount = 500000000 msat, missing = 332400 sat, reserve = 20000 sat, fees = 12400 sat)
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add))))
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Origin.Local(add.upstream.asInstanceOf[Upstream.Local].id, Some(sender.ref)), Some(initialState.channelUpdate), Some(add))))
|
||||
alice2bob.expectNoMsg(200 millis)
|
||||
}
|
||||
|
||||
|
@ -237,10 +237,25 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
import f._
|
||||
val sender = TestProbe()
|
||||
val initialState = bob.stateData.asInstanceOf[DATA_NORMAL]
|
||||
val add = CMD_ADD_HTLC(151000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))
|
||||
val add = CMD_ADD_HTLC(151000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
|
||||
sender.send(bob, add)
|
||||
val error = HtlcValueTooHighInFlight(channelId(bob), maximum = 150000000, actual = 151000000 msat)
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(bob), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add))))
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(bob), add.paymentHash, error, Origin.Local(add.upstream.asInstanceOf[Upstream.Local].id, Some(sender.ref)), Some(initialState.channelUpdate), Some(add))))
|
||||
bob2alice.expectNoMsg(200 millis)
|
||||
}
|
||||
|
||||
test("recv CMD_ADD_HTLC (over max inflight htlc value with duplicate amounts)") { f =>
|
||||
import f._
|
||||
val sender = TestProbe()
|
||||
val initialState = bob.stateData.asInstanceOf[DATA_NORMAL]
|
||||
val add = CMD_ADD_HTLC(75500000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
|
||||
sender.send(bob, add)
|
||||
sender.expectMsg("ok")
|
||||
bob2alice.expectMsgType[UpdateAddHtlc]
|
||||
val add1 = CMD_ADD_HTLC(75500000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
|
||||
sender.send(bob, add1)
|
||||
val error = HtlcValueTooHighInFlight(channelId(bob), maximum = 150000000, actual = 151000000 msat)
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(bob), add1.paymentHash, error, Origin.Local(add1.upstream.asInstanceOf[Upstream.Local].id, Some(sender.ref)), Some(initialState.channelUpdate), Some(add1))))
|
||||
bob2alice.expectNoMsg(200 millis)
|
||||
}
|
||||
|
||||
|
@ -250,14 +265,14 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
|
||||
// Bob accepts a maximum of 30 htlcs
|
||||
for (i <- 0 until 30) {
|
||||
sender.send(alice, CMD_ADD_HTLC(10000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())))
|
||||
sender.send(alice, CMD_ADD_HTLC(10000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID())))
|
||||
sender.expectMsg("ok")
|
||||
alice2bob.expectMsgType[UpdateAddHtlc]
|
||||
}
|
||||
val add = CMD_ADD_HTLC(10000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))
|
||||
val add = CMD_ADD_HTLC(10000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
|
||||
sender.send(alice, add)
|
||||
val error = TooManyAcceptedHtlcs(channelId(alice), maximum = 30)
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add))))
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Origin.Local(add.upstream.asInstanceOf[Upstream.Local].id, Some(sender.ref)), Some(initialState.channelUpdate), Some(add))))
|
||||
alice2bob.expectNoMsg(200 millis)
|
||||
}
|
||||
|
||||
|
@ -265,7 +280,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
import f._
|
||||
val sender = TestProbe()
|
||||
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
|
||||
val add1 = CMD_ADD_HTLC(TestConstants.fundingSatoshis.toMilliSatoshi * 2 / 3, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))
|
||||
val add1 = CMD_ADD_HTLC(TestConstants.fundingSatoshis.toMilliSatoshi * 2 / 3, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
|
||||
sender.send(alice, add1)
|
||||
sender.expectMsg("ok")
|
||||
alice2bob.expectMsgType[UpdateAddHtlc]
|
||||
|
@ -273,10 +288,10 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
sender.expectMsg("ok")
|
||||
alice2bob.expectMsgType[CommitSig]
|
||||
// this is over channel-capacity
|
||||
val add2 = CMD_ADD_HTLC(TestConstants.fundingSatoshis.toMilliSatoshi * 2 / 3, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))
|
||||
val add2 = CMD_ADD_HTLC(TestConstants.fundingSatoshis.toMilliSatoshi * 2 / 3, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
|
||||
sender.send(alice, add2)
|
||||
val error = InsufficientFunds(channelId(alice), add2.amount, 564013 sat, 20000 sat, 10680 sat)
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add2.paymentHash, error, Local(add2.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add2))))
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add2.paymentHash, error, Origin.Local(add2.upstream.asInstanceOf[Upstream.Local].id, Some(sender.ref)), Some(initialState.channelUpdate), Some(add2))))
|
||||
alice2bob.expectNoMsg(200 millis)
|
||||
}
|
||||
|
||||
|
@ -290,10 +305,10 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].localShutdown.isDefined && alice.stateData.asInstanceOf[DATA_NORMAL].remoteShutdown.isEmpty)
|
||||
|
||||
// actual test starts here
|
||||
val add = CMD_ADD_HTLC(500000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))
|
||||
val add = CMD_ADD_HTLC(500000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
|
||||
sender.send(alice, add)
|
||||
val error = NoMoreHtlcsClosingInProgress(channelId(alice))
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add))))
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Origin.Local(add.upstream.asInstanceOf[Upstream.Local].id, Some(sender.ref)), Some(initialState.channelUpdate), Some(add))))
|
||||
alice2bob.expectNoMsg(200 millis)
|
||||
}
|
||||
|
||||
|
@ -302,14 +317,14 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
val sender = TestProbe()
|
||||
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
|
||||
// let's make alice send an htlc
|
||||
val add1 = CMD_ADD_HTLC(500000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))
|
||||
val add1 = CMD_ADD_HTLC(500000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
|
||||
sender.send(alice, add1)
|
||||
sender.expectMsg("ok")
|
||||
// at the same time bob initiates a closing
|
||||
sender.send(bob, CMD_CLOSE(None))
|
||||
sender.expectMsg("ok")
|
||||
// this command will be received by alice right after having received the shutdown
|
||||
val add2 = CMD_ADD_HTLC(100000000 msat, randomBytes32, CltvExpiry(300000), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))
|
||||
val add2 = CMD_ADD_HTLC(100000000 msat, randomBytes32, CltvExpiry(300000), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
|
||||
// messages cross
|
||||
alice2bob.expectMsgType[UpdateAddHtlc]
|
||||
alice2bob.forward(bob)
|
||||
|
@ -317,7 +332,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
bob2alice.forward(alice)
|
||||
sender.send(alice, add2)
|
||||
val error = NoMoreHtlcsClosingInProgress(channelId(alice))
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add2.paymentHash, error, Local(add2.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add2))))
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add2.paymentHash, error, Origin.Local(add2.upstream.asInstanceOf[Upstream.Local].id, Some(sender.ref)), Some(initialState.channelUpdate), Some(add2))))
|
||||
}
|
||||
|
||||
test("recv UpdateAddHtlc") { f =>
|
||||
|
@ -456,7 +471,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
test("recv CMD_SIGN (two identical htlcs in each direction)") { f =>
|
||||
import f._
|
||||
val sender = TestProbe()
|
||||
val add = CMD_ADD_HTLC(10000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))
|
||||
val add = CMD_ADD_HTLC(10000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
|
||||
sender.send(alice, add)
|
||||
sender.expectMsg("ok")
|
||||
alice2bob.expectMsgType[UpdateAddHtlc]
|
||||
|
@ -503,19 +518,19 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
assert(a2b_2 > aliceMinOffer && a2b_2 > bobMinReceive)
|
||||
assert(b2a_1 > aliceMinReceive && b2a_1 > bobMinOffer)
|
||||
assert(b2a_2 < aliceMinReceive && b2a_2 > bobMinOffer)
|
||||
sender.send(alice, CMD_ADD_HTLC(a2b_1.toMilliSatoshi, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())))
|
||||
sender.send(alice, CMD_ADD_HTLC(a2b_1.toMilliSatoshi, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID())))
|
||||
sender.expectMsg("ok")
|
||||
alice2bob.expectMsgType[UpdateAddHtlc]
|
||||
alice2bob.forward(bob)
|
||||
sender.send(alice, CMD_ADD_HTLC(a2b_2.toMilliSatoshi, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())))
|
||||
sender.send(alice, CMD_ADD_HTLC(a2b_2.toMilliSatoshi, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID())))
|
||||
sender.expectMsg("ok")
|
||||
alice2bob.expectMsgType[UpdateAddHtlc]
|
||||
alice2bob.forward(bob)
|
||||
sender.send(bob, CMD_ADD_HTLC(b2a_1.toMilliSatoshi, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())))
|
||||
sender.send(bob, CMD_ADD_HTLC(b2a_1.toMilliSatoshi, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID())))
|
||||
sender.expectMsg("ok")
|
||||
bob2alice.expectMsgType[UpdateAddHtlc]
|
||||
bob2alice.forward(alice)
|
||||
sender.send(bob, CMD_ADD_HTLC(b2a_2.toMilliSatoshi, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())))
|
||||
sender.send(bob, CMD_ADD_HTLC(b2a_2.toMilliSatoshi, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID())))
|
||||
sender.expectMsg("ok")
|
||||
bob2alice.expectMsgType[UpdateAddHtlc]
|
||||
bob2alice.forward(alice)
|
||||
|
@ -535,7 +550,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
test("recv CMD_SIGN (htlcs with same pubkeyScript but different amounts)") { f =>
|
||||
import f._
|
||||
val sender = TestProbe()
|
||||
val add = CMD_ADD_HTLC(10000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))
|
||||
val add = CMD_ADD_HTLC(10000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
|
||||
val epsilons = List(3, 1, 5, 7, 6) // unordered on purpose
|
||||
val htlcCount = epsilons.size
|
||||
for (i <- epsilons) {
|
||||
|
@ -733,12 +748,12 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
val r = randomBytes32
|
||||
val h = Crypto.sha256(r)
|
||||
|
||||
sender.send(alice, CMD_ADD_HTLC(50000000 msat, h, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())))
|
||||
sender.send(alice, CMD_ADD_HTLC(50000000 msat, h, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID())))
|
||||
sender.expectMsg("ok")
|
||||
val htlc1 = alice2bob.expectMsgType[UpdateAddHtlc]
|
||||
alice2bob.forward(bob)
|
||||
|
||||
sender.send(alice, CMD_ADD_HTLC(50000000 msat, h, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())))
|
||||
sender.send(alice, CMD_ADD_HTLC(50000000 msat, h, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID())))
|
||||
sender.expectMsg("ok")
|
||||
val htlc2 = alice2bob.expectMsgType[UpdateAddHtlc]
|
||||
alice2bob.forward(bob)
|
||||
|
@ -1491,7 +1506,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
bob2blockchain.expectMsgType[WatchConfirmed]
|
||||
}
|
||||
|
||||
ignore("recv CMD_UPDATE_RELAY_FEE ") { f =>
|
||||
test("recv CMD_UPDATE_RELAY_FEE ") { f =>
|
||||
import f._
|
||||
val sender = TestProbe()
|
||||
val newFeeBaseMsat = TestConstants.Alice.nodeParams.feeBase * 2
|
||||
|
@ -2077,7 +2092,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
// alice = 800 000
|
||||
// bob = 200 000
|
||||
|
||||
val add = CMD_ADD_HTLC(10000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))
|
||||
val add = CMD_ADD_HTLC(10000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
|
||||
sender.send(alice, add)
|
||||
sender.expectMsg("ok")
|
||||
alice2bob.expectMsgType[UpdateAddHtlc]
|
||||
|
@ -2295,7 +2310,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
alice2bob.expectMsg(annSigsA)
|
||||
}
|
||||
|
||||
ignore("recv TickRefreshChannelUpdate", Tag("channels_public")) { f =>
|
||||
test("recv BroadcastChannelUpdate", Tag("channels_public")) { f =>
|
||||
import f._
|
||||
val sender = TestProbe()
|
||||
sender.send(alice, WatchEventConfirmed(BITCOIN_FUNDING_DEEPLYBURIED, 400000, 42, null))
|
||||
|
|
|
@ -23,8 +23,8 @@ import akka.testkit.{TestActorRef, TestProbe}
|
|||
import fr.acinq.bitcoin.Crypto.PrivateKey
|
||||
import fr.acinq.bitcoin.{ByteVector32, ScriptFlags, Transaction}
|
||||
import fr.acinq.eclair.TestConstants.Alice
|
||||
import fr.acinq.eclair.blockchain.fee.FeeratesPerKw
|
||||
import fr.acinq.eclair.blockchain._
|
||||
import fr.acinq.eclair.blockchain.fee.FeeratesPerKw
|
||||
import fr.acinq.eclair.channel.Channel.LocalError
|
||||
import fr.acinq.eclair.channel._
|
||||
import fr.acinq.eclair.channel.states.StateTestsHelperMethods
|
||||
|
@ -71,7 +71,7 @@ class OfflineStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
import f._
|
||||
val sender = TestProbe()
|
||||
|
||||
sender.send(alice, CMD_ADD_HTLC(1000000 msat, ByteVector32.Zeroes, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())))
|
||||
sender.send(alice, CMD_ADD_HTLC(1000000 msat, ByteVector32.Zeroes, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID())))
|
||||
val ab_add_0 = alice2bob.expectMsgType[UpdateAddHtlc]
|
||||
// add ->b
|
||||
alice2bob.forward(bob)
|
||||
|
@ -152,7 +152,7 @@ class OfflineStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
import f._
|
||||
val sender = TestProbe()
|
||||
|
||||
sender.send(alice, CMD_ADD_HTLC(1000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())))
|
||||
sender.send(alice, CMD_ADD_HTLC(1000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID())))
|
||||
val ab_add_0 = alice2bob.expectMsgType[UpdateAddHtlc]
|
||||
// add ->b
|
||||
alice2bob.forward(bob, ab_add_0)
|
||||
|
@ -400,7 +400,7 @@ class OfflineStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
channelUpdateListener.expectNoMsg(300 millis)
|
||||
|
||||
// we attempt to send a payment
|
||||
sender.send(alice, CMD_ADD_HTLC(4200 msat, randomBytes32, CltvExpiry(123456), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())))
|
||||
sender.send(alice, CMD_ADD_HTLC(4200 msat, randomBytes32, CltvExpiry(123456), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID())))
|
||||
val failure = sender.expectMsgType[Status.Failure]
|
||||
val AddHtlcFailed(_, _, ChannelUnavailable(_), _, _, _) = failure.cause
|
||||
|
||||
|
|
|
@ -104,10 +104,10 @@ class ShutdownStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
test("recv CMD_ADD_HTLC") { f =>
|
||||
import f._
|
||||
val sender = TestProbe()
|
||||
val add = CMD_ADD_HTLC(500000000 msat, r1, cltvExpiry = CltvExpiry(300000), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))
|
||||
val add = CMD_ADD_HTLC(500000000 msat, r1, cltvExpiry = CltvExpiry(300000), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
|
||||
sender.send(alice, add)
|
||||
val error = ChannelUnavailable(channelId(alice))
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), None, Some(add))))
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Origin.Local(add.upstream.asInstanceOf[Upstream.Local].id, Some(sender.ref)), None, Some(add))))
|
||||
alice2bob.expectNoMsg(200 millis)
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ import fr.acinq.eclair.blockchain.fee.FeeratesPerKw
|
|||
import fr.acinq.eclair.channel.Helpers.Closing
|
||||
import fr.acinq.eclair.channel._
|
||||
import fr.acinq.eclair.channel.states.StateTestsHelperMethods
|
||||
import fr.acinq.eclair.payment.Local
|
||||
import fr.acinq.eclair.payment.Origin
|
||||
import fr.acinq.eclair.wire.{ClosingSigned, Error, Shutdown}
|
||||
import fr.acinq.eclair.{CltvExpiry, LongToBtcAmount, TestConstants, TestkitBaseClass}
|
||||
import org.scalatest.{Outcome, Tag}
|
||||
|
@ -85,10 +85,10 @@ class NegotiatingStateSpec extends TestkitBaseClass with StateTestsHelperMethods
|
|||
import f._
|
||||
alice2bob.expectMsgType[ClosingSigned]
|
||||
val sender = TestProbe()
|
||||
val add = CMD_ADD_HTLC(5000000000L msat, ByteVector32(ByteVector.fill(32)(1)), cltvExpiry = CltvExpiry(300000), onion = TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))
|
||||
val add = CMD_ADD_HTLC(5000000000L msat, ByteVector32(ByteVector.fill(32)(1)), cltvExpiry = CltvExpiry(300000), onion = TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
|
||||
sender.send(alice, add)
|
||||
val error = ChannelUnavailable(channelId(alice))
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), None, Some(add))))
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Origin.Local(add.upstream.asInstanceOf[Upstream.Local].id, Some(sender.ref)), None, Some(add))))
|
||||
alice2bob.expectNoMsg(200 millis)
|
||||
}
|
||||
|
||||
|
|
|
@ -299,10 +299,10 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
|
||||
// actual test starts here
|
||||
val sender = TestProbe()
|
||||
val add = CMD_ADD_HTLC(500000000 msat, ByteVector32(ByteVector.fill(32)(1)), cltvExpiry = CltvExpiry(300000), onion = TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))
|
||||
val add = CMD_ADD_HTLC(500000000 msat, ByteVector32(ByteVector.fill(32)(1)), cltvExpiry = CltvExpiry(300000), onion = TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
|
||||
sender.send(alice, add)
|
||||
val error = ChannelUnavailable(channelId(alice))
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), None, Some(add))))
|
||||
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Origin.Local(add.upstream.asInstanceOf[Upstream.Local].id, Some(sender.ref)), None, Some(add))))
|
||||
alice2bob.expectNoMsg(200 millis)
|
||||
}
|
||||
|
||||
|
@ -418,7 +418,7 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
system.eventStream.subscribe(listener.ref, classOf[LocalCommitConfirmed])
|
||||
system.eventStream.subscribe(listener.ref, classOf[PaymentSettlingOnChain])
|
||||
// alice sends an htlc to bob
|
||||
val (ra1, htlca1) = addHtlc(50000000 msat, alice, bob, alice2bob, bob2alice)
|
||||
val (_, htlca1) = addHtlc(50000000 msat, alice, bob, alice2bob, bob2alice)
|
||||
crossSign(alice, bob, alice2bob, bob2alice)
|
||||
// an error occurs and alice publishes her commit tx
|
||||
val aliceCommitTx = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx
|
||||
|
@ -452,7 +452,7 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
system.eventStream.subscribe(listener.ref, classOf[PaymentSettlingOnChain])
|
||||
val aliceCommitTx = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx
|
||||
// alice sends an htlc
|
||||
val (r, htlc) = addHtlc(4200000 msat, alice, bob, alice2bob, bob2alice)
|
||||
val (_, htlc) = addHtlc(4200000 msat, alice, bob, alice2bob, bob2alice)
|
||||
// and signs it (but bob doesn't sign it)
|
||||
sender.send(alice, CMD_SIGN)
|
||||
sender.expectMsg("ok")
|
||||
|
@ -483,7 +483,7 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
|||
system.eventStream.subscribe(listener.ref, classOf[PaymentSettlingOnChain])
|
||||
val bobCommitTx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx
|
||||
// alice sends an htlc
|
||||
val (r, htlc) = addHtlc(4200000 msat, alice, bob, alice2bob, bob2alice)
|
||||
val (_, htlc) = addHtlc(4200000 msat, alice, bob, alice2bob, bob2alice)
|
||||
// and signs it (but bob doesn't sign it)
|
||||
sender.send(alice, CMD_SIGN)
|
||||
sender.expectMsg("ok")
|
||||
|
|
|
@ -19,7 +19,7 @@ package fr.acinq.eclair.db
|
|||
import java.sql.Connection
|
||||
|
||||
import fr.acinq.bitcoin.Crypto.PrivateKey
|
||||
import fr.acinq.bitcoin.{Block, Crypto}
|
||||
import fr.acinq.bitcoin.{Block, ByteVector32, ByteVector64, Crypto, Satoshi}
|
||||
import fr.acinq.eclair.db.sqlite.SqliteNetworkDb
|
||||
import fr.acinq.eclair.db.sqlite.SqliteUtils._
|
||||
import fr.acinq.eclair.router.{Announcements, PublicChannel}
|
||||
|
@ -28,7 +28,7 @@ import fr.acinq.eclair.{CltvExpiryDelta, LongToBtcAmount, ShortChannelId, TestCo
|
|||
import org.scalatest.FunSuite
|
||||
import scodec.bits.HexStringSyntax
|
||||
|
||||
import scala.collection.SortedMap
|
||||
import scala.collection.{SortedMap, mutable}
|
||||
|
||||
class SqliteNetworkDbSpec extends FunSuite {
|
||||
|
||||
|
@ -104,6 +104,17 @@ class SqliteNetworkDbSpec extends FunSuite {
|
|||
assert(node_4.addresses == List(Tor2("aaaqeayeaudaocaj", 42000)))
|
||||
}
|
||||
|
||||
test("correctly handle txids that start with 0") {
|
||||
val sqlite = TestConstants.sqliteInMemory()
|
||||
val db = new SqliteNetworkDb(sqlite, Block.RegtestGenesisBlock.hash)
|
||||
val sig = ByteVector64.Zeroes
|
||||
val c = Announcements.makeChannelAnnouncement(Block.RegtestGenesisBlock.hash, ShortChannelId(42), randomKey.publicKey, randomKey.publicKey, randomKey.publicKey, randomKey.publicKey, sig, sig, sig, sig)
|
||||
val c_shrunk = shrink(c)
|
||||
val txid = ByteVector32.fromValidHex("0001" * 16)
|
||||
db.addChannel(c, txid, Satoshi(42))
|
||||
assert(db.listChannels() === SortedMap(c.shortChannelId -> PublicChannel(c_shrunk, txid, Satoshi(42), None, None)))
|
||||
}
|
||||
|
||||
def shrink(c: ChannelAnnouncement) = c.copy(bitcoinKey1 = null, bitcoinKey2 = null, bitcoinSignature1 = null, bitcoinSignature2 = null, nodeSignature1 = null, nodeSignature2 = null, chainHash = null, features = null)
|
||||
|
||||
def shrink(c: ChannelUpdate) = c.copy(signature = null)
|
||||
|
|
|
@ -56,8 +56,8 @@ import scala.concurrent.ExecutionContext.Implicits.global
|
|||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* Created by PM on 15/03/2017.
|
||||
*/
|
||||
* Created by PM on 15/03/2017.
|
||||
*/
|
||||
@Ignore
|
||||
class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService with FunSuiteLike with BeforeAndAfterAll with Logging {
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ class SynchronizationPipe(latch: CountDownLatch) extends Actor with ActorLogging
|
|||
|
||||
script match {
|
||||
case offer(x, amount, rhash) :: rest =>
|
||||
resolve(x) ! CMD_ADD_HTLC(MilliSatoshi(amount.toInt), ByteVector32.fromValidHex(rhash), CltvExpiry(144), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))
|
||||
resolve(x) ! CMD_ADD_HTLC(MilliSatoshi(amount.toInt), ByteVector32.fromValidHex(rhash), CltvExpiry(144), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
|
||||
exec(rest, a, b)
|
||||
case fulfill(x, id, r) :: rest =>
|
||||
resolve(x) ! CMD_FULFILL_HTLC(id.toInt, ByteVector32.fromValidHex(r))
|
||||
|
|
|
@ -18,7 +18,7 @@ package fr.acinq.eclair.payment
|
|||
|
||||
import fr.acinq.bitcoin.Crypto.PublicKey
|
||||
import fr.acinq.bitcoin.{Block, ByteVector32}
|
||||
import fr.acinq.eclair.channel.{CMD_ADD_HTLC, CMD_FAIL_HTLC}
|
||||
import fr.acinq.eclair.channel.{CMD_ADD_HTLC, CMD_FAIL_HTLC, Upstream}
|
||||
import fr.acinq.eclair.payment.HtlcGenerationSpec.makeCommitments
|
||||
import fr.acinq.eclair.payment.Relayer.{OutgoingChannel, RelayFailure, RelayPayload, RelaySuccess}
|
||||
import fr.acinq.eclair.router.Announcements
|
||||
|
@ -50,7 +50,7 @@ class ChannelSelectionSpec extends FunSuite {
|
|||
val channelUpdate = dummyUpdate(ShortChannelId(12345), CltvExpiryDelta(10), 100 msat, 1000 msat, 100, 10000000 msat, true)
|
||||
|
||||
// nominal case
|
||||
assert(Relayer.relayOrFail(relayPayload, Some(channelUpdate)) === RelaySuccess(ShortChannelId(12345), CMD_ADD_HTLC(relayPayload.payload.amountToForward, relayPayload.add.paymentHash, relayPayload.payload.outgoingCltv, relayPayload.nextPacket, upstream = Right(relayPayload.add), commit = true)))
|
||||
assert(Relayer.relayOrFail(relayPayload, Some(channelUpdate)) === RelaySuccess(ShortChannelId(12345), CMD_ADD_HTLC(relayPayload.payload.amountToForward, relayPayload.add.paymentHash, relayPayload.payload.outgoingCltv, relayPayload.nextPacket, Upstream.Relayed(relayPayload.add), commit = true)))
|
||||
// no channel_update
|
||||
assert(Relayer.relayOrFail(relayPayload, channelUpdate_opt = None) === RelayFailure(CMD_FAIL_HTLC(relayPayload.add.id, Right(UnknownNextPeer), commit = true)))
|
||||
// channel disabled
|
||||
|
@ -67,7 +67,7 @@ class ChannelSelectionSpec extends FunSuite {
|
|||
assert(Relayer.relayOrFail(relayPayload_insufficientfee, Some(channelUpdate)) === RelayFailure(CMD_FAIL_HTLC(relayPayload.add.id, Right(FeeInsufficient(relayPayload_insufficientfee.add.amountMsat, channelUpdate)), commit = true)))
|
||||
// note that a generous fee is ok!
|
||||
val relayPayload_highfee = relayPayload.copy(payload = onionPayload.copy(amountToForward = 900000 msat))
|
||||
assert(Relayer.relayOrFail(relayPayload_highfee, Some(channelUpdate)) === RelaySuccess(ShortChannelId(12345), CMD_ADD_HTLC(relayPayload_highfee.payload.amountToForward, relayPayload_highfee.add.paymentHash, relayPayload_highfee.payload.outgoingCltv, relayPayload_highfee.nextPacket, upstream = Right(relayPayload.add), commit = true)))
|
||||
assert(Relayer.relayOrFail(relayPayload_highfee, Some(channelUpdate)) === RelaySuccess(ShortChannelId(12345), CMD_ADD_HTLC(relayPayload_highfee.payload.amountToForward, relayPayload_highfee.add.paymentHash, relayPayload_highfee.payload.outgoingCltv, relayPayload_highfee.nextPacket, Upstream.Relayed(relayPayload.add), commit = true)))
|
||||
}
|
||||
|
||||
test("channel selection") {
|
||||
|
|
|
@ -30,8 +30,10 @@ import fr.acinq.eclair.channel.{AddHtlcFailed, Channel, ChannelUnavailable}
|
|||
import fr.acinq.eclair.crypto.Sphinx
|
||||
import fr.acinq.eclair.db.{OutgoingPayment, OutgoingPaymentStatus}
|
||||
import fr.acinq.eclair.io.Peer.PeerRoutingMessage
|
||||
import fr.acinq.eclair.payment.Origin.Local
|
||||
import fr.acinq.eclair.payment.PaymentInitiator.SendPaymentRequest
|
||||
import fr.acinq.eclair.payment.PaymentLifecycle._
|
||||
import fr.acinq.eclair.payment.PaymentRequest.ExtraHop
|
||||
import fr.acinq.eclair.payment.PaymentSent.PartialPayment
|
||||
import fr.acinq.eclair.router.Announcements.{makeChannelUpdate, makeNodeAnnouncement}
|
||||
import fr.acinq.eclair.router._
|
||||
|
@ -358,6 +360,61 @@ class PaymentLifecycleSpec extends BaseRouterSpec {
|
|||
awaitCond(paymentDb.getOutgoingPayment(id).exists(_.status.isInstanceOf[OutgoingPaymentStatus.Failed]))
|
||||
}
|
||||
|
||||
test("payment failed (Update in assisted route)") { fixture =>
|
||||
import fixture._
|
||||
val nodeParams = TestConstants.Alice.nodeParams.copy(keyManager = testKeyManager)
|
||||
val paymentDb = nodeParams.db.payments
|
||||
val relayer = TestProbe()
|
||||
val routerForwarder = TestProbe()
|
||||
val id = UUID.randomUUID()
|
||||
val progressHandler = PaymentLifecycle.DefaultPaymentProgressHandler(id, defaultPaymentRequest, paymentDb)
|
||||
val paymentFSM = TestFSMRef(new PaymentLifecycle(nodeParams, progressHandler, routerForwarder.ref, relayer.ref))
|
||||
val monitor = TestProbe()
|
||||
val sender = TestProbe()
|
||||
|
||||
paymentFSM ! SubscribeTransitionCallBack(monitor.ref)
|
||||
val CurrentState(_, WAITING_FOR_REQUEST) = monitor.expectMsgClass(classOf[CurrentState[_]])
|
||||
|
||||
// we build an assisted route for channel bc and cd
|
||||
val assistedRoutes = Seq(Seq(
|
||||
ExtraHop(b, channelId_bc, channelUpdate_bc.feeBaseMsat, channelUpdate_bc.feeProportionalMillionths, channelUpdate_bc.cltvExpiryDelta),
|
||||
ExtraHop(c, channelId_cd, channelUpdate_cd.feeBaseMsat, channelUpdate_cd.feeProportionalMillionths, channelUpdate_cd.cltvExpiryDelta)
|
||||
))
|
||||
|
||||
val request = SendPayment(defaultPaymentHash, d, FinalLegacyPayload(defaultAmountMsat, defaultExpiryDelta.toCltvExpiry(nodeParams.currentBlockHeight)), maxAttempts = 5, assistedRoutes = assistedRoutes)
|
||||
sender.send(paymentFSM, request)
|
||||
awaitCond(paymentFSM.stateName == WAITING_FOR_ROUTE && paymentDb.getOutgoingPayment(id).exists(_.status === OutgoingPaymentStatus.Pending))
|
||||
|
||||
val WaitingForRoute(_, _, Nil) = paymentFSM.stateData
|
||||
routerForwarder.expectMsg(RouteRequest(nodeParams.nodeId, d, defaultAmountMsat, assistedRoutes = assistedRoutes, ignoreNodes = Set.empty, ignoreChannels = Set.empty))
|
||||
routerForwarder.forward(router)
|
||||
awaitCond(paymentFSM.stateName == WAITING_FOR_PAYMENT_COMPLETE)
|
||||
val WaitingForComplete(_, _, cmd1, Nil, sharedSecrets1, _, _, _) = paymentFSM.stateData
|
||||
relayer.expectMsg(ForwardShortId(channelId_ab, cmd1))
|
||||
|
||||
// we change the cltv expiry
|
||||
val channelUpdate_bc_modified = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_b, c, channelId_bc, CltvExpiryDelta(42), htlcMinimumMsat = channelUpdate_bc.htlcMinimumMsat, feeBaseMsat = channelUpdate_bc.feeBaseMsat, feeProportionalMillionths = channelUpdate_bc.feeProportionalMillionths, htlcMaximumMsat = channelUpdate_bc.htlcMaximumMsat.get)
|
||||
val failure = IncorrectCltvExpiry(CltvExpiry(5), channelUpdate_bc_modified)
|
||||
// and node replies with a failure containing a new channel update
|
||||
sender.send(paymentFSM, UpdateFailHtlc(ByteVector32.Zeroes, 0, Sphinx.FailurePacket.create(sharedSecrets1.head._1, failure)))
|
||||
|
||||
// payment lifecycle forwards the embedded channelUpdate to the router
|
||||
routerForwarder.expectMsg(channelUpdate_bc_modified)
|
||||
awaitCond(paymentFSM.stateName == WAITING_FOR_ROUTE && paymentDb.getOutgoingPayment(id).exists(_.status === OutgoingPaymentStatus.Pending)) // 1 failure but not final, the payment is still PENDING
|
||||
val assistedRoutes1 = Seq(Seq(
|
||||
ExtraHop(b, channelId_bc, channelUpdate_bc.feeBaseMsat, channelUpdate_bc.feeProportionalMillionths, channelUpdate_bc_modified.cltvExpiryDelta),
|
||||
ExtraHop(c, channelId_cd, channelUpdate_cd.feeBaseMsat, channelUpdate_cd.feeProportionalMillionths, channelUpdate_cd.cltvExpiryDelta)
|
||||
))
|
||||
routerForwarder.expectMsg(RouteRequest(nodeParams.nodeId, d, defaultAmountMsat, assistedRoutes = assistedRoutes1, ignoreNodes = Set.empty, ignoreChannels = Set.empty))
|
||||
routerForwarder.forward(router)
|
||||
|
||||
// router answers with a new route, taking into account the new update
|
||||
awaitCond(paymentFSM.stateName == WAITING_FOR_PAYMENT_COMPLETE)
|
||||
val WaitingForComplete(_, _, cmd2, _, _, _, _, _) = paymentFSM.stateData
|
||||
relayer.expectMsg(ForwardShortId(channelId_ab, cmd2))
|
||||
assert(cmd2.cltvExpiry > cmd1.cltvExpiry)
|
||||
}
|
||||
|
||||
def testPermanentFailure(fixture: FixtureParam, failure: FailureMessage): Unit = {
|
||||
import fixture._
|
||||
val nodeParams = TestConstants.Alice.nodeParams.copy(keyManager = testKeyManager)
|
||||
|
|
|
@ -24,6 +24,7 @@ import fr.acinq.bitcoin.ByteVector32
|
|||
import fr.acinq.bitcoin.Crypto.PublicKey
|
||||
import fr.acinq.eclair.channel._
|
||||
import fr.acinq.eclair.crypto.Sphinx
|
||||
import fr.acinq.eclair.payment.Origin.Relayed
|
||||
import fr.acinq.eclair.payment.PaymentLifecycle.{buildCommand, buildOnion}
|
||||
import fr.acinq.eclair.router.Announcements
|
||||
import fr.acinq.eclair.wire.Onion.{FinalLegacyPayload, FinalTlvPayload, PerHopPayload, RelayTlvPayload}
|
||||
|
@ -77,7 +78,7 @@ class RelayerSpec extends TestkitBaseClass {
|
|||
assert(fwd.shortChannelId === channelUpdate_bc.shortChannelId)
|
||||
assert(fwd.message.amount === amount_bc)
|
||||
assert(fwd.message.cltvExpiry === expiry_bc)
|
||||
assert(fwd.message.upstream === Right(add_ab))
|
||||
assert(fwd.message.upstream === Upstream.Relayed(add_ab))
|
||||
|
||||
sender.expectNoMsg(100 millis)
|
||||
paymentHandler.expectNoMsg(100 millis)
|
||||
|
@ -106,7 +107,7 @@ class RelayerSpec extends TestkitBaseClass {
|
|||
assert(fwd.shortChannelId === channelUpdate_bc.shortChannelId)
|
||||
assert(fwd.message.amount === amount_bc)
|
||||
assert(fwd.message.cltvExpiry === expiry_bc)
|
||||
assert(fwd.message.upstream === Right(add_ab))
|
||||
assert(fwd.message.upstream === Upstream.Relayed(add_ab))
|
||||
|
||||
sender.expectNoMsg(100 millis)
|
||||
paymentHandler.expectNoMsg(100 millis)
|
||||
|
@ -133,7 +134,7 @@ class RelayerSpec extends TestkitBaseClass {
|
|||
// first try
|
||||
val fwd1 = register.expectMsgType[Register.ForwardShortId[CMD_ADD_HTLC]]
|
||||
assert(fwd1.shortChannelId === channelUpdate_bc_1.shortChannelId)
|
||||
assert(fwd1.message.upstream === Right(add_ab))
|
||||
assert(fwd1.message.upstream === Upstream.Relayed(add_ab))
|
||||
|
||||
// channel returns an error
|
||||
val origin = Relayed(channelId_ab, originHtlcId = 42, amountIn = 1100000 msat, amountOut = 1000000 msat)
|
||||
|
@ -142,7 +143,7 @@ class RelayerSpec extends TestkitBaseClass {
|
|||
// second try
|
||||
val fwd2 = register.expectMsgType[Register.ForwardShortId[CMD_ADD_HTLC]]
|
||||
assert(fwd2.shortChannelId === channelUpdate_bc.shortChannelId)
|
||||
assert(fwd2.message.upstream === Right(add_ab))
|
||||
assert(fwd2.message.upstream === Upstream.Relayed(add_ab))
|
||||
|
||||
// failure again
|
||||
sender.send(relayer, Status.Failure(AddHtlcFailed(channelId_bc, paymentHash, HtlcValueTooHighInFlight(channelId_bc, UInt64(1000000000L), 1516977616L msat), origin, Some(channelUpdate_bc), originalCommand = Some(fwd2.message))))
|
||||
|
@ -206,7 +207,7 @@ class RelayerSpec extends TestkitBaseClass {
|
|||
|
||||
val fwd1 = register.expectMsgType[Register.ForwardShortId[CMD_ADD_HTLC]]
|
||||
assert(fwd1.shortChannelId === channelUpdate_bc.shortChannelId)
|
||||
assert(fwd1.message.upstream === Right(add_ab))
|
||||
assert(fwd1.message.upstream === Upstream.Relayed(add_ab))
|
||||
|
||||
sender.send(relayer, Status.Failure(Register.ForwardShortIdFailure(fwd1)))
|
||||
|
||||
|
@ -232,7 +233,7 @@ class RelayerSpec extends TestkitBaseClass {
|
|||
|
||||
val fwd = register.expectMsgType[Register.ForwardShortId[CMD_ADD_HTLC]]
|
||||
assert(fwd.shortChannelId === channelUpdate_bc.shortChannelId)
|
||||
assert(fwd.message.upstream === Right(add_ab))
|
||||
assert(fwd.message.upstream === Upstream.Relayed(add_ab))
|
||||
|
||||
sender.expectNoMsg(100 millis)
|
||||
paymentHandler.expectNoMsg(100 millis)
|
||||
|
|
|
@ -37,6 +37,7 @@ import scala.collection.{SortedSet, immutable, mutable}
|
|||
import scala.compat.Platform
|
||||
import scala.concurrent.duration._
|
||||
|
||||
|
||||
//TODO: re-enable this using a modified version of the old test
|
||||
// as is it won't work on because on Android router just ignore querier
|
||||
@Ignore
|
||||
|
|
|
@ -27,7 +27,7 @@ import fr.acinq.bitcoin.{Block, ByteVector32, ByteVector64, Crypto, Deterministi
|
|||
import fr.acinq.eclair.channel.Helpers.Funding
|
||||
import fr.acinq.eclair.channel._
|
||||
import fr.acinq.eclair.crypto.{LocalKeyManager, ShaChain}
|
||||
import fr.acinq.eclair.payment.{Local, Relayed}
|
||||
import fr.acinq.eclair.payment.Origin.{Relayed, Local}
|
||||
import fr.acinq.eclair.router.Announcements
|
||||
import fr.acinq.eclair.transactions.Transactions.{CommitTx, InputInfo, TransactionWithInputInfo}
|
||||
import fr.acinq.eclair.transactions._
|
||||
|
@ -46,8 +46,8 @@ import scala.io.Source
|
|||
import scala.util.Random
|
||||
|
||||
/**
|
||||
* Created by PM on 31/05/2016.
|
||||
*/
|
||||
* Created by PM on 31/05/2016.
|
||||
*/
|
||||
|
||||
class ChannelCodecsSpec extends FunSuite {
|
||||
|
||||
|
@ -558,4 +558,5 @@ object ChannelCodecsSpec {
|
|||
new ChannelVersionSerializer +
|
||||
new InputInfoSerializer
|
||||
}
|
||||
|
||||
}
|
|
@ -1,3 +1,7 @@
|
|||
eclair {
|
||||
enable-kamon = false
|
||||
}
|
||||
|
||||
akka {
|
||||
|
||||
loggers = ["akka.event.slf4j.Slf4jLogger"]
|
||||
|
|
|
@ -22,7 +22,6 @@ import java.util.UUID
|
|||
import com.google.common.net.HostAndPort
|
||||
import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey}
|
||||
import fr.acinq.bitcoin.{ByteVector32, ByteVector64, OutPoint, Satoshi, Transaction}
|
||||
import fr.acinq.eclair.{MilliSatoshi, ShortChannelId, UInt64}
|
||||
import fr.acinq.eclair.channel.{ChannelVersion, State}
|
||||
import fr.acinq.eclair.crypto.ShaChain
|
||||
import fr.acinq.eclair.db.{IncomingPaymentStatus, OutgoingPaymentStatus}
|
||||
|
@ -248,6 +247,8 @@ object JsonSupport extends Json4sJacksonSupport {
|
|||
new UInt64Serializer +
|
||||
new SatoshiSerializer +
|
||||
new MilliSatoshiSerializer +
|
||||
new CltvExpirySerializer +
|
||||
new CltvExpiryDeltaSerializer +
|
||||
new ShortChannelIdSerializer +
|
||||
new StateSerializer +
|
||||
new ShaChainSerializer +
|
||||
|
|
|
@ -25,7 +25,6 @@ import fr.acinq.eclair.api.JsonSupport.CustomTypeHints
|
|||
import fr.acinq.eclair.payment.{PaymentRequest, PaymentSettlingOnChain}
|
||||
import fr.acinq.eclair.transactions.{IN, OUT}
|
||||
import fr.acinq.eclair.wire.{NodeAddress, Tor2, Tor3}
|
||||
import org.json4s.jackson.Serialization
|
||||
import org.scalatest.{FunSuite, Matchers}
|
||||
import scodec.bits._
|
||||
|
||||
|
@ -34,8 +33,6 @@ class JsonSerializersSpec extends FunSuite with Matchers {
|
|||
test("deserialize Map[OutPoint, ByteVector]") {
|
||||
val output1 = OutPoint(ByteVector32(hex"11418a2d282a40461966e4f578e1fdf633ad15c1b7fb3e771d14361127233be1"), 0)
|
||||
val output2 = OutPoint(ByteVector32(hex"3d62bd4f71dc63798418e59efbc7532380c900b5e79db3a5521374b161dd0e33"), 1)
|
||||
|
||||
|
||||
val map = Map(
|
||||
output1 -> hex"dead",
|
||||
output2 -> hex"beef"
|
||||
|
@ -43,12 +40,12 @@ class JsonSerializersSpec extends FunSuite with Matchers {
|
|||
|
||||
// it won't work with the default key serializer
|
||||
val error = intercept[org.json4s.MappingException] {
|
||||
Serialization.write(map)(org.json4s.DefaultFormats)
|
||||
JsonSupport.serialization.write(map)(org.json4s.DefaultFormats)
|
||||
}
|
||||
assert(error.msg.contains("Do not know how to serialize key of type class fr.acinq.bitcoin.OutPoint."))
|
||||
|
||||
// but it works with our custom key serializer
|
||||
val json = Serialization.write(map)(org.json4s.DefaultFormats + new ByteVectorSerializer + new OutPointKeySerializer)
|
||||
val json = JsonSupport.serialization.write(map)(org.json4s.DefaultFormats + new ByteVectorSerializer + new OutPointKeySerializer)
|
||||
assert(json === s"""{"${output1.txid}:0":"dead","${output2.txid}:1":"beef"}""")
|
||||
}
|
||||
|
||||
|
@ -58,15 +55,15 @@ class JsonSerializersSpec extends FunSuite with Matchers {
|
|||
val tor2 = Tor2("aaaqeayeaudaocaj", 7777)
|
||||
val tor3 = Tor3("aaaqeayeaudaocajbifqydiob4ibceqtcqkrmfyydenbwha5dypsaijc", 9999)
|
||||
|
||||
Serialization.write(ipv4)(org.json4s.DefaultFormats + new NodeAddressSerializer) shouldBe s""""10.0.0.1:8888""""
|
||||
Serialization.write(ipv6LocalHost)(org.json4s.DefaultFormats + new NodeAddressSerializer) shouldBe s""""[0:0:0:0:0:0:0:1]:9735""""
|
||||
Serialization.write(tor2)(org.json4s.DefaultFormats + new NodeAddressSerializer) shouldBe s""""aaaqeayeaudaocaj.onion:7777""""
|
||||
Serialization.write(tor3)(org.json4s.DefaultFormats + new NodeAddressSerializer) shouldBe s""""aaaqeayeaudaocajbifqydiob4ibceqtcqkrmfyydenbwha5dypsaijc.onion:9999""""
|
||||
JsonSupport.serialization.write(ipv4)(org.json4s.DefaultFormats + new NodeAddressSerializer) shouldBe s""""10.0.0.1:8888""""
|
||||
JsonSupport.serialization.write(ipv6LocalHost)(org.json4s.DefaultFormats + new NodeAddressSerializer) shouldBe s""""[0:0:0:0:0:0:0:1]:9735""""
|
||||
JsonSupport.serialization.write(tor2)(org.json4s.DefaultFormats + new NodeAddressSerializer) shouldBe s""""aaaqeayeaudaocaj.onion:7777""""
|
||||
JsonSupport.serialization.write(tor3)(org.json4s.DefaultFormats + new NodeAddressSerializer) shouldBe s""""aaaqeayeaudaocajbifqydiob4ibceqtcqkrmfyydenbwha5dypsaijc.onion:9999""""
|
||||
}
|
||||
|
||||
test("Direction serialization") {
|
||||
Serialization.write(IN)(org.json4s.DefaultFormats + new DirectionSerializer) shouldBe s""""IN""""
|
||||
Serialization.write(OUT)(org.json4s.DefaultFormats + new DirectionSerializer) shouldBe s""""OUT""""
|
||||
JsonSupport.serialization.write(IN)(org.json4s.DefaultFormats + new DirectionSerializer) shouldBe s""""IN""""
|
||||
JsonSupport.serialization.write(OUT)(org.json4s.DefaultFormats + new DirectionSerializer) shouldBe s""""OUT""""
|
||||
}
|
||||
|
||||
test("Payment Request") {
|
||||
|
@ -78,7 +75,7 @@ class JsonSerializersSpec extends FunSuite with Matchers {
|
|||
test("type hints") {
|
||||
implicit val formats = JsonSupport.json4sJacksonFormats.withTypeHintFieldName("type") + CustomTypeHints(Map(classOf[PaymentSettlingOnChain] -> "payment-settling-onchain")) + new MilliSatoshiSerializer
|
||||
val e1 = PaymentSettlingOnChain(UUID.randomUUID, 42 msat, randomBytes32)
|
||||
assert(Serialization.writePretty(e1).contains("\"type\" : \"payment-settling-onchain\""))
|
||||
assert(JsonSupport.serialization.writePretty(e1).contains("\"type\" : \"payment-settling-onchain\""))
|
||||
}
|
||||
|
||||
test("transaction serializer") {
|
||||
|
|
7
pom.xml
7
pom.xml
|
@ -64,8 +64,8 @@
|
|||
<maven.compiler.target>1.7</maven.compiler.target>
|
||||
<scala.version>2.11.12</scala.version>
|
||||
<scala.version.short>2.11</scala.version.short>
|
||||
<akka.version>2.3.14</akka.version>
|
||||
<akka.http.version>10.0.11</akka.http.version>
|
||||
<akka.version>2.3.14</akka.version>
|
||||
<akka.http.version>10.0.11</akka.http.version>
|
||||
<sttp.version>1.3.9</sttp.version>
|
||||
<bitcoinlib.version>0.15</bitcoinlib.version>
|
||||
<guava.version>24.0-android</guava.version>
|
||||
|
@ -221,7 +221,8 @@
|
|||
<systemProperties>
|
||||
<buildDirectory>${project.build.directory}</buildDirectory>
|
||||
</systemProperties>
|
||||
<argLine>-Xmx1024m -Dfile.encoding=UTF-8</argLine>
|
||||
<argLine>-Xmx1024m</argLine>
|
||||
<argLine>-Dfile.encoding=UTF-8</argLine>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
|
|
Loading…
Add table
Reference in a new issue