mirror of
https://github.com/ACINQ/eclair.git
synced 2024-11-20 02:27:32 +01:00
Add a sync whitelist (#954)
We will only sync with whilelisted peer. If the whitelist is empty then we sync with everyone.
This commit is contained in:
parent
8d1354a21d
commit
d67ba48fc0
@ -44,6 +44,7 @@ eclair {
|
||||
# local-features = ""
|
||||
# }
|
||||
]
|
||||
sync-whitelist = [] // a list of public keys; if non-empty, we will only do the initial sync with those peers
|
||||
channel-flags = 1 // announce channels
|
||||
|
||||
dust-limit-satoshis = 546
|
||||
|
@ -47,6 +47,7 @@ case class NodeParams(keyManager: KeyManager,
|
||||
globalFeatures: ByteVector,
|
||||
localFeatures: ByteVector,
|
||||
overrideFeatures: Map[PublicKey, (ByteVector, ByteVector)],
|
||||
syncWhitelist: Set[PublicKey],
|
||||
dustLimit: Satoshi,
|
||||
onChainFeeConf: OnChainFeeConf,
|
||||
maxHtlcValueInFlightMsat: UInt64,
|
||||
@ -162,6 +163,8 @@ object NodeParams {
|
||||
p -> (gf, lf)
|
||||
}.toMap
|
||||
|
||||
val syncWhitelist: Set[PublicKey] = config.getStringList("sync-whitelist").map(s => PublicKey(ByteVector.fromValidHex(s))).toSet
|
||||
|
||||
val socksProxy_opt = if (config.getBoolean("socks5.enabled")) {
|
||||
Some(Socks5ProxyParams(
|
||||
address = new InetSocketAddress(config.getString("socks5.host"), config.getInt("socks5.port")),
|
||||
@ -204,6 +207,7 @@ object NodeParams {
|
||||
globalFeatures = ByteVector.fromValidHex(config.getString("global-features")),
|
||||
localFeatures = ByteVector.fromValidHex(config.getString("local-features")),
|
||||
overrideFeatures = overrideFeatures,
|
||||
syncWhitelist = syncWhitelist,
|
||||
dustLimit = dustLimitSatoshis,
|
||||
onChainFeeConf = OnChainFeeConf(
|
||||
feeTargets = feeTargets,
|
||||
|
@ -159,8 +159,12 @@ class Peer(val nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: A
|
||||
Some(QueryChannelRangeTlv.QueryFlags(QueryChannelRangeTlv.QueryFlags.WANT_ALL))
|
||||
case _ => None
|
||||
}
|
||||
log.info(s"sending sync channel range query with flags_opt=$flags_opt")
|
||||
router ! SendChannelQuery(remoteNodeId, d.transport, flags_opt = flags_opt)
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
// let's bring existing/requested channels online
|
||||
|
@ -70,6 +70,7 @@ object TestConstants {
|
||||
globalFeatures = ByteVector.empty,
|
||||
localFeatures = ByteVector(0),
|
||||
overrideFeatures = Map.empty,
|
||||
syncWhitelist = Set.empty,
|
||||
dustLimit = 1100 sat,
|
||||
onChainFeeConf = OnChainFeeConf(
|
||||
feeTargets = FeeTargets(6, 2, 2, 6),
|
||||
@ -142,6 +143,7 @@ object TestConstants {
|
||||
globalFeatures = ByteVector.empty,
|
||||
localFeatures = ByteVector.empty, // no announcement
|
||||
overrideFeatures = Map.empty,
|
||||
syncWhitelist = Set.empty,
|
||||
dustLimit = 1000 sat,
|
||||
onChainFeeConf = OnChainFeeConf(
|
||||
feeTargets = FeeTargets(6, 2, 2, 6),
|
||||
|
@ -22,7 +22,7 @@ import akka.actor.FSM.{CurrentState, SubscribeTransitionCallBack, Transition}
|
||||
import akka.actor.{ActorRef, PoisonPill}
|
||||
import akka.testkit.{TestFSMRef, TestProbe}
|
||||
import fr.acinq.bitcoin.Crypto.PublicKey
|
||||
import fr.acinq.bitcoin.{Satoshi}
|
||||
import fr.acinq.bitcoin.Satoshi
|
||||
import fr.acinq.eclair.TestConstants._
|
||||
import fr.acinq.eclair._
|
||||
import fr.acinq.eclair.blockchain.{EclairWallet, TestWallet}
|
||||
@ -31,10 +31,10 @@ import fr.acinq.eclair.channel.{ChannelCreated, HasCommitments}
|
||||
import fr.acinq.eclair.crypto.TransportHandler
|
||||
import fr.acinq.eclair.io.Peer._
|
||||
import fr.acinq.eclair.router.RoutingSyncSpec.makeFakeRoutingInfo
|
||||
import fr.acinq.eclair.router.{Rebroadcast, RoutingSyncSpec}
|
||||
import fr.acinq.eclair.wire.{ChannelCodecsSpec, Color, EncodedShortChannelIds, EncodingType, Error, IPv4, NodeAddress, NodeAnnouncement, Ping, Pong, QueryShortChannelIds, Tlv, TlvStream}
|
||||
import fr.acinq.eclair.router.{Rebroadcast, RoutingSyncSpec, SendChannelQuery}
|
||||
import fr.acinq.eclair.wire.{ChannelCodecsSpec, Color, EncodedShortChannelIds, EncodingType, Error, IPv4, NodeAddress, NodeAnnouncement, Ping, Pong, QueryShortChannelIds, TlvStream}
|
||||
import org.scalatest.{Outcome, Tag}
|
||||
import scodec.bits.ByteVector
|
||||
import scodec.bits.{ByteVector, _}
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
@ -52,15 +52,6 @@ class PeerSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
||||
case class FixtureParam(remoteNodeId: PublicKey, authenticator: TestProbe, watcher: TestProbe, router: TestProbe, relayer: TestProbe, connection: TestProbe, transport: TestProbe, peer: TestFSMRef[Peer.State, Peer.Data, Peer])
|
||||
|
||||
override protected def withFixture(test: OneArgTest): Outcome = {
|
||||
val aParams = Alice.nodeParams
|
||||
val aliceParams = test.tags.contains("with_node_announcements") match {
|
||||
case true =>
|
||||
val bobAnnouncement = NodeAnnouncement(randomBytes64, ByteVector.empty, 1, Bob.nodeParams.nodeId, Color(100.toByte, 200.toByte, 300.toByte), "node-alias", fakeIPAddress :: Nil)
|
||||
aParams.db.network.addNode(bobAnnouncement)
|
||||
aParams
|
||||
case false => aParams
|
||||
}
|
||||
|
||||
val authenticator = TestProbe()
|
||||
val watcher = TestProbe()
|
||||
val router = TestProbe()
|
||||
@ -69,20 +60,35 @@ class PeerSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
||||
val transport = TestProbe()
|
||||
val wallet: EclairWallet = new TestWallet()
|
||||
val remoteNodeId = Bob.nodeParams.nodeId
|
||||
|
||||
import com.softwaremill.quicklens._
|
||||
val aliceParams = TestConstants.Alice.nodeParams
|
||||
.modify(_.syncWhitelist).setToIf(test.tags.contains("sync-whitelist-bob"))(Set(remoteNodeId))
|
||||
.modify(_.syncWhitelist).setToIf(test.tags.contains("sync-whitelist-random"))(Set(randomKey.publicKey))
|
||||
|
||||
if (test.tags.contains("with_node_announcements")) {
|
||||
val bobAnnouncement = NodeAnnouncement(randomBytes64, ByteVector.empty, 1, Bob.nodeParams.nodeId, Color(100.toByte, 200.toByte, 300.toByte), "node-alias", fakeIPAddress :: Nil)
|
||||
aliceParams.db.network.addNode(bobAnnouncement)
|
||||
}
|
||||
|
||||
val peer: TestFSMRef[Peer.State, Peer.Data, Peer] = TestFSMRef(new Peer(aliceParams, remoteNodeId, authenticator.ref, watcher.ref, router.ref, relayer.ref, wallet))
|
||||
withFixture(test.toNoArgTest(FixtureParam(remoteNodeId, authenticator, watcher, router, relayer, connection, transport, peer)))
|
||||
}
|
||||
|
||||
def connect(remoteNodeId: PublicKey, authenticator: TestProbe, watcher: TestProbe, router: TestProbe, relayer: TestProbe, connection: TestProbe, transport: TestProbe, peer: ActorRef, channels: Set[HasCommitments] = Set.empty): Unit = {
|
||||
def connect(remoteNodeId: PublicKey, authenticator: TestProbe, watcher: TestProbe, router: TestProbe, relayer: TestProbe, connection: TestProbe, transport: TestProbe, peer: ActorRef, channels: Set[HasCommitments] = Set.empty, remoteInit: wire.Init = wire.Init(Bob.nodeParams.globalFeatures, Bob.nodeParams.localFeatures), expectSync: Boolean = false): Unit = {
|
||||
// let's simulate a connection
|
||||
val probe = TestProbe()
|
||||
probe.send(peer, Peer.Init(None, channels))
|
||||
authenticator.send(peer, Authenticator.Authenticated(connection.ref, transport.ref, remoteNodeId, fakeIPAddress.socketAddress, outgoing = true, None))
|
||||
transport.expectMsgType[TransportHandler.Listener]
|
||||
transport.expectMsgType[wire.Init]
|
||||
transport.send(peer, wire.Init(Bob.nodeParams.globalFeatures, Bob.nodeParams.localFeatures))
|
||||
transport.send(peer, remoteInit)
|
||||
transport.expectMsgType[TransportHandler.ReadAck]
|
||||
router.expectNoMsg(1 second) // bob's features require no sync
|
||||
if (expectSync) {
|
||||
router.expectMsgType[SendChannelQuery]
|
||||
} else {
|
||||
router.expectNoMsg(1 second)
|
||||
}
|
||||
probe.send(peer, Peer.GetPeerInfo)
|
||||
assert(probe.expectMsgType[Peer.PeerInfo].state == "CONNECTED")
|
||||
}
|
||||
@ -255,6 +261,24 @@ class PeerSpec extends TestkitBaseClass with StateTestsHelperMethods {
|
||||
assert(channelCreated.fundingTxFeeratePerKw.get == peer.feeEstimator.getFeeratePerKw(peer.feeTargets.fundingBlockTarget))
|
||||
}
|
||||
|
||||
test("sync if no whitelist is defined") { f =>
|
||||
import f._
|
||||
val remoteInit = wire.Init(Bob.nodeParams.globalFeatures, bin"10000000".toByteVector) // bob support channel range queries
|
||||
connect(remoteNodeId, authenticator, watcher, router, relayer, connection, transport, peer, Set.empty, remoteInit, expectSync = true)
|
||||
}
|
||||
|
||||
test("sync if whitelist contains peer", Tag("sync-whitelist-bob")) { f =>
|
||||
import f._
|
||||
val remoteInit = wire.Init(Bob.nodeParams.globalFeatures, bin"10000000".toByteVector) // bob support channel range queries
|
||||
connect(remoteNodeId, authenticator, watcher, router, relayer, connection, transport, peer, Set.empty, remoteInit, expectSync = true)
|
||||
}
|
||||
|
||||
test("don't sync if whitelist doesn't contain peer", Tag("sync-whitelist-random")) { f =>
|
||||
import f._
|
||||
val remoteInit = wire.Init(Bob.nodeParams.globalFeatures, bin"10000000".toByteVector) // bob support channel range queries
|
||||
connect(remoteNodeId, authenticator, watcher, router, relayer, connection, transport, peer, Set.empty, remoteInit, expectSync = false)
|
||||
}
|
||||
|
||||
test("reply to ping") { f =>
|
||||
import f._
|
||||
val probe = TestProbe()
|
||||
|
Loading…
Reference in New Issue
Block a user