diff --git a/Makefile b/Makefile index e62625527..6c1689dd3 100644 --- a/Makefile +++ b/Makefile @@ -158,6 +158,11 @@ example2: | build deps echo -e $(BUILD_MSG) "build/$@" && \ $(ENV_SCRIPT) nim example2 $(NIM_PARAMS) waku.nims +# Waku tooling targets +wakucanary: | build deps + echo -e $(BUILD_MSG) "build/$@" && \ + $(ENV_SCRIPT) nim wakucanary $(NIM_PARAMS) waku.nims + installganache: ifeq ($(ONCHAIN_RLN), true) diff --git a/README.md b/README.md index 29af88b20..a824aaa77 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,10 @@ make test Examples can be found in the examples folder. For Waku v2, there is a fully featured chat example. +### Tools + +Different tools and their corresponding how-to guides can be found in the `tools` folder. + ### Bugs, Questions & Features For an inquiry, or if you would like to propose new features, feel free to [open a general issue](https://github.com/status-im/nim-waku/issues/new/). diff --git a/tools/README.md b/tools/README.md new file mode 100644 index 000000000..62e66cf12 --- /dev/null +++ b/tools/README.md @@ -0,0 +1,39 @@ +## waku canary tool + +Attempts to dial a peer and asserts it supports a given set of protocols. + +```console +Usage: + +wakucanary [OPTIONS]... + +The following options are available: + + -a, --address Multiaddress of the peer node to attempt to dial. + -t, --timeout Timeout to consider that the connection failed [=chronos.seconds(10)]. + -p, --protocol Protocol required to be supported: store,relay,lightpush,filter (can be used + multiple times). + -l, --log-level Sets the log level [=LogLevel.DEBUG]. +``` + +The tool can be built as: + +```console +$ make wakucanary +``` + +And used as follows. A reachable node that supports both `store` and `filter` protocols. + + +```console +$ ./build/wakucanary --address=/ip4/8.210.222.231/tcp/30303/p2p/16Uiu2HAm4v86W3bmT1BiH6oSPzcsSr24iDQpSN5Qa992BCjjwgrD --protocol=store --protocol=filter +$ echo $? +0 +``` + +A node that can't be reached. +```console +$ ./build/wakucanary --address=/ip4/8.210.222.231/tcp/1000/p2p/16Uiu2HAm4v86W3bmT1BiH6oSPzcsSr24iDQpSN5Qa992BCjjwgrD --protocol=store --protocol=filter +$ echo $? +1 +``` \ No newline at end of file diff --git a/tools/wakucanary/wakucanary.nim b/tools/wakucanary/wakucanary.nim new file mode 100644 index 000000000..5c0dd53a8 --- /dev/null +++ b/tools/wakucanary/wakucanary.nim @@ -0,0 +1,133 @@ +import + std/[strutils, sequtils, tables], + confutils, + chronos, + stew/shims/net, + chronicles/topics_registry +import + libp2p/protocols/ping, + libp2p/crypto/[crypto, secp] +import + ../../waku/v2/node/peer_manager/peer_manager, + ../../waku/v2/utils/peers, + ../../waku/v2/node/wakunode2, + ../../waku/v2/node/waku_payload, + ../../waku/v2/utils/peers + +# protocols and their tag +const ProtocolsTable = { + "store": "/vac/waku/store/", + "relay": "/vac/waku/relay/", + "lightpush": "/vac/waku/lightpush/", + "filter": "/vac/waku/filter/", +}.toTable + +# cli flags +type + WakuCanaryConf* = object + address* {. + desc: "Multiaddress of the peer node to attempt to dial", + defaultValue: "", + name: "address", + abbr: "a" }: string + + timeout* {. + desc: "Timeout to consider that the connection failed", + defaultValue: chronos.seconds(10), + name: "timeout", + abbr: "t" }: chronos.Duration + + protocols* {. + desc: "Protocol required to be supported: store,relay,lightpush,filter (can be used multiple times)", + name: "protocol", + abbr: "p" }: seq[string] + + logLevel* {. + desc: "Sets the log level", + defaultValue: LogLevel.DEBUG, + name: "log-level", + abbr: "l" .}: LogLevel + + +proc parseCmdArg*(T: type chronos.Duration, p: TaintedString): T = + try: + result = chronos.seconds(parseInt(p)) + except CatchableError as e: + raise newException(ConfigurationError, "Invalid timeout value") + +proc completeCmdArg*(T: type chronos.Duration, val: TaintedString): seq[string] = + return @[] + +# checks if rawProtocols (skipping version) are supported in nodeProtocols +proc areProtocolsSupported( + rawProtocols: seq[string], + nodeProtocols: seq[string]): bool = + + var numOfSupportedProt: int = 0 + + for nodeProtocol in nodeProtocols: + for rawProtocol in rawProtocols: + let protocolTag = ProtocolsTable[rawProtocol] + if nodeProtocol.startsWith(protocolTag): + info "Supported protocol ok", expected=protocolTag, supported=nodeProtocol + numOfSupportedProt += 1 + break + + if numOfSupportedProt == rawProtocols.len: + return true + + return false + +proc main(): Future[int] {.async.} = + let conf: WakuCanaryConf = WakuCanaryConf.load() + + if conf.logLevel != LogLevel.NONE: + setLogLevel(conf.logLevel) + + # ensure input protocols are valid + for p in conf.protocols: + if p notin ProtocolsTable: + error "invalid protocol", protocol=p, valid=ProtocolsTable + raise newException(ConfigurationError, "Invalid cli flag values" & p) + + info "Cli flags", + address=conf.address, + timeout=conf.timeout, + protocols=conf.protocols, + logLevel=conf.logLevel + + let + peer: RemotePeerInfo = parseRemotePeerInfo(conf.address) + rng = crypto.newRng() + nodeKey = crypto.PrivateKey.random(Secp256k1, rng[])[] + node = WakuNode.new( + nodeKey, + ValidIpAddress.init("0.0.0.0"), + Port(60000)) + + await node.start() + + let timedOut = not await node.connectToNodes(@[peer]).withTimeout(conf.timeout) + if timedOut: + error "Timedout after", timeout=conf.timeout + + let lp2pPeerStore = node.switch.peerStore + let conStatus = node.peerManager.peerStore.connectionBook[peer.peerId] + + if conStatus in [Connected, CanConnect]: + let nodeProtocols = lp2pPeerStore[ProtoBook][peer.peerId] + if not areProtocolsSupported(conf.protocols, nodeProtocols): + error "Not all protocols are supported", expected=conf.protocols, supported=nodeProtocols + return 1 + elif conStatus == CannotConnect: + error "Could not connect", peerId = peer.peerId + return 1 + return 0 + +when isMainModule: + let status = waitFor main() + if status == 0: + info "The node is reachable and supports all specified protocols" + else: + error "The node has some problems (see logs)" + quit status diff --git a/waku.nimble b/waku.nimble index ff6a71380..bfc8379e6 100644 --- a/waku.nimble +++ b/waku.nimble @@ -99,3 +99,7 @@ task chat2bridge, "Build chat2-matterbridge": buildBinary name, "examples/v2/matterbridge/", "-d:chronicles_log_level=DEBUG" +### Waku Tooling +task wakucanary, "Build waku-canary tool": + let name = "wakucanary" + buildBinary name, "tools/wakucanary/", "-d:chronicles_log_level=DEBUG -d:chronicles_runtime_filtering:on" \ No newline at end of file