Merge pull request #1903 from tendermint/zach/lint-md
lint the docs markdown & fix codeblocks for website
This commit is contained in:
commit
82a5e9604c
186
docs/abci-cli.md
186
docs/abci-cli.md
|
@ -10,21 +10,26 @@ Make sure you [have Go installed](https://golang.org/doc/install).
|
|||
|
||||
Next, install the `abci-cli` tool and example applications:
|
||||
|
||||
go get github.com/tendermint/tendermint
|
||||
```
|
||||
go get github.com/tendermint/tendermint
|
||||
```
|
||||
|
||||
to get vendored dependencies:
|
||||
|
||||
cd $GOPATH/src/github.com/tendermint/tendermint
|
||||
make get_tools
|
||||
make get_vendor_deps
|
||||
make install_abci
|
||||
```
|
||||
cd $GOPATH/src/github.com/tendermint/tendermint
|
||||
make get_tools
|
||||
make get_vendor_deps
|
||||
make install_abci
|
||||
```
|
||||
|
||||
Now run `abci-cli` to see the list of commands:
|
||||
|
||||
Usage:
|
||||
```
|
||||
Usage:
|
||||
abci-cli [command]
|
||||
|
||||
Available Commands:
|
||||
Available Commands:
|
||||
batch Run a batch of abci commands against an application
|
||||
check_tx Validate a tx
|
||||
commit Commit the application state and return the Merkle root hash
|
||||
|
@ -38,13 +43,14 @@ Now run `abci-cli` to see the list of commands:
|
|||
query Query the application state
|
||||
set_option Set an options on the application
|
||||
|
||||
Flags:
|
||||
Flags:
|
||||
--abci string socket or grpc (default "socket")
|
||||
--address string address of application socket (default "tcp://127.0.0.1:26658")
|
||||
-h, --help help for abci-cli
|
||||
-v, --verbose print the command and results as if it were a console session
|
||||
|
||||
Use "abci-cli [command] --help" for more information about a command.
|
||||
Use "abci-cli [command] --help" for more information about a command.
|
||||
```
|
||||
|
||||
## KVStore - First Example
|
||||
|
||||
|
@ -63,7 +69,8 @@ Its code can be found
|
|||
[here](https://github.com/tendermint/tendermint/blob/develop/abci/cmd/abci-cli/abci-cli.go)
|
||||
and looks like:
|
||||
|
||||
func cmdKVStore(cmd *cobra.Command, args []string) error {
|
||||
```
|
||||
func cmdKVStore(cmd *cobra.Command, args []string) error {
|
||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
|
||||
|
||||
// Create the application - in memory or persisted to disk
|
||||
|
@ -91,26 +98,35 @@ and looks like:
|
|||
srv.Stop()
|
||||
})
|
||||
return nil
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Start by running:
|
||||
|
||||
abci-cli kvstore
|
||||
```
|
||||
abci-cli kvstore
|
||||
```
|
||||
|
||||
And in another terminal, run
|
||||
|
||||
abci-cli echo hello
|
||||
abci-cli info
|
||||
```
|
||||
abci-cli echo hello
|
||||
abci-cli info
|
||||
```
|
||||
|
||||
You'll see something like:
|
||||
|
||||
-> data: hello
|
||||
-> data.hex: 68656C6C6F
|
||||
```
|
||||
-> data: hello
|
||||
-> data.hex: 68656C6C6F
|
||||
```
|
||||
|
||||
and:
|
||||
|
||||
-> data: {"size":0}
|
||||
-> data.hex: 7B2273697A65223A307D
|
||||
```
|
||||
-> data: {"size":0}
|
||||
-> data.hex: 7B2273697A65223A307D
|
||||
```
|
||||
|
||||
An ABCI application must provide two things:
|
||||
|
||||
|
@ -144,52 +160,54 @@ speaking ABCI messages to your application.
|
|||
|
||||
Try running these commands:
|
||||
|
||||
> echo hello
|
||||
-> code: OK
|
||||
-> data: hello
|
||||
-> data.hex: 0x68656C6C6F
|
||||
```
|
||||
> echo hello
|
||||
-> code: OK
|
||||
-> data: hello
|
||||
-> data.hex: 0x68656C6C6F
|
||||
|
||||
> info
|
||||
-> code: OK
|
||||
-> data: {"size":0}
|
||||
-> data.hex: 0x7B2273697A65223A307D
|
||||
> info
|
||||
-> code: OK
|
||||
-> data: {"size":0}
|
||||
-> data.hex: 0x7B2273697A65223A307D
|
||||
|
||||
> commit
|
||||
-> code: OK
|
||||
-> data.hex: 0x0000000000000000
|
||||
> commit
|
||||
-> code: OK
|
||||
-> data.hex: 0x0000000000000000
|
||||
|
||||
> deliver_tx "abc"
|
||||
-> code: OK
|
||||
> deliver_tx "abc"
|
||||
-> code: OK
|
||||
|
||||
> info
|
||||
-> code: OK
|
||||
-> data: {"size":1}
|
||||
-> data.hex: 0x7B2273697A65223A317D
|
||||
> info
|
||||
-> code: OK
|
||||
-> data: {"size":1}
|
||||
-> data.hex: 0x7B2273697A65223A317D
|
||||
|
||||
> commit
|
||||
-> code: OK
|
||||
-> data.hex: 0x0200000000000000
|
||||
> commit
|
||||
-> code: OK
|
||||
-> data.hex: 0x0200000000000000
|
||||
|
||||
> query "abc"
|
||||
-> code: OK
|
||||
-> log: exists
|
||||
-> height: 0
|
||||
-> value: abc
|
||||
-> value.hex: 616263
|
||||
> query "abc"
|
||||
-> code: OK
|
||||
-> log: exists
|
||||
-> height: 0
|
||||
-> value: abc
|
||||
-> value.hex: 616263
|
||||
|
||||
> deliver_tx "def=xyz"
|
||||
-> code: OK
|
||||
> deliver_tx "def=xyz"
|
||||
-> code: OK
|
||||
|
||||
> commit
|
||||
-> code: OK
|
||||
-> data.hex: 0x0400000000000000
|
||||
> commit
|
||||
-> code: OK
|
||||
-> data.hex: 0x0400000000000000
|
||||
|
||||
> query "def"
|
||||
-> code: OK
|
||||
-> log: exists
|
||||
-> height: 0
|
||||
-> value: xyz
|
||||
-> value.hex: 78797A
|
||||
> query "def"
|
||||
-> code: OK
|
||||
-> log: exists
|
||||
-> height: 0
|
||||
-> value: xyz
|
||||
-> value.hex: 78797A
|
||||
```
|
||||
|
||||
Note that if we do `deliver_tx "abc"` it will store `(abc, abc)`, but if
|
||||
we do `deliver_tx "abc=efg"` it will store `(abc, efg)`.
|
||||
|
@ -206,7 +224,8 @@ Like the kvstore app, its code can be found
|
|||
[here](https://github.com/tendermint/tendermint/blob/master/abci/cmd/abci-cli/abci-cli.go)
|
||||
and looks like:
|
||||
|
||||
func cmdCounter(cmd *cobra.Command, args []string) error {
|
||||
```
|
||||
func cmdCounter(cmd *cobra.Command, args []string) error {
|
||||
|
||||
app := counter.NewCounterApplication(flagSerial)
|
||||
|
||||
|
@ -228,7 +247,8 @@ and looks like:
|
|||
srv.Stop()
|
||||
})
|
||||
return nil
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The counter app doesn't use a Merkle tree, it just counts how many times
|
||||
we've sent a transaction, asked for a hash, or committed the state. The
|
||||
|
@ -256,38 +276,42 @@ whose integer is greater than the last committed one.
|
|||
Let's kill the console and the kvstore application, and start the
|
||||
counter app:
|
||||
|
||||
abci-cli counter
|
||||
```
|
||||
abci-cli counter
|
||||
```
|
||||
|
||||
In another window, start the `abci-cli console`:
|
||||
|
||||
> set_option serial on
|
||||
-> code: OK
|
||||
-> log: OK (SetOption doesn't return anything.)
|
||||
```
|
||||
> set_option serial on
|
||||
-> code: OK
|
||||
-> log: OK (SetOption doesn't return anything.)
|
||||
|
||||
> check_tx 0x00
|
||||
-> code: OK
|
||||
> check_tx 0x00
|
||||
-> code: OK
|
||||
|
||||
> check_tx 0xff
|
||||
-> code: OK
|
||||
> check_tx 0xff
|
||||
-> code: OK
|
||||
|
||||
> deliver_tx 0x00
|
||||
-> code: OK
|
||||
> deliver_tx 0x00
|
||||
-> code: OK
|
||||
|
||||
> check_tx 0x00
|
||||
-> code: BadNonce
|
||||
-> log: Invalid nonce. Expected >= 1, got 0
|
||||
> check_tx 0x00
|
||||
-> code: BadNonce
|
||||
-> log: Invalid nonce. Expected >= 1, got 0
|
||||
|
||||
> deliver_tx 0x01
|
||||
-> code: OK
|
||||
> deliver_tx 0x01
|
||||
-> code: OK
|
||||
|
||||
> deliver_tx 0x04
|
||||
-> code: BadNonce
|
||||
-> log: Invalid nonce. Expected 2, got 4
|
||||
> deliver_tx 0x04
|
||||
-> code: BadNonce
|
||||
-> log: Invalid nonce. Expected 2, got 4
|
||||
|
||||
> info
|
||||
-> code: OK
|
||||
-> data: {"hashes":0,"txs":2}
|
||||
-> data.hex: 0x7B22686173686573223A302C22747873223A327D
|
||||
> info
|
||||
-> code: OK
|
||||
-> data: {"hashes":0,"txs":2}
|
||||
-> data.hex: 0x7B22686173686573223A302C22747873223A327D
|
||||
```
|
||||
|
||||
This is a very simple application, but between `counter` and `kvstore`,
|
||||
its easy to see how you can build out arbitrary application states on
|
||||
|
@ -304,7 +328,9 @@ example directory](https://github.com/tendermint/tendermint/tree/develop/abci/ex
|
|||
|
||||
To run the Node JS version, `cd` to `example/js` and run
|
||||
|
||||
node app.js
|
||||
```
|
||||
node app.js
|
||||
```
|
||||
|
||||
(you'll have to kill the other counter application process). In another
|
||||
window, run the console and those previous ABCI commands. You should get
|
||||
|
|
|
@ -17,7 +17,7 @@ transaction is actually processed.
|
|||
|
||||
The ABCI application must be a deterministic result of the Tendermint
|
||||
consensus - any external influence on the application state that didn't
|
||||
come through Tendermint could cause a consensus failure. Thus *nothing*
|
||||
come through Tendermint could cause a consensus failure. Thus _nothing_
|
||||
should communicate with the application except Tendermint via ABCI.
|
||||
|
||||
If the application is written in Go, it can be compiled into the
|
||||
|
@ -43,6 +43,7 @@ all transactions, and possibly all queries, should still pass through
|
|||
Tendermint.
|
||||
|
||||
See the following for more extensive documentation:
|
||||
|
||||
- [Interchain Standard for the Light-Client REST API](https://github.com/cosmos/cosmos-sdk/pull/1028)
|
||||
- [Tendermint RPC Docs](https://tendermint.github.io/slate/)
|
||||
- [Tendermint in Production](https://github.com/tendermint/tendermint/pull/1618)
|
||||
|
|
|
@ -34,8 +34,7 @@ The ABCI design has a few distinct components:
|
|||
only uses `CheckTx`
|
||||
- [consensus connection](#consensus-connection): for executing
|
||||
transactions that have been committed. Message sequence is
|
||||
-for every block
|
||||
-`BeginBlock, [DeliverTx, ...], EndBlock, Commit`
|
||||
-for every block -`BeginBlock, [DeliverTx, ...], EndBlock, Commit`
|
||||
- [query connection](#query-connection): for querying the
|
||||
application state; only uses Query and Info
|
||||
|
||||
|
@ -161,7 +160,7 @@ connection, to query the local state of the app.
|
|||
|
||||
### Mempool Connection
|
||||
|
||||
The mempool connection is used *only* for CheckTx requests. Transactions
|
||||
The mempool connection is used _only_ for CheckTx requests. Transactions
|
||||
are run using CheckTx in the same order they were received by the
|
||||
validator. If the CheckTx returns `OK`, the transaction is kept in
|
||||
memory and relayed to other peers in the same order it was received.
|
||||
|
@ -180,13 +179,16 @@ mempool state (this behaviour can be turned off with
|
|||
|
||||
In go:
|
||||
|
||||
func (app *KVStoreApplication) CheckTx(tx []byte) types.Result {
|
||||
```
|
||||
func (app *KVStoreApplication) CheckTx(tx []byte) types.Result {
|
||||
return types.OK
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In Java:
|
||||
|
||||
ResponseCheckTx requestCheckTx(RequestCheckTx req) {
|
||||
```
|
||||
ResponseCheckTx requestCheckTx(RequestCheckTx req) {
|
||||
byte[] transaction = req.getTx().toByteArray();
|
||||
|
||||
// validate transaction
|
||||
|
@ -196,7 +198,8 @@ In Java:
|
|||
} else {
|
||||
return ResponseCheckTx.newBuilder().setCode(CodeType.OK).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Replay Protection
|
||||
|
||||
|
@ -242,8 +245,9 @@ merkle root of the data returned by the DeliverTx requests, or both.
|
|||
|
||||
In go:
|
||||
|
||||
// tx is either "key=value" or just arbitrary bytes
|
||||
func (app *KVStoreApplication) DeliverTx(tx []byte) types.Result {
|
||||
```
|
||||
// tx is either "key=value" or just arbitrary bytes
|
||||
func (app *KVStoreApplication) DeliverTx(tx []byte) types.Result {
|
||||
parts := strings.Split(string(tx), "=")
|
||||
if len(parts) == 2 {
|
||||
app.state.Set([]byte(parts[0]), []byte(parts[1]))
|
||||
|
@ -251,14 +255,16 @@ In go:
|
|||
app.state.Set(tx, tx)
|
||||
}
|
||||
return types.OK
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In Java:
|
||||
|
||||
/**
|
||||
```
|
||||
/**
|
||||
* Using Protobuf types from the protoc compiler, we always start with a byte[]
|
||||
*/
|
||||
ResponseDeliverTx deliverTx(RequestDeliverTx request) {
|
||||
ResponseDeliverTx deliverTx(RequestDeliverTx request) {
|
||||
byte[] transaction = request.getTx().toByteArray();
|
||||
|
||||
// validate your transaction
|
||||
|
@ -269,7 +275,8 @@ In Java:
|
|||
ResponseDeliverTx.newBuilder().setCode(CodeType.OK).build();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Commit
|
||||
|
||||
|
@ -277,8 +284,8 @@ Once all processing of the block is complete, Tendermint sends the
|
|||
Commit request and blocks waiting for a response. While the mempool may
|
||||
run concurrently with block processing (the BeginBlock, DeliverTxs, and
|
||||
EndBlock), it is locked for the Commit request so that its state can be
|
||||
safely reset during Commit. This means the app *MUST NOT* do any
|
||||
blocking communication with the mempool (ie. broadcast\_tx) during
|
||||
safely reset during Commit. This means the app _MUST NOT_ do any
|
||||
blocking communication with the mempool (ie. broadcast_tx) during
|
||||
Commit, or there will be deadlock. Note also that all remaining
|
||||
transactions in the mempool are replayed on the mempool connection
|
||||
(CheckTx) following a commit.
|
||||
|
@ -294,21 +301,25 @@ job of the [Handshake](#handshake).
|
|||
|
||||
In go:
|
||||
|
||||
func (app *KVStoreApplication) Commit() types.Result {
|
||||
```
|
||||
func (app *KVStoreApplication) Commit() types.Result {
|
||||
hash := app.state.Hash()
|
||||
return types.NewResultOK(hash, "")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In Java:
|
||||
|
||||
ResponseCommit requestCommit(RequestCommit requestCommit) {
|
||||
```
|
||||
ResponseCommit requestCommit(RequestCommit requestCommit) {
|
||||
|
||||
// update the internal app-state
|
||||
byte[] newAppState = calculateAppState();
|
||||
|
||||
// and return it to the node
|
||||
return ResponseCommit.newBuilder().setCode(CodeType.OK).setData(ByteString.copyFrom(newAppState)).build();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### BeginBlock
|
||||
|
||||
|
@ -322,21 +333,24 @@ pick up from when it restarts. See information on the Handshake, below.
|
|||
|
||||
In go:
|
||||
|
||||
// Track the block hash and header information
|
||||
func (app *PersistentKVStoreApplication) BeginBlock(params types.RequestBeginBlock) {
|
||||
```
|
||||
// Track the block hash and header information
|
||||
func (app *PersistentKVStoreApplication) BeginBlock(params types.RequestBeginBlock) {
|
||||
// update latest block info
|
||||
app.blockHeader = params.Header
|
||||
|
||||
// reset valset changes
|
||||
app.changes = make([]*types.Validator, 0)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In Java:
|
||||
|
||||
/*
|
||||
```
|
||||
/*
|
||||
* all types come from protobuf definition
|
||||
*/
|
||||
ResponseBeginBlock requestBeginBlock(RequestBeginBlock req) {
|
||||
ResponseBeginBlock requestBeginBlock(RequestBeginBlock req) {
|
||||
|
||||
Header header = req.getHeader();
|
||||
byte[] prevAppHash = header.getAppHash().toByteArray();
|
||||
|
@ -346,7 +360,8 @@ In Java:
|
|||
// run your pre-block logic. Maybe prepare a state snapshot, message components, etc
|
||||
|
||||
return ResponseBeginBlock.newBuilder().build();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### EndBlock
|
||||
|
||||
|
@ -364,17 +379,20 @@ for details on how it tracks validators.
|
|||
|
||||
In go:
|
||||
|
||||
// Update the validator set
|
||||
func (app *PersistentKVStoreApplication) EndBlock(req types.RequestEndBlock) types.ResponseEndBlock {
|
||||
```
|
||||
// Update the validator set
|
||||
func (app *PersistentKVStoreApplication) EndBlock(req types.RequestEndBlock) types.ResponseEndBlock {
|
||||
return types.ResponseEndBlock{ValidatorUpdates: app.ValUpdates}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In Java:
|
||||
|
||||
/*
|
||||
```
|
||||
/*
|
||||
* Assume that one validator changes. The new validator has a power of 10
|
||||
*/
|
||||
ResponseEndBlock requestEndBlock(RequestEndBlock req) {
|
||||
ResponseEndBlock requestEndBlock(RequestEndBlock req) {
|
||||
final long currentHeight = req.getHeight();
|
||||
final byte[] validatorPubKey = getValPubKey();
|
||||
|
||||
|
@ -382,7 +400,8 @@ In Java:
|
|||
builder.addDiffs(1, Types.Validator.newBuilder().setPower(10L).setPubKey(ByteString.copyFrom(validatorPubKey)).build());
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Query Connection
|
||||
|
||||
|
@ -406,7 +425,8 @@ Note: these query formats are subject to change!
|
|||
|
||||
In go:
|
||||
|
||||
func (app *KVStoreApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) {
|
||||
```
|
||||
func (app *KVStoreApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) {
|
||||
if reqQuery.Prove {
|
||||
value, proof, exists := app.state.Proof(reqQuery.Data)
|
||||
resQuery.Index = -1 // TODO make Proof return index
|
||||
|
@ -430,11 +450,13 @@ In go:
|
|||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In Java:
|
||||
|
||||
ResponseQuery requestQuery(RequestQuery req) {
|
||||
```
|
||||
ResponseQuery requestQuery(RequestQuery req) {
|
||||
final boolean isProveQuery = req.getProve();
|
||||
final ResponseQuery.Builder responseBuilder = ResponseQuery.newBuilder();
|
||||
|
||||
|
@ -457,7 +479,8 @@ In Java:
|
|||
}
|
||||
|
||||
return responseBuilder.build();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Handshake
|
||||
|
||||
|
@ -477,17 +500,21 @@ all blocks.
|
|||
|
||||
In go:
|
||||
|
||||
func (app *KVStoreApplication) Info(req types.RequestInfo) (resInfo types.ResponseInfo) {
|
||||
```
|
||||
func (app *KVStoreApplication) Info(req types.RequestInfo) (resInfo types.ResponseInfo) {
|
||||
return types.ResponseInfo{Data: cmn.Fmt("{\"size\":%v}", app.state.Size())}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In Java:
|
||||
|
||||
ResponseInfo requestInfo(RequestInfo req) {
|
||||
```
|
||||
ResponseInfo requestInfo(RequestInfo req) {
|
||||
final byte[] lastAppHash = getLastAppHash();
|
||||
final long lastHeight = getLastHeight();
|
||||
return ResponseInfo.newBuilder().setLastBlockAppHash(ByteString.copyFrom(lastAppHash)).setLastBlockHeight(lastHeight).build();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Genesis
|
||||
|
||||
|
@ -497,22 +524,25 @@ consensus params.
|
|||
|
||||
In go:
|
||||
|
||||
// Save the validators in the merkle tree
|
||||
func (app *PersistentKVStoreApplication) InitChain(params types.RequestInitChain) {
|
||||
```
|
||||
// Save the validators in the merkle tree
|
||||
func (app *PersistentKVStoreApplication) InitChain(params types.RequestInitChain) {
|
||||
for _, v := range params.Validators {
|
||||
r := app.updateValidator(v)
|
||||
if r.IsErr() {
|
||||
app.logger.Error("Error updating validators", "r", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In Java:
|
||||
|
||||
/*
|
||||
```
|
||||
/*
|
||||
* all types come from protobuf definition
|
||||
*/
|
||||
ResponseInitChain requestInitChain(RequestInitChain req) {
|
||||
ResponseInitChain requestInitChain(RequestInitChain req) {
|
||||
final int validatorsCount = req.getValidatorsCount();
|
||||
final List<Types.Validator> validatorsList = req.getValidatorsList();
|
||||
|
||||
|
@ -524,4 +554,5 @@ In Java:
|
|||
});
|
||||
|
||||
return ResponseInitChain.newBuilder().build();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
|
@ -16,30 +16,29 @@ to.
|
|||
|
||||
Here are the steps to setting up a testnet manually:
|
||||
|
||||
1) Provision nodes on your cloud provider of choice
|
||||
2) Install Tendermint and the application of interest on all nodes
|
||||
3) Generate a private key and a node key for each validator using
|
||||
1. Provision nodes on your cloud provider of choice
|
||||
2. Install Tendermint and the application of interest on all nodes
|
||||
3. Generate a private key and a node key for each validator using
|
||||
`tendermint init`
|
||||
4) Compile a list of public keys for each validator into a
|
||||
4. Compile a list of public keys for each validator into a
|
||||
`genesis.json` file and replace the existing file with it.
|
||||
5) Run
|
||||
`tendermint node --proxy_app=kvstore --p2p.persistent_peers=< peer
|
||||
addresses >` on each node, where `< peer addresses >` is a comma separated
|
||||
5. Run
|
||||
`tendermint node --proxy_app=kvstore --p2p.persistent_peers=< peer addresses >` on each node, where `< peer addresses >` is a comma separated
|
||||
list of the ID@IP:PORT combination for each node. The default port for
|
||||
Tendermint is `26656`. The ID of a node can be obtained by running
|
||||
`tendermint show_node_id` command. Thus, if the IP addresses of your nodes
|
||||
were `192.168.0.1, 192.168.0.2, 192.168.0.3, 192.168.0.4`, the command
|
||||
would look like:
|
||||
|
||||
|
||||
tendermint node --proxy_app=kvstore --p2p.persistent_peers=96663a3dd0d7b9d17d4c8211b191af259621c693@192.168.0.1:26656, 429fcf25974313b95673f58d77eacdd434402665@192.168.0.2:26656, 0491d373a8e0fcf1023aaf18c51d6a1d0d4f31bd@192.168.0.3:26656, f9baeaa15fedf5e1ef7448dd60f46c01f1a9e9c4@192.168.0.4:26656
|
||||
```
|
||||
tendermint node --proxy_app=kvstore --p2p.persistent_peers=96663a3dd0d7b9d17d4c8211b191af259621c693@192.168.0.1:26656, 429fcf25974313b95673f58d77eacdd434402665@192.168.0.2:26656, 0491d373a8e0fcf1023aaf18c51d6a1d0d4f31bd@192.168.0.3:26656, f9baeaa15fedf5e1ef7448dd60f46c01f1a9e9c4@192.168.0.4:26656
|
||||
```
|
||||
|
||||
After a few seconds, all the nodes should connect to each other and
|
||||
start making blocks! For more information, see the Tendermint Networks
|
||||
section of [the guide to using Tendermint](./using-tendermint.md).
|
||||
|
||||
But wait! Steps 3, 4 and 5 are quite manual. Instead, use the `tendermint
|
||||
testnet` command. By default, running `tendermint testnet` will create all the
|
||||
But wait! Steps 3, 4 and 5 are quite manual. Instead, use the `tendermint testnet` command. By default, running `tendermint testnet` will create all the
|
||||
required files, but it won't populate the list of persistent peers. It will do
|
||||
it however if you provide the `--populate-persistent-peers` flag and optional
|
||||
`--starting-ip-address` flag. Run `tendermint testnet --help` for more details
|
||||
|
@ -63,7 +62,9 @@ The easiest and fastest way to get a testnet up in less than 5 minutes.
|
|||
|
||||
With `docker` and `docker-compose` installed, run the command:
|
||||
|
||||
make localnet-start
|
||||
```
|
||||
make localnet-start
|
||||
```
|
||||
|
||||
from the root of the tendermint repository. This will spin up a 4-node
|
||||
local testnet. Review the target in the Makefile to debug any problems.
|
||||
|
|
|
@ -24,6 +24,7 @@ The script is also used to facilitate cluster deployment below.
|
|||
### Manual Install
|
||||
|
||||
Requires:
|
||||
|
||||
- `go` minimum version 1.10
|
||||
- `$GOPATH` environment variable must be set
|
||||
- `$GOPATH/bin` must be on your `$PATH` (see https://github.com/tendermint/tendermint/wiki/Setting-GOPATH)
|
||||
|
|
|
@ -25,11 +25,13 @@ more info.
|
|||
|
||||
Then run
|
||||
|
||||
go get github.com/tendermint/tendermint
|
||||
cd $GOPATH/src/github.com/tendermint/tendermint
|
||||
make get_tools
|
||||
make get_vendor_deps
|
||||
make install_abci
|
||||
```
|
||||
go get github.com/tendermint/tendermint
|
||||
cd $GOPATH/src/github.com/tendermint/tendermint
|
||||
make get_tools
|
||||
make get_vendor_deps
|
||||
make install_abci
|
||||
```
|
||||
|
||||
Now you should have the `abci-cli` installed; you'll see a couple of
|
||||
commands (`counter` and `kvstore`) that are example applications written
|
||||
|
@ -47,13 +49,17 @@ full transaction bytes are stored as the key and the value.
|
|||
|
||||
Let's start a kvstore application.
|
||||
|
||||
abci-cli kvstore
|
||||
```
|
||||
abci-cli kvstore
|
||||
```
|
||||
|
||||
In another terminal, we can start Tendermint. If you have never run
|
||||
Tendermint before, use:
|
||||
|
||||
tendermint init
|
||||
tendermint node
|
||||
```
|
||||
tendermint init
|
||||
tendermint node
|
||||
```
|
||||
|
||||
If you have used Tendermint, you may want to reset the data for a new
|
||||
blockchain by running `tendermint unsafe_reset_all`. Then you can run
|
||||
|
@ -63,14 +69,18 @@ details, see [the guide on using Tendermint](./using-tendermint.md).
|
|||
You should see Tendermint making blocks! We can get the status of our
|
||||
Tendermint node as follows:
|
||||
|
||||
curl -s localhost:26657/status
|
||||
```
|
||||
curl -s localhost:26657/status
|
||||
```
|
||||
|
||||
The `-s` just silences `curl`. For nicer output, pipe the result into a
|
||||
tool like [jq](https://stedolan.github.io/jq/) or `json_pp`.
|
||||
|
||||
Now let's send some transactions to the kvstore.
|
||||
|
||||
curl -s 'localhost:26657/broadcast_tx_commit?tx="abcd"'
|
||||
```
|
||||
curl -s 'localhost:26657/broadcast_tx_commit?tx="abcd"'
|
||||
```
|
||||
|
||||
Note the single quote (`'`) around the url, which ensures that the
|
||||
double quotes (`"`) are not escaped by bash. This command sent a
|
||||
|
@ -78,7 +88,8 @@ transaction with bytes `abcd`, so `abcd` will be stored as both the key
|
|||
and the value in the Merkle tree. The response should look something
|
||||
like:
|
||||
|
||||
{
|
||||
```
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": "",
|
||||
"result": {
|
||||
|
@ -101,16 +112,20 @@ like:
|
|||
"hash": "9DF66553F98DE3C26E3C3317A3E4CED54F714E39",
|
||||
"height": 14
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We can confirm that our transaction worked and the value got stored by
|
||||
querying the app:
|
||||
|
||||
curl -s 'localhost:26657/abci_query?data="abcd"'
|
||||
```
|
||||
curl -s 'localhost:26657/abci_query?data="abcd"'
|
||||
```
|
||||
|
||||
The result should look like:
|
||||
|
||||
{
|
||||
```
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": "",
|
||||
"result": {
|
||||
|
@ -121,7 +136,8 @@ The result should look like:
|
|||
"value": "YWJjZA=="
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note the `value` in the result (`YWJjZA==`); this is the base64-encoding
|
||||
of the ASCII of `abcd`. You can verify this in a python 2 shell by
|
||||
|
@ -132,12 +148,16 @@ human-readable](https://github.com/tendermint/tendermint/issues/1794).
|
|||
|
||||
Now let's try setting a different key and value:
|
||||
|
||||
curl -s 'localhost:26657/broadcast_tx_commit?tx="name=satoshi"'
|
||||
```
|
||||
curl -s 'localhost:26657/broadcast_tx_commit?tx="name=satoshi"'
|
||||
```
|
||||
|
||||
Now if we query for `name`, we should get `satoshi`, or `c2F0b3NoaQ==`
|
||||
in base64:
|
||||
|
||||
curl -s 'localhost:26657/abci_query?data="name"'
|
||||
```
|
||||
curl -s 'localhost:26657/abci_query?data="name"'
|
||||
```
|
||||
|
||||
Try some other transactions and queries to make sure everything is
|
||||
working!
|
||||
|
@ -171,24 +191,31 @@ Let's kill the previous instance of `tendermint` and the `kvstore`
|
|||
application, and start the counter app. We can enable `serial=on` with a
|
||||
flag:
|
||||
|
||||
abci-cli counter --serial
|
||||
```
|
||||
abci-cli counter --serial
|
||||
```
|
||||
|
||||
In another window, reset then start Tendermint:
|
||||
|
||||
tendermint unsafe_reset_all
|
||||
tendermint node
|
||||
```
|
||||
tendermint unsafe_reset_all
|
||||
tendermint node
|
||||
```
|
||||
|
||||
Once again, you can see the blocks streaming by. Let's send some
|
||||
transactions. Since we have set `serial=on`, the first transaction must
|
||||
be the number `0`:
|
||||
|
||||
curl localhost:26657/broadcast_tx_commit?tx=0x00
|
||||
```
|
||||
curl localhost:26657/broadcast_tx_commit?tx=0x00
|
||||
```
|
||||
|
||||
Note the empty (hence successful) response. The next transaction must be
|
||||
the number `1`. If instead, we try to send a `5`, we get an error:
|
||||
|
||||
> curl localhost:26657/broadcast_tx_commit?tx=0x05
|
||||
{
|
||||
```
|
||||
> curl localhost:26657/broadcast_tx_commit?tx=0x05
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": "",
|
||||
"result": {
|
||||
|
@ -203,12 +230,14 @@ the number `1`. If instead, we try to send a `5`, we get an error:
|
|||
"hash": "33B93DFF98749B0D6996A70F64071347060DC19C",
|
||||
"height": 34
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
But if we send a `1`, it works again:
|
||||
|
||||
> curl localhost:26657/broadcast_tx_commit?tx=0x01
|
||||
{
|
||||
```
|
||||
> curl localhost:26657/broadcast_tx_commit?tx=0x01
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": "",
|
||||
"result": {
|
||||
|
@ -221,7 +250,8 @@ But if we send a `1`, it works again:
|
|||
"hash": "F17854A977F6FA7EEA1BD758E296710B86F72F3D",
|
||||
"height": 60
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For more details on the `broadcast_tx` API, see [the guide on using
|
||||
Tendermint](./using-tendermint.md).
|
||||
|
@ -236,26 +266,34 @@ You'll also need to fetch the relevant repository, from
|
|||
[here](https://github.com/tendermint/js-abci) then install it. As go
|
||||
devs, we keep all our code under the `$GOPATH`, so run:
|
||||
|
||||
go get github.com/tendermint/js-abci &> /dev/null
|
||||
cd $GOPATH/src/github.com/tendermint/js-abci/example
|
||||
npm install
|
||||
cd ..
|
||||
```
|
||||
go get github.com/tendermint/js-abci &> /dev/null
|
||||
cd $GOPATH/src/github.com/tendermint/js-abci/example
|
||||
npm install
|
||||
cd ..
|
||||
```
|
||||
|
||||
Kill the previous `counter` and `tendermint` processes. Now run the app:
|
||||
|
||||
node example/counter.js
|
||||
```
|
||||
node example/counter.js
|
||||
```
|
||||
|
||||
In another window, reset and start `tendermint`:
|
||||
|
||||
tendermint unsafe_reset_all
|
||||
tendermint node
|
||||
```
|
||||
tendermint unsafe_reset_all
|
||||
tendermint node
|
||||
```
|
||||
|
||||
Once again, you should see blocks streaming by - but now, our
|
||||
application is written in javascript! Try sending some transactions, and
|
||||
like before - the results should be the same:
|
||||
|
||||
curl localhost:26657/broadcast_tx_commit?tx=0x00 # ok
|
||||
curl localhost:26657/broadcast_tx_commit?tx=0x05 # invalid nonce
|
||||
curl localhost:26657/broadcast_tx_commit?tx=0x01 # ok
|
||||
```
|
||||
curl localhost:26657/broadcast_tx_commit?tx=0x00 # ok
|
||||
curl localhost:26657/broadcast_tx_commit?tx=0x05 # invalid nonce
|
||||
curl localhost:26657/broadcast_tx_commit?tx=0x01 # ok
|
||||
```
|
||||
|
||||
Neat, eh?
|
||||
|
|
|
@ -5,49 +5,59 @@
|
|||
We first create three connections (mempool, consensus and query) to the
|
||||
application (running `kvstore` locally in this case).
|
||||
|
||||
I[10-04|13:54:27.364] Starting multiAppConn module=proxy impl=multiAppConn
|
||||
I[10-04|13:54:27.366] Starting localClient module=abci-client connection=query impl=localClient
|
||||
I[10-04|13:54:27.366] Starting localClient module=abci-client connection=mempool impl=localClient
|
||||
I[10-04|13:54:27.367] Starting localClient module=abci-client connection=consensus impl=localClient
|
||||
```
|
||||
I[10-04|13:54:27.364] Starting multiAppConn module=proxy impl=multiAppConn
|
||||
I[10-04|13:54:27.366] Starting localClient module=abci-client connection=query impl=localClient
|
||||
I[10-04|13:54:27.366] Starting localClient module=abci-client connection=mempool impl=localClient
|
||||
I[10-04|13:54:27.367] Starting localClient module=abci-client connection=consensus impl=localClient
|
||||
```
|
||||
|
||||
Then Tendermint Core and the application perform a handshake.
|
||||
|
||||
I[10-04|13:54:27.367] ABCI Handshake module=consensus appHeight=90 appHash=E0FBAFBF6FCED8B9786DDFEB1A0D4FA2501BADAD
|
||||
I[10-04|13:54:27.368] ABCI Replay Blocks module=consensus appHeight=90 storeHeight=90 stateHeight=90
|
||||
I[10-04|13:54:27.368] Completed ABCI Handshake - Tendermint and App are synced module=consensus appHeight=90 appHash=E0FBAFBF6FCED8B9786DDFEB1A0D4FA2501BADAD
|
||||
```
|
||||
I[10-04|13:54:27.367] ABCI Handshake module=consensus appHeight=90 appHash=E0FBAFBF6FCED8B9786DDFEB1A0D4FA2501BADAD
|
||||
I[10-04|13:54:27.368] ABCI Replay Blocks module=consensus appHeight=90 storeHeight=90 stateHeight=90
|
||||
I[10-04|13:54:27.368] Completed ABCI Handshake - Tendermint and App are synced module=consensus appHeight=90 appHash=E0FBAFBF6FCED8B9786DDFEB1A0D4FA2501BADAD
|
||||
```
|
||||
|
||||
After that, we start a few more things like the event switch, reactors,
|
||||
and perform UPNP discover in order to detect the IP address.
|
||||
|
||||
I[10-04|13:54:27.374] Starting EventSwitch module=types impl=EventSwitch
|
||||
I[10-04|13:54:27.375] This node is a validator module=consensus
|
||||
I[10-04|13:54:27.379] Starting Node module=main impl=Node
|
||||
I[10-04|13:54:27.381] Local listener module=p2p ip=:: port=26656
|
||||
I[10-04|13:54:27.382] Getting UPNP external address module=p2p
|
||||
I[10-04|13:54:30.386] Could not perform UPNP discover module=p2p err="write udp4 0.0.0.0:38238->239.255.255.250:1900: i/o timeout"
|
||||
I[10-04|13:54:30.386] Starting DefaultListener module=p2p impl=Listener(@10.0.2.15:26656)
|
||||
I[10-04|13:54:30.387] Starting P2P Switch module=p2p impl="P2P Switch"
|
||||
I[10-04|13:54:30.387] Starting MempoolReactor module=mempool impl=MempoolReactor
|
||||
I[10-04|13:54:30.387] Starting BlockchainReactor module=blockchain impl=BlockchainReactor
|
||||
I[10-04|13:54:30.387] Starting ConsensusReactor module=consensus impl=ConsensusReactor
|
||||
I[10-04|13:54:30.387] ConsensusReactor module=consensus fastSync=false
|
||||
I[10-04|13:54:30.387] Starting ConsensusState module=consensus impl=ConsensusState
|
||||
I[10-04|13:54:30.387] Starting WAL module=consensus wal=/home/vagrant/.tendermint/data/cs.wal/wal impl=WAL
|
||||
I[10-04|13:54:30.388] Starting TimeoutTicker module=consensus impl=TimeoutTicker
|
||||
```
|
||||
I[10-04|13:54:27.374] Starting EventSwitch module=types impl=EventSwitch
|
||||
I[10-04|13:54:27.375] This node is a validator module=consensus
|
||||
I[10-04|13:54:27.379] Starting Node module=main impl=Node
|
||||
I[10-04|13:54:27.381] Local listener module=p2p ip=:: port=26656
|
||||
I[10-04|13:54:27.382] Getting UPNP external address module=p2p
|
||||
I[10-04|13:54:30.386] Could not perform UPNP discover module=p2p err="write udp4 0.0.0.0:38238->239.255.255.250:1900: i/o timeout"
|
||||
I[10-04|13:54:30.386] Starting DefaultListener module=p2p impl=Listener(@10.0.2.15:26656)
|
||||
I[10-04|13:54:30.387] Starting P2P Switch module=p2p impl="P2P Switch"
|
||||
I[10-04|13:54:30.387] Starting MempoolReactor module=mempool impl=MempoolReactor
|
||||
I[10-04|13:54:30.387] Starting BlockchainReactor module=blockchain impl=BlockchainReactor
|
||||
I[10-04|13:54:30.387] Starting ConsensusReactor module=consensus impl=ConsensusReactor
|
||||
I[10-04|13:54:30.387] ConsensusReactor module=consensus fastSync=false
|
||||
I[10-04|13:54:30.387] Starting ConsensusState module=consensus impl=ConsensusState
|
||||
I[10-04|13:54:30.387] Starting WAL module=consensus wal=/home/vagrant/.tendermint/data/cs.wal/wal impl=WAL
|
||||
I[10-04|13:54:30.388] Starting TimeoutTicker module=consensus impl=TimeoutTicker
|
||||
```
|
||||
|
||||
Notice the second row where Tendermint Core reports that "This node is a
|
||||
validator". It also could be just an observer (regular node).
|
||||
|
||||
Next we replay all the messages from the WAL.
|
||||
|
||||
I[10-04|13:54:30.390] Catchup by replaying consensus messages module=consensus height=91
|
||||
I[10-04|13:54:30.390] Replay: New Step module=consensus height=91 round=0 step=RoundStepNewHeight
|
||||
I[10-04|13:54:30.390] Replay: Done module=consensus
|
||||
```
|
||||
I[10-04|13:54:30.390] Catchup by replaying consensus messages module=consensus height=91
|
||||
I[10-04|13:54:30.390] Replay: New Step module=consensus height=91 round=0 step=RoundStepNewHeight
|
||||
I[10-04|13:54:30.390] Replay: Done module=consensus
|
||||
```
|
||||
|
||||
"Started node" message signals that everything is ready for work.
|
||||
|
||||
I[10-04|13:54:30.391] Starting RPC HTTP server on tcp socket 0.0.0.0:26657 module=rpc-server
|
||||
I[10-04|13:54:30.392] Started node module=main nodeInfo="NodeInfo{id: DF22D7C92C91082324A1312F092AA1DA197FA598DBBFB6526E, moniker: anonymous, network: test-chain-3MNw2N [remote , listen 10.0.2.15:26656], version: 0.11.0-10f361fc ([wire_version=0.6.2 p2p_version=0.5.0 consensus_version=v1/0.2.2 rpc_version=0.7.0/3 tx_index=on rpc_addr=tcp://0.0.0.0:26657])}"
|
||||
```
|
||||
I[10-04|13:54:30.391] Starting RPC HTTP server on tcp socket 0.0.0.0:26657 module=rpc-server
|
||||
I[10-04|13:54:30.392] Started node module=main nodeInfo="NodeInfo{id: DF22D7C92C91082324A1312F092AA1DA197FA598DBBFB6526E, moniker: anonymous, network: test-chain-3MNw2N [remote , listen 10.0.2.15:26656], version: 0.11.0-10f361fc ([wire_version=0.6.2 p2p_version=0.5.0 consensus_version=v1/0.2.2 rpc_version=0.7.0/3 tx_index=on rpc_addr=tcp://0.0.0.0:26657])}"
|
||||
```
|
||||
|
||||
Next follows a standard block creation cycle, where we enter a new
|
||||
round, propose a block, receive more than 2/3 of prevotes, then
|
||||
|
@ -56,22 +66,23 @@ please refer to [Consensus
|
|||
Overview](./introduction.md#consensus-overview) or [Byzantine Consensus
|
||||
Algorithm](./spec/consensus).
|
||||
|
||||
I[10-04|13:54:30.393] enterNewRound(91/0). Current: 91/0/RoundStepNewHeight module=consensus
|
||||
I[10-04|13:54:30.393] enterPropose(91/0). Current: 91/0/RoundStepNewRound module=consensus
|
||||
I[10-04|13:54:30.393] enterPropose: Our turn to propose module=consensus proposer=125B0E3C5512F5C2B0E1109E31885C4511570C42 privValidator="PrivValidator{125B0E3C5512F5C2B0E1109E31885C4511570C42 LH:90, LR:0, LS:3}"
|
||||
I[10-04|13:54:30.394] Signed proposal module=consensus height=91 round=0 proposal="Proposal{91/0 1:21B79872514F (-1,:0:000000000000) {/10EDEDD7C84E.../}}"
|
||||
I[10-04|13:54:30.397] Received complete proposal block module=consensus height=91 hash=F671D562C7B9242900A286E1882EE64E5556FE9E
|
||||
I[10-04|13:54:30.397] enterPrevote(91/0). Current: 91/0/RoundStepPropose module=consensus
|
||||
I[10-04|13:54:30.397] enterPrevote: ProposalBlock is valid module=consensus height=91 round=0
|
||||
I[10-04|13:54:30.398] Signed and pushed vote module=consensus height=91 round=0 vote="Vote{0:125B0E3C5512 91/00/1(Prevote) F671D562C7B9 {/89047FFC21D8.../}}" err=null
|
||||
I[10-04|13:54:30.401] Added to prevote module=consensus vote="Vote{0:125B0E3C5512 91/00/1(Prevote) F671D562C7B9 {/89047FFC21D8.../}}" prevotes="VoteSet{H:91 R:0 T:1 +2/3:F671D562C7B9242900A286E1882EE64E5556FE9E:1:21B79872514F BA{1:X} map[]}"
|
||||
I[10-04|13:54:30.401] enterPrecommit(91/0). Current: 91/0/RoundStepPrevote module=consensus
|
||||
I[10-04|13:54:30.401] enterPrecommit: +2/3 prevoted proposal block. Locking module=consensus hash=F671D562C7B9242900A286E1882EE64E5556FE9E
|
||||
I[10-04|13:54:30.402] Signed and pushed vote module=consensus height=91 round=0 vote="Vote{0:125B0E3C5512 91/00/2(Precommit) F671D562C7B9 {/80533478E41A.../}}" err=null
|
||||
I[10-04|13:54:30.404] Added to precommit module=consensus vote="Vote{0:125B0E3C5512 91/00/2(Precommit) F671D562C7B9 {/80533478E41A.../}}" precommits="VoteSet{H:91 R:0 T:2 +2/3:F671D562C7B9242900A286E1882EE64E5556FE9E:1:21B79872514F BA{1:X} map[]}"
|
||||
I[10-04|13:54:30.404] enterCommit(91/0). Current: 91/0/RoundStepPrecommit module=consensus
|
||||
I[10-04|13:54:30.405] Finalizing commit of block with 0 txs module=consensus height=91 hash=F671D562C7B9242900A286E1882EE64E5556FE9E root=E0FBAFBF6FCED8B9786DDFEB1A0D4FA2501BADAD
|
||||
I[10-04|13:54:30.405] Block{
|
||||
```
|
||||
I[10-04|13:54:30.393] enterNewRound(91/0). Current: 91/0/RoundStepNewHeight module=consensus
|
||||
I[10-04|13:54:30.393] enterPropose(91/0). Current: 91/0/RoundStepNewRound module=consensus
|
||||
I[10-04|13:54:30.393] enterPropose: Our turn to propose module=consensus proposer=125B0E3C5512F5C2B0E1109E31885C4511570C42 privValidator="PrivValidator{125B0E3C5512F5C2B0E1109E31885C4511570C42 LH:90, LR:0, LS:3}"
|
||||
I[10-04|13:54:30.394] Signed proposal module=consensus height=91 round=0 proposal="Proposal{91/0 1:21B79872514F (-1,:0:000000000000) {/10EDEDD7C84E.../}}"
|
||||
I[10-04|13:54:30.397] Received complete proposal block module=consensus height=91 hash=F671D562C7B9242900A286E1882EE64E5556FE9E
|
||||
I[10-04|13:54:30.397] enterPrevote(91/0). Current: 91/0/RoundStepPropose module=consensus
|
||||
I[10-04|13:54:30.397] enterPrevote: ProposalBlock is valid module=consensus height=91 round=0
|
||||
I[10-04|13:54:30.398] Signed and pushed vote module=consensus height=91 round=0 vote="Vote{0:125B0E3C5512 91/00/1(Prevote) F671D562C7B9 {/89047FFC21D8.../}}" err=null
|
||||
I[10-04|13:54:30.401] Added to prevote module=consensus vote="Vote{0:125B0E3C5512 91/00/1(Prevote) F671D562C7B9 {/89047FFC21D8.../}}" prevotes="VoteSet{H:91 R:0 T:1 +2/3:F671D562C7B9242900A286E1882EE64E5556FE9E:1:21B79872514F BA{1:X} map[]}"
|
||||
I[10-04|13:54:30.401] enterPrecommit(91/0). Current: 91/0/RoundStepPrevote module=consensus
|
||||
I[10-04|13:54:30.401] enterPrecommit: +2/3 prevoted proposal block. Locking module=consensus hash=F671D562C7B9242900A286E1882EE64E5556FE9E
|
||||
I[10-04|13:54:30.402] Signed and pushed vote module=consensus height=91 round=0 vote="Vote{0:125B0E3C5512 91/00/2(Precommit) F671D562C7B9 {/80533478E41A.../}}" err=null
|
||||
I[10-04|13:54:30.404] Added to precommit module=consensus vote="Vote{0:125B0E3C5512 91/00/2(Precommit) F671D562C7B9 {/80533478E41A.../}}" precommits="VoteSet{H:91 R:0 T:2 +2/3:F671D562C7B9242900A286E1882EE64E5556FE9E:1:21B79872514F BA{1:X} map[]}"
|
||||
I[10-04|13:54:30.404] enterCommit(91/0). Current: 91/0/RoundStepPrecommit module=consensus
|
||||
I[10-04|13:54:30.405] Finalizing commit of block with 0 txs module=consensus height=91 hash=F671D562C7B9242900A286E1882EE64E5556FE9E root=E0FBAFBF6FCED8B9786DDFEB1A0D4FA2501BADAD
|
||||
I[10-04|13:54:30.405] Block{
|
||||
Header{
|
||||
ChainID: test-chain-3MNw2N
|
||||
Height: 91
|
||||
|
@ -90,10 +101,11 @@ Algorithm](./spec/consensus).
|
|||
BlockID: F15AB8BEF9A6AAB07E457A6E16BC410546AA4DC6:1:D505DA273544
|
||||
Precommits: Vote{0:125B0E3C5512 90/00/2(Precommit) F15AB8BEF9A6 {/FE98E2B956F0.../}}
|
||||
}#56FEF2EFDB8B37E9C6E6D635749DF3169D5F005D
|
||||
}#F671D562C7B9242900A286E1882EE64E5556FE9E module=consensus
|
||||
I[10-04|13:54:30.408] Executed block module=state height=91 validTxs=0 invalidTxs=0
|
||||
I[10-04|13:54:30.410] Committed state module=state height=91 txs=0 hash=E0FBAFBF6FCED8B9786DDFEB1A0D4FA2501BADAD
|
||||
I[10-04|13:54:30.410] Recheck txs module=mempool numtxs=0 height=91
|
||||
}#F671D562C7B9242900A286E1882EE64E5556FE9E module=consensus
|
||||
I[10-04|13:54:30.408] Executed block module=state height=91 validTxs=0 invalidTxs=0
|
||||
I[10-04|13:54:30.410] Committed state module=state height=91 txs=0 hash=E0FBAFBF6FCED8B9786DDFEB1A0D4FA2501BADAD
|
||||
I[10-04|13:54:30.410] Recheck txs module=mempool numtxs=0 height=91
|
||||
```
|
||||
|
||||
## List of modules
|
||||
|
||||
|
|
|
@ -5,27 +5,29 @@ to their results.
|
|||
|
||||
Let's take a look at the `[tx_index]` config section:
|
||||
|
||||
##### transactions indexer configuration options #####
|
||||
[tx_index]
|
||||
```
|
||||
##### 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 = "kv"
|
||||
# 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 = "kv"
|
||||
|
||||
# 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 = ""
|
||||
# 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 = ""
|
||||
|
||||
# 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 = false
|
||||
# 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 = false
|
||||
```
|
||||
|
||||
By default, Tendermint will index all transactions by their respective
|
||||
hashes using an embedded simple indexer. Note, we are planning to add
|
||||
|
@ -39,7 +41,8 @@ pairs of UTF-8 encoded strings (e.g. "account.owner": "Bob", "balance":
|
|||
|
||||
Example:
|
||||
|
||||
func (app *KVStoreApplication) DeliverTx(tx []byte) types.Result {
|
||||
```
|
||||
func (app *KVStoreApplication) DeliverTx(tx []byte) types.Result {
|
||||
...
|
||||
tags := []cmn.KVPair{
|
||||
{[]byte("account.name"), []byte("igor")},
|
||||
|
@ -47,7 +50,8 @@ Example:
|
|||
{[]byte("tx.amount"), []byte("7")},
|
||||
}
|
||||
return types.ResponseDeliverTx{Code: code.CodeTypeOK, Tags: tags}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you want Tendermint to only index transactions by "account.name" tag,
|
||||
in the config set `tx_index.index_tags="account.name"`. If you to index
|
||||
|
@ -66,7 +70,9 @@ Tendermint will throw a warning if you try to use any of the above keys.
|
|||
You can query the transaction results by calling `/tx_search` RPC
|
||||
endpoint:
|
||||
|
||||
curl "localhost:26657/tx_search?query=\"account.name='igor'\"&prove=true"
|
||||
```
|
||||
curl "localhost:26657/tx_search?query=\"account.name='igor'\"&prove=true"
|
||||
```
|
||||
|
||||
Check out [API docs](https://tendermint.github.io/slate/?shell#txsearch)
|
||||
for more information on query syntax and other options.
|
||||
|
@ -76,14 +82,16 @@ for more information on query syntax and other options.
|
|||
Clients can subscribe to transactions with the given tags via Websocket
|
||||
by providing a query to `/subscribe` RPC endpoint.
|
||||
|
||||
{
|
||||
```
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "subscribe",
|
||||
"id": "0",
|
||||
"params": {
|
||||
"query": "account.name='igor'"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Check out [API docs](https://tendermint.github.io/slate/#subscribe) for
|
||||
more information on query syntax and other options.
|
||||
|
|
|
@ -61,13 +61,14 @@ providing basic services to distributed systems, such as dynamic
|
|||
configuration, service discovery, locking, leader-election, and so on.
|
||||
|
||||
Tendermint is in essence similar software, but with two key differences:
|
||||
|
||||
- It is Byzantine Fault Tolerant, meaning it can only tolerate up to a
|
||||
1/3 of failures, but those failures can include arbitrary behaviour -
|
||||
including hacking and malicious attacks. - It does not specify a
|
||||
particular application, like a fancy key-value store. Instead, it
|
||||
focuses on arbitrary state machine replication, so developers can build
|
||||
the application logic that's right for them, from key-value store to
|
||||
cryptocurrency to e-voting platform and beyond.
|
||||
1/3 of failures, but those failures can include arbitrary behaviour -
|
||||
including hacking and malicious attacks. - It does not specify a
|
||||
particular application, like a fancy key-value store. Instead, it
|
||||
focuses on arbitrary state machine replication, so developers can build
|
||||
the application logic that's right for them, from key-value store to
|
||||
cryptocurrency to e-voting platform and beyond.
|
||||
|
||||
The layout of this Tendermint website content is also ripped directly
|
||||
and without shame from [consul.io](https://www.consul.io/) and the other
|
||||
|
@ -298,8 +299,8 @@ introduces a few **locking** rules which modulate which paths can be
|
|||
followed in the flow diagram. Once a validator precommits a block, it is
|
||||
locked on that block. Then,
|
||||
|
||||
1) it must prevote for the block it is locked on
|
||||
2) it can only unlock, and precommit for a new block, if there is a
|
||||
1. it must prevote for the block it is locked on
|
||||
2. it can only unlock, and precommit for a new block, if there is a
|
||||
polka for that block in a later round
|
||||
|
||||
## Stake
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"prettier": "^1.13.7",
|
||||
"remark-cli": "^5.0.0",
|
||||
"remark-lint-no-dead-urls": "^0.3.0",
|
||||
"textlint": "^10.2.1"
|
||||
},
|
||||
"name": "tendermint",
|
||||
"description": "Tendermint Core Documentation",
|
||||
"version": "0.0.1",
|
||||
"main": "README.md",
|
||||
"devDependencies": {},
|
||||
"scripts": {
|
||||
"lint:json": "prettier \"**/*.json\" --write",
|
||||
"lint:md": "prettier \"**/*.md\" --write && remark . && textlint \"md/**\"",
|
||||
"lint": "yarn lint:json && yarn lint:md"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/tendermint/tendermint.git"
|
||||
},
|
||||
"keywords": [
|
||||
"tendermint",
|
||||
"blockchain"
|
||||
],
|
||||
"author": "Tendermint",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/tendermint/tendermint/issues"
|
||||
},
|
||||
"homepage": "https://tendermint.com/docs/",
|
||||
"remarkConfig": {
|
||||
"plugins": [
|
||||
"remark-lint-no-dead-urls"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -49,14 +49,18 @@ second TODO is to query the /status RPC endpoint. It provides the
|
|||
necessary info: whenever the node is syncing or not, what height it is
|
||||
on, etc.
|
||||
|
||||
$ curl http(s)://{ip}:{rpcPort}/status
|
||||
```
|
||||
curl http(s)://{ip}:{rpcPort}/status
|
||||
```
|
||||
|
||||
`dump_consensus_state` will give you a detailed overview of the
|
||||
consensus state (proposer, lastest validators, peers states). From it,
|
||||
you should be able to figure out why, for example, the network had
|
||||
halted.
|
||||
|
||||
$ curl http(s)://{ip}:{rpcPort}/dump_consensus_state
|
||||
```
|
||||
curl http(s)://{ip}:{rpcPort}/dump_consensus_state
|
||||
```
|
||||
|
||||
There is a reduced version of this endpoint - `consensus_state`, which
|
||||
returns just the votes seen at the current height.
|
||||
|
@ -152,12 +156,14 @@ If you are going to use Tendermint in a private domain and you have a
|
|||
private high-speed network among your peers, it makes sense to lower
|
||||
flush throttle timeout and increase other params.
|
||||
|
||||
[p2p]
|
||||
```
|
||||
[p2p]
|
||||
|
||||
send_rate=20000000 # 2MB/s
|
||||
recv_rate=20000000 # 2MB/s
|
||||
flush_throttle_timeout=10
|
||||
max_packet_msg_payload_size=10240 # 10KB
|
||||
send_rate=20000000 # 2MB/s
|
||||
recv_rate=20000000 # 2MB/s
|
||||
flush_throttle_timeout=10
|
||||
max_packet_msg_payload_size=10240 # 10KB
|
||||
```
|
||||
|
||||
- `mempool.recheck`
|
||||
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
|
||||
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
|
||||
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.
|
||||
|
||||
## Options
|
||||
|
|
|
@ -9,14 +9,16 @@ for third-party applications (for analysys) or inspecting state.
|
|||
You can subscribe to any of the events above by calling `subscribe` RPC
|
||||
method via Websocket.
|
||||
|
||||
{
|
||||
```
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "subscribe",
|
||||
"id": "0",
|
||||
"params": {
|
||||
"query": "tm.event='NewBlock'"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Check out [API docs](https://tendermint.github.io/slate/#subscribe) for
|
||||
more information on query syntax and other options.
|
||||
|
|
|
@ -44,6 +44,7 @@ then:
|
|||
terraform init
|
||||
terraform apply -var DO_API_TOKEN="$DO_API_TOKEN" -var SSH_KEY_FILE="$SSH_KEY_FILE"
|
||||
```
|
||||
|
||||
and you will get a list of IP addresses that belong to your droplets.
|
||||
|
||||
With the droplets created and running, let's setup Ansible.
|
||||
|
|
|
@ -16,7 +16,9 @@ this by setting the `TMHOME` environment variable.
|
|||
|
||||
Initialize the root directory by running:
|
||||
|
||||
tendermint init
|
||||
```
|
||||
tendermint init
|
||||
```
|
||||
|
||||
This will create a new private key (`priv_validator.json`), and a
|
||||
genesis file (`genesis.json`) containing the associated public key, in
|
||||
|
@ -25,24 +27,30 @@ with one validator.
|
|||
|
||||
For more elaborate initialization, see the tesnet command:
|
||||
|
||||
tendermint testnet --help
|
||||
```
|
||||
tendermint testnet --help
|
||||
```
|
||||
|
||||
## Run
|
||||
|
||||
To run a Tendermint node, use
|
||||
|
||||
tendermint node
|
||||
```
|
||||
tendermint node
|
||||
```
|
||||
|
||||
By default, Tendermint will try to connect to an ABCI application on
|
||||
[127.0.0.1:26658](127.0.0.1:26658). If you have the `kvstore` ABCI app
|
||||
installed, run it in another window. If you don't, kill Tendermint and
|
||||
run an in-process version of the `kvstore` app:
|
||||
|
||||
tendermint node --proxy_app=kvstore
|
||||
```
|
||||
tendermint node --proxy_app=kvstore
|
||||
```
|
||||
|
||||
After a few seconds you should see blocks start streaming in. Note that
|
||||
blocks are produced regularly, even if there are no transactions. See
|
||||
*No Empty Blocks*, below, to modify this setting.
|
||||
_No Empty Blocks_, below, to modify this setting.
|
||||
|
||||
Tendermint supports in-process versions of the `counter`, `kvstore` and
|
||||
`nil` apps that ship as examples with `abci-cli`. It's easy to compile
|
||||
|
@ -51,22 +59,30 @@ app is not written in Go, simply run it in another process, and use the
|
|||
`--proxy_app` flag to specify the address of the socket it is listening
|
||||
on, for instance:
|
||||
|
||||
tendermint node --proxy_app=/var/run/abci.sock
|
||||
```
|
||||
tendermint node --proxy_app=/var/run/abci.sock
|
||||
```
|
||||
|
||||
## Transactions
|
||||
|
||||
To send a transaction, use `curl` to make requests to the Tendermint RPC
|
||||
server, for example:
|
||||
|
||||
curl http://localhost:26657/broadcast_tx_commit?tx=\"abcd\"
|
||||
```
|
||||
curl http://localhost:26657/broadcast_tx_commit?tx=\"abcd\"
|
||||
```
|
||||
|
||||
We can see the chain's status at the `/status` end-point:
|
||||
|
||||
curl http://localhost:26657/status | json_pp
|
||||
```
|
||||
curl http://localhost:26657/status | json_pp
|
||||
```
|
||||
|
||||
and the `latest_app_hash` in particular:
|
||||
|
||||
curl http://localhost:26657/status | json_pp | grep latest_app_hash
|
||||
```
|
||||
curl http://localhost:26657/status | json_pp | grep latest_app_hash
|
||||
```
|
||||
|
||||
Visit http://localhost:26657> in your browser to see the list of other
|
||||
endpoints. Some take no arguments (like `/status`), while others specify
|
||||
|
@ -81,30 +97,40 @@ With `GET`:
|
|||
|
||||
To send a UTF8 string byte array, quote the value of the tx pramater:
|
||||
|
||||
curl 'http://localhost:26657/broadcast_tx_commit?tx="hello"'
|
||||
```
|
||||
curl 'http://localhost:26657/broadcast_tx_commit?tx="hello"'
|
||||
```
|
||||
|
||||
which sends a 5 byte transaction: "h e l l o" \[68 65 6c 6c 6f\].
|
||||
|
||||
Note the URL must be wrapped with single quoes, else bash will ignore
|
||||
the double quotes. To avoid the single quotes, escape the double quotes:
|
||||
|
||||
curl http://localhost:26657/broadcast_tx_commit?tx=\"hello\"
|
||||
```
|
||||
curl http://localhost:26657/broadcast_tx_commit?tx=\"hello\"
|
||||
```
|
||||
|
||||
Using a special character:
|
||||
|
||||
curl 'http://localhost:26657/broadcast_tx_commit?tx="€5"'
|
||||
```
|
||||
curl 'http://localhost:26657/broadcast_tx_commit?tx="€5"'
|
||||
```
|
||||
|
||||
sends a 4 byte transaction: "€5" (UTF8) \[e2 82 ac 35\].
|
||||
|
||||
To send as raw hex, omit quotes AND prefix the hex string with `0x`:
|
||||
|
||||
curl http://localhost:26657/broadcast_tx_commit?tx=0x01020304
|
||||
```
|
||||
curl http://localhost:26657/broadcast_tx_commit?tx=0x01020304
|
||||
```
|
||||
|
||||
which sends a 4 byte transaction: \[01 02 03 04\].
|
||||
|
||||
With `POST` (using `json`), the raw hex must be `base64` encoded:
|
||||
|
||||
curl --data-binary '{"jsonrpc":"2.0","id":"anything","method":"broadcast_tx_commit","params": {"tx": "AQIDBA=="}}' -H 'content-type:text/plain;' http://localhost:26657
|
||||
```
|
||||
curl --data-binary '{"jsonrpc":"2.0","id":"anything","method":"broadcast_tx_commit","params": {"tx": "AQIDBA=="}}' -H 'content-type:text/plain;' http://localhost:26657
|
||||
```
|
||||
|
||||
which sends the same 4 byte transaction: \[01 02 03 04\].
|
||||
|
||||
|
@ -118,7 +144,9 @@ afford to lose all blockchain data!
|
|||
To reset a blockchain, stop the node, remove the `~/.tendermint/data`
|
||||
directory and run
|
||||
|
||||
tendermint unsafe_reset_priv_validator
|
||||
```
|
||||
tendermint unsafe_reset_priv_validator
|
||||
```
|
||||
|
||||
This final step is necessary to reset the `priv_validator.json`, which
|
||||
otherwise prevents you from making conflicting votes in the consensus
|
||||
|
@ -150,21 +178,27 @@ To configure Tendermint to not produce empty blocks unless there are
|
|||
transactions or the app hash changes, run Tendermint with this
|
||||
additional flag:
|
||||
|
||||
tendermint node --consensus.create_empty_blocks=false
|
||||
```
|
||||
tendermint node --consensus.create_empty_blocks=false
|
||||
```
|
||||
|
||||
or set the configuration via the `config.toml` file:
|
||||
|
||||
[consensus]
|
||||
create_empty_blocks = false
|
||||
```
|
||||
[consensus]
|
||||
create_empty_blocks = false
|
||||
```
|
||||
|
||||
Remember: because the default is to *create empty blocks*, avoiding
|
||||
Remember: because the default is to _create empty blocks_, avoiding
|
||||
empty blocks requires the config option to be set to `false`.
|
||||
|
||||
The block interval setting allows for a delay (in seconds) between the
|
||||
creation of each new empty block. It is set via the `config.toml`:
|
||||
|
||||
[consensus]
|
||||
create_empty_blocks_interval = 5
|
||||
```
|
||||
[consensus]
|
||||
create_empty_blocks_interval = 5
|
||||
```
|
||||
|
||||
With this setting, empty blocks will be produced every 5s if no block
|
||||
has been produced otherwise, regardless of the value of
|
||||
|
@ -181,9 +215,11 @@ eventually included in a block.
|
|||
Since there are multiple phases to processing a transaction, we offer
|
||||
multiple endpoints to broadcast a transaction:
|
||||
|
||||
/broadcast_tx_async
|
||||
/broadcast_tx_sync
|
||||
/broadcast_tx_commit
|
||||
```
|
||||
/broadcast_tx_async
|
||||
/broadcast_tx_sync
|
||||
/broadcast_tx_commit
|
||||
```
|
||||
|
||||
These correspond to no-processing, processing through the mempool, and
|
||||
processing through a block, respectively. That is, `broadcast_tx_async`,
|
||||
|
@ -208,7 +244,8 @@ When `tendermint init` is run, both a `genesis.json` and
|
|||
`priv_validator.json` are created in `~/.tendermint/config`. The
|
||||
`genesis.json` might look like:
|
||||
|
||||
{
|
||||
```
|
||||
{
|
||||
"validators" : [
|
||||
{
|
||||
"pub_key" : {
|
||||
|
@ -222,11 +259,13 @@ When `tendermint init` is run, both a `genesis.json` and
|
|||
"app_hash" : "",
|
||||
"chain_id" : "test-chain-rDlYSN",
|
||||
"genesis_time" : "0001-01-01T00:00:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And the `priv_validator.json`:
|
||||
|
||||
{
|
||||
```
|
||||
{
|
||||
"last_step" : 0,
|
||||
"last_round" : "0",
|
||||
"address" : "B788DEDE4F50AD8BC9462DE76741CCAFF87D51E2",
|
||||
|
@ -239,7 +278,8 @@ And the `priv_validator.json`:
|
|||
"value" : "JPivl82x+LfVkp8i3ztoTjY6c6GJ4pBxQexErOCyhwqHeGT5ATxzpAtPJKnxNx/NyUnD8Ebv3OIYH+kgD4N88Q==",
|
||||
"type" : "tendermint/PrivKeyEd25519"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `priv_validator.json` actually contains a private key, and should
|
||||
thus be kept absolutely secret; for now we work with the plain text.
|
||||
|
@ -272,6 +312,7 @@ with the consensus protocol.
|
|||
### Peers
|
||||
|
||||
#### Seed
|
||||
|
||||
A seed node is a node who relays the addresses of other peers which they know
|
||||
of. These nodes constantly crawl the network to try to get more peers. The
|
||||
addresses which the seed node relays get saved into a local address book. Once
|
||||
|
@ -282,6 +323,7 @@ only need them on the first start. The seed node will immediately disconnect
|
|||
from you after sending you some addresses.
|
||||
|
||||
#### Persistent Peer
|
||||
|
||||
Persistent peers are people you want to be constantly connected with. If you
|
||||
disconnect you will try to connect directly back to them as opposed to using
|
||||
another address from the address book. On restarts you will always try to
|
||||
|
@ -302,12 +344,16 @@ persistent connections with.
|
|||
|
||||
For example,
|
||||
|
||||
tendermint node --p2p.seeds "f9baeaa15fedf5e1ef7448dd60f46c01f1a9e9c4@1.2.3.4:26656,0491d373a8e0fcf1023aaf18c51d6a1d0d4f31bd@5.6.7.8:26656"
|
||||
```
|
||||
tendermint node --p2p.seeds "f9baeaa15fedf5e1ef7448dd60f46c01f1a9e9c4@1.2.3.4:26656,0491d373a8e0fcf1023aaf18c51d6a1d0d4f31bd@5.6.7.8:26656"
|
||||
```
|
||||
|
||||
Alternatively, you can use the `/dial_seeds` endpoint of the RPC to
|
||||
specify seeds for a running node to connect to:
|
||||
|
||||
curl 'localhost:26657/dial_seeds?seeds=\["f9baeaa15fedf5e1ef7448dd60f46c01f1a9e9c4@1.2.3.4:26656","0491d373a8e0fcf1023aaf18c51d6a1d0d4f31bd@5.6.7.8:26656"\]'
|
||||
```
|
||||
curl 'localhost:26657/dial_seeds?seeds=\["f9baeaa15fedf5e1ef7448dd60f46c01f1a9e9c4@1.2.3.4:26656","0491d373a8e0fcf1023aaf18c51d6a1d0d4f31bd@5.6.7.8:26656"\]'
|
||||
```
|
||||
|
||||
Note, with PeX enabled, you
|
||||
should not need seeds after the first start.
|
||||
|
@ -318,8 +364,11 @@ maintain a persistent connection with each, you can use the
|
|||
`config.toml` or the `/dial_peers` RPC endpoint to do it without
|
||||
stopping Tendermint core instance.
|
||||
|
||||
tendermint node --p2p.persistent_peers "429fcf25974313b95673f58d77eacdd434402665@10.11.12.13:26656,96663a3dd0d7b9d17d4c8211b191af259621c693@10.11.12.14:26656"
|
||||
curl 'localhost:26657/dial_peers?persistent=true&peers=\["429fcf25974313b95673f58d77eacdd434402665@10.11.12.13:26656","96663a3dd0d7b9d17d4c8211b191af259621c693@10.11.12.14:26656"\]'
|
||||
```
|
||||
tendermint node --p2p.persistent_peers "429fcf25974313b95673f58d77eacdd434402665@10.11.12.13:26656,96663a3dd0d7b9d17d4c8211b191af259621c693@10.11.12.14:26656"
|
||||
|
||||
curl 'localhost:26657/dial_peers?persistent=true&peers=\["429fcf25974313b95673f58d77eacdd434402665@10.11.12.13:26656","96663a3dd0d7b9d17d4c8211b191af259621c693@10.11.12.14:26656"\]'
|
||||
```
|
||||
|
||||
### Adding a Non-Validator
|
||||
|
||||
|
@ -338,12 +387,15 @@ before starting the network. For instance, we could make a new
|
|||
|
||||
We can generate a new `priv_validator.json` with the command:
|
||||
|
||||
tendermint gen_validator
|
||||
```
|
||||
tendermint gen_validator
|
||||
```
|
||||
|
||||
Now we can update our genesis file. For instance, if the new
|
||||
`priv_validator.json` looks like:
|
||||
|
||||
{
|
||||
```
|
||||
{
|
||||
"address" : "5AF49D2A2D4F5AD4C7C8C4CC2FB020131E9C4902",
|
||||
"pub_key" : {
|
||||
"value" : "l9X9+fjkeBzDfPGbUM7AMIRE6uJN78zN5+lk5OYotek=",
|
||||
|
@ -356,11 +408,13 @@ Now we can update our genesis file. For instance, if the new
|
|||
"last_step" : 0,
|
||||
"last_round" : "0",
|
||||
"last_height" : "0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
then the new `genesis.json` will be:
|
||||
|
||||
{
|
||||
```
|
||||
{
|
||||
"validators" : [
|
||||
{
|
||||
"pub_key" : {
|
||||
|
@ -382,7 +436,8 @@ then the new `genesis.json` will be:
|
|||
"app_hash" : "",
|
||||
"chain_id" : "test-chain-rDlYSN",
|
||||
"genesis_time" : "0001-01-01T00:00:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Update the `genesis.json` in `~/.tendermint/config`. Copy the genesis
|
||||
file and the new `priv_validator.json` to the `~/.tendermint/config` on
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue