This tool is under heavy development and the documentation/code snippets below may vary slightly from the actual code in the repository. Updates to the documentation may happen some time after an update to the codebase. Nonetheless, this README should provide valuable information about using this tool.
This is a tool to run and interact with a local Camino network. This tool may be especially useful for development and testing.
To download a binary for the latest release, run:
curl -sSfL https://raw.githubusercontent.com/chain4travel/camino-network-runner/chain4travel/scripts/install.sh | sh -s
To install a specific version, just append the desired version to the command (must be an existing github tag like v1.3.1)
curl -sSfL https://raw.githubusercontent.com/chain4travel/camino-network-runner/chain4travel/scripts/install.sh | sh -s v1.3.1
The binary will be installed inside the ./bin
directory.
To add the binary to your path, run
export PATH=$PWD/bin:$PATH
To add it to your path permanently, add an export command to your shell initialization script (ex: .bashrc).
To download the binary into a specific directory, run:
curl -sSfL https://raw.githubusercontent.com/chain4travel/camino-network-runner/chain4travel/scripts/install.sh | sh -s -- -b <directory>
git clone https://github.com/chain4travel/camino-network-runner.git
From inside the cloned directory:
./scripts/build.sh
The binary will be installed inside the ./bin
directory.
To add the binary to your path, run:
export PATH=$PWD/bin:$PATH
Inside the directory cloned above:
go test ./...
The E2E test checks camino-network-runner
RPC communication and control. It starts a network against a fresh RPC
server and executes a set of query and control operations on it.
To start it, execute inside the cloned directory:
./scripts/tests.e2e.sh CAMINO_NODE_VERSION1 CAMINO_NODE_VERSION2
The E2E test checks whether a node can be restarted with a different binary version. Provide two different versions as arguments. For Example:
./scripts/tests.e2e.sh v0.4.1-rc1 v0.4.1-rc1
To specify that the E2E test should be run with go test
, set environment variable RUN_E2E
to any non-empty value.
This environment variable is correctly set when executing ./scripts/tests.e2e.sh
, but the user should consider
setting it if trying to execute E2E tests without using that script.
You can import this repository as a library in your Go program, but we recommend running camino-network-runner
as a binary. This creates an RPC server that you can send requests to in order to start a network, add nodes to the network, remove nodes from the network, restart nodes, etc.. You can make requests through the camino-network-runner
command or by making API calls. Requests are "translated" into gRPC and sent to the server.
Why does camino-network-runner
need an RPC server? camino-network-runner
needs to provide complex workflows such as replacing nodes, restarting nodes, injecting fail points, etc.. The RPC server exposes basic operations to enable a separation of concerns such that one team develops a test framework, and the other writes test cases and controlling logic.
Why gRPC? The RPC server leads to more modular test components, and gRPC enables greater flexibility. The protocol buffer increases flexibility as we develop more complicated test cases. And gRPC opens up a variety of different approaches for how to write test controller (e.g., Rust). See rpcpb/rpc.proto
for service definition.
Why gRPC gateway? gRPC gateway exposes gRPC API via HTTP, without us writing any code. Which can be useful if a test controller writer does not want to deal with gRPC.
To start the server:
camino-network-runner server \
--log-level debug \
--port=":8080" \
--grpc-gateway-port=":8081"
# set "--disable-grpc-gateway" to disable gRPC gateway
Note that the above command will run until you stop it with CTRL + C
. You should run further commands in a separate terminal.
To ping the server:
curl -X POST -k http://localhost:8081/v1/ping -d ''
# or
camino-network-runner ping \
--log-level debug \
--endpoint="0.0.0.0:8080"
To start a new Camino network with five nodes (a cluster):
# replace execPath with the path to CaminoNode on your machine
# e.g., ${HOME}/go/src/github.com/chain4travel/camino-node/build/camino-node
CAMINO_NODE_EXEC_PATH="camino-node"
curl -X POST -k http://localhost:8081/v1/control/start -d '{"execPath":"'${CAMINO_NODE_EXEC_PATH}'","numNodes":5,"logLevel":"INFO"}'
# or
camino-network-runner control start \
--log-level debug \
--endpoint="0.0.0.0:8080" \
--number-of-nodes=5 \
--camino-node ${CAMINO_NODE_EXEC_PATH}
Additional optional parameters which can be passed to the start command:
--plugin-dir ${CAMINO_NODE_PLUGIN_PATH} \
--blockchain-specs '[{"vm_name": "subnetevm", "genesis": "/tmp/subnet-evm.genesis.json"}]'
--global-node-config '{"index-enabled":false, "api-admin-enabled-secret":"runner","network-peer-list-gossip-frequency":"300ms"}'
--custom-node-configs" '{"node1":{"log-level":"debug"},"node2":{...},...}'
For example, to set camino-node --http-host
flag for all nodes:
# to expose local RPC server to all traffic
# (e.g., run network runner within cloud instance)
curl -X POST -k http://localhost:8081/v1/control/start -d '{"execPath":"'${CAMINO_NODE_EXEC_PATH}'","globalNodeConfig":"{\"http-host\":\"0.0.0.0\"}"}'
# or
camino-network-runner control start \
--camino-node ${CAMINO_NODE_EXEC_PATH} \
--global-node-config '{"http-host":"0.0.0.0"}'
--plugin-dir
and --blockchain-specs
are parameters relevant to subnet operation.
See the subnet section for details about how to run subnets.
The network-runner supports camino-node configuration at different levels.
-
If neither
--global-node-config
nor--custom-node-configs
is supplied, all nodes get a standard set of config options. Currently this set contains:{ "network-peer-list-gossip-frequency":"250ms", "network-max-reconnect-delay":"1s", "public-ip":"127.0.0.1", "health-check-frequency":"2s", "api-admin-enabled-secret":"runner", "api-ipcs-enabled":true, "index-enabled":true }
-
--global-node-config
is a JSON string representing a single camino-node config, which will be applied to all nodes. This makes it easy to define common properties to all nodes. Whatever is set here will be combined with the standard set above. -
--custom-node-configs
is a map of JSON strings representing the complete network with individual configs. This allows to configure each node independently. If set,--number-of-nodes
will be ignored to avoid conflicts. -
The configs can be combined and will be merged, i.e. one could set global
--global-node-config
entries applied to each node, and also set--custom-node-configs
for additional entries. -
Common
--custom-node-configs
entries override--global-node-config
entries which override the standard set.
Example usage of --custom-node-configs
to get deterministic API port numbers:
curl -X POST -k http://localhost:8081/v1/control/start -d\
'{"execPath":"'${CAMINO_NODE_EXEC_PATH}'","customNodeConfigs":
{
"node1":"{\"http-port\":9650}",
"node2":"{\"http-port\":9652}",
"node3":"{\"http-port\":9654}",
"node4":"{\"http-port\":9656}",
"node5":"{\"http-port\":9658}"
}
}'
# or
camino-network-runner control start \
--camino-node ${CAMINO_NODE_EXEC_PATH} \
--custom-node-configs \
'{
"node1":"{\"http-port\":9650}",
"node2":"{\"http-port\":9652}",
"node3":"{\"http-port\":9654}",
"node4":"{\"http-port\":9656}",
"node5":"{\"http-port\":9658}"
}'
NAMING CONVENTION: Currently, node names should be called node
+ a number, i.e. node1,node2,node3,...node 101
To wait for all the nodes in the cluster to become healthy:
curl -X POST -k http://localhost:8081/v1/control/health -d ''
# or
camino-network-runner control health \
--log-level debug \
--endpoint="0.0.0.0:8080"
To get the API endpoints of all nodes in the cluster:
curl -X POST -k http://localhost:8081/v1/control/uris -d ''
# or
camino-network-runner control uris \
--log-level debug \
--endpoint="0.0.0.0:8080"
To query the cluster status from the server:
curl -X POST -k http://localhost:8081/v1/control/status -d ''
# or
camino-network-runner control status \
--log-level debug \
--endpoint="0.0.0.0:8080"
To stream cluster status:
camino-network-runner control \
--request-timeout=3m \
stream-status \
--push-interval=5s \
--log-level debug \
--endpoint="0.0.0.0:8080"
To save the network to a snapshot:
curl -X POST -k http://localhost:8081/v1/control/savesnapshot -d '{"snapshot_name":"node5"}'
# or
camino-network-runner control save-snapshot snapshotName
To load a network from a snapshot:
curl -X POST -k http://localhost:8081/v1/control/loadsnapshot -d '{"snapshot_name":"node5"}'
# or
camino-network-runner control load-snapshot snapshotName
An camino-node binary path and/or plugin dir can be specified when loading the snapshot. This is optional. If not specified, will use the paths saved with the snapshot:
curl -X POST -k http://localhost:8081/v1/control/loadsnapshot -d '{"snapshot_name":"node5","execPath":"'${CAMINO_NODE_EXEC_PATH}'","pluginDir":"'${CAMINO_NODE_PLUGIN_PATH}'"}'
# or
camino-network-runner control load-snapshot snapshotName --camino-node ${CAMINO_NODE_EXEC_PATH} --plugin-dir ${CAMINO_NODE_PLUGIN_PATH}
To get the list of snapshots:
curl -X POST -k http://localhost:8081/v1/control/getsnapshotnames
# or
camino-network-runner control get-snapshot-names
To remove a snapshot:
curl -X POST -k http://localhost:8081/v1/control/removesnapshot -d '{"snapshot_name":"node5"}'
# or
camino-network-runner control remove-snapshot snapshotName
To create 1 validated subnet, with all existing nodes as participants (requires network restart):
curl -X POST -k http://localhost:8081/v1/control/createsubnets -d '[{}]'
# or
camino-network-runner control create-subnets '[{}]'
To create 1 validated subnet, with some of existing nodes as participants (requires network restart):
curl -X POST -k http://localhost:8081/v1/control/createsubnets -d '[{"participants": ["node1", "node2"]}]'
# or
camino-network-runner control create-subnets '[{"participants": ["node1", "node2"]}]'
To create 1 validated subnet, with some of existing nodes and another new node as participants (requires network restart):
curl -X POST -k http://localhost:8081/v1/control/createsubnets -d '[{"participants": ["node1", "node2", "testNode"]}]'
# or
camino-network-runner control create-subnets '[{"participants": ["node1", "node2", "testNode"]}]'
To create N validated subnets (requires network restart):
curl -X POST -k http://localhost:8081/v1/control/createsubnets -d '[{}, {"participants": ["node1", "node2", "node3"]}, {"participants": ["node1", "node2", "testNode"]}]'
# or
camino-network-runner control create-subnets '[{}, {"participants": ["node1", "node2", "node3"]}, {"participants": ["node1", "node2", "testNode"]}]'
To create a blockchain without a subnet id (requires network restart):
curl -X POST -k http://localhost:8081/v1/control/createblockchains -d '{"pluginDir":"'$PLUGIN_DIR'","blockchainSpecs":[{"vm_name":"'$VM_NAME'","genesis":"'$GENESIS_PATH'"}]}'
# or
camino-network-runner control create-blockchains '[{"vm_name":"'$VM_NAME'","genesis":"'$GENESIS_PATH'"}]' --plugin-dir $PLUGIN_DIR
To create a blockchain with a subnet id (does not require restart):
curl -X POST -k http://localhost:8081/v1/control/createblockchains -d '{"pluginDir":"'$PLUGIN_DIR'","blockchainSpecs":[{"vm_name":"'$VM_NAME'","genesis":"'$GENESIS_PATH'", "subnet_id": "'$SUBNET_ID'"}]}'
# or
camino-network-runner control create-blockchains '[{"vm_name":"'$VM_NAME'","genesis":"'$GENESIS_PATH'", "subnet_id": "'$SUBNET_ID'"}]' --plugin-dir $PLUGIN_DIR
To create a blockchain with a subnet id, and chain config, network upgrade and subnet config file paths (requires network restart):
curl -X POST -k http://localhost:8081/v1/control/createblockchains -d '{"pluginDir":"'$PLUGIN_DIR'","blockchainSpecs":[{"vm_name":"'$VM_NAME'","genesis":"'$GENESIS_PATH'", "subnet_id": "'$SUBNET_ID'", "chain_config": "'$CHAIN_CONFIG_PATH'", "network_upgrade": "'$NETWORK_UPGRADE_PATH'", "subnet_config": "'$SUBNET_CONFIG_PATH'"}]}'
# or
camino-network-runner control create-blockchains '[{"vm_name":"'$VM_NAME'","genesis":"'$GENESIS_PATH'", "subnet_id": "'$SUBNET_ID'", "chain_config": "'$CHAIN_CONFIG_PATH'", "network_upgrade": "'$NETWORK_UPGRADE_PATH'", "subnet_config": "'$SUBNET_CONFIG_PATH'"}]' --plugin-dir $PLUGIN_DIR
To create a blockchain with a new subnet id with select nodes as participants (requires network restart):
(New nodes will first be added as primary validators similar to the process in create-subnets
)
curl -X POST -k http://localhost:8081/v1/control/createblockchains -d '{"pluginDir":"'$PLUGIN_DIR'","blockchainSpecs":[{"vm_name":"'$VM_NAME'","genesis":"'$GENESIS_PATH'", "subnet_spec": "{"participants": ["node1", "node2", "testNode"]}"]}'
# or
camino-network-runner control create-blockchains '[{"vm_name":"'$VM_NAME'","genesis":"'$GENESIS_PATH'", "subnet_spec": "{"participants": ["node1", "node2", "testNode"]}"]' --plugin-dir $PLUGIN_DIR
Chain config can also be defined on a per node basis. For that, a per node chain config file is needed, which is a JSON that specifies the chain config per node. For example, given the following as the contents of the file with path $PER_NODE_CHAIN_CONFIG
:
{
"node1": {"rpc-tx-fee-cap": 101},
"node2": {"rpc-tx-fee-cap": 102},
"node3": {"rpc-tx-fee-cap": 103},
"node4": {"rpc-tx-fee-cap": 104},
"node5": {"rpc-tx-fee-cap": 105}
}
Then a blockchain with different chain configs per node can be created with this command:
curl -X POST -k http://localhost:8081/v1/control/createblockchains -d '{"pluginDir":"'$PLUGIN_DIR'","blockchainSpecs":[{"vm_name":"'$VM_NAME'","genesis":"'$GENESIS_PATH'", "subnet_id": "'$SUBNET_ID'", "per_node_chain_config": "'$PER_NODE_CHAIN_CONFIG'", "network_upgrade": "'$NETWORK_UPGRADE_PATH'", "subnet_config": "'$SUBNET_CONFIG_PATH'"}]}'
# or
camino-network-runner control create-blockchains '[{"vm_name":"'$VM_NAME'","genesis":"'$GENESIS_PATH'", "subnet_id": "'$SUBNET_ID'", "per_node_chain_config": "'$PER_NODE_CHAIN_CONFIG'", "network_upgrade": "'$NETWORK_UPGRADE_PATH'", "subnet_config": "'$SUBNET_CONFIG_PATH'"}]' --plugin-dir $PLUGIN_DIR
To remove (stop) a node:
curl -X POST -k http://localhost:8081/v1/control/removenode -d '{"name":"node5"}'
# or
camino-network-runner control remove-node \
--request-timeout=3m \
--log-level debug \
--endpoint="0.0.0.0:8080" \
node5
To restart a node (in this case, the one named node1
):
# e.g., ${HOME}/go/src/github.com/chain4travel/camino-node/build/camino-node
CAMINO_NODE_EXEC_PATH="camino-node"
# Note that you can restart the node with a different binary by providing
# a different execPath
curl -X POST -k http://localhost:8081/v1/control/restartnode -d '{"name":"node1","execPath":"'${CAMINO_NODE_EXEC_PATH}'","logLevel":"INFO"}'
# or
camino-network-runner control restart-node \
--request-timeout=3m \
--log-level debug \
--endpoint="0.0.0.0:8080" \
--camino-node ${CAMINO_NODE_EXEC_PATH} \
node1
To add a node (in this case, a new node named node99
):
# e.g., ${HOME}/go/src/github.com/chain4travel/camino-node/build/camino-node
CAMINO_NODE_EXEC_PATH="camino-node"
# Note that you can add the new node with a different binary by providing
# a different execPath
curl -X POST -k http://localhost:8081/v1/control/addnode -d '{"name":"node99","execPath":"'${CAMINO_NODE_EXEC_PATH}'","logLevel":"INFO"}'
# or
camino-network-runner control add-node \
--request-timeout=3m \
--log-level debug \
--endpoint="0.0.0.0:8080" \
--camino-node ${CAMINO_NODE_EXEC_PATH} \
node99
To pause a node (in this case, node named node99
):
# e.g., ${HOME}/go/src/github.com/chain4travel/camino-node/build/camino-node
CAMINO_NODE_EXEC_PATH="camino-node"
curl -X POST -k http://localhost:8081/v1/control/pausenode -d '{"name":"node99","logLevel":"INFO"}'
# or
camino-network-runner control pause-node \
--request-timeout=3m \
--log-level debug \
--endpoint="0.0.0.0:8080" \
node99
To resume a paused node (in this case, node named node99
):
# e.g., ${HOME}/go/src/github.com/chain4travel/camino-node/build/camino-node
CAMINO_NODE_EXEC_PATH="camino-node"
curl -X POST -k http://localhost:8081/v1/control/resumenode -d '{"name":"node99","logLevel":"INFO"}'
# or
camino-network-runner control resume-node \
--request-timeout=3m \
--log-level debug \
--endpoint="0.0.0.0:8080" \
node99
You can also provide additional flags that specify the node's config:
--node-config '{"index-enabled":false, "api-admin-enabled-secret":"runner","network-peer-list-gossip-frequency":"300ms"}'
--node-config
allows to specify specific camino-node config parameters to the new node. See here for the reference of supported flags.
Note: The following parameters will be ignored if set in --node-config
, because the network runner needs to set its own in order to function properly:
--log-dir
--db-dir
Caminogo exposes a "test peer", which you can attach to a node. (See here for more information.) You can send messages through the test peer to the node it is attached to.
To attach a test peer to a node (in this case, node1
):
curl -X POST -k http://localhost:8081/v1/control/attachpeer -d '{"nodeName":"node1"}'
# or
camino-network-runner control attach-peer \
--request-timeout=3m \
--log-level debug \
--endpoint="0.0.0.0:8080" \
--node-name node1
To send a chit message to the node through the test peer:
curl -X POST -k http://localhost:8081/v1/control/sendoutboundmessage -d '{"nodeName":"node1","peerId":"7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg","op":16,"bytes":"EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKgAAAAPpAqmoZkC/2xzQ42wMyYK4Pldl+tX2u+ar3M57WufXx0oXcgXfXCmSnQbbnZQfg9XqmF3jAgFemSUtFkaaZhDbX6Ke1DVpA9rCNkcTxg9X2EcsfdpKXgjYioitjqca7WA="}'
# or
camino-network-runner control send-outbound-message \
--request-timeout=3m \
--log-level debug \
--endpoint="0.0.0.0:8080" \
--node-name node1 \
--peer-id "7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg" \
--message-op=16 \
--message-bytes-b64="EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKgAAAAPpAqmoZkC/2xzQ42wMyYK4Pldl+tX2u+ar3M57WufXx0oXcgXfXCmSnQbbnZQfg9XqmF3jAgFemSUtFkaaZhDbX6Ke1DVpA9rCNkcTxg9X2EcsfdpKXgjYioitjqca7WA="
To terminate the cluster:
curl -X POST -k http://localhost:8081/v1/control/stop -d ''
# or
camino-network-runner control stop \
--log-level debug \
--endpoint="0.0.0.0:8080"
To start the server:
camino-network-runner server \
--log-level debug \
--port=":8080" \
--grpc-gateway-port=":8081"
# make sure network-runner server is up
curl -X POST -k http://localhost:8081/v1/ping -d ''
To start the cluster with custom chains:
# or download from https://github.com/ava-labs/subnet-cli/releases
cd ${HOME}/go/src/github.com/ava-labs/subnet-cli
go install -v .
subnet-cli create VMID subnetevm
# srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy
# download from https://github.com/ava-labs/avalanchego/releases
# or build
rm -rf ${HOME}/go/src/github.com/ava-labs/avalanchego/build
cd ${HOME}/go/src/github.com/ava-labs/avalanchego
./scripts/build.sh
# ref. https://github.com/ava-labs/subnet-evm/blob/b69e47e0398b5237cda0422f6a32969e64bde346/scripts/run.sh
cd ${HOME}/go/src/github.com/ava-labs/subnet-evm
go build -v \
-o ${HOME}/go/src/github.com/chain4travel/camino-node/build/plugins/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy \
./plugin
# make sure binaries are built
find ${HOME}/go/src/github.com/ava-labs/avalanchego/build
# for example
# .../build
# .../build/plugins
# .../build/plugins/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy
# .../build/plugins/evm
# .../build/avalanchego
# generate the genesis for the custom chain
export CHAIN_ID=99999
export GENESIS_ADDRESS="0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC"
cat <<EOF > /tmp/subnet-evm.genesis.json
{
"config": {
"chainId": $CHAIN_ID,
"homesteadBlock": 0,
"eip150Block": 0,
"eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0",
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"istanbulBlock": 0,
"muirGlacierBlock": 0,
"subnetEVMTimestamp": 0,
"feeConfig": {
"gasLimit": 20000000,
"minBaseFee": 1000000000,
"targetGas": 100000000,
"baseFeeChangeDenominator": 48,
"minBlockGasCost": 0,
"maxBlockGasCost": 10000000,
"targetBlockRate": 2,
"blockGasCostStep": 500000
}
},
"alloc": {
"${GENESIS_ADDRESS}": {
"balance": "0x52B7D2DCC80CD2E4000000"
}
},
"nonce": "0x0",
"timestamp": "0x0",
"extraData": "0x00",
"gasLimit": "0x1312D00",
"difficulty": "0x0",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x0000000000000000000000000000000000000000",
"number": "0x0",
"gasUsed": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
EOF
cat /tmp/subnet-evm.genesis.json
# replace execPath with the path to AvalancheGo on your machine
CAMINO_NODE_EXEC_PATH="${HOME}/go/src/github.com/chain4travel/camino-node/build/camino-node"
CAMINO_NODE_PLUGIN_PATH="${HOME}/go/src/github.com/chain4travel/camino-node/build/plugins"
curl -X POST -k http://localhost:8081/v1/control/start -d '{"execPath":"'${CAMINO_NODE_EXEC_PATH}'","numNodes":5,"logLevel":"INFO","pluginDir":"'${CAMINO_NODE_PLUGIN_PATH}'","blockchainSpecs":[{"vm_name":"subnetevm","genesis":"/tmp/subnet-evm.genesis.json"}]}'
# or
camino-network-runner control start \
--log-level debug \
--endpoint="0.0.0.0:8080" \
--camino-node ${CAMINO_NODE_EXEC_PATH} \
--plugin-dir ${CAMINO_NODE_PLUGIN_PATH} \
--blockchain-specs '[{"vm_name": "subnetevm", "genesis": "/tmp/subnet-evm.genesis.json"}]'
# to get cluster information including blockchain ID
curl -X POST -k http://localhost:8081/v1/control/status -d ''
Blockchain config file, network upgrade file, and subnet config file paths can be optionally specified at network start, eg:
curl -X POST -k http://localhost:8081/v1/control/start -d '{"execPath":"'${CAMINO_NODE_EXEC_PATH}'","numNodes":5,"logLevel":"INFO","pluginDir":"'${CAMINO_NODE_PLUGIN_PATH}'","blockchainSpecs":[{"vm_name":"subnetevm","genesis":"/tmp/subnet-evm.genesis.json","chain_config":"'$CHAIN_CONFIG_PATH'","network_upgrade":"'$NETWORK_UPGRADE_PATH'","subnet_config":"'$SUBNET_CONFIG_PATH'"}]}'
# or
camino-network-runner control start \
--log-level debug \
--endpoint="0.0.0.0:8080" \
--camino-node ${CAMINO_NODE_EXEC_PATH} \
--plugin-dir ${CAMINO_NODE_PLUGIN_PATH} \
--blockchain-specs '[{"vm_name": "subnetevm", "genesis": "/tmp/subnet-evm.genesis.json", "chain_config": "'$CHAIN_CONFIG_PATH'", "network_upgrade": "'$NETWORK_UPGRADE_PATH'", "subnet_config": "'$SUBNET_CONFIG_PATH'"}]'
To start the server:
camino-network-runner server \
--log-level debug \
--port=":8080" \
--grpc-gateway-port=":8081"
# make sure network-runner server is up
curl -X POST -k http://localhost:8081/v1/ping -d ''
To start the cluster with custom chains:
# or download from https://github.com/ava-labs/subnet-cli/releases
cd ${HOME}/go/src/github.com/ava-labs/subnet-cli
go install -v .
subnet-cli create VMID blobvm
# kM6h4LYe3AcEU1MB2UNg6ubzAiDAALZzpVrbX8zn3hXF6Avd8
# download from https://github.com/ava-labs/avalanchego/releases
# or build
rm -rf ${HOME}/go/src/github.com/ava-labs/avalanchego/build
cd ${HOME}/go/src/github.com/ava-labs/avalanchego
./scripts/build.sh
cd ${HOME}/go/src/github.com/ava-labs/blobvm
go build -v \
-o ${HOME}/go/src/github.com/chain4travel/camino-node/build/plugins/kM6h4LYe3AcEU1MB2UNg6ubzAiDAALZzpVrbX8zn3hXF6Avd8 \
./cmd/blobvm
# make sure binaries are built
find ${HOME}/go/src/github.com/ava-labs/avalanchego/build
# for example
# .../build
# .../build/plugins
# .../build/plugins/kM6h4LYe3AcEU1MB2UNg6ubzAiDAALZzpVrbX8zn3hXF6Avd8
# .../build/plugins/evm
# .../build/avalanchego
# generate the genesis for the custom chain
cd ${HOME}/go/src/github.com/ava-labs/blobvm
go install -v ./cmd/blob-cli
echo "[]" > /tmp/alloc.json
blob-cli genesis 1 /tmp/alloc.json --genesis-file /tmp/blobvm.genesis.json
cat /tmp/blobvm.genesis.json
# replace execPath with the path to AvalancheGo on your machine
CAMINO_NODE_EXEC_PATH="${HOME}/go/src/github.com/chain4travel/camino-node/build/camino-node"
CAMINO_NODE_PLUGIN_PATH="${HOME}/go/src/github.com/chain4travel/camino-node/build/plugins"
curl -X POST -k http://localhost:8081/v1/control/start -d '{"execPath":"'${CAMINO_NODE_EXEC_PATH}'","numNodes":5,"logLevel":"INFO","pluginDir":"'${CAMINO_NODE_PLUGIN_PATH}'","blockchainSpecs":[{"vm_name":"blobvm","genesis":"/tmp/blobvm.genesis.json"}]}'
# or
camino-network-runner control start \
--log-level debug \
--endpoint="0.0.0.0:8080" \
--camino-node ${CAMINO_NODE_EXEC_PATH} \
--plugin-dir ${CAMINO_NODE_PLUGIN_PATH} \
--blockchain-specs '[{"vm_name": "blobvm", "genesis": "/tmp/blobvm.genesis.json"}]'
# to get cluster information including blockchain ID
curl -X POST -k http://localhost:8081/v1/control/status -d ''
Blockchain config file and network upgrade file paths can be optionally specified at network start, eg:
curl -X POST -k http://localhost:8081/v1/control/start -d '{"execPath":"'${CAMINO_NODE_EXEC_PATH}'","numNodes":5,"logLevel":"INFO","pluginDir":"'${CAMINO_NODE_PLUGIN_PATH}'","blockchainSpecs":[{"vm_name":"blobvm","genesis":"/tmp/blobvm.json","chain_config":"'$CHAIN_CONFIG_PATH'","network_upgrade":"'$NETWORK_UPGRADE_PATH'","subnet_config":"'$SUBNET_CONFIG_PATH'"}]}'
# or
camino-network-runner control start \
--log-level debug \
--endpoint="0.0.0.0:8080" \
--camino-node ${CAMINO_NODE_EXEC_PATH} \
--plugin-dir ${CAMINO_NODE_PLUGIN_PATH} \
--blockchain-specs '[{"vm_name": "blobvm", "genesis": "/tmp/blobvm.genesis.json", "chain_config": "'$CHAIN_CONFIG_PATH'", "network_upgrade": "'$NETWORK_UPGRADE_PATH'", "subnet_config": "'$SUBNET_CONFIG_PATH'"}]'
To start the server:
camino-network-runner server \
--log-level debug \
--port=":8080" \
--grpc-gateway-port=":8081"
# make sure network-runner server is up
curl -X POST -k http://localhost:8081/v1/ping -d ''
To start the cluster with custom chains:
# or download from https://github.com/ava-labs/subnet-cli/releases
cd ${HOME}/go/src/github.com/ava-labs/subnet-cli
go install -v .
subnet-cli create VMID timestampvm
# tGas3T58KzdjcJ2iKSyiYsWiqYctRXaPTqBCA11BqEkNg8kPc
# download from https://github.com/ava-labs/avalanchego/releases
# or build
rm -rf ${HOME}/go/src/github.com/ava-labs/avalanchego/build
cd ${HOME}/go/src/github.com/ava-labs/avalanchego
./scripts/build.sh
# or download from https://github.com/ava-labs/timestampvm/releases
# cd ${HOME}/go/src/github.com/ava-labs/timestampvm
# ./scripts/build.sh
cd ${HOME}/go/src/github.com/ava-labs/timestampvm
go build -v \
-o ${HOME}/go/src/github.com/chain4travel/camino-node/build/plugins/tGas3T58KzdjcJ2iKSyiYsWiqYctRXaPTqBCA11BqEkNg8kPc \
./main
# make sure binaries are built
find ${HOME}/go/src/github.com/ava-labs/avalanchego/build
# for example
# .../build
# .../build/plugins
# .../build/plugins/tGas3T58KzdjcJ2iKSyiYsWiqYctRXaPTqBCA11BqEkNg8kPc
# .../build/plugins/evm
# .../build/avalanchego
# generate the genesis for the custom chain
# NOTE: timestampvm takes arbitrary data for its genesis
echo hello > /tmp/timestampvm.genesis.json
# replace execPath with the path to AvalancheGo on your machine
CAMINO_NODE_EXEC_PATH="${HOME}/go/src/github.com/chain4travel/camino-node/build/camino-node"
CAMINO_NODE_PLUGIN_PATH="${HOME}/go/src/github.com/chain4travel/camino-node/build/plugins"
curl -X POST -k http://localhost:8081/v1/control/start -d '{"execPath":"'${CAMINO_NODE_EXEC_PATH}'","numNodes":5,"logLevel":"INFO","pluginDir":"'${CAMINO_NODE_PLUGIN_PATH}'","blockchainSpecs":[{"vmName":"timestampvm","genesis":"/tmp/timestampvm.genesis.json","blockchain_alias":"timestamp"}]}'
# or
camino-network-runner control start \
--log-level debug \
--endpoint="0.0.0.0:8080" \
--camino-node ${CAMINO_NODE_EXEC_PATH} \
--plugin-dir ${CAMINO_NODE_PLUGIN_PATH} \
--blockchain-specs '[{"vm_name":"timestampvm","genesis":"/tmp/timestampvm.genesis.json","blockchain_alias":"timestamp"}]'
# to get cluster information including blockchain ID
curl -X POST -k http://localhost:8081/v1/control/status -d ''
To call timestampvm
APIs:
# in this example,
# "tGas3T58KzdjcJ2iKSyiYsWiqYctRXaPTqBCA11BqEkNg8kPc" is the Vm Id for the static service
curl -X POST --data '{
"jsonrpc": "2.0",
"id" : 1,
"method" : "timestampvm.encode",
"params" : {
"data": "mynewblock",
"length": 32
}
}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/vm/tGas3T58KzdjcJ2iKSyiYsWiqYctRXaPTqBCA11BqEkNg8kPc
# in this example,
# "E8isHenre76NMxbJ3munSQatV8GoQ4XKWQg9vD34xMBqEFJGf" is the blockchain Id
# "timestamp" is the blockchain alias
# You can use 127.0.0.1:9650/ext/bc/timestamp or 127.0.0.1:9650/ext/bc/E8isHenre76NMxbJ3munSQatV8GoQ4XKWQg9vD34xMBqEFJGf
curl -X POST --data '{
"jsonrpc": "2.0",
"method": "timestampvm.proposeBlock",
"params":{
"data":"0x6d796e6577626c6f636b0000000000000000000000000000000000000000000014228326"
},
"id": 1
}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/bc/timestamp
curl -X POST --data '{
"jsonrpc": "2.0",
"method": "timestampvm.getBlock",
"params":{},
"id": 1
}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/bc/timestamp
When the user creates a network, they specify the configurations of the nodes that are in the network upon creation.
A node config is defined by this struct:
type Config struct {
// A node's name must be unique from all other nodes
// in a network. If Name is the empty string, a
// unique name is assigned on node creation.
Name string `json:"name"`
// True if other nodes should use this node
// as a bootstrap beacon.
IsBeacon bool `json:"isBeacon"`
// Must not be nil.
StakingKey string `json:"stakingKey"`
// Must not be nil.
StakingCert string `json:"stakingCert"`
// Must not be nil.
StakingSigningKey string `json:"stakingSigningKey"`
// May be nil.
ConfigFile string `json:"configFile"`
// May be nil.
ChainConfigFiles map[string]string `json:"chainConfigFiles"`
// May be nil.
UpgradeConfigFiles map[string]string `json:"upgradeConfigFiles"`
// May be nil.
SubnetConfigFiles map[string]string `json:"subnetConfigFiles"`
// Flags can hold additional flags for the node.
// It can be empty.
// The precedence of flags handling is:
// 1. Flags defined in node.Config (this struct) override
// 2. Flags defined in network.Config override
// 3. Flags defined in the json config file
Flags map[string]interface{} `json:"flags"`
// What type of node this is
BinaryPath string `json:"binaryPath"`
// If non-nil, direct this node's Stdout to os.Stdout
RedirectStdout bool `json:"redirectStdout"`
// If non-nil, direct this node's Stderr to os.Stderr
RedirectStderr bool `json:"redirectStderr"`
}
As you can see, some fields of the config must be set, while others will be auto-generated if not provided. Bootstrap IPs/ IDs will be overwritten even if provided.
You can create a custom AvalancheGo genesis with function network.NewAvalancheGoGenesis
:
// Return a genesis JSON where:
// The nodes in [genesisVdrs] are validators.
// The C-Chain and X-Chain balances are given by
// [cChainBalances] and [xChainBalances].
// Note that many of the genesis fields (i.e. reward addresses)
// are randomly generated or hard-coded.
func NewAvalancheGoGenesis(
log logging.Logger,
networkID uint32,
xChainBalances []AddrAndBalance,
cChainBalances []AddrAndBalance,
genesisVdrs []ids.ShortID,
) ([]byte, error)
Later on the genesis contents can be used in network creation.
The function NewNetwork
returns a new network, parameterized on network.Config
:
type Config struct {
// Must not be empty
Genesis string `json:"genesis"`
// May have length 0
// (i.e. network may have no nodes on creation.)
NodeConfigs []node.Config `json:"nodeConfigs"`
// Flags that will be passed to each node in this network.
// It can be empty.
// Config flags may also be passed in a node's config struct
// or config file.
// The precedence of flags handling is, from highest to lowest:
// 1. Flags defined in a node's node.Config
// 2. Flags defined in a network's network.Config
// 3. Flags defined in a node's config file
// For example, if a network.Config has flag W set to X,
// and a node within that network has flag W set to Y,
// and the node's config file has flag W set to Z,
// then the node will be started with flag W set to Y.
Flags map[string]interface{} `json:"flags"`
}
The function that returns a new network may have additional configuration fields.
The helper function NewDefaultNetwork
returns a network using a pre-defined configuration. This allows users to create a new network without needing to define any configurations.
If set, the environment variable ${CAMINO_NETWORK} can be used to create a camino network.
// NewDefaultNetwork returns a new network using a pre-defined
// network configuration.
// The following addresses are pre-funded:
// X-Chain Address 1: X-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p
// X-Chain Address 1 Key: PrivateKey-ewoqjP7PxY4yr3iLTpLisriqt94hdyDFNgchSxGGztUrTXtNN
// X-Chain Address 2: X-custom16045mxr3s2cjycqe2xfluk304xv3ezhkhsvkpr
// X-Chain Address 2 Key: PrivateKey-2fzYBh3bbWemKxQmMfX6DSuL2BFmDSLQWTvma57xwjQjtf8gFq
// P-Chain Address 1: P-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p
// P-Chain Address 1 Key: PrivateKey-ewoqjP7PxY4yr3iLTpLisriqt94hdyDFNgchSxGGztUrTXtNN
// P-Chain Address 2: P-custom16045mxr3s2cjycqe2xfluk304xv3ezhkhsvkpr
// P-Chain Address 2 Key: PrivateKey-2fzYBh3bbWemKxQmMfX6DSuL2BFmDSLQWTvma57xwjQjtf8gFq
// C-Chain Address: 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC
// C-Chain Address Key: 56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027
// The following nodes are validators:
// * NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg
// * NodeID-MFrZFVCXPv5iCn6M9K6XduxGTYp891xXZ
// * NodeID-NFBbbJ4qCmNaCzeW7sxErhvWqvEQMnYcN
// * NodeID-GWPcbFJZFfZreETSoWjPimr846mXEKCtu
// * NodeID-P7oB2McjBGgW2NXXWVYjV8JEDFoW9xDE5
func NewDefaultNetwork(
log logging.Logger,
binaryPath string,
reassignPortsIfUsed,
) (network.Network, error)
The associated pre-defined configuration is also available to users by calling NewDefaultConfig
function.
A given network state, including the node ports and the full blockchain state, can be saved to a named snapshot. The network can then be restarted from such a snapshot any time later.
// Save network snapshot
// Network is stopped in order to do a safe persistence
// Returns the full local path to the snapshot dir
SaveSnapshot(context.Context, string) (string, error)
// Remove network snapshot
RemoveSnapshot(string) error
// Get names of all available snapshots
GetSnapshotNames() ([]string, error)
To create a new network from a snapshot, the function NewNetworkFromSnapshot
is provided.
The network runner allows users to interact with an CaminoGo network using the network.Network
interface:
// Network is an abstraction of an Avalanche network
type Network interface {
// Returns nil if all the nodes in the network are healthy.
// A stopped network is considered unhealthy.
// Timeout is given by the context parameter.
Healthy(context.Context) error
// Stop all the nodes.
// Returns ErrStopped if Stop() was previously called.
Stop(context.Context) error
// Start a new node with the given config.
// Returns ErrStopped if Stop() was previously called.
AddNode(node.Config) (node.Node, error)
// Stop the node with this name.
// Returns ErrStopped if Stop() was previously called.
RemoveNode(name string) error
// Return the node with this name.
// Returns ErrStopped if Stop() was previously called.
GetNode(name string) (node.Node, error)
// Return all the nodes in this network.
// Node name --> Node.
// Returns ErrStopped if Stop() was previously called.
GetAllNodes() (map[string]node.Node, error)
// Returns the names of all nodes in this network.
// Returns ErrStopped if Stop() was previously called.
GetNodeNames() ([]string, error)
// Save network snapshot
// Network is stopped in order to do a safe preservation
// Returns the full local path to the snapshot dir
SaveSnapshot(context.Context, string) (string, error)
// Remove network snapshot
RemoveSnapshot(string) error
// Get name of available snapshots
GetSnapshotNames() ([]string, error)
}
and allows users to interact with a node using the node.Node
interface:
// Node represents a Camino node
type Node interface {
// Return this node's name, which is unique
// across all the nodes in its network.
GetName() string
// Return this node's Avalanche node ID.
GetNodeID() ids.ShortID
// Return a client that can be used to make API calls.
GetAPIClient() api.Client
// Return this node's IP (e.g. 127.0.0.1).
GetURL() string
// Return this node's P2P (staking) port.
GetP2PPort() uint16
// Return this node's HTTP API port.
GetAPIPort() uint16
// Starts a new test peer, connects it to the given node, and returns the peer.
// [handler] defines how the test peer handles messages it receives.
// The test peer can be used to send messages to the node it's attached to.
// It's left to the caller to maintain a reference to the returned peer.
// The caller should call StartClose() on the peer when they're done with it.
AttachPeer(ctx context.Context, handler router.InboundHandler) (peer.Peer, error)
// Return this node's camino-node binary path
GetBinaryPath() string
// Return this node's db dir
GetDbDir() string
// Return this node's logs dir
GetLogsDir() string
// Return this node's config file contents
GetConfigFile() string
}