diff --git a/LLM.md b/LLM.md index edb74e15..8fa3ddfc 100644 --- a/LLM.md +++ b/LLM.md @@ -6,7 +6,7 @@ ## Project Overview -Lux Netrunner is a powerful network orchestration and testing framework for blockchain development. It provides comprehensive tools for creating, managing, and testing multi-node blockchain networks with support for custom VMs, chains (L2 blockchains), and complex network topologies. +Lux Netrunner is a powerful network orchestration and testing framework for blockchain development. It provides comprehensive tools for creating, managing, and testing multi-node blockchain networks with support for custom VMs, application chains, and complex network topologies. ## Essential Commands @@ -1091,7 +1091,7 @@ After fixes, successfully deployed Zoo chain on mainnet: ### RLP Block Import Limitation -**Discovery**: The L2 EVM doesn't support `admin_importChain` RPC method. Blocks in L2 EVM chains come through Lux consensus, not direct import. +**Discovery**: The EVM plugin doesn't support `admin_importChain` RPC method. Blocks in EVM chains come through Lux consensus, not direct import. **Available RPC modules on Zoo chain**: eth, net, rpc, web3 (no admin or debug) diff --git a/README.md b/README.md index cfd72791..9497dcfe 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,7 @@ Additional optional parameters which can be passed to the start command: ```bash --plugin-dir ${LUXD_PLUGIN_PATH} \ ---blockchain-specs '[{"vm_name": "subnetevm", "genesis": "/tmp/subnet-evm.genesis.json"}]' +--blockchain-specs '[{"vm_name": "evm", "genesis": "/tmp/evm.genesis.json"}]' --global-node-config '{"index-enabled":false, "api-admin-enabled":true,"network-peer-list-gossip-frequency":"300ms"}' --custom-node-configs" '{"node1":{"log-level":"debug","api-admin-enabled":false},"node2":{...},...}' ``` @@ -155,8 +155,8 @@ netrunner control start \ --global-node-config '{"http-host":"0.0.0.0"}' ``` -`--plugin-dir` and `--blockchain-specs` are parameters relevant to chain (L2 blockchain) operation. -See the [chain deployment](#network-runner-rpc-server-l2-evm-example) section for details about how to run chains. +`--plugin-dir` and `--blockchain-specs` are parameters relevant to chain operation. +See the [chain deployment](#network-runner-rpc-server-evm-example) section for details about how to run chains. The network-runner supports node node configuration at different levels. @@ -302,38 +302,38 @@ netrunner control remove-snapshot snapshotName To create 1 chain (validator set), with all existing nodes as participants (requires network restart): ```bash -curl -X POST -k http://localhost:8081/v1/control/createsubnets -d '[{}]' +curl -X POST -k http://localhost:8081/v1/control/createchains -d '[{}]' # or -netrunner control create-subnets '[{}]' +netrunner control create-chains '[{}]' ``` To create 1 chain (validator set), with some of existing nodes as participants (requires network restart): ```bash -curl -X POST -k http://localhost:8081/v1/control/createsubnets -d '[{"participants": ["node1", "node2"]}]' +curl -X POST -k http://localhost:8081/v1/control/createchains -d '[{"participants": ["node1", "node2"]}]' # or -netrunner control create-subnets '[{"participants": ["node1", "node2"]}]' +netrunner control create-chains '[{"participants": ["node1", "node2"]}]' ``` To create 1 chain (validator set), with some of existing nodes and another new node as participants (requires network restart): ```bash -curl -X POST -k http://localhost:8081/v1/control/createsubnets -d '[{"participants": ["node1", "node2", "testNode"]}]' +curl -X POST -k http://localhost:8081/v1/control/createchains -d '[{"participants": ["node1", "node2", "testNode"]}]' # or -netrunner control create-subnets '[{"participants": ["node1", "node2", "testNode"]}]' +netrunner control create-chains '[{"participants": ["node1", "node2", "testNode"]}]' ``` To create N chains (validator sets) (requires network restart): ```bash -curl -X POST -k http://localhost:8081/v1/control/createsubnets -d '[{}, {"participants": ["node1", "node2", "node3"]}, {"participants": ["node1", "node2", "testNode"]}]' +curl -X POST -k http://localhost:8081/v1/control/createchains -d '[{}, {"participants": ["node1", "node2", "node3"]}, {"participants": ["node1", "node2", "testNode"]}]' # or -netrunner control create-subnets '[{}, {"participants": ["node1", "node2", "node3"]}, {"participants": ["node1", "node2", "testNode"]}]' +netrunner control create-chains '[{}, {"participants": ["node1", "node2", "node3"]}, {"participants": ["node1", "node2", "testNode"]}]' ``` @@ -358,38 +358,38 @@ netrunner control create-blockchains '[{"vm_name":"'$VM_NAME'","genesis":"'$GENE To create a blockchain with a chain ID (does not require restart): ```bash -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'"}]}' +curl -X POST -k http://localhost:8081/v1/control/createblockchains -d '{"pluginDir":"'$PLUGIN_DIR'","blockchainSpecs":[{"vm_name":"'$VM_NAME'","genesis":"'$GENESIS_PATH'", "blockchain_id": "'$BLOCKCHAIN_ID'"}]}' # or -netrunner control create-blockchains '[{"vm_name":"'$VM_NAME'","genesis":"'$GENESIS_PATH'", "subnet_id": "'$SUBNET_ID'"}]' --plugin-dir $PLUGIN_DIR +netrunner control create-blockchains '[{"vm_name":"'$VM_NAME'","genesis":"'$GENESIS_PATH'", "blockchain_id": "'$BLOCKCHAIN_ID'"}]' --plugin-dir $PLUGIN_DIR ``` To create a blockchain with a chain ID, and chain config, network upgrade and chain config file paths (requires network restart): ```bash -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'"}]}' +curl -X POST -k http://localhost:8081/v1/control/createblockchains -d '{"pluginDir":"'$PLUGIN_DIR'","blockchainSpecs":[{"vm_name":"'$VM_NAME'","genesis":"'$GENESIS_PATH'", "blockchain_id": "'$BLOCKCHAIN_ID'", "chain_config": "'$CHAIN_CONFIG_PATH'", "network_upgrade": "'$NETWORK_UPGRADE_PATH'", "chain_config_per_chain": "'$CHAIN_CONFIG_PER_CHAIN_PATH'"}]}' # or -netrunner 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 +netrunner control create-blockchains '[{"vm_name":"'$VM_NAME'","genesis":"'$GENESIS_PATH'", "blockchain_id": "'$BLOCKCHAIN_ID'", "chain_config": "'$CHAIN_CONFIG_PATH'", "network_upgrade": "'$NETWORK_UPGRADE_PATH'", "chain_config_per_chain": "'$CHAIN_CONFIG_PER_CHAIN_PATH'"}]' --plugin-dir $PLUGIN_DIR ``` To create a blockchain with a new chain with select nodes as participants (requires network restart): -(New nodes will first be added as primary validators similar to the process in `create-subnets`) +(New nodes will first be added as primary validators similar to the process in `create-chains`) ```bash -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"]}"]}' +curl -X POST -k http://localhost:8081/v1/control/createblockchains -d '{"pluginDir":"'$PLUGIN_DIR'","blockchainSpecs":[{"vm_name":"'$VM_NAME'","genesis":"'$GENESIS_PATH'", "chain_spec": "{"participants": ["node1", "node2", "testNode"]}"]}' # or -netrunner control create-blockchains '[{"vm_name":"'$VM_NAME'","genesis":"'$GENESIS_PATH'", "subnet_spec": "{"participants": ["node1", "node2", "testNode"]}]' --plugin-dir $PLUGIN_DIR +netrunner control create-blockchains '[{"vm_name":"'$VM_NAME'","genesis":"'$GENESIS_PATH'", "chain_spec": "{"participants": ["node1", "node2", "testNode"]}]' --plugin-dir $PLUGIN_DIR ``` To create two blockchains in two disjoint chains (not shared validators), and where all validators have bls keys (participants new to the network): ```bash -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": ["new_node1", "new_node2"]}},{"vm_name":"'$VM_NAME'","genesis":"'$GENESIS_PATH'", "subnet_spec": {"participants": ["new_node3", "new_node4"]}}]' +curl -X POST -k http://localhost:8081/v1/control/createblockchains -d '{"pluginDir":"'$PLUGIN_DIR'","blockchainSpecs":[{"vm_name":"'$VM_NAME'","genesis":"'$GENESIS_PATH'", "chain_spec": {"participants": ["new_node1", "new_node2"]}},{"vm_name":"'$VM_NAME'","genesis":"'$GENESIS_PATH'", "chain_spec": {"participants": ["new_node3", "new_node4"]}}]' # or -go run main.go control create-blockchains '[{"vm_name":"'$VM_NAME'","genesis":"'$GENESIS_PATH'", "subnet_spec": {"participants": ["new_node1", "new_node2"]}},{"vm_name":"'$VM_NAME'","genesis":"'$GENESIS_PATH'", "subnet_spec": {"participants": ["new_node3", "new_node4"]}}]' +go run main.go control create-blockchains '[{"vm_name":"'$VM_NAME'","genesis":"'$GENESIS_PATH'", "chain_spec": {"participants": ["new_node1", "new_node2"]}},{"vm_name":"'$VM_NAME'","genesis":"'$GENESIS_PATH'", "chain_spec": {"participants": ["new_node3", "new_node4"]}}]' ``` 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`: @@ -407,10 +407,10 @@ Chain config can also be defined on a per node basis. For that, a per node chain Then a blockchain with different chain configs per node can be created with this command: ```bash -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'"}]}' +curl -X POST -k http://localhost:8081/v1/control/createblockchains -d '{"pluginDir":"'$PLUGIN_DIR'","blockchainSpecs":[{"vm_name":"'$VM_NAME'","genesis":"'$GENESIS_PATH'", "blockchain_id": "'$BLOCKCHAIN_ID'", "per_node_chain_config": "'$PER_NODE_CHAIN_CONFIG'", "network_upgrade": "'$NETWORK_UPGRADE_PATH'", "chain_config_per_chain": "'$CHAIN_CONFIG_PER_CHAIN_PATH'"}]}' # or -netrunner 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 +netrunner control create-blockchains '[{"vm_name":"'$VM_NAME'","genesis":"'$GENESIS_PATH'", "blockchain_id": "'$BLOCKCHAIN_ID'", "per_node_chain_config": "'$PER_NODE_CHAIN_CONFIG'", "network_upgrade": "'$NETWORK_UPGRADE_PATH'", "chain_config_per_chain": "'$CHAIN_CONFIG_PER_CHAIN_PATH'"}]' --plugin-dir $PLUGIN_DIR ``` To remove (stop) a node: @@ -552,7 +552,7 @@ netrunner control stop \ --endpoint="0.0.0.0:8080" ``` -## `network-runner` RPC server: L2 EVM example +## `network-runner` RPC server: EVM chain example To start the server: @@ -569,10 +569,10 @@ curl -X POST -k http://localhost:8081/v1/ping -d '' To start the cluster with custom chains: ```bash -# or download from https://github.com/luxfi/subnet-cli/releases -cd ${HOME}/go/src/github.com/luxfi/subnet-cli +# or download from https://github.com/luxfi/cli/releases +cd ${HOME}/go/src/github.com/luxfi/cli go install -v . -subnet-cli create VMID subnetevm +lux chain create VMID evm # srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy # download from https://github.com/luxfi/sdk/node/releases @@ -581,8 +581,8 @@ rm -rf ${HOME}/go/src/github.com/luxfi/sdk/node/build cd ${HOME}/go/src/github.com/luxfi/node ./scripts/build.sh -# ref. https://github.com/luxfi/subnet-evm/blob/b69e47e0398b5237cda0422f6a32969e64bde346/scripts/run.sh -cd ${HOME}/go/src/github.com/luxfi/subnet-evm +# ref. https://github.com/luxfi/evm/blob/b69e47e0398b5237cda0422f6a32969e64bde346/scripts/run.sh +cd ${HOME}/go/src/github.com/luxfi/evm go build -v \ -o ${HOME}/go/src/github.com/luxfi/sdk/node/build/plugins/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy \ ./plugin @@ -599,7 +599,7 @@ find ${HOME}/go/src/github.com/luxfi/sdk/node/build # generate the genesis for the custom chain export CHAIN_ID=99999 export GENESIS_ADDRESS="0x9011E888251AB053B7bD1cdB598Db4f9DEd94714" -cat < /tmp/subnet-evm.genesis.json +cat < /tmp/evm.genesis.json { "config": { "chainId": $CHAIN_ID, @@ -642,7 +642,7 @@ cat < /tmp/subnet-evm.genesis.json "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" } EOF -cat /tmp/subnet-evm.genesis.json +cat /tmp/evm.genesis.json ``` ```bash @@ -650,7 +650,7 @@ cat /tmp/subnet-evm.genesis.json LUXD_EXEC_PATH="${HOME}/go/src/github.com/luxfi/sdk/node/build/node" LUXD_PLUGIN_PATH="${HOME}/go/src/github.com/luxfi/sdk/node/build/plugins" -curl -X POST -k http://localhost:8081/v1/control/start -d '{"execPath":"'${LUXD_EXEC_PATH}'","numNodes":5,"logLevel":"INFO","pluginDir":"'${LUXD_PLUGIN_PATH}'","blockchainSpecs":[{"vm_name":"subnetevm","genesis":"/tmp/subnet-evm.genesis.json"}]}' +curl -X POST -k http://localhost:8081/v1/control/start -d '{"execPath":"'${LUXD_EXEC_PATH}'","numNodes":5,"logLevel":"INFO","pluginDir":"'${LUXD_PLUGIN_PATH}'","blockchainSpecs":[{"vm_name":"evm","genesis":"/tmp/evm.genesis.json"}]}' # or netrunner control start \ @@ -658,7 +658,7 @@ netrunner control start \ --endpoint="0.0.0.0:8080" \ --node-path ${LUXD_EXEC_PATH} \ --plugin-dir ${LUXD_PLUGIN_PATH} \ ---blockchain-specs '[{"vm_name": "subnetevm", "genesis": "/tmp/subnet-evm.genesis.json"}]' +--blockchain-specs '[{"vm_name": "evm", "genesis": "/tmp/evm.genesis.json"}]' ``` ```bash @@ -669,7 +669,7 @@ curl -X POST -k http://localhost:8081/v1/control/status -d '' Blockchain config file, network upgrade file, and network config file paths can be optionally specified at network start, eg: ```bash -curl -X POST -k http://localhost:8081/v1/control/start -d '{"execPath":"'${LUXD_EXEC_PATH}'","numNodes":5,"logLevel":"INFO","pluginDir":"'${LUXD_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'"}]}' +curl -X POST -k http://localhost:8081/v1/control/start -d '{"execPath":"'${LUXD_EXEC_PATH}'","numNodes":5,"logLevel":"INFO","pluginDir":"'${LUXD_PLUGIN_PATH}'","blockchainSpecs":[{"vm_name":"evm","genesis":"/tmp/evm.genesis.json","chain_config":"'$CHAIN_CONFIG_PATH'","network_upgrade":"'$NETWORK_UPGRADE_PATH'","chain_config_per_chain":"'$CHAIN_CONFIG_PER_CHAIN_PATH'"}]}' # or netrunner control start \ @@ -677,7 +677,7 @@ netrunner control start \ --endpoint="0.0.0.0:8080" \ --node-path ${LUXD_EXEC_PATH} \ --plugin-dir ${LUXD_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'"}]' +--blockchain-specs '[{"vm_name": "evm", "genesis": "/tmp/evm.genesis.json", "chain_config": "'$CHAIN_CONFIG_PATH'", "network_upgrade": "'$NETWORK_UPGRADE_PATH'", "chain_config_per_chain": "'$CHAIN_CONFIG_PER_CHAIN_PATH'"}]' ``` ## `network-runner` RPC server: `blobvm` example @@ -697,10 +697,10 @@ curl -X POST -k http://localhost:8081/v1/ping -d '' To start the cluster with custom chains: ```bash -# or download from https://github.com/luxfi/subnet-cli/releases -cd ${HOME}/go/src/github.com/luxfi/subnet-cli +# or download from https://github.com/luxfi/cli/releases +cd ${HOME}/go/src/github.com/luxfi/cli go install -v . -subnet-cli create VMID blobvm +lux chain create VMID blobvm # kM6h4LYe3AcEU1MB2UNg6ubzAiDAALZzpVrbX8zn3hXF6Avd8 # download from https://github.com/luxfi/sdk/node/releases @@ -755,7 +755,7 @@ 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: ```bash -curl -X POST -k http://localhost:8081/v1/control/start -d '{"execPath":"'${LUXD_EXEC_PATH}'","numNodes":5,"logLevel":"INFO","pluginDir":"'${LUXD_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'"}]}' +curl -X POST -k http://localhost:8081/v1/control/start -d '{"execPath":"'${LUXD_EXEC_PATH}'","numNodes":5,"logLevel":"INFO","pluginDir":"'${LUXD_PLUGIN_PATH}'","blockchainSpecs":[{"vm_name":"blobvm","genesis":"/tmp/blobvm.json","chain_config":"'$CHAIN_CONFIG_PATH'","network_upgrade":"'$NETWORK_UPGRADE_PATH'","chain_config_per_chain":"'$CHAIN_CONFIG_PER_CHAIN_PATH'"}]}' # or netrunner control start \ @@ -763,7 +763,7 @@ netrunner control start \ --endpoint="0.0.0.0:8080" \ --node-path ${LUXD_EXEC_PATH} \ --plugin-dir ${LUXD_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'"}]' +--blockchain-specs '[{"vm_name": "blobvm", "genesis": "/tmp/blobvm.genesis.json", "chain_config": "'$CHAIN_CONFIG_PATH'", "network_upgrade": "'$NETWORK_UPGRADE_PATH'", "chain_config_per_chain": "'$CHAIN_CONFIG_PER_CHAIN_PATH'"}]' ``` ## `network-runner` RPC server: `timestampvm` example @@ -783,10 +783,10 @@ curl -X POST -k http://localhost:8081/v1/ping -d '' To start the cluster with custom chains: ```bash -# or download from https://github.com/luxfi/subnet-cli/releases -cd ${HOME}/go/src/github.com/luxfi/subnet-cli +# or download from https://github.com/luxfi/cli/releases +cd ${HOME}/go/src/github.com/luxfi/cli go install -v . -subnet-cli create VMID timestampvm +lux chain create VMID timestampvm # tGas3T58KzdjcJ2iKSyiYsWiqYctRXaPTqBCA11BqEkNg8kPc # download from https://github.com/luxfi/sdk/node/releases @@ -904,7 +904,7 @@ type Config struct { // May be nil. UpgradeConfigFiles map[string]string `json:"upgradeConfigFiles"` // May be nil. - SubnetConfigFiles map[string]string `json:"subnetConfigFiles"` + ChainConfigPerChainFiles map[string]string `json:"chainConfigPerChainFiles"` // Flags can hold additional flags for the node. // It can be empty. // The precedence of flags handling is: diff --git a/docs/content/docs/index.mdx b/docs/content/docs/index.mdx index 76d8f4e5..4a5c469c 100644 --- a/docs/content/docs/index.mdx +++ b/docs/content/docs/index.mdx @@ -5,7 +5,7 @@ description: Lux Netrunner - Network orchestration and testing framework for Lux # Lux Netrunner -Lux Netrunner is a powerful network orchestration and testing framework designed for blockchain development. It provides comprehensive tools for creating, managing, and testing multi-node blockchain networks with support for custom VMs, chains (L2 blockchains), and complex network topologies. +Lux Netrunner is a powerful network orchestration and testing framework designed for blockchain development. It provides comprehensive tools for creating, managing, and testing multi-node blockchain networks with support for custom VMs, application chains, and complex network topologies. ## Overview diff --git a/docs/content/docs/test-network-setup.mdx b/docs/content/docs/test-network-setup.mdx index 5e20632a..99888162 100644 --- a/docs/content/docs/test-network-setup.mdx +++ b/docs/content/docs/test-network-setup.mdx @@ -194,26 +194,26 @@ cat > elastic-network.json < /tmp/subnet-evm.genesis.json < /tmp/evm.genesis.json < /tmp/subnet-evm.genesis.json <> /dev/stderr + echo "Usage: ${0} [VERSION_1] [VERSION_2] [EVM_VERSION]" >> /dev/stderr exit 255 fi VERSION_2=$2 if [[ -z "${VERSION_2}" ]]; then echo "Missing version argument!" - echo "Usage: ${0} [VERSION_1] [VERSION_2] [SUBNET_EVM_VERSION]" >> /dev/stderr + echo "Usage: ${0} [VERSION_1] [VERSION_2] [EVM_VERSION]" >> /dev/stderr exit 255 fi - SUBNET_EVM_VERSION=$3 - if [[ -z "${SUBNET_EVM_VERSION}" ]]; then + EVM_VERSION=$3 + if [[ -z "${EVM_VERSION}" ]]; then echo "Missing version argument!" - echo "Usage: ${0} [VERSION_1] [VERSION_2] [SUBNET_EVM_VERSION]" >> /dev/stderr + echo "Usage: ${0} [VERSION_1] [VERSION_2] [EVM_VERSION]" >> /dev/stderr exit 255 fi fi @@ -41,7 +41,7 @@ fi echo "Running e2e tests with:" echo VERSION_1: ${VERSION_1} echo VERSION_2: ${VERSION_2} -echo SUBNET_EVM_VERSION: ${SUBNET_EVM_VERSION} +echo EVM_VERSION: ${EVM_VERSION} if [ ! -f /tmp/node-v${VERSION_1}/node ] then @@ -115,7 +115,7 @@ then find /tmp/node-v${VERSION_2} fi -if [ ! -f /tmp/evm-v${SUBNET_EVM_VERSION}/evm ] +if [ ! -f /tmp/evm-v${EVM_VERSION}/evm ] then ############################ # download evm plugin @@ -123,22 +123,22 @@ then GOARCH=$(go env GOARCH) GOOS=$(go env GOOS) # evm releases are raw binaries named evm-plugin-{os}-{arch} - DOWNLOAD_URL=https://github.com/luxfi/evm/releases/download/v${SUBNET_EVM_VERSION}/evm-plugin-linux-${GOARCH} + DOWNLOAD_URL=https://github.com/luxfi/evm/releases/download/v${EVM_VERSION}/evm-plugin-linux-${GOARCH} if [[ ${GOOS} == "darwin" ]]; then - DOWNLOAD_URL=https://github.com/luxfi/evm/releases/download/v${SUBNET_EVM_VERSION}/evm-plugin-darwin-${GOARCH} + DOWNLOAD_URL=https://github.com/luxfi/evm/releases/download/v${EVM_VERSION}/evm-plugin-darwin-${GOARCH} fi - rm -rf /tmp/evm-v${SUBNET_EVM_VERSION} - mkdir -p /tmp/evm-v${SUBNET_EVM_VERSION} + rm -rf /tmp/evm-v${EVM_VERSION} + mkdir -p /tmp/evm-v${EVM_VERSION} - echo "downloading evm ${SUBNET_EVM_VERSION} at ${DOWNLOAD_URL}" - curl -L ${DOWNLOAD_URL} -o /tmp/evm-v${SUBNET_EVM_VERSION}/evm - chmod +x /tmp/evm-v${SUBNET_EVM_VERSION}/evm + echo "downloading evm ${EVM_VERSION} at ${DOWNLOAD_URL}" + curl -L ${DOWNLOAD_URL} -o /tmp/evm-v${EVM_VERSION}/evm + chmod +x /tmp/evm-v${EVM_VERSION}/evm # NOTE: We are copying the evm binary here to a plugin hardcoded as srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy which corresponds to the VM name `subnetevm` used as such in the test mkdir -p /tmp/node-v${VERSION_1}/plugins/ - cp /tmp/evm-v${SUBNET_EVM_VERSION}/evm /tmp/node-v${VERSION_1}/plugins/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy - find /tmp/evm-v${SUBNET_EVM_VERSION}/evm + cp /tmp/evm-v${EVM_VERSION}/evm /tmp/node-v${VERSION_1}/plugins/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy + find /tmp/evm-v${EVM_VERSION}/evm fi ############################ echo "building runner" @@ -187,4 +187,4 @@ echo "running e2e tests" --grpc-gateway-endpoint="0.0.0.0:8081" \ --node-path-1=/tmp/node-v${VERSION_1}/node \ --node-path-2=/tmp/node-v${VERSION_2}/node \ ---subnet-evm-path=/tmp/evm-v${SUBNET_EVM_VERSION}/evm +--evm-path=/tmp/evm-v${EVM_VERSION}/evm diff --git a/testkeys b/testkeys new file mode 100755 index 00000000..040951e8 Binary files /dev/null and b/testkeys differ diff --git a/tests/e2e/e2e_test.go.skip b/tests/e2e/e2e_test.go.skip deleted file mode 100644 index 6ebf3589..00000000 --- a/tests/e2e/e2e_test.go.skip +++ /dev/null @@ -1,1128 +0,0 @@ -// Copyright (C) 2021-2025, Lux Industries Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -// e2e implements the e2e tests. -package e2e_test - -import ( - "context" - "flag" - "os" - "path/filepath" - "sort" - "strings" - "testing" - "time" - - "github.com/luxfi/sdk/node/api/admin" - "github.com/luxfi/p2p/message" - luxd_constants "github.com/luxfi/utils/constants" - "github.com/luxfi/sdk/node/vms/platformvm" - "golang.org/x/exp/maps" - - "github.com/luxfi/netrunner/client" - "github.com/luxfi/netrunner/rpcpb" - "github.com/luxfi/netrunner/server" - "github.com/luxfi/netrunner/utils" - "github.com/luxfi/netrunner/utils/constants" - "github.com/luxfi/netrunner/ux" - "github.com/luxfi/ids" - luxlog "github.com/luxfi/log" - "github.com/luxfi/metric" - ginkgo "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" -) - -func TestE2e(t *testing.T) { - if os.Getenv("RUN_E2E") == "" { - t.Skip("Environment variable RUN_E2E not set; skipping E2E tests") - } - gomega.RegisterFailHandler(ginkgo.Fail) - ginkgo.RunSpecs(t, "network-runner-example e2e test suites") -} - -const clientRootDirPrefix = "client" - -var ( - logLevel string - logDir string - gRPCEp string - gRPCGatewayEp string - execPath1 string - execPath2 string - subnetEvmPath string - genesisPath string - genesisContents string - - newNodeName = "test-add-node" - newNodeName2 = "test-add-node2" - newNode2NodeID = "" - pausedNodeURI = "" - pausedNodeName = "node1" - createdSubnetID = "" - elasticAssetID = "" - newSubnetID = "" - customNodeConfigs = map[string]string{ - "node1": `{"api-admin-enabled":true}`, - "node2": `{"api-admin-enabled":true}`, - "node3": `{"api-admin-enabled":true}`, - "node4": `{"api-admin-enabled":false}`, - "node5": `{"api-admin-enabled":false}`, - "node6": `{"api-admin-enabled":false}`, - "node7": `{"api-admin-enabled":false}`, - } - numNodes = uint32(5) - subnetParticipants = []string{"node1", "node2", "node3"} - newParticipantNode = "new_participant_node" - subnetParticipants2 = []string{"node1", "node2", newParticipantNode} - existingNodes = []string{"node1", "node2", "node3", "node4", "node5"} - disjointNewSubnetParticipants = [][]string{ - {"new_node1", "new_node2"}, - {"new_node3", "new_node4"}, - } - testElasticSubnetConfig = rpcpb.ElasticSubnetSpec{ - SubnetId: "", - AssetName: "BLIZZARD", - AssetSymbol: "BRRR", - InitialSupply: 240000000, - MaxSupply: 720000000, - MinConsumptionRate: 100000, - MaxConsumptionRate: 120000, - MinValidatorStake: 2000, - MaxValidatorStake: 3000000, - MinStakeDuration: 14 * 24, - MaxStakeDuration: 365 * 24, - MinDelegationFee: 20000, - MinDelegatorStake: 25, - MaxValidatorWeightFactor: 5, - UptimeRequirement: 0.8 * 1_000_000, - } - - testValidatorConfig = rpcpb.PermissionlessValidatorSpec{ - StakedTokenAmount: 2000, - StartTime: time.Now().Add(1 * time.Hour).UTC().Format(server.TimeParseLayout), - StakeDuration: 336, - } -) - -func init() { - flag.StringVar( - &logLevel, - "log-level", - logging.Info.String(), - "log level", - ) - flag.StringVar( - &logDir, - "log-dir", - "", - "log directory", - ) - flag.StringVar( - &gRPCEp, - "grpc-endpoint", - "0.0.0.0:8080", - "gRPC server endpoint", - ) - flag.StringVar( - &gRPCGatewayEp, - "grpc-gateway-endpoint", - "0.0.0.0:8081", - "gRPC gateway endpoint", - ) - flag.StringVar( - &execPath1, - "node-path-1", - "", - "node executable path (to upgrade from)", - ) - flag.StringVar( - &execPath2, - "node-path-2", - "", - "node executable path (to upgrade to)", - ) - flag.StringVar( - &subnetEvmPath, - "subnet-evm-path", - "", - "path to subnet-evm binary", - ) -} - -var ( - cli client.Client - log luxlog.Logger -) - -var _ = ginkgo.BeforeSuite(func() { - var err error - if logDir == "" { - anrRootDir := filepath.Join(os.TempDir(), constants.RootDirPrefix) - err = os.MkdirAll(anrRootDir, os.ModePerm) - gomega.Ω(err).Should(gomega.BeNil()) - clientRootDir := filepath.Join(anrRootDir, clientRootDirPrefix) - logDir, err = utils.MkDirWithTimestamp(clientRootDir) - gomega.Ω(err).Should(gomega.BeNil()) - } - lvl, err := logging.ToLevel(logLevel) - gomega.Ω(err).Should(gomega.BeNil()) - logFactory := logging.NewFactory(logging.Config{ - RotatingWriterConfig: logging.RotatingWriterConfig{ - Directory: logDir, - }, - DisplayLevel: lvl, - LogLevel: lvl, - }) - log, err = logFactory.Make(constants.LogNameTest) - gomega.Ω(err).Should(gomega.BeNil()) - - cli, err = client.New(client.Config{ - Endpoint: gRPCEp, - DialTimeout: 10 * time.Second, - }, log) - gomega.Ω(err).Should(gomega.BeNil()) - - genesisPath = "tests/e2e/subnet-evm-genesis.json" - genesisByteContents, err := os.ReadFile(genesisPath) - gomega.Ω(err).Should(gomega.BeNil()) - genesisContents = string(genesisByteContents) -}) - -var _ = ginkgo.AfterSuite(func() { - ux.Print(log, logging.Red.Wrap("shutting down cluster")) - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - _, err := cli.Stop(ctx) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - - ux.Print(log, logging.Red.Wrap("shutting down client")) - err = cli.Close() - gomega.Ω(err).Should(gomega.BeNil()) -}) - -var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() { - ginkgo.It("can create blockhains", func() { - existingSubnetID := "" - createdBlockchainID := "" - createdBlockchainID2 := "" - ginkgo.By("start with blockchain specs", func() { - ux.Print(log, logging.Green.Wrap("sending 'start' with the valid binary path: %s"), execPath1) - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - resp, err := cli.Start(ctx, execPath1, - client.WithPluginDir(filepath.Join(filepath.Dir(execPath1), "plugins")), - client.WithBlockchainSpecs([]*rpcpb.BlockchainSpec{ - { - VmName: "subnetevm", - Genesis: genesisPath, // test genesis path usage - }, - }), - ) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(len(resp.ChainIds)).Should(gomega.Equal(1)) - createdBlockchainID = resp.ChainIds[0] - ux.Print(log, logging.Green.Wrap("successfully started, node-names: %s"), resp.ClusterInfo.NodeNames) - }) - - ginkgo.By("can create a blockchain with a new subnet id", func() { - ux.Print(log, logging.Blue.Wrap("can create a blockchain in a new subnet")) - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - resp, err := cli.CreateBlockchains(ctx, - []*rpcpb.BlockchainSpec{ - { - VmName: "subnetevm", - Genesis: genesisPath, // test genesis path usage - }, - }, - ) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(len(resp.ChainIds)).Should(gomega.Equal(1)) - }) - - ginkgo.By("get subnet ID", func() { - cctx, ccancel := context.WithTimeout(context.Background(), 15*time.Second) - status, err := cli.Status(cctx) - ccancel() - gomega.Ω(err).Should(gomega.BeNil()) - customChains := status.ClusterInfo.GetCustomChains() - _, ok := customChains[createdBlockchainID] - gomega.Ω(ok).Should(gomega.Equal(true)) - existingSubnetID = customChains[createdBlockchainID].SubnetId - gomega.Ω(existingSubnetID).Should(gomega.Not(gomega.BeNil())) - }) - - ginkgo.By("verify the subnet also has all existing nodes as participants", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - defer cancel() - status, err := cli.Status(ctx) - gomega.Ω(err).Should(gomega.BeNil()) - subnetIDs := maps.Keys(status.ClusterInfo.Subnets) - sort.Strings(subnetIDs) - createdSubnetIDString := subnetIDs[0] - subnetHasCorrectParticipants := utils.VerifySubnetHasCorrectParticipants(log, existingNodes, status.ClusterInfo, createdSubnetIDString) - gomega.Ω(subnetHasCorrectParticipants).Should(gomega.Equal(true)) - }) - - ginkgo.By("can create a blockchain with an existing subnet id", func() { - ux.Print(log, logging.Blue.Wrap("can create a blockchain in an existing subnet")) - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - resp, err := cli.CreateBlockchains(ctx, - []*rpcpb.BlockchainSpec{ - { - VmName: "subnetevm", - Genesis: genesisContents, - SubnetId: &existingSubnetID, - }, - }, - ) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(len(resp.ChainIds)).Should(gomega.Equal(1)) - }) - - ginkgo.By("can save snapshot", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - _, err := cli.SaveSnapshot(ctx, "test") - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - }) - - ginkgo.By("can load snapshot", func() { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - _, err := cli.LoadSnapshot(ctx, "test") - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - }) - - // need to remove the snapshot otherwise it fails later in the 2nd part of snapshot tests - // (testing for no snapshots) - ginkgo.By("can remove snapshot", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - _, err := cli.RemoveSnapshot(ctx, "test") - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - }) - - ginkgo.By("can create a blockchain with an existing subnet id loaded from snapshot", func() { - ux.Print(log, logging.Blue.Wrap("can create a blockchain in an existing subnet")) - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - _, err := cli.CreateBlockchains(ctx, - []*rpcpb.BlockchainSpec{ - { - VmName: "subnetevm", - Genesis: genesisContents, - SubnetId: &existingSubnetID, - }, - }, - ) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - }) - - ginkgo.By("can create a blockchain with new subnet id with some of existing participating nodes", func() { - ux.Print(log, logging.Blue.Wrap("can create a blockchain with new subnet id with some of existing participating nodes")) - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - resp, err := cli.CreateBlockchains(ctx, - []*rpcpb.BlockchainSpec{ - { - VmName: "subnetevm", - Genesis: genesisContents, - SubnetSpec: &rpcpb.SubnetSpec{Participants: subnetParticipants}, - }, - }, - ) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(len(resp.ChainIds)).Should(gomega.Equal(1)) - createdBlockchainID = resp.ChainIds[0] - }) - - ginkgo.By("verify subnet has correct participants", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - defer cancel() - - status, err := cli.Status(ctx) - gomega.Ω(err).Should(gomega.BeNil()) - customChains := status.ClusterInfo.GetCustomChains() - createdSubnetIDString := customChains[createdBlockchainID].SubnetId - subnetHasCorrectParticipants := utils.VerifySubnetHasCorrectParticipants(log, subnetParticipants, status.ClusterInfo, createdSubnetIDString) - gomega.Ω(subnetHasCorrectParticipants).Should(gomega.Equal(true)) - // verify that no new nodes is added to cluster - gomega.Ω(len(status.ClusterInfo.NodeNames)).Should(gomega.Equal(5)) - }) - - ginkgo.By("can create a blockchain with new subnet id with some of existing participating nodes and a new node", func() { - ux.Print(log, logging.Blue.Wrap("can create a blockchain new subnet id with some of existing participating nodes and a new node")) - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - resp, err := cli.CreateBlockchains(ctx, - []*rpcpb.BlockchainSpec{ - { - VmName: "subnetevm", - Genesis: genesisContents, - SubnetSpec: &rpcpb.SubnetSpec{Participants: subnetParticipants2}, - }, - }, - ) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - // verify that a new node is added to cluster - gomega.Ω(len(resp.ClusterInfo.NodeNames)).Should(gomega.Equal(6)) - _, ok := resp.ClusterInfo.NodeInfos[newParticipantNode] - gomega.Ω(ok).Should(gomega.Equal(true)) - gomega.Ω(len(resp.ChainIds)).Should(gomega.Equal(1)) - createdBlockchainID = resp.ChainIds[0] - }) - - ginkgo.By("verify the newer subnet also has correct participants", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - defer cancel() - status, err := cli.Status(ctx) - gomega.Ω(err).Should(gomega.BeNil()) - customChains := status.ClusterInfo.GetCustomChains() - createdSubnetIDString := customChains[createdBlockchainID].SubnetId - subnetHasCorrectParticipants := utils.VerifySubnetHasCorrectParticipants(log, subnetParticipants2, status.ClusterInfo, createdSubnetIDString) - gomega.Ω(subnetHasCorrectParticipants).Should(gomega.Equal(true)) - }) - - ginkgo.By("can create two blockchains at a time", func() { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - resp, err := cli.CreateBlockchains(ctx, - []*rpcpb.BlockchainSpec{ - { - VmName: "subnetevm", - Genesis: genesisContents, - SubnetId: &existingSubnetID, - }, - { - VmName: "subnetevm", - Genesis: genesisContents, - SubnetId: &existingSubnetID, - }, - }, - ) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(len(resp.ChainIds)).Should(gomega.Equal(2)) - }) - - ginkgo.By("can create two blockchains in two new disjoint subnets with bls validators", func() { - // get prev status - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - prevStatus, err := cli.Status(ctx) - gomega.Ω(err).Should(gomega.BeNil()) - cancel() - // create blockchains - ctx, cancel = context.WithTimeout(context.Background(), 3*time.Minute) - resp, err := cli.CreateBlockchains(ctx, - []*rpcpb.BlockchainSpec{ - { - VmName: "subnetevm", - Genesis: genesisContents, - SubnetSpec: &rpcpb.SubnetSpec{Participants: disjointNewSubnetParticipants[0]}, - }, - { - VmName: "subnetevm", - Genesis: genesisContents, - SubnetSpec: &rpcpb.SubnetSpec{Participants: disjointNewSubnetParticipants[1]}, - }, - }, - ) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - // check new nodes - allNewParticipants := append(disjointNewSubnetParticipants[0], disjointNewSubnetParticipants[1]...) - expectedLen := len(prevStatus.ClusterInfo.NodeNames) + len(allNewParticipants) - gomega.Ω(len(resp.ClusterInfo.NodeNames)).Should(gomega.Equal(expectedLen)) - for _, nodeName := range allNewParticipants { - _, ok := resp.ClusterInfo.NodeInfos[nodeName] - gomega.Ω(ok).Should(gomega.Equal(true)) - } - gomega.Ω(len(resp.ChainIds)).Should(gomega.Equal(2)) - createdBlockchainID = resp.ChainIds[0] - createdBlockchainID2 = resp.ChainIds[1] - }) - - ginkgo.By("verify the new subnets also has correct participants", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - defer cancel() - status, err := cli.Status(ctx) - gomega.Ω(err).Should(gomega.BeNil()) - customChains := status.ClusterInfo.GetCustomChains() - createdSubnetIDString := customChains[createdBlockchainID].SubnetId - subnetHasCorrectParticipants := utils.VerifySubnetHasCorrectParticipants(log, disjointNewSubnetParticipants[0], status.ClusterInfo, createdSubnetIDString) - gomega.Ω(subnetHasCorrectParticipants).Should(gomega.Equal(true)) - createdSubnetID2String := customChains[createdBlockchainID2].SubnetId - subnet2HasCorrectParticipants := utils.VerifySubnetHasCorrectParticipants(log, disjointNewSubnetParticipants[1], status.ClusterInfo, createdSubnetID2String) - gomega.Ω(subnet2HasCorrectParticipants).Should(gomega.Equal(true)) - }) - - ginkgo.By("verify that new validators have BLS Keys", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - clientURIs, err := cli.URIs(ctx) - gomega.Ω(err).Should(gomega.BeNil()) - var clientURI string - for _, uri := range clientURIs { - clientURI = uri - break - } - platformCli := platformvm.NewClient(clientURI) - vdrs, err := platformCli.GetCurrentValidators(ctx, ids.Empty, nil) - gomega.Ω(err).Should(gomega.BeNil()) - status, err := cli.Status(ctx) - gomega.Ω(err).Should(gomega.BeNil()) - allNewParticipants := append(disjointNewSubnetParticipants[0], disjointNewSubnetParticipants[1]...) - for _, nodeName := range allNewParticipants { - nodeInfo, ok := status.ClusterInfo.NodeInfos[nodeName] - gomega.Ω(ok).Should(gomega.Equal(true)) - found := false - for _, v := range vdrs { - if v.NodeID.String() == nodeInfo.Id { - gomega.Ω(v.Signer).Should(gomega.Not(gomega.BeNil())) - found = true - } - } - gomega.Ω(found).Should(gomega.Equal(true)) - } - cancel() - }) - - ginkgo.By("stop the network", func() { - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - _, err := cli.Stop(ctx) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - }) - }) - - ginkgo.It("can start", func() { - ginkgo.By("start request with invalid exec path should fail", func() { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - _, err := cli.Start(ctx, "") - cancel() - gomega.Ω(err.Error()).Should(gomega.ContainSubstring(utils.ErrInvalidExecPath.Error())) - }) - - ginkgo.By("start request with invalid exec path should fail", func() { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - _, err := cli.Start(ctx, "invalid") - cancel() - gomega.Ω(err.Error()).Should(gomega.ContainSubstring(utils.ErrNotExists.Error())) - }) - - ginkgo.By("start request with invalid custom VM path should fail", func() { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - _, err := cli.Start(ctx, execPath1, - client.WithPluginDir(os.TempDir()), - client.WithBlockchainSpecs([]*rpcpb.BlockchainSpec{ - { - VmName: "invalid", - }, - }), - ) - cancel() - gomega.Ω(err.Error()).Should(gomega.ContainSubstring(utils.ErrNotExistsPlugin.Error())) - }) - - ginkgo.By("start request with invalid custom VM name format should fail", func() { - f, err := os.CreateTemp(os.TempDir(), strings.Repeat("a", 33)) - gomega.Ω(err).Should(gomega.BeNil()) - filePath := f.Name() - gomega.Ω(f.Close()).Should(gomega.BeNil()) - - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - _, err = cli.Start(ctx, execPath1, - client.WithPluginDir(filepath.Dir(filePath)), - client.WithBlockchainSpecs([]*rpcpb.BlockchainSpec{ - { - VmName: filepath.Base(filePath), - }, - }), - ) - cancel() - gomega.Ω(err.Error()).Should(gomega.ContainSubstring(server.ErrInvalidVMName.Error())) - - os.RemoveAll(filePath) - }) - - ginkgo.By("calling start API with the valid binary path", func() { - ux.Print(log, logging.Green.Wrap("sending 'start' with the valid binary path: %s"), execPath1) - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - resp, err := cli.Start(ctx, execPath1) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - ux.Print(log, logging.Green.Wrap("successfully started, node-names: %s"), resp.ClusterInfo.NodeNames) - }) - }) - - ginkgo.It("can wait for health", func() { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - _, err := cli.Health(ctx) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - }) - - ginkgo.It("can get URIs", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - uris, err := cli.URIs(ctx) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - ux.Print(log, logging.Blue.Wrap("URIs: %s"), uris) - }) - - ginkgo.It("can fetch status", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - resp, err := cli.Status(ctx) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - // get paused node URI for pause node test later - _, ok := resp.ClusterInfo.NodeInfos[pausedNodeName] - gomega.Ω(ok).Should(gomega.Equal(true)) - pausedNodeURI = resp.ClusterInfo.NodeInfos[pausedNodeName].Uri - }) - - ginkgo.It("can poll status", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - defer cancel() - ch, err := cli.StreamStatus(ctx, 5*time.Second) - gomega.Ω(err).Should(gomega.BeNil()) - for info := range ch { - ux.Print(log, logging.Green.Wrap("fetched info, node-names: %s"), info.NodeNames) - if info.Healthy { - break - } - } - }) - - ginkgo.It("can remove", func() { - ginkgo.By("calling remove API with the first binary", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - resp, err := cli.RemoveNode(ctx, "node5") - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - ux.Print(log, logging.Green.Wrap("successfully removed, node-names: %s"), resp.ClusterInfo.NodeNames) - }) - }) - - ginkgo.It("can restart", func() { - ginkgo.By("calling restart API with the second binary", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - resp, err := cli.RestartNode(ctx, "node4", client.WithExecPath(execPath2)) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - ux.Print(log, logging.Green.Wrap("successfully restarted, node-names: %s"), resp.ClusterInfo.NodeNames) - }) - }) - - ginkgo.It("can attach a peer", func() { - ginkgo.By("calling attach peer API", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - resp, err := cli.AttachPeer(ctx, "node1") - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - - v, ok := resp.ClusterInfo.AttachedPeerInfos["node1"] - gomega.Ω(ok).Should(gomega.BeTrue()) - ux.Print(log, logging.Green.Wrap("successfully attached peer, peers: %+v"), v.Peers) - - mc, err := message.NewCreator( - logging.NoLog{}, - metrics.NewRegistry(), - "", - luxd_constants.DefaultNetworkCompressionType, - 10*time.Second, - ) - gomega.Ω(err).Should(gomega.BeNil()) - - containerIDs := []ids.ID{ - ids.GenerateTestID(), - ids.GenerateTestID(), - ids.GenerateTestID(), - } - requestID := uint32(42) - chainID := luxd_constants.PlatformChainID - msg, err := mc.Chits(chainID, requestID, []ids.ID{}, containerIDs) - gomega.Ω(err).Should(gomega.BeNil()) - - ctx, cancel = context.WithTimeout(context.Background(), 15*time.Second) - sresp, err := cli.SendOutboundMessage(ctx, "node1", v.Peers[0].Id, uint32(msg.Op()), msg.Bytes()) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(sresp.Sent).Should(gomega.BeTrue()) - }) - }) - - ginkgo.It("can add a node", func() { - ginkgo.By("calling AddNode", func() { - ux.Print(log, logging.Green.Wrap("calling 'add-node' with the valid binary path: %s"), execPath1) - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - resp, err := cli.AddNode(ctx, newNodeName, execPath1) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - ux.Print(log, logging.Green.Wrap("successfully started, node-names: %s"), resp.ClusterInfo.NodeNames) - }) - - ginkgo.By("calling AddNode with existing node name, should fail", func() { - ux.Print(log, logging.Green.Wrap("calling 'add-node' with the valid binary path: %s"), execPath1) - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - resp, err := cli.AddNode(ctx, newNodeName, execPath1) - cancel() - gomega.Ω(err.Error()).Should(gomega.ContainSubstring("repeated node name")) - gomega.Ω(resp).Should(gomega.BeNil()) - ux.Print(log, logging.Green.Wrap("'add-node' failed as expected")) - }) - }) - - ginkgo.It("can start with custom config", func() { - ginkgo.By("stopping network first", func() { - ux.Print(log, logging.Red.Wrap("shutting down cluster")) - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - _, err := cli.Stop(ctx) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - - ux.Print(log, logging.Red.Wrap("shutting down client")) - gomega.Ω(err).Should(gomega.BeNil()) - }) - ginkgo.By("calling start API with custom config", func() { - ux.Print(log, logging.Green.Wrap("sending 'start' with the valid binary path: %s"), execPath1) - opts := []client.OpOption{ - client.WithNumNodes(numNodes), - client.WithCustomNodeConfigs(customNodeConfigs), - } - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - resp, err := cli.Start(ctx, execPath1, opts...) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - ux.Print(log, logging.Green.Wrap("successfully started, node-names: %s"), resp.ClusterInfo.NodeNames) - }) - ginkgo.By("can wait for health", func() { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - _, err := cli.Health(ctx) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - }) - ginkgo.By("overrides num-nodes", func() { - ux.Print(log, logging.Green.Wrap("checking that given num-nodes %d have been overridden by custom configs: %d"), numNodes, len(customNodeConfigs)) - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - uris, err := cli.URIs(ctx) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(uris).Should(gomega.HaveLen(len(customNodeConfigs))) - ux.Print(log, logging.Green.Wrap("expected number of nodes up: %d"), len(customNodeConfigs)) - - ux.Print(log, logging.Green.Wrap("checking correct admin APIs are enabled resp. disabled")) - // we have 7 nodes, 3 have the admin API enabled, the other 4 disabled - // therefore we expect exactly 4 calls to fail and exactly 3 to succeed. - ctx, cancel = context.WithTimeout(context.Background(), 15*time.Second) - errCnt := 0 - for i := 0; i < len(uris); i++ { - cli := admin.NewClient(uris[i]) - err := cli.LockProfile(ctx) - if err != nil { - errCnt++ - } - } - cancel() - gomega.Ω(errCnt).Should(gomega.Equal(4)) - }) - }) - - ginkgo.It("start with default network, for subsequent steps", func() { - ginkgo.By("stopping network first", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - _, err := cli.Stop(ctx) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - }) - ginkgo.By("starting", func() { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - _, err := cli.Start(ctx, execPath1) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - }) - ginkgo.By("wait for health", func() { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - _, err := cli.Health(ctx) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - }) - }) - ginkgo.It("can pause a node", func() { - ginkgo.By("calling PauseNode", func() { - ux.Print(log, logging.Green.Wrap("calling 'pause-node'")) - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - resp, err := cli.PauseNode(ctx, pausedNodeName) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - ux.Print(log, logging.Green.Wrap("successfully paused, node-names: %s"), resp.ClusterInfo.NodeNames) - }) - ginkgo.By("can wait for health", func() { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - _, err := cli.Health(ctx) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - }) - ginkgo.By("API Call using paused node URI will fail", func() { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - defer cancel() - platformCli := platformvm.NewClient(pausedNodeURI) - _, err := platformCli.GetCurrentValidators(ctx, ids.Empty, nil) - gomega.Ω(err).Should(gomega.HaveOccurred()) - }) - ginkgo.By("calling resumeNode API", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - resp, err := cli.ResumeNode(ctx, pausedNodeName) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - ux.Print(log, logging.Green.Wrap("successfully resumed %s, cluster node-names: %s"), "node1", resp.ClusterInfo.NodeNames) - }) - ginkgo.By("can wait for health", func() { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - _, err := cli.Health(ctx) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - }) - ginkgo.By("API Call using resumed node URI", func() { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - defer cancel() - platformCli := platformvm.NewClient(pausedNodeURI) - _, err := platformCli.GetCurrentValidators(ctx, ids.Empty, nil) - gomega.Ω(err).Should(gomega.BeNil()) - }) - }) - - ginkgo.It("can add primary validator with BLS Keys", func() { - ginkgo.By("calling AddNode", func() { - ux.Print(log, logging.Green.Wrap("calling 'add-node' with the valid binary path: %s"), execPath1) - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - resp, err := cli.AddNode(ctx, newNodeName2, execPath1) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - for nodeName, nodeInfo := range resp.ClusterInfo.NodeInfos { - if nodeName == newNodeName2 { - newNode2NodeID = nodeInfo.Id - } - } - ux.Print(log, logging.Green.Wrap("successfully started, node-names: %s"), resp.ClusterInfo.NodeNames) - }) - ginkgo.By("can wait for health", func() { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - _, err := cli.Health(ctx) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - }) - ginkgo.By("add 1 subnet", func() { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - resp, err := cli.CreateSubnets(ctx, []*rpcpb.SubnetSpec{{}}) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(len(resp.SubnetIds)).Should(gomega.Equal(1)) - }) - ginkgo.By("verify that new validator has BLS Keys", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - clientURIs, err := cli.URIs(ctx) - gomega.Ω(err).Should(gomega.BeNil()) - var clientURI string - for _, uri := range clientURIs { - clientURI = uri - break - } - platformCli := platformvm.NewClient(clientURI) - vdrs, err := platformCli.GetCurrentValidators(ctx, ids.Empty, nil) - - gomega.Ω(err).Should(gomega.BeNil()) - for _, v := range vdrs { - if v.NodeID.String() == newNode2NodeID { - gomega.Ω(v.Signer).Should(gomega.Not(gomega.BeNil())) - } - } - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - }) - }) - - ginkgo.It("subnet creation", func() { - ginkgo.By("add 1 subnet", func() { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - _, err := cli.CreateSubnets(ctx, []*rpcpb.SubnetSpec{{}}) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - }) - ginkgo.By("check subnet number is 2", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - status, err := cli.Status(ctx) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - numSubnets := len(status.ClusterInfo.Subnets) - gomega.Ω(numSubnets).Should(gomega.Equal(2)) - }) - ginkgo.By("add 1 subnet with participants", func() { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - response, err := cli.CreateSubnets(ctx, []*rpcpb.SubnetSpec{{Participants: subnetParticipants}}) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(len(response.SubnetIds)).Should(gomega.Equal(1)) - newSubnetID = response.SubnetIds[0] - }) - ginkgo.By("verify subnet has correct participants", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - defer cancel() - status, err := cli.Status(ctx) - gomega.Ω(err).Should(gomega.BeNil()) - subnetHasCorrectParticipants := utils.VerifySubnetHasCorrectParticipants(log, subnetParticipants, status.ClusterInfo, newSubnetID) - gomega.Ω(subnetHasCorrectParticipants).Should(gomega.Equal(true)) - }) - ginkgo.By("add 1 subnet with node not currently added", func() { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - response, err := cli.CreateSubnets(ctx, []*rpcpb.SubnetSpec{{Participants: subnetParticipants2}}) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - // verify that a new node is added to cluster - gomega.Ω(len(response.ClusterInfo.NodeNames)).Should(gomega.Equal(7)) - _, ok := response.ClusterInfo.NodeInfos[newParticipantNode] - gomega.Ω(ok).Should(gomega.Equal(true)) - gomega.Ω(len(response.SubnetIds)).Should(gomega.Equal(1)) - newSubnetID = response.SubnetIds[0] - }) - ginkgo.By("calling AddNode with existing node name, should fail", func() { - ux.Print(log, logging.Green.Wrap("calling 'add-node' with the valid binary path: %s"), execPath1) - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - resp, err := cli.AddNode(ctx, newParticipantNode, execPath1) - cancel() - gomega.Ω(err.Error()).Should(gomega.ContainSubstring("repeated node name")) - gomega.Ω(resp).Should(gomega.BeNil()) - ux.Print(log, logging.Green.Wrap("'add-node' failed as expected")) - }) - ginkgo.By("verify the newer subnet also has correct participants", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - defer cancel() - status, err := cli.Status(ctx) - gomega.Ω(err).Should(gomega.BeNil()) - subnetHasCorrectParticipants := utils.VerifySubnetHasCorrectParticipants(log, subnetParticipants2, status.ClusterInfo, newSubnetID) - gomega.Ω(subnetHasCorrectParticipants).Should(gomega.Equal(true)) - }) - }) - - ginkgo.It("can remove subnet validator", func() { - ginkgo.By("removing a subnet validator", func() { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - defer cancel() - testRemoveSubnetValidatorConfig := rpcpb.RemoveSubnetValidatorSpec{ - NodeNames: []string{"node2"}, - SubnetId: newSubnetID, - } - _, err := cli.RemoveSubnetValidator(ctx, []*rpcpb.RemoveSubnetValidatorSpec{&testRemoveSubnetValidatorConfig}) - gomega.Ω(err).Should(gomega.BeNil()) - }) - ginkgo.By("verify there are only two validators left for subnet", func() { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - defer cancel() - clientURIs, err := cli.URIs(ctx) - gomega.Ω(err).Should(gomega.BeNil()) - var clientURI string - for _, uri := range clientURIs { - clientURI = uri - break - } - subnetID, err := ids.FromString(newSubnetID) - gomega.Ω(err).Should(gomega.BeNil()) - platformCli := platformvm.NewClient(clientURI) - vdrs, err := platformCli.GetCurrentValidators(ctx, subnetID, nil) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(len(vdrs)).Should(gomega.Equal(2)) - }) - }) - - ginkgo.It("transform subnet to elastic subnets", func() { - var elasticSubnetID string - ginkgo.By("add 1 subnet", func() { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - resp, err := cli.CreateSubnets(ctx, []*rpcpb.SubnetSpec{{}}) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(len(resp.SubnetIds)).Should(gomega.Equal(1)) - gomega.Ω(len(resp.ClusterInfo.Subnets)).Should(gomega.Equal(5)) - createdSubnetID = resp.SubnetIds[0] - }) - ginkgo.By("check expected non elastic status", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - status, err := cli.Status(ctx) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - subnetInfo := status.ClusterInfo.Subnets[createdSubnetID] - gomega.Ω(subnetInfo.IsElastic).Should(gomega.Equal(false)) - gomega.Ω(subnetInfo.ElasticSubnetId).Should(gomega.Equal(ids.Empty.String())) - }) - ginkgo.By("transform 1 subnet to elastic subnet", func() { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - defer cancel() - testElasticSubnetConfig.SubnetId = createdSubnetID - response, err := cli.TransformElasticSubnets(ctx, []*rpcpb.ElasticSubnetSpec{&testElasticSubnetConfig}) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(len(response.TxIds)).Should(gomega.Equal(1)) - gomega.Ω(len(response.AssetIds)).Should(gomega.Equal(1)) - elasticAssetID = response.AssetIds[0] - }) - ginkgo.By("check expected elastic status", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - status, err := cli.Status(ctx) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - subnetInfo := status.ClusterInfo.Subnets[createdSubnetID] - gomega.Ω(subnetInfo.IsElastic).Should(gomega.Equal(true)) - gomega.Ω(subnetInfo.ElasticSubnetId).ShouldNot(gomega.Equal(ids.Empty.String())) - elasticSubnetID = subnetInfo.ElasticSubnetId - }) - ginkgo.By("transforming a subnet with same subnetID to elastic subnet will fail", func() { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - defer cancel() - testElasticSubnetConfig.SubnetId = createdSubnetID - _, err := cli.TransformElasticSubnets(ctx, []*rpcpb.ElasticSubnetSpec{&testElasticSubnetConfig}) - gomega.Ω(err).Should(gomega.HaveOccurred()) - }) - ginkgo.By("save snapshot with elastic info", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - _, err := cli.SaveSnapshot(ctx, "elastic_snapshot") - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - }) - ginkgo.By("load snapshot with elastic info", func() { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - _, err := cli.LoadSnapshot(ctx, "elastic_snapshot") - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - }) - ginkgo.By("check expected elastic status", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - status, err := cli.Status(ctx) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - subnetInfo := status.ClusterInfo.Subnets[createdSubnetID] - gomega.Ω(subnetInfo.IsElastic).Should(gomega.Equal(true)) - gomega.Ω(subnetInfo.ElasticSubnetId).Should(gomega.Equal(elasticSubnetID)) - }) - ginkgo.By("remove snapshot with elastic info", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - _, err := cli.RemoveSnapshot(ctx, "elastic_snapshot") - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - }) - }) - - ginkgo.It("add permissionless validator to elastic subnets", func() { - ginkgo.By("adding a permissionless validator to elastic subnet", func() { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - defer cancel() - testValidatorConfig.SubnetId = createdSubnetID - testValidatorConfig.AssetId = elasticAssetID - testValidatorConfig.NodeName = "permissionlessNode" - _, err := cli.AddPermissionlessValidator(ctx, []*rpcpb.PermissionlessValidatorSpec{&testValidatorConfig}) - gomega.Ω(err).Should(gomega.BeNil()) - }) - }) - - ginkgo.It("snapshots + blockchain creation", func() { - var originalUris []string - var originalSubnets []string - ginkgo.By("get original URIs", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - var err error - originalUris, err = cli.URIs(ctx) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(len(originalUris)).Should(gomega.Equal(8)) - }) - ginkgo.By("get original subnets", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - status, err := cli.Status(ctx) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - numSubnets := len(status.ClusterInfo.Subnets) - gomega.Ω(numSubnets).Should(gomega.Equal(5)) - originalSubnets = maps.Keys(status.ClusterInfo.Subnets) - }) - ginkgo.By("check there are no snapshots", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - snapshotNames, err := cli.GetSnapshotNames(ctx) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(snapshotNames).Should(gomega.Equal([]string(nil))) - }) - ginkgo.By("can save snapshot", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - _, err := cli.SaveSnapshot(ctx, "pepe") - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - }) - ginkgo.By("wait fail for stopped network", func() { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - _, err := cli.Health(ctx) - cancel() - gomega.Ω(err.Error()).Should(gomega.ContainSubstring(server.ErrNotBootstrapped.Error())) - }) - ginkgo.By("load fail for unknown snapshot", func() { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - _, err := cli.LoadSnapshot(ctx, "papa") - cancel() - gomega.Ω(err.Error()).Should(gomega.ContainSubstring("snapshot not found")) - }) - ginkgo.By("can load snapshot", func() { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - _, err := cli.LoadSnapshot(ctx, "pepe") - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - }) - ginkgo.By("check URIs", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - var err error - uris, err := cli.URIs(ctx) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(uris).Should(gomega.Equal(originalUris)) - }) - ginkgo.By("check subnets", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - status, err := cli.Status(ctx) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - subnetIDs := maps.Keys(status.ClusterInfo.Subnets) - sort.Strings(subnetIDs) - sort.Strings(originalSubnets) - gomega.Ω(subnetIDs).Should(gomega.Equal(originalSubnets)) - }) - ginkgo.By("save fail for already saved snapshot", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - _, err := cli.SaveSnapshot(ctx, "pepe") - cancel() - gomega.Ω(err.Error()).Should(gomega.ContainSubstring("snapshot \"pepe\" already exists")) - }) - ginkgo.By("check there is a snapshot", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - snapshotNames, err := cli.GetSnapshotNames(ctx) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(snapshotNames).Should(gomega.Equal([]string{"pepe"})) - }) - ginkgo.By("can remove snapshot", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - _, err := cli.RemoveSnapshot(ctx, "pepe") - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - }) - ginkgo.By("check there are no snapshots", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - snapshotNames, err := cli.GetSnapshotNames(ctx) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(snapshotNames).Should(gomega.Equal([]string(nil))) - }) - ginkgo.By("remove fail for unknown snapshot", func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - _, err := cli.RemoveSnapshot(ctx, "pepe") - cancel() - gomega.Ω(err.Error()).Should(gomega.ContainSubstring("snapshot not found")) - }) - }) -})