feat: Waku API create node (#3580)

* introduce createNode

# Conflicts:
#	apps/wakunode2/cli_args.nim

* remove confutils dependency on the library

* test: remove websocket in default test config

* update to latest specs

* test: cli_args

* align to spec changes (sovereign, message conf, entrypoints

* accept enr, entree and multiaddr as entry points

* post rebase

* format

* change from "sovereign" to "core"

* add example

* get example to continue running

* nitpicks

* idiomatic constructors

* fix enum naming

* replace procs with consts

* remove messageConfirmation

* use pure enum

* rename example file
This commit is contained in:
fryorcraken 2025-10-01 16:31:34 +10:00 committed by GitHub
parent 08d14fb082
commit bc8acf7611
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
45 changed files with 1086 additions and 70 deletions

View File

@ -14,8 +14,8 @@ import
libp2p/wire libp2p/wire
import import
../../tools/confutils/cli_args,
waku/[ waku/[
factory/external_config,
node/peer_manager, node/peer_manager,
waku_lightpush/common, waku_lightpush/common,
waku_relay, waku_relay,

View File

@ -11,11 +11,11 @@ import
confutils confutils
import import
../../tools/confutils/cli_args,
waku/[ waku/[
common/enr, common/enr,
common/logging, common/logging,
factory/waku as waku_factory, factory/waku as waku_factory,
factory/external_config,
waku_node, waku_node,
node/waku_metrics, node/waku_metrics,
node/peer_manager, node/peer_manager,

View File

@ -11,8 +11,8 @@ import
libp2p/wire libp2p/wire
import import
../wakunode2/cli_args,
waku/[ waku/[
factory/external_config,
common/enr, common/enr,
waku_node, waku_node,
node/peer_manager, node/peer_manager,

View File

@ -12,14 +12,9 @@ import
secp256k1 secp256k1
import import
waku/[ ../../tools/confutils/
common/confutils/envvar/defs as confEnvvarDefs, [cli_args, envvar as confEnvvarDefs, envvar_net as confEnvvarNet],
common/confutils/envvar/std/net as confEnvvarNet, waku/[common/logging, waku_core, waku_core/topics/pubsub_topic]
common/logging,
factory/external_config,
waku_core,
waku_core/topics/pubsub_topic,
]
export confTomlDefs, confTomlNet, confEnvvarDefs, confEnvvarNet export confTomlDefs, confTomlNet, confEnvvarDefs, confEnvvarNet

View File

@ -1,5 +1,5 @@
import results, options, chronos import results, options, chronos
import waku/[waku_node, waku_core, waku_lightpush] import waku/[waku_node, waku_core, waku_lightpush, waku_lightpush/common]
import publisher_base import publisher_base
type V3Publisher* = ref object of PublisherBase type V3Publisher* = ref object of PublisherBase

View File

@ -9,13 +9,13 @@ import
system/ansi_c, system/ansi_c,
libp2p/crypto/crypto libp2p/crypto/crypto
import import
../../tools/rln_keystore_generator/rln_keystore_generator, ../../tools/[rln_keystore_generator/rln_keystore_generator, confutils/cli_args],
waku/[ waku/[
common/logging, common/logging,
factory/external_config,
factory/waku, factory/waku,
node/health_monitor, node/health_monitor,
waku_api/rest/builder as rest_server_builder, waku_api/rest/builder as rest_server_builder,
waku_core/message/default_values,
] ]
logScope: logScope:

View File

@ -7,9 +7,21 @@ Make all examples.
make example2 make example2
``` ```
## basic2 ## Waku API
TODO Uses the simplified Waku API to create and start a node,
you need an RPC endpoint for Linea Sepolia for RLN:
```console
./build/waku_api --ethRpcEndpoint=https://linea-sepolia.infura.io/v3/<your key>
```
If you can't be bothered but still want to see some action,
just run the binary and it will use a non-RLN network:
```console
./build/waku_api
```
## publisher/subscriber ## publisher/subscriber

38
examples/waku_example.nim Normal file
View File

@ -0,0 +1,38 @@
import std/options
import chronos, results, confutils, confutils/defs
import waku
type CliArgs = object
ethRpcEndpoint* {.
defaultValue: "", desc: "ETH RPC Endpoint, if passed, RLN is enabled"
.}: string
when isMainModule:
let args = CliArgs.load()
echo "Starting Waku node..."
let config =
if (args.ethRpcEndpoint == ""):
# Create a basic configuration for the Waku node
# No RLN as we don't have an ETH RPC Endpoint
NodeConfig.init(wakuConfig = WakuConfig.init(entryNodes = @[], clusterId = 42))
else:
# Connect to TWN, use ETH RPC Endpoint for RLN
NodeConfig.init(ethRpcEndpoints = @[args.ethRpcEndpoint])
# Create the node using the library API's createNode function
let node = (waitFor createNode(config)).valueOr:
echo "Failed to create node: ", error
quit(QuitFailure)
echo("Waku node created successfully!")
# Start the node
(waitFor startWaku(addr node)).isOkOr:
echo "Failed to start node: ", error
quit(QuitFailure)
echo "Node started successfully!"
runForever()

View File

@ -1,6 +1,7 @@
{.push raises: [].} {.push raises: [].}
import waku/[common/logging, factory/[waku, networks_config, external_config]] import ../../apps/wakunode2/cli_args
import waku/[common/logging, factory/[waku, networks_config]]
import import
std/[options, strutils, os, sequtils], std/[options, strutils, os, sequtils],
chronicles, chronicles,

View File

@ -3,7 +3,7 @@ import chronos, chronicles, results, confutils, confutils/std/net
import import
../../../waku/node/peer_manager/peer_manager, ../../../waku/node/peer_manager/peer_manager,
../../../waku/factory/external_config, ../../../tools/confutils/cli_args,
../../../waku/factory/waku, ../../../waku/factory/waku,
../../../waku/factory/node_factory, ../../../waku/factory/node_factory,
../../../waku/factory/networks_config, ../../../waku/factory/networks_config,

View File

@ -2,7 +2,8 @@ import std/[net, sequtils, strutils]
import chronicles, chronos, stew/byteutils, results import chronicles, chronos, stew/byteutils, results
import import
../../../../waku/waku_core/message/message, ../../../../waku/waku_core/message/message,
../../../../waku/factory/[external_config, validator_signed, waku], ../../../../waku/factory/[validator_signed, waku],
../../../../tools/confutils/cli_args,
../../../../waku/waku_node, ../../../../waku/waku_node,
../../../../waku/waku_core/message, ../../../../waku/waku_core/message,
../../../../waku/waku_core/time, # Timestamp ../../../../waku/waku_core/time, # Timestamp

View File

@ -1,5 +1,7 @@
## Waku v2 ## Waku v2
import ./test_waku
# Waku core test suite # Waku core test suite
import import
./waku_core/test_namespaced_topics, ./waku_core/test_namespaced_topics,
@ -96,3 +98,9 @@ import ./waku_rln_relay/test_all
# Node Factory # Node Factory
import ./factory/test_all import ./factory/test_all
# Waku API tests
import ./api/test_all
# Waku tools tests
import ./tools/test_all

3
tests/api/test_all.nim Normal file
View File

@ -0,0 +1,3 @@
{.used.}
import ./test_entry_nodes, ./test_node_conf

View File

@ -0,0 +1,264 @@
{.used.}
import std/options, results, testutils/unittests
import waku/api/entry_nodes
# Since classifyEntryNode is internal, we test it indirectly through processEntryNodes behavior
# The enum is exported so we can test against it
suite "Entry Nodes Classification":
test "Process ENRTree - standard format":
let result = processEntryNodes(
@[
"enrtree://AIRVQ5DDA4FFWLRBCHJWUWOO6X6S4ZTZ5B667LQ6AJU6PEYDLRD5O@sandbox.waku.nodes.status.im"
]
)
check:
result.isOk()
let (enrTreeUrls, bootstrapEnrs, staticNodes) = result.get()
check:
enrTreeUrls.len == 1
bootstrapEnrs.len == 0
staticNodes.len == 0
test "Process ENRTree - case insensitive":
let result = processEntryNodes(
@[
"ENRTREE://AIRVQ5DDA4FFWLRBCHJWUWOO6X6S4ZTZ5B667LQ6AJU6PEYDLRD5O@sandbox.waku.nodes.status.im"
]
)
check:
result.isOk()
let (enrTreeUrls, bootstrapEnrs, staticNodes) = result.get()
check:
enrTreeUrls.len == 1
bootstrapEnrs.len == 0
staticNodes.len == 0
test "Process ENR - standard format":
let result = processEntryNodes(
@[
"enr:-QESuEC1p_s3xJzAC_XlOuuNrhVUETmfhbm1wxRGis0f7DlqGSw2FM-p2Vn7gmfkTTnAe8Ys2cgGBN8ufJnvzKQFZqFMBgmlkgnY0iXNlY3AyNTZrMaEDS8-D878DrdbNwcuY-3p1qdDp5MOoCurhdsNPJTXZ3c5g3RjcIJ2X4N1ZHCCd2g"
]
)
check:
result.isOk()
let (enrTreeUrls, bootstrapEnrs, staticNodes) = result.get()
check:
enrTreeUrls.len == 0
bootstrapEnrs.len == 1
staticNodes.len == 0
test "Process ENR - case insensitive":
let result = processEntryNodes(
@[
"ENR:-QESuEC1p_s3xJzAC_XlOuuNrhVUETmfhbm1wxRGis0f7DlqGSw2FM-p2Vn7gmfkTTnAe8Ys2cgGBN8ufJnvzKQFZqFMBgmlkgnY0iXNlY3AyNTZrMaEDS8-D878DrdbNwcuY-3p1qdDp5MOoCurhdsNPJTXZ3c5g3RjcIJ2X4N1ZHCCd2g"
]
)
check:
result.isOk()
let (enrTreeUrls, bootstrapEnrs, staticNodes) = result.get()
check:
enrTreeUrls.len == 0
bootstrapEnrs.len == 1
staticNodes.len == 0
test "Process Multiaddress - IPv4":
let result = processEntryNodes(
@[
"/ip4/127.0.0.1/tcp/60000/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYc"
]
)
check:
result.isOk()
let (enrTreeUrls, bootstrapEnrs, staticNodes) = result.get()
check:
enrTreeUrls.len == 0
bootstrapEnrs.len == 0
staticNodes.len == 1
test "Process Multiaddress - IPv6":
let result = processEntryNodes(
@["/ip6/::1/tcp/60000/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYc"]
)
check:
result.isOk()
let (enrTreeUrls, bootstrapEnrs, staticNodes) = result.get()
check:
enrTreeUrls.len == 0
bootstrapEnrs.len == 0
staticNodes.len == 1
test "Process Multiaddress - DNS":
let result = processEntryNodes(
@[
"/dns4/example.com/tcp/60000/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYc"
]
)
check:
result.isOk()
let (enrTreeUrls, bootstrapEnrs, staticNodes) = result.get()
check:
enrTreeUrls.len == 0
bootstrapEnrs.len == 0
staticNodes.len == 1
test "Process empty string":
let result = processEntryNodes(@[""])
check:
result.isErr()
result.error == "Entry node error: Empty entry node address"
test "Process invalid format - HTTP URL":
let result = processEntryNodes(@["http://example.com"])
check:
result.isErr()
result.error ==
"Entry node error: Unrecognized entry node format. Must start with 'enrtree:', 'enr:', or '/'"
test "Process invalid format - some string":
let result = processEntryNodes(@["some-string-here"])
check:
result.isErr()
result.error ==
"Entry node error: Unrecognized entry node format. Must start with 'enrtree:', 'enr:', or '/'"
suite "Entry Nodes Processing":
test "Process mixed entry nodes":
let entryNodes =
@[
"enrtree://AIRVQ5DDA4FFWLRBCHJWUWOO6X6S4ZTZ5B667LQ6AJU6PEYDLRD5O@sandbox.waku.nodes.status.im",
"/ip4/127.0.0.1/tcp/60000/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYc",
"enr:-QESuEC1p_s3xJzAC_XlOuuNrhVUETmfhbm1wxRGis0f7DlqGSw2FM-p2Vn7gmfkTTnAe8Ys2cgGBN8ufJnvzKQFZqFMBgmlkgnY0iXNlY3AyNTZrMaEDS8-D878DrdbNwcuY-3p1qdDp5MOoCurhdsNPJTXZ3c5g3RjcIJ2X4N1ZHCCd2g",
]
let result = processEntryNodes(entryNodes)
check:
result.isOk()
let (enrTreeUrls, bootstrapEnrs, staticNodes) = result.get()
check:
enrTreeUrls.len == 1 # enrtree
bootstrapEnrs.len == 1 # enr
staticNodes.len >= 1 # at least the multiaddr
enrTreeUrls[0] == entryNodes[0] # enrtree unchanged
bootstrapEnrs[0] == entryNodes[2] # enr unchanged
staticNodes[0] == entryNodes[1] # multiaddr added to static
test "Process only ENRTree nodes":
let entryNodes =
@[
"enrtree://AIRVQ5DDA4FFWLRBCHJWUWOO6X6S4ZTZ5B667LQ6AJU6PEYDLRD5O@sandbox.waku.nodes.status.im",
"enrtree://ANOTHER_TREE@example.com",
]
let result = processEntryNodes(entryNodes)
check:
result.isOk()
let (enrTreeUrls, bootstrapEnrs, staticNodes) = result.get()
check:
enrTreeUrls.len == 2
bootstrapEnrs.len == 0
staticNodes.len == 0
enrTreeUrls == entryNodes
test "Process only multiaddresses":
let entryNodes =
@[
"/ip4/127.0.0.1/tcp/60000/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYc",
"/ip4/192.168.1.1/tcp/60001/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYd",
]
let result = processEntryNodes(entryNodes)
check:
result.isOk()
let (enrTreeUrls, bootstrapEnrs, staticNodes) = result.get()
check:
enrTreeUrls.len == 0
bootstrapEnrs.len == 0
staticNodes.len == 2
staticNodes == entryNodes
test "Process only ENR nodes":
let entryNodes =
@[
"enr:-QESuEC1p_s3xJzAC_XlOuuNrhVUETmfhbm1wxRGis0f7DlqGSw2FM-p2Vn7gmfkTTnAe8Ys2cgGBN8ufJnvzKQFZqFMBgmlkgnY0iXNlY3AyNTZrMaEDS8-D878DrdbNwcuY-3p1qdDp5MOoCurhdsNPJTXZ3c5g3RjcIJ2X4N1ZHCCd2g",
"enr:-QEkuECnZ3IbVAgkOzv-QLnKC4dRKAPRY80m1-R7G8jZ7yfT3ipEfBrhKN7ARcQgQ-vg-h40AQzyvAkPYlHPaFKk6u9MBgmlkgnY0iXNlY3AyNTZrMaEDk49D8JjMSns4p1XVNBvJquOUzT4PENSJknkROspfAFGg3RjcIJ2X4N1ZHCCd2g",
]
let result = processEntryNodes(entryNodes)
check:
result.isOk()
let (enrTreeUrls, bootstrapEnrs, staticNodes) = result.get()
check:
enrTreeUrls.len == 0
bootstrapEnrs.len == 2
staticNodes.len == 0
bootstrapEnrs == entryNodes
# Note: staticNodes may or may not be populated depending on ENR parsing
test "Process empty list":
let entryNodes: seq[string] = @[]
let result = processEntryNodes(entryNodes)
check:
result.isOk()
let (enrTreeUrls, bootstrapEnrs, staticNodes) = result.get()
check:
enrTreeUrls.len == 0
bootstrapEnrs.len == 0
staticNodes.len == 0
test "Process with invalid entry":
let entryNodes = @["enrtree://VALID@example.com", "invalid://notvalid"]
let result = processEntryNodes(entryNodes)
check:
result.isErr()
result.error ==
"Entry node error: Unrecognized entry node format. Must start with 'enrtree:', 'enr:', or '/'"
test "Process different multiaddr formats":
let entryNodes =
@[
"/ip4/127.0.0.1/tcp/60000/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYc",
"/ip6/::1/tcp/60000/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYd",
"/dns4/example.com/tcp/60000/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYe",
"/dns/node.example.org/tcp/443/wss/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYf",
]
let result = processEntryNodes(entryNodes)
check:
result.isOk()
let (enrTreeUrls, bootstrapEnrs, staticNodes) = result.get()
check:
enrTreeUrls.len == 0
bootstrapEnrs.len == 0
staticNodes.len == 4
staticNodes == entryNodes
test "Process with duplicate entries":
let entryNodes =
@[
"/ip4/127.0.0.1/tcp/60000/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYc",
"/ip4/127.0.0.1/tcp/60000/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYc",
"enrtree://AIRVQ5DDA4FFWLRBCHJWUWOO6X6S4ZTZ5B667LQ6AJU6PEYDLRD5O@sandbox.waku.nodes.status.im",
"enrtree://AIRVQ5DDA4FFWLRBCHJWUWOO6X6S4ZTZ5B667LQ6AJU6PEYDLRD5O@sandbox.waku.nodes.status.im",
]
let result = processEntryNodes(entryNodes)
check:
result.isOk()
let (enrTreeUrls, bootstrapEnrs, staticNodes) = result.get()
check:
# Duplicates are not filtered out (by design - let downstream handle it)
enrTreeUrls.len == 2
bootstrapEnrs.len == 0
staticNodes.len == 2

View File

@ -0,0 +1,277 @@
{.used.}
import std/options, results, stint, testutils/unittests
import waku/api/api_conf, waku/factory/waku_conf, waku/factory/networks_config
suite "LibWaku Conf - toWakuConf":
test "Minimal configuration":
## Given
let nodeConfig = NodeConfig.init(ethRpcEndpoints = @["http://someaddress"])
## When
let wakuConfRes = toWakuConf(nodeConfig)
## Then
let wakuConf = wakuConfRes.valueOr:
raiseAssert error
wakuConf.validate().isOkOr:
raiseAssert error
check:
wakuConf.clusterId == 1
wakuConf.shardingConf.numShardsInCluster == 8
wakuConf.staticNodes.len == 0
test "Core mode configuration":
## Given
let wakuConfig = WakuConfig.init(entryNodes = @[], clusterId = 1)
let nodeConfig = NodeConfig.init(mode = Core, wakuConfig = wakuConfig)
## When
let wakuConfRes = toWakuConf(nodeConfig)
## Then
require wakuConfRes.isOk()
let wakuConf = wakuConfRes.get()
require wakuConf.validate().isOk()
check:
wakuConf.relay == true
wakuConf.lightPush == true
wakuConf.peerExchangeService == true
wakuConf.clusterId == 1
test "Auto-sharding configuration":
## Given
let nodeConfig = NodeConfig.init(
mode = Core,
wakuConfig = WakuConfig.init(
entryNodes = @[],
staticStoreNodes = @[],
clusterId = 42,
autoShardingConfig = AutoShardingConfig(numShardsInCluster: 16),
),
)
## When
let wakuConfRes = toWakuConf(nodeConfig)
## Then
require wakuConfRes.isOk()
let wakuConf = wakuConfRes.get()
require wakuConf.validate().isOk()
check:
wakuConf.clusterId == 42
wakuConf.shardingConf.numShardsInCluster == 16
test "Bootstrap nodes configuration":
## Given
let entryNodes =
@[
"enr:-QESuEC1p_s3xJzAC_XlOuuNrhVUETmfhbm1wxRGis0f7DlqGSw2FM-p2Vn7gmfkTTnAe8Ys2cgGBN8ufJnvzKQFZqFMBgmlkgnY0iXNlY3AyNTZrMaEDS8-D878DrdbNwcuY-3p1qdDp5MOoCurhdsNPJTXZ3c5g3RjcIJ2X4N1ZHCCd2g",
"enr:-QEkuECnZ3IbVAgkOzv-QLnKC4dRKAPRY80m1-R7G8jZ7yfT3ipEfBrhKN7ARcQgQ-vg-h40AQzyvAkPYlHPaFKk6u9MBgmlkgnY0iXNlY3AyNTZrMaEDk49D8JjMSns4p1XVNBvJquOUzT4PENSJknkROspfAFGg3RjcIJ2X4N1ZHCCd2g",
]
let libConf = NodeConfig.init(
mode = Core,
wakuConfig =
WakuConfig.init(entryNodes = entryNodes, staticStoreNodes = @[], clusterId = 1),
)
## When
let wakuConfRes = toWakuConf(libConf)
## Then
require wakuConfRes.isOk()
let wakuConf = wakuConfRes.get()
require wakuConf.validate().isOk()
require wakuConf.discv5Conf.isSome()
check:
wakuConf.discv5Conf.get().bootstrapNodes == entryNodes
test "Static store nodes configuration":
## Given
let staticStoreNodes =
@[
"/ip4/127.0.0.1/tcp/60000/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYc",
"/ip4/192.168.1.1/tcp/60001/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYd",
]
let nodeConf = NodeConfig.init(
wakuConfig = WakuConfig.init(
entryNodes = @[], staticStoreNodes = staticStoreNodes, clusterId = 1
)
)
## When
let wakuConfRes = toWakuConf(nodeConf)
## Then
require wakuConfRes.isOk()
let wakuConf = wakuConfRes.get()
require wakuConf.validate().isOk()
check:
wakuConf.staticNodes == staticStoreNodes
test "Message validation with max message size":
## Given
let nodeConfig = NodeConfig.init(
wakuConfig = WakuConfig.init(
entryNodes = @[],
staticStoreNodes = @[],
clusterId = 1,
messageValidation =
MessageValidation(maxMessageSize: "100KiB", rlnConfig: none(RlnConfig)),
)
)
## When
let wakuConfRes = toWakuConf(nodeConfig)
## Then
require wakuConfRes.isOk()
let wakuConf = wakuConfRes.get()
require wakuConf.validate().isOk()
check:
wakuConf.maxMessageSizeBytes == 100'u64 * 1024'u64
test "Message validation with RLN config":
## Given
let nodeConfig = NodeConfig.init(
wakuConfig = WakuConfig.init(
entryNodes = @[],
clusterId = 1,
messageValidation = MessageValidation(
maxMessageSize: "150 KiB",
rlnConfig: some(
RlnConfig(
contractAddress: "0x1234567890123456789012345678901234567890",
chainId: 1'u,
epochSizeSec: 600'u64,
)
),
),
),
ethRpcEndpoints = @["http://127.0.0.1:1111"],
)
## When
let wakuConf = toWakuConf(nodeConfig).valueOr:
raiseAssert error
wakuConf.validate().isOkOr:
raiseAssert error
check:
wakuConf.maxMessageSizeBytes == 150'u64 * 1024'u64
require wakuConf.rlnRelayConf.isSome()
let rlnConf = wakuConf.rlnRelayConf.get()
check:
rlnConf.dynamic == true
rlnConf.ethContractAddress == "0x1234567890123456789012345678901234567890"
rlnConf.chainId == 1'u256
rlnConf.epochSizeSec == 600'u64
test "Full Core mode configuration with all fields":
## Given
let nodeConfig = NodeConfig.init(
mode = Core,
wakuConfig = WakuConfig.init(
entryNodes =
@[
"enr:-QESuEC1p_s3xJzAC_XlOuuNrhVUETmfhbm1wxRGis0f7DlqGSw2FM-p2Vn7gmfkTTnAe8Ys2cgGBN8ufJnvzKQFZqFMBgmlkgnY0iXNlY3AyNTZrMaEDS8-D878DrdbNwcuY-3p1qdDp5MOoCurhdsNPJTXZ3c5g3RjcIJ2X4N1ZHCCd2g"
],
staticStoreNodes =
@[
"/ip4/127.0.0.1/tcp/60000/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYc"
],
clusterId = 99,
autoShardingConfig = AutoShardingConfig(numShardsInCluster: 12),
messageValidation = MessageValidation(
maxMessageSize: "512KiB",
rlnConfig: some(
RlnConfig(
contractAddress: "0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
chainId: 5'u, # Goerli
epochSizeSec: 300'u64,
)
),
),
),
ethRpcEndpoints = @["https://127.0.0.1:8333"],
)
## When
let wakuConfRes = toWakuConf(nodeConfig)
## Then
let wakuConf = wakuConfRes.valueOr:
raiseAssert error
wakuConf.validate().isOkOr:
raiseAssert error
# Check basic settings
check:
wakuConf.relay == true
wakuConf.lightPush == true
wakuConf.peerExchangeService == true
wakuConf.rendezvous == true
wakuConf.clusterId == 99
# Check sharding
check:
wakuConf.shardingConf.numShardsInCluster == 12
# Check bootstrap nodes
require wakuConf.discv5Conf.isSome()
check:
wakuConf.discv5Conf.get().bootstrapNodes.len == 1
# Check static nodes
check:
wakuConf.staticNodes.len == 1
wakuConf.staticNodes[0] ==
"/ip4/127.0.0.1/tcp/60000/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYc"
# Check message validation
check:
wakuConf.maxMessageSizeBytes == 512'u64 * 1024'u64
# Check RLN config
require wakuConf.rlnRelayConf.isSome()
let rlnConf = wakuConf.rlnRelayConf.get()
check:
rlnConf.dynamic == true
rlnConf.ethContractAddress == "0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
rlnConf.chainId == 5'u256
rlnConf.epochSizeSec == 300'u64
test "NodeConfig with mixed entry nodes (integration test)":
## Given
let entryNodes =
@[
"enrtree://AIRVQ5DDA4FFWLRBCHJWUWOO6X6S4ZTZ5B667LQ6AJU6PEYDLRD5O@sandbox.waku.nodes.status.im",
"/ip4/127.0.0.1/tcp/60000/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYc",
]
let nodeConfig = NodeConfig.init(
mode = Core,
wakuConfig =
WakuConfig.init(entryNodes = entryNodes, staticStoreNodes = @[], clusterId = 1),
)
## When
let wakuConfRes = toWakuConf(nodeConfig)
## Then
require wakuConfRes.isOk()
let wakuConf = wakuConfRes.get()
require wakuConf.validate().isOk()
# Check that ENRTree went to DNS discovery
require wakuConf.dnsDiscoveryConf.isSome()
check:
wakuConf.dnsDiscoveryConf.get().enrTreeUrl == entryNodes[0]
# Check that multiaddr went to static nodes
check:
wakuConf.staticNodes.len == 1
wakuConf.staticNodes[0] == entryNodes[1]

View File

@ -2,9 +2,7 @@
import import
./test_base64_codec, ./test_base64_codec,
./test_confutils_envvar,
./test_enr_builder, ./test_enr_builder,
./test_envvar_serialization,
./test_protobuf_validation, ./test_protobuf_validation,
./test_sqlite_migrations, ./test_sqlite_migrations,
./test_parse_size, ./test_parse_size,

View File

@ -1,3 +1,3 @@
{.used.} {.used.}
import ./test_external_config, ./test_node_factory, ./test_waku_conf import ./test_waku_conf, ./test_node_factory

View File

@ -4,9 +4,10 @@ import testutils/unittests, chronos, libp2p/protocols/connectivity/relay/relay
import import
../testlib/wakunode, ../testlib/wakunode,
waku/factory/node_factory,
waku/waku_node, waku/waku_node,
waku/factory/conf_builder/conf_builder waku/factory/node_factory,
waku/factory/conf_builder/conf_builder,
waku/factory/conf_builder/web_socket_conf_builder
suite "Node Factory": suite "Node Factory":
asynctest "Set up a node based on default configurations": asynctest "Set up a node based on default configurations":
@ -48,8 +49,9 @@ asynctest "Set up a node with Filter enabled":
check: check:
not node.isNil() not node.isNil()
not node.wakuFilter.isNil() not node.wakuFilter.isNil()
echo "TEST END"
asynctest "Start a node based on default configurations": asynctest "Start a node based on default test configuration":
let conf = defaultTestWakuConf() let conf = defaultTestWakuConf()
let node = (await setupNode(conf, relay = Relay.new())).valueOr: let node = (await setupNode(conf, relay = Relay.new())).valueOr:

91
tests/test_waku.nim Normal file
View File

@ -0,0 +1,91 @@
{.used.}
import chronos, testutils/unittests, std/options
import waku
suite "Waku API - Create node":
asyncTest "Create node with minimal configuration":
## Given
let nodeConfig =
NodeConfig.init(wakuConfig = WakuConfig.init(entryNodes = @[], clusterId = 1))
# This is the actual minimal config but as the node auto-start, it is not suitable for tests
# NodeConfig.init(ethRpcEndpoints = @["http://someaddress"])
## When
let node = (await createNode(nodeConfig)).valueOr:
raiseAssert error
## Then
check:
not node.isNil()
node.conf.clusterId == 1
node.conf.relay == true
asyncTest "Create node with full configuration":
## Given
let nodeConfig = NodeConfig.init(
mode = Core,
wakuConfig = WakuConfig.init(
entryNodes =
@[
"enr:-QESuEC1p_s3xJzAC_XlOuuNrhVUETmfhbm1wxRGis0f7DlqGSw2FM-p2Vn7gmfkTTnAe8Ys2cgGBN8ufJnvzKQFZqFMBgmlkgnY0iXNlY3AyNTZrMaEDS8-D878DrdbNwcuY-3p1qdDp5MOoCurhdsNPJTXZ3c5g3RjcIJ2X4N1ZHCCd2g"
],
staticStoreNodes =
@[
"/ip4/127.0.0.1/tcp/60000/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYc"
],
clusterId = 99,
autoShardingConfig = AutoShardingConfig(numShardsInCluster: 16),
messageValidation =
MessageValidation(maxMessageSize: "1024 KiB", rlnConfig: none(RlnConfig)),
),
)
## When
let node = (await createNode(nodeConfig)).valueOr:
raiseAssert error
## Then
check:
not node.isNil()
node.conf.clusterId == 99
node.conf.shardingConf.numShardsInCluster == 16
node.conf.maxMessageSizeBytes == 1024'u64 * 1024'u64
node.conf.staticNodes.len == 1
node.conf.relay == true
node.conf.lightPush == true
node.conf.peerExchangeService == true
node.conf.rendezvous == true
asyncTest "Create node with mixed entry nodes (enrtree, multiaddr)":
## Given
let nodeConfig = NodeConfig.init(
mode = Core,
wakuConfig = WakuConfig.init(
entryNodes =
@[
"enrtree://AIRVQ5DDA4FFWLRBCHJWUWOO6X6S4ZTZ5B667LQ6AJU6PEYDLRD5O@sandbox.waku.nodes.status.im",
"/ip4/127.0.0.1/tcp/60000/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYc",
],
clusterId = 42,
),
)
## When
let node = (await createNode(nodeConfig)).valueOr:
raiseAssert error
## Then
check:
not node.isNil()
node.conf.clusterId == 42
# ENRTree should go to DNS discovery
node.conf.dnsDiscoveryConf.isSome()
node.conf.dnsDiscoveryConf.get().enrTreeUrl ==
"enrtree://AIRVQ5DDA4FFWLRBCHJWUWOO6X6S4ZTZ5B667LQ6AJU6PEYDLRD5O@sandbox.waku.nodes.status.im"
# Multiaddr should go to static nodes
node.conf.staticNodes.len == 1
node.conf.staticNodes[0] ==
"/ip4/127.0.0.1/tcp/60000/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYc"

View File

@ -4,7 +4,10 @@ import chronos, confutils/toml/std/net, libp2p/multiaddress, testutils/unittests
import ./testlib/wakunode, waku/waku_enr/capabilities import ./testlib/wakunode, waku/waku_enr/capabilities
include waku/node/net_config include
waku/node/net_config,
waku/factory/conf_builder/web_socket_conf_builder,
waku/factory/conf_builder/conf_builder
proc defaultTestWakuFlags(): CapabilitiesBitfield = proc defaultTestWakuFlags(): CapabilitiesBitfield =
CapabilitiesBitfield.init( CapabilitiesBitfield.init(
@ -150,9 +153,15 @@ suite "Waku NetConfig":
netConfig.announcedAddresses[0] == dns4TcpEndPoint(dns4DomainName, extPort) netConfig.announcedAddresses[0] == dns4TcpEndPoint(dns4DomainName, extPort)
asyncTest "AnnouncedAddresses includes WebSocket addresses when enabled": asyncTest "AnnouncedAddresses includes WebSocket addresses when enabled":
var var confBuilder = defaultTestWakuConfBuilder()
conf = defaultTestWakuConf()
wssEnabled = false confBuilder.webSocketConf.withEnabled(true)
confBuilder.webSocketConf.withWebSocketPort(Port(8000))
let conf = confBuilder.build().valueOr:
raiseAssert error
var wssEnabled = false
var netConfigRes = NetConfig.init( var netConfigRes = NetConfig.init(
bindIp = conf.endpointConf.p2pListenAddress, bindIp = conf.endpointConf.p2pListenAddress,
@ -197,8 +206,14 @@ suite "Waku NetConfig":
) )
asyncTest "Announced WebSocket address contains external IP if provided": asyncTest "Announced WebSocket address contains external IP if provided":
var confBuilder = defaultTestWakuConfBuilder()
confBuilder.webSocketConf.withEnabled(true)
confBuilder.webSocketConf.withWebSocketPort(Port(8000))
let conf = confBuilder.build().valueOr:
raiseAssert error
let let
conf = defaultTestWakuConf()
extIp = parseIpAddress("1.2.3.4") extIp = parseIpAddress("1.2.3.4")
extPort = Port(1234) extPort = Port(1234)
wssEnabled = false wssEnabled = false
@ -222,8 +237,14 @@ suite "Waku NetConfig":
(ip4TcpEndPoint(extIp, conf.websocketConf.get().port) & wsFlag(wssEnabled)) (ip4TcpEndPoint(extIp, conf.websocketConf.get().port) & wsFlag(wssEnabled))
asyncTest "Announced WebSocket address contains dns4DomainName if provided": asyncTest "Announced WebSocket address contains dns4DomainName if provided":
var confBuilder = defaultTestWakuConfBuilder()
confBuilder.webSocketConf.withEnabled(true)
confBuilder.webSocketConf.withWebSocketPort(Port(8000))
let conf = confBuilder.build().valueOr:
raiseAssert error
let let
conf = defaultTestWakuConf()
dns4DomainName = "example.com" dns4DomainName = "example.com"
extPort = Port(1234) extPort = Port(1234)
wssEnabled = false wssEnabled = false
@ -249,8 +270,14 @@ suite "Waku NetConfig":
) )
asyncTest "Announced WebSocket address contains dns4DomainName if provided alongside extIp": asyncTest "Announced WebSocket address contains dns4DomainName if provided alongside extIp":
var confBuilder = defaultTestWakuConfBuilder()
confBuilder.webSocketConf.withEnabled(true)
confBuilder.webSocketConf.withWebSocketPort(Port(8000))
let conf = confBuilder.build().valueOr:
raiseAssert error
let let
conf = defaultTestWakuConf()
dns4DomainName = "example.com" dns4DomainName = "example.com"
extIp = parseIpAddress("1.2.3.4") extIp = parseIpAddress("1.2.3.4")
extPort = Port(1234) extPort = Port(1234)

View File

@ -42,8 +42,6 @@ proc defaultTestWakuConfBuilder*(): WakuConfBuilder =
builder.withRendezvous(true) builder.withRendezvous(true)
builder.storeServiceConf.withDbMigration(false) builder.storeServiceConf.withDbMigration(false)
builder.storeServiceConf.withSupportV2(false) builder.storeServiceConf.withSupportV2(false)
builder.webSocketConf.withWebSocketPort(Port(8000))
builder.webSocketConf.withEnabled(true)
return builder return builder
proc defaultTestWakuConf*(): WakuConf = proc defaultTestWakuConf*(): WakuConf =

3
tests/tools/test_all.nim Normal file
View File

@ -0,0 +1,3 @@
{.used.}
import ./test_confutils_envvar, ./test_confutils_envvar_serialization.nim

View File

@ -7,9 +7,7 @@ import
confutils, confutils,
confutils/defs, confutils/defs,
confutils/std/net confutils/std/net
import import ../../tools/confutils/[envvar as confEnvvarDefs, envvar_net as confEnvvarNet]
waku/common/confutils/envvar/defs as confEnvvarDefs,
waku/common/confutils/envvar/std/net as confEnvvarNet
type ConfResult[T] = Result[T, string] type ConfResult[T] = Result[T, string]

View File

@ -1,7 +1,7 @@
{.used.} {.used.}
import testutils/unittests import testutils/unittests
import waku/common/envvar_serialization/utils import ../../tools/confutils/envvar_serialization/utils
suite "nim-envvar-serialization - utils": suite "nim-envvar-serialization - utils":
test "construct env var key": test "construct env var key":

View File

@ -439,7 +439,6 @@ suite "Waku Discovery v5":
confBuilder.discv5Conf.withEnabled(true) confBuilder.discv5Conf.withEnabled(true)
confBuilder.discv5Conf.withUdpPort(9001.Port) confBuilder.discv5Conf.withUdpPort(9001.Port)
confBuilder.withP2pTcpPort(60001.Port) confBuilder.withP2pTcpPort(60001.Port)
confBuilder.websocketConf.withEnabled(false)
let conf1 = confBuilder.build().valueOr: let conf1 = confBuilder.build().valueOr:
raiseAssert error raiseAssert error
@ -456,7 +455,6 @@ suite "Waku Discovery v5":
confBuilder.withP2pTcpPort(60003.Port) confBuilder.withP2pTcpPort(60003.Port)
confBuilder.discv5Conf.withUdpPort(9003.Port) confBuilder.discv5Conf.withUdpPort(9003.Port)
confBuilder.withNodeKey(crypto.PrivateKey.random(Secp256k1, myRng[])[]) confBuilder.withNodeKey(crypto.PrivateKey.random(Secp256k1, myRng[])[])
confBuilder.websocketConf.withEnabled(false)
let conf2 = confBuilder.build().valueOr: let conf2 = confBuilder.build().valueOr:
raiseAssert error raiseAssert error

View File

@ -1,3 +1,3 @@
{.used.} {.used.}
import ./test_app, ./test_validators import ./test_app, ./test_validators, ./test_cli_args

View File

@ -10,12 +10,15 @@ import
secp256k1, secp256k1,
confutils, confutils,
stint stint
import tools/confutils/cli_args
import import
../../waku/factory/external_config,
../../waku/factory/networks_config, ../../waku/factory/networks_config,
../../waku/factory/waku_conf, ../../waku/factory/waku_conf,
../../waku/common/logging, ../../waku/common/logging,
../../waku/common/utils/parse_size_units ../../waku/common/utils/parse_size_units,
../../waku/waku_core/message/default_values
suite "Waku external config - default values": suite "Waku external config - default values":
test "Default sharding value": test "Default sharding value":

View File

@ -12,14 +12,8 @@ import
libp2p/multihash, libp2p/multihash,
secp256k1 secp256k1
import import
waku/[ waku/[waku_core, node/peer_manager, waku_node, factory/validator_signed],
waku_core, tools/confutils/cli_args,
node/peer_manager,
waku_node,
waku_relay,
factory/external_config,
factory/validator_signed,
],
../testlib/wakucore, ../testlib/wakucore,
../testlib/wakunode ../testlib/wakunode

View File

@ -19,23 +19,22 @@ import
json json
import import
./waku_conf, waku/factory/[waku_conf, conf_builder/conf_builder, networks_config],
./conf_builder/conf_builder, waku/common/[logging],
./networks_config, waku/[
../common/confutils/envvar/defs as confEnvvarDefs, waku_enr,
../common/confutils/envvar/std/net as confEnvvarNet, node/peer_manager,
../common/logging, waku_core/topics/pubsub_topic,
../waku_enr, waku_core/message/default_values,
../node/peer_manager, ],
../waku_core/topics/pubsub_topic,
../../tools/rln_keystore_generator/rln_keystore_generator ../../tools/rln_keystore_generator/rln_keystore_generator
include ../waku_core/message/default_values import ./envvar as confEnvvarDefs, ./envvar_net as confEnvvarNet
export confTomlDefs, confTomlNet, confEnvvarDefs, confEnvvarNet, ProtectedShard export confTomlDefs, confTomlNet, confEnvvarDefs, confEnvvarNet, ProtectedShard
logScope: logScope:
topics = "waku external config" topics = "waku cli args"
# Git version in git describe format (defined at compile time) # Git version in git describe format (defined at compile time)
const git_version* {.strdefine.} = "n/a" const git_version* {.strdefine.} = "n/a"
@ -161,7 +160,7 @@ type WakuNodeConf* = object
.}: uint16 .}: uint16
agentString* {. agentString* {.
defaultValue: "nwaku-" & external_config.git_version, defaultValue: "nwaku-" & cli_args.git_version,
desc: "Node agent string which is used as identifier in network", desc: "Node agent string which is used as identifier in network",
name: "agent-string" name: "agent-string"
.}: string .}: string

View File

@ -1,7 +1,7 @@
{.push raises: [].} {.push raises: [].}
import confutils/defs as confutilsDefs import confutils/defs as confutilsDefs
import ../../envvar_serialization import ./envvar_serialization
export envvar_serialization, confutilsDefs export envvar_serialization, confutilsDefs

View File

@ -1,7 +1,7 @@
{.push raises: [].} {.push raises: [].}
import std/[strutils, net] import std/[strutils, net]
import ../../../envvar_serialization import ./envvar_serialization
export net, envvar_serialization export net, envvar_serialization

10
waku.nim Normal file
View File

@ -0,0 +1,10 @@
## Main module for using nwaku as a Nimble library
##
## This module re-exports the public API for creating and managing Waku nodes
## when using nwaku as a library dependency.
import waku/api/[api, api_conf]
export api, api_conf
import waku/factory/waku
export waku

View File

@ -140,6 +140,7 @@ task testwakunode2, "Build & run wakunode2 app tests":
test "all_tests_wakunode2" test "all_tests_wakunode2"
task example2, "Build Waku examples": task example2, "Build Waku examples":
buildBinary "waku_example", "examples/"
buildBinary "publisher", "examples/" buildBinary "publisher", "examples/"
buildBinary "subscriber", "examples/" buildBinary "subscriber", "examples/"
buildBinary "filter_subscriber", "examples/" buildBinary "filter_subscriber", "examples/"

17
waku/api/api.nim Normal file
View File

@ -0,0 +1,17 @@
import chronicles, chronos, results
import waku/factory/waku
import ./api_conf
# TODO: Specs says it should return a `WakuNode`. As `send` and other APIs are defined, we can align.
proc createNode*(config: NodeConfig): Future[Result[Waku, string]] {.async.} =
let wakuConf = toWakuConf(config).valueOr:
return err("Failed to handle the configuration: " & error)
## We are not defining app callbacks at node creation
let wakuRes = (await Waku.new(wakuConf)).valueOr:
error "waku initialization failed", error = error
return err("Failed setting up Waku: " & $error)
return ok(wakuRes)

203
waku/api/api_conf.nim Normal file
View File

@ -0,0 +1,203 @@
import std/[net, options]
import results
import
waku/common/utils/parse_size_units,
waku/factory/waku_conf,
waku/factory/conf_builder/conf_builder,
waku/factory/networks_config,
./entry_nodes
type AutoShardingConfig* {.requiresInit.} = object
numShardsInCluster*: uint16
type RlnConfig* {.requiresInit.} = object
contractAddress*: string
chainId*: uint
epochSizeSec*: uint64
type NetworkingConfig* {.requiresInit.} = object
listenIpv4*: string
p2pTcpPort*: uint16
discv5UdpPort*: uint16
type MessageValidation* {.requiresInit.} = object
maxMessageSize*: string # Accepts formats like "150 KiB", "1500 B"
rlnConfig*: Option[RlnConfig]
type WakuConfig* {.requiresInit.} = object
entryNodes: seq[string]
staticStoreNodes: seq[string]
clusterId: uint16
autoShardingConfig: AutoShardingConfig
messageValidation: MessageValidation
const DefaultNetworkingConfig* =
NetworkingConfig(listenIpv4: "0.0.0.0", p2pTcpPort: 60000, discv5UdpPort: 9000)
const DefaultAutoShardingConfig* = AutoShardingConfig(numShardsInCluster: 1)
const DefaultMessageValidation* =
MessageValidation(maxMessageSize: "150 KiB", rlnConfig: none(RlnConfig))
proc init*(
T: typedesc[WakuConfig],
entryNodes: seq[string],
staticStoreNodes: seq[string] = @[],
clusterId: uint16,
autoShardingConfig: AutoShardingConfig = DefaultAutoShardingConfig,
messageValidation: MessageValidation = DefaultMessageValidation,
): T =
return T(
entryNodes: entryNodes,
staticStoreNodes: staticStoreNodes,
clusterId: clusterId,
autoShardingConfig: autoShardingConfig,
messageValidation: messageValidation,
)
const TheWakuNetworkPreset* = WakuConfig(
entryNodes:
@[
"enrtree://AIRVQ5DDA4FFWLRBCHJWUWOO6X6S4ZTZ5B667LQ6AJU6PEYDLRD5O@sandbox.waku.nodes.status.im"
],
staticStoreNodes: @[],
clusterId: 1,
autoShardingConfig: AutoShardingConfig(numShardsInCluster: 8),
messageValidation: MessageValidation(
maxMessageSize: "150 KiB",
rlnConfig: some(
RlnConfig(
contractAddress: "0xB9cd878C90E49F797B4431fBF4fb333108CB90e6",
chainId: 59141,
epochSizeSec: 600, # 10 minutes
)
),
),
)
type WakuMode* {.pure.} = enum
Edge
Core
type NodeConfig* {.requiresInit.} = object
mode: WakuMode
wakuConfig: WakuConfig
networkingConfig: NetworkingConfig
ethRpcEndpoints: seq[string]
proc init*(
T: typedesc[NodeConfig],
mode: WakuMode = WakuMode.Core,
wakuConfig: WakuConfig = TheWakuNetworkPreset,
networkingConfig: NetworkingConfig = DefaultNetworkingConfig,
ethRpcEndpoints: seq[string] = @[],
): T =
return T(
mode: mode,
wakuConfig: wakuConfig,
networkingConfig: networkingConfig,
ethRpcEndpoints: ethRpcEndpoints,
)
proc toWakuConf*(nodeConfig: NodeConfig): Result[WakuConf, string] =
var b = WakuConfBuilder.init()
# Apply networking configuration
let networkingConfig = nodeConfig.networkingConfig
let ip = parseIpAddress(networkingConfig.listenIpv4)
b.withP2pListenAddress(ip)
b.withP2pTcpPort(networkingConfig.p2pTcpPort)
b.discv5Conf.withUdpPort(networkingConfig.discv5UdpPort)
case nodeConfig.mode
of Core:
b.withRelay(true)
# Metadata is always mounted
b.filterServiceConf.withEnabled(true)
b.filterServiceConf.withMaxPeersToServe(20)
b.withLightPush(true)
b.discv5Conf.withEnabled(true)
b.withPeerExchange(true)
b.withRendezvous(true)
# TODO: fix store as client usage
b.rateLimitConf.withRateLimits(@["filter:100/1s", "lightpush:5/1s", "px:5/1s"])
of Edge:
return err("Edge mode is not implemented")
## Network Conf
let wakuConfig = nodeConfig.wakuConfig
# Set cluster ID
b.withClusterId(wakuConfig.clusterId)
# Set sharding configuration
b.withShardingConf(ShardingConfKind.AutoSharding)
let autoShardingConfig = wakuConfig.autoShardingConfig
b.withNumShardsInCluster(autoShardingConfig.numShardsInCluster)
# Process entry nodes - supports enrtree:, enr:, and multiaddress formats
if wakuConfig.entryNodes.len > 0:
let (enrTreeUrls, bootstrapEnrs, staticNodesFromEntry) = processEntryNodes(
wakuConfig.entryNodes
).valueOr:
return err("Failed to process entry nodes: " & error)
# Set ENRTree URLs for DNS discovery
if enrTreeUrls.len > 0:
for url in enrTreeUrls:
b.dnsDiscoveryConf.withEnrTreeUrl(url)
b.dnsDiscoveryconf.withNameServers(
@[parseIpAddress("1.1.1.1"), parseIpAddress("1.0.0.1")]
)
# Set ENR records as bootstrap nodes for discv5
if bootstrapEnrs.len > 0:
b.discv5Conf.withBootstrapNodes(bootstrapEnrs)
# Add static nodes (multiaddrs and those extracted from ENR entries)
if staticNodesFromEntry.len > 0:
b.withStaticNodes(staticNodesFromEntry)
# TODO: verify behaviour
# Set static store nodes
if wakuConfig.staticStoreNodes.len > 0:
b.withStaticNodes(wakuConfig.staticStoreNodes)
# Set message validation
let msgValidation = wakuConfig.messageValidation
let maxSizeBytes = parseMsgSize(msgValidation.maxMessageSize).valueOr:
return err("Failed to parse max message size: " & error)
b.withMaxMessageSize(maxSizeBytes)
# Set RLN config if provided
if msgValidation.rlnConfig.isSome():
let rlnConfig = msgValidation.rlnConfig.get()
b.rlnRelayConf.withEnabled(true)
b.rlnRelayConf.withEthContractAddress(rlnConfig.contractAddress)
b.rlnRelayConf.withChainId(rlnConfig.chainId)
b.rlnRelayConf.withEpochSizeSec(rlnConfig.epochSizeSec)
b.rlnRelayConf.withDynamic(true)
b.rlnRelayConf.withEthClientUrls(nodeConfig.ethRpcEndpoints)
# TODO: we should get rid of those two
b.rlnRelayconf.withUserMessageLimit(100)
## Various configurations
b.withNatStrategy("any")
let wakuConf = b.build().valueOr:
return err("Failed to build configuration: " & error)
wakuConf.validate().isOkOr:
return err("Failed to validate configuration: " & error)
return ok(wakuConf)

77
waku/api/entry_nodes.nim Normal file
View File

@ -0,0 +1,77 @@
import std/strutils
import results, eth/p2p/discoveryv5/enr
import waku/waku_core/peers
type EntryNodeType {.pure.} = enum
EnrTree
Enr
Multiaddr
proc classifyEntryNode(address: string): Result[EntryNodeType, string] =
## Classifies an entry node address by its type
## Returns the type as EntryNodeType enum
if address.len == 0:
return err("Empty entry node address")
let lowerAddress = address.toLowerAscii()
if lowerAddress.startsWith("enrtree:"):
return ok(EnrTree)
elif lowerAddress.startsWith("enr:"):
return ok(Enr)
elif address[0] == '/':
return ok(Multiaddr)
else:
return
err("Unrecognized entry node format. Must start with 'enrtree:', 'enr:', or '/'")
proc parseEnrToMultiaddrs(enrStr: string): Result[seq[string], string] =
## Parses an ENR string and extracts multiaddresses from it
let enrRec = enr.Record.fromURI(enrStr).valueOr:
return err("Invalid ENR record")
let remotePeerInfo = toRemotePeerInfo(enrRec).valueOr:
return err("Failed to convert ENR to peer info: " & $error)
# Convert RemotePeerInfo addresses to multiaddr strings
var multiaddrs: seq[string]
for addr in remotePeerInfo.addrs:
multiaddrs.add($addr & "/p2p/" & $remotePeerInfo.peerId)
if multiaddrs.len == 0:
return err("No valid addresses found in ENR")
return ok(multiaddrs)
proc processEntryNodes*(
entryNodes: seq[string]
): Result[(seq[string], seq[string], seq[string]), string] =
## Processes entry nodes and returns (enrTreeUrls, bootstrapEnrs, staticNodes)
## ENRTree URLs for DNS discovery, ENR records for bootstrap, multiaddrs for static nodes
var enrTreeUrls: seq[string]
var bootstrapEnrs: seq[string]
var staticNodes: seq[string]
for node in entryNodes:
let nodeType = classifyEntryNode(node).valueOr:
return err("Entry node error: " & error)
case nodeType
of EnrTree:
# ENRTree URLs go to DNS discovery configuration
enrTreeUrls.add(node)
of Enr:
# ENR records go to bootstrap nodes for discv5
bootstrapEnrs.add(node)
# Additionally, extract multiaddrs for static connections
let multiaddrsRes = parseEnrToMultiaddrs(node)
if multiaddrsRes.isOk():
for maddr in multiaddrsRes.get():
staticNodes.add(maddr)
# If we can't extract multiaddrs, just use it as bootstrap (already added above)
of Multiaddr:
# Multiaddresses go to static nodes
staticNodes.add(node)
return ok((enrTreeUrls, bootstrapEnrs, staticNodes))

View File

@ -1,7 +1,7 @@
import import
libp2p/crypto/crypto, libp2p/crypto/crypto,
libp2p/multiaddress, libp2p/multiaddress,
std/[net, options, sequtils, strutils], std/[net, options, sequtils],
stint, stint,
chronicles, chronicles,
chronos, chronos,

View File

@ -5,7 +5,7 @@ import
libp2p/crypto/curve25519, libp2p/crypto/curve25519,
libp2p/multiaddress, libp2p/multiaddress,
libp2p/nameresolving/dnsresolver, libp2p/nameresolving/dnsresolver,
std/[options, sequtils, net, strutils], std/[options, sequtils, net],
results results
import ../common/utils/nat, ../node/net_config, ../waku_enr, ../waku_core, ./waku_conf import ../common/utils/nat, ../node/net_config, ../waku_enr, ../waku_core, ./waku_conf

View File

@ -42,7 +42,6 @@ import
../waku_filter_v2, ../waku_filter_v2,
../factory/node_factory, ../factory/node_factory,
../factory/internal_config, ../factory/internal_config,
../factory/external_config,
../factory/app_callbacks, ../factory/app_callbacks,
../waku_enr/multiaddr, ../waku_enr/multiaddr,
./waku_conf ./waku_conf

View File

@ -57,7 +57,6 @@ import
../common/rate_limit/setting, ../common/rate_limit/setting,
../common/callbacks, ../common/callbacks,
../common/nimchronos, ../common/nimchronos,
../waku_enr/mix,
../waku_mix ../waku_mix
declarePublicCounter waku_node_messages, "number of messages received", ["type"] declarePublicCounter waku_node_messages, "number of messages received", ["type"]

View File

@ -3,7 +3,7 @@
{.push raises: [].} {.push raises: [].}
import import
std/[options, sequtils, sets, strutils, tables], std/[options, sequtils, sets, tables],
stew/byteutils, stew/byteutils,
chronicles, chronicles,
chronos, chronos,