Simply run the following command to configure and launch testnet with 4 nodes:
docker run -p 8000-8007:8000-8007 vitvakatu/exonum-configuration-example 4
Docker will automatically pull image from the repository and run 4 nodes with
public endpoints at 127.0.0.1:8000
, ..., 127.0.0.1:8003
and
private ones at 127.0.0.1:8004
, ..., 127.0.0.1:8007
.
You can also use helper script:
./docker/example-start.sh <number of nodes>
To stop docker container, use docker stop <container id>
command.
To build an example binary of exonum blockchain with the single configuration service mounted, run:
cargo install --example configuration
exonum
crate system dependencies and rust toolchain configuration -
exonum install instructions.
4
is a required indexed parameter and stands for the number of nodes in
testnet:
mkdir -p testnet/configuration_service
cd testnet/configuration_service
configuration generate-testnet --start 5400 4 --output-dir .
cd ..
This should create the following config for testnet:
$ tree configuration_service/
configuration_service/
└── validators
├── 0.toml
├── 1.toml
├── 2.toml
└── 3.toml
configuration run --node-config configuration_service/validators/0.toml --db-path configuration_service/db/0 --public-api-address 127.0.0.1:8000 --private-api-address 127.0.0.1:8010
...
configuration run --node-config configuration_service/validators/3.toml --db-path configuration_service/db/3 --public-api-address 127.0.0.1:8003 --private-api-address 127.0.0.1:8013
--public-api-address
is for Exonum public http api endpoints--private-api-address
is for Exonum private http api endpoints--node-config
path to the node config--db-path
path to the database
All hash
es, public-key
s and signature
s in tables are hexadecimal strings.
config-body
is a valid json, corresponding to exonum config serialization.
This config is called actual config.
-
Only single config may be scheduled to become next config at any moment of time. This config is called following config.
-
For any current config, its following config will have
actual_from
greater than theactual_from
of current config. -
For any current config, its following config will have
previous_cfg_hash
equal to hash of current config. -
Any config propose gets scheduled to become the following config only if it gets 2/3+1 supermajority of votes of
validators
of actual config. Thus, which entities can determine what the following config will be is specified in the contents of actual config.
Endpoint | HTTP method | Description | Query parameters | Response template |
---|---|---|---|---|
/api/services/configuration/v1/configs/actual |
GET | Lookup actual config | None | { "config": config-body, "hash": config-hash } |
/api/services/configuration/v1/configs/following |
GET | Lookup already scheduled following config which hasn't yet taken effect.null if no config is scheduled |
None | { "config": config-body, "hash": config-hash } |
/api/services/configuration/v1/configs?hash=<config-hash> |
GET | Lookup config by config hash. If no propose was submitted for a config (genesis config) - "propose" is null . If only propose is present, then "committed_config" is null ."propose" key has json-object values, that match propose-template. |
<config-hash> - hash of looked up config. |
{ "committed_config": config_body, "propose": { "num_validators": integer, "tx_propose": propose_transaction_body, "votes_history_hash": vote-history-hash } } |
/api/services/configuration/v1/configs/votes?hash=<config-hash> |
GET | Lookup votes for a config propose by config hash. If a vote from validator is absent, null returned at the corresponding index in json array. If the config is absent altogether, null is returned instead of the array. |
<config-hash> - hash of looked up config. |
[ vote_for_propose_transaction_body, null, ... ] |
/api/services/configuration/v1/configs/committed?previous_cfg_hash=<config-hash>&actual_from=<lowest-actual-from> |
GET | Lookup all committed configs in commit order. | <previous_cfg_hash> and <lowest_actual_from> are optional filtering parameters.config-body is included in response if its previous_cfg_hash field equals the corresponding parameter. It's included if its actual_from field is greater or equal than corresponding parameter. |
[ { "config": config-body, "hash": config-hash }, { "config": config-body, "hash": config-hash }, ... ] |
/api/services/configuration/v1/configs/proposed?previous_cfg_hash=<config-hash>&actual_from=<lowest-actual-from> |
GET | Lookup all proposed configs in commit order. |
<previous_cfg_hash> and <lowest_actual_from> are optional filtering parameters.propose-template is included in response if its previous_cfg_hash field equals the corresponding parameter. It's included if its actual_from field is greater or equal than corresponding parameter. |
[ { "propose-data": propose-template, "hash": config-hash }, { "propose-data": propose-template, "hash": config-hash }, ... ] |
Posting a new config can be performed by any validator maintainer via private endpoint.
-
Propose transactions will only get submitted and executed with state change if all of the following conditions take place:
-
new config body constitutes a valid json string and corresponds to StoredConfiguration format.
-
previous_cfg_hash
in proposed config body equals to hash of actual config. -
actual_from
in proposed config body is greater than current height. current height is determined as the height of the last committed block + 1. This is important to obtain a sequential view of configs commit history. And, more important, the linear view of history of votes which conditioned scheduling of a config. -
a following config isn't already present.
-
actual config contains the node-sender public key in the
validators
field array, as specified infrom
field of the propose transaction. Thefrom
field is determined by the public key of the node whichpostpropose
endpoint is accessed for signing the transaction on maintainer's behalf. -
propose of config, which evaluates to the same hash, hasn't already been submitted.
-
-
Vote transactions will only get submitted and executed with state change if all of the following conditions take place:
-
the vote transaction references a config propose with known config hash.
-
a following config isn't already present.
-
actual config contains the node-sender's public key in
validators
field, as specified infrom
field of vote transaction. Thefrom
field is determined by public key of node whosepostvote
endpoint is accessed for signing the transaction on the maintainer's behalf. -
previous_cfg_hash
in the config propose, which is referenced by vote transaction, is equal to hash of actual config. -
actual_from
in the config propose, which is referenced by vote transaction, is greater than current height. -
no vote from the same node public key has been submitted previously.
-
Endpoint | HTTP method | Description | Response template |
---|---|---|---|
/api/services/configuration/v1/configs/postpropose |
POST | Post proposed config body | { "cfg_hash": configuration-hash, "tx_hash": transaction-hash } |
/api/services/configuration/v1/configs/postvote |
POST | Vote for a configuration having specific hash | { "tx_hash": transaction-hash } |
/api/services/configuration/v1/configs/postagainst |
POST | Vote against a configuration having specific hash | { "tx_hash": transaction-hash } |