mirror of
https://github.com/ACINQ/eclair.git
synced 2025-02-23 22:46:44 +01:00
Replace FeatureScope
by type hierarchy (#2207)
Instead of defining a separate type `FeatureScope` with its own hierarchy, that was then mixed in `Feature` using the cake pattern, we go with a simpler type hierarchy for `Feature`. This significantly simplifies type declarations (no more `Feature with FeatureScope`) especially in the tests.
This commit is contained in:
parent
f14300e33f
commit
7b5cefaf99
22 changed files with 118 additions and 119 deletions
|
@ -60,7 +60,7 @@ import scala.collection.immutable.SortedMap
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.{ExecutionContext, Future, Promise}
|
import scala.concurrent.{ExecutionContext, Future, Promise}
|
||||||
|
|
||||||
case class GetInfoResponse(version: String, nodeId: PublicKey, alias: String, color: String, features: Features[FeatureScope], chainHash: ByteVector32, network: String, blockHeight: Int, publicAddresses: Seq[NodeAddress], onionAddress: Option[NodeAddress], instanceId: String)
|
case class GetInfoResponse(version: String, nodeId: PublicKey, alias: String, color: String, features: Features[Feature], chainHash: ByteVector32, network: String, blockHeight: Int, publicAddresses: Seq[NodeAddress], onionAddress: Option[NodeAddress], instanceId: String)
|
||||||
|
|
||||||
case class AuditResponse(sent: Seq[PaymentSent], received: Seq[PaymentReceived], relayed: Seq[PaymentRelayed])
|
case class AuditResponse(sent: Seq[PaymentSent], received: Seq[PaymentReceived], relayed: Seq[PaymentRelayed])
|
||||||
|
|
||||||
|
|
|
@ -34,10 +34,9 @@ object FeatureSupport {
|
||||||
case object Optional extends FeatureSupport { override def toString: String = "optional" }
|
case object Optional extends FeatureSupport { override def toString: String = "optional" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Not a sealed trait, so it can be extended by plugins. */
|
||||||
trait Feature {
|
trait Feature {
|
||||||
|
|
||||||
this: FeatureScope =>
|
|
||||||
|
|
||||||
def rfcName: String
|
def rfcName: String
|
||||||
def mandatory: Int
|
def mandatory: Int
|
||||||
def optional: Int = mandatory + 1
|
def optional: Int = mandatory + 1
|
||||||
|
@ -48,24 +47,22 @@ trait Feature {
|
||||||
}
|
}
|
||||||
|
|
||||||
override def toString = rfcName
|
override def toString = rfcName
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Feature scope as defined in Bolt 9. */
|
/** Feature scope as defined in Bolt 9. */
|
||||||
sealed trait FeatureScope
|
|
||||||
/** Feature that should be advertised in init messages. */
|
/** Feature that should be advertised in init messages. */
|
||||||
trait InitFeature extends FeatureScope
|
trait InitFeature extends Feature
|
||||||
/** Feature that should be advertised in node announcements. */
|
/** Feature that should be advertised in node announcements. */
|
||||||
trait NodeFeature extends FeatureScope
|
trait NodeFeature extends Feature
|
||||||
/** Feature that should be advertised in invoices. */
|
/** Feature that should be advertised in invoices. */
|
||||||
trait InvoiceFeature extends FeatureScope
|
trait InvoiceFeature extends Feature
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
|
||||||
case class UnknownFeature(bitIndex: Int)
|
case class UnknownFeature(bitIndex: Int)
|
||||||
|
|
||||||
case class Features[T <: FeatureScope](activated: Map[Feature with T, FeatureSupport], unknown: Set[UnknownFeature] = Set.empty) {
|
case class Features[T <: Feature](activated: Map[T, FeatureSupport], unknown: Set[UnknownFeature] = Set.empty) {
|
||||||
|
|
||||||
def hasFeature(feature: Feature with T, support: Option[FeatureSupport] = None): Boolean = support match {
|
def hasFeature(feature: T, support: Option[FeatureSupport] = None): Boolean = support match {
|
||||||
case Some(s) => activated.get(feature).contains(s)
|
case Some(s) => activated.get(feature).contains(s)
|
||||||
case None => activated.contains(feature)
|
case None => activated.contains(feature)
|
||||||
}
|
}
|
||||||
|
@ -84,13 +81,13 @@ case class Features[T <: FeatureScope](activated: Map[Feature with T, FeatureSup
|
||||||
unknownFeaturesOk && knownFeaturesOk
|
unknownFeaturesOk && knownFeaturesOk
|
||||||
}
|
}
|
||||||
|
|
||||||
def initFeatures(): Features[InitFeature] = Features[InitFeature](activated.collect { case (f: InitFeature, s) => (f, s) }, unknown)
|
def initFeatures(): Features[InitFeature] = Features(activated.collect { case (f: InitFeature, s) => (f, s) }, unknown)
|
||||||
|
|
||||||
def nodeAnnouncementFeatures(): Features[NodeFeature] = Features[NodeFeature](activated.collect { case (f: NodeFeature, s) => (f, s) }, unknown)
|
def nodeAnnouncementFeatures(): Features[NodeFeature] = Features(activated.collect { case (f: NodeFeature, s) => (f, s) }, unknown)
|
||||||
|
|
||||||
def invoiceFeatures(): Features[InvoiceFeature] = Features[InvoiceFeature](activated.collect { case (f: InvoiceFeature, s) => (f, s) }, unknown)
|
def invoiceFeatures(): Features[InvoiceFeature] = Features(activated.collect { case (f: InvoiceFeature, s) => (f, s) }, unknown)
|
||||||
|
|
||||||
def unscoped(): Features[FeatureScope] = Features[FeatureScope](activated.collect { case (f, s) => (f: Feature with FeatureScope, s) }, unknown)
|
def unscoped(): Features[Feature] = Features[Feature](activated.collect { case (f, s) => (f: Feature, s) }, unknown)
|
||||||
|
|
||||||
def toByteVector: ByteVector = {
|
def toByteVector: ByteVector = {
|
||||||
val activatedFeatureBytes = toByteVectorFromIndex(activated.map { case (feature, support) => feature.supportBit(support) }.toSet)
|
val activatedFeatureBytes = toByteVectorFromIndex(activated.map { case (feature, support) => feature.supportBit(support) }.toSet)
|
||||||
|
@ -116,28 +113,28 @@ case class Features[T <: FeatureScope](activated: Map[Feature with T, FeatureSup
|
||||||
|
|
||||||
object Features {
|
object Features {
|
||||||
|
|
||||||
def empty[T <: FeatureScope]: Features[T] = Features[T](Map.empty[Feature with T, FeatureSupport])
|
def empty[T <: Feature]: Features[T] = Features[T](Map.empty[T, FeatureSupport])
|
||||||
|
|
||||||
def apply[T <: FeatureScope](features: (Feature with T, FeatureSupport)*): Features[T] = Features[T](Map.from(features))
|
def apply[T <: Feature](features: (T, FeatureSupport)*): Features[T] = Features[T](Map.from(features))
|
||||||
|
|
||||||
def apply(bytes: ByteVector): Features[FeatureScope] = apply(bytes.bits)
|
def apply(bytes: ByteVector): Features[Feature] = apply(bytes.bits)
|
||||||
|
|
||||||
def apply(bits: BitVector): Features[FeatureScope] = {
|
def apply(bits: BitVector): Features[Feature] = {
|
||||||
val all = bits.toIndexedSeq.reverse.zipWithIndex.collect {
|
val all = bits.toIndexedSeq.reverse.zipWithIndex.collect {
|
||||||
case (true, idx) if knownFeatures.exists(_.optional == idx) => Right((knownFeatures.find(_.optional == idx).get, Optional))
|
case (true, idx) if knownFeatures.exists(_.optional == idx) => Right((knownFeatures.find(_.optional == idx).get, Optional))
|
||||||
case (true, idx) if knownFeatures.exists(_.mandatory == idx) => Right((knownFeatures.find(_.mandatory == idx).get, Mandatory))
|
case (true, idx) if knownFeatures.exists(_.mandatory == idx) => Right((knownFeatures.find(_.mandatory == idx).get, Mandatory))
|
||||||
case (true, idx) => Left(UnknownFeature(idx))
|
case (true, idx) => Left(UnknownFeature(idx))
|
||||||
}
|
}
|
||||||
Features[FeatureScope](
|
Features[Feature](
|
||||||
activated = all.collect { case Right((feature, support)) => feature -> support }.toMap,
|
activated = all.collect { case Right((feature, support)) => feature -> support }.toMap,
|
||||||
unknown = all.collect { case Left(inf) => inf }.toSet
|
unknown = all.collect { case Left(inf) => inf }.toSet
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def fromConfiguration[T <: FeatureScope](config: Config, validFeatures: Set[Feature with T]): Features[T] = Features[T](
|
def fromConfiguration[T <: Feature](config: Config, validFeatures: Set[T]): Features[T] = Features[T](
|
||||||
config.root().entrySet().asScala.flatMap { entry =>
|
config.root().entrySet().asScala.flatMap { entry =>
|
||||||
val featureName = entry.getKey
|
val featureName = entry.getKey
|
||||||
val feature: Feature with T = validFeatures.find(_.rfcName == featureName).getOrElse(throw new IllegalArgumentException(s"Invalid feature name ($featureName)"))
|
val feature: T = validFeatures.find(_.rfcName == featureName).getOrElse(throw new IllegalArgumentException(s"Invalid feature name ($featureName)"))
|
||||||
config.getString(featureName) match {
|
config.getString(featureName) match {
|
||||||
case support if support == Mandatory.toString => Some(feature -> Mandatory)
|
case support if support == Mandatory.toString => Some(feature -> Mandatory)
|
||||||
case support if support == Optional.toString => Some(feature -> Optional)
|
case support if support == Optional.toString => Some(feature -> Optional)
|
||||||
|
@ -146,7 +143,7 @@ object Features {
|
||||||
}
|
}
|
||||||
}.toMap)
|
}.toMap)
|
||||||
|
|
||||||
def fromConfiguration(config: Config): Features[FeatureScope] = fromConfiguration[FeatureScope](config, knownFeatures)
|
def fromConfiguration(config: Config): Features[Feature] = fromConfiguration[Feature](config, knownFeatures)
|
||||||
|
|
||||||
case object DataLossProtect extends Feature with InitFeature with NodeFeature {
|
case object DataLossProtect extends Feature with InitFeature with NodeFeature {
|
||||||
val rfcName = "option_data_loss_protect"
|
val rfcName = "option_data_loss_protect"
|
||||||
|
@ -242,7 +239,7 @@ object Features {
|
||||||
val mandatory = 54
|
val mandatory = 54
|
||||||
}
|
}
|
||||||
|
|
||||||
val knownFeatures: Set[Feature with FeatureScope] = Set(
|
val knownFeatures: Set[Feature] = Set(
|
||||||
DataLossProtect,
|
DataLossProtect,
|
||||||
InitialRoutingSync,
|
InitialRoutingSync,
|
||||||
UpfrontShutdownScript,
|
UpfrontShutdownScript,
|
||||||
|
@ -278,16 +275,16 @@ object Features {
|
||||||
|
|
||||||
case class FeatureException(message: String) extends IllegalArgumentException(message)
|
case class FeatureException(message: String) extends IllegalArgumentException(message)
|
||||||
|
|
||||||
def validateFeatureGraph[T <: FeatureScope](features: Features[T]): Option[FeatureException] = featuresDependency.collectFirst {
|
def validateFeatureGraph[T <: Feature](features: Features[T]): Option[FeatureException] = featuresDependency.collectFirst {
|
||||||
case (feature, dependencies) if features.unscoped().hasFeature(feature) && dependencies.exists(d => !features.unscoped().hasFeature(d)) =>
|
case (feature, dependencies) if features.unscoped().hasFeature(feature) && dependencies.exists(d => !features.unscoped().hasFeature(d)) =>
|
||||||
FeatureException(s"$feature is set but is missing a dependency (${dependencies.filter(d => !features.unscoped().hasFeature(d)).mkString(" and ")})")
|
FeatureException(s"$feature is set but is missing a dependency (${dependencies.filter(d => !features.unscoped().hasFeature(d)).mkString(" and ")})")
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns true if both feature sets are compatible. */
|
/** Returns true if both feature sets are compatible. */
|
||||||
def areCompatible[T <: FeatureScope](ours: Features[T], theirs: Features[T]): Boolean = ours.areSupported(theirs) && theirs.areSupported(ours)
|
def areCompatible[T <: Feature](ours: Features[T], theirs: Features[T]): Boolean = ours.areSupported(theirs) && theirs.areSupported(ours)
|
||||||
|
|
||||||
/** returns true if both have at least optional support */
|
/** returns true if both have at least optional support */
|
||||||
def canUseFeature[T <: FeatureScope](localFeatures: Features[T], remoteFeatures: Features[T], feature: Feature with T): Boolean = {
|
def canUseFeature[T <: Feature](localFeatures: Features[T], remoteFeatures: Features[T], feature: T): Boolean = {
|
||||||
localFeatures.hasFeature(feature) && remoteFeatures.hasFeature(feature)
|
localFeatures.hasFeature(feature) && remoteFeatures.hasFeature(feature)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,7 @@ case class NodeParams(nodeKeyManager: NodeKeyManager,
|
||||||
color: Color,
|
color: Color,
|
||||||
publicAddresses: List[NodeAddress],
|
publicAddresses: List[NodeAddress],
|
||||||
torAddress_opt: Option[NodeAddress],
|
torAddress_opt: Option[NodeAddress],
|
||||||
features: Features[FeatureScope],
|
features: Features[Feature],
|
||||||
private val overrideInitFeatures: Map[PublicKey, Features[InitFeature]],
|
private val overrideInitFeatures: Map[PublicKey, Features[InitFeature]],
|
||||||
syncWhitelist: Set[PublicKey],
|
syncWhitelist: Set[PublicKey],
|
||||||
pluginParams: Seq[PluginParams],
|
pluginParams: Seq[PluginParams],
|
||||||
|
@ -271,7 +271,7 @@ object NodeParams extends Logging {
|
||||||
val nodeAlias = config.getString("node-alias")
|
val nodeAlias = config.getString("node-alias")
|
||||||
require(nodeAlias.getBytes("UTF-8").length <= 32, "invalid alias, too long (max allowed 32 bytes)")
|
require(nodeAlias.getBytes("UTF-8").length <= 32, "invalid alias, too long (max allowed 32 bytes)")
|
||||||
|
|
||||||
def validateFeatures(features: Features[FeatureScope]): Unit = {
|
def validateFeatures(features: Features[Feature]): Unit = {
|
||||||
val featuresErr = Features.validateFeatureGraph(features)
|
val featuresErr = Features.validateFeatureGraph(features)
|
||||||
require(featuresErr.isEmpty, featuresErr.map(_.message))
|
require(featuresErr.isEmpty, featuresErr.map(_.message))
|
||||||
require(features.hasFeature(Features.VariableLengthOnion, Some(FeatureSupport.Mandatory)), s"${Features.VariableLengthOnion.rfcName} must be enabled and mandatory")
|
require(features.hasFeature(Features.VariableLengthOnion, Some(FeatureSupport.Mandatory)), s"${Features.VariableLengthOnion.rfcName} must be enabled and mandatory")
|
||||||
|
@ -291,11 +291,11 @@ object NodeParams extends Logging {
|
||||||
require(Features.knownFeatures.map(_.mandatory).intersect(pluginFeatureSet).isEmpty, "Plugin feature bit overlaps with known feature bit")
|
require(Features.knownFeatures.map(_.mandatory).intersect(pluginFeatureSet).isEmpty, "Plugin feature bit overlaps with known feature bit")
|
||||||
require(pluginFeatureSet.size == pluginMessageParams.size, "Duplicate plugin feature bits found")
|
require(pluginFeatureSet.size == pluginMessageParams.size, "Duplicate plugin feature bits found")
|
||||||
|
|
||||||
val coreAndPluginFeatures: Features[FeatureScope] = features.copy(unknown = features.unknown ++ pluginMessageParams.map(_.pluginFeature))
|
val coreAndPluginFeatures: Features[Feature] = features.copy(unknown = features.unknown ++ pluginMessageParams.map(_.pluginFeature))
|
||||||
|
|
||||||
val overrideInitFeatures: Map[PublicKey, Features[InitFeature]] = config.getConfigList("override-init-features").asScala.map { e =>
|
val overrideInitFeatures: Map[PublicKey, Features[InitFeature]] = config.getConfigList("override-init-features").asScala.map { e =>
|
||||||
val p = PublicKey(ByteVector.fromValidHex(e.getString("nodeid")))
|
val p = PublicKey(ByteVector.fromValidHex(e.getString("nodeid")))
|
||||||
val f = Features.fromConfiguration[InitFeature](e.getConfig("features"), Features.knownFeatures.collect { case f: Feature with InitFeature => f })
|
val f = Features.fromConfiguration[InitFeature](e.getConfig("features"), Features.knownFeatures.collect { case f: InitFeature => f })
|
||||||
validateFeatures(f.unscoped())
|
validateFeatures(f.unscoped())
|
||||||
p -> (f.copy(unknown = f.unknown ++ pluginMessageParams.map(_.pluginFeature)): Features[InitFeature])
|
p -> (f.copy(unknown = f.unknown ++ pluginMessageParams.map(_.pluginFeature)): Features[InitFeature])
|
||||||
}.toMap
|
}.toMap
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
package fr.acinq.eclair.channel
|
package fr.acinq.eclair.channel
|
||||||
|
|
||||||
import fr.acinq.eclair.transactions.Transactions.{CommitmentFormat, DefaultCommitmentFormat, UnsafeLegacyAnchorOutputsCommitmentFormat, ZeroFeeHtlcTxAnchorOutputsCommitmentFormat}
|
import fr.acinq.eclair.transactions.Transactions.{CommitmentFormat, DefaultCommitmentFormat, UnsafeLegacyAnchorOutputsCommitmentFormat, ZeroFeeHtlcTxAnchorOutputsCommitmentFormat}
|
||||||
import fr.acinq.eclair.{Feature, FeatureScope, FeatureSupport, Features, InitFeature}
|
import fr.acinq.eclair.{FeatureSupport, Features, InitFeature, Feature}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by t-bast on 24/06/2021.
|
* Created by t-bast on 24/06/2021.
|
||||||
|
@ -28,7 +28,7 @@ import fr.acinq.eclair.{Feature, FeatureScope, FeatureSupport, Features, InitFea
|
||||||
* Even if one of these features is later disabled at the connection level, it will still apply to the channel until the
|
* Even if one of these features is later disabled at the connection level, it will still apply to the channel until the
|
||||||
* channel is upgraded or closed.
|
* channel is upgraded or closed.
|
||||||
*/
|
*/
|
||||||
case class ChannelFeatures(features: Set[Feature with FeatureScope]) {
|
case class ChannelFeatures(features: Set[Feature]) {
|
||||||
|
|
||||||
val channelType: SupportedChannelType = {
|
val channelType: SupportedChannelType = {
|
||||||
if (hasFeature(Features.AnchorOutputsZeroFeeHtlcTx)) {
|
if (hasFeature(Features.AnchorOutputsZeroFeeHtlcTx)) {
|
||||||
|
@ -45,7 +45,7 @@ case class ChannelFeatures(features: Set[Feature with FeatureScope]) {
|
||||||
val paysDirectlyToWallet: Boolean = channelType.paysDirectlyToWallet
|
val paysDirectlyToWallet: Boolean = channelType.paysDirectlyToWallet
|
||||||
val commitmentFormat: CommitmentFormat = channelType.commitmentFormat
|
val commitmentFormat: CommitmentFormat = channelType.commitmentFormat
|
||||||
|
|
||||||
def hasFeature(feature: Feature with FeatureScope): Boolean = features.contains(feature)
|
def hasFeature(feature: Feature): Boolean = features.contains(feature)
|
||||||
|
|
||||||
override def toString: String = features.mkString(",")
|
override def toString: String = features.mkString(",")
|
||||||
|
|
||||||
|
@ -53,13 +53,13 @@ case class ChannelFeatures(features: Set[Feature with FeatureScope]) {
|
||||||
|
|
||||||
object ChannelFeatures {
|
object ChannelFeatures {
|
||||||
|
|
||||||
def apply(features: (Feature with FeatureScope)*): ChannelFeatures = ChannelFeatures(Set.from(features))
|
def apply(features: Feature*): ChannelFeatures = ChannelFeatures(Set.from(features))
|
||||||
|
|
||||||
/** Enrich the channel type with other permanent features that will be applied to the channel. */
|
/** Enrich the channel type with other permanent features that will be applied to the channel. */
|
||||||
def apply(channelType: ChannelType, localFeatures: Features[InitFeature], remoteFeatures: Features[InitFeature]): ChannelFeatures = {
|
def apply(channelType: ChannelType, localFeatures: Features[InitFeature], remoteFeatures: Features[InitFeature]): ChannelFeatures = {
|
||||||
// NB: we don't include features that can be safely activated/deactivated without impacting the channel's operation,
|
// NB: we don't include features that can be safely activated/deactivated without impacting the channel's operation,
|
||||||
// such as option_dataloss_protect or option_shutdown_anysegwit.
|
// such as option_dataloss_protect or option_shutdown_anysegwit.
|
||||||
val availableFeatures: Seq[Feature with FeatureScope] = Seq(Features.Wumbo, Features.UpfrontShutdownScript).filter(f => Features.canUseFeature(localFeatures, remoteFeatures, f))
|
val availableFeatures = Seq(Features.Wumbo, Features.UpfrontShutdownScript).filter(f => Features.canUseFeature(localFeatures, remoteFeatures, f))
|
||||||
val allFeatures = channelType.features.toSeq ++ availableFeatures
|
val allFeatures = channelType.features.toSeq ++ availableFeatures
|
||||||
ChannelFeatures(allFeatures: _*)
|
ChannelFeatures(allFeatures: _*)
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ object ChannelFeatures {
|
||||||
/** A channel type is a specific set of even feature bits that represent persistent channel features as defined in Bolt 2. */
|
/** A channel type is a specific set of even feature bits that represent persistent channel features as defined in Bolt 2. */
|
||||||
sealed trait ChannelType {
|
sealed trait ChannelType {
|
||||||
/** Features representing that channel type. */
|
/** Features representing that channel type. */
|
||||||
def features: Set[Feature with InitFeature]
|
def features: Set[InitFeature]
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed trait SupportedChannelType extends ChannelType {
|
sealed trait SupportedChannelType extends ChannelType {
|
||||||
|
@ -84,31 +84,31 @@ object ChannelTypes {
|
||||||
|
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
case object Standard extends SupportedChannelType {
|
case object Standard extends SupportedChannelType {
|
||||||
override def features: Set[Feature with InitFeature] = Set.empty
|
override def features: Set[InitFeature] = Set.empty
|
||||||
override def paysDirectlyToWallet: Boolean = false
|
override def paysDirectlyToWallet: Boolean = false
|
||||||
override def commitmentFormat: CommitmentFormat = DefaultCommitmentFormat
|
override def commitmentFormat: CommitmentFormat = DefaultCommitmentFormat
|
||||||
override def toString: String = "standard"
|
override def toString: String = "standard"
|
||||||
}
|
}
|
||||||
case object StaticRemoteKey extends SupportedChannelType {
|
case object StaticRemoteKey extends SupportedChannelType {
|
||||||
override def features: Set[Feature with InitFeature] = Set(Features.StaticRemoteKey)
|
override def features: Set[InitFeature] = Set(Features.StaticRemoteKey)
|
||||||
override def paysDirectlyToWallet: Boolean = true
|
override def paysDirectlyToWallet: Boolean = true
|
||||||
override def commitmentFormat: CommitmentFormat = DefaultCommitmentFormat
|
override def commitmentFormat: CommitmentFormat = DefaultCommitmentFormat
|
||||||
override def toString: String = "static_remotekey"
|
override def toString: String = "static_remotekey"
|
||||||
}
|
}
|
||||||
case object AnchorOutputs extends SupportedChannelType {
|
case object AnchorOutputs extends SupportedChannelType {
|
||||||
override def features: Set[Feature with InitFeature] = Set(Features.StaticRemoteKey, Features.AnchorOutputs)
|
override def features: Set[InitFeature] = Set(Features.StaticRemoteKey, Features.AnchorOutputs)
|
||||||
override def paysDirectlyToWallet: Boolean = false
|
override def paysDirectlyToWallet: Boolean = false
|
||||||
override def commitmentFormat: CommitmentFormat = UnsafeLegacyAnchorOutputsCommitmentFormat
|
override def commitmentFormat: CommitmentFormat = UnsafeLegacyAnchorOutputsCommitmentFormat
|
||||||
override def toString: String = "anchor_outputs"
|
override def toString: String = "anchor_outputs"
|
||||||
}
|
}
|
||||||
case object AnchorOutputsZeroFeeHtlcTx extends SupportedChannelType {
|
case object AnchorOutputsZeroFeeHtlcTx extends SupportedChannelType {
|
||||||
override def features: Set[Feature with InitFeature] = Set(Features.StaticRemoteKey, Features.AnchorOutputsZeroFeeHtlcTx)
|
override def features: Set[InitFeature] = Set(Features.StaticRemoteKey, Features.AnchorOutputsZeroFeeHtlcTx)
|
||||||
override def paysDirectlyToWallet: Boolean = false
|
override def paysDirectlyToWallet: Boolean = false
|
||||||
override def commitmentFormat: CommitmentFormat = ZeroFeeHtlcTxAnchorOutputsCommitmentFormat
|
override def commitmentFormat: CommitmentFormat = ZeroFeeHtlcTxAnchorOutputsCommitmentFormat
|
||||||
override def toString: String = "anchor_outputs_zero_fee_htlc_tx"
|
override def toString: String = "anchor_outputs_zero_fee_htlc_tx"
|
||||||
}
|
}
|
||||||
case class UnsupportedChannelType(featureBits: Features[InitFeature]) extends ChannelType {
|
case class UnsupportedChannelType(featureBits: Features[InitFeature]) extends ChannelType {
|
||||||
override def features: Set[Feature with InitFeature] = featureBits.activated.keySet
|
override def features: Set[InitFeature] = featureBits.activated.keySet
|
||||||
override def toString: String = s"0x${featureBits.toByteVector.toHex}"
|
override def toString: String = s"0x${featureBits.toByteVector.toHex}"
|
||||||
}
|
}
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
|
|
@ -225,7 +225,7 @@ object Helpers {
|
||||||
}
|
}
|
||||||
|
|
||||||
def makeAnnouncementSignatures(nodeParams: NodeParams, commitments: Commitments, shortChannelId: ShortChannelId): AnnouncementSignatures = {
|
def makeAnnouncementSignatures(nodeParams: NodeParams, commitments: Commitments, shortChannelId: ShortChannelId): AnnouncementSignatures = {
|
||||||
val features = Features.empty[FeatureScope] // empty features for now
|
val features = Features.empty[Feature] // empty features for now
|
||||||
val fundingPubKey = nodeParams.channelKeyManager.fundingPublicKey(commitments.localParams.fundingKeyPath)
|
val fundingPubKey = nodeParams.channelKeyManager.fundingPublicKey(commitments.localParams.fundingKeyPath)
|
||||||
val witness = Announcements.generateChannelAnnouncementWitness(
|
val witness = Announcements.generateChannelAnnouncementWitness(
|
||||||
nodeParams.chainHash,
|
nodeParams.chainHash,
|
||||||
|
|
|
@ -28,7 +28,7 @@ import fr.acinq.eclair.remote.EclairInternalsSerializer.RemoteTypes
|
||||||
import fr.acinq.eclair.router.Router._
|
import fr.acinq.eclair.router.Router._
|
||||||
import fr.acinq.eclair.wire.protocol
|
import fr.acinq.eclair.wire.protocol
|
||||||
import fr.acinq.eclair.wire.protocol._
|
import fr.acinq.eclair.wire.protocol._
|
||||||
import fr.acinq.eclair.{FSMDiagnosticActorLogging, FeatureScope, Features, InitFeature, Logs, TimestampMilli, TimestampSecond}
|
import fr.acinq.eclair.{FSMDiagnosticActorLogging, Feature, Features, InitFeature, Logs, TimestampMilli, TimestampSecond}
|
||||||
import scodec.Attempt
|
import scodec.Attempt
|
||||||
import scodec.bits.ByteVector
|
import scodec.bits.ByteVector
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ import fr.acinq.eclair.transactions.DirectedHtlc
|
||||||
import fr.acinq.eclair.transactions.Transactions._
|
import fr.acinq.eclair.transactions.Transactions._
|
||||||
import fr.acinq.eclair.wire.protocol.MessageOnionCodecs.blindedRouteCodec
|
import fr.acinq.eclair.wire.protocol.MessageOnionCodecs.blindedRouteCodec
|
||||||
import fr.acinq.eclair.wire.protocol._
|
import fr.acinq.eclair.wire.protocol._
|
||||||
import fr.acinq.eclair.{CltvExpiry, CltvExpiryDelta, Feature, FeatureSupport, MilliSatoshi, ShortChannelId, TimestampMilli, TimestampSecond, UInt64, UnknownFeature}
|
import fr.acinq.eclair.{CltvExpiry, CltvExpiryDelta, FeatureSupport, Feature, MilliSatoshi, ShortChannelId, TimestampMilli, TimestampSecond, UInt64, UnknownFeature}
|
||||||
import org.json4s
|
import org.json4s
|
||||||
import org.json4s.JsonAST._
|
import org.json4s.JsonAST._
|
||||||
import org.json4s.jackson.Serialization
|
import org.json4s.jackson.Serialization
|
||||||
|
|
|
@ -18,7 +18,7 @@ package fr.acinq.eclair.payment
|
||||||
|
|
||||||
import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey}
|
import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey}
|
||||||
import fr.acinq.bitcoin.{Base58, Base58Check, Bech32, Block, ByteVector32, ByteVector64, Crypto}
|
import fr.acinq.bitcoin.{Base58, Base58Check, Bech32, Block, ByteVector32, ByteVector64, Crypto}
|
||||||
import fr.acinq.eclair.{CltvExpiryDelta, FeatureScope, FeatureSupport, Features, InvoiceFeature, MilliSatoshi, MilliSatoshiLong, ShortChannelId, TimestampSecond, randomBytes32}
|
import fr.acinq.eclair.{CltvExpiryDelta, Feature, FeatureSupport, Features, InvoiceFeature, MilliSatoshi, MilliSatoshiLong, ShortChannelId, TimestampSecond, randomBytes32}
|
||||||
import scodec.bits.{BitVector, ByteOrdering, ByteVector}
|
import scodec.bits.{BitVector, ByteOrdering, ByteVector}
|
||||||
import scodec.codecs.{list, ubyte}
|
import scodec.codecs.{list, ubyte}
|
||||||
import scodec.{Codec, Err}
|
import scodec.{Codec, Err}
|
||||||
|
@ -299,7 +299,7 @@ object Bolt11Invoice {
|
||||||
* This returns a bitvector with the minimum size necessary to encode the features, left padded to have a length (in
|
* This returns a bitvector with the minimum size necessary to encode the features, left padded to have a length (in
|
||||||
* bits) that is a multiple of 5.
|
* bits) that is a multiple of 5.
|
||||||
*/
|
*/
|
||||||
def features2bits[T <: FeatureScope](features: Features[T]): BitVector = leftPaddedBits(features.toByteVector.bits)
|
def features2bits[T <: Feature](features: Features[T]): BitVector = leftPaddedBits(features.toByteVector.bits)
|
||||||
|
|
||||||
private def leftPaddedBits(bits: BitVector): BitVector = {
|
private def leftPaddedBits(bits: BitVector): BitVector = {
|
||||||
var highest = -1
|
var highest = -1
|
||||||
|
@ -365,7 +365,7 @@ object Bolt11Invoice {
|
||||||
/**
|
/**
|
||||||
* Features supported or required for receiving this payment.
|
* Features supported or required for receiving this payment.
|
||||||
*/
|
*/
|
||||||
case class InvoiceFeatures(features: Features[FeatureScope]) extends TaggedField
|
case class InvoiceFeatures(features: Features[Feature]) extends TaggedField
|
||||||
|
|
||||||
object Codecs {
|
object Codecs {
|
||||||
|
|
||||||
|
@ -408,7 +408,7 @@ object Bolt11Invoice {
|
||||||
.typecase(2, dataCodec(bits).as[UnknownTag2])
|
.typecase(2, dataCodec(bits).as[UnknownTag2])
|
||||||
.typecase(3, dataCodec(listOfN(extraHopsLengthCodec, extraHopCodec)).as[RoutingInfo])
|
.typecase(3, dataCodec(listOfN(extraHopsLengthCodec, extraHopCodec)).as[RoutingInfo])
|
||||||
.typecase(4, dataCodec(bits).as[UnknownTag4])
|
.typecase(4, dataCodec(bits).as[UnknownTag4])
|
||||||
.typecase(5, dataCodec(bits).xmap[Features[FeatureScope]](Features(_), features2bits).as[InvoiceFeatures])
|
.typecase(5, dataCodec(bits).xmap[Features[Feature]](Features(_), features2bits).as[InvoiceFeatures])
|
||||||
.typecase(6, dataCodec(bits).as[Expiry])
|
.typecase(6, dataCodec(bits).as[Expiry])
|
||||||
.typecase(7, dataCodec(bits).as[UnknownTag7])
|
.typecase(7, dataCodec(bits).as[UnknownTag7])
|
||||||
.typecase(8, dataCodec(bits).as[UnknownTag8])
|
.typecase(8, dataCodec(bits).as[UnknownTag8])
|
||||||
|
|
|
@ -31,7 +31,7 @@ import fr.acinq.eclair.wire.protocol.CommonCodecs._
|
||||||
import fr.acinq.eclair.wire.protocol.LightningMessageCodecs._
|
import fr.acinq.eclair.wire.protocol.LightningMessageCodecs._
|
||||||
import fr.acinq.eclair.wire.protocol.QueryChannelRangeTlv.queryFlagsCodec
|
import fr.acinq.eclair.wire.protocol.QueryChannelRangeTlv.queryFlagsCodec
|
||||||
import fr.acinq.eclair.wire.protocol._
|
import fr.acinq.eclair.wire.protocol._
|
||||||
import fr.acinq.eclair.{CltvExpiryDelta, FeatureScope, Features, InitFeature}
|
import fr.acinq.eclair.{CltvExpiryDelta, Feature, Features, InitFeature}
|
||||||
import scodec._
|
import scodec._
|
||||||
import scodec.codecs._
|
import scodec.codecs._
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ object EclairInternalsSerializer {
|
||||||
("channelQueryChunkSize" | int32) ::
|
("channelQueryChunkSize" | int32) ::
|
||||||
("pathFindingExperimentConf" | pathFindingExperimentConfCodec)).as[RouterConf]
|
("pathFindingExperimentConf" | pathFindingExperimentConfCodec)).as[RouterConf]
|
||||||
|
|
||||||
val overrideFeaturesListCodec: Codec[List[(PublicKey, Features[FeatureScope])]] = listOfN(uint16, publicKey ~ variableSizeBytes(uint16, featuresCodec))
|
val overrideFeaturesListCodec: Codec[List[(PublicKey, Features[Feature])]] = listOfN(uint16, publicKey ~ variableSizeBytes(uint16, featuresCodec))
|
||||||
|
|
||||||
val peerConnectionConfCodec: Codec[PeerConnection.Conf] = (
|
val peerConnectionConfCodec: Codec[PeerConnection.Conf] = (
|
||||||
("authTimeout" | finiteDurationCodec) ::
|
("authTimeout" | finiteDurationCodec) ::
|
||||||
|
|
|
@ -19,7 +19,7 @@ package fr.acinq.eclair.router
|
||||||
import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey, sha256, verifySignature}
|
import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey, sha256, verifySignature}
|
||||||
import fr.acinq.bitcoin.{ByteVector32, ByteVector64, Crypto, LexicographicalOrdering}
|
import fr.acinq.bitcoin.{ByteVector32, ByteVector64, Crypto, LexicographicalOrdering}
|
||||||
import fr.acinq.eclair.wire.protocol._
|
import fr.acinq.eclair.wire.protocol._
|
||||||
import fr.acinq.eclair.{CltvExpiryDelta, FeatureScope, Features, MilliSatoshi, NodeFeature, ShortChannelId, TimestampSecond, TimestampSecondLong, serializationResult}
|
import fr.acinq.eclair.{CltvExpiryDelta, Feature, Features, MilliSatoshi, NodeFeature, ShortChannelId, TimestampSecond, TimestampSecondLong, serializationResult}
|
||||||
import scodec.bits.ByteVector
|
import scodec.bits.ByteVector
|
||||||
import shapeless.HNil
|
import shapeless.HNil
|
||||||
|
|
||||||
|
@ -28,16 +28,16 @@ import shapeless.HNil
|
||||||
*/
|
*/
|
||||||
object Announcements {
|
object Announcements {
|
||||||
|
|
||||||
def channelAnnouncementWitnessEncode(chainHash: ByteVector32, shortChannelId: ShortChannelId, nodeId1: PublicKey, nodeId2: PublicKey, bitcoinKey1: PublicKey, bitcoinKey2: PublicKey, features: Features[FeatureScope], tlvStream: TlvStream[ChannelAnnouncementTlv]): ByteVector =
|
def channelAnnouncementWitnessEncode(chainHash: ByteVector32, shortChannelId: ShortChannelId, nodeId1: PublicKey, nodeId2: PublicKey, bitcoinKey1: PublicKey, bitcoinKey2: PublicKey, features: Features[Feature], tlvStream: TlvStream[ChannelAnnouncementTlv]): ByteVector =
|
||||||
sha256(sha256(serializationResult(LightningMessageCodecs.channelAnnouncementWitnessCodec.encode(features :: chainHash :: shortChannelId :: nodeId1 :: nodeId2 :: bitcoinKey1 :: bitcoinKey2 :: tlvStream :: HNil))))
|
sha256(sha256(serializationResult(LightningMessageCodecs.channelAnnouncementWitnessCodec.encode(features :: chainHash :: shortChannelId :: nodeId1 :: nodeId2 :: bitcoinKey1 :: bitcoinKey2 :: tlvStream :: HNil))))
|
||||||
|
|
||||||
def nodeAnnouncementWitnessEncode(timestamp: TimestampSecond, nodeId: PublicKey, rgbColor: Color, alias: String, features: Features[FeatureScope], addresses: List[NodeAddress], tlvStream: TlvStream[NodeAnnouncementTlv]): ByteVector =
|
def nodeAnnouncementWitnessEncode(timestamp: TimestampSecond, nodeId: PublicKey, rgbColor: Color, alias: String, features: Features[Feature], addresses: List[NodeAddress], tlvStream: TlvStream[NodeAnnouncementTlv]): ByteVector =
|
||||||
sha256(sha256(serializationResult(LightningMessageCodecs.nodeAnnouncementWitnessCodec.encode(features :: timestamp :: nodeId :: rgbColor :: alias :: addresses :: tlvStream :: HNil))))
|
sha256(sha256(serializationResult(LightningMessageCodecs.nodeAnnouncementWitnessCodec.encode(features :: timestamp :: nodeId :: rgbColor :: alias :: addresses :: tlvStream :: HNil))))
|
||||||
|
|
||||||
def channelUpdateWitnessEncode(chainHash: ByteVector32, shortChannelId: ShortChannelId, timestamp: TimestampSecond, channelFlags: ChannelUpdate.ChannelFlags, cltvExpiryDelta: CltvExpiryDelta, htlcMinimumMsat: MilliSatoshi, feeBaseMsat: MilliSatoshi, feeProportionalMillionths: Long, htlcMaximumMsat: Option[MilliSatoshi], tlvStream: TlvStream[ChannelUpdateTlv]): ByteVector =
|
def channelUpdateWitnessEncode(chainHash: ByteVector32, shortChannelId: ShortChannelId, timestamp: TimestampSecond, channelFlags: ChannelUpdate.ChannelFlags, cltvExpiryDelta: CltvExpiryDelta, htlcMinimumMsat: MilliSatoshi, feeBaseMsat: MilliSatoshi, feeProportionalMillionths: Long, htlcMaximumMsat: Option[MilliSatoshi], tlvStream: TlvStream[ChannelUpdateTlv]): ByteVector =
|
||||||
sha256(sha256(serializationResult(LightningMessageCodecs.channelUpdateWitnessCodec.encode(chainHash :: shortChannelId :: timestamp :: channelFlags :: cltvExpiryDelta :: htlcMinimumMsat :: feeBaseMsat :: feeProportionalMillionths :: htlcMaximumMsat :: tlvStream :: HNil))))
|
sha256(sha256(serializationResult(LightningMessageCodecs.channelUpdateWitnessCodec.encode(chainHash :: shortChannelId :: timestamp :: channelFlags :: cltvExpiryDelta :: htlcMinimumMsat :: feeBaseMsat :: feeProportionalMillionths :: htlcMaximumMsat :: tlvStream :: HNil))))
|
||||||
|
|
||||||
def generateChannelAnnouncementWitness(chainHash: ByteVector32, shortChannelId: ShortChannelId, localNodeId: PublicKey, remoteNodeId: PublicKey, localFundingKey: PublicKey, remoteFundingKey: PublicKey, features: Features[FeatureScope]): ByteVector =
|
def generateChannelAnnouncementWitness(chainHash: ByteVector32, shortChannelId: ShortChannelId, localNodeId: PublicKey, remoteNodeId: PublicKey, localFundingKey: PublicKey, remoteFundingKey: PublicKey, features: Features[Feature]): ByteVector =
|
||||||
if (isNode1(localNodeId, remoteNodeId)) {
|
if (isNode1(localNodeId, remoteNodeId)) {
|
||||||
channelAnnouncementWitnessEncode(chainHash, shortChannelId, localNodeId, remoteNodeId, localFundingKey, remoteFundingKey, features, TlvStream.empty)
|
channelAnnouncementWitnessEncode(chainHash, shortChannelId, localNodeId, remoteNodeId, localFundingKey, remoteFundingKey, features, TlvStream.empty)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -23,7 +23,7 @@ import fr.acinq.eclair.channel._
|
||||||
import fr.acinq.eclair.crypto.ShaChain
|
import fr.acinq.eclair.crypto.ShaChain
|
||||||
import fr.acinq.eclair.transactions.CommitmentSpec
|
import fr.acinq.eclair.transactions.CommitmentSpec
|
||||||
import fr.acinq.eclair.transactions.Transactions._
|
import fr.acinq.eclair.transactions.Transactions._
|
||||||
import fr.acinq.eclair.{BlockHeight, Feature, FeatureScope, Features, channel}
|
import fr.acinq.eclair.{BlockHeight, Features, Feature, channel}
|
||||||
import scodec.bits.BitVector
|
import scodec.bits.BitVector
|
||||||
|
|
||||||
private[channel] object ChannelTypes0 {
|
private[channel] object ChannelTypes0 {
|
||||||
|
@ -196,8 +196,8 @@ private[channel] object ChannelTypes0 {
|
||||||
ChannelConfig()
|
ChannelConfig()
|
||||||
}
|
}
|
||||||
val isWumboChannel = commitInput.txOut.amount > Satoshi(16777215)
|
val isWumboChannel = commitInput.txOut.amount > Satoshi(16777215)
|
||||||
val baseChannelFeatures: Set[Feature with FeatureScope] = if (isWumboChannel) Set(Features.Wumbo) else Set.empty
|
val baseChannelFeatures: Set[Feature] = if (isWumboChannel) Set(Features.Wumbo) else Set.empty
|
||||||
val commitmentFeatures: Set[Feature with FeatureScope] = if (channelVersion.hasAnchorOutputs) {
|
val commitmentFeatures: Set[Feature] = if (channelVersion.hasAnchorOutputs) {
|
||||||
Set(Features.StaticRemoteKey, Features.AnchorOutputs)
|
Set(Features.StaticRemoteKey, Features.AnchorOutputs)
|
||||||
} else if (channelVersion.hasStaticRemotekey) {
|
} else if (channelVersion.hasStaticRemotekey) {
|
||||||
Set(Features.StaticRemoteKey)
|
Set(Features.StaticRemoteKey)
|
||||||
|
|
|
@ -18,7 +18,7 @@ package fr.acinq.eclair.wire.protocol
|
||||||
|
|
||||||
import fr.acinq.eclair.wire.Monitoring.{Metrics, Tags}
|
import fr.acinq.eclair.wire.Monitoring.{Metrics, Tags}
|
||||||
import fr.acinq.eclair.wire.protocol.CommonCodecs._
|
import fr.acinq.eclair.wire.protocol.CommonCodecs._
|
||||||
import fr.acinq.eclair.{FeatureScope, Features, InitFeature, KamonExt, NodeFeature}
|
import fr.acinq.eclair.{Feature, Features, InitFeature, KamonExt, NodeFeature}
|
||||||
import scodec.bits.{BitVector, ByteVector}
|
import scodec.bits.{BitVector, ByteVector}
|
||||||
import scodec.codecs._
|
import scodec.codecs._
|
||||||
import scodec.{Attempt, Codec}
|
import scodec.{Attempt, Codec}
|
||||||
|
@ -29,7 +29,7 @@ import shapeless._
|
||||||
*/
|
*/
|
||||||
object LightningMessageCodecs {
|
object LightningMessageCodecs {
|
||||||
|
|
||||||
val featuresCodec: Codec[Features[FeatureScope]] = varsizebinarydata.xmap[Features[FeatureScope]](
|
val featuresCodec: Codec[Features[Feature]] = varsizebinarydata.xmap[Features[Feature]](
|
||||||
{ bytes => Features(bytes) },
|
{ bytes => Features(bytes) },
|
||||||
{ features => features.toByteVector }
|
{ features => features.toByteVector }
|
||||||
)
|
)
|
||||||
|
|
|
@ -21,7 +21,7 @@ import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey}
|
||||||
import fr.acinq.bitcoin.{ByteVector32, ByteVector64, Satoshi}
|
import fr.acinq.bitcoin.{ByteVector32, ByteVector64, Satoshi}
|
||||||
import fr.acinq.eclair.blockchain.fee.FeeratePerKw
|
import fr.acinq.eclair.blockchain.fee.FeeratePerKw
|
||||||
import fr.acinq.eclair.channel.{ChannelFlags, ChannelType}
|
import fr.acinq.eclair.channel.{ChannelFlags, ChannelType}
|
||||||
import fr.acinq.eclair.{BlockHeight, CltvExpiry, CltvExpiryDelta, FeatureScope, Features, InitFeature, MilliSatoshi, NodeFeature, ShortChannelId, TimestampSecond, UInt64}
|
import fr.acinq.eclair.{BlockHeight, CltvExpiry, CltvExpiryDelta, Feature, Features, InitFeature, MilliSatoshi, NodeFeature, ShortChannelId, TimestampSecond, UInt64}
|
||||||
import scodec.bits.ByteVector
|
import scodec.bits.ByteVector
|
||||||
|
|
||||||
import java.net.{Inet4Address, Inet6Address, InetAddress, InetSocketAddress}
|
import java.net.{Inet4Address, Inet6Address, InetAddress, InetSocketAddress}
|
||||||
|
@ -200,7 +200,7 @@ case class ChannelAnnouncement(nodeSignature1: ByteVector64,
|
||||||
nodeSignature2: ByteVector64,
|
nodeSignature2: ByteVector64,
|
||||||
bitcoinSignature1: ByteVector64,
|
bitcoinSignature1: ByteVector64,
|
||||||
bitcoinSignature2: ByteVector64,
|
bitcoinSignature2: ByteVector64,
|
||||||
features: Features[FeatureScope],
|
features: Features[Feature],
|
||||||
chainHash: ByteVector32,
|
chainHash: ByteVector32,
|
||||||
shortChannelId: ShortChannelId,
|
shortChannelId: ShortChannelId,
|
||||||
nodeId1: PublicKey,
|
nodeId1: PublicKey,
|
||||||
|
@ -263,7 +263,7 @@ case class Tor3(tor3: String, port: Int) extends OnionAddress { override def soc
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
|
||||||
case class NodeAnnouncement(signature: ByteVector64,
|
case class NodeAnnouncement(signature: ByteVector64,
|
||||||
features: Features[FeatureScope],
|
features: Features[Feature],
|
||||||
timestamp: TimestampSecond,
|
timestamp: TimestampSecond,
|
||||||
nodeId: PublicKey,
|
nodeId: PublicKey,
|
||||||
rgbColor: Color,
|
rgbColor: Color,
|
||||||
|
|
|
@ -109,7 +109,7 @@ class FeaturesSpec extends AnyFunSuite {
|
||||||
}
|
}
|
||||||
|
|
||||||
test("features compatibility") {
|
test("features compatibility") {
|
||||||
case class TestCase(ours: Features[FeatureScope], theirs: Features[FeatureScope], oursSupportTheirs: Boolean, theirsSupportOurs: Boolean, compatible: Boolean)
|
case class TestCase(ours: Features[Feature], theirs: Features[Feature], oursSupportTheirs: Boolean, theirsSupportOurs: Boolean, compatible: Boolean)
|
||||||
val testCases = Seq(
|
val testCases = Seq(
|
||||||
// Empty features
|
// Empty features
|
||||||
TestCase(
|
TestCase(
|
||||||
|
@ -168,7 +168,7 @@ class FeaturesSpec extends AnyFunSuite {
|
||||||
// They have unknown optional features
|
// They have unknown optional features
|
||||||
TestCase(
|
TestCase(
|
||||||
Features(VariableLengthOnion -> Optional),
|
Features(VariableLengthOnion -> Optional),
|
||||||
Features[FeatureScope](Map[Feature with FeatureScope, FeatureSupport](VariableLengthOnion -> Optional), Set(UnknownFeature(141))),
|
Features(Map(VariableLengthOnion -> Optional), unknown = Set(UnknownFeature(141))),
|
||||||
oursSupportTheirs = true,
|
oursSupportTheirs = true,
|
||||||
theirsSupportOurs = true,
|
theirsSupportOurs = true,
|
||||||
compatible = true
|
compatible = true
|
||||||
|
@ -176,7 +176,7 @@ class FeaturesSpec extends AnyFunSuite {
|
||||||
// They have unknown mandatory features
|
// They have unknown mandatory features
|
||||||
TestCase(
|
TestCase(
|
||||||
Features(VariableLengthOnion -> Optional),
|
Features(VariableLengthOnion -> Optional),
|
||||||
Features[FeatureScope](Map[Feature with FeatureScope, FeatureSupport](VariableLengthOnion -> Optional), Set(UnknownFeature(142))),
|
Features(Map(VariableLengthOnion -> Optional), unknown = Set(UnknownFeature(142))),
|
||||||
oursSupportTheirs = false,
|
oursSupportTheirs = false,
|
||||||
theirsSupportOurs = true,
|
theirsSupportOurs = true,
|
||||||
compatible = false
|
compatible = false
|
||||||
|
@ -198,10 +198,10 @@ class FeaturesSpec extends AnyFunSuite {
|
||||||
compatible = false
|
compatible = false
|
||||||
),
|
),
|
||||||
// nonreg testing of future features (needs to be updated with every new supported mandatory bit)
|
// nonreg testing of future features (needs to be updated with every new supported mandatory bit)
|
||||||
TestCase(Features.empty, Features[FeatureScope](Map.empty[Feature with FeatureScope, FeatureSupport], Set(UnknownFeature(24))), oursSupportTheirs = false, theirsSupportOurs = true, compatible = false),
|
TestCase(Features.empty, Features(Map.empty, unknown = Set(UnknownFeature(24))), oursSupportTheirs = false, theirsSupportOurs = true, compatible = false),
|
||||||
TestCase(Features.empty, Features[FeatureScope](Map.empty[Feature with FeatureScope, FeatureSupport], Set(UnknownFeature(25))), oursSupportTheirs = true, theirsSupportOurs = true, compatible = true),
|
TestCase(Features.empty, Features(Map.empty, unknown = Set(UnknownFeature(25))), oursSupportTheirs = true, theirsSupportOurs = true, compatible = true),
|
||||||
TestCase(Features.empty, Features[FeatureScope](Map.empty[Feature with FeatureScope, FeatureSupport], Set(UnknownFeature(28))), oursSupportTheirs = false, theirsSupportOurs = true, compatible = false),
|
TestCase(Features.empty, Features(Map.empty, unknown = Set(UnknownFeature(28))), oursSupportTheirs = false, theirsSupportOurs = true, compatible = false),
|
||||||
TestCase(Features.empty, Features[FeatureScope](Map.empty[Feature with FeatureScope, FeatureSupport], Set(UnknownFeature(29))), oursSupportTheirs = true, theirsSupportOurs = true, compatible = true),
|
TestCase(Features.empty, Features(Map.empty, unknown = Set(UnknownFeature(29))), oursSupportTheirs = true, theirsSupportOurs = true, compatible = true),
|
||||||
)
|
)
|
||||||
|
|
||||||
for (testCase <- testCases) {
|
for (testCase <- testCases) {
|
||||||
|
@ -212,20 +212,20 @@ class FeaturesSpec extends AnyFunSuite {
|
||||||
}
|
}
|
||||||
|
|
||||||
test("filter features based on their usage") {
|
test("filter features based on their usage") {
|
||||||
val features = Features[FeatureScope](
|
val features = Features(
|
||||||
Map[Feature with FeatureScope, FeatureSupport](DataLossProtect -> Optional, InitialRoutingSync -> Optional, VariableLengthOnion -> Mandatory, PaymentMetadata -> Optional),
|
Map(DataLossProtect -> Optional, InitialRoutingSync -> Optional, VariableLengthOnion -> Mandatory, PaymentMetadata -> Optional),
|
||||||
Set(UnknownFeature(753), UnknownFeature(852), UnknownFeature(65303))
|
Set(UnknownFeature(753), UnknownFeature(852), UnknownFeature(65303))
|
||||||
)
|
)
|
||||||
assert(features.initFeatures() === Features[InitFeature](
|
assert(features.initFeatures() === Features(
|
||||||
Map[Feature with InitFeature, FeatureSupport](DataLossProtect -> Optional, InitialRoutingSync -> Optional, VariableLengthOnion -> Mandatory),
|
Map(DataLossProtect -> Optional, InitialRoutingSync -> Optional, VariableLengthOnion -> Mandatory),
|
||||||
Set(UnknownFeature(753), UnknownFeature(852), UnknownFeature(65303))
|
Set(UnknownFeature(753), UnknownFeature(852), UnknownFeature(65303))
|
||||||
))
|
))
|
||||||
assert(features.nodeAnnouncementFeatures() === Features[NodeFeature](
|
assert(features.nodeAnnouncementFeatures() === Features(
|
||||||
Map[Feature with NodeFeature, FeatureSupport](DataLossProtect -> Optional, VariableLengthOnion -> Mandatory),
|
Map(DataLossProtect -> Optional, VariableLengthOnion -> Mandatory),
|
||||||
Set(UnknownFeature(753), UnknownFeature(852), UnknownFeature(65303))
|
Set(UnknownFeature(753), UnknownFeature(852), UnknownFeature(65303))
|
||||||
))
|
))
|
||||||
assert(features.invoiceFeatures() === Features[InvoiceFeature](
|
assert(features.invoiceFeatures() === Features(
|
||||||
Map[Feature with InvoiceFeature, FeatureSupport](VariableLengthOnion -> Mandatory, PaymentMetadata -> Optional),
|
Map(VariableLengthOnion -> Mandatory, PaymentMetadata -> Optional),
|
||||||
Set(UnknownFeature(753), UnknownFeature(852), UnknownFeature(65303))
|
Set(UnknownFeature(753), UnknownFeature(852), UnknownFeature(65303))
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -235,8 +235,8 @@ class FeaturesSpec extends AnyFunSuite {
|
||||||
hex"" -> Features.empty,
|
hex"" -> Features.empty,
|
||||||
hex"0100" -> Features(VariableLengthOnion -> Mandatory),
|
hex"0100" -> Features(VariableLengthOnion -> Mandatory),
|
||||||
hex"028a8a" -> Features(DataLossProtect -> Optional, InitialRoutingSync -> Optional, ChannelRangeQueries -> Optional, VariableLengthOnion -> Optional, ChannelRangeQueriesExtended -> Optional, PaymentSecret -> Optional, BasicMultiPartPayment -> Optional),
|
hex"028a8a" -> Features(DataLossProtect -> Optional, InitialRoutingSync -> Optional, ChannelRangeQueries -> Optional, VariableLengthOnion -> Optional, ChannelRangeQueriesExtended -> Optional, PaymentSecret -> Optional, BasicMultiPartPayment -> Optional),
|
||||||
hex"09004200" -> Features[FeatureScope](Map[Feature with FeatureScope, FeatureSupport](VariableLengthOnion -> Optional, PaymentSecret -> Mandatory, ShutdownAnySegwit -> Optional), Set(UnknownFeature(24))),
|
hex"09004200" -> Features(Map(VariableLengthOnion -> Optional, PaymentSecret -> Mandatory, ShutdownAnySegwit -> Optional), Set(UnknownFeature(24))),
|
||||||
hex"52000000" -> Features[FeatureScope](Map.empty[Feature with FeatureScope, FeatureSupport], Set(UnknownFeature(25), UnknownFeature(28), UnknownFeature(30)))
|
hex"52000000" -> Features(Map.empty[Feature, FeatureSupport], Set(UnknownFeature(25), UnknownFeature(28), UnknownFeature(30)))
|
||||||
)
|
)
|
||||||
|
|
||||||
for ((bin, features) <- testCases) {
|
for ((bin, features) <- testCases) {
|
||||||
|
|
|
@ -84,8 +84,8 @@ object TestConstants {
|
||||||
color = Color(1, 2, 3),
|
color = Color(1, 2, 3),
|
||||||
publicAddresses = NodeAddress.fromParts("localhost", 9731).get :: Nil,
|
publicAddresses = NodeAddress.fromParts("localhost", 9731).get :: Nil,
|
||||||
torAddress_opt = None,
|
torAddress_opt = None,
|
||||||
features = Features[FeatureScope](
|
features = Features(
|
||||||
Map[Feature with FeatureScope, FeatureSupport](
|
Map(
|
||||||
DataLossProtect -> Optional,
|
DataLossProtect -> Optional,
|
||||||
ChannelRangeQueries -> Optional,
|
ChannelRangeQueries -> Optional,
|
||||||
ChannelRangeQueriesExtended -> Optional,
|
ChannelRangeQueriesExtended -> Optional,
|
||||||
|
@ -94,7 +94,7 @@ object TestConstants {
|
||||||
BasicMultiPartPayment -> Optional,
|
BasicMultiPartPayment -> Optional,
|
||||||
PaymentMetadata -> Optional,
|
PaymentMetadata -> Optional,
|
||||||
),
|
),
|
||||||
Set(UnknownFeature(TestFeature.optional))
|
unknown = Set(UnknownFeature(TestFeature.optional))
|
||||||
),
|
),
|
||||||
pluginParams = List(pluginParams),
|
pluginParams = List(pluginParams),
|
||||||
overrideInitFeatures = Map.empty,
|
overrideInitFeatures = Map.empty,
|
||||||
|
|
|
@ -20,7 +20,7 @@ import fr.acinq.eclair.FeatureSupport._
|
||||||
import fr.acinq.eclair.Features._
|
import fr.acinq.eclair.Features._
|
||||||
import fr.acinq.eclair.channel.states.ChannelStateTestsHelperMethods
|
import fr.acinq.eclair.channel.states.ChannelStateTestsHelperMethods
|
||||||
import fr.acinq.eclair.transactions.Transactions
|
import fr.acinq.eclair.transactions.Transactions
|
||||||
import fr.acinq.eclair.{Features, InitFeature, TestKitBaseClass}
|
import fr.acinq.eclair.{Features, InitFeature, NodeFeature, TestKitBaseClass}
|
||||||
import org.scalatest.funsuite.AnyFunSuiteLike
|
import org.scalatest.funsuite.AnyFunSuiteLike
|
||||||
|
|
||||||
class ChannelFeaturesSpec extends TestKitBaseClass with AnyFunSuiteLike with ChannelStateTestsHelperMethods {
|
class ChannelFeaturesSpec extends TestKitBaseClass with AnyFunSuiteLike with ChannelStateTestsHelperMethods {
|
||||||
|
@ -80,29 +80,31 @@ class ChannelFeaturesSpec extends TestKitBaseClass with AnyFunSuiteLike with Cha
|
||||||
}
|
}
|
||||||
|
|
||||||
test("create channel type from features") {
|
test("create channel type from features") {
|
||||||
|
case class TestCase(features: Features[InitFeature], expectedChannelType: ChannelType)
|
||||||
|
|
||||||
val validChannelTypes = Seq(
|
val validChannelTypes = Seq(
|
||||||
Features.empty[InitFeature] -> ChannelTypes.Standard,
|
TestCase(Features.empty[InitFeature], ChannelTypes.Standard),
|
||||||
Features[InitFeature](StaticRemoteKey -> Mandatory) -> ChannelTypes.StaticRemoteKey,
|
TestCase(Features(StaticRemoteKey -> Mandatory), ChannelTypes.StaticRemoteKey),
|
||||||
Features[InitFeature](StaticRemoteKey -> Mandatory, AnchorOutputs -> Mandatory) -> ChannelTypes.AnchorOutputs,
|
TestCase(Features(StaticRemoteKey -> Mandatory, AnchorOutputs -> Mandatory), ChannelTypes.AnchorOutputs),
|
||||||
Features[InitFeature](StaticRemoteKey -> Mandatory, AnchorOutputsZeroFeeHtlcTx -> Mandatory) -> ChannelTypes.AnchorOutputsZeroFeeHtlcTx,
|
TestCase(Features(StaticRemoteKey -> Mandatory, AnchorOutputsZeroFeeHtlcTx -> Mandatory), ChannelTypes.AnchorOutputsZeroFeeHtlcTx),
|
||||||
)
|
)
|
||||||
for ((features, expected) <- validChannelTypes) {
|
for (testCase <- validChannelTypes) {
|
||||||
assert(ChannelTypes.fromFeatures(features) === expected)
|
assert(ChannelTypes.fromFeatures(testCase.features) === testCase.expectedChannelType)
|
||||||
}
|
}
|
||||||
|
|
||||||
val invalidChannelTypes = Seq(
|
val invalidChannelTypes: Seq[Features[InitFeature]] = Seq(
|
||||||
Features[InitFeature](Wumbo -> Optional),
|
Features(Wumbo -> Optional),
|
||||||
Features[InitFeature](StaticRemoteKey -> Optional),
|
Features(StaticRemoteKey -> Optional),
|
||||||
Features[InitFeature](StaticRemoteKey -> Mandatory, Wumbo -> Optional),
|
Features(StaticRemoteKey -> Mandatory, Wumbo -> Optional),
|
||||||
Features[InitFeature](StaticRemoteKey -> Optional, AnchorOutputs -> Optional),
|
Features(StaticRemoteKey -> Optional, AnchorOutputs -> Optional),
|
||||||
Features[InitFeature](StaticRemoteKey -> Mandatory, AnchorOutputs -> Optional),
|
Features(StaticRemoteKey -> Mandatory, AnchorOutputs -> Optional),
|
||||||
Features[InitFeature](StaticRemoteKey -> Optional, AnchorOutputs -> Mandatory),
|
Features(StaticRemoteKey -> Optional, AnchorOutputs -> Mandatory),
|
||||||
Features[InitFeature](StaticRemoteKey -> Optional, AnchorOutputsZeroFeeHtlcTx -> Optional),
|
Features(StaticRemoteKey -> Optional, AnchorOutputsZeroFeeHtlcTx -> Optional),
|
||||||
Features[InitFeature](StaticRemoteKey -> Optional, AnchorOutputsZeroFeeHtlcTx -> Mandatory),
|
Features(StaticRemoteKey -> Optional, AnchorOutputsZeroFeeHtlcTx -> Mandatory),
|
||||||
Features[InitFeature](StaticRemoteKey -> Mandatory, AnchorOutputsZeroFeeHtlcTx -> Optional),
|
Features(StaticRemoteKey -> Mandatory, AnchorOutputsZeroFeeHtlcTx -> Optional),
|
||||||
Features[InitFeature](StaticRemoteKey -> Mandatory, AnchorOutputs -> Mandatory, AnchorOutputsZeroFeeHtlcTx -> Mandatory),
|
Features(StaticRemoteKey -> Mandatory, AnchorOutputs -> Mandatory, AnchorOutputsZeroFeeHtlcTx -> Mandatory),
|
||||||
Features[InitFeature](StaticRemoteKey -> Mandatory, AnchorOutputs -> Optional, AnchorOutputsZeroFeeHtlcTx -> Mandatory),
|
Features(StaticRemoteKey -> Mandatory, AnchorOutputs -> Optional, AnchorOutputsZeroFeeHtlcTx -> Mandatory),
|
||||||
Features[InitFeature](StaticRemoteKey -> Mandatory, AnchorOutputs -> Mandatory, Wumbo -> Optional),
|
Features(StaticRemoteKey -> Mandatory, AnchorOutputs -> Mandatory, Wumbo -> Optional),
|
||||||
)
|
)
|
||||||
for (features <- invalidChannelTypes) {
|
for (features <- invalidChannelTypes) {
|
||||||
assert(ChannelTypes.fromFeatures(features) === ChannelTypes.UnsupportedChannelType(features))
|
assert(ChannelTypes.fromFeatures(features) === ChannelTypes.UnsupportedChannelType(features))
|
||||||
|
|
|
@ -353,7 +353,7 @@ class PeerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with Paralle
|
||||||
}
|
}
|
||||||
// They want to use a channel type we don't support yet.
|
// They want to use a channel type we don't support yet.
|
||||||
{
|
{
|
||||||
val open = createOpenChannelMessage(TlvStream[OpenChannelTlv](ChannelTlv.ChannelTypeTlv(UnsupportedChannelType(Features[InitFeature](Map[Feature with InitFeature, FeatureSupport](StaticRemoteKey -> Mandatory), Set(UnknownFeature(22)))))))
|
val open = createOpenChannelMessage(TlvStream[OpenChannelTlv](ChannelTlv.ChannelTypeTlv(UnsupportedChannelType(Features(Map(StaticRemoteKey -> Mandatory), unknown = Set(UnknownFeature(22)))))))
|
||||||
peerConnection.send(peer, open)
|
peerConnection.send(peer, open)
|
||||||
peerConnection.expectMsg(Error(open.temporaryChannelId, "invalid channel_type=0x401000, expected channel_type=standard"))
|
peerConnection.expectMsg(Error(open.temporaryChannelId, "invalid channel_type=0x401000, expected channel_type=standard"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ import fr.acinq.bitcoin.{Block, BtcDouble, ByteVector32, Crypto, MilliBtcDouble,
|
||||||
import fr.acinq.eclair.FeatureSupport.{Mandatory, Optional}
|
import fr.acinq.eclair.FeatureSupport.{Mandatory, Optional}
|
||||||
import fr.acinq.eclair.Features.{PaymentMetadata, PaymentSecret, _}
|
import fr.acinq.eclair.Features.{PaymentMetadata, PaymentSecret, _}
|
||||||
import fr.acinq.eclair.payment.Bolt11Invoice._
|
import fr.acinq.eclair.payment.Bolt11Invoice._
|
||||||
import fr.acinq.eclair.{CltvExpiryDelta, Feature, FeatureScope, FeatureSupport, Features, InvoiceFeature, MilliSatoshi, MilliSatoshiLong, ShortChannelId, TestConstants, TimestampSecond, TimestampSecondLong, ToMilliSatoshiConversion, UnknownFeature, randomBytes32, randomKey}
|
import fr.acinq.eclair.{CltvExpiryDelta, FeatureSupport, Features, Feature, MilliSatoshi, MilliSatoshiLong, ShortChannelId, TestConstants, TimestampSecond, TimestampSecondLong, ToMilliSatoshiConversion, UnknownFeature, randomBytes32}
|
||||||
import org.scalatest.funsuite.AnyFunSuite
|
import org.scalatest.funsuite.AnyFunSuite
|
||||||
import scodec.DecodeResult
|
import scodec.DecodeResult
|
||||||
import scodec.bits._
|
import scodec.bits._
|
||||||
|
@ -54,7 +54,7 @@ class Bolt11InvoiceSpec extends AnyFunSuite {
|
||||||
timestamp: TimestampSecond = TimestampSecond.now(),
|
timestamp: TimestampSecond = TimestampSecond.now(),
|
||||||
paymentSecret: ByteVector32 = randomBytes32(),
|
paymentSecret: ByteVector32 = randomBytes32(),
|
||||||
paymentMetadata: Option[ByteVector] = None,
|
paymentMetadata: Option[ByteVector] = None,
|
||||||
features: Features[FeatureScope] = defaultFeatures.unscoped()): Bolt11Invoice = {
|
features: Features[Feature] = defaultFeatures.unscoped()): Bolt11Invoice = {
|
||||||
require(features.hasFeature(Features.PaymentSecret, Some(FeatureSupport.Mandatory)), "invoices must require a payment secret")
|
require(features.hasFeature(Features.PaymentSecret, Some(FeatureSupport.Mandatory)), "invoices must require a payment secret")
|
||||||
val prefix = prefixes(chainHash)
|
val prefix = prefixes(chainHash)
|
||||||
val tags = {
|
val tags = {
|
||||||
|
@ -624,11 +624,11 @@ class Bolt11InvoiceSpec extends AnyFunSuite {
|
||||||
test("no unknown feature in invoice"){
|
test("no unknown feature in invoice"){
|
||||||
assert(TestConstants.Alice.nodeParams.features.invoiceFeatures().unknown.nonEmpty)
|
assert(TestConstants.Alice.nodeParams.features.invoiceFeatures().unknown.nonEmpty)
|
||||||
val invoice = Bolt11Invoice(Block.LivenetGenesisBlock.hash, Some(123 msat), ByteVector32.One, priv, Left("Some invoice"), CltvExpiryDelta(18), features = TestConstants.Alice.nodeParams.features.invoiceFeatures())
|
val invoice = Bolt11Invoice(Block.LivenetGenesisBlock.hash, Some(123 msat), ByteVector32.One, priv, Left("Some invoice"), CltvExpiryDelta(18), features = TestConstants.Alice.nodeParams.features.invoiceFeatures())
|
||||||
assert(invoice.features === Features[InvoiceFeature](Map[Feature with InvoiceFeature, FeatureSupport](PaymentSecret -> Mandatory, BasicMultiPartPayment -> Optional, PaymentMetadata -> Optional, VariableLengthOnion -> Mandatory)))
|
assert(invoice.features === Features(PaymentSecret -> Mandatory, BasicMultiPartPayment -> Optional, PaymentMetadata -> Optional, VariableLengthOnion -> Mandatory))
|
||||||
assert(Bolt11Invoice.fromString(invoice.toString) === invoice)
|
assert(Bolt11Invoice.fromString(invoice.toString) === invoice)
|
||||||
}
|
}
|
||||||
|
|
||||||
test("Invoices can't have high features"){
|
test("Invoices can't have high features"){
|
||||||
assertThrows[Exception](createInvoiceUnsafe(Block.LivenetGenesisBlock.hash, Some(123 msat), ByteVector32.One, priv, Left("Some invoice"), CltvExpiryDelta(18), features = Features[FeatureScope](Map[Feature with FeatureScope, FeatureSupport](VariableLengthOnion -> Mandatory, PaymentSecret -> Mandatory), Set(UnknownFeature(424242)))))
|
assertThrows[Exception](createInvoiceUnsafe(Block.LivenetGenesisBlock.hash, Some(123 msat), ByteVector32.One, priv, Left("Some invoice"), CltvExpiryDelta(18), features = Features[Feature](Map[Feature, FeatureSupport](VariableLengthOnion -> Mandatory, PaymentSecret -> Mandatory), Set(UnknownFeature(424242)))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ import fr.acinq.eclair.payment.receive.MultiPartPaymentFSM.HtlcPart
|
||||||
import fr.acinq.eclair.payment.receive.{MultiPartPaymentFSM, PaymentHandler}
|
import fr.acinq.eclair.payment.receive.{MultiPartPaymentFSM, PaymentHandler}
|
||||||
import fr.acinq.eclair.wire.protocol.PaymentOnion.FinalTlvPayload
|
import fr.acinq.eclair.wire.protocol.PaymentOnion.FinalTlvPayload
|
||||||
import fr.acinq.eclair.wire.protocol._
|
import fr.acinq.eclair.wire.protocol._
|
||||||
import fr.acinq.eclair.{CltvExpiry, CltvExpiryDelta, FeatureScope, Features, MilliSatoshiLong, NodeParams, ShortChannelId, TestConstants, TestKitBaseClass, TimestampMilliLong, randomBytes32, randomKey}
|
import fr.acinq.eclair.{CltvExpiry, CltvExpiryDelta, Feature, Features, MilliSatoshiLong, NodeParams, ShortChannelId, TestConstants, TestKitBaseClass, TimestampMilliLong, randomBytes32, randomKey}
|
||||||
import org.scalatest.Outcome
|
import org.scalatest.Outcome
|
||||||
import org.scalatest.funsuite.FixtureAnyFunSuiteLike
|
import org.scalatest.funsuite.FixtureAnyFunSuiteLike
|
||||||
import scodec.bits.HexStringSyntax
|
import scodec.bits.HexStringSyntax
|
||||||
|
@ -46,18 +46,18 @@ import scala.concurrent.duration._
|
||||||
|
|
||||||
class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
|
class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
|
||||||
|
|
||||||
val featuresWithoutMpp = Features[FeatureScope](
|
val featuresWithoutMpp = Features[Feature](
|
||||||
VariableLengthOnion -> Mandatory,
|
VariableLengthOnion -> Mandatory,
|
||||||
PaymentSecret -> Mandatory,
|
PaymentSecret -> Mandatory,
|
||||||
)
|
)
|
||||||
|
|
||||||
val featuresWithMpp = Features[FeatureScope](
|
val featuresWithMpp = Features[Feature](
|
||||||
VariableLengthOnion -> Mandatory,
|
VariableLengthOnion -> Mandatory,
|
||||||
PaymentSecret -> Mandatory,
|
PaymentSecret -> Mandatory,
|
||||||
BasicMultiPartPayment -> Optional
|
BasicMultiPartPayment -> Optional
|
||||||
)
|
)
|
||||||
|
|
||||||
val featuresWithKeySend = Features[FeatureScope](
|
val featuresWithKeySend = Features[Feature](
|
||||||
VariableLengthOnion -> Mandatory,
|
VariableLengthOnion -> Mandatory,
|
||||||
PaymentSecret -> Mandatory,
|
PaymentSecret -> Mandatory,
|
||||||
KeySend -> Optional
|
KeySend -> Optional
|
||||||
|
|
|
@ -37,10 +37,10 @@ import fr.acinq.eclair.router.Router._
|
||||||
import fr.acinq.eclair.wire.protocol.OnionPaymentPayloadTlv.{AmountToForward, KeySend, OutgoingCltv}
|
import fr.acinq.eclair.wire.protocol.OnionPaymentPayloadTlv.{AmountToForward, KeySend, OutgoingCltv}
|
||||||
import fr.acinq.eclair.wire.protocol.PaymentOnion.FinalTlvPayload
|
import fr.acinq.eclair.wire.protocol.PaymentOnion.FinalTlvPayload
|
||||||
import fr.acinq.eclair.wire.protocol._
|
import fr.acinq.eclair.wire.protocol._
|
||||||
import fr.acinq.eclair.{CltvExpiryDelta, Feature, FeatureSupport, Features, InvoiceFeature, MilliSatoshiLong, NodeParams, TestConstants, TestKitBaseClass, TimestampSecond, UnknownFeature, randomBytes32, randomKey}
|
import fr.acinq.eclair.{CltvExpiryDelta, Features, InvoiceFeature, MilliSatoshiLong, NodeParams, TestConstants, TestKitBaseClass, TimestampSecond, UnknownFeature, randomBytes32, randomKey}
|
||||||
import org.scalatest.funsuite.FixtureAnyFunSuiteLike
|
import org.scalatest.funsuite.FixtureAnyFunSuiteLike
|
||||||
import org.scalatest.{Outcome, Tag}
|
import org.scalatest.{Outcome, Tag}
|
||||||
import scodec.bits.{BinStringSyntax, ByteVector, HexStringSyntax}
|
import scodec.bits.{ByteVector, HexStringSyntax}
|
||||||
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
@ -53,18 +53,18 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
|
||||||
|
|
||||||
case class FixtureParam(nodeParams: NodeParams, initiator: TestActorRef[PaymentInitiator], payFsm: TestProbe, multiPartPayFsm: TestProbe, sender: TestProbe, eventListener: TestProbe)
|
case class FixtureParam(nodeParams: NodeParams, initiator: TestActorRef[PaymentInitiator], payFsm: TestProbe, multiPartPayFsm: TestProbe, sender: TestProbe, eventListener: TestProbe)
|
||||||
|
|
||||||
val featuresWithoutMpp: Features[InvoiceFeature] = Features[InvoiceFeature](
|
val featuresWithoutMpp: Features[InvoiceFeature] = Features(
|
||||||
VariableLengthOnion -> Mandatory,
|
VariableLengthOnion -> Mandatory,
|
||||||
PaymentSecret -> Mandatory
|
PaymentSecret -> Mandatory
|
||||||
)
|
)
|
||||||
|
|
||||||
val featuresWithMpp: Features[InvoiceFeature] = Features[InvoiceFeature](
|
val featuresWithMpp: Features[InvoiceFeature] = Features(
|
||||||
VariableLengthOnion -> Mandatory,
|
VariableLengthOnion -> Mandatory,
|
||||||
PaymentSecret -> Mandatory,
|
PaymentSecret -> Mandatory,
|
||||||
BasicMultiPartPayment -> Optional,
|
BasicMultiPartPayment -> Optional,
|
||||||
)
|
)
|
||||||
|
|
||||||
val featuresWithTrampoline: Features[InvoiceFeature] = Features[InvoiceFeature](
|
val featuresWithTrampoline: Features[InvoiceFeature] = Features(
|
||||||
VariableLengthOnion -> Mandatory,
|
VariableLengthOnion -> Mandatory,
|
||||||
PaymentSecret -> Mandatory,
|
PaymentSecret -> Mandatory,
|
||||||
BasicMultiPartPayment -> Optional,
|
BasicMultiPartPayment -> Optional,
|
||||||
|
@ -128,7 +128,7 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
|
||||||
Bolt11Invoice.Description("Some invoice"),
|
Bolt11Invoice.Description("Some invoice"),
|
||||||
Bolt11Invoice.PaymentSecret(randomBytes32()),
|
Bolt11Invoice.PaymentSecret(randomBytes32()),
|
||||||
Bolt11Invoice.Expiry(3600),
|
Bolt11Invoice.Expiry(3600),
|
||||||
Bolt11Invoice.InvoiceFeatures(Features[InvoiceFeature](Map[Feature with InvoiceFeature, FeatureSupport](VariableLengthOnion -> Mandatory, PaymentSecret -> Mandatory), Set(UnknownFeature(42))).unscoped())
|
Bolt11Invoice.InvoiceFeatures(Features(Map(VariableLengthOnion -> Mandatory, PaymentSecret -> Mandatory), unknown = Set(UnknownFeature(42))))
|
||||||
)
|
)
|
||||||
val invoice = Bolt11Invoice("lnbc", Some(finalAmount), TimestampSecond.now(), randomKey().publicKey, taggedFields, ByteVector.empty)
|
val invoice = Bolt11Invoice("lnbc", Some(finalAmount), TimestampSecond.now(), randomKey().publicKey, taggedFields, ByteVector.empty)
|
||||||
val req = SendPaymentToNode(finalAmount + 100.msat, invoice, 1, routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams)
|
val req = SendPaymentToNode(finalAmount + 100.msat, invoice, 1, routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams)
|
||||||
|
|
|
@ -54,7 +54,7 @@ class AnnouncementsSpec extends AnyFunSuite {
|
||||||
}
|
}
|
||||||
|
|
||||||
test("create valid signed node announcement") {
|
test("create valid signed node announcement") {
|
||||||
val features = Features[FeatureScope](
|
val features = Features(
|
||||||
Features.DataLossProtect -> FeatureSupport.Optional,
|
Features.DataLossProtect -> FeatureSupport.Optional,
|
||||||
Features.InitialRoutingSync -> FeatureSupport.Optional,
|
Features.InitialRoutingSync -> FeatureSupport.Optional,
|
||||||
Features.ChannelRangeQueries -> FeatureSupport.Optional,
|
Features.ChannelRangeQueries -> FeatureSupport.Optional,
|
||||||
|
|
|
@ -271,7 +271,7 @@ class LightningMessageCodecsSpec extends AnyFunSuite {
|
||||||
val commit_sig = CommitSig(randomBytes32(), randomBytes64(), randomBytes64() :: randomBytes64() :: randomBytes64() :: Nil)
|
val commit_sig = CommitSig(randomBytes32(), randomBytes64(), randomBytes64() :: randomBytes64() :: randomBytes64() :: Nil)
|
||||||
val revoke_and_ack = RevokeAndAck(randomBytes32(), scalar(0), point(1))
|
val revoke_and_ack = RevokeAndAck(randomBytes32(), scalar(0), point(1))
|
||||||
val channel_announcement = ChannelAnnouncement(randomBytes64(), randomBytes64(), randomBytes64(), randomBytes64(), Features(bin(7, 9)), Block.RegtestGenesisBlock.hash, ShortChannelId(1), randomKey().publicKey, randomKey().publicKey, randomKey().publicKey, randomKey().publicKey)
|
val channel_announcement = ChannelAnnouncement(randomBytes64(), randomBytes64(), randomBytes64(), randomBytes64(), Features(bin(7, 9)), Block.RegtestGenesisBlock.hash, ShortChannelId(1), randomKey().publicKey, randomKey().publicKey, randomKey().publicKey, randomKey().publicKey)
|
||||||
val node_announcement = NodeAnnouncement(randomBytes64(), Features[FeatureScope](DataLossProtect -> Optional), 1 unixsec, randomKey().publicKey, Color(100.toByte, 200.toByte, 300.toByte), "node-alias", IPv4(InetAddress.getByAddress(Array[Byte](192.toByte, 168.toByte, 1.toByte, 42.toByte)).asInstanceOf[Inet4Address], 42000) :: Nil)
|
val node_announcement = NodeAnnouncement(randomBytes64(), Features(DataLossProtect -> Optional), 1 unixsec, randomKey().publicKey, Color(100.toByte, 200.toByte, 300.toByte), "node-alias", IPv4(InetAddress.getByAddress(Array[Byte](192.toByte, 168.toByte, 1.toByte, 42.toByte)).asInstanceOf[Inet4Address], 42000) :: Nil)
|
||||||
val channel_update = ChannelUpdate(randomBytes64(), Block.RegtestGenesisBlock.hash, ShortChannelId(1), 2 unixsec, ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(3), 4 msat, 5 msat, 6, None)
|
val channel_update = ChannelUpdate(randomBytes64(), Block.RegtestGenesisBlock.hash, ShortChannelId(1), 2 unixsec, ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(3), 4 msat, 5 msat, 6, None)
|
||||||
val announcement_signatures = AnnouncementSignatures(randomBytes32(), ShortChannelId(42), randomBytes64(), randomBytes64())
|
val announcement_signatures = AnnouncementSignatures(randomBytes32(), ShortChannelId(42), randomBytes64(), randomBytes64())
|
||||||
val gossip_timestamp_filter = GossipTimestampFilter(Block.RegtestGenesisBlock.blockId, 100000 unixsec, 1500)
|
val gossip_timestamp_filter = GossipTimestampFilter(Block.RegtestGenesisBlock.blockId, 100000 unixsec, 1500)
|
||||||
|
|
Loading…
Add table
Reference in a new issue