├── img └── peers.png ├── tests ├── rpc-apis │ ├── adminExists.js │ ├── test.sh │ ├── test-node.sh │ ├── genesis.json │ ├── README.md │ └── docker-compose.yaml ├── connectivity │ ├── test.sh │ ├── README.MD │ ├── genesis.json │ └── docker-compose.yaml └── README.md ├── Dockerfile.test ├── genesis.json ├── Dockerfile ├── LICENSE ├── testrunner.sh ├── docker-compose.yaml ├── run.sh └── README.md /img/peers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-chaingang/ethereal/HEAD/img/peers.png -------------------------------------------------------------------------------- /tests/rpc-apis/adminExists.js: -------------------------------------------------------------------------------- 1 | function adminExists() { 2 | try { 3 | admin 4 | } catch (e) { 5 | return false 6 | } 7 | 8 | return true 9 | } 10 | -------------------------------------------------------------------------------- /Dockerfile.test: -------------------------------------------------------------------------------- 1 | ARG ETHEREAL_TAG=v0.4-release-1.7 2 | FROM chaingang/ethereal:${ETHEREAL_TAG} 3 | 4 | RUN mkdir -p /opt/runner /opt/tests 5 | 6 | VOLUME /opt/tests 7 | 8 | WORKDIR /opt/runner 9 | 10 | COPY testrunner.sh . 11 | 12 | ENV TEST_SCRIPT=/opt/tests/test.sh 13 | ENV SIGNAL_DIR=/opt/shared 14 | 15 | ENTRYPOINT ["bash", "testrunner.sh"] 16 | CMD [] 17 | -------------------------------------------------------------------------------- /tests/rpc-apis/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Leave some time for HTTP endpoints to open 4 | sleep 1 5 | 6 | set -eux -o pipefail 7 | 8 | # Assert variables are defined (thanks to "set -u") 9 | RPC_URL_1=$RPC_URL_1 10 | RPC_URL_2=$RPC_URL_2 11 | 12 | bash /opt/tests/test-node.sh $RPC_URL_1 false 13 | bash /opt/tests/test-node.sh $RPC_URL_2 true 14 | 15 | exit 0 16 | -------------------------------------------------------------------------------- /tests/connectivity/test.sh: -------------------------------------------------------------------------------- 1 | NODES=( "node-1" "node-2" ) 2 | 3 | for NODE in ${NODES[@]} ; do 4 | NUM_PEERS=$(/opt/go-ethereum/build/bin/geth --verbosity 0 attach ipc:/opt/shared/$NODE/geth.ipc --exec "(function() {return admin.peers.length})()") 5 | echo "Node $NODE has $NUM_PEERS peers" 6 | if [ $NUM_PEERS -ne 1 ] ; then 7 | exit 1 8 | fi 9 | done 10 | 11 | exit 0 12 | -------------------------------------------------------------------------------- /tests/connectivity/README.MD: -------------------------------------------------------------------------------- 1 | # `connectivity` test 2 | 3 | The goal of this test is to bring up two fullnodes along with a bootnode, and test (and verify) that fullnodes are peers to each other. 4 | 5 | This proves that Ethereal framwork can be used to reliably bring multiple nodes that can network with each other as peers. 6 | 7 | # How to run 8 | `docker-compose up --build --exit-code-from test` 9 | 10 | ## Expected output 11 | `connectivity_test_1 exited with code 0` -------------------------------------------------------------------------------- /tests/rpc-apis/test-node.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Call this script as follows: 4 | # $ bash test.sh {true|false} 5 | 6 | set -e -o pipefail 7 | 8 | RPC_URL=$1 9 | EXISTENCE=$2 10 | 11 | DATA_DIR="/tmp/$NODE_ID" 12 | mkdir -p $DATA_DIR 13 | 14 | ADMIN_EXISTS=$(/opt/go-ethereum/build/bin/geth attach $RPC_URL --preload /opt/tests/adminExists.js --exec "adminExists()") 15 | 16 | if [ "$ADMIN_EXISTS" == "$EXISTENCE" ] ; then 17 | exit 0 18 | else 19 | exit 1 20 | fi 21 | -------------------------------------------------------------------------------- /genesis.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "chainId": 19543, 4 | "homesteadBlock": 0, 5 | "eip155Block": 0, 6 | "eip158Block": 0 7 | }, 8 | "alloc" : {}, 9 | "coinbase" : "0x0000000000000000000000000000000000000000", 10 | "difficulty" : "0x20000", 11 | "extraData" : "", 12 | "gasLimit" : "0x2fefd8", 13 | "nonce" : "0x0000000000000042", 14 | "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000", 15 | "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", 16 | "timestamp" : "0x00" 17 | } 18 | -------------------------------------------------------------------------------- /tests/rpc-apis/genesis.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "chainId": 19543, 4 | "homesteadBlock": 0, 5 | "eip155Block": 0, 6 | "eip158Block": 0 7 | }, 8 | "alloc" : {}, 9 | "coinbase" : "0x0000000000000000000000000000000000000000", 10 | "difficulty" : "0x20000", 11 | "extraData" : "", 12 | "gasLimit" : "0x2fefd8", 13 | "nonce" : "0x0000000000000042", 14 | "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000", 15 | "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", 16 | "timestamp" : "0x00" 17 | } 18 | -------------------------------------------------------------------------------- /tests/connectivity/genesis.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "chainId": 19543, 4 | "homesteadBlock": 0, 5 | "eip155Block": 0, 6 | "eip158Block": 0 7 | }, 8 | "alloc" : {}, 9 | "coinbase" : "0x0000000000000000000000000000000000000000", 10 | "difficulty" : "0x20000", 11 | "extraData" : "", 12 | "gasLimit" : "0x2fefd8", 13 | "nonce" : "0x0000000000000042", 14 | "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000", 15 | "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", 16 | "timestamp" : "0x00" 17 | } 18 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.8-jessie 2 | 3 | ARG CHECKOUT_TARGET=master 4 | 5 | # TARGET={bootnode|fullnode|miningnode} 6 | ENV TARGET=fullnode 7 | 8 | RUN mkdir -p /opt/go-ethereum 9 | 10 | RUN git clone https://github.com/ethereum/go-ethereum.git /opt/go-ethereum 11 | 12 | WORKDIR /opt/go-ethereum 13 | 14 | RUN git checkout $CHECKOUT_TARGET 15 | 16 | RUN make all 17 | 18 | RUN mkdir /opt/target 19 | 20 | WORKDIR /opt/target 21 | 22 | RUN mkdir /opt/shared 23 | VOLUME /opt/shared 24 | 25 | RUN mkdir /opt/genesis 26 | VOLUME /opt/genesis 27 | 28 | EXPOSE 8545 29 | EXPOSE 8546 30 | EXPOSE 30301 31 | EXPOSE 30303 32 | EXPOSE 30304 33 | EXPOSE 6060 34 | 35 | COPY run.sh . 36 | 37 | ENV NODE_NAME="" 38 | ENV GENESIS_FILE="genesis.json" 39 | ENV MINERTHREADS=1 40 | 41 | ENTRYPOINT ["bash", "run.sh"] 42 | CMD [] 43 | -------------------------------------------------------------------------------- /tests/rpc-apis/README.md: -------------------------------------------------------------------------------- 1 | # Configuring RPC APIs 2 | 3 | This test confirms that `ethereal` nodes can be configured independently of each other to expose 4 | different [Ethereum Management APIs](https://github.com/ethereum/go-ethereum/wiki/Management-APIs) 5 | over the JSON-RPC port. 6 | 7 | There are two nodes in the test network: 8 | 9 | 1. `node-1` is a `geth` node, spun up to expose the JSON-RPC server in its default configuration 10 | which does not expose the `admin` API. 11 | 12 | 2. `node-2` is a `geth` node, spun up to expose the JSON-RPC server in a configuration which exposes 13 | the `admin` API. 14 | 15 | The [tests](./test.sh) check that: 16 | 17 | 1. The `admin` API *is not* accessible when a `geth` client attaches to `node-1` through the 18 | JSON-RPC API. 19 | 20 | 2. The `admin` API *is* accessiblye when a `geth` client attaches to `node-2` through the JSON-RPC 21 | API. 22 | 23 | Note that configuring `node-2` to expose the `admin` API was as simple as passing the 24 | 25 | ``` 26 | --rpcapi ,admin 27 | ``` 28 | 29 | to the node at runtime (see `node-2` definition in the [manifest](./docker-compose.yaml)). 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 the-chaingang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # `ethereal` tests 2 | 3 | This directory contains the `ethereal` unit tests. The tests are grouped in suites, by 4 | network topology and topic. Each suite of tests lives in a distinct subdirectory of 5 | this directory. 6 | 7 | To run a particular suite of tests: 8 | 9 | 1. Determine which `ethereal` image you would like to test. This will either be a 10 | `chaingang/ethereal` image that you have built locally or an 11 | image from our DockerHub page -- 12 | [List of available DockerHub tags](https://hub.docker.com/r/chaingang/ethereal/tags/). 13 | 14 | 1. Save the image tag under the `ETHEREAL_TAG` environment variable. 15 | 16 | 1. Enter the corresponding subdirectory of this directory. 17 | 18 | 1. Run `docker-compose up --build --exit-code-from test`. 19 | 20 | 1. Watch the test run. It will exit with code `0` if the test passed and code `1` otherwise. 21 | 22 | 1. You can check the exit code after the test has completed using `echo $?`. 23 | 24 | 1. Run `docker-compose down -v` to clean up after the test has completed. 25 | 26 | 27 | ## Available suites 28 | 29 | - [connectivity](./connectivity) - Tests connectivity between two full nodes connected through a 30 | bootnode. 31 | 32 | - [rpc-apis](./rpc-apis) - Tests that RPC interface on `geth` nodes can be configured to expose 33 | different management APIs. 34 | -------------------------------------------------------------------------------- /testrunner.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e -u -o pipefail 4 | 5 | # Parameters: 6 | # 1. IDENTITY - Test identifier; a name which will make it easy to identify logs for this test in your console 7 | # 2. SIGNAL_DIR - Directory in which to poll for signalling files 8 | # 3. POLL_INTERVAL - Seconds to wait between polls 9 | # 4. POLL_ATTEMPTS - Maximum number of attempted polls of the signalling directory 10 | # 5. TEST_SCRIPT - Script in which tests are defined; to be run after all signals have successfully been detected 11 | # 12 | # Command line arguments: 13 | # A list of files to poll the $SIGNAL_DIR for 14 | 15 | IDENTITY=$IDENTITY 16 | SIGNAL_DIR=$SIGNAL_DIR 17 | POLL_INTERVAL=${POLL_INTERVAL:-1} 18 | POLL_ATTEMPTS=${POLL_ATTEMPTS:-5} 19 | TEST_SCRIPT=${TEST_SCRIPT} 20 | 21 | ATTEMPTS=0 22 | while true ; do 23 | echo "$IDENTITY: Poll $[$ATTEMPTS+1]/$POLL_ATTEMPTS" 24 | 25 | if [ $ATTEMPTS -eq $POLL_ATTEMPTS ] ; then 26 | echo "$IDENTITY: Maximum polls exceeded" 27 | exit 1 28 | elif [ $# -eq 0 ] ; then 29 | echo "$IDENTITY: No signal expected" 30 | break 31 | else 32 | NUM_SIGNALS=0 33 | for SIGNAL in $@ ; do 34 | if [ -e "$SIGNAL_DIR/$SIGNAL" ] ; then 35 | NUM_SIGNALS=$[$NUM_SIGNALS+1] 36 | fi 37 | done 38 | 39 | if [ $NUM_SIGNALS -eq $# ] ; then 40 | echo "$IDENTITY: All signals detected - $@" 41 | break 42 | fi 43 | 44 | sleep $POLL_INTERVAL 45 | ATTEMPTS=$[$ATTEMPTS+1] 46 | fi 47 | done 48 | 49 | bash $TEST_SCRIPT 50 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | # Parameters: 2 | # ----------- 3 | # - ETHEREAL_TAG: Specific tag under chaingang/ethereal you wish to deploy, defaults 4 | # to "latest" 5 | # 6 | # - NETWORK_ID: Defaults to 158972 7 | # 8 | # - HOST_SHARED_DIR: Local path to directory you would like to use as the ethereal 9 | # network shared directory; if not specified, uses a docker volume 10 | # 11 | # - HOST_GENESIS_DIR: Local path to directory containing your genesis file, defaults 12 | # to directory from which you are running "docker-compose up" 13 | # 14 | # - GENESIS_FILE: Name of genesis file you wish to use, defaults to genesis.json 15 | # (Your local path to the genesis file should be $GENESIS_DIR/$GENESIS_FILE) 16 | 17 | version: '2.1' 18 | services: 19 | bootnode: 20 | image: chaingang/ethereal:${ETHEREAL_TAG:-latest} 21 | command: ["--verbosity", "6"] 22 | environment: 23 | - TARGET=bootnode 24 | networks: 25 | ethnet: 26 | ipv4_address: 13.3.7.2 27 | ports: 28 | - "30301:30301" 29 | volumes: 30 | - ${HOST_SHARED_DIR:-shared}:/opt/shared 31 | node-1: 32 | image: chaingang/ethereal:${ETHEREAL_TAG:-latest} 33 | command: ["--rpc", "--networkid", "${NETWORK_ID:-158972}"] 34 | networks: 35 | - ethnet 36 | volumes: 37 | - ${HOST_SHARED_DIR:-shared}:/opt/shared 38 | - ${HOST_GENESIS_DIR:-./}:/opt/genesis 39 | networks: 40 | ethnet: 41 | ipv4_address: 13.3.7.3 42 | ports: 43 | - "8545:8545" 44 | - "30303:30303" 45 | depends_on: 46 | - bootnode 47 | environment: 48 | - TARGET=fullnode 49 | - GENESIS_FILE=${GENESIS_FILE:-genesis.json} 50 | - NODE_NAME=node-1 51 | - BOOTNODE_ADDRESS=13.3.7.2 52 | - BOOTNODE_UDP_PORT=30301 53 | node-2: 54 | image: chaingang/ethereal:${ETHEREAL_TAG:-latest} 55 | command: ["--rpc", "--networkid", "${NETWORK_ID:-158972}"] 56 | networks: 57 | - ethnet 58 | volumes: 59 | - ${HOST_SHARED_DIR:-shared}:/opt/shared 60 | - ${HOST_GENESIS_DIR:-./}:/opt/genesis 61 | networks: 62 | ethnet: 63 | ipv4_address: 13.3.7.4 64 | ports: 65 | - "8645:8545" 66 | - "31303:30303" 67 | depends_on: 68 | - bootnode 69 | environment: 70 | - TARGET=miningnode 71 | - GENESIS_FILE=${GENESIS_FILE:-genesis.json} 72 | - NODE_NAME=node-2 73 | - BOOTNODE_ADDRESS=13.3.7.2 74 | - BOOTNODE_UDP_PORT=30301 75 | networks: 76 | ethnet: 77 | driver: bridge 78 | ipam: 79 | config: 80 | - subnet: 13.3.7.0/16 81 | gateway: 13.3.7.1 82 | volumes: 83 | shared: 84 | -------------------------------------------------------------------------------- /tests/connectivity/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | # Parameters: 2 | # ----------- 3 | # - ETHEREAL_HOME: Path to ethereal home directory, defaults to "../../" 4 | # 5 | # - ETHEREAL_TAG: Specific tag under chaingang/ethereal you wish to deploy, defaults 6 | # to "latest" 7 | # 8 | # - NETWORK_ID: Defaults to 158972 9 | # 10 | # - HOST_SHARED_DIR: Local path to directory you would like to use as the ethereal 11 | # network shared directory; if not specified, uses a docker volume 12 | 13 | version: '2.1' 14 | services: 15 | bootnode: 16 | image: chaingang/ethereal:${ETHEREAL_TAG:-latest} 17 | command: ["--verbosity", "6"] 18 | environment: 19 | - TARGET=bootnode 20 | networks: 21 | ethnet: 22 | ipv4_address: 13.3.7.2 23 | ports: 24 | - "30301:30301" 25 | volumes: 26 | - ${HOST_SHARED_DIR:-shared}:/opt/shared 27 | node-1: 28 | image: chaingang/ethereal:${ETHEREAL_TAG:-latest} 29 | command: ["--rpc", "--networkid", "${NETWORK_ID:-158972}"] 30 | networks: 31 | - ethnet 32 | volumes: 33 | - ${HOST_SHARED_DIR:-shared}:/opt/shared 34 | - ${ETHEREAL_HOME:-../../}:/opt/genesis 35 | networks: 36 | ethnet: 37 | ipv4_address: 13.3.7.3 38 | ports: 39 | - "8545:8545" 40 | - "30303:30303" 41 | depends_on: 42 | - bootnode 43 | environment: 44 | - TARGET=fullnode 45 | - GENESIS_FILE=genesis.json 46 | - NODE_NAME=node-1 47 | - BOOTNODE_ADDRESS=13.3.7.2 48 | - BOOTNODE_UDP_PORT=30301 49 | node-2: 50 | image: chaingang/ethereal:${ETHEREAL_TAG:-latest} 51 | command: ["--rpc", "--networkid", "${NETWORK_ID:-158972}"] 52 | networks: 53 | - ethnet 54 | volumes: 55 | - ${HOST_SHARED_DIR:-shared}:/opt/shared 56 | - ${ETHEREAL_HOME:-../../}:/opt/genesis 57 | networks: 58 | ethnet: 59 | ipv4_address: 13.3.7.4 60 | ports: 61 | - "8645:8545" 62 | - "31303:30303" 63 | depends_on: 64 | - bootnode 65 | environment: 66 | - TARGET=fullnode 67 | - GENESIS_FILE=genesis.json 68 | - NODE_NAME=node-2 69 | - BOOTNODE_ADDRESS=13.3.7.2 70 | - BOOTNODE_UDP_PORT=30301 71 | test: 72 | build: 73 | context: ${ETHEREAL_HOME:-../../} 74 | dockerfile: Dockerfile.test 75 | command: ["node-1/geth.ipc", "node-2/geth.ipc"] 76 | volumes: 77 | - ${HOST_SHARED_DIR:-shared}:/opt/shared 78 | - ./:/opt/tests 79 | depends_on: 80 | - node-1 81 | - node-2 82 | environment: 83 | - IDENTITY=connectivity-test 84 | - POLL_INTERVAL=10 85 | - POLL_ATTEMPTS=10 86 | networks: 87 | ethnet: 88 | driver: bridge 89 | ipam: 90 | config: 91 | - subnet: 13.3.7.0/16 92 | gateway: 13.3.7.1 93 | volumes: 94 | shared: 95 | -------------------------------------------------------------------------------- /tests/rpc-apis/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | # Parameters: 2 | # ----------- 3 | # - ETHEREAL_HOME: Path to ethereal home directory, defaults to "../../" 4 | # 5 | # - ETHEREAL_TAG: Specific tag under chaingang/ethereal you wish to deploy, defaults 6 | # to "latest" 7 | # 8 | # - NETWORK_ID: Defaults to 158972 9 | # 10 | # - HOST_SHARED_DIR: Local path to directory you would like to use as the ethereal 11 | # network shared directory; if not specified, uses a docker volume 12 | 13 | version: '2.1' 14 | services: 15 | bootnode: 16 | image: chaingang/ethereal:${ETHEREAL_TAG:-latest} 17 | command: ["--verbosity", "6"] 18 | environment: 19 | - TARGET=bootnode 20 | networks: 21 | ethnet: 22 | ipv4_address: 13.3.7.2 23 | ports: 24 | - "30301:30301" 25 | volumes: 26 | - ${HOST_SHARED_DIR:-shared}:/opt/shared 27 | node-1: 28 | image: chaingang/ethereal:${ETHEREAL_TAG:-latest} 29 | command: ["--rpc", "--networkid", "${NETWORK_ID:-158972}"] 30 | networks: 31 | - ethnet 32 | volumes: 33 | - ${HOST_SHARED_DIR:-shared}:/opt/shared 34 | - ${ETHEREAL_HOME:-../../}:/opt/genesis 35 | networks: 36 | ethnet: 37 | ipv4_address: 13.3.7.3 38 | ports: 39 | - "8545:8545" 40 | - "30303:30303" 41 | depends_on: 42 | - bootnode 43 | environment: 44 | - TARGET=fullnode 45 | - GENESIS_FILE=genesis.json 46 | - NODE_NAME=node-1 47 | - BOOTNODE_ADDRESS=13.3.7.2 48 | - BOOTNODE_UDP_PORT=30301 49 | node-2: 50 | image: chaingang/ethereal:${ETHEREAL_TAG:-latest} 51 | command: ["--rpc", "--rpcapi", "db,eth,net,web3,admin", "--networkid", "${NETWORK_ID:-158972}"] 52 | networks: 53 | - ethnet 54 | volumes: 55 | - ${HOST_SHARED_DIR:-shared}:/opt/shared 56 | - ${ETHEREAL_HOME:-../../}:/opt/genesis 57 | networks: 58 | ethnet: 59 | ipv4_address: 13.3.7.4 60 | ports: 61 | - "8645:8545" 62 | - "31303:30303" 63 | depends_on: 64 | - bootnode 65 | environment: 66 | - TARGET=fullnode 67 | - GENESIS_FILE=genesis.json 68 | - NODE_NAME=node-2 69 | - BOOTNODE_ADDRESS=13.3.7.2 70 | - BOOTNODE_UDP_PORT=30301 71 | test: 72 | build: 73 | context: ${ETHEREAL_HOME:-../../} 74 | dockerfile: Dockerfile.test 75 | command: ["node-1/geth.ipc", "node-2/geth.ipc"] 76 | volumes: 77 | - ${HOST_SHARED_DIR:-shared}:/opt/shared 78 | - ./:/opt/tests 79 | depends_on: 80 | - node-1 81 | - node-2 82 | environment: 83 | - IDENTITY=rpc-api-test 84 | - POLL_INTERVAL=10 85 | - POLL_ATTEMPTS=10 86 | - RPC_URL_1=http://13.3.7.3:8545 87 | - RPC_URL_2=http://13.3.7.4:8545 88 | networks: 89 | ethnet: 90 | networks: 91 | ethnet: 92 | driver: bridge 93 | ipam: 94 | config: 95 | - subnet: 13.3.7.0/16 96 | gateway: 13.3.7.1 97 | volumes: 98 | shared: 99 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BOOTNODE_TARGET="bootnode" 4 | FULLNODE_TARGET="fullnode" 5 | MININGNODE_TARGET="miningnode" 6 | 7 | BIN_PATH=/opt/go-ethereum/build/bin 8 | SHARED_DIR=/opt/shared 9 | GENESIS_DIR=/opt/genesis 10 | 11 | BOOTNODE_KEY_FILE=boot.key 12 | 13 | BOOTNODE=$BIN_PATH/bootnode 14 | GETH=$BIN_PATH/geth 15 | 16 | echo "Running ethereal with target: $TARGET" 17 | 18 | if [ $TARGET == $BOOTNODE_TARGET ] ; then 19 | echo $(ls -l $SHARED_DIR) 20 | if [ -z $(ls -l $SHARED_DIR| grep $BOOTNODE_KEY_FILE) ] ; then 21 | $BOOTNODE -genkey $SHARED_DIR/$BOOTNODE_KEY_FILE 22 | fi 23 | 24 | $BOOTNODE -nodekey $SHARED_DIR/$BOOTNODE_KEY_FILE $@ 25 | 26 | elif [ $TARGET == $FULLNODE_TARGET ] || [ $TARGET == $MININGNODE_TARGET ] ; then 27 | if [ -z $NODE_NAME ] ; then 28 | echo "ERROR: Container run with empty NODE_NAME" 29 | exit 1 30 | fi 31 | 32 | if [ ! -z $(ls -1 $SHARED_DIR | grep $NODE_NAME) ] ; then 33 | echo "WARNING: Node with name $NODE_NAME already exists" 34 | else 35 | mkdir -p $SHARED_DIR/$NODE_NAME 36 | fi 37 | 38 | if [ -z $BOOTNODE_ADDRESS ] ; then 39 | echo "ERROR: BOOTNODE_ADDRESS not specified" 40 | exit 1 41 | fi 42 | 43 | if [ -z $BOOTNODE_UDP_PORT ] ; then 44 | echo "WARNING: BOOTNODE_UDP_PORT not specified - using 30301" 45 | BOOTNODE_UDP_PORT=30301 46 | fi 47 | 48 | if [ -z $(ls -1 $GENESIS_DIR | grep $GENESIS_FILE) ] ; then 49 | echo "ERROR: $GENESIS_FILE not present in genesis directory" 50 | exit 1 51 | fi 52 | 53 | echo "Getting ready to POLL..." 54 | 55 | # Wait for bootnode hash to become available 56 | if [ -z $POLL_INTERVAL ] ; then 57 | POLL_INTERVAL=1 58 | fi 59 | if [ -z $RETRIES ] ; then 60 | RETRIES=20 61 | fi 62 | CURRENT=0 63 | while true ; do 64 | if [ $CURRENT -eq $RETRIES ] ; then 65 | exit 1 66 | fi 67 | 68 | sleep $POLL_INTERVAL 69 | 70 | if [ ! -z $(ls -1 $SHARED_DIR | grep $BOOTNODE_KEY_FILE) ] ; then 71 | break 72 | fi 73 | 74 | CURRENT=$[$CURRENT+1] 75 | done 76 | 77 | # At this point, $SHARED_DIR/$BOOTNODE_KEY_FILE exists 78 | BOOTNODE_PUBLIC_KEY=$($BOOTNODE -nodekey $SHARED_DIR/$BOOTNODE_KEY_FILE -writeaddress) 79 | BOOTNODE_ENODE_URL="enode://${BOOTNODE_PUBLIC_KEY}@${BOOTNODE_ADDRESS}:${BOOTNODE_UDP_PORT}" 80 | 81 | echo "Acquired bootnode enode URL: $BOOTNODE_ENODE_URL" 82 | 83 | $GETH --datadir $SHARED_DIR/$NODE_NAME init $GENESIS_DIR/$GENESIS_FILE 84 | 85 | MININGNODE_ARGS="" 86 | if [ $TARGET == $MININGNODE_TARGET ] ; then 87 | MININGNODE_PASSWORD="$NODE_NAME-password" 88 | ACCOUNT_OUT=$($GETH --datadir $SHARED_DIR/$NODE_NAME account new --password <(echo $MININGNODE_PASSWORD)) 89 | ACCOUNT=$(echo $ACCOUNT_OUT | sed -e "s/Address: {\(.\+\)}/\1/") 90 | 91 | echo "Setting geth up to mine into account: $ACCOUNT" 92 | 93 | MININGNODE_ARGS="--mine --minerthreads $MINERTHREADS --etherbase $ACCOUNT" 94 | fi 95 | 96 | $GETH --datadir $SHARED_DIR/$NODE_NAME \ 97 | --rpcaddr "0.0.0.0" \ 98 | --bootnodes $BOOTNODE_ENODE_URL \ 99 | $MININGNODE_ARGS $@ 100 | 101 | else 102 | echo "ERROR: Invalid TARGET=$TARGET" 103 | exit 1 104 | fi -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ethereal 2 | 3 | Dockerized Ethereum testnets 4 | 5 | - - - 6 | 7 | ## Table of Contents 8 | 9 | * [Requirements](#requirements) 10 | * [Network up](#network-up) 11 | * [Network topology and configuration](#network-topology-and-configuration) 12 | * [Working with nodes from docker host](#working-with-nodes-from-docker-host) 13 | * [JSON-RPC access with `geth`](#json-rpc-access-with-geth) 14 | * [IPC access with `geth`](#ipc-access-with-geth) 15 | * [Working with a node from its container](#working-with-a-node-from-its-container) 16 | * [Example: Checking a node's `admin.peers`](#example-checking-a-nodes-adminpeers) 17 | * [Building and using a specific version of Geth](#building-and-using-a-specific-version-of-geth) 18 | * [Tests](#tests) 19 | 20 | 21 | ## Requirements 22 | 23 | + [Docker](https://www.docker.com/get-docker) 24 | 25 | + [Docker compose](https://docs.docker.com/compose/) 26 | 27 | + (Optional but recommended) [Geth](https://github.com/ethereum/go-ethereum/wiki/geth) 28 | 29 | 30 | ## Network up 31 | 32 | To bring up your ethereal network, run 33 | 34 | ```commandline 35 | docker-compose up 36 | ``` 37 | 38 | 39 | This uses the [`chaingang/ethereal` docker image](https://hub.docker.com/r/chaingang/ethereal/), 40 | build from this repository's [Dockerfile](./Dockerfile). 41 | 42 | 43 | ## Network topology and configuration 44 | 45 | Our default network consists of a single bootnode, a single full node, and a single mining node. 46 | For more information about private ethereum networks, refer to the 47 | [ethereum/go-ethereum documentation on the subject](https://github.com/ethereum/go-ethereum/wiki/Setting-up-private-network-or-local-cluster). 48 | 49 | The network is specified in [docker-compose.yaml](./docker-compose.yaml). If you would like to deploy a network with a different topology, this is the place to start. 50 | 51 | 52 | ## Working with nodes from docker host 53 | 54 | `ethereal` uses [geth](https://github.com/ethereum/go-ethereum/wiki/geth) under the hood as its 55 | default ethereum node implementation. 56 | 57 | 58 | ### JSON-RPC access with `geth` 59 | 60 | By default, we expose the [JSON-RPC interface](https://github.com/ethereum/wiki/wiki/JSON-RPC). To 61 | attach to this from your host machine, run 62 | 63 | ```commandline 64 | geth attach http://localhost:${NODE_RPC_PORT} 65 | ``` 66 | 67 | Here, `$NODE_RPC_PORT` refers to the host port specified for your desired node when you brought up 68 | your network. For our [sample network](./docker-compose.yaml), you can use `NODE_RPC_PORT=8545` for 69 | `node-1` or `NODE_RPC_PORT=8645` for `node-2`. 70 | 71 | 72 | ### IPC access with `geth` 73 | 74 | You can also use the IPC API to access administrative functions on the node. To do this from your 75 | host machine, you will have to access the local mount point for your `ethereal` network's `shared` 76 | volume. You can see the path to this mount point using: 77 | 78 | ```commandline 79 | docker volume inspect ethereal_shared -f "{{.Mountpoint}}" 80 | ``` 81 | 82 | Note that your `$USER` probably does not have permissions to access this directory and so any 83 | commands run against this directory will have to be run with superuser privileges. 84 | 85 | To attach to the IPC interface for your desired node, simply run 86 | 87 | ```commandline 88 | sudo geth attach ipc:$(docker volume inspect ethereal_shared -f "{{.Mountpoint}}")/$NODE_NAME/geth.ipc 89 | ``` 90 | 91 | Here, the `$NODE_NAME` specifies which node you are interacting with. For our 92 | [sample network](./docker-compose.yaml), if you want to connect to `node-1`, you would set 93 | `NODE_NAME=node-1`. 94 | 95 | Note that if you built `geth` from source, it probably won't be available under your superuser 96 | `PATH` and you may therefore, in the above command, have to specify the path to your `geth` binary 97 | instead. 98 | 99 | 100 | ## Working with a node from its container 101 | 102 | For administrative functions especially, you are better off working with a node from its container. 103 | You can do so by running a `bash` process on that container: 104 | 105 | ```commandline 106 | docker exec -it $NODE_CONTAINER bash 107 | ``` 108 | 109 | Here, `$NODE_CONTAINER` specifies the name of the container running the desired node. For our 110 | [sample network](./docker-compose.yaml), you can specify `NODE_CONTAINER=ethereal_nod-1_1` to work 111 | with `node-1`. 112 | 113 | From this shell, run 114 | 115 | ```commandline 116 | /opt/go-ethereum/build/bin/geth attach ipc:/opt/shared/$NODE_NAME/geth.ipc 117 | ``` 118 | 119 | for access to the IPC console. Note that you will have to use the appropriate `$NODE_NAME` and also 120 | that you can access the `geth` console for any other node from this container (as a matter of 121 | convenience). 122 | 123 | 124 | ### Example: Checking a node's `admin.peers` 125 | 126 | In the [sample network](./docker-compose.yaml), attach to the `node-2` container using: 127 | 128 | ```commandline 129 | docker exec -it ethereal_node-2_1 bash 130 | ``` 131 | 132 | Attach a `geth` console to the node using: 133 | 134 | ```commandline 135 | /opt/go-ethereum/build/bin/geth attach ipc:/opt/shared/node-2/geth.ipc 136 | ``` 137 | 138 | In the geth console, run: 139 | 140 | ``` 141 | admin.peers 142 | ``` 143 | 144 | You should see something like this: 145 | 146 | ![node-2 peers](./img/peers.png) 147 | 148 | Exit the `geth` console using 149 | 150 | ``` 151 | exit 152 | ``` 153 | 154 | Exit from the container using 155 | 156 | ```commandline 157 | exit 158 | ``` 159 | 160 | ## Building and using a specific version of Geth 161 | 162 | By default the Ethereal image is using the `master` branch of [go-ethereum](https://github.com/ethereum/go-ethereum). If you want to build from another branch, to use a specific version, you need to follow these steps: 163 | 164 | To build a specific branch, run the following command from the `ethereal` folder. This example is using the `release/1.7` branch. 165 | 166 | ```commandline 167 | docker build --build-arg CHECKOUT_TARGET="release/1.7" -t chaingang/ethereal:release-1.7 . 168 | ``` 169 | 170 | Once the image is built you can use the newly created image when you start Ethereal by using this command. 171 | 172 | ```commandline 173 | ETHEREAL_TAG=chaingang/ethereal:relase-1.7 docker-compose up 174 | ``` 175 | 176 | To verify that the correct version is running you can check the console output. You should see a line indicating the Geth version similar to this example: 177 | 178 | ``` 179 | Starting peer-to-peer node instance=Geth/v1.7.3-stable-4bb3c89d/linux-amd64/go1.8.5 180 | ``` 181 | 182 | 183 | ## Tests 184 | 185 | The `ethereal` unit tests can be found in the [tests/](./tests) directory. Each suite of tests is contained 186 | in a separate subdirectory of `tests/`. The [testing README](./tests/README.md) provides detailed 187 | instructions for how to run these tests. 188 | --------------------------------------------------------------------------------