@ -0,0 +1,139 @@ | |||
# Tendermint | |||
## Overview | |||
This is a quick start guide. If you have a vague idea about how Tendermint works | |||
and want to get started right away, continue. Otherwise, [review the documentation](http://tendermint.readthedocs.io/en/master/) | |||
## Install | |||
### Quick Install | |||
On a fresh Ubuntu 16.04 machine can be done with [this script](https://git.io/vNLfY), like so: | |||
``` | |||
curl -L https://git.io/vNLfY | bash | |||
source ~/.profile | |||
``` | |||
WARNING: do not run the above on your local machine. | |||
The script is also used to facilitate cluster deployment below. | |||
### Manual Install | |||
Requires: | |||
- `go` minimum version 1.9.2 | |||
- `$GOPATH` set and `$GOPATH/bin` on your $PATH (see https://github.com/tendermint/tendermint/wiki/Setting-GOPATH) | |||
To install Tendermint, run: | |||
``` | |||
go get github.com/tendermint/tendermint | |||
cd $GOPATH/src/github.com/tendermint/tendermint | |||
make get_vendor_deps | |||
make install | |||
``` | |||
Confirm installation: | |||
``` | |||
$ tendermint version | |||
0.15.0-381fe19 | |||
``` | |||
## Initialization | |||
Running: | |||
``` | |||
tendermint init | |||
``` | |||
will create the required files for a single, local node. | |||
These files are found in `$HOME/.tendermint`: | |||
``` | |||
$ ls $HOME/.tendermint | |||
config.toml data genesis.json priv_validator.json | |||
``` | |||
For a single, local node, no further configuration is required. | |||
Configuring a cluster is covered further below. | |||
## Local Node | |||
Start tendermint with a simple in-process application: | |||
``` | |||
tendermint node --proxy_app=dummy | |||
``` | |||
and blocks will start to stream in: | |||
``` | |||
I[01-06|01:45:15.592] Executed block module=state height=1 validTxs=0 invalidTxs=0 | |||
I[01-06|01:45:15.624] Committed state module=state height=1 txs=0 appHash= | |||
``` | |||
Check the status with: | |||
``` | |||
curl -s localhost:46657/status | |||
``` | |||
### Sending Transactions | |||
With the dummy app running, we can send transactions: | |||
``` | |||
curl -s 'localhost:46657/broadcast_tx_commit?tx="abcd"' | |||
``` | |||
and check that it worked with: | |||
``` | |||
curl -s 'localhost:46657/abci_query?data="abcd"' | |||
``` | |||
We can send transactions with a key:value store: | |||
``` | |||
curl -s 'localhost:46657/broadcast_tx_commit?tx="name=satoshi"' | |||
``` | |||
and query the key: | |||
``` | |||
curl -s 'localhost:46657/abci_query?data="name"' | |||
``` | |||
where the value is returned in hex. | |||
## Cluster of Nodes | |||
First create four Ubuntu cloud machines. The following was testing on Digital Ocean Ubuntu 16.04 x64 (3GB/1CPU, 20GB SSD). We'll refer to their respective IP addresses below as IP1, IP2, IP3, IP4. | |||
Then, `ssh` into each machine, and `curl` then execute [this script](https://git.io/vNLfY): | |||
``` | |||
curl -L https://git.io/vNLfY | bash | |||
source ~/.profile | |||
``` | |||
This will install `go` and other dependencies, get the Tendermint source code, then compile the `tendermint` binary. | |||
Next, `cd` into `docs/examples`. Each command below should be run from each node, in sequence: | |||
``` | |||
tendermint node --home ./node1 --proxy_app=dummy | |||
tendermint node --home ./node2 --proxy_app=dummy --p2p.seeds IP1:46656 | |||
tendermint node --home ./node3 --proxy_app=dummy --p2p.seeds IP1:46656,IP2:46656 | |||
tendermint node --home ./node4 --proxy_app=dummy --p2p.seeds IP1:46656,IP2:46656,IP3:46656 | |||
``` | |||
Note that after the third node is started, blocks will start to stream in because >2/3 of validators (defined in the `genesis.json` have come online). Seeds can also be specified in the `config.toml`. See [this PR](https://github.com/tendermint/tendermint/pull/792) for more information about configuration options. | |||
Transactions can then be sent as covered in the single, local node example above. |
@ -0,0 +1,32 @@ | |||
#!/usr/bin/env bash | |||
# XXX: this script is meant to be used only on a fresh Ubuntu 16.04 instance | |||
# and has only been tested on Digital Ocean | |||
# get and unpack golang | |||
curl -O https://storage.googleapis.com/golang/go1.9.2.linux-amd64.tar.gz | |||
tar -xvf go1.9.2.linux-amd64.tar.gz | |||
apt install make | |||
## move go and add binary to path | |||
mv go /usr/local | |||
echo "export PATH=\$PATH:/usr/local/go/bin" >> ~/.profile | |||
## create the GOPATH directory, set GOPATH and put on PATH | |||
mkdir goApps | |||
echo "export GOPATH=/root/goApps" >> ~/.profile | |||
echo "export PATH=\$PATH:\$GOPATH/bin" >> ~/.profile | |||
source ~/.profile | |||
## get the code and move into it | |||
REPO=github.com/tendermint/tendermint | |||
go get $REPO | |||
cd $GOPATH/src/$REPO | |||
## build | |||
git checkout v0.15.0 | |||
make get_tools | |||
make get_vendor_deps | |||
make install |
@ -0,0 +1,15 @@ | |||
# This is a TOML config file. | |||
# For more information, see https://github.com/toml-lang/toml | |||
proxy_app = "tcp://127.0.0.1:46658" | |||
moniker = "penguin" | |||
fast_sync = true | |||
db_backend = "leveldb" | |||
log_level = "state:info,*:error" | |||
[rpc] | |||
laddr = "tcp://0.0.0.0:46657" | |||
[p2p] | |||
laddr = "tcp://0.0.0.0:46656" | |||
seeds = "" |
@ -0,0 +1,42 @@ | |||
{ | |||
"genesis_time":"0001-01-01T00:00:00Z", | |||
"chain_id":"test-chain-wt7apy", | |||
"validators":[ | |||
{ | |||
"pub_key":{ | |||
"type":"ed25519", | |||
"data":"F08446C80A33E10D620E21450821B58D053778528F2B583D423B3E46EC647D30" | |||
}, | |||
"power":10, | |||
"name":"node1" | |||
} | |||
, | |||
{ | |||
"pub_key":{ | |||
"type":"ed25519", | |||
"data": "A8423F70A9E512643B4B00F7C3701ECAD1F31B0A1FAA45852C41046353B9A07F" | |||
}, | |||
"power":10, | |||
"name":"node2" | |||
} | |||
, | |||
{ | |||
"pub_key":{ | |||
"type":"ed25519", | |||
"data": "E52EFFAEDFE1D618ECDA71DE3B23592B3612CAABA0C10826E4C3120B2198C29A" | |||
}, | |||
"power":10, | |||
"name":"node3" | |||
} | |||
, | |||
{ | |||
"pub_key":{ | |||
"type":"ed25519", | |||
"data": "2B8FC09C07955A02998DFE5AF1AAD1C44115ECA7635FF51A867CF4265D347C07" | |||
}, | |||
"power":10, | |||
"name":"node4" | |||
} | |||
], | |||
"app_hash":"" | |||
} |
@ -0,0 +1,15 @@ | |||
{ | |||
"address":"4DC2756029CE0D8F8C6C3E4C3CE6EE8C30AF352F", | |||
"pub_key":{ | |||
"type":"ed25519", | |||
"data":"F08446C80A33E10D620E21450821B58D053778528F2B583D423B3E46EC647D30" | |||
}, | |||
"last_height":0, | |||
"last_round":0, | |||
"last_step":0, | |||
"last_signature":null, | |||
"priv_key":{ | |||
"type":"ed25519", | |||
"data":"4D3648E1D93C8703E436BFF814728B6BD270CFDFD686DF5385E8ACBEB7BE2D7DF08446C80A33E10D620E21450821B58D053778528F2B583D423B3E46EC647D30" | |||
} | |||
} |
@ -0,0 +1,15 @@ | |||
# This is a TOML config file. | |||
# For more information, see https://github.com/toml-lang/toml | |||
proxy_app = "tcp://127.0.0.1:46658" | |||
moniker = "penguin" | |||
fast_sync = true | |||
db_backend = "leveldb" | |||
log_level = "state:info,*:error" | |||
[rpc] | |||
laddr = "tcp://0.0.0.0:46657" | |||
[p2p] | |||
laddr = "tcp://0.0.0.0:46656" | |||
seeds = "" |
@ -0,0 +1,42 @@ | |||
{ | |||
"genesis_time":"0001-01-01T00:00:00Z", | |||
"chain_id":"test-chain-wt7apy", | |||
"validators":[ | |||
{ | |||
"pub_key":{ | |||
"type":"ed25519", | |||
"data":"F08446C80A33E10D620E21450821B58D053778528F2B583D423B3E46EC647D30" | |||
}, | |||
"power":10, | |||
"name":"node1" | |||
} | |||
, | |||
{ | |||
"pub_key":{ | |||
"type":"ed25519", | |||
"data": "A8423F70A9E512643B4B00F7C3701ECAD1F31B0A1FAA45852C41046353B9A07F" | |||
}, | |||
"power":10, | |||
"name":"node2" | |||
} | |||
, | |||
{ | |||
"pub_key":{ | |||
"type":"ed25519", | |||
"data": "E52EFFAEDFE1D618ECDA71DE3B23592B3612CAABA0C10826E4C3120B2198C29A" | |||
}, | |||
"power":10, | |||
"name":"node3" | |||
} | |||
, | |||
{ | |||
"pub_key":{ | |||
"type":"ed25519", | |||
"data": "2B8FC09C07955A02998DFE5AF1AAD1C44115ECA7635FF51A867CF4265D347C07" | |||
}, | |||
"power":10, | |||
"name":"node4" | |||
} | |||
], | |||
"app_hash":"" | |||
} |
@ -0,0 +1,15 @@ | |||
{ | |||
"address": "DD6C63A762608A9DDD4A845657743777F63121D6", | |||
"pub_key": { | |||
"type": "ed25519", | |||
"data": "A8423F70A9E512643B4B00F7C3701ECAD1F31B0A1FAA45852C41046353B9A07F" | |||
}, | |||
"last_height": 0, | |||
"last_round": 0, | |||
"last_step": 0, | |||
"last_signature": null, | |||
"priv_key": { | |||
"type": "ed25519", | |||
"data": "7B0DE666FF5E9B437D284BCE767F612381890C018B93B0A105D2E829A568DA6FA8423F70A9E512643B4B00F7C3701ECAD1F31B0A1FAA45852C41046353B9A07F" | |||
} | |||
} |
@ -0,0 +1,15 @@ | |||
# This is a TOML config file. | |||
# For more information, see https://github.com/toml-lang/toml | |||
proxy_app = "tcp://127.0.0.1:46658" | |||
moniker = "penguin" | |||
fast_sync = true | |||
db_backend = "leveldb" | |||
log_level = "state:info,*:error" | |||
[rpc] | |||
laddr = "tcp://0.0.0.0:46657" | |||
[p2p] | |||
laddr = "tcp://0.0.0.0:46656" | |||
seeds = "" |
@ -0,0 +1,42 @@ | |||
{ | |||
"genesis_time":"0001-01-01T00:00:00Z", | |||
"chain_id":"test-chain-wt7apy", | |||
"validators":[ | |||
{ | |||
"pub_key":{ | |||
"type":"ed25519", | |||
"data":"F08446C80A33E10D620E21450821B58D053778528F2B583D423B3E46EC647D30" | |||
}, | |||
"power":10, | |||
"name":"node1" | |||
} | |||
, | |||
{ | |||
"pub_key":{ | |||
"type":"ed25519", | |||
"data": "A8423F70A9E512643B4B00F7C3701ECAD1F31B0A1FAA45852C41046353B9A07F" | |||
}, | |||
"power":10, | |||
"name":"node2" | |||
} | |||
, | |||
{ | |||
"pub_key":{ | |||
"type":"ed25519", | |||
"data": "E52EFFAEDFE1D618ECDA71DE3B23592B3612CAABA0C10826E4C3120B2198C29A" | |||
}, | |||
"power":10, | |||
"name":"node3" | |||
} | |||
, | |||
{ | |||
"pub_key":{ | |||
"type":"ed25519", | |||
"data": "2B8FC09C07955A02998DFE5AF1AAD1C44115ECA7635FF51A867CF4265D347C07" | |||
}, | |||
"power":10, | |||
"name":"node4" | |||
} | |||
], | |||
"app_hash":"" | |||
} |
@ -0,0 +1,15 @@ | |||
{ | |||
"address": "6D6A1E313B407B5474106CA8759C976B777AB659", | |||
"pub_key": { | |||
"type": "ed25519", | |||
"data": "E52EFFAEDFE1D618ECDA71DE3B23592B3612CAABA0C10826E4C3120B2198C29A" | |||
}, | |||
"last_height": 0, | |||
"last_round": 0, | |||
"last_step": 0, | |||
"last_signature": null, | |||
"priv_key": { | |||
"type": "ed25519", | |||
"data": "622432A370111A5C25CFE121E163FE709C9D5C95F551EDBD7A2C69A8545C9B76E52EFFAEDFE1D618ECDA71DE3B23592B3612CAABA0C10826E4C3120B2198C29A" | |||
} | |||
} |
@ -0,0 +1,15 @@ | |||
# This is a TOML config file. | |||
# For more information, see https://github.com/toml-lang/toml | |||
proxy_app = "tcp://127.0.0.1:46658" | |||
moniker = "penguin" | |||
fast_sync = true | |||
db_backend = "leveldb" | |||
log_level = "state:info,*:error" | |||
[rpc] | |||
laddr = "tcp://0.0.0.0:46657" | |||
[p2p] | |||
laddr = "tcp://0.0.0.0:46656" | |||
seeds = "" |
@ -0,0 +1,42 @@ | |||
{ | |||
"genesis_time":"0001-01-01T00:00:00Z", | |||
"chain_id":"test-chain-wt7apy", | |||
"validators":[ | |||
{ | |||
"pub_key":{ | |||
"type":"ed25519", | |||
"data":"F08446C80A33E10D620E21450821B58D053778528F2B583D423B3E46EC647D30" | |||
}, | |||
"power":10, | |||
"name":"node1" | |||
} | |||
, | |||
{ | |||
"pub_key":{ | |||
"type":"ed25519", | |||
"data": "A8423F70A9E512643B4B00F7C3701ECAD1F31B0A1FAA45852C41046353B9A07F" | |||
}, | |||
"power":10, | |||
"name":"node2" | |||
} | |||
, | |||
{ | |||
"pub_key":{ | |||
"type":"ed25519", | |||
"data": "E52EFFAEDFE1D618ECDA71DE3B23592B3612CAABA0C10826E4C3120B2198C29A" | |||
}, | |||
"power":10, | |||
"name":"node3" | |||
} | |||
, | |||
{ | |||
"pub_key":{ | |||
"type":"ed25519", | |||
"data": "2B8FC09C07955A02998DFE5AF1AAD1C44115ECA7635FF51A867CF4265D347C07" | |||
}, | |||
"power":10, | |||
"name":"node4" | |||
} | |||
], | |||
"app_hash":"" | |||
} |
@ -0,0 +1,15 @@ | |||
{ | |||
"address": "829A9663611D3DD88A3D84EA0249679D650A0755", | |||
"pub_key": { | |||
"type": "ed25519", | |||
"data": "2B8FC09C07955A02998DFE5AF1AAD1C44115ECA7635FF51A867CF4265D347C07" | |||
}, | |||
"last_height": 0, | |||
"last_round": 0, | |||
"last_step": 0, | |||
"last_signature": null, | |||
"priv_key": { | |||
"type": "ed25519", | |||
"data": "0A604D1C9AE94A50150BF39E603239092F9392E4773F4D8F4AC1D86E6438E89E2B8FC09C07955A02998DFE5AF1AAD1C44115ECA7635FF51A867CF4265D347C07" | |||
} | |||
} |
@ -1,58 +1,173 @@ | |||
Configuration | |||
============= | |||
TendermintCore can be configured via a TOML file in | |||
``$TMHOME/config.toml``. Some of these parameters can be overridden by | |||
command-line flags. | |||
Config parameters | |||
~~~~~~~~~~~~~~~~~ | |||
The main config parameters are defined | |||
`here <https://github.com/tendermint/tendermint/blob/master/config/config.go>`__. | |||
- ``abci``: ABCI transport (socket \| grpc). *Default*: ``socket`` | |||
- ``db_backend``: Database backend for the blockchain and | |||
TendermintCore state. ``leveldb`` or ``memdb``. *Default*: | |||
``"leveldb"`` | |||
- ``db_dir``: Database dir. *Default*: ``"$TMHOME/data"`` | |||
- ``fast_sync``: Whether to sync faster from the block pool. *Default*: | |||
``true`` | |||
- ``genesis_file``: The location of the genesis file. *Default*: | |||
``"$TMHOME/genesis.json"`` | |||
- ``log_level``: *Default*: ``"state:info,*:error"`` | |||
- ``moniker``: Name of this node. *Default*: the host name or ``"anonymous"`` | |||
if runtime fails to get the host name | |||
- ``priv_validator_file``: Validator private key file. *Default*: | |||
``"$TMHOME/priv_validator.json"`` | |||
- ``prof_laddr``: Profile listen address. *Default*: ``""`` | |||
- ``proxy_app``: The ABCI app endpoint. *Default*: | |||
``"tcp://127.0.0.1:46658"`` | |||
- ``consensus.max_block_size_txs``: Maximum number of block txs. | |||
*Default*: ``10000`` | |||
- ``consensus.create_empty_blocks``: Create empty blocks w/o txs. | |||
*Default*: ``true`` | |||
- ``consensus.create_empty_blocks_interval``: Block creation interval, even if empty. | |||
- ``consensus.timeout_*``: Various consensus timeout parameters | |||
- ``consensus.wal_file``: Consensus state WAL. *Default*: | |||
``"$TMHOME/data/cs.wal/wal"`` | |||
- ``consensus.wal_light``: Whether to use light-mode for Consensus | |||
state WAL. *Default*: ``false`` | |||
- ``mempool.*``: Various mempool parameters | |||
- ``p2p.addr_book_file``: Peer address book. *Default*: | |||
``"$TMHOME/addrbook.json"``. **NOT USED** | |||
- ``p2p.laddr``: Node listen address. (0.0.0.0:0 means any interface, | |||
any port). *Default*: ``"0.0.0.0:46656"`` | |||
- ``p2p.pex``: Enable Peer-Exchange (dev feature). *Default*: ``false`` | |||
- ``p2p.seeds``: Comma delimited host:port seed nodes. *Default*: | |||
``""`` | |||
- ``p2p.skip_upnp``: Skip UPNP detection. *Default*: ``false`` | |||
- ``rpc.grpc_laddr``: GRPC listen address (BroadcastTx only). Port | |||
required. *Default*: ``""`` | |||
- ``rpc.laddr``: RPC listen address. Port required. *Default*: | |||
``"0.0.0.0:46657"`` | |||
- ``rpc.unsafe``: Enabled unsafe rpc methods. *Default*: ``true`` | |||
Tendermint Core can be configured via a TOML file in | |||
``$TMHOME/config/config.toml``. Some of these parameters can be overridden by | |||
command-line flags. For most users, the options in the ``##### main | |||
base configuration options #####`` are intended to be modified while | |||
config options further below are intended for advance power users. | |||
Config options | |||
~~~~~~~~~~~~~~ | |||
The default configuration file create by ``tendermint init`` has all | |||
the parameters set with their default values. It will look something | |||
like the file below, however, double check by inspecting the | |||
``config.toml`` created with your version of ``tendermint`` installed: | |||
:: | |||
# This is a TOML config file. | |||
# For more information, see https://github.com/toml-lang/toml | |||
##### main base config options ##### | |||
# TCP or UNIX socket address of the ABCI application, | |||
# or the name of an ABCI application compiled in with the Tendermint binary | |||
proxy_app = "tcp://127.0.0.1:46658" | |||
# A custom human readable name for this node | |||
moniker = "anonymous" | |||
# If this node is many blocks behind the tip of the chain, FastSync | |||
# allows them to catchup quickly by downloading blocks in parallel | |||
# and verifying their commits | |||
fast_sync = true | |||
# Database backend: leveldb | memdb | |||
db_backend = "leveldb" | |||
# Database directory | |||
db_path = "data" | |||
# Output level for logging | |||
log_level = "state:info,*:error" | |||
##### additional base config options ##### | |||
# The ID of the chain to join (should be signed with every transaction and vote) | |||
chain_id = "" | |||
# Path to the JSON file containing the initial validator set and other meta data | |||
genesis_file = "genesis.json" | |||
# Path to the JSON file containing the private key to use as a validator in the consensus protocol | |||
priv_validator_file = "priv_validator.json" | |||
# Mechanism to connect to the ABCI application: socket | grpc | |||
abci = "socket" | |||
# TCP or UNIX socket address for the profiling server to listen on | |||
prof_laddr = "" | |||
# If true, query the ABCI app on connecting to a new peer | |||
# so the app can decide if we should keep the connection or not | |||
filter_peers = false | |||
##### advanced configuration options ##### | |||
##### rpc server configuration options ##### | |||
[rpc] | |||
# TCP or UNIX socket address for the RPC server to listen on | |||
laddr = "tcp://0.0.0.0:46657" | |||
# TCP or UNIX socket address for the gRPC server to listen on | |||
# NOTE: This server only supports /broadcast_tx_commit | |||
grpc_laddr = "" | |||
# Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool | |||
unsafe = false | |||
##### peer to peer configuration options ##### | |||
[p2p] | |||
# Address to listen for incoming connections | |||
laddr = "tcp://0.0.0.0:46656" | |||
# Comma separated list of seed nodes to connect to | |||
seeds = "" | |||
# Comma separated list of nodes to keep persistent connections to | |||
persistent_peers = "" | |||
# Path to address book | |||
addr_book_file = "addrbook.json" | |||
# Set true for strict address routability rules | |||
addr_book_strict = true | |||
# Time to wait before flushing messages out on the connection, in ms | |||
flush_throttle_timeout = 100 | |||
# Maximum number of peers to connect to | |||
max_num_peers = 50 | |||
# Maximum size of a message packet payload, in bytes | |||
max_msg_packet_payload_size = 1024 | |||
# Rate at which packets can be sent, in bytes/second | |||
send_rate = 512000 | |||
# Rate at which packets can be received, in bytes/second | |||
recv_rate = 512000 | |||
##### mempool configuration options ##### | |||
[mempool] | |||
recheck = true | |||
recheck_empty = true | |||
broadcast = true | |||
wal_dir = "data/mempool.wal" | |||
##### consensus configuration options ##### | |||
[consensus] | |||
wal_file = "data/cs.wal/wal" | |||
wal_light = false | |||
# All timeouts are in milliseconds | |||
timeout_propose = 3000 | |||
timeout_propose_delta = 500 | |||
timeout_prevote = 1000 | |||
timeout_prevote_delta = 500 | |||
timeout_precommit = 1000 | |||
timeout_precommit_delta = 500 | |||
timeout_commit = 1000 | |||
# Make progress as soon as we have all the precommits (as if TimeoutCommit = 0) | |||
skip_timeout_commit = false | |||
# BlockSize | |||
max_block_size_txs = 10000 | |||
max_block_size_bytes = 1 | |||
# EmptyBlocks mode and possible interval between empty blocks in seconds | |||
create_empty_blocks = true | |||
create_empty_blocks_interval = 0 | |||
# Reactor sleep duration parameters are in milliseconds | |||
peer_gossip_sleep_duration = 100 | |||
peer_query_maj23_sleep_duration = 2000 | |||
##### transactions indexer configuration options ##### | |||
[tx_index] | |||
# What indexer to use for transactions | |||
# | |||
# Options: | |||
# 1) "null" (default) | |||
# 2) "kv" - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend). | |||
indexer = "{{ .TxIndex.Indexer }}" | |||
# Comma-separated list of tags to index (by default the only tag is tx hash) | |||
# | |||
# It's recommended to index only a subset of tags due to possible memory | |||
# bloat. This is, of course, depends on the indexer's DB and the volume of | |||
# transactions. | |||
index_tags = "{{ .TxIndex.IndexTags }}" | |||
# When set to true, tells indexer to index all tags. Note this may be not | |||
# desirable (see the comment above). IndexTags has a precedence over | |||
# IndexAllTags (i.e. when given both, IndexTags will be indexed). | |||
index_all_tags = {{ .TxIndex.IndexAllTags }} |
@ -1,155 +0,0 @@ | |||
package lite | |||
import ( | |||
"github.com/tendermint/tendermint/types" | |||
liteErr "github.com/tendermint/tendermint/lite/errors" | |||
) | |||
// Inquiring wraps a dynamic certifier and implements an auto-update strategy. If a call to Certify | |||
// fails due to a change it validator set, Inquiring will try and find a previous FullCommit which | |||
// it can use to safely update the validator set. It uses a source provider to obtain the needed | |||
// FullCommits. It stores properly validated data on the local system. | |||
type Inquiring struct { | |||
cert *Dynamic | |||
// These are only properly validated data, from local system | |||
trusted Provider | |||
// This is a source of new info, like a node rpc, or other import method | |||
Source Provider | |||
} | |||
// NewInquiring returns a new Inquiring object. It uses the trusted provider to store validated | |||
// data and the source provider to obtain missing FullCommits. | |||
// | |||
// Example: The trusted provider should a CacheProvider, MemProvider or files.Provider. The source | |||
// provider should be a client.HTTPProvider. | |||
func NewInquiring(chainID string, fc FullCommit, trusted Provider, source Provider) *Inquiring { | |||
// store the data in trusted | |||
// TODO: StoredCommit() can return an error and we need to handle this. | |||
trusted.StoreCommit(fc) | |||
return &Inquiring{ | |||
cert: NewDynamic(chainID, fc.Validators, fc.Height()), | |||
trusted: trusted, | |||
Source: source, | |||
} | |||
} | |||
// ChainID returns the chain id. | |||
func (c *Inquiring) ChainID() string { | |||
return c.cert.ChainID() | |||
} | |||
// Validators returns the validator set. | |||
func (c *Inquiring) Validators() *types.ValidatorSet { | |||
return c.cert.cert.vSet | |||
} | |||
// LastHeight returns the last height. | |||
func (c *Inquiring) LastHeight() int64 { | |||
return c.cert.lastHeight | |||
} | |||
// Certify makes sure this is checkpoint is valid. | |||
// | |||
// If the validators have changed since the last know time, it looks | |||
// for a path to prove the new validators. | |||
// | |||
// On success, it will store the checkpoint in the store for later viewing | |||
func (c *Inquiring) Certify(commit Commit) error { | |||
err := c.useClosestTrust(commit.Height()) | |||
if err != nil { | |||
return err | |||
} | |||
err = c.cert.Certify(commit) | |||
if !liteErr.IsValidatorsChangedErr(err) { | |||
return err | |||
} | |||
err = c.updateToHash(commit.Header.ValidatorsHash) | |||
if err != nil { | |||
return err | |||
} | |||
err = c.cert.Certify(commit) | |||
if err != nil { | |||
return err | |||
} | |||
// store the new checkpoint | |||
return c.trusted.StoreCommit(NewFullCommit(commit, c.Validators())) | |||
} | |||
// Update will verify if this is a valid change and update | |||
// the certifying validator set if safe to do so. | |||
func (c *Inquiring) Update(fc FullCommit) error { | |||
err := c.useClosestTrust(fc.Height()) | |||
if err != nil { | |||
return err | |||
} | |||
err = c.cert.Update(fc) | |||
if err == nil { | |||
err = c.trusted.StoreCommit(fc) | |||
} | |||
return err | |||
} | |||
func (c *Inquiring) useClosestTrust(h int64) error { | |||
closest, err := c.trusted.GetByHeight(h) | |||
if err != nil { | |||
return err | |||
} | |||
// if the best seed is not the one we currently use, | |||
// let's just reset the dynamic validator | |||
if closest.Height() != c.LastHeight() { | |||
c.cert = NewDynamic(c.ChainID(), closest.Validators, closest.Height()) | |||
} | |||
return nil | |||
} | |||
// updateToHash gets the validator hash we want to update to | |||
// if IsTooMuchChangeErr, we try to find a path by binary search over height | |||
func (c *Inquiring) updateToHash(vhash []byte) error { | |||
// try to get the match, and update | |||
fc, err := c.Source.GetByHash(vhash) | |||
if err != nil { | |||
return err | |||
} | |||
err = c.cert.Update(fc) | |||
// handle IsTooMuchChangeErr by using divide and conquer | |||
if liteErr.IsTooMuchChangeErr(err) { | |||
err = c.updateToHeight(fc.Height()) | |||
} | |||
return err | |||
} | |||
// updateToHeight will use divide-and-conquer to find a path to h | |||
func (c *Inquiring) updateToHeight(h int64) error { | |||
// try to update to this height (with checks) | |||
fc, err := c.Source.GetByHeight(h) | |||
if err != nil { | |||
return err | |||
} | |||
start, end := c.LastHeight(), fc.Height() | |||
if end <= start { | |||
return liteErr.ErrNoPathFound() | |||
} | |||
err = c.Update(fc) | |||
// we can handle IsTooMuchChangeErr specially | |||
if !liteErr.IsTooMuchChangeErr(err) { | |||
return err | |||
} | |||
// try to update to mid | |||
mid := (start + end) / 2 | |||
err = c.updateToHeight(mid) | |||
if err != nil { | |||
return err | |||
} | |||
// if we made it to mid, we recurse | |||
return c.updateToHeight(h) | |||
} |
@ -0,0 +1,163 @@ | |||
package lite | |||
import ( | |||
"github.com/tendermint/tendermint/types" | |||
liteErr "github.com/tendermint/tendermint/lite/errors" | |||
) | |||
var _ Certifier = (*InquiringCertifier)(nil) | |||
// InquiringCertifier wraps a dynamic certifier and implements an auto-update strategy. If a call | |||
// to Certify fails due to a change it validator set, InquiringCertifier will try and find a | |||
// previous FullCommit which it can use to safely update the validator set. It uses a source | |||
// provider to obtain the needed FullCommits. It stores properly validated data on the local system. | |||
type InquiringCertifier struct { | |||
cert *DynamicCertifier | |||
// These are only properly validated data, from local system | |||
trusted Provider | |||
// This is a source of new info, like a node rpc, or other import method | |||
Source Provider | |||
} | |||
// NewInquiringCertifier returns a new Inquiring object. It uses the trusted provider to store | |||
// validated data and the source provider to obtain missing FullCommits. | |||
// | |||
// Example: The trusted provider should a CacheProvider, MemProvider or files.Provider. The source | |||
// provider should be a client.HTTPProvider. | |||
func NewInquiringCertifier(chainID string, fc FullCommit, trusted Provider, | |||
source Provider) (*InquiringCertifier, error) { | |||
// store the data in trusted | |||
err := trusted.StoreCommit(fc) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return &InquiringCertifier{ | |||
cert: NewDynamicCertifier(chainID, fc.Validators, fc.Height()), | |||
trusted: trusted, | |||
Source: source, | |||
}, nil | |||
} | |||
// ChainID returns the chain id. | |||
// Implements Certifier. | |||
func (ic *InquiringCertifier) ChainID() string { | |||
return ic.cert.ChainID() | |||
} | |||
// Validators returns the validator set. | |||
func (ic *InquiringCertifier) Validators() *types.ValidatorSet { | |||
return ic.cert.cert.vSet | |||
} | |||
// LastHeight returns the last height. | |||
func (ic *InquiringCertifier) LastHeight() int64 { | |||
return ic.cert.lastHeight | |||
} | |||
// Certify makes sure this is checkpoint is valid. | |||
// | |||
// If the validators have changed since the last know time, it looks | |||
// for a path to prove the new validators. | |||
// | |||
// On success, it will store the checkpoint in the store for later viewing | |||
// Implements Certifier. | |||
func (ic *InquiringCertifier) Certify(commit Commit) error { | |||
err := ic.useClosestTrust(commit.Height()) | |||
if err != nil { | |||
return err | |||
} | |||
err = ic.cert.Certify(commit) | |||
if !liteErr.IsValidatorsChangedErr(err) { | |||
return err | |||
} | |||
err = ic.updateToHash(commit.Header.ValidatorsHash) | |||
if err != nil { | |||
return err | |||
} | |||
err = ic.cert.Certify(commit) | |||
if err != nil { | |||
return err | |||
} | |||
// store the new checkpoint | |||
return ic.trusted.StoreCommit(NewFullCommit(commit, ic.Validators())) | |||
} | |||
// Update will verify if this is a valid change and update | |||
// the certifying validator set if safe to do so. | |||
func (ic *InquiringCertifier) Update(fc FullCommit) error { | |||
err := ic.useClosestTrust(fc.Height()) | |||
if err != nil { | |||
return err | |||
} | |||
err = ic.cert.Update(fc) | |||
if err == nil { | |||
err = ic.trusted.StoreCommit(fc) | |||
} | |||
return err | |||
} | |||
func (ic *InquiringCertifier) useClosestTrust(h int64) error { | |||
closest, err := ic.trusted.GetByHeight(h) | |||
if err != nil { | |||
return err | |||
} | |||
// if the best seed is not the one we currently use, | |||
// let's just reset the dynamic validator | |||
if closest.Height() != ic.LastHeight() { | |||
ic.cert = NewDynamicCertifier(ic.ChainID(), closest.Validators, closest.Height()) | |||
} | |||
return nil | |||
} | |||
// updateToHash gets the validator hash we want to update to | |||
// if IsTooMuchChangeErr, we try to find a path by binary search over height | |||
func (ic *InquiringCertifier) updateToHash(vhash []byte) error { | |||
// try to get the match, and update | |||
fc, err := ic.Source.GetByHash(vhash) | |||
if err != nil { | |||
return err | |||
} | |||
err = ic.cert.Update(fc) | |||
// handle IsTooMuchChangeErr by using divide and conquer | |||
if liteErr.IsTooMuchChangeErr(err) { | |||
err = ic.updateToHeight(fc.Height()) | |||
} | |||
return err | |||
} | |||
// updateToHeight will use divide-and-conquer to find a path to h | |||
func (ic *InquiringCertifier) updateToHeight(h int64) error { | |||
// try to update to this height (with checks) | |||
fc, err := ic.Source.GetByHeight(h) | |||
if err != nil { | |||
return err | |||
} | |||
start, end := ic.LastHeight(), fc.Height() | |||
if end <= start { | |||
return liteErr.ErrNoPathFound() | |||
} | |||
err = ic.Update(fc) | |||
// we can handle IsTooMuchChangeErr specially | |||
if !liteErr.IsTooMuchChangeErr(err) { | |||
return err | |||
} | |||
// try to update to mid | |||
mid := (start + end) / 2 | |||
err = ic.updateToHeight(mid) | |||
if err != nil { | |||
return err | |||
} | |||
// if we made it to mid, we recurse | |||
return ic.updateToHeight(h) | |||
} |
@ -0,0 +1,12 @@ | |||
#! /bin/bash | |||
set -eu | |||
N=$1 | |||
cd "$GOPATH/src/github.com/tendermint/tendermint" | |||
persistent_peers="$(test/p2p/ip.sh 1):46656" | |||
for i in $(seq 2 $N); do | |||
persistent_peers="$persistent_peers,$(test/p2p/ip.sh $i):46656" | |||
done | |||
echo "$persistent_peers" |
@ -1,12 +0,0 @@ | |||
#! /bin/bash | |||
set -eu | |||
N=$1 | |||
cd "$GOPATH/src/github.com/tendermint/tendermint" | |||
seeds="$(test/p2p/ip.sh 1):46656" | |||
for i in $(seq 2 $N); do | |||
seeds="$seeds,$(test/p2p/ip.sh $i):46656" | |||
done | |||
echo "$seeds" |