Nodeos

Local Multi-Node Testnet

This tutorial describes how to set up a multi-node blockchain configuration running on a single host. This is referred to as a single host, multi-node testnet. Initially we will set up two nodes on your local computer and have them communicate with each other. The examples in this section rely on three command-line applications, nodeos, keosd, and cleos. We will also need to build and deploy the reference smart contract eosio.bios using eosio.cdt.

The following diagram depicts the testnet configuration.

Finally, we will add one more node, to show two producing nodes, alternating block production.

The single host multi-node testnet configuration.

The single host multi-node testnet configuration.

It is assumed that keosd, cleos, and nodeos have been installed in your path, and that you know how to start these applications. It is also assumed that you have installed the eosio.cdt.

Open four "terminal" windows to perform the steps in this tutorial.

Start the Wallet Manager

In the first terminal window, start keosd, the wallet management application:

keosd --http-server-address 127.0.0.1:8899

If successful, keosd will display some information, starting with:

```
2493323ms thread-0   wallet_plugin.cpp:39          plugin_initialize    ] initializing wallet plugin
2493323ms thread-0   http_plugin.cpp:141           plugin_initialize    ] host: 127.0.0.1 port: 8899
2493323ms thread-0   http_plugin.cpp:144           plugin_initialize    ] configured http to listen on 127.0.0.1:8899
2493323ms thread-0   http_plugin.cpp:213           plugin_startup       ] start listening for http requests
2493324ms thread-0   wallet_api_plugin.cpp:70      plugin_startup       ] starting wallet_api_plugin
```

Look for a line saying the wallet is listening on 127.0.0.1 port 8899. This will indicate that keosd started correctly and is listening on the correct port. If you see anything else, or you see some error report prior to "starting wallet_api_plugin", then you need to diagnose the issue and restart before moving forward.

When keosd is running correctly, leave that window open with the wallet app running and move to the next terminal window.

Create a Default Wallet

In the next terminal window, use cleos, the command-line utility, to create the default wallet.

cleos --wallet-url http://127.0.0.1:8899  wallet create --to-console

cleos will indicate that it created the "default" wallet, and will provide a password for future wallet access. As the message says, be sure to preserve this password for future use. The following is an example of the output:

Creating wallet: default
Save password to use in the future to unlock this wallet.
Without password imported keys will not be retrievable.
"PW5JsmfYz2wrdUEotTzBamUCAunAA8TeRZGT57Ce6PkvM12tre8Sm"

keosd will generate some status output in its window. We will continue to use this second window for subsequent cleos commands.

Loading the eosio Key

The private blockchain launched in the steps above is created with a default initial key which must be loaded into the wallet. Use the private key specified in the command below.

$ cleos --wallet-url http://127.0.0.1:8899 wallet import --private-key 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
imported private key for: EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV

Start the First Producer Node

We can now start the first producer node. In the third terminal window run:

nodeos --enable-stale-production --producer-name eosio --plugin
eosio::chain_api_plugin --plugin eosio::net_api_plugin

This creates a special producer, known as the "bios" producer. Assuming everything has executed correctly to this point, you should see output from the nodeos process reporting block creation.

Start the Second Producer Node

To start additional nodes, you must first build and deploy the reference smart contract eosio.bios using eosio.cdt. This contract enables you to have direct control over the resource allocation of other accounts and to access other privileged API calls.

The reference smart contract eosio.bios is available in the eosio.contracts repository, download the source and locate the eosio.bios smart contract in eosio.contracts/contracts/eosio.bios/.

Go to the eosio.contracts directory and create a new directory build/eosio.bios. We will copy the generated .abi and .wasm files here to make deploying the smart contracts easier.

cd eosio.contracts/contracts/eosio.bios
mkdir build
cd build
mkdir eosio.bios

We should now have a convenience directory eosio.contracts/contracts/eosio.bios/build/eosio.bios.

Go to the directory containing the eosio.bios.cpp file and use the eosio-cpp command below to create the eosio.bios.abi and eosio.bios.wasm files.

cd eosio.contracts/contracts/eosio.bios/src

eosio-cpp -I ../include -abigen -o eosio.bios.wasm eosio.bios.cpp

eosio.contracts/contracts/eosio.bios/src should now contain eosio.bios.abi, eosio.bios.wasm, (and eosio.bios.cpp). Move the .wasm and .abi files to our convenience directory eosio.contracts/contracts/eosio.bios/build/eosio.bios

mv eosio.bios.wasm ../build/eosio.bios
mv eosio.bios.abi ../build/eosio.bios

eosio.contracts/contracts/eosio.bios/build/eosio.bios should now contain eosio.bios.abi and eosio.bios.wasm.

We now need to deploy eosio.bios to the blockchain.

Return to the second terminal window and run the following commands to load the contract:

cd eosio.contracts/contracts/eosio.bios/build

cleos --wallet-url http://127.0.0.1:8899 set contract eosio eosio.bios --abi eosio.bios.abi

We will create an account to become a producer, using the account name inita. To create the account, we need to generate keys to associate with the account, and import those into our wallet.

Run the create key command:

cleos create key --to-console

This will report newly generated public and private keypairs that will look similar to the following.

IMPORTANT: The command line instructions that follow use the keys shown below. In order to be able to cut-and-paste the command line instructions directly from this tutorial, do NOT use the keys that you just generated. If, instead, you want to use your newly generated keys, you will need to replace the key values with yours in the commands.

Private key: 5JgbL2ZnoEAhTudReWH1RnMuQS6DBeLZt4ucV6t8aymVEuYg7sr
Public key: EOS6hMjoWRF2L8x9YpeqtUEcsDKAyxSuM1APicxgRU1E3oyV5sDEg

Now import the private key portion into your wallet. If successful, the matching public key will be reported. This should match the previously generated public key:

cleos --wallet-url http://127.0.0.1:8899 wallet import --private-key  5JgbL2ZnoEAhTudReWH1RnMuQS6DBeLZt4ucV6t8aymVEuYg7sr
imported private key for: EOS6hMjoWRF2L8x9YpeqtUEcsDKAyxSuM1APicxgRU1E3oyV5sDEg

Create the inita account that we will use to become a producer. The create account command requires two public keys, one for the account's owner key and one for its active key. In this example, the newly created public key is used twice, as both the owner key and the active key. Example output from the create command is shown:

cleos --wallet-url http://127.0.0.1:8899 create account eosio inita EOS6hMjoWRF2L8x9YpeqtUEcsDKAyxSuM1APicxgRU1E3oyV5sDEg EOS6hMjoWRF2L8x9YpeqtUEcsDKAyxSuM1APicxgRU1E3oyV5sDEg
executed transaction: d1ea511977803d2d88f46deb554f5b6cce355b9cc3174bec0da45fc16fe9d5f3  352 bytes  102400 cycles
#         eosio <= eosio::newaccount            {"creator":"eosio","name":"inita","owner":{"threshold":1,"keys":[{"key":"EOS6hMjoWRF2L8x9YpeqtUEcsDK...

We now have an account that is available to have a contract assigned to it, enabling it to do meaningful work. In other tutorials, the account has been used to establish simple contracts. In this case, the account will be designated as a block producer.

In the fourth terminal window, start a second nodeos instance. Notice that this command line is substantially longer than the one we used above to create the first producer. This is necessary to avoid collisions with the first nodeos instance. Fortunately, you can just cut and paste this command line and adjust the keys:

nodeos --enable-stale-production --producer-name inita --plugin eosio::chain_api_plugin --plugin eosio::net_api_plugin --http-server-address 127.0.0.1:8889 --p2p-listen-endpoint 127.0.0.1:9877 --p2p-peer-address 127.0.0.1:9876 --config-dir node2 --data-dir node2 --signature-provider =  EOS6hMjoWRF2L8x9YpeqtUEcsDKAyxSuM1APicxgRU1E3oyV5sDEg=KEY:5JgbL2ZnoEAhTudReWH1RnMuQS6DBeLZt4ucV6t8aymVEuYg7sr

The output from this new node will show a little activity but will stop reporting until the last step in this tutorial, when the inita account is registered as a producer account and activated. Here is some example output from a newly started node. Your output might look a little different, depending on how much time you took entering each of these commands. Furthermore, this example is only the last few lines of output:

2393147ms thread-0   producer_plugin.cpp:176       plugin_startup       ] producer plugin:  plugin_startup() end
2393157ms thread-0   net_plugin.cpp:1271           start_sync           ] Catching up with chain, our last req is 0, theirs is 8249 peer dhcp15.ociweb.com:9876 - 295f5fd
2393158ms thread-0   chain_controller.cpp:1402     validate_block_heade ] head_block_time 2018-03-01T12:00:00.000, next_block 2018-04-05T22:31:08.500, block_interval 500
2393158ms thread-0   chain_controller.cpp:1404     validate_block_heade ] Did not produce block within block_interval 500ms, took 3061868500ms)
2393512ms thread-0   producer_plugin.cpp:241       block_production_loo ] Not producing block because production is disabled until we receive a recent block (see: --enable-stale-production)
2395680ms thread-0   net_plugin.cpp:1385           recv_notice          ] sync_manager got last irreversible block notice
2395680ms thread-0   net_plugin.cpp:1271           start_sync           ] Catching up with chain, our last req is 8248, theirs is 8255 peer dhcp15.ociweb.com:9876 - 295f5fd
2396002ms thread-0   producer_plugin.cpp:226       block_production_loo ] Previous result occurred 5 times
2396002ms thread-0   producer_plugin.cpp:244       block_production_loo ] Not producing block because it isn't my turn, its eosio

At this point, the second nodeos is an idle producer. To turn it into an active producer, inita needs to be registered as a producer with the bios node, and the bios node needs to perform an action to update the producer schedule.

cleos --wallet-url http://127.0.0.1:8899 push action eosio setprods "{ \"schedule\": [{\"producer_name\": \"inita\",\"block_signing_key\": \"EOS6hMjoWRF2L8x9YpeqtUEcsDKAyxSuM1APicxgRU1E3oyV5sDEg\"}]}" -p eosio@active
executed transaction: 2cff4d96814752aefaf9908a7650e867dab74af02253ae7d34672abb9c58235a  272 bytes  105472 cycles
#         eosio <= eosio::setprods              {"version":1,"producers":[{"producer_name":"inita","block_signing_key":"EOS6hMjoWRF2L8x9YpeqtUEcsDKA...

Congratulations, you have now configured a two-node testnet! You can see that the original node is no longer producing blocks but is instead receiving them. You can verify this by running the get info command against each node.

Get info about the first node:

cleos get info

This should produce output that looks similar to this:

{
  "server_version": "223565e8",
  "head_block_num": 11412,
  "last_irreversible_block_num": 11411,
  "head_block_id": "00002c94daf7dff456cd940bd585c4d9b38e520e356d295d3531144329c8b6c3",
  "head_block_time": "2018-04-06T00:06:14",
  "head_block_producer": "inita"
}

Now for the second node:

cleos --url http://127.0.0.1:8889 get info

This should produce output that looks similar to this:

{
  "server_version": "223565e8",
  "head_block_num": 11438,
  "last_irreversible_block_num": 11437,
  "head_block_id": "00002cae32697444fa9a2964e4db85b5e8fd4c8b51529a0c13e38587c1bf3c6f",
  "head_block_time": "2018-04-06T00:06:27",
  "head_block_producer": "inita"
}

Now let's add a third node, so we have two producing nodes.

Repeat the steps for creating the second node. However, now use the account name initb for the producer name.

Create the account, generate keys for this account, and load them into your local wallet.
Open a new terminal window and start a new nodeos instance, changing the name of the producer and allocating unused ports for communication (--http-server-address, --p2p-listen-endpoint, and connect --p2p-peer-address to node2.) Remember to use the correct key pairs!

nodeos --enable-stale-production --producer-name initb --plugin
eosio::chain_api_plugin --plugin eosio::net_api_plugin --http-server-address
127.0.0.1:8887 --p2p-listen-endpoint 127.0.0.1:9878 --p2p-peer-address
127.0.0.1:9877 -d node3 --config-dir node3 --signature-provider = EOS7wk5N9V9HxZG7TU33vq6MMoVz3gT1JFf2yxksCuXatMb24CcVd=KEY:5KfYf55ocaAKdcFbG94ThnP3HLAptPsA58dovCQQX3jAzfSqdFa

At this point, the third nodeos is an idle producer. To turn it into an active producer, initb needs to be registered as a producer with the bios node, and the bios node needs to perform an action to update the producer schedule. The new version of the action now registers and schedules two producers, inita and initb.

cleos --wallet-url http://127.0.0.1:8899 push action eosio setprods "{
\"schedule\": [{\"producer_name\": \"inita\",\"block_signing_key\":
\"EOS6mDtkVSFeYTWcEjDB3NtszBYWoqtdKD75Lr3i7W1ynXMBG3u6s\"}, {\"producer_name\":
\"initb\",\"block_signing_key\":
\"EOS7wk5N9V9HxZG7TU33vq6MMoVz3gT1JFf2yxksCuXatMb24CcVd\"}]}" -p eosio@active

Congratulations, you have now configured a three-node testnet! You can see that the original node is no longer producing blocks but it is receiving them and you should see production on blocks switching between node 2 and node 3.

An example of output from one of the running nodes, showing the node receiving and producing blocks is shown below.

info  2019-05-21T08:24:34.006 thread-0  producer_plugin.cpp:345       on_incoming_block    ] Received block 5aaebf54756b2d3b... #58712 @ 2019-05-21T08:24:34.000 signed by initb [trxs: 0, lib: 58691, conf: 0, latency: 6 ms]
info  2019-05-21T08:24:34.505 thread-0  producer_plugin.cpp:345       on_incoming_block    ] Received block bc886bfd54e63425... #58713 @ 2019-05-21T08:24:34.500 signed by initb [trxs: 0, lib: 58691, conf: 0, latency: 5 ms]
info  2019-05-21T08:24:35.003 thread-0  producer_plugin.cpp:345       on_incoming_block    ] Received block 368db98db00f6378... #58714 @ 2019-05-21T08:24:35.000 signed by initb [trxs: 0, lib: 58691, conf: 0, latency: 3 ms]
info  2019-05-21T08:24:35.509 thread-0  producer_plugin.cpp:345       on_incoming_block    ] Received block 33eeca5a354d3261... #58715 @ 2019-05-21T08:24:35.500 signed by initb [trxs: 0, lib: 58691, conf: 0, latency: 9 ms]
info  2019-05-21T08:24:36.003 thread-0  producer_plugin.cpp:1597      produce_block        ] Produced block 0000e55c8f78add9... #58716 @ 2019-05-21T08:24:36.000 signed by inita [trxs: 0, lib: 58703, confirmed: 12]
info  2019-05-21T08:24:36.505 thread-0  producer_plugin.cpp:1597      produce_block        ] Produced block 0000e55d06ec9f71... #58717 @ 2019-05-21T08:24:36.500 signed by inita [trxs: 0, lib: 58703, confirmed: 0]
info  2019-05-21T08:24:37.005 thread-0  producer_plugin.cpp:1597      produce_block        ] Produced block 0000e55eb19eef37... #58718 @ 2019-05-21T08:24:37.000 signed by inita [trxs: 0, lib: 58703, confirmed: 0]
info  2019-05-21T08:24:37.504 thread-0  producer_plugin.cpp:1597      produce_block        ] Produced block 0000e55fab7f0345... #58719 @ 2019-05-21T08:24:37.500 signed by inita [trxs: 0, lib: 58703, confirmed: 0]
i

In a later tutorial we will explore how to use more advanced tools to run a multi-host, multi-node testnet.


Local Multi-Node Testnet


Suggested Edits are limited on API Reference Pages

You can only suggest edits to Markdown body content, but not to the API spec.