diff --git a/eclair-core/src/main/resources/reference.conf b/eclair-core/src/main/resources/reference.conf index 8f441fe41b..0d31877937 100644 --- a/eclair-core/src/main/resources/reference.conf +++ b/eclair-core/src/main/resources/reference.conf @@ -36,7 +36,7 @@ eclair { node-color = "49daaa" global-features = "0200" // variable_length_onion - local-features = "8a" // initial_routing_sync + option_data_loss_protect + option_channel_range_queries + local-features = "088a" // initial_routing_sync + option_data_loss_protect + option_channel_range_queries + option_channel_range_queries_ex override-features = [ // optional per-node features # { # nodeid = "02aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/Features.scala b/eclair-core/src/main/scala/fr/acinq/eclair/Features.scala index 145961614c..8fc1b27901 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/Features.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/Features.scala @@ -32,6 +32,9 @@ object Features { val CHANNEL_RANGE_QUERIES_BIT_MANDATORY = 6 val CHANNEL_RANGE_QUERIES_BIT_OPTIONAL = 7 + 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 diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala b/eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala index bad45c0681..0aee107c95 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala @@ -23,7 +23,7 @@ import akka.event.Logging.MDC import akka.util.Timeout import com.google.common.net.HostAndPort import fr.acinq.bitcoin.Crypto.PublicKey -import fr.acinq.bitcoin.{Block, ByteVector32, DeterministicWallet, Satoshi} +import fr.acinq.bitcoin.{ByteVector32, DeterministicWallet, Satoshi} import fr.acinq.eclair.blockchain.EclairWallet import fr.acinq.eclair.channel._ import fr.acinq.eclair.crypto.TransportHandler @@ -132,38 +132,36 @@ class Peer(val nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: A when(INITIALIZING) { case Event(remoteInit: wire.Init, d: InitializingData) => d.transport ! TransportHandler.ReadAck(remoteInit) - val remoteHasInitialRoutingSync = Features.hasFeature(remoteInit.localFeatures, Features.INITIAL_ROUTING_SYNC_BIT_OPTIONAL) - val remoteHasChannelRangeQueriesOptional = Features.hasFeature(remoteInit.localFeatures, Features.CHANNEL_RANGE_QUERIES_BIT_OPTIONAL) - val remoteHasChannelRangeQueriesMandatory = Features.hasFeature(remoteInit.localFeatures, Features.CHANNEL_RANGE_QUERIES_BIT_MANDATORY) + log.info(s"peer is using globalFeatures=${remoteInit.globalFeatures.toBin} and localFeatures=${remoteInit.localFeatures.toBin}") - log.info(s"$remoteNodeId has features: initialRoutingSync=$remoteHasInitialRoutingSync channelRangeQueriesOptional=$remoteHasChannelRangeQueriesOptional channelRangeQueriesMandatory=$remoteHasChannelRangeQueriesMandatory") + if (Features.areSupported(remoteInit.localFeatures)) { d.origin_opt.foreach(origin => origin ! "connected") - if (remoteHasInitialRoutingSync) { - if (remoteHasChannelRangeQueriesOptional || remoteHasChannelRangeQueriesMandatory) { - // if they support channel queries we do nothing, they will send us their filters - log.info("peer has set initial routing sync and supports channel range queries, we do nothing (they will send us a query)") - } else { - // "old" nodes, do as before - log.info("peer requested a full routing table dump") - router ! GetRoutingState - } - } - if (remoteHasChannelRangeQueriesOptional || remoteHasChannelRangeQueriesMandatory) { - // if they support channel queries, always ask for their filter - // TODO: for now we do not activate extended queries on mainnet - val flags_opt = nodeParams.chainHash match { - case Block.RegtestGenesisBlock.hash | Block.TestnetGenesisBlock.hash => - Some(QueryChannelRangeTlv.QueryFlags(QueryChannelRangeTlv.QueryFlags.WANT_ALL)) - case _ => None - } + import Features._ + + def hasLocalFeature(bit: Int) = Features.hasFeature(d.localInit.localFeatures, bit) + + def hasRemoteFeature(bit: Int) = Features.hasFeature(remoteInit.localFeatures, bit) + + val canUseChannelRangeQueries = (hasLocalFeature(CHANNEL_RANGE_QUERIES_BIT_OPTIONAL) || hasLocalFeature(CHANNEL_RANGE_QUERIES_BIT_MANDATORY)) && (hasRemoteFeature(CHANNEL_RANGE_QUERIES_BIT_OPTIONAL) || hasRemoteFeature(CHANNEL_RANGE_QUERIES_BIT_MANDATORY)) + + val canUseChannelRangeQueriesEx = (hasLocalFeature(CHANNEL_RANGE_QUERIES_EX_BIT_OPTIONAL) || hasLocalFeature(CHANNEL_RANGE_QUERIES_EX_BIT_MANDATORY)) && (hasRemoteFeature(CHANNEL_RANGE_QUERIES_EX_BIT_OPTIONAL) || hasRemoteFeature(CHANNEL_RANGE_QUERIES_EX_BIT_MANDATORY)) + + if (canUseChannelRangeQueries || canUseChannelRangeQueriesEx) { + // if they support channel queries we don't send routing info yet, if they want it they will query us + // we will query them, using extended queries if supported + val flags_opt = if (canUseChannelRangeQueriesEx) Some(QueryChannelRangeTlv.QueryFlags(QueryChannelRangeTlv.QueryFlags.WANT_ALL)) else None if (nodeParams.syncWhitelist.isEmpty || nodeParams.syncWhitelist.contains(remoteNodeId)) { log.info(s"sending sync channel range query with flags_opt=$flags_opt") router ! SendChannelQuery(remoteNodeId, d.transport, flags_opt = flags_opt) } else { log.info("not syncing with this peer") } + } else if (hasRemoteFeature(INITIAL_ROUTING_SYNC_BIT_OPTIONAL)) { + // "old" nodes, do as before + log.info("peer requested a full routing table dump") + router ! GetRoutingState } // let's bring existing/requested channels online diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/TestConstants.scala b/eclair-core/src/test/scala/fr/acinq/eclair/TestConstants.scala index 757f37f368..f68d3575e8 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/TestConstants.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/TestConstants.scala @@ -71,7 +71,7 @@ object TestConstants { color = Color(1, 2, 3), publicAddresses = NodeAddress.fromParts("localhost", 9731).get :: Nil, globalFeatures = globalFeatures, - localFeatures = ByteVector(0), + localFeatures = ByteVector.fromValidHex("088a"), overrideFeatures = Map.empty, syncWhitelist = Set.empty, dustLimit = 1100 sat,