mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-02-02 23:35:31 +00:00
Add local testnet script and required json-rpc calls (#891)
- Add basic discv5 and portal json-rpc calls and activate them in fluffy - Renames in the rpc folder - Add local testnet script and run this script in CI - bump nim-eth
This commit is contained in:
parent
f9ed83c221
commit
903350bdde
11
.github/workflows/fluffy.yml
vendored
11
.github/workflows/fluffy.yml
vendored
@ -98,6 +98,13 @@ jobs:
|
|||||||
chmod 755 external/bin/gcc external/bin/g++
|
chmod 755 external/bin/gcc external/bin/g++
|
||||||
echo "${{ github.workspace }}/external/bin" >> $GITHUB_PATH
|
echo "${{ github.workspace }}/external/bin" >> $GITHUB_PATH
|
||||||
|
|
||||||
|
# Required for running the local testnet script
|
||||||
|
- name: Install build dependencies (MacOS)
|
||||||
|
if: runner.os == 'macOS'
|
||||||
|
run: |
|
||||||
|
brew install gnu-getopt
|
||||||
|
brew link --force gnu-getopt
|
||||||
|
|
||||||
- name: MSYS2 (Windows i386)
|
- name: MSYS2 (Windows i386)
|
||||||
if: runner.os == 'Windows' && matrix.target.cpu == 'i386'
|
if: runner.os == 'Windows' && matrix.target.cpu == 'i386'
|
||||||
uses: msys2/setup-msys2@v2
|
uses: msys2/setup-msys2@v2
|
||||||
@ -193,3 +200,7 @@ jobs:
|
|||||||
build/fluffy --help
|
build/fluffy --help
|
||||||
# "-static" option will not work for osx unless static system libraries are provided
|
# "-static" option will not work for osx unless static system libraries are provided
|
||||||
make ${DEFAULT_MAKE_FLAGS} fluffy-test fluffy-test-reproducibility
|
make ${DEFAULT_MAKE_FLAGS} fluffy-test fluffy-test-reproducibility
|
||||||
|
|
||||||
|
- name: Run fluffy testnet
|
||||||
|
run: |
|
||||||
|
./fluffy/scripts/launch_local_testnet.sh
|
||||||
|
@ -14,8 +14,7 @@ import
|
|||||||
json_rpc/rpcproxy, stew/byteutils,
|
json_rpc/rpcproxy, stew/byteutils,
|
||||||
eth/keys, eth/net/nat,
|
eth/keys, eth/net/nat,
|
||||||
eth/p2p/discoveryv5/protocol as discv5_protocol,
|
eth/p2p/discoveryv5/protocol as discv5_protocol,
|
||||||
eth/p2p/discoveryv5/node,
|
./conf, ./rpc/[rpc_eth_api, bridge_client, rpc_discovery_api, rpc_portal_api],
|
||||||
./conf, ./rpc/[eth_api, bridge_client, discovery_api],
|
|
||||||
./network/state/[state_network, state_content],
|
./network/state/[state_network, state_content],
|
||||||
./network/history/[history_network, history_content],
|
./network/history/[history_network, history_content],
|
||||||
./content_db
|
./content_db
|
||||||
@ -88,6 +87,8 @@ proc run(config: PortalConf) {.raises: [CatchableError, Defect].} =
|
|||||||
var rpcHttpServerWithProxy = RpcProxy.new([ta], config.proxyUri)
|
var rpcHttpServerWithProxy = RpcProxy.new([ta], config.proxyUri)
|
||||||
rpcHttpServerWithProxy.installEthApiHandlers()
|
rpcHttpServerWithProxy.installEthApiHandlers()
|
||||||
rpcHttpServerWithProxy.installDiscoveryApiHandlers(d)
|
rpcHttpServerWithProxy.installDiscoveryApiHandlers(d)
|
||||||
|
rpcHttpServerWithProxy.installPortalStateApiHandlers(stateNetwork.portalProtocol)
|
||||||
|
rpcHttpServerWithProxy.installPortalHistoryApiHandlers(historyNetwork.portalProtocol)
|
||||||
# TODO for now we can only proxy to local node (or remote one without ssl) to make it possible
|
# TODO for now we can only proxy to local node (or remote one without ssl) to make it possible
|
||||||
# to call infura https://github.com/status-im/nim-json-rpc/pull/101 needs to get merged for http client to support https/
|
# to call infura https://github.com/status-im/nim-json-rpc/pull/101 needs to get merged for http client to support https/
|
||||||
waitFor rpcHttpServerWithProxy.start()
|
waitFor rpcHttpServerWithProxy.start()
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
# Nimbus
|
|
||||||
# Copyright (c) 2021 Status Research & Development GmbH
|
|
||||||
# Licensed and distributed under either of
|
|
||||||
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
|
||||||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
|
||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
||||||
|
|
||||||
{.push raises: [Defect].}
|
|
||||||
|
|
||||||
import
|
|
||||||
json_rpc/[rpcproxy, rpcserver], stint,
|
|
||||||
eth/p2p/discoveryv5/protocol as discv5_protocol, eth/p2p/discoveryv5/enr
|
|
||||||
|
|
||||||
type
|
|
||||||
NodeInfoResponse = object
|
|
||||||
node_id: string
|
|
||||||
enr: string
|
|
||||||
|
|
||||||
proc installDiscoveryApiHandlers*(rpcServerWithProxy: var RpcProxy, discovery: discv5_protocol.Protocol)
|
|
||||||
{.raises: [Defect, CatchableError].} =
|
|
||||||
|
|
||||||
# https://ddht.readthedocs.io/en/latest/jsonrpc.html#discv5-nodeinfo
|
|
||||||
rpcServerWithProxy.rpc("discv5_nodeInfo") do() -> NodeInfoResponse:
|
|
||||||
let localNodeId = "0x" & discovery.localNode.id.toHex()
|
|
||||||
let localNodeEnr = discovery.localNode.record.toURI()
|
|
||||||
return NodeInfoResponse(node_id: localNodeId, enr: localNodeEnr)
|
|
||||||
|
|
3
fluffy/rpc/rpc_calls/rpc_discovery_calls.nim
Normal file
3
fluffy/rpc/rpc_calls/rpc_discovery_calls.nim
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Discovery v5 json-rpc calls
|
||||||
|
proc discv5_nodeInfo(): NodeInfo
|
||||||
|
proc discv5_routingTableInfo(): RoutingTableInfo
|
7
fluffy/rpc/rpc_calls/rpc_portal_calls.nim
Normal file
7
fluffy/rpc/rpc_calls/rpc_portal_calls.nim
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
## Portal State Network json-rpc calls
|
||||||
|
proc portal_state_nodeInfo(): NodeInfo
|
||||||
|
proc portal_state_routingTableInfo(): RoutingTableInfo
|
||||||
|
|
||||||
|
## Portal History Network json-rpc calls
|
||||||
|
proc portal_history_nodeInfo(): NodeInfo
|
||||||
|
proc portal_history_routingTableInfo(): RoutingTableInfo
|
35
fluffy/rpc/rpc_discovery_api.nim
Normal file
35
fluffy/rpc/rpc_discovery_api.nim
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# Nimbus
|
||||||
|
# Copyright (c) 2021 Status Research & Development GmbH
|
||||||
|
# Licensed and distributed under either of
|
||||||
|
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||||
|
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||||
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
|
{.push raises: [Defect].}
|
||||||
|
|
||||||
|
import
|
||||||
|
std/sequtils,
|
||||||
|
json_rpc/[rpcproxy, rpcserver],
|
||||||
|
eth/p2p/discoveryv5/protocol as discv5_protocol,
|
||||||
|
./rpc_types
|
||||||
|
|
||||||
|
proc installDiscoveryApiHandlers*(rpcServerWithProxy: var RpcProxy,
|
||||||
|
d: discv5_protocol.Protocol) {.raises: [Defect, CatchableError].} =
|
||||||
|
## Discovery v5 JSON-RPC API such as defined here:
|
||||||
|
## https://ddht.readthedocs.io/en/latest/jsonrpc.html
|
||||||
|
## and here:
|
||||||
|
## https://github.com/ethereum/portal-network-specs/pull/88
|
||||||
|
## Note: There are quite some descrepencies between the two, can only
|
||||||
|
## implement exactly once specification is settled.
|
||||||
|
|
||||||
|
rpcServerWithProxy.rpc("discv5_nodeInfo") do() -> NodeInfo:
|
||||||
|
return d.routingTable.getNodeInfo()
|
||||||
|
|
||||||
|
rpcServerWithProxy.rpc("discv5_routingTableInfo") do() -> RoutingTableInfo:
|
||||||
|
return getRoutingTableInfo(d.routingTable)
|
||||||
|
|
||||||
|
rpcServerWithProxy.rpc("discv5_recursiveFindNodes") do() -> seq[string]:
|
||||||
|
# TODO: Not according to the specification currently. Should do a lookup
|
||||||
|
# here instead of query, and the node_id is a parameter to be passed.
|
||||||
|
let discovered = await d.queryRandom()
|
||||||
|
return discovered.map(proc(n: Node): string = n.record.toURI())
|
37
fluffy/rpc/rpc_portal_api.nim
Normal file
37
fluffy/rpc/rpc_portal_api.nim
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# Nimbus
|
||||||
|
# Copyright (c) 2021 Status Research & Development GmbH
|
||||||
|
# Licensed and distributed under either of
|
||||||
|
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||||
|
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||||
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
|
{.push raises: [Defect].}
|
||||||
|
|
||||||
|
import
|
||||||
|
json_rpc/[rpcproxy, rpcserver],
|
||||||
|
../network/wire/portal_protocol,
|
||||||
|
./rpc_types
|
||||||
|
|
||||||
|
# TODO:
|
||||||
|
# Trying to make this dynamic by passing in a network sub string results in:
|
||||||
|
# Error: Invalid node kind nnkInfix for macros.`$`
|
||||||
|
proc installPortalStateApiHandlers*(rpcServerWithProxy: var RpcProxy, p: PortalProtocol)
|
||||||
|
{.raises: [Defect, CatchableError].} =
|
||||||
|
## Portal routing table and portal wire json-rpc API is not yet defined but
|
||||||
|
## will look something similar as what exists here now:
|
||||||
|
## https://github.com/ethereum/portal-network-specs/pull/88
|
||||||
|
|
||||||
|
rpcServerWithProxy.rpc("portal_state_nodeInfo") do() -> NodeInfo:
|
||||||
|
return p.routingTable.getNodeInfo()
|
||||||
|
|
||||||
|
rpcServerWithProxy.rpc("portal_state_routingTableInfo") do() -> RoutingTableInfo:
|
||||||
|
return getRoutingTableInfo(p.routingTable)
|
||||||
|
|
||||||
|
proc installPortalHistoryApiHandlers*(rpcServerWithProxy: var RpcProxy, p: PortalProtocol)
|
||||||
|
{.raises: [Defect, CatchableError].} =
|
||||||
|
|
||||||
|
rpcServerWithProxy.rpc("portal_history_nodeInfo") do() -> NodeInfo:
|
||||||
|
return p.routingTable.getNodeInfo()
|
||||||
|
|
||||||
|
rpcServerWithProxy.rpc("portal_history_routingTableInfo") do() -> RoutingTableInfo:
|
||||||
|
return getRoutingTableInfo(p.routingTable)
|
38
fluffy/rpc/rpc_types.nim
Normal file
38
fluffy/rpc/rpc_types.nim
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# Nimbus
|
||||||
|
# Copyright (c) 2021 Status Research & Development GmbH
|
||||||
|
# Licensed and distributed under either of
|
||||||
|
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||||
|
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||||
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
|
{.push raises: [Defect].}
|
||||||
|
|
||||||
|
import
|
||||||
|
eth/p2p/discoveryv5/[routing_table, enr, node]
|
||||||
|
|
||||||
|
type
|
||||||
|
NodeInfo* = object
|
||||||
|
nodeId: string
|
||||||
|
nodeENR: string
|
||||||
|
|
||||||
|
RoutingTableInfo* = object
|
||||||
|
localKey: string
|
||||||
|
buckets: seq[seq[string]]
|
||||||
|
|
||||||
|
proc getNodeInfo*(r: RoutingTable): NodeInfo =
|
||||||
|
let id = "0x" & r.localNode.id.toHex()
|
||||||
|
let enr = r.localNode.record.toURI()
|
||||||
|
return NodeInfo(nodeId: id, nodeENR: enr)
|
||||||
|
|
||||||
|
proc getRoutingTableInfo*(r: RoutingTable): RoutingTableInfo =
|
||||||
|
var info: RoutingTableInfo
|
||||||
|
for b in r.buckets:
|
||||||
|
var bucket: seq[string]
|
||||||
|
for n in b.nodes:
|
||||||
|
bucket.add("0x" & n.id.toHex())
|
||||||
|
|
||||||
|
info.buckets.add(bucket)
|
||||||
|
|
||||||
|
info.localKey = "0x" & r.localNode.id.toHex()
|
||||||
|
|
||||||
|
info
|
350
fluffy/scripts/launch_local_testnet.sh
Executable file
350
fluffy/scripts/launch_local_testnet.sh
Executable file
@ -0,0 +1,350 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Copyright (c) 2021 Status Research & Development GmbH. Licensed under
|
||||||
|
# either of:
|
||||||
|
# - Apache License, version 2.0
|
||||||
|
# - MIT license
|
||||||
|
# at your option. This file may not be copied, modified, or distributed except
|
||||||
|
# according to those terms.
|
||||||
|
|
||||||
|
# This script is for a big part a copy of the nimbus-eth2 launch_local_testnet
|
||||||
|
# script. This script however does not expect fluffy nodes to exit 0 in the good
|
||||||
|
# case, but instead the json-rpc interface is used to check whether certain
|
||||||
|
# values are what we expect them to be.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
cd "$(dirname "${BASH_SOURCE[0]}")"/../..
|
||||||
|
|
||||||
|
####################
|
||||||
|
# argument parsing #
|
||||||
|
####################
|
||||||
|
|
||||||
|
GETOPT_BINARY="getopt"
|
||||||
|
if uname | grep -qi darwin; then
|
||||||
|
# macOS
|
||||||
|
GETOPT_BINARY="/usr/local/opt/gnu-getopt/bin/getopt"
|
||||||
|
[[ -f "$GETOPT_BINARY" ]] || { echo "GNU getopt not installed. Please run 'brew install gnu-getopt'. Aborting."; exit 1; }
|
||||||
|
fi
|
||||||
|
|
||||||
|
! ${GETOPT_BINARY} --test > /dev/null
|
||||||
|
if [ ${PIPESTATUS[0]} != 4 ]; then
|
||||||
|
echo '`getopt --test` failed in this environment.'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
OPTS="h:n:d"
|
||||||
|
LONGOPTS="help,nodes:,data-dir:,enable-htop,log-level:,base-port:,base-rpc-port:,base-metrics-port:,reuse-existing-data-dir,timeout:,kill-old-processes"
|
||||||
|
|
||||||
|
# default values
|
||||||
|
NUM_NODES="17"
|
||||||
|
DATA_DIR="local_testnet_data"
|
||||||
|
USE_HTOP="0"
|
||||||
|
LOG_LEVEL="TRACE"
|
||||||
|
BASE_PORT="9000"
|
||||||
|
BASE_METRICS_PORT="8008"
|
||||||
|
BASE_RPC_PORT="7000"
|
||||||
|
REUSE_EXISTING_DATA_DIR="0"
|
||||||
|
TIMEOUT_DURATION="0"
|
||||||
|
KILL_OLD_PROCESSES="0"
|
||||||
|
SCRIPTS_DIR="fluffy/scripts/"
|
||||||
|
|
||||||
|
print_help() {
|
||||||
|
cat <<EOF
|
||||||
|
Usage: $(basename "$0") [OPTIONS] -- [BEACON NODE OPTIONS]
|
||||||
|
E.g.: $(basename "$0") --nodes ${NUM_NODES} --data-dir "${DATA_DIR}" # defaults
|
||||||
|
|
||||||
|
-h, --help this help message
|
||||||
|
-n, --nodes number of nodes to launch (default: ${NUM_NODES})
|
||||||
|
-d, --data-dir directory where all the node data and logs will end up
|
||||||
|
(default: "${DATA_DIR}")
|
||||||
|
--base-port bootstrap node's discv5 port (default: ${BASE_PORT})
|
||||||
|
--base-rpc-port bootstrap node's RPC port (default: ${BASE_RPC_PORT})
|
||||||
|
--base-metrics-port bootstrap node's metrics server port (default: ${BASE_METRICS_PORT})
|
||||||
|
--enable-htop use "htop" to see the fluffy processes without doing any tests
|
||||||
|
--log-level set the log level (default: ${LOG_LEVEL})
|
||||||
|
--reuse-existing-data-dir instead of deleting and recreating the data dir, keep it and reuse everything we can from it
|
||||||
|
--timeout timeout in seconds (default: ${TIMEOUT_DURATION} - no timeout)
|
||||||
|
--kill-old-processes if any process is found listening on a port we use, kill it (default: disabled)
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
! PARSED=$(${GETOPT_BINARY} --options=${OPTS} --longoptions=${LONGOPTS} --name "$0" -- "$@")
|
||||||
|
if [ ${PIPESTATUS[0]} != 0 ]; then
|
||||||
|
# getopt has complained about wrong arguments to stdout
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# read getopt's output this way to handle the quoting right
|
||||||
|
eval set -- "$PARSED"
|
||||||
|
while true; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
print_help
|
||||||
|
exit
|
||||||
|
;;
|
||||||
|
-n|--nodes)
|
||||||
|
NUM_NODES="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-d|--data-dir)
|
||||||
|
DATA_DIR="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--enable-htop)
|
||||||
|
USE_HTOP="1"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--log-level)
|
||||||
|
LOG_LEVEL="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--base-port)
|
||||||
|
BASE_PORT="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--base-rpc-port)
|
||||||
|
BASE_RPC_PORT="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--base-metrics-port)
|
||||||
|
BASE_METRICS_PORT="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--reuse-existing-data-dir)
|
||||||
|
REUSE_EXISTING_DATA_DIR="1"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--timeout)
|
||||||
|
TIMEOUT_DURATION="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--kill-old-processes)
|
||||||
|
KILL_OLD_PROCESSES="1"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "argument parsing error"
|
||||||
|
print_help
|
||||||
|
exit 1
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# when sourcing env.sh, it will try to execute $@, so empty it
|
||||||
|
EXTRA_ARGS="$@"
|
||||||
|
if [[ $# != 0 ]]; then
|
||||||
|
shift $#
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$REUSE_EXISTING_DATA_DIR" == "0" ]]; then
|
||||||
|
rm -rf "${DATA_DIR}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
"${SCRIPTS_DIR}"/makedir.sh "${DATA_DIR}"
|
||||||
|
|
||||||
|
HAVE_LSOF=0
|
||||||
|
|
||||||
|
# Windows detection
|
||||||
|
if uname | grep -qiE "mingw|msys"; then
|
||||||
|
MAKE="mingw32-make"
|
||||||
|
else
|
||||||
|
MAKE="make"
|
||||||
|
which lsof &>/dev/null && HAVE_LSOF=1 || { echo "'lsof' not installed and we need it to check for ports already in use. Aborting."; exit 1; }
|
||||||
|
fi
|
||||||
|
|
||||||
|
# number of CPU cores
|
||||||
|
if uname | grep -qi darwin; then
|
||||||
|
NPROC="$(sysctl -n hw.logicalcpu)"
|
||||||
|
else
|
||||||
|
NPROC="$(nproc)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# kill lingering processes from a previous run
|
||||||
|
if [[ "${HAVE_LSOF}" == "1" ]]; then
|
||||||
|
for NUM_NODE in $(seq 0 $(( NUM_NODES - 1 ))); do
|
||||||
|
for PORT in $(( BASE_PORT + NUM_NODE )) $(( BASE_METRICS_PORT + NUM_NODE )) $(( BASE_RPC_PORT + NUM_NODE )); do
|
||||||
|
for PID in $(lsof -n -i tcp:${PORT} -sTCP:LISTEN -t); do
|
||||||
|
echo -n "Found old process listening on port ${PORT}, with PID ${PID}. "
|
||||||
|
if [[ "${KILL_OLD_PROCESSES}" == "1" ]]; then
|
||||||
|
echo "Killing it."
|
||||||
|
kill -9 ${PID} || true
|
||||||
|
else
|
||||||
|
echo "Aborting."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build the binaries
|
||||||
|
BINARIES="fluffy"
|
||||||
|
$MAKE -j ${NPROC} LOG_LEVEL=TRACE ${BINARIES} NIMFLAGS="-d:chronicles_colors=off -d:chronicles_sinks=textlines" #V=2
|
||||||
|
|
||||||
|
# Kill child processes on Ctrl-C/SIGTERM/exit, passing the PID of this shell
|
||||||
|
# instance as the parent and the target process name as a pattern to the
|
||||||
|
# "pkill" command.
|
||||||
|
cleanup() {
|
||||||
|
pkill -f -P $$ fluffy &>/dev/null || true
|
||||||
|
sleep 2
|
||||||
|
pkill -f -9 -P $$ fluffy &>/dev/null || true
|
||||||
|
|
||||||
|
# Delete the binaries we just built, because these are with none default logs.
|
||||||
|
# TODO: When fluffy gets run time log options a la nimbus-eth2 we can keep
|
||||||
|
# the binaries around.
|
||||||
|
for BINARY in ${BINARIES}; do
|
||||||
|
rm build/${BINARY}
|
||||||
|
done
|
||||||
|
}
|
||||||
|
trap 'cleanup' SIGINT SIGTERM EXIT
|
||||||
|
|
||||||
|
# timeout - implemented with a background job
|
||||||
|
timeout_reached() {
|
||||||
|
echo -e "\nTimeout reached. Aborting.\n"
|
||||||
|
cleanup
|
||||||
|
}
|
||||||
|
trap 'timeout_reached' SIGALRM
|
||||||
|
|
||||||
|
# TODO: This doesn't seem to work in Windows CI as it can't find the process
|
||||||
|
# with WATCHER_PID when doing the taskkill later on.
|
||||||
|
if [[ "${TIMEOUT_DURATION}" != "0" ]]; then
|
||||||
|
export PARENT_PID=$$
|
||||||
|
( sleep ${TIMEOUT_DURATION} && kill -ALRM ${PARENT_PID} ) 2>/dev/null & WATCHER_PID=$!
|
||||||
|
fi
|
||||||
|
|
||||||
|
PIDS=""
|
||||||
|
BOOTSTRAP_TIMEOUT=30 # in seconds
|
||||||
|
NUM_JOBS=${NUM_NODES}
|
||||||
|
|
||||||
|
dump_logs() {
|
||||||
|
LOG_LINES=20
|
||||||
|
for LOG in "${DATA_DIR}"/log*.txt; do
|
||||||
|
echo "Last ${LOG_LINES} lines of ${LOG}:"
|
||||||
|
tail -n ${LOG_LINES} "${LOG}"
|
||||||
|
echo "======"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOTSTRAP_NODE=0
|
||||||
|
# TODO:
|
||||||
|
# For now we just hardcode a network key and the resulting ENR until fluffy
|
||||||
|
# stores network keys and enrs locally in files.
|
||||||
|
NETWORK_KEY="0x29738ba0c1a4397d6a65f292eee07f02df8e58d41594ba2be3cf84ce0fc58169"
|
||||||
|
HARDCODED_BOOTSTRAP_ENR="enr:-IS4QDBoE7JdB3W9Jqc3Yoatk3Zw3PgkAcnhDKNFszdiPfm3IGNlvPl5CKiJLn9u5Kk-2QaieAGYvtMgR-EBqIWIqe0BgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQNStnoFzDBxNc8-0t6xLnFpoJbovjIq_QeEHCVcfOKck4N1ZHCCIyg"
|
||||||
|
|
||||||
|
for NUM_NODE in $(seq 0 $(( NUM_NODES - 1 ))); do
|
||||||
|
NODE_DATA_DIR="${DATA_DIR}/node${NUM_NODE}"
|
||||||
|
rm -rf "${NODE_DATA_DIR}"
|
||||||
|
"${SCRIPTS_DIR}"/makedir.sh "${NODE_DATA_DIR}" 2>&1
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Starting ${NUM_NODES} nodes."
|
||||||
|
for NUM_NODE in $(seq 0 $(( NUM_NODES - 1 ))); do
|
||||||
|
NODE_DATA_DIR="${DATA_DIR}/node${NUM_NODE}"
|
||||||
|
if [[ ${NUM_NODE} == ${BOOTSTRAP_NODE} ]]; then
|
||||||
|
BOOTSTRAP_ARG="--nodekey:${NETWORK_KEY}"
|
||||||
|
else
|
||||||
|
BOOTSTRAP_ARG="--bootnode=${HARDCODED_BOOTSTRAP_ENR} --portal-bootnode=${HARDCODED_BOOTSTRAP_ENR}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increasing the loopback address here with NUM_NODE as listen address to
|
||||||
|
# avoid hitting the IP limits in the routing tables.
|
||||||
|
# TODO: This simple increase will limit the amount of max nodes to 255.
|
||||||
|
# Could also fix this by having a compiler flag that starts the routing tables
|
||||||
|
# in fluffy with a very high limit or simply an adjustment in the routing
|
||||||
|
# table code that disable the checks on loopback address.
|
||||||
|
|
||||||
|
# macOS doesn't have these default
|
||||||
|
if uname | grep -qi darwin; then
|
||||||
|
sudo ifconfig lo0 alias 127.0.0.$((1 + NUM_NODE))
|
||||||
|
fi
|
||||||
|
./build/fluffy \
|
||||||
|
--listen-address:127.0.0.$((1 + NUM_NODE)) \
|
||||||
|
--nat:extip:127.0.0.$((1 + NUM_NODE)) \
|
||||||
|
--log-level="${LOG_LEVEL}" \
|
||||||
|
--udp-port=$(( BASE_PORT + NUM_NODE )) \
|
||||||
|
--data-dir="${NODE_DATA_DIR}" \
|
||||||
|
${BOOTSTRAP_ARG} \
|
||||||
|
--rpc \
|
||||||
|
--rpc-address="127.0.0.1" \
|
||||||
|
--rpc-port="$(( BASE_RPC_PORT + NUM_NODE ))" \
|
||||||
|
--metrics \
|
||||||
|
--metrics-address="127.0.0.1" \
|
||||||
|
--metrics-port="$(( BASE_METRICS_PORT + NUM_NODE ))" \
|
||||||
|
${EXTRA_ARGS} \
|
||||||
|
> "${DATA_DIR}/log${NUM_NODE}.txt" 2>&1 &
|
||||||
|
|
||||||
|
if [[ "${PIDS}" == "" ]]; then
|
||||||
|
PIDS="$!"
|
||||||
|
else
|
||||||
|
PIDS="${PIDS},$!"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# give the regular nodes time to crash
|
||||||
|
sleep 5
|
||||||
|
BG_JOBS="$(jobs | wc -l | tr -d ' ')"
|
||||||
|
if [[ "${TIMEOUT_DURATION}" != "0" ]]; then
|
||||||
|
BG_JOBS=$(( BG_JOBS - 1 )) # minus the timeout bg job
|
||||||
|
fi
|
||||||
|
if [[ "$BG_JOBS" != "$NUM_JOBS" ]]; then
|
||||||
|
echo "$(( NUM_JOBS - BG_JOBS )) fluffy instance(s) exited early. Aborting."
|
||||||
|
dump_logs
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# TODO: Move this to a separate script or create nim process that is rpc client
|
||||||
|
# once things get more complicated
|
||||||
|
check_nodes() {
|
||||||
|
echo "Checking routing table of all nodes."
|
||||||
|
for NUM_NODE in $(seq 0 $(( NUM_NODES - 1 ))); do
|
||||||
|
if [[ ${NUM_NODE} == ${BOOTSTRAP_NODE} ]]; then
|
||||||
|
RPC_PORT="$(( BASE_RPC_PORT + NUM_NODE ))"
|
||||||
|
ROUTING_TABLE_NODES=$(curl -s -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":"id","method":"discv5_routingTableInfo","params":[]}' http://localhost:${RPC_PORT} | jq '.result.buckets' | jq 'flatten' | jq '. | length')
|
||||||
|
if [[ $ROUTING_TABLE_NODES != $(( NUM_NODES - 1 )) ]]; then
|
||||||
|
echo "Check for node ${NUM_NODE} failed."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
curl -s -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":"id","method":"discv5_recursiveFindNodes","params":[]}' http://localhost:${RPC_PORT} &>/dev/null
|
||||||
|
ROUTING_TABLE_NODES=$(curl -s -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":"id","method":"discv5_routingTableInfo","params":[]}' http://localhost:${RPC_PORT} | jq '.result.buckets' | jq 'flatten' | jq '. | length')
|
||||||
|
if [[ $ROUTING_TABLE_NODES != $(( NUM_NODES - 1 )) ]]; then
|
||||||
|
echo "Check for node ${NUM_NODE} failed."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# launch htop and run until `TIMEOUT_DURATION` or check the nodes and quit.
|
||||||
|
if [[ "$USE_HTOP" == "1" ]]; then
|
||||||
|
htop -p "$PIDS"
|
||||||
|
cleanup
|
||||||
|
else
|
||||||
|
check_nodes
|
||||||
|
FAILED=$?
|
||||||
|
if [[ "$FAILED" != "0" ]]; then
|
||||||
|
dump_logs
|
||||||
|
if [[ "${TIMEOUT_DURATION}" != "0" ]]; then
|
||||||
|
if uname | grep -qiE "mingw|msys"; then
|
||||||
|
echo ${WATCHER_PID}
|
||||||
|
taskkill //F //PID ${WATCHER_PID}
|
||||||
|
else
|
||||||
|
pkill -HUP -P ${WATCHER_PID}
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${TIMEOUT_DURATION}" != "0" ]]; then
|
||||||
|
if uname | grep -qiE "mingw|msys"; then
|
||||||
|
taskkill //F //PID ${WATCHER_PID}
|
||||||
|
else
|
||||||
|
pkill -HUP -P ${WATCHER_PID}
|
||||||
|
fi
|
||||||
|
fi
|
30
fluffy/scripts/makedir.sh
Executable file
30
fluffy/scripts/makedir.sh
Executable file
@ -0,0 +1,30 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Copyright (c) 2018-2021 Status Research & Development GmbH. Licensed under
|
||||||
|
# either of:
|
||||||
|
# - Apache License, version 2.0
|
||||||
|
# - MIT license
|
||||||
|
# at your option. This file may not be copied, modified, or distributed except
|
||||||
|
# according to those terms.
|
||||||
|
|
||||||
|
[[ -z "$1" ]] && { echo "Usage: $(basename $0) path"; exit 1; }
|
||||||
|
|
||||||
|
if uname | grep -qiE "mingw|msys"; then
|
||||||
|
ON_WINDOWS=1
|
||||||
|
else
|
||||||
|
ON_WINDOWS=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${ON_WINDOWS}" == "1" ]]; then
|
||||||
|
if [[ ! -d "$1" ]]; then
|
||||||
|
# Create full path.
|
||||||
|
mkdir -p "$1";
|
||||||
|
# Remove all inherited access from path $1 ACL and grant full access rights
|
||||||
|
# to current user only in $1 ACL.
|
||||||
|
icacls "$1" /inheritance:r /grant:r $USERDOMAIN\\$USERNAME:\(OI\)\(CI\)\(F\)&>/dev/null;
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Create full path with proper permissions.
|
||||||
|
mkdir -m 0700 -p $1
|
||||||
|
fi
|
||||||
|
|
@ -12,7 +12,7 @@ import
|
|||||||
json_rpc/[rpcproxy, rpcserver], json_rpc/clients/httpclient,
|
json_rpc/[rpcproxy, rpcserver], json_rpc/clients/httpclient,
|
||||||
stint,eth/p2p/discoveryv5/enr, eth/keys,
|
stint,eth/p2p/discoveryv5/enr, eth/keys,
|
||||||
eth/p2p/discoveryv5/protocol as discv5_protocol,
|
eth/p2p/discoveryv5/protocol as discv5_protocol,
|
||||||
../rpc/discovery_api, ./test_helpers
|
../rpc/rpc_discovery_api, ./test_helpers
|
||||||
|
|
||||||
type TestCase = ref object
|
type TestCase = ref object
|
||||||
localDiscovery: discv5_protocol.Protocol
|
localDiscovery: discv5_protocol.Protocol
|
||||||
@ -50,13 +50,13 @@ procSuite "Discovery Rpc":
|
|||||||
let resp = await tc.client.call("discv5_nodeInfo", %[])
|
let resp = await tc.client.call("discv5_nodeInfo", %[])
|
||||||
|
|
||||||
check:
|
check:
|
||||||
resp.contains("node_id")
|
resp.contains("nodeId")
|
||||||
resp["node_id"].kind == JString
|
resp["nodeId"].kind == JString
|
||||||
resp.contains("enr")
|
resp.contains("nodeENR")
|
||||||
resp["enr"].kind == JString
|
resp["nodeENR"].kind == JString
|
||||||
|
|
||||||
let nodeId = resp["node_id"].getStr()
|
let nodeId = resp["nodeId"].getStr()
|
||||||
let nodeEnr = resp["enr"].getStr()
|
let nodeEnr = resp["nodeENR"].getStr()
|
||||||
|
|
||||||
check:
|
check:
|
||||||
nodeEnr == tc.localDiscovery.localNode.record.toURI()
|
nodeEnr == tc.localDiscovery.localNode.record.toURI()
|
||||||
|
2
vendor/nim-eth
vendored
2
vendor/nim-eth
vendored
@ -1 +1 @@
|
|||||||
Subproject commit ce296ff76ec1cd7ece22dfefaaf631d9aa23abb0
|
Subproject commit 84f755d792538e160d97467490878c4166aa20a0
|
Loading…
x
Reference in New Issue
Block a user