Import Waku folder from Nimbus

This commit is contained in:
Oskar Thoren 2020-04-28 14:04:27 +08:00
parent 0567865b1e
commit 2f290d95d6
No known key found for this signature in database
GPG Key ID: B2ECCFD3BC2EF77E
12 changed files with 12330 additions and 0 deletions

103
waku/README.md Normal file
View File

@ -0,0 +1,103 @@
# Introduction
`wakunode` is a cli application that allows you to run a
[Waku](https://github.com/vacp2p/specs/blob/master/waku.md) enabled node.
The application and Waku specification are still experimental and fully in flux.
Additionally the original Whisper (EIP-627) protocol can also be enabled as can
an experimental Whisper - Waku bridging option.
# How to Build & Run
## Prerequisites
* GNU Make, Bash and the usual POSIX utilities. Git 2.9.4 or newer.
* PCRE
More information on the installation of these can be found [here](https://github.com/status-im/nimbus#prerequisites).
## Build & Run
```bash
make # The first `make` invocation will update all Git submodules and prompt you to run `make` again.
# It's only required once per Git clone. You'll run `make update` after each `git pull`, in the future,
# to keep those submodules up to date.
make wakunode
./build/wakunode --help
```
# Using Metrics
Metrics are available for valid envelopes and dropped envelopes.
To compile in an HTTP endpoint for accessing the metrics we need to provide the
`insecure` flag:
```bash
make NIMFLAGS="-d:insecure" wakunode
./build/wakunode --metrics-server
```
Ensure your Prometheus config `prometheus.yml` contains the targets you care about, e.g.:
```
scrape_configs:
- job_name: "waku"
static_configs:
- targets: ['localhost:8008', 'localhost:8009', 'localhost:8010']
```
For visualisation, similar steps can be used as is written down for Nimbus
[here](https://github.com/status-im/nimbus#metric-visualisation).
There is a similar example dashboard that includes visualisation of the
envelopes available at `waku/examples/waku-grafana-dashboard.json`.
# Testing Waku Protocol
One can set up several nodes, get them connected and then instruct them via the
JSON-RPC interface. This can be done via e.g. web3.js, nim-web3 (needs to be
updated) or simply curl your way out.
The JSON-RPC interface is currently the same as the one of Whisper. The only
difference is the addition of broadcasting the topics interest when a filter
with a certain set of topics is subcribed.
Example of a quick simulation using this approach:
```bash
# Build wakunode + quicksim
make NIMFLAGS="-d:insecure" wakusim
# Start the simulation nodes, this currently requires multitail to be installed
./build/start_network --topology:FullMesh --amount:6 --test-node-peers:2
# In another shell run
./build/quicksim
```
The `start_network` tool will also provide a `prometheus.yml` with targets
set to all simulation nodes that are started. This way you can easily start
prometheus with this config, e.g.:
```bash
cd waku/metrics/prometheus
prometheus
```
A Grafana dashboard containing the example dashboard for each simulation node
is also generated and can be imported in case you have Grafana running.
This dashboard can be found at `./waku/metrics/waku-sim-all-nodes-grafana-dashboard.json`
# Spec support
*This section last updated April 7, 2020*
This client of Waku is spec compliant with [Waku spec v0.4](https://specs.vac.dev/waku/waku.html).
It doesn't yet implement the following recommended features:
- No support for rate limiting
- No support for DNS discovery to find Waku nodes
- It doesn't disconnect a peer if it receives a message before a Status message
- No support for negotiation with peer supporting multiple versions via Devp2p capabilities in `Hello` packet
Additionally it makes the following choices:
- It doesn't send message confirmations
- It has partial support for accounting:
- Accounting of total resource usage and total circulated envelopes is done through metrics But no accounting is done for individual peers.

154
waku/config.nim Normal file
View File

@ -0,0 +1,154 @@
import
confutils/defs, chronicles, chronos,
eth/keys, eth/p2p/rlpx_protocols/waku_protocol
type
Fleet* = enum
none
prod
staging
test
WakuNodeConf* = object
logLevel* {.
desc: "Sets the log level."
defaultValue: LogLevel.INFO
name: "log-level" }: LogLevel
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
discovery* {.
desc: "Enable/disable discovery v4."
defaultValue: true
name: "discovery" }: bool
noListen* {.
desc: "Disable listening for incoming peers."
defaultValue: false
name: "no-listen" }: bool
fleet* {.
desc: "Select the fleet to connect to."
defaultValue: Fleet.none
name: "fleet" }: Fleet
bootnodes* {.
desc: "Enode URL to bootstrap P2P discovery with. Argument may be repeated."
name: "bootnode" }: seq[string]
staticnodes* {.
desc: "Enode URL to directly connect with. Argument may be repeated."
name: "staticnode" }: seq[string]
whisper* {.
desc: "Enable the Whisper protocol."
defaultValue: false
name: "whisper" }: bool
whisperBridge* {.
desc: "Enable the Whisper protocol and bridge with Waku protocol."
defaultValue: false
name: "whisper-bridge" }: bool
lightNode* {.
desc: "Run as light node (no message relay).",
defaultValue: false
name: "light-node" }: bool
wakuTopicInterest* {.
desc: "Run as node with a topic-interest",
defaultValue: false
name: "waku-topic-interest" }: bool
wakuPow* {.
desc: "PoW requirement of Waku node.",
defaultValue: 0.002
name: "waku-pow" }: float64
nodekey* {.
desc: "P2P node private key as hex.",
defaultValue: KeyPair.random().tryGet()
name: "nodekey" }: KeyPair
# TODO: Add nodekey file option
bootnodeOnly* {.
desc: "Run only as discovery bootnode."
defaultValue: false
name: "bootnode-only" }: bool
rpc* {.
desc: "Enable Waku RPC server.",
defaultValue: false
name: "rpc" }: bool
rpcAddress* {.
desc: "Listening address of the RPC server.",
defaultValue: parseIpAddress("127.0.0.1")
name: "rpc-address" }: IpAddress
rpcPort* {.
desc: "Listening port of the RPC server.",
defaultValue: 8545
name: "rpc-port" }: uint16
metricsServer* {.
desc: "Enable the metrics server."
defaultValue: false
name: "metrics-server" }: bool
metricsServerAddress* {.
desc: "Listening address of the metrics server."
defaultValue: parseIpAddress("127.0.0.1")
name: "metrics-server-address" }: IpAddress
metricsServerPort* {.
desc: "Listening HTTP port of the metrics server."
defaultValue: 8008
name: "metrics-server-port" }: uint16
logMetrics* {.
desc: "Enable metrics logging."
defaultValue: false
name: "log-metrics" }: bool
# TODO:
# - discv5 + topic register
# - mailserver functionality
proc parseCmdArg*(T: type KeyPair, p: TaintedString): T =
try:
# TODO: add isValidPrivateKey check from Nimbus?
result.seckey = PrivateKey.fromHex(string(p)).tryGet()
result.pubkey = result.seckey.toPublicKey()[]
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 @[]

36
waku/docker/Dockerfile Normal file
View File

@ -0,0 +1,36 @@
FROM debian:bullseye-slim AS build
SHELL ["/bin/bash", "-c"]
RUN apt-get -qq update \
&& apt-get -qq -y install build-essential make wget libpcre3-dev git curl &>/dev/null \
&& apt-get -qq clean
ARG GIT_REVISION
RUN cd /root \
&& git clone https://github.com/status-im/nimbus.git \
&& cd nimbus \
&& git reset --hard ${GIT_REVISION} \
&& { make &>/dev/null || true; } \
&& make -j$(nproc) update \
&& make NIMFLAGS="-d:debug -d:insecure" wakunode
# --------------------------------- #
# Starting new image to reduce size #
# --------------------------------- #
FROM debian:bullseye-slim
SHELL ["/bin/bash", "-c"]
RUN apt-get -qq update \
&& apt-get -qq -y install libpcre3 &>/dev/null \
&& apt-get -qq clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
COPY --from=build /root/nimbus/build/wakunode /usr/bin/wakunode
MAINTAINER Kim De Mey <kimdemey@status.im>
LABEL description="Wakunode: Waku and Whisper client"
ENTRYPOINT ["/usr/bin/wakunode"]

16
waku/docker/Makefile Normal file
View File

@ -0,0 +1,16 @@
SHELL := bash # the shell used internally by "make"
# These default settings can be overriden by exporting env variables
GIT_REVISION ?= $(shell git rev-parse HEAD)
IMAGE_TAG ?= latest
IMAGE_NAME ?= statusteam/nimbus_wakunode:$(IMAGE_TAG)
build:
docker build \
--build-arg="GIT_REVISION=$(GIT_REVISION)" \
-t $(IMAGE_NAME) .
push: build
docker push $(IMAGE_NAME)

View File

@ -0,0 +1,812 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"gnetId": null,
"graphTooltip": 0,
"id": 8,
"links": [],
"panels": [
{
"datasource": null,
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 0
},
"id": 16,
"options": {
"fieldOptions": {
"calcs": [
"last"
],
"defaults": {
"mappings": [],
"max": 100,
"min": 0,
"thresholds": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"override": {},
"values": false
},
"orientation": "auto",
"showThresholdLabels": false,
"showThresholdMarkers": true
},
"pluginVersion": "6.4.5",
"targets": [
{
"expr": "connected_peers{node=\"0\"}",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Connected Peers #0",
"type": "gauge"
},
{
"cacheTimeout": null,
"colorBackground": false,
"colorValue": false,
"colors": [
"#299c46",
"rgba(237, 129, 40, 0.89)",
"#d44a3a"
],
"datasource": null,
"format": "none",
"gauge": {
"maxValue": 100,
"minValue": 0,
"show": false,
"thresholdLabels": false,
"thresholdMarkers": true
},
"gridPos": {
"h": 4,
"w": 4,
"x": 6,
"y": 0
},
"id": 22,
"interval": null,
"links": [],
"mappingType": 1,
"mappingTypes": [
{
"name": "value to text",
"value": 1
},
{
"name": "range to text",
"value": 2
}
],
"maxDataPoints": 100,
"nullPointMode": "connected",
"nullText": null,
"options": {},
"postfix": "",
"postfixFontSize": "50%",
"prefix": "",
"prefixFontSize": "50%",
"rangeMaps": [
{
"from": "null",
"text": "N/A",
"to": "null"
}
],
"sparkline": {
"fillColor": "rgba(31, 118, 189, 0.18)",
"full": false,
"lineColor": "rgb(31, 120, 193)",
"show": false,
"ymax": null,
"ymin": null
},
"tableColumn": "",
"targets": [
{
"expr": "valid_envelopes_total{node=\"0\"}",
"refId": "A"
}
],
"thresholds": "",
"timeFrom": null,
"timeShift": null,
"title": "Valid Envelopes #0",
"type": "singlestat",
"valueFontSize": "80%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "current"
},
{
"cacheTimeout": null,
"colorBackground": false,
"colorValue": false,
"colors": [
"#299c46",
"rgba(237, 129, 40, 0.89)",
"#d44a3a"
],
"datasource": null,
"format": "none",
"gauge": {
"maxValue": 100,
"minValue": 0,
"show": false,
"thresholdLabels": false,
"thresholdMarkers": true
},
"gridPos": {
"h": 4,
"w": 4,
"x": 10,
"y": 0
},
"id": 20,
"interval": null,
"links": [],
"mappingType": 1,
"mappingTypes": [
{
"name": "value to text",
"value": 1
},
{
"name": "range to text",
"value": 2
}
],
"maxDataPoints": 100,
"nullPointMode": "connected",
"nullText": null,
"options": {},
"pluginVersion": "6.4.5",
"postfix": "",
"postfixFontSize": "50%",
"prefix": "",
"prefixFontSize": "50%",
"rangeMaps": [
{
"from": "null",
"text": "N/A",
"to": "null"
}
],
"sparkline": {
"fillColor": "rgba(31, 118, 189, 0.18)",
"full": false,
"lineColor": "rgb(31, 120, 193)",
"show": false,
"ymax": null,
"ymin": null
},
"tableColumn": "",
"targets": [
{
"expr": "dropped_expired_envelopes_total{node=\"0\"} + dropped_from_future_envelopes_total{node=\"0\"} + dropped_low_pow_envelopes_total{node=\"0\"} + dropped_too_large_envelopes_total{node=\"0\"} + dropped_bloom_filter_mismatch_envelopes_total{node=\"0\"} + dropped_topic_mismatch_envelopes_total{node=\"0\"} +dropped_benign_duplicate_envelopes_total{node=\"0\"} + dropped_duplicate_envelopes_total{node=\"0\"}",
"legendFormat": "Invalid envelopes",
"refId": "A"
}
],
"thresholds": "",
"timeFrom": null,
"timeShift": null,
"title": "Invalid Envelopes #0",
"type": "singlestat",
"valueFontSize": "80%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "current"
},
{
"datasource": null,
"gridPos": {
"h": 4,
"w": 5,
"x": 14,
"y": 0
},
"id": 14,
"options": {
"fieldOptions": {
"calcs": [
"lastNotNull"
],
"defaults": {
"mappings": [],
"max": 200,
"min": 0,
"thresholds": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 200
}
],
"unit": "percent"
},
"override": {},
"values": false
},
"orientation": "auto",
"showThresholdLabels": false,
"showThresholdMarkers": true
},
"pluginVersion": "6.4.5",
"targets": [
{
"expr": "rate(process_cpu_seconds_total{node=\"0\"}[5s]) * 100",
"legendFormat": "CPU Usage",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "CPU Usage #0",
"type": "gauge"
},
{
"datasource": null,
"gridPos": {
"h": 4,
"w": 5,
"x": 19,
"y": 0
},
"id": 18,
"options": {
"fieldOptions": {
"calcs": [
"lastNotNull"
],
"defaults": {
"mappings": [],
"max": 2147483648,
"min": 0,
"thresholds": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 2147483648
}
],
"unit": "bytes"
},
"override": {},
"values": false
},
"orientation": "auto",
"showThresholdLabels": false,
"showThresholdMarkers": true
},
"pluginVersion": "6.4.5",
"targets": [
{
"expr": "process_resident_memory_bytes{node=\"0\"}",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "RSS Memory #0",
"type": "gauge"
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": null,
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 9,
"w": 12,
"x": 0,
"y": 4
},
"id": 6,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"dataLinks": []
},
"percentage": false,
"pluginVersion": "6.4.5",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "valid_envelopes_total{node=\"0\"}",
"hide": false,
"instant": false,
"legendFormat": "Valid",
"refId": "A"
},
{
"expr": "dropped_benign_duplicate_envelopes_total{node=\"0\"}",
"hide": false,
"instant": false,
"legendFormat": "Benign duplicate",
"refId": "B"
},
{
"expr": "dropped_duplicate_envelopes_total{node=\"0\"}",
"hide": false,
"legendFormat": "Duplicate",
"refId": "C"
},
{
"expr": "dropped_expired_envelopes_total{node=\"0\"}",
"hide": false,
"legendFormat": "Expired",
"refId": "D"
},
{
"expr": "dropped_from_future_envelopes_total{node=\"0\"}",
"hide": false,
"legendFormat": "Future timestamped",
"refId": "E"
},
{
"expr": "dropped_low_pow_envelopes_total{node=\"0\"}",
"hide": false,
"legendFormat": "Too low PoW",
"refId": "F"
},
{
"expr": "dropped_bloom_filter_mismatch_envelopes_total{node=\"0\"}",
"hide": false,
"legendFormat": "Bloom filter mismatch",
"refId": "G"
},
{
"expr": "dropped_topic_mismatch_envelopes_total{node=\"0\"}",
"hide": false,
"legendFormat": "Topic mismatch",
"refId": "H"
},
{
"expr": "dropped_too_large_envelopes_total{node=\"0\"}",
"hide": false,
"legendFormat": "Too Large",
"refId": "I"
},
{
"expr": "dropped_full_queue_new_envelopes_total{node=\"0\"}",
"hide": false,
"legendFormat": "Full queue new",
"refId": "J"
},
{
"expr": "dropped_full_queue_old_envelopes_total{node=\"0\"}",
"hide": false,
"legendFormat": "Full queue old",
"refId": "K"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "Waku Envelopes #0",
"tooltip": {
"shared": true,
"sort": 1,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": null,
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 9,
"w": 12,
"x": 12,
"y": 4
},
"id": 2,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"dataLinks": []
},
"percentage": false,
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [
{
"alias": "RSS Memory",
"yaxis": 2
}
],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "connected_peers{node=\"0\"}",
"intervalFactor": 1,
"legendFormat": "Connected Peers",
"refId": "A"
},
{
"expr": "process_resident_memory_bytes{node=\"0\"}",
"interval": "",
"intervalFactor": 1,
"legendFormat": "RSS Memory",
"refId": "B"
},
{
"expr": "rate(process_cpu_seconds_total{node=\"0\"}[15s]) * 100",
"legendFormat": "CPU usage %",
"refId": "C"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "Waku Node #0",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": null,
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 13
},
"id": 8,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"dataLinks": []
},
"percentage": false,
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "process_max_fds{node=\"0\"}",
"legendFormat": "Maximum file descriptors",
"refId": "A"
},
{
"expr": "process_open_fds{node=\"0\"}",
"legendFormat": "Open file descriptors",
"refId": "B"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "File Descriptors #0",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": null,
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 13
},
"id": 4,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"dataLinks": []
},
"percentage": false,
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "nim_gc_mem_bytes{node=\"0\"}",
"legendFormat": "Nim GC total memory",
"refId": "A"
},
{
"expr": "nim_gc_mem_occupied_bytes{node=\"0\"}",
"legendFormat": "Nim GC used memory",
"refId": "B"
},
{
"expr": "process_resident_memory_bytes{node=\"0\"}",
"legendFormat": "RSS memory",
"refId": "C"
},
{
"expr": "process_virtual_memory_bytes{node=\"0\"}",
"legendFormat": "Virtual memory",
"refId": "D"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "Memory Usage #0",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
}
],
"refresh": "5s",
"schemaVersion": 20,
"style": "dark",
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-30m",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
]
},
"timezone": "",
"title": "Waku Node",
"uid": "K7Z6IoBZk",
"version": 2
}

View File

@ -0,0 +1,48 @@
global:
scrape_interval: 1s
scrape_configs:
- job_name: "wakusim"
static_configs:
- targets: ['127.0.0.1:8010']
labels:
node: '0'
- targets: ['127.0.0.1:8011']
labels:
node: '1'
- targets: ['127.0.0.1:8012']
labels:
node: '2'
- targets: ['127.0.0.1:8013']
labels:
node: '3'
- targets: ['127.0.0.1:8014']
labels:
node: '4'
- targets: ['127.0.0.1:8015']
labels:
node: '5'
- targets: ['127.0.0.1:8016']
labels:
node: '6'
- targets: ['127.0.0.1:8017']
labels:
node: '7'
- targets: ['127.0.0.1:8018']
labels:
node: '8'
- targets: ['127.0.0.1:8019']
labels:
node: '9'
- targets: ['127.0.0.1:8020']
labels:
node: '10'
- targets: ['127.0.0.1:8021']
labels:
node: '11'
- targets: ['127.0.0.1:8008']
labels:
node: '12'
- targets: ['127.0.0.1:8009']
labels:
node: '13'

File diff suppressed because it is too large Load Diff

4
waku/nim.cfg Normal file
View File

@ -0,0 +1,4 @@
-d:chronicles_line_numbers
-d:"chronicles_runtime_filtering=on"
-d:nimDebugDlOpen

76
waku/quicksim.nim Normal file
View File

@ -0,0 +1,76 @@
import
os, strformat, chronicles, json_rpc/[rpcclient, rpcserver], nimcrypto/sysrand,
eth/common as eth_common, eth/keys, eth/p2p/rlpx_protocols/waku_protocol,
../nimbus/rpc/[hexstrings, rpc_types, waku],
options as what # TODO: Huh? Redefinition?
from os import DirSep
from strutils import rsplit
template sourceDir: string = currentSourcePath.rsplit(DirSep, 1)[0]
const sigWakuPath = &"{sourceDir}{DirSep}rpc{DirSep}wakucallsigs.nim"
createRpcSigs(RpcHttpClient, sigWakuPath)
const topicAmount = 100
let
trafficNode = newRpcHttpClient()
lightNode = newRpcHttpClient()
lightNode2 = newRpcHttpClient()
waitFor lightNode.connect("localhost", Port(8545))
waitFor lightNode2.connect("localhost", Port(8546))
waitFor trafficNode.connect("localhost", Port(8548))
proc generateTopics(amount = topicAmount): seq[waku_protocol.Topic] =
var topic: waku_protocol.Topic
for i in 0..<amount:
if randomBytes(topic) != 4:
raise newException(ValueError, "Generation of random topic failed.")
result.add(topic)
let
symKey = "0x0000000000000000000000000000000000000000000000000000000000000001"
topics = generateTopics()
symKeyID = waitFor lightNode.waku_addSymKey(symKey)
options = WhisperFilterOptions(symKeyID: some(symKeyID),
topics: some(topics))
filterID = waitFor lightNode.waku_newMessageFilter(options)
symKeyID2 = waitFor lightNode2.waku_addSymKey(symKey)
options2 = WhisperFilterOptions(symKeyID: some(symKeyID2),
topics: some(topics))
filterID2 = waitFor lightNode2.waku_newMessageFilter(options2)
symkeyID3 = waitFor trafficNode.waku_addSymKey(symKey)
var message = WhisperPostMessage(symKeyID: some(symkeyID3),
ttl: 30,
topic: some(topics[0]),
payload: "0x45879632".HexDataStr,
powTime: 1.0,
powTarget: 0.002)
info "Posting envelopes on all subscribed topics"
for i in 0..<topicAmount:
message.topic = some(topics[i])
discard waitFor trafficNode.waku_post(message)
# Check if the subscription for the topics works
waitFor sleepAsync(1000.milliseconds) # This is a bit brittle
let
messages = waitFor lightNode.waku_getFilterMessages(filterID)
messages2 = waitFor lightNode2.waku_getFilterMessages(filterID2)
if messages.len != topicAmount or messages2.len != topicAmount:
error "Light node did not receive envelopes on all subscribed topics",
lightnode1=messages.len, lightnode2=messages2.len
quit 1
info "Received envelopes on all subscribed topics"
# Generate test traffic on node
discard waitFor trafficNode.wakusim_generateRandomTraffic(10_000)
info "Started random traffic generation"

27
waku/rpc/wakucallsigs.nim Normal file
View File

@ -0,0 +1,27 @@
proc waku_version(): string
proc waku_info(): WhisperInfo
proc waku_setMaxMessageSize(size: uint64): bool
proc waku_setMinPoW(pow: float): bool
proc waku_markTrustedPeer(enode: string): bool
proc waku_newKeyPair(): Identifier
proc waku_addPrivateKey(key: string): Identifier
proc waku_deleteKeyPair(id: Identifier): bool
proc waku_hasKeyPair(id: Identifier): bool
proc waku_getPublicKey(id: Identifier): PublicKey
proc waku_getPrivateKey(id: Identifier): PrivateKey
proc waku_newSymKey(): Identifier
proc waku_addSymKey(key: string): Identifier
proc waku_generateSymKeyFromPassword(password: string): Identifier
proc waku_hasSymKey(id: Identifier): bool
proc waku_getSymKey(id: Identifier): SymKey
proc waku_deleteSymKey(id: Identifier): bool
proc waku_newMessageFilter(options: WhisperFilterOptions): Identifier
proc waku_deleteMessageFilter(id: Identifier): bool
proc waku_getFilterMessages(id: Identifier): seq[WhisperFilterMessage]
proc waku_post(message: WhisperPostMessage): bool
proc wakusim_generateTraffic(amount: int): bool
proc wakusim_generateRandomTraffic(amount: int): bool

197
waku/start_network.nim Normal file
View File

@ -0,0 +1,197 @@
import
strformat, os, osproc, net, confutils, strformat, chronicles, json, strutils,
eth/keys, eth/p2p/enode
const
defaults ="--log-level:DEBUG --log-metrics --metrics-server --rpc"
wakuNodeBin = "build" / "wakunode"
metricsDir = "waku" / "metrics"
portOffset = 2
type
NodeType = enum
FullNode = "",
LightNode = "--light-node:on",
Topology = enum
Star,
FullMesh,
DiscoveryBased # Whatever topology the discovery brings
WakuNetworkConf* = object
topology* {.
desc: "Set the network topology."
defaultValue: Star
name: "topology" .}: Topology
amount* {.
desc: "Amount of full nodes to be started."
defaultValue: 4
name: "amount" .}: int
testNodePeers* {.
desc: "Amount of peers a test node should connect to."
defaultValue: 1
name: "test-node-peers" .}: int
NodeInfo* = object
cmd: string
master: bool
enode: string
shift: int
label: string
proc initNodeCmd(nodeType: NodeType, shift: int, staticNodes: seq[string] = @[],
discovery = false, bootNodes: seq[string] = @[], topicInterest = false,
master = false, label: string): NodeInfo =
let
keypair = KeyPair.random().tryGet()
address = Address(ip: parseIpAddress("127.0.0.1"),
udpPort: (30303 + shift).Port, tcpPort: (30303 + shift).Port)
enode = ENode(pubkey: keypair.pubkey, address: address)
result.cmd = wakuNodeBin & " " & defaults & " "
result.cmd &= $nodeType & " "
result.cmd &= "--waku-topic-interest:" & $topicInterest & " "
result.cmd &= "--nodekey:" & $keypair.seckey & " "
result.cmd &= "--ports-shift:" & $shift & " "
if discovery:
result.cmd &= "--discovery:on" & " "
if bootNodes.len > 0:
for bootNode in bootNodes:
result.cmd &= "--bootnode:" & bootNode & " "
else:
result.cmd &= "--discovery:off" & " "
if staticNodes.len > 0:
for staticNode in staticNodes:
result.cmd &= "--staticnode:" & staticNode & " "
result.master = master
result.enode = $enode
result.shift = shift
result.label = label
debug "Node command created.", cmd=result.cmd
proc starNetwork(amount: int): seq[NodeInfo] =
let masterNode = initNodeCmd(FullNode, portOffset, master = true,
label = "master node")
result.add(masterNode)
for i in 1..<amount:
result.add(initNodeCmd(FullNode, portOffset + i, @[masterNode.enode],
label = "full node"))
proc fullMeshNetwork(amount: int): seq[NodeInfo] =
debug "amount", amount
for i in 0..<amount:
var staticnodes: seq[string]
for item in result:
staticnodes.add(item.enode)
result.add(initNodeCmd(FullNode, portOffset + i, staticnodes,
label = "full node"))
proc discoveryNetwork(amount: int): seq[NodeInfo] =
let bootNode = initNodeCmd(FullNode, portOffset, discovery = true,
master = true, label = "boot node")
result.add(bootNode)
for i in 1..<amount:
result.add(initNodeCmd(FullNode, portOffset + i, label = "full node",
discovery = true, bootNodes = @[bootNode.enode]))
proc generatePrometheusConfig(nodes: seq[NodeInfo], outputFile: string) =
var config = """
global:
scrape_interval: 1s
scrape_configs:
- job_name: "wakusim"
static_configs:"""
var count = 0
for node in nodes:
let port = 8008 + node.shift
config &= &"""
- targets: ['127.0.0.1:{port}']
labels:
node: '{count}'"""
count += 1
var (path, file) = splitPath(outputFile)
createDir(path)
writeFile(outputFile, config)
proc proccessGrafanaDashboard(nodes: int, inputFile: string,
outputFile: string) =
# from https://github.com/status-im/nim-beacon-chain/blob/master/tests/simulation/process_dashboard.nim
var
inputData = parseFile(inputFile)
panels = inputData["panels"].copy()
numPanels = len(panels)
gridHeight = 0
outputData = inputData
for panel in panels:
if panel["gridPos"]["x"].getInt() == 0:
gridHeight += panel["gridPos"]["h"].getInt()
outputData["panels"] = %* []
for nodeNum in 0 .. (nodes - 1):
var
nodePanels = panels.copy()
panelIndex = 0
for panel in nodePanels.mitems:
panel["title"] = %* replace(panel["title"].getStr(), "#0", "#" & $nodeNum)
panel["id"] = %* (panelIndex + (nodeNum * numPanels))
panel["gridPos"]["y"] = %* (panel["gridPos"]["y"].getInt() + (nodeNum * gridHeight))
var targets = panel["targets"]
for target in targets.mitems:
target["expr"] = %* replace(target["expr"].getStr(), "{node=\"0\"}", "{node=\"" & $nodeNum & "\"}")
outputData["panels"].add(panel)
panelIndex.inc()
outputData["uid"] = %* (outputData["uid"].getStr() & "a")
outputData["title"] = %* (outputData["title"].getStr() & " (all nodes)")
writeFile(outputFile, pretty(outputData))
when isMainModule:
let conf = WakuNetworkConf.load()
var nodes: seq[NodeInfo]
case conf.topology:
of Star:
nodes = starNetwork(conf.amount)
of FullMesh:
nodes = fullMeshNetwork(conf.amount)
of DiscoveryBased:
nodes = discoveryNetwork(conf.amount)
var staticnodes: seq[string]
for i in 0..<conf.testNodePeers:
# TODO: could also select nodes randomly
staticnodes.add(nodes[i].enode)
# light node with topic interest
nodes.add(initNodeCmd(LightNode, 0, staticnodes, topicInterest = true,
label = "light node topic interest"))
# Regular light node
nodes.add(initNodeCmd(LightNode, 1, staticnodes, label = "light node"))
var commandStr = "multitail -s 2 -M 0 -x \"Waku Simulation\""
var count = 0
var sleepDuration = 0
for node in nodes:
if conf.topology in {Star, DiscoveryBased}:
sleepDuration = if node.master: 0
else: 1
commandStr &= &" -cT ansi -t 'node #{count} {node.label}' -l 'sleep {sleepDuration}; {node.cmd}; echo [node execution completed]; while true; do sleep 100; done'"
if conf.topology == FullMesh:
sleepDuration += 1
count += 1
generatePrometheusConfig(nodes, metricsDir / "prometheus" / "prometheus.yml")
proccessGrafanaDashboard(nodes.len,
"waku" / "examples" / "waku-grafana-dashboard.json",
metricsDir / "waku-sim-all-nodes-grafana-dashboard.json")
let errorCode = execCmd(commandStr)
if errorCode != 0:
error "launch command failed", command=commandStr

152
waku/wakunode.nim Normal file
View File

@ -0,0 +1,152 @@
import
confutils, config, strutils, chronos, json_rpc/rpcserver, metrics,
chronicles/topics_registry, # TODO: What? Need this for setLoglevel, weird.
eth/[keys, p2p, async_utils], eth/common/utils, eth/net/nat,
eth/p2p/[discovery, enode, peer_pool, bootnodes, whispernodes],
eth/p2p/rlpx_protocols/[whisper_protocol, waku_protocol, waku_bridge],
../nimbus/rpc/[waku, wakusim, key_storage]
const clientId = "Nimbus waku node"
let globalListeningAddr = parseIpAddress("0.0.0.0")
proc setBootNodes(nodes: openArray[string]): seq[ENode] =
result = newSeqOfCap[ENode](nodes.len)
for nodeId in nodes:
# TODO: something more user friendly than an expect
result.add(ENode.fromString(nodeId).expect("correct node"))
proc connectToNodes(node: EthereumNode, nodes: openArray[string]) =
for nodeId in nodes:
# TODO: something more user friendly than an assert
let whisperENode = ENode.fromString(nodeId).expect("correct node")
traceAsyncErrors node.peerPool.connectToNode(newNode(whisperENode))
proc setupNat(conf: WakuNodeConf): tuple[ip: IpAddress,
tcpPort: Port,
udpPort: Port] =
# defaults
result.ip = globalListeningAddr
result.tcpPort = Port(conf.tcpPort + conf.portsShift)
result.udpPort = Port(conf.udpPort + conf.portsShift)
var nat: NatStrategy
case conf.nat.toLowerAscii():
of "any":
nat = NatAny
of "none":
nat = NatNone
of "upnp":
nat = NatUpnp
of "pmp":
nat = NatPmp
else:
if conf.nat.startsWith("extip:") and isIpAddress(conf.nat[6..^1]):
# any required port redirection is assumed to be done by hand
result.ip = parseIpAddress(conf.nat[6..^1])
nat = NatNone
else:
error "not a valid NAT mechanism, nor a valid IP address", value = conf.nat
quit(QuitFailure)
if nat != NatNone:
let extIP = getExternalIP(nat)
if extIP.isSome:
result.ip = extIP.get()
let extPorts = redirectPorts(tcpPort = result.tcpPort,
udpPort = result.udpPort,
description = clientId)
if extPorts.isSome:
(result.tcpPort, result.udpPort) = extPorts.get()
proc run(config: WakuNodeConf) =
if config.logLevel != LogLevel.NONE:
setLogLevel(config.logLevel)
let
(ip, tcpPort, udpPort) = setupNat(config)
address = Address(ip: ip, tcpPort: tcpPort, udpPort: udpPort)
# Set-up node
var node = newEthereumNode(config.nodekey, address, 1, nil, clientId,
addAllCapabilities = false)
if not config.bootnodeOnly:
node.addCapability Waku # Always enable Waku protocol
var topicInterest: Option[seq[waku_protocol.Topic]]
var bloom: Option[Bloom]
if config.wakuTopicInterest:
var topics: seq[waku_protocol.Topic]
topicInterest = some(topics)
else:
bloom = some(fullBloom())
let wakuConfig = WakuConfig(powRequirement: config.wakuPow,
bloom: bloom,
isLightNode: config.lightNode,
maxMsgSize: waku_protocol.defaultMaxMsgSize,
topics: topicInterest)
node.configureWaku(wakuConfig)
if config.whisper or config.whisperBridge:
node.addCapability Whisper
node.protocolState(Whisper).config.powRequirement = 0.002
if config.whisperBridge:
node.shareMessageQueue()
# TODO: Status fleet bootnodes are discv5? That will not work.
let bootnodes = if config.bootnodes.len > 0: setBootNodes(config.bootnodes)
elif config.fleet == prod: setBootNodes(StatusBootNodes)
elif config.fleet == staging: setBootNodes(StatusBootNodesStaging)
elif config.fleet == test : setBootNodes(StatusBootNodesTest)
else: @[]
traceAsyncErrors node.connectToNetwork(bootnodes, not config.noListen,
config.discovery)
if not config.bootnodeOnly:
# Optionally direct connect with a set of nodes
if config.staticnodes.len > 0: connectToNodes(node, config.staticnodes)
elif config.fleet == prod: connectToNodes(node, WhisperNodes)
elif config.fleet == staging: connectToNodes(node, WhisperNodesStaging)
elif config.fleet == test: connectToNodes(node, WhisperNodesTest)
if config.rpc:
let ta = initTAddress(config.rpcAddress,
Port(config.rpcPort + config.portsShift))
var rpcServer = newRpcHttpServer([ta])
let keys = newKeyStorage()
setupWakuRPC(node, keys, rpcServer)
setupWakuSimRPC(node, rpcServer)
rpcServer.start()
when defined(insecure):
if config.metricsServer:
let
address = config.metricsServerAddress
port = config.metricsServerPort + config.portsShift
info "Starting metrics HTTP server", address, port
metrics.startHttpServer($address, Port(port))
if config.logMetrics:
proc logMetrics(udata: pointer) {.closure, gcsafe.} =
{.gcsafe.}:
let
connectedPeers = connected_peers.value
validEnvelopes = waku_protocol.valid_envelopes.value
invalidEnvelopes = waku_protocol.dropped_expired_envelopes.value +
waku_protocol.dropped_from_future_envelopes.value +
waku_protocol.dropped_low_pow_envelopes.value +
waku_protocol.dropped_too_large_envelopes.value +
waku_protocol.dropped_bloom_filter_mismatch_envelopes.value +
waku_protocol.dropped_topic_mismatch_envelopes.value +
waku_protocol.dropped_benign_duplicate_envelopes.value +
waku_protocol.dropped_duplicate_envelopes.value
info "Node metrics", connectedPeers, validEnvelopes, invalidEnvelopes
addTimer(Moment.fromNow(2.seconds), logMetrics)
addTimer(Moment.fromNow(2.seconds), logMetrics)
runForever()
when isMainModule:
let conf = WakuNodeConf.load()
run(conf)