Add Waku v1 usage example, fix #54 (#106)

* Add Waku v1 usage example, fix #54

* Add readme for example and fix typos
This commit is contained in:
Kim De Mey
2020-08-26 14:20:04 +02:00
committed by GitHub
parent 893134b536
commit 4314dcf6e9
9 changed files with 279 additions and 61 deletions
+42
View File
@@ -0,0 +1,42 @@
# Waku v1 example
## Introduction
This is a basic Waku v1 example to show the Waku v1 API usage.
It can be run as a single node, in which case it will just post and receive its
own messages.
Or multiple nodes can be started and can connect to each other, so that
messages can be passed around.
## How to build
```sh
make wakuexample
```
## How to run
### Single node
```sh
# Lauch example node
./build/example
```
Messages will be posted and received.
### Multiple nodes
```sh
# Launch first example node
./build/example
```
Now look for an `INFO` log containing the enode address, e.g.:
`enode://26..5b@0.0.0.0:30303` (but with full address)
Copy the full enode string of the first node and start the second
node with that enode string as staticnode config option:
```sh
# Launch second example node, providing the enode address of the first node
./build/example --staticnode:enode://26..5b@0.0.0.0:30303 --ports-shift:1
```
Now both nodes will receive also messages from each other.
+65
View File
@@ -0,0 +1,65 @@
import
confutils/defs, chronicles, chronos, eth/keys
type
WakuNodeCmd* = enum
noCommand
WakuNodeConf* = object
logLevel* {.
desc: "Sets the log level."
defaultValue: LogLevel.INFO
name: "log-level" .}: LogLevel
case cmd* {.
command
defaultValue: noCommand .}: WakuNodeCmd
of noCommand:
tcpPort* {.
desc: "TCP listening port."
defaultValue: 30303
name: "tcp-port" .}: uint16
udpPort* {.
desc: "UDP listening port."
defaultValue: 30303
name: "udp-port" .}: uint16
portsShift* {.
desc: "Add a shift to all port numbers."
defaultValue: 0
name: "ports-shift" .}: uint16
nat* {.
desc: "Specify method to use for determining public address. " &
"Must be one of: any, none, upnp, pmp, extip:<IP>."
defaultValue: "any" .}: string
staticnodes* {.
desc: "Enode URL to directly connect with. Argument may be repeated."
name: "staticnode" .}: seq[string]
nodekey* {.
desc: "P2P node private key as hex.",
defaultValue: KeyPair.random(keys.newRng()[])
name: "nodekey" .}: KeyPair
proc parseCmdArg*(T: type KeyPair, p: TaintedString): T =
try:
let privkey = PrivateKey.fromHex(string(p)).tryGet()
result = privkey.toKeyPair()
except CatchableError as e:
raise newException(ConfigurationError, "Invalid private key")
proc completeCmdArg*(T: type KeyPair, val: TaintedString): seq[string] =
return @[]
proc parseCmdArg*(T: type IpAddress, p: TaintedString): T =
try:
result = parseIpAddress(p)
except CatchableError as e:
raise newException(ConfigurationError, "Invalid IP address")
proc completeCmdArg*(T: type IpAddress, val: TaintedString): seq[string] =
return @[]
BIN
View File
Binary file not shown.
+91
View File
@@ -0,0 +1,91 @@
import
confutils, chronicles, chronos, stew/byteutils,
eth/[keys, p2p, async_utils],
../../waku/protocol/v1/waku_protocol,
../../waku/node/v1/waku_helpers,
./config_example
## This is a simple Waku v1 example to show the Waku v1 API usage.
const clientId = "Waku example v1"
let
# Load the cli configuration from `config_example.nim`.
config = WakuNodeConf.load()
# Seed the rng.
rng = keys.newRng()
# Set up the address according to NAT information.
(ip, tcpPort, udpPort) = setupNat(config.nat, clientId, config.tcpPort,
config.udpPort, config.portsShift)
address = Address(ip: ip, tcpPort: tcpPort, udpPort: udpPort)
# Create Ethereum Node
var node = newEthereumNode(config.nodekey, # Node identifier
address, # Address reachable for incoming requests
1, # Network Id, only applicable for ETH protocol
nil, # Database, not required for Waku
clientId, # Client id string
addAllCapabilities = false, # Disable default all RLPx capabilities
rng = rng)
node.addCapability Waku # Enable only the Waku protocol.
# Set up the Waku configuration.
let wakuConfig = WakuConfig(powRequirement: 0.002,
bloom: some(fullBloom()), # Full bloom filter
isLightNode: false, # Full node
maxMsgSize: waku_protocol.defaultMaxMsgSize,
topics: none(seq[waku_protocol.Topic]) # empty topic interest
)
node.configureWaku(wakuConfig)
# Optionally direct connect to a set of nodes.
if config.staticnodes.len > 0:
connectToNodes(node, config.staticnodes)
# Connect to the network, which will make the node start listening and/or
# connect to bootnodes, and/or start discovery.
# This will block until first connection is made, which in this case can only
# happen if we directly connect to nodes (step above) or if an incoming
# connection occurs, which is why we use `traceAsyncErrors` instead of `await`.
# TODO: This looks a bit awkward and the API should perhaps be altered here.
traceAsyncErrors node.connectToNetwork(@[],
true, # Enable listening
false # Disable discovery (only discovery v4 is currently supported)
)
# Using a hardcoded symmetric key for encryption of the payload for the sake of
# simplicity.
var symKey: SymKey
symKey[31] = 1
# Asymmetric keypair to sign the payload.
let signKeyPair = KeyPair.random(rng[])
# Code to be executed on receival of a message on filter.
proc handler(msg: ReceivedMessage) =
if msg.decoded.src.isSome():
echo "Received message from ", $msg.decoded.src.get(), ": ",
string.fromBytes(msg.decoded.payload)
# Create and subscribe filter with above handler.
let
topic = [byte 0, 0, 0, 0]
filter = initFilter(symKey = some(symKey), topics = @[topic])
discard node.subscribeFilter(filter, handler)
# Repeat the posting of a message every 5 seconds.
proc repeatMessage(udata: pointer) {.gcsafe.} =
{.gcsafe.}:
# Post a waku message on the network, encrypted with provided symmetric key,
# signed with asymmetric key, on topic and with ttl of 30 seconds.
let posted = node.postMessage(
symKey = some(symKey), src = some(signKeyPair.seckey),
ttl = 30, topic = topic, payload = @[byte 0x48, 0x65, 0x6C, 0x6C, 0x6F])
if posted: echo "Posted message as ", $signKeyPair.pubkey
else: echo "Posting message failed."
discard setTimer(Moment.fromNow(5.seconds), repeatMessage)
discard setTimer(Moment.fromNow(5.seconds), repeatMessage)
runForever()