Merge pull request #1903 from tendermint/zach/lint-md
lint the docs markdown & fix codeblocks for website
This commit is contained in:
commit
82a5e9604c
308
docs/abci-cli.md
308
docs/abci-cli.md
|
@ -10,41 +10,47 @@ Make sure you [have Go installed](https://golang.org/doc/install).
|
||||||
|
|
||||||
Next, install the `abci-cli` tool and example applications:
|
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:
|
to get vendored dependencies:
|
||||||
|
|
||||||
cd $GOPATH/src/github.com/tendermint/tendermint
|
```
|
||||||
make get_tools
|
cd $GOPATH/src/github.com/tendermint/tendermint
|
||||||
make get_vendor_deps
|
make get_tools
|
||||||
make install_abci
|
make get_vendor_deps
|
||||||
|
make install_abci
|
||||||
|
```
|
||||||
|
|
||||||
Now run `abci-cli` to see the list of commands:
|
Now run `abci-cli` to see the list of commands:
|
||||||
|
|
||||||
Usage:
|
```
|
||||||
abci-cli [command]
|
Usage:
|
||||||
|
abci-cli [command]
|
||||||
|
|
||||||
Available Commands:
|
Available Commands:
|
||||||
batch Run a batch of abci commands against an application
|
batch Run a batch of abci commands against an application
|
||||||
check_tx Validate a tx
|
check_tx Validate a tx
|
||||||
commit Commit the application state and return the Merkle root hash
|
commit Commit the application state and return the Merkle root hash
|
||||||
console Start an interactive abci console for multiple commands
|
console Start an interactive abci console for multiple commands
|
||||||
counter ABCI demo example
|
counter ABCI demo example
|
||||||
deliver_tx Deliver a new tx to the application
|
deliver_tx Deliver a new tx to the application
|
||||||
kvstore ABCI demo example
|
kvstore ABCI demo example
|
||||||
echo Have the application echo a message
|
echo Have the application echo a message
|
||||||
help Help about any command
|
help Help about any command
|
||||||
info Get some info about the application
|
info Get some info about the application
|
||||||
query Query the application state
|
query Query the application state
|
||||||
set_option Set an options on the application
|
set_option Set an options on the application
|
||||||
|
|
||||||
Flags:
|
Flags:
|
||||||
--abci string socket or grpc (default "socket")
|
--abci string socket or grpc (default "socket")
|
||||||
--address string address of application socket (default "tcp://127.0.0.1:26658")
|
--address string address of application socket (default "tcp://127.0.0.1:26658")
|
||||||
-h, --help help for abci-cli
|
-h, --help help for abci-cli
|
||||||
-v, --verbose print the command and results as if it were a console session
|
-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
|
## KVStore - First Example
|
||||||
|
|
||||||
|
@ -63,59 +69,69 @@ Its code can be found
|
||||||
[here](https://github.com/tendermint/tendermint/blob/develop/abci/cmd/abci-cli/abci-cli.go)
|
[here](https://github.com/tendermint/tendermint/blob/develop/abci/cmd/abci-cli/abci-cli.go)
|
||||||
and looks like:
|
and looks like:
|
||||||
|
|
||||||
func cmdKVStore(cmd *cobra.Command, args []string) error {
|
```
|
||||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
|
func cmdKVStore(cmd *cobra.Command, args []string) error {
|
||||||
|
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
|
||||||
|
|
||||||
// Create the application - in memory or persisted to disk
|
// Create the application - in memory or persisted to disk
|
||||||
var app types.Application
|
var app types.Application
|
||||||
if flagPersist == "" {
|
if flagPersist == "" {
|
||||||
app = kvstore.NewKVStoreApplication()
|
app = kvstore.NewKVStoreApplication()
|
||||||
} else {
|
} else {
|
||||||
app = kvstore.NewPersistentKVStoreApplication(flagPersist)
|
app = kvstore.NewPersistentKVStoreApplication(flagPersist)
|
||||||
app.(*kvstore.PersistentKVStoreApplication).SetLogger(logger.With("module", "kvstore"))
|
app.(*kvstore.PersistentKVStoreApplication).SetLogger(logger.With("module", "kvstore"))
|
||||||
}
|
|
||||||
|
|
||||||
// Start the listener
|
|
||||||
srv, err := server.NewServer(flagAddrD, flagAbci, app)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
srv.SetLogger(logger.With("module", "abci-server"))
|
|
||||||
if err := srv.Start(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait forever
|
|
||||||
cmn.TrapSignal(func() {
|
|
||||||
// Cleanup
|
|
||||||
srv.Stop()
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start the listener
|
||||||
|
srv, err := server.NewServer(flagAddrD, flagAbci, app)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
srv.SetLogger(logger.With("module", "abci-server"))
|
||||||
|
if err := srv.Start(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait forever
|
||||||
|
cmn.TrapSignal(func() {
|
||||||
|
// Cleanup
|
||||||
|
srv.Stop()
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Start by running:
|
Start by running:
|
||||||
|
|
||||||
abci-cli kvstore
|
```
|
||||||
|
abci-cli kvstore
|
||||||
|
```
|
||||||
|
|
||||||
And in another terminal, run
|
And in another terminal, run
|
||||||
|
|
||||||
abci-cli echo hello
|
```
|
||||||
abci-cli info
|
abci-cli echo hello
|
||||||
|
abci-cli info
|
||||||
|
```
|
||||||
|
|
||||||
You'll see something like:
|
You'll see something like:
|
||||||
|
|
||||||
-> data: hello
|
```
|
||||||
-> data.hex: 68656C6C6F
|
-> data: hello
|
||||||
|
-> data.hex: 68656C6C6F
|
||||||
|
```
|
||||||
|
|
||||||
and:
|
and:
|
||||||
|
|
||||||
-> data: {"size":0}
|
```
|
||||||
-> data.hex: 7B2273697A65223A307D
|
-> data: {"size":0}
|
||||||
|
-> data.hex: 7B2273697A65223A307D
|
||||||
|
```
|
||||||
|
|
||||||
An ABCI application must provide two things:
|
An ABCI application must provide two things:
|
||||||
|
|
||||||
- a socket server
|
- a socket server
|
||||||
- a handler for ABCI messages
|
- a handler for ABCI messages
|
||||||
|
|
||||||
When we run the `abci-cli` tool we open a new connection to the
|
When we run the `abci-cli` tool we open a new connection to the
|
||||||
application's socket server, send the given ABCI message, and wait for a
|
application's socket server, send the given ABCI message, and wait for a
|
||||||
|
@ -144,52 +160,54 @@ speaking ABCI messages to your application.
|
||||||
|
|
||||||
Try running these commands:
|
Try running these commands:
|
||||||
|
|
||||||
> echo hello
|
```
|
||||||
-> code: OK
|
> echo hello
|
||||||
-> data: hello
|
-> code: OK
|
||||||
-> data.hex: 0x68656C6C6F
|
-> data: hello
|
||||||
|
-> data.hex: 0x68656C6C6F
|
||||||
|
|
||||||
> info
|
> info
|
||||||
-> code: OK
|
-> code: OK
|
||||||
-> data: {"size":0}
|
-> data: {"size":0}
|
||||||
-> data.hex: 0x7B2273697A65223A307D
|
-> data.hex: 0x7B2273697A65223A307D
|
||||||
|
|
||||||
> commit
|
> commit
|
||||||
-> code: OK
|
-> code: OK
|
||||||
-> data.hex: 0x0000000000000000
|
-> data.hex: 0x0000000000000000
|
||||||
|
|
||||||
> deliver_tx "abc"
|
> deliver_tx "abc"
|
||||||
-> code: OK
|
-> code: OK
|
||||||
|
|
||||||
> info
|
> info
|
||||||
-> code: OK
|
-> code: OK
|
||||||
-> data: {"size":1}
|
-> data: {"size":1}
|
||||||
-> data.hex: 0x7B2273697A65223A317D
|
-> data.hex: 0x7B2273697A65223A317D
|
||||||
|
|
||||||
> commit
|
> commit
|
||||||
-> code: OK
|
-> code: OK
|
||||||
-> data.hex: 0x0200000000000000
|
-> data.hex: 0x0200000000000000
|
||||||
|
|
||||||
> query "abc"
|
> query "abc"
|
||||||
-> code: OK
|
-> code: OK
|
||||||
-> log: exists
|
-> log: exists
|
||||||
-> height: 0
|
-> height: 0
|
||||||
-> value: abc
|
-> value: abc
|
||||||
-> value.hex: 616263
|
-> value.hex: 616263
|
||||||
|
|
||||||
> deliver_tx "def=xyz"
|
> deliver_tx "def=xyz"
|
||||||
-> code: OK
|
-> code: OK
|
||||||
|
|
||||||
> commit
|
> commit
|
||||||
-> code: OK
|
-> code: OK
|
||||||
-> data.hex: 0x0400000000000000
|
-> data.hex: 0x0400000000000000
|
||||||
|
|
||||||
> query "def"
|
> query "def"
|
||||||
-> code: OK
|
-> code: OK
|
||||||
-> log: exists
|
-> log: exists
|
||||||
-> height: 0
|
-> height: 0
|
||||||
-> value: xyz
|
-> value: xyz
|
||||||
-> value.hex: 78797A
|
-> value.hex: 78797A
|
||||||
|
```
|
||||||
|
|
||||||
Note that if we do `deliver_tx "abc"` it will store `(abc, abc)`, but if
|
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)`.
|
we do `deliver_tx "abc=efg"` it will store `(abc, efg)`.
|
||||||
|
@ -206,29 +224,31 @@ Like the kvstore app, its code can be found
|
||||||
[here](https://github.com/tendermint/tendermint/blob/master/abci/cmd/abci-cli/abci-cli.go)
|
[here](https://github.com/tendermint/tendermint/blob/master/abci/cmd/abci-cli/abci-cli.go)
|
||||||
and looks like:
|
and looks like:
|
||||||
|
|
||||||
func cmdCounter(cmd *cobra.Command, args []string) error {
|
```
|
||||||
|
func cmdCounter(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
app := counter.NewCounterApplication(flagSerial)
|
app := counter.NewCounterApplication(flagSerial)
|
||||||
|
|
||||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
|
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
|
||||||
|
|
||||||
// Start the listener
|
// Start the listener
|
||||||
srv, err := server.NewServer(flagAddrC, flagAbci, app)
|
srv, err := server.NewServer(flagAddrC, flagAbci, app)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
|
||||||
srv.SetLogger(logger.With("module", "abci-server"))
|
|
||||||
if err := srv.Start(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait forever
|
|
||||||
cmn.TrapSignal(func() {
|
|
||||||
// Cleanup
|
|
||||||
srv.Stop()
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
srv.SetLogger(logger.With("module", "abci-server"))
|
||||||
|
if err := srv.Start(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait forever
|
||||||
|
cmn.TrapSignal(func() {
|
||||||
|
// Cleanup
|
||||||
|
srv.Stop()
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
The counter app doesn't use a Merkle tree, it just counts how many times
|
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
|
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
|
Let's kill the console and the kvstore application, and start the
|
||||||
counter app:
|
counter app:
|
||||||
|
|
||||||
abci-cli counter
|
```
|
||||||
|
abci-cli counter
|
||||||
|
```
|
||||||
|
|
||||||
In another window, start the `abci-cli console`:
|
In another window, start the `abci-cli console`:
|
||||||
|
|
||||||
> set_option serial on
|
```
|
||||||
-> code: OK
|
> set_option serial on
|
||||||
-> log: OK (SetOption doesn't return anything.)
|
-> code: OK
|
||||||
|
-> log: OK (SetOption doesn't return anything.)
|
||||||
|
|
||||||
> check_tx 0x00
|
> check_tx 0x00
|
||||||
-> code: OK
|
-> code: OK
|
||||||
|
|
||||||
> check_tx 0xff
|
> check_tx 0xff
|
||||||
-> code: OK
|
-> code: OK
|
||||||
|
|
||||||
> deliver_tx 0x00
|
> deliver_tx 0x00
|
||||||
-> code: OK
|
-> code: OK
|
||||||
|
|
||||||
> check_tx 0x00
|
> check_tx 0x00
|
||||||
-> code: BadNonce
|
-> code: BadNonce
|
||||||
-> log: Invalid nonce. Expected >= 1, got 0
|
-> log: Invalid nonce. Expected >= 1, got 0
|
||||||
|
|
||||||
> deliver_tx 0x01
|
> deliver_tx 0x01
|
||||||
-> code: OK
|
-> code: OK
|
||||||
|
|
||||||
> deliver_tx 0x04
|
> deliver_tx 0x04
|
||||||
-> code: BadNonce
|
-> code: BadNonce
|
||||||
-> log: Invalid nonce. Expected 2, got 4
|
-> log: Invalid nonce. Expected 2, got 4
|
||||||
|
|
||||||
> info
|
> info
|
||||||
-> code: OK
|
-> code: OK
|
||||||
-> data: {"hashes":0,"txs":2}
|
-> data: {"hashes":0,"txs":2}
|
||||||
-> data.hex: 0x7B22686173686573223A302C22747873223A327D
|
-> data.hex: 0x7B22686173686573223A302C22747873223A327D
|
||||||
|
```
|
||||||
|
|
||||||
This is a very simple application, but between `counter` and `kvstore`,
|
This is a very simple application, but between `counter` and `kvstore`,
|
||||||
its easy to see how you can build out arbitrary application states on
|
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
|
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
|
(you'll have to kill the other counter application process). In another
|
||||||
window, run the console and those previous ABCI commands. You should get
|
window, run the console and those previous ABCI commands. You should get
|
||||||
|
|
|
@ -36,9 +36,9 @@ Commit are included in the header of the next block.
|
||||||
Tendermint opens three connections to the application to handle the
|
Tendermint opens three connections to the application to handle the
|
||||||
different message types:
|
different message types:
|
||||||
|
|
||||||
- `Consensus Connection - InitChain, BeginBlock, DeliverTx, EndBlock, Commit`
|
- `Consensus Connection - InitChain, BeginBlock, DeliverTx, EndBlock, Commit`
|
||||||
- `Mempool Connection - CheckTx`
|
- `Mempool Connection - CheckTx`
|
||||||
- `Info Connection - Info, SetOption, Query`
|
- `Info Connection - Info, SetOption, Query`
|
||||||
|
|
||||||
The `Flush` message is used on every connection, and the `Echo` message
|
The `Flush` message is used on every connection, and the `Echo` message
|
||||||
is only used for debugging.
|
is only used for debugging.
|
||||||
|
@ -54,272 +54,272 @@ See below for more details on the message types and how they are used.
|
||||||
|
|
||||||
### Echo
|
### Echo
|
||||||
|
|
||||||
- **Request**:
|
- **Request**:
|
||||||
- `Message (string)`: A string to echo back
|
- `Message (string)`: A string to echo back
|
||||||
- **Response**:
|
- **Response**:
|
||||||
- `Message (string)`: The input string
|
- `Message (string)`: The input string
|
||||||
- **Usage**:
|
- **Usage**:
|
||||||
- Echo a string to test an abci client/server implementation
|
- Echo a string to test an abci client/server implementation
|
||||||
|
|
||||||
### Flush
|
### Flush
|
||||||
|
|
||||||
- **Usage**:
|
- **Usage**:
|
||||||
- Signals that messages queued on the client should be flushed to
|
- Signals that messages queued on the client should be flushed to
|
||||||
the server. It is called periodically by the client
|
the server. It is called periodically by the client
|
||||||
implementation to ensure asynchronous requests are actually
|
implementation to ensure asynchronous requests are actually
|
||||||
sent, and is called immediately to make a synchronous request,
|
sent, and is called immediately to make a synchronous request,
|
||||||
which returns when the Flush response comes back.
|
which returns when the Flush response comes back.
|
||||||
|
|
||||||
### Info
|
### Info
|
||||||
|
|
||||||
- **Request**:
|
- **Request**:
|
||||||
- `Version (string)`: The Tendermint version
|
- `Version (string)`: The Tendermint version
|
||||||
- **Response**:
|
- **Response**:
|
||||||
- `Data (string)`: Some arbitrary information
|
- `Data (string)`: Some arbitrary information
|
||||||
- `Version (Version)`: Version information
|
- `Version (Version)`: Version information
|
||||||
- `LastBlockHeight (int64)`: Latest block for which the app has
|
- `LastBlockHeight (int64)`: Latest block for which the app has
|
||||||
called Commit
|
called Commit
|
||||||
- `LastBlockAppHash ([]byte)`: Latest result of Commit
|
- `LastBlockAppHash ([]byte)`: Latest result of Commit
|
||||||
- **Usage**:
|
- **Usage**:
|
||||||
- Return information about the application state.
|
- Return information about the application state.
|
||||||
- Used to sync Tendermint with the application during a handshake
|
- Used to sync Tendermint with the application during a handshake
|
||||||
that happens on startup.
|
that happens on startup.
|
||||||
- Tendermint expects `LastBlockAppHash` and `LastBlockHeight` to
|
- Tendermint expects `LastBlockAppHash` and `LastBlockHeight` to
|
||||||
be updated during `Commit`, ensuring that `Commit` is never
|
be updated during `Commit`, ensuring that `Commit` is never
|
||||||
called twice for the same block height.
|
called twice for the same block height.
|
||||||
|
|
||||||
### SetOption
|
### SetOption
|
||||||
|
|
||||||
- **Request**:
|
- **Request**:
|
||||||
- `Key (string)`: Key to set
|
- `Key (string)`: Key to set
|
||||||
- `Value (string)`: Value to set for key
|
- `Value (string)`: Value to set for key
|
||||||
- **Response**:
|
- **Response**:
|
||||||
- `Code (uint32)`: Response code
|
- `Code (uint32)`: Response code
|
||||||
- `Log (string)`: The output of the application's logger. May
|
- `Log (string)`: The output of the application's logger. May
|
||||||
be non-deterministic.
|
be non-deterministic.
|
||||||
- `Info (string)`: Additional information. May
|
- `Info (string)`: Additional information. May
|
||||||
be non-deterministic.
|
be non-deterministic.
|
||||||
- **Usage**:
|
- **Usage**:
|
||||||
- Set non-consensus critical application specific options.
|
- Set non-consensus critical application specific options.
|
||||||
- e.g. Key="min-fee", Value="100fermion" could set the minimum fee
|
- e.g. Key="min-fee", Value="100fermion" could set the minimum fee
|
||||||
required for CheckTx (but not DeliverTx - that would be
|
required for CheckTx (but not DeliverTx - that would be
|
||||||
consensus critical).
|
consensus critical).
|
||||||
|
|
||||||
### InitChain
|
### InitChain
|
||||||
|
|
||||||
- **Request**:
|
- **Request**:
|
||||||
- `Validators ([]Validator)`: Initial genesis validators
|
- `Validators ([]Validator)`: Initial genesis validators
|
||||||
- `AppStateBytes ([]byte)`: Serialized initial application state
|
- `AppStateBytes ([]byte)`: Serialized initial application state
|
||||||
- **Response**:
|
- **Response**:
|
||||||
- `ConsensusParams (ConsensusParams)`: Initial
|
- `ConsensusParams (ConsensusParams)`: Initial
|
||||||
consensus-critical parameters.
|
consensus-critical parameters.
|
||||||
- `Validators ([]Validator)`: Initial validator set.
|
- `Validators ([]Validator)`: Initial validator set.
|
||||||
- **Usage**:
|
- **Usage**:
|
||||||
- Called once upon genesis.
|
- Called once upon genesis.
|
||||||
|
|
||||||
### Query
|
### Query
|
||||||
|
|
||||||
- **Request**:
|
- **Request**:
|
||||||
- `Data ([]byte)`: Raw query bytes. Can be used with or in lieu
|
- `Data ([]byte)`: Raw query bytes. Can be used with or in lieu
|
||||||
of Path.
|
of Path.
|
||||||
- `Path (string)`: Path of request, like an HTTP GET path. Can be
|
- `Path (string)`: Path of request, like an HTTP GET path. Can be
|
||||||
used with or in liue of Data.
|
used with or in liue of Data.
|
||||||
- Apps MUST interpret '/store' as a query by key on the
|
- Apps MUST interpret '/store' as a query by key on the
|
||||||
underlying store. The key SHOULD be specified in the Data field.
|
underlying store. The key SHOULD be specified in the Data field.
|
||||||
- Apps SHOULD allow queries over specific types like
|
- Apps SHOULD allow queries over specific types like
|
||||||
'/accounts/...' or '/votes/...'
|
'/accounts/...' or '/votes/...'
|
||||||
- `Height (int64)`: The block height for which you want the query
|
- `Height (int64)`: The block height for which you want the query
|
||||||
(default=0 returns data for the latest committed block). Note
|
(default=0 returns data for the latest committed block). Note
|
||||||
that this is the height of the block containing the
|
that this is the height of the block containing the
|
||||||
application's Merkle root hash, which represents the state as it
|
application's Merkle root hash, which represents the state as it
|
||||||
was after committing the block at Height-1
|
was after committing the block at Height-1
|
||||||
- `Prove (bool)`: Return Merkle proof with response if possible
|
- `Prove (bool)`: Return Merkle proof with response if possible
|
||||||
- **Response**:
|
- **Response**:
|
||||||
- `Code (uint32)`: Response code.
|
- `Code (uint32)`: Response code.
|
||||||
- `Log (string)`: The output of the application's logger. May
|
- `Log (string)`: The output of the application's logger. May
|
||||||
be non-deterministic.
|
be non-deterministic.
|
||||||
- `Info (string)`: Additional information. May
|
- `Info (string)`: Additional information. May
|
||||||
be non-deterministic.
|
be non-deterministic.
|
||||||
- `Index (int64)`: The index of the key in the tree.
|
- `Index (int64)`: The index of the key in the tree.
|
||||||
- `Key ([]byte)`: The key of the matching data.
|
- `Key ([]byte)`: The key of the matching data.
|
||||||
- `Value ([]byte)`: The value of the matching data.
|
- `Value ([]byte)`: The value of the matching data.
|
||||||
- `Proof ([]byte)`: Proof for the data, if requested.
|
- `Proof ([]byte)`: Proof for the data, if requested.
|
||||||
- `Height (int64)`: The block height from which data was derived.
|
- `Height (int64)`: The block height from which data was derived.
|
||||||
Note that this is the height of the block containing the
|
Note that this is the height of the block containing the
|
||||||
application's Merkle root hash, which represents the state as it
|
application's Merkle root hash, which represents the state as it
|
||||||
was after committing the block at Height-1
|
was after committing the block at Height-1
|
||||||
- **Usage**:
|
- **Usage**:
|
||||||
- Query for data from the application at current or past height.
|
- Query for data from the application at current or past height.
|
||||||
- Optionally return Merkle proof.
|
- Optionally return Merkle proof.
|
||||||
|
|
||||||
### BeginBlock
|
### BeginBlock
|
||||||
|
|
||||||
- **Request**:
|
- **Request**:
|
||||||
- `Hash ([]byte)`: The block's hash. This can be derived from the
|
- `Hash ([]byte)`: The block's hash. This can be derived from the
|
||||||
block header.
|
block header.
|
||||||
- `Header (struct{})`: The block header
|
- `Header (struct{})`: The block header
|
||||||
- `Validators ([]SigningValidator)`: List of validators in the current validator
|
- `Validators ([]SigningValidator)`: List of validators in the current validator
|
||||||
set and whether or not they signed a vote in the LastCommit
|
set and whether or not they signed a vote in the LastCommit
|
||||||
- `ByzantineValidators ([]Evidence)`: List of evidence of
|
- `ByzantineValidators ([]Evidence)`: List of evidence of
|
||||||
validators that acted maliciously
|
validators that acted maliciously
|
||||||
- **Response**:
|
- **Response**:
|
||||||
- `Tags ([]cmn.KVPair)`: Key-Value tags for filtering and indexing
|
- `Tags ([]cmn.KVPair)`: Key-Value tags for filtering and indexing
|
||||||
- **Usage**:
|
- **Usage**:
|
||||||
- Signals the beginning of a new block. Called prior to
|
- Signals the beginning of a new block. Called prior to
|
||||||
any DeliverTxs.
|
any DeliverTxs.
|
||||||
- The header is expected to at least contain the Height.
|
- The header is expected to at least contain the Height.
|
||||||
- The `Validators` and `ByzantineValidators` can be used to
|
- The `Validators` and `ByzantineValidators` can be used to
|
||||||
determine rewards and punishments for the validators.
|
determine rewards and punishments for the validators.
|
||||||
|
|
||||||
### CheckTx
|
### CheckTx
|
||||||
|
|
||||||
- **Request**:
|
- **Request**:
|
||||||
- `Tx ([]byte)`: The request transaction bytes
|
- `Tx ([]byte)`: The request transaction bytes
|
||||||
- **Response**:
|
- **Response**:
|
||||||
- `Code (uint32)`: Response code
|
- `Code (uint32)`: Response code
|
||||||
- `Data ([]byte)`: Result bytes, if any.
|
- `Data ([]byte)`: Result bytes, if any.
|
||||||
- `Log (string)`: The output of the application's logger. May
|
- `Log (string)`: The output of the application's logger. May
|
||||||
be non-deterministic.
|
be non-deterministic.
|
||||||
- `Info (string)`: Additional information. May
|
- `Info (string)`: Additional information. May
|
||||||
be non-deterministic.
|
be non-deterministic.
|
||||||
- `GasWanted (int64)`: Amount of gas request for transaction.
|
- `GasWanted (int64)`: Amount of gas request for transaction.
|
||||||
- `GasUsed (int64)`: Amount of gas consumed by transaction.
|
- `GasUsed (int64)`: Amount of gas consumed by transaction.
|
||||||
- `Tags ([]cmn.KVPair)`: Key-Value tags for filtering and indexing
|
- `Tags ([]cmn.KVPair)`: Key-Value tags for filtering and indexing
|
||||||
transactions (eg. by account).
|
transactions (eg. by account).
|
||||||
- `Fee (cmn.KI64Pair)`: Fee paid for the transaction.
|
- `Fee (cmn.KI64Pair)`: Fee paid for the transaction.
|
||||||
- **Usage**: Validate a mempool transaction, prior to broadcasting
|
- **Usage**: Validate a mempool transaction, prior to broadcasting
|
||||||
or proposing. CheckTx should perform stateful but light-weight
|
or proposing. CheckTx should perform stateful but light-weight
|
||||||
checks of the validity of the transaction (like checking signatures
|
checks of the validity of the transaction (like checking signatures
|
||||||
and account balances), but need not execute in full (like running a
|
and account balances), but need not execute in full (like running a
|
||||||
smart contract).
|
smart contract).
|
||||||
|
|
||||||
Tendermint runs CheckTx and DeliverTx concurrently with eachother,
|
Tendermint runs CheckTx and DeliverTx concurrently with eachother,
|
||||||
though on distinct ABCI connections - the mempool connection and the
|
though on distinct ABCI connections - the mempool connection and the
|
||||||
consensus connection, respectively.
|
consensus connection, respectively.
|
||||||
|
|
||||||
The application should maintain a separate state to support CheckTx.
|
The application should maintain a separate state to support CheckTx.
|
||||||
This state can be reset to the latest committed state during
|
This state can be reset to the latest committed state during
|
||||||
`Commit`. Before calling Commit, Tendermint will lock and flush the mempool,
|
`Commit`. Before calling Commit, Tendermint will lock and flush the mempool,
|
||||||
ensuring that all existing CheckTx are responded to and no new ones can
|
ensuring that all existing CheckTx are responded to and no new ones can
|
||||||
begin. After `Commit`, the mempool will rerun
|
begin. After `Commit`, the mempool will rerun
|
||||||
CheckTx for all remaining transactions, throwing out any that are no longer valid.
|
CheckTx for all remaining transactions, throwing out any that are no longer valid.
|
||||||
Then the mempool will unlock and start sending CheckTx again.
|
Then the mempool will unlock and start sending CheckTx again.
|
||||||
|
|
||||||
Keys and values in Tags must be UTF-8 encoded strings (e.g.
|
Keys and values in Tags must be UTF-8 encoded strings (e.g.
|
||||||
"account.owner": "Bob", "balance": "100.0", "date": "2018-01-02")
|
"account.owner": "Bob", "balance": "100.0", "date": "2018-01-02")
|
||||||
|
|
||||||
### DeliverTx
|
### DeliverTx
|
||||||
|
|
||||||
- **Request**:
|
- **Request**:
|
||||||
- `Tx ([]byte)`: The request transaction bytes.
|
- `Tx ([]byte)`: The request transaction bytes.
|
||||||
- **Response**:
|
- **Response**:
|
||||||
- `Code (uint32)`: Response code.
|
- `Code (uint32)`: Response code.
|
||||||
- `Data ([]byte)`: Result bytes, if any.
|
- `Data ([]byte)`: Result bytes, if any.
|
||||||
- `Log (string)`: The output of the application's logger. May
|
- `Log (string)`: The output of the application's logger. May
|
||||||
be non-deterministic.
|
be non-deterministic.
|
||||||
- `Info (string)`: Additional information. May
|
- `Info (string)`: Additional information. May
|
||||||
be non-deterministic.
|
be non-deterministic.
|
||||||
- `GasWanted (int64)`: Amount of gas requested for transaction.
|
- `GasWanted (int64)`: Amount of gas requested for transaction.
|
||||||
- `GasUsed (int64)`: Amount of gas consumed by transaction.
|
- `GasUsed (int64)`: Amount of gas consumed by transaction.
|
||||||
- `Tags ([]cmn.KVPair)`: Key-Value tags for filtering and indexing
|
- `Tags ([]cmn.KVPair)`: Key-Value tags for filtering and indexing
|
||||||
transactions (eg. by account).
|
transactions (eg. by account).
|
||||||
- `Fee (cmn.KI64Pair)`: Fee paid for the transaction.
|
- `Fee (cmn.KI64Pair)`: Fee paid for the transaction.
|
||||||
- **Usage**:
|
- **Usage**:
|
||||||
- Deliver a transaction to be executed in full by the application.
|
- Deliver a transaction to be executed in full by the application.
|
||||||
If the transaction is valid, returns CodeType.OK.
|
If the transaction is valid, returns CodeType.OK.
|
||||||
- Keys and values in Tags must be UTF-8 encoded strings (e.g.
|
- Keys and values in Tags must be UTF-8 encoded strings (e.g.
|
||||||
"account.owner": "Bob", "balance": "100.0",
|
"account.owner": "Bob", "balance": "100.0",
|
||||||
"time": "2018-01-02T12:30:00Z")
|
"time": "2018-01-02T12:30:00Z")
|
||||||
|
|
||||||
### EndBlock
|
### EndBlock
|
||||||
|
|
||||||
- **Request**:
|
- **Request**:
|
||||||
- `Height (int64)`: Height of the block just executed.
|
- `Height (int64)`: Height of the block just executed.
|
||||||
- **Response**:
|
- **Response**:
|
||||||
- `ValidatorUpdates ([]Validator)`: Changes to validator set (set
|
- `ValidatorUpdates ([]Validator)`: Changes to validator set (set
|
||||||
voting power to 0 to remove).
|
voting power to 0 to remove).
|
||||||
- `ConsensusParamUpdates (ConsensusParams)`: Changes to
|
- `ConsensusParamUpdates (ConsensusParams)`: Changes to
|
||||||
consensus-critical time, size, and other parameters.
|
consensus-critical time, size, and other parameters.
|
||||||
- `Tags ([]cmn.KVPair)`: Key-Value tags for filtering and indexing
|
- `Tags ([]cmn.KVPair)`: Key-Value tags for filtering and indexing
|
||||||
- **Usage**:
|
- **Usage**:
|
||||||
- Signals the end of a block.
|
- Signals the end of a block.
|
||||||
- Called prior to each Commit, after all transactions.
|
- Called prior to each Commit, after all transactions.
|
||||||
- Validator set and consensus params are updated with the result.
|
- Validator set and consensus params are updated with the result.
|
||||||
- Validator pubkeys are expected to be go-wire encoded.
|
- Validator pubkeys are expected to be go-wire encoded.
|
||||||
|
|
||||||
### Commit
|
### Commit
|
||||||
|
|
||||||
- **Response**:
|
- **Response**:
|
||||||
- `Data ([]byte)`: The Merkle root hash
|
- `Data ([]byte)`: The Merkle root hash
|
||||||
- **Usage**:
|
- **Usage**:
|
||||||
- Persist the application state.
|
- Persist the application state.
|
||||||
- Return a Merkle root hash of the application state.
|
- Return a Merkle root hash of the application state.
|
||||||
- It's critical that all application instances return the
|
- It's critical that all application instances return the
|
||||||
same hash. If not, they will not be able to agree on the next
|
same hash. If not, they will not be able to agree on the next
|
||||||
block, because the hash is included in the next block!
|
block, because the hash is included in the next block!
|
||||||
|
|
||||||
## Data Messages
|
## Data Messages
|
||||||
|
|
||||||
### Header
|
### Header
|
||||||
|
|
||||||
- **Fields**:
|
- **Fields**:
|
||||||
- `ChainID (string)`: ID of the blockchain
|
- `ChainID (string)`: ID of the blockchain
|
||||||
- `Height (int64)`: Height of the block in the chain
|
- `Height (int64)`: Height of the block in the chain
|
||||||
- `Time (int64)`: Unix time of the block
|
- `Time (int64)`: Unix time of the block
|
||||||
- `NumTxs (int32)`: Number of transactions in the block
|
- `NumTxs (int32)`: Number of transactions in the block
|
||||||
- `TotalTxs (int64)`: Total number of transactions in the blockchain until
|
- `TotalTxs (int64)`: Total number of transactions in the blockchain until
|
||||||
now
|
now
|
||||||
- `LastBlockHash ([]byte)`: Hash of the previous (parent) block
|
- `LastBlockHash ([]byte)`: Hash of the previous (parent) block
|
||||||
- `ValidatorsHash ([]byte)`: Hash of the validator set for this block
|
- `ValidatorsHash ([]byte)`: Hash of the validator set for this block
|
||||||
- `AppHash ([]byte)`: Data returned by the last call to `Commit` - typically the
|
- `AppHash ([]byte)`: Data returned by the last call to `Commit` - typically the
|
||||||
Merkle root of the application state after executing the previous block's
|
Merkle root of the application state after executing the previous block's
|
||||||
transactions
|
transactions
|
||||||
- `Proposer (Validator)`: Original proposer for the block
|
- `Proposer (Validator)`: Original proposer for the block
|
||||||
- **Usage**:
|
- **Usage**:
|
||||||
- Provided in RequestBeginBlock
|
- Provided in RequestBeginBlock
|
||||||
- Provides important context about the current state of the blockchain -
|
- Provides important context about the current state of the blockchain -
|
||||||
especially height and time.
|
especially height and time.
|
||||||
- Provides the proposer of the current block, for use in proposer-based
|
- Provides the proposer of the current block, for use in proposer-based
|
||||||
reward mechanisms.
|
reward mechanisms.
|
||||||
|
|
||||||
### Validator
|
### Validator
|
||||||
|
|
||||||
- **Fields**:
|
- **Fields**:
|
||||||
- `Address ([]byte)`: Address of the validator (hash of the public key)
|
- `Address ([]byte)`: Address of the validator (hash of the public key)
|
||||||
- `PubKey (PubKey)`: Public key of the validator
|
- `PubKey (PubKey)`: Public key of the validator
|
||||||
- `Power (int64)`: Voting power of the validator
|
- `Power (int64)`: Voting power of the validator
|
||||||
- **Usage**:
|
- **Usage**:
|
||||||
- Provides all identifying information about the validator
|
- Provides all identifying information about the validator
|
||||||
|
|
||||||
### SigningValidator
|
### SigningValidator
|
||||||
|
|
||||||
- **Fields**:
|
- **Fields**:
|
||||||
- `Validator (Validator)`: A validator
|
- `Validator (Validator)`: A validator
|
||||||
- `SignedLastBlock (bool)`: Indicated whether or not the validator signed
|
- `SignedLastBlock (bool)`: Indicated whether or not the validator signed
|
||||||
the last block
|
the last block
|
||||||
- **Usage**:
|
- **Usage**:
|
||||||
- Indicates whether a validator signed the last block, allowing for rewards
|
- Indicates whether a validator signed the last block, allowing for rewards
|
||||||
based on validator availability
|
based on validator availability
|
||||||
|
|
||||||
### PubKey
|
### PubKey
|
||||||
|
|
||||||
- **Fields**:
|
- **Fields**:
|
||||||
- `Type (string)`: Type of the public key. A simple string like `"ed25519"`.
|
- `Type (string)`: Type of the public key. A simple string like `"ed25519"`.
|
||||||
In the future, may indicate a serialization algorithm to parse the `Data`,
|
In the future, may indicate a serialization algorithm to parse the `Data`,
|
||||||
for instance `"amino"`.
|
for instance `"amino"`.
|
||||||
- `Data ([]byte)`: Public key data. For a simple public key, it's just the
|
- `Data ([]byte)`: Public key data. For a simple public key, it's just the
|
||||||
raw bytes. If the `Type` indicates an encoding algorithm, this is the
|
raw bytes. If the `Type` indicates an encoding algorithm, this is the
|
||||||
encoded public key.
|
encoded public key.
|
||||||
- **Usage**:
|
- **Usage**:
|
||||||
- A generic and extensible typed public key
|
- A generic and extensible typed public key
|
||||||
|
|
||||||
### Evidence
|
### Evidence
|
||||||
|
|
||||||
- **Fields**:
|
- **Fields**:
|
||||||
- `Type (string)`: Type of the evidence. A hierarchical path like
|
- `Type (string)`: Type of the evidence. A hierarchical path like
|
||||||
"duplicate/vote".
|
"duplicate/vote".
|
||||||
- `Validator (Validator`: The offending validator
|
- `Validator (Validator`: The offending validator
|
||||||
- `Height (int64)`: Height when the offense was committed
|
- `Height (int64)`: Height when the offense was committed
|
||||||
- `Time (int64)`: Unix time of the block at height `Height`
|
- `Time (int64)`: Unix time of the block at height `Height`
|
||||||
- `TotalVotingPower (int64)`: Total voting power of the validator set at
|
- `TotalVotingPower (int64)`: Total voting power of the validator set at
|
||||||
height `Height`
|
height `Height`
|
||||||
|
|
|
@ -17,7 +17,7 @@ transaction is actually processed.
|
||||||
|
|
||||||
The ABCI application must be a deterministic result of the Tendermint
|
The ABCI application must be a deterministic result of the Tendermint
|
||||||
consensus - any external influence on the application state that didn't
|
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.
|
should communicate with the application except Tendermint via ABCI.
|
||||||
|
|
||||||
If the application is written in Go, it can be compiled into the
|
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.
|
Tendermint.
|
||||||
|
|
||||||
See the following for more extensive documentation:
|
See the following for more extensive documentation:
|
||||||
|
|
||||||
- [Interchain Standard for the Light-Client REST API](https://github.com/cosmos/cosmos-sdk/pull/1028)
|
- [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 RPC Docs](https://tendermint.github.io/slate/)
|
||||||
- [Tendermint in Production](https://github.com/tendermint/tendermint/pull/1618)
|
- [Tendermint in Production](https://github.com/tendermint/tendermint/pull/1618)
|
||||||
|
|
|
@ -16,28 +16,27 @@ committed in hash-linked blocks.
|
||||||
|
|
||||||
The ABCI design has a few distinct components:
|
The ABCI design has a few distinct components:
|
||||||
|
|
||||||
- message protocol
|
- message protocol
|
||||||
- pairs of request and response messages
|
- pairs of request and response messages
|
||||||
- consensus makes requests, application responds
|
- consensus makes requests, application responds
|
||||||
- defined using protobuf
|
- defined using protobuf
|
||||||
- server/client
|
- server/client
|
||||||
- consensus engine runs the client
|
- consensus engine runs the client
|
||||||
- application runs the server
|
- application runs the server
|
||||||
- two implementations:
|
- two implementations:
|
||||||
- async raw bytes
|
- async raw bytes
|
||||||
- grpc
|
- grpc
|
||||||
- blockchain protocol
|
- blockchain protocol
|
||||||
- abci is connection oriented
|
- abci is connection oriented
|
||||||
- Tendermint Core maintains three connections:
|
- Tendermint Core maintains three connections:
|
||||||
- [mempool connection](#mempool-connection): for checking if
|
- [mempool connection](#mempool-connection): for checking if
|
||||||
transactions should be relayed before they are committed;
|
transactions should be relayed before they are committed;
|
||||||
only uses `CheckTx`
|
only uses `CheckTx`
|
||||||
- [consensus connection](#consensus-connection): for executing
|
- [consensus connection](#consensus-connection): for executing
|
||||||
transactions that have been committed. Message sequence is
|
transactions that have been committed. Message sequence is
|
||||||
-for every block
|
-for every block -`BeginBlock, [DeliverTx, ...], EndBlock, Commit`
|
||||||
-`BeginBlock, [DeliverTx, ...], EndBlock, Commit`
|
- [query connection](#query-connection): for querying the
|
||||||
- [query connection](#query-connection): for querying the
|
application state; only uses Query and Info
|
||||||
application state; only uses Query and Info
|
|
||||||
|
|
||||||
The mempool and consensus logic act as clients, and each maintains an
|
The mempool and consensus logic act as clients, and each maintains an
|
||||||
open ABCI connection with the application, which hosts an ABCI server.
|
open ABCI connection with the application, which hosts an ABCI server.
|
||||||
|
@ -64,9 +63,9 @@ To use ABCI in your programming language of choice, there must be a ABCI
|
||||||
server in that language. Tendermint supports two kinds of implementation
|
server in that language. Tendermint supports two kinds of implementation
|
||||||
of the server:
|
of the server:
|
||||||
|
|
||||||
- Asynchronous, raw socket server (Tendermint Socket Protocol, also
|
- Asynchronous, raw socket server (Tendermint Socket Protocol, also
|
||||||
known as TSP or Teaspoon)
|
known as TSP or Teaspoon)
|
||||||
- GRPC
|
- GRPC
|
||||||
|
|
||||||
Both can be tested using the `abci-cli` by setting the `--abci` flag
|
Both can be tested using the `abci-cli` by setting the `--abci` flag
|
||||||
appropriately (ie. to `socket` or `grpc`).
|
appropriately (ie. to `socket` or `grpc`).
|
||||||
|
@ -161,7 +160,7 @@ connection, to query the local state of the app.
|
||||||
|
|
||||||
### Mempool Connection
|
### 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
|
are run using CheckTx in the same order they were received by the
|
||||||
validator. If the CheckTx returns `OK`, the transaction is kept in
|
validator. If the CheckTx returns `OK`, the transaction is kept in
|
||||||
memory and relayed to other peers in the same order it was received.
|
memory and relayed to other peers in the same order it was received.
|
||||||
|
@ -180,23 +179,27 @@ mempool state (this behaviour can be turned off with
|
||||||
|
|
||||||
In go:
|
In go:
|
||||||
|
|
||||||
func (app *KVStoreApplication) CheckTx(tx []byte) types.Result {
|
```
|
||||||
return types.OK
|
func (app *KVStoreApplication) CheckTx(tx []byte) types.Result {
|
||||||
}
|
return types.OK
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
In Java:
|
In Java:
|
||||||
|
|
||||||
ResponseCheckTx requestCheckTx(RequestCheckTx req) {
|
```
|
||||||
byte[] transaction = req.getTx().toByteArray();
|
ResponseCheckTx requestCheckTx(RequestCheckTx req) {
|
||||||
|
byte[] transaction = req.getTx().toByteArray();
|
||||||
|
|
||||||
// validate transaction
|
// validate transaction
|
||||||
|
|
||||||
if (notValid) {
|
if (notValid) {
|
||||||
return ResponseCheckTx.newBuilder().setCode(CodeType.BadNonce).setLog("invalid tx").build();
|
return ResponseCheckTx.newBuilder().setCode(CodeType.BadNonce).setLog("invalid tx").build();
|
||||||
} else {
|
} else {
|
||||||
return ResponseCheckTx.newBuilder().setCode(CodeType.OK).build();
|
return ResponseCheckTx.newBuilder().setCode(CodeType.OK).build();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Replay Protection
|
### Replay Protection
|
||||||
|
|
||||||
|
@ -242,43 +245,47 @@ merkle root of the data returned by the DeliverTx requests, or both.
|
||||||
|
|
||||||
In go:
|
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
|
||||||
parts := strings.Split(string(tx), "=")
|
func (app *KVStoreApplication) DeliverTx(tx []byte) types.Result {
|
||||||
if len(parts) == 2 {
|
parts := strings.Split(string(tx), "=")
|
||||||
app.state.Set([]byte(parts[0]), []byte(parts[1]))
|
if len(parts) == 2 {
|
||||||
} else {
|
app.state.Set([]byte(parts[0]), []byte(parts[1]))
|
||||||
app.state.Set(tx, tx)
|
} else {
|
||||||
}
|
app.state.Set(tx, tx)
|
||||||
return types.OK
|
}
|
||||||
}
|
return types.OK
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
In Java:
|
In Java:
|
||||||
|
|
||||||
/**
|
```
|
||||||
* Using Protobuf types from the protoc compiler, we always start with a byte[]
|
/**
|
||||||
*/
|
* Using Protobuf types from the protoc compiler, we always start with a byte[]
|
||||||
ResponseDeliverTx deliverTx(RequestDeliverTx request) {
|
*/
|
||||||
byte[] transaction = request.getTx().toByteArray();
|
ResponseDeliverTx deliverTx(RequestDeliverTx request) {
|
||||||
|
byte[] transaction = request.getTx().toByteArray();
|
||||||
|
|
||||||
// validate your transaction
|
// validate your transaction
|
||||||
|
|
||||||
if (notValid) {
|
|
||||||
return ResponseDeliverTx.newBuilder().setCode(CodeType.BadNonce).setLog("transaction was invalid").build();
|
|
||||||
} else {
|
|
||||||
ResponseDeliverTx.newBuilder().setCode(CodeType.OK).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (notValid) {
|
||||||
|
return ResponseDeliverTx.newBuilder().setCode(CodeType.BadNonce).setLog("transaction was invalid").build();
|
||||||
|
} else {
|
||||||
|
ResponseDeliverTx.newBuilder().setCode(CodeType.OK).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Commit
|
### Commit
|
||||||
|
|
||||||
Once all processing of the block is complete, Tendermint sends the
|
Once all processing of the block is complete, Tendermint sends the
|
||||||
Commit request and blocks waiting for a response. While the mempool may
|
Commit request and blocks waiting for a response. While the mempool may
|
||||||
run concurrently with block processing (the BeginBlock, DeliverTxs, and
|
run concurrently with block processing (the BeginBlock, DeliverTxs, and
|
||||||
EndBlock), it is locked for the Commit request so that its state can be
|
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
|
safely reset during Commit. This means the app _MUST NOT_ do any
|
||||||
blocking communication with the mempool (ie. broadcast\_tx) during
|
blocking communication with the mempool (ie. broadcast_tx) during
|
||||||
Commit, or there will be deadlock. Note also that all remaining
|
Commit, or there will be deadlock. Note also that all remaining
|
||||||
transactions in the mempool are replayed on the mempool connection
|
transactions in the mempool are replayed on the mempool connection
|
||||||
(CheckTx) following a commit.
|
(CheckTx) following a commit.
|
||||||
|
@ -294,21 +301,25 @@ job of the [Handshake](#handshake).
|
||||||
|
|
||||||
In go:
|
In go:
|
||||||
|
|
||||||
func (app *KVStoreApplication) Commit() types.Result {
|
```
|
||||||
hash := app.state.Hash()
|
func (app *KVStoreApplication) Commit() types.Result {
|
||||||
return types.NewResultOK(hash, "")
|
hash := app.state.Hash()
|
||||||
}
|
return types.NewResultOK(hash, "")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
In Java:
|
In Java:
|
||||||
|
|
||||||
ResponseCommit requestCommit(RequestCommit requestCommit) {
|
```
|
||||||
|
ResponseCommit requestCommit(RequestCommit requestCommit) {
|
||||||
|
|
||||||
// update the internal app-state
|
// update the internal app-state
|
||||||
byte[] newAppState = calculateAppState();
|
byte[] newAppState = calculateAppState();
|
||||||
|
|
||||||
// and return it to the node
|
// and return it to the node
|
||||||
return ResponseCommit.newBuilder().setCode(CodeType.OK).setData(ByteString.copyFrom(newAppState)).build();
|
return ResponseCommit.newBuilder().setCode(CodeType.OK).setData(ByteString.copyFrom(newAppState)).build();
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### BeginBlock
|
### BeginBlock
|
||||||
|
|
||||||
|
@ -322,31 +333,35 @@ pick up from when it restarts. See information on the Handshake, below.
|
||||||
|
|
||||||
In go:
|
In go:
|
||||||
|
|
||||||
// Track the block hash and header information
|
```
|
||||||
func (app *PersistentKVStoreApplication) BeginBlock(params types.RequestBeginBlock) {
|
// Track the block hash and header information
|
||||||
// update latest block info
|
func (app *PersistentKVStoreApplication) BeginBlock(params types.RequestBeginBlock) {
|
||||||
app.blockHeader = params.Header
|
// update latest block info
|
||||||
|
app.blockHeader = params.Header
|
||||||
|
|
||||||
// reset valset changes
|
// reset valset changes
|
||||||
app.changes = make([]*types.Validator, 0)
|
app.changes = make([]*types.Validator, 0)
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
In Java:
|
In Java:
|
||||||
|
|
||||||
/*
|
```
|
||||||
* all types come from protobuf definition
|
/*
|
||||||
*/
|
* all types come from protobuf definition
|
||||||
ResponseBeginBlock requestBeginBlock(RequestBeginBlock req) {
|
*/
|
||||||
|
ResponseBeginBlock requestBeginBlock(RequestBeginBlock req) {
|
||||||
|
|
||||||
Header header = req.getHeader();
|
Header header = req.getHeader();
|
||||||
byte[] prevAppHash = header.getAppHash().toByteArray();
|
byte[] prevAppHash = header.getAppHash().toByteArray();
|
||||||
long prevHeight = header.getHeight();
|
long prevHeight = header.getHeight();
|
||||||
long numTxs = header.getNumTxs();
|
long numTxs = header.getNumTxs();
|
||||||
|
|
||||||
// run your pre-block logic. Maybe prepare a state snapshot, message components, etc
|
// run your pre-block logic. Maybe prepare a state snapshot, message components, etc
|
||||||
|
|
||||||
return ResponseBeginBlock.newBuilder().build();
|
return ResponseBeginBlock.newBuilder().build();
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### EndBlock
|
### EndBlock
|
||||||
|
|
||||||
|
@ -364,25 +379,29 @@ for details on how it tracks validators.
|
||||||
|
|
||||||
In go:
|
In go:
|
||||||
|
|
||||||
// Update the validator set
|
```
|
||||||
func (app *PersistentKVStoreApplication) EndBlock(req types.RequestEndBlock) types.ResponseEndBlock {
|
// Update the validator set
|
||||||
return types.ResponseEndBlock{ValidatorUpdates: app.ValUpdates}
|
func (app *PersistentKVStoreApplication) EndBlock(req types.RequestEndBlock) types.ResponseEndBlock {
|
||||||
}
|
return types.ResponseEndBlock{ValidatorUpdates: app.ValUpdates}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
In Java:
|
In Java:
|
||||||
|
|
||||||
/*
|
```
|
||||||
* Assume that one validator changes. The new validator has a power of 10
|
/*
|
||||||
*/
|
* Assume that one validator changes. The new validator has a power of 10
|
||||||
ResponseEndBlock requestEndBlock(RequestEndBlock req) {
|
*/
|
||||||
final long currentHeight = req.getHeight();
|
ResponseEndBlock requestEndBlock(RequestEndBlock req) {
|
||||||
final byte[] validatorPubKey = getValPubKey();
|
final long currentHeight = req.getHeight();
|
||||||
|
final byte[] validatorPubKey = getValPubKey();
|
||||||
|
|
||||||
ResponseEndBlock.Builder builder = ResponseEndBlock.newBuilder();
|
ResponseEndBlock.Builder builder = ResponseEndBlock.newBuilder();
|
||||||
builder.addDiffs(1, Types.Validator.newBuilder().setPower(10L).setPubKey(ByteString.copyFrom(validatorPubKey)).build());
|
builder.addDiffs(1, Types.Validator.newBuilder().setPower(10L).setPubKey(ByteString.copyFrom(validatorPubKey)).build());
|
||||||
|
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Query Connection
|
### Query Connection
|
||||||
|
|
||||||
|
@ -398,67 +417,71 @@ connecting, according to IP address or node ID. For instance,
|
||||||
returning non-OK ABCI response to either of the following queries will
|
returning non-OK ABCI response to either of the following queries will
|
||||||
cause Tendermint to not connect to the corresponding peer:
|
cause Tendermint to not connect to the corresponding peer:
|
||||||
|
|
||||||
- `p2p/filter/addr/<ip addr>`, where `<ip addr>` is an IP address.
|
- `p2p/filter/addr/<ip addr>`, where `<ip addr>` is an IP address.
|
||||||
- `p2p/filter/id/<id>`, where `<is>` is the hex-encoded node ID (the hash of
|
- `p2p/filter/id/<id>`, where `<is>` is the hex-encoded node ID (the hash of
|
||||||
the node's p2p pubkey).
|
the node's p2p pubkey).
|
||||||
|
|
||||||
Note: these query formats are subject to change!
|
Note: these query formats are subject to change!
|
||||||
|
|
||||||
In go:
|
In go:
|
||||||
|
|
||||||
func (app *KVStoreApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) {
|
```
|
||||||
if reqQuery.Prove {
|
func (app *KVStoreApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) {
|
||||||
value, proof, exists := app.state.Proof(reqQuery.Data)
|
if reqQuery.Prove {
|
||||||
resQuery.Index = -1 // TODO make Proof return index
|
value, proof, exists := app.state.Proof(reqQuery.Data)
|
||||||
resQuery.Key = reqQuery.Data
|
resQuery.Index = -1 // TODO make Proof return index
|
||||||
resQuery.Value = value
|
resQuery.Key = reqQuery.Data
|
||||||
resQuery.Proof = proof
|
resQuery.Value = value
|
||||||
if exists {
|
resQuery.Proof = proof
|
||||||
resQuery.Log = "exists"
|
if exists {
|
||||||
} else {
|
resQuery.Log = "exists"
|
||||||
resQuery.Log = "does not exist"
|
} else {
|
||||||
}
|
resQuery.Log = "does not exist"
|
||||||
return
|
|
||||||
} else {
|
|
||||||
index, value, exists := app.state.Get(reqQuery.Data)
|
|
||||||
resQuery.Index = int64(index)
|
|
||||||
resQuery.Value = value
|
|
||||||
if exists {
|
|
||||||
resQuery.Log = "exists"
|
|
||||||
} else {
|
|
||||||
resQuery.Log = "does not exist"
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
index, value, exists := app.state.Get(reqQuery.Data)
|
||||||
|
resQuery.Index = int64(index)
|
||||||
|
resQuery.Value = value
|
||||||
|
if exists {
|
||||||
|
resQuery.Log = "exists"
|
||||||
|
} else {
|
||||||
|
resQuery.Log = "does not exist"
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
In Java:
|
In Java:
|
||||||
|
|
||||||
ResponseQuery requestQuery(RequestQuery req) {
|
```
|
||||||
final boolean isProveQuery = req.getProve();
|
ResponseQuery requestQuery(RequestQuery req) {
|
||||||
final ResponseQuery.Builder responseBuilder = ResponseQuery.newBuilder();
|
final boolean isProveQuery = req.getProve();
|
||||||
|
final ResponseQuery.Builder responseBuilder = ResponseQuery.newBuilder();
|
||||||
|
|
||||||
if (isProveQuery) {
|
if (isProveQuery) {
|
||||||
com.app.example.ProofResult proofResult = generateProof(req.getData().toByteArray());
|
com.app.example.ProofResult proofResult = generateProof(req.getData().toByteArray());
|
||||||
final byte[] proofAsByteArray = proofResult.getAsByteArray();
|
final byte[] proofAsByteArray = proofResult.getAsByteArray();
|
||||||
|
|
||||||
responseBuilder.setProof(ByteString.copyFrom(proofAsByteArray));
|
responseBuilder.setProof(ByteString.copyFrom(proofAsByteArray));
|
||||||
responseBuilder.setKey(req.getData());
|
responseBuilder.setKey(req.getData());
|
||||||
responseBuilder.setValue(ByteString.copyFrom(proofResult.getData()));
|
responseBuilder.setValue(ByteString.copyFrom(proofResult.getData()));
|
||||||
responseBuilder.setLog(result.getLogValue());
|
responseBuilder.setLog(result.getLogValue());
|
||||||
} else {
|
} else {
|
||||||
byte[] queryData = req.getData().toByteArray();
|
byte[] queryData = req.getData().toByteArray();
|
||||||
|
|
||||||
final com.app.example.QueryResult result = generateQueryResult(queryData);
|
final com.app.example.QueryResult result = generateQueryResult(queryData);
|
||||||
|
|
||||||
responseBuilder.setIndex(result.getIndex());
|
responseBuilder.setIndex(result.getIndex());
|
||||||
responseBuilder.setValue(ByteString.copyFrom(result.getValue()));
|
responseBuilder.setValue(ByteString.copyFrom(result.getValue()));
|
||||||
responseBuilder.setLog(result.getLogValue());
|
responseBuilder.setLog(result.getLogValue());
|
||||||
}
|
|
||||||
|
|
||||||
return responseBuilder.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return responseBuilder.build();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Handshake
|
### Handshake
|
||||||
|
|
||||||
When the app or tendermint restarts, they need to sync to a common
|
When the app or tendermint restarts, they need to sync to a common
|
||||||
|
@ -477,17 +500,21 @@ all blocks.
|
||||||
|
|
||||||
In go:
|
In go:
|
||||||
|
|
||||||
func (app *KVStoreApplication) Info(req types.RequestInfo) (resInfo types.ResponseInfo) {
|
```
|
||||||
return types.ResponseInfo{Data: cmn.Fmt("{\"size\":%v}", app.state.Size())}
|
func (app *KVStoreApplication) Info(req types.RequestInfo) (resInfo types.ResponseInfo) {
|
||||||
}
|
return types.ResponseInfo{Data: cmn.Fmt("{\"size\":%v}", app.state.Size())}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
In Java:
|
In Java:
|
||||||
|
|
||||||
ResponseInfo requestInfo(RequestInfo req) {
|
```
|
||||||
final byte[] lastAppHash = getLastAppHash();
|
ResponseInfo requestInfo(RequestInfo req) {
|
||||||
final long lastHeight = getLastHeight();
|
final byte[] lastAppHash = getLastAppHash();
|
||||||
return ResponseInfo.newBuilder().setLastBlockAppHash(ByteString.copyFrom(lastAppHash)).setLastBlockHeight(lastHeight).build();
|
final long lastHeight = getLastHeight();
|
||||||
}
|
return ResponseInfo.newBuilder().setLastBlockAppHash(ByteString.copyFrom(lastAppHash)).setLastBlockHeight(lastHeight).build();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Genesis
|
### Genesis
|
||||||
|
|
||||||
|
@ -497,31 +524,35 @@ consensus params.
|
||||||
|
|
||||||
In go:
|
In go:
|
||||||
|
|
||||||
// Save the validators in the merkle tree
|
```
|
||||||
func (app *PersistentKVStoreApplication) InitChain(params types.RequestInitChain) {
|
// Save the validators in the merkle tree
|
||||||
for _, v := range params.Validators {
|
func (app *PersistentKVStoreApplication) InitChain(params types.RequestInitChain) {
|
||||||
r := app.updateValidator(v)
|
for _, v := range params.Validators {
|
||||||
if r.IsErr() {
|
r := app.updateValidator(v)
|
||||||
app.logger.Error("Error updating validators", "r", r)
|
if r.IsErr() {
|
||||||
}
|
app.logger.Error("Error updating validators", "r", r)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
In Java:
|
In Java:
|
||||||
|
|
||||||
/*
|
```
|
||||||
* all types come from protobuf definition
|
/*
|
||||||
*/
|
* all types come from protobuf definition
|
||||||
ResponseInitChain requestInitChain(RequestInitChain req) {
|
*/
|
||||||
final int validatorsCount = req.getValidatorsCount();
|
ResponseInitChain requestInitChain(RequestInitChain req) {
|
||||||
final List<Types.Validator> validatorsList = req.getValidatorsList();
|
final int validatorsCount = req.getValidatorsCount();
|
||||||
|
final List<Types.Validator> validatorsList = req.getValidatorsList();
|
||||||
|
|
||||||
validatorsList.forEach((validator) -> {
|
validatorsList.forEach((validator) -> {
|
||||||
long power = validator.getPower();
|
long power = validator.getPower();
|
||||||
byte[] validatorPubKey = validator.getPubKey().toByteArray();
|
byte[] validatorPubKey = validator.getPubKey().toByteArray();
|
||||||
|
|
||||||
// do somehing for validator setup in app
|
// do somehing for validator setup in app
|
||||||
});
|
});
|
||||||
|
|
||||||
return ResponseInitChain.newBuilder().build();
|
return ResponseInitChain.newBuilder().build();
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -16,30 +16,29 @@ to.
|
||||||
|
|
||||||
Here are the steps to setting up a testnet manually:
|
Here are the steps to setting up a testnet manually:
|
||||||
|
|
||||||
1) Provision nodes on your cloud provider of choice
|
1. Provision nodes on your cloud provider of choice
|
||||||
2) Install Tendermint and the application of interest on all nodes
|
2. Install Tendermint and the application of interest on all nodes
|
||||||
3) Generate a private key and a node key for each validator using
|
3. Generate a private key and a node key for each validator using
|
||||||
`tendermint init`
|
`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.
|
`genesis.json` file and replace the existing file with it.
|
||||||
5) Run
|
5. Run
|
||||||
`tendermint node --proxy_app=kvstore --p2p.persistent_peers=< peer
|
`tendermint node --proxy_app=kvstore --p2p.persistent_peers=< peer addresses >` on each node, where `< peer addresses >` is a comma separated
|
||||||
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
|
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 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
|
`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
|
were `192.168.0.1, 192.168.0.2, 192.168.0.3, 192.168.0.4`, the command
|
||||||
would look like:
|
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
|
After a few seconds, all the nodes should connect to each other and
|
||||||
start making blocks! For more information, see the Tendermint Networks
|
start making blocks! For more information, see the Tendermint Networks
|
||||||
section of [the guide to using Tendermint](./using-tendermint.md).
|
section of [the guide to using Tendermint](./using-tendermint.md).
|
||||||
|
|
||||||
But wait! Steps 3, 4 and 5 are quite manual. Instead, use the `tendermint
|
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
|
||||||
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
|
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
|
it however if you provide the `--populate-persistent-peers` flag and optional
|
||||||
`--starting-ip-address` flag. Run `tendermint testnet --help` for more details
|
`--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:
|
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
|
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.
|
local testnet. Review the target in the Makefile to debug any problems.
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
The growing list of applications built using various pieces of the
|
The growing list of applications built using various pieces of the
|
||||||
Tendermint stack can be found at:
|
Tendermint stack can be found at:
|
||||||
|
|
||||||
- https://tendermint.com/ecosystem
|
- https://tendermint.com/ecosystem
|
||||||
|
|
||||||
We thank the community for their contributions thus far and welcome the
|
We thank the community for their contributions thus far and welcome the
|
||||||
addition of new projects. A pull request can be submitted to [this
|
addition of new projects. A pull request can be submitted to [this
|
||||||
|
|
|
@ -24,6 +24,7 @@ The script is also used to facilitate cluster deployment below.
|
||||||
### Manual Install
|
### Manual Install
|
||||||
|
|
||||||
Requires:
|
Requires:
|
||||||
|
|
||||||
- `go` minimum version 1.10
|
- `go` minimum version 1.10
|
||||||
- `$GOPATH` environment variable must be set
|
- `$GOPATH` environment variable must be set
|
||||||
- `$GOPATH/bin` must be on your `$PATH` (see https://github.com/tendermint/tendermint/wiki/Setting-GOPATH)
|
- `$GOPATH/bin` must be on your `$PATH` (see https://github.com/tendermint/tendermint/wiki/Setting-GOPATH)
|
||||||
|
|
|
@ -25,11 +25,13 @@ more info.
|
||||||
|
|
||||||
Then run
|
Then run
|
||||||
|
|
||||||
go get github.com/tendermint/tendermint
|
```
|
||||||
cd $GOPATH/src/github.com/tendermint/tendermint
|
go get github.com/tendermint/tendermint
|
||||||
make get_tools
|
cd $GOPATH/src/github.com/tendermint/tendermint
|
||||||
make get_vendor_deps
|
make get_tools
|
||||||
make install_abci
|
make get_vendor_deps
|
||||||
|
make install_abci
|
||||||
|
```
|
||||||
|
|
||||||
Now you should have the `abci-cli` installed; you'll see a couple of
|
Now you should have the `abci-cli` installed; you'll see a couple of
|
||||||
commands (`counter` and `kvstore`) that are example applications written
|
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.
|
Let's start a kvstore application.
|
||||||
|
|
||||||
abci-cli kvstore
|
```
|
||||||
|
abci-cli kvstore
|
||||||
|
```
|
||||||
|
|
||||||
In another terminal, we can start Tendermint. If you have never run
|
In another terminal, we can start Tendermint. If you have never run
|
||||||
Tendermint before, use:
|
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
|
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
|
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
|
You should see Tendermint making blocks! We can get the status of our
|
||||||
Tendermint node as follows:
|
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
|
The `-s` just silences `curl`. For nicer output, pipe the result into a
|
||||||
tool like [jq](https://stedolan.github.io/jq/) or `json_pp`.
|
tool like [jq](https://stedolan.github.io/jq/) or `json_pp`.
|
||||||
|
|
||||||
Now let's send some transactions to the kvstore.
|
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
|
Note the single quote (`'`) around the url, which ensures that the
|
||||||
double quotes (`"`) are not escaped by bash. This command sent a
|
double quotes (`"`) are not escaped by bash. This command sent a
|
||||||
|
@ -78,50 +88,56 @@ 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
|
and the value in the Merkle tree. The response should look something
|
||||||
like:
|
like:
|
||||||
|
|
||||||
{
|
```
|
||||||
"jsonrpc": "2.0",
|
{
|
||||||
"id": "",
|
"jsonrpc": "2.0",
|
||||||
"result": {
|
"id": "",
|
||||||
"check_tx": {
|
"result": {
|
||||||
"fee": {}
|
"check_tx": {
|
||||||
|
"fee": {}
|
||||||
|
},
|
||||||
|
"deliver_tx": {
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"key": "YXBwLmNyZWF0b3I=",
|
||||||
|
"value": "amFl"
|
||||||
},
|
},
|
||||||
"deliver_tx": {
|
{
|
||||||
"tags": [
|
"key": "YXBwLmtleQ==",
|
||||||
{
|
"value": "YWJjZA=="
|
||||||
"key": "YXBwLmNyZWF0b3I=",
|
}
|
||||||
"value": "amFl"
|
],
|
||||||
},
|
"fee": {}
|
||||||
{
|
},
|
||||||
"key": "YXBwLmtleQ==",
|
"hash": "9DF66553F98DE3C26E3C3317A3E4CED54F714E39",
|
||||||
"value": "YWJjZA=="
|
"height": 14
|
||||||
}
|
}
|
||||||
],
|
}
|
||||||
"fee": {}
|
```
|
||||||
},
|
|
||||||
"hash": "9DF66553F98DE3C26E3C3317A3E4CED54F714E39",
|
|
||||||
"height": 14
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
We can confirm that our transaction worked and the value got stored by
|
We can confirm that our transaction worked and the value got stored by
|
||||||
querying the app:
|
querying the app:
|
||||||
|
|
||||||
curl -s 'localhost:26657/abci_query?data="abcd"'
|
```
|
||||||
|
curl -s 'localhost:26657/abci_query?data="abcd"'
|
||||||
|
```
|
||||||
|
|
||||||
The result should look like:
|
The result should look like:
|
||||||
|
|
||||||
{
|
```
|
||||||
"jsonrpc": "2.0",
|
{
|
||||||
"id": "",
|
"jsonrpc": "2.0",
|
||||||
"result": {
|
"id": "",
|
||||||
"response": {
|
"result": {
|
||||||
"log": "exists",
|
"response": {
|
||||||
"index": "-1",
|
"log": "exists",
|
||||||
"key": "YWJjZA==",
|
"index": "-1",
|
||||||
"value": "YWJjZA=="
|
"key": "YWJjZA==",
|
||||||
}
|
"value": "YWJjZA=="
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Note the `value` in the result (`YWJjZA==`); this is the base64-encoding
|
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
|
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:
|
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==`
|
Now if we query for `name`, we should get `satoshi`, or `c2F0b3NoaQ==`
|
||||||
in base64:
|
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
|
Try some other transactions and queries to make sure everything is
|
||||||
working!
|
working!
|
||||||
|
@ -171,57 +191,67 @@ Let's kill the previous instance of `tendermint` and the `kvstore`
|
||||||
application, and start the counter app. We can enable `serial=on` with a
|
application, and start the counter app. We can enable `serial=on` with a
|
||||||
flag:
|
flag:
|
||||||
|
|
||||||
abci-cli counter --serial
|
```
|
||||||
|
abci-cli counter --serial
|
||||||
|
```
|
||||||
|
|
||||||
In another window, reset then start Tendermint:
|
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
|
Once again, you can see the blocks streaming by. Let's send some
|
||||||
transactions. Since we have set `serial=on`, the first transaction must
|
transactions. Since we have set `serial=on`, the first transaction must
|
||||||
be the number `0`:
|
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
|
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:
|
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": "",
|
"jsonrpc": "2.0",
|
||||||
"result": {
|
"id": "",
|
||||||
"check_tx": {
|
"result": {
|
||||||
"fee": {}
|
"check_tx": {
|
||||||
},
|
"fee": {}
|
||||||
"deliver_tx": {
|
},
|
||||||
"code": 2,
|
"deliver_tx": {
|
||||||
"log": "Invalid nonce. Expected 1, got 5",
|
"code": 2,
|
||||||
"fee": {}
|
"log": "Invalid nonce. Expected 1, got 5",
|
||||||
},
|
"fee": {}
|
||||||
"hash": "33B93DFF98749B0D6996A70F64071347060DC19C",
|
},
|
||||||
"height": 34
|
"hash": "33B93DFF98749B0D6996A70F64071347060DC19C",
|
||||||
}
|
"height": 34
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
But if we send a `1`, it works again:
|
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": "",
|
"jsonrpc": "2.0",
|
||||||
"result": {
|
"id": "",
|
||||||
"check_tx": {
|
"result": {
|
||||||
"fee": {}
|
"check_tx": {
|
||||||
},
|
"fee": {}
|
||||||
"deliver_tx": {
|
},
|
||||||
"fee": {}
|
"deliver_tx": {
|
||||||
},
|
"fee": {}
|
||||||
"hash": "F17854A977F6FA7EEA1BD758E296710B86F72F3D",
|
},
|
||||||
"height": 60
|
"hash": "F17854A977F6FA7EEA1BD758E296710B86F72F3D",
|
||||||
}
|
"height": 60
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
For more details on the `broadcast_tx` API, see [the guide on using
|
For more details on the `broadcast_tx` API, see [the guide on using
|
||||||
Tendermint](./using-tendermint.md).
|
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
|
[here](https://github.com/tendermint/js-abci) then install it. As go
|
||||||
devs, we keep all our code under the `$GOPATH`, so run:
|
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
|
go get github.com/tendermint/js-abci &> /dev/null
|
||||||
npm install
|
cd $GOPATH/src/github.com/tendermint/js-abci/example
|
||||||
cd ..
|
npm install
|
||||||
|
cd ..
|
||||||
|
```
|
||||||
|
|
||||||
Kill the previous `counter` and `tendermint` processes. Now run the app:
|
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`:
|
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
|
Once again, you should see blocks streaming by - but now, our
|
||||||
application is written in javascript! Try sending some transactions, and
|
application is written in javascript! Try sending some transactions, and
|
||||||
like before - the results should be the same:
|
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=0x00 # ok
|
||||||
curl localhost:26657/broadcast_tx_commit?tx=0x01 # ok
|
curl localhost:26657/broadcast_tx_commit?tx=0x05 # invalid nonce
|
||||||
|
curl localhost:26657/broadcast_tx_commit?tx=0x01 # ok
|
||||||
|
```
|
||||||
|
|
||||||
Neat, eh?
|
Neat, eh?
|
||||||
|
|
|
@ -5,49 +5,59 @@
|
||||||
We first create three connections (mempool, consensus and query) to the
|
We first create three connections (mempool, consensus and query) to the
|
||||||
application (running `kvstore` locally in this case).
|
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.364] Starting multiAppConn module=proxy impl=multiAppConn
|
||||||
I[10-04|13:54:27.366] Starting localClient module=abci-client connection=mempool impl=localClient
|
I[10-04|13:54:27.366] Starting localClient module=abci-client connection=query impl=localClient
|
||||||
I[10-04|13:54:27.367] Starting localClient module=abci-client connection=consensus 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.
|
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.367] ABCI Handshake module=consensus appHeight=90 appHash=E0FBAFBF6FCED8B9786DDFEB1A0D4FA2501BADAD
|
||||||
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.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,
|
After that, we start a few more things like the event switch, reactors,
|
||||||
and perform UPNP discover in order to detect the IP address.
|
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.374] Starting EventSwitch module=types impl=EventSwitch
|
||||||
I[10-04|13:54:27.379] Starting Node module=main impl=Node
|
I[10-04|13:54:27.375] This node is a validator module=consensus
|
||||||
I[10-04|13:54:27.381] Local listener module=p2p ip=:: port=26656
|
I[10-04|13:54:27.379] Starting Node module=main impl=Node
|
||||||
I[10-04|13:54:27.382] Getting UPNP external address module=p2p
|
I[10-04|13:54:27.381] Local listener module=p2p ip=:: port=26656
|
||||||
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:27.382] Getting UPNP external address module=p2p
|
||||||
I[10-04|13:54:30.386] Starting DefaultListener module=p2p impl=Listener(@10.0.2.15:26656)
|
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.387] Starting P2P Switch module=p2p impl="P2P Switch"
|
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 MempoolReactor module=mempool impl=MempoolReactor
|
I[10-04|13:54:30.387] Starting P2P Switch module=p2p impl="P2P Switch"
|
||||||
I[10-04|13:54:30.387] Starting BlockchainReactor module=blockchain impl=BlockchainReactor
|
I[10-04|13:54:30.387] Starting MempoolReactor module=mempool impl=MempoolReactor
|
||||||
I[10-04|13:54:30.387] Starting ConsensusReactor module=consensus impl=ConsensusReactor
|
I[10-04|13:54:30.387] Starting BlockchainReactor module=blockchain impl=BlockchainReactor
|
||||||
I[10-04|13:54:30.387] ConsensusReactor module=consensus fastSync=false
|
I[10-04|13:54:30.387] Starting ConsensusReactor module=consensus impl=ConsensusReactor
|
||||||
I[10-04|13:54:30.387] Starting ConsensusState module=consensus impl=ConsensusState
|
I[10-04|13:54:30.387] ConsensusReactor module=consensus fastSync=false
|
||||||
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.387] Starting ConsensusState module=consensus impl=ConsensusState
|
||||||
I[10-04|13:54:30.388] Starting TimeoutTicker module=consensus impl=TimeoutTicker
|
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
|
Notice the second row where Tendermint Core reports that "This node is a
|
||||||
validator". It also could be just an observer (regular node).
|
validator". It also could be just an observer (regular node).
|
||||||
|
|
||||||
Next we replay all the messages from the WAL.
|
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] Catchup by replaying consensus messages module=consensus height=91
|
||||||
I[10-04|13:54:30.390] Replay: Done module=consensus
|
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.
|
"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
|
Next follows a standard block creation cycle, where we enter a new
|
||||||
round, propose a block, receive more than 2/3 of prevotes, then
|
round, propose a block, receive more than 2/3 of prevotes, then
|
||||||
|
@ -56,75 +66,77 @@ please refer to [Consensus
|
||||||
Overview](./introduction.md#consensus-overview) or [Byzantine Consensus
|
Overview](./introduction.md#consensus-overview) or [Byzantine Consensus
|
||||||
Algorithm](./spec/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] enterNewRound(91/0). Current: 91/0/RoundStepNewHeight 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.393] enterPropose(91/0). Current: 91/0/RoundStepNewRound module=consensus
|
||||||
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.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.397] Received complete proposal block module=consensus height=91 hash=F671D562C7B9242900A286E1882EE64E5556FE9E
|
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] enterPrevote(91/0). Current: 91/0/RoundStepPropose module=consensus
|
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: ProposalBlock is valid module=consensus height=91 round=0
|
I[10-04|13:54:30.397] enterPrevote(91/0). Current: 91/0/RoundStepPropose module=consensus
|
||||||
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.397] enterPrevote: ProposalBlock is valid module=consensus height=91 round=0
|
||||||
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.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] enterPrecommit(91/0). Current: 91/0/RoundStepPrevote module=consensus
|
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: +2/3 prevoted proposal block. Locking module=consensus hash=F671D562C7B9242900A286E1882EE64E5556FE9E
|
I[10-04|13:54:30.401] enterPrecommit(91/0). Current: 91/0/RoundStepPrevote module=consensus
|
||||||
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.401] enterPrecommit: +2/3 prevoted proposal block. Locking module=consensus hash=F671D562C7B9242900A286E1882EE64E5556FE9E
|
||||||
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.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] enterCommit(91/0). Current: 91/0/RoundStepPrecommit module=consensus
|
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.405] Finalizing commit of block with 0 txs module=consensus height=91 hash=F671D562C7B9242900A286E1882EE64E5556FE9E root=E0FBAFBF6FCED8B9786DDFEB1A0D4FA2501BADAD
|
I[10-04|13:54:30.404] enterCommit(91/0). Current: 91/0/RoundStepPrecommit module=consensus
|
||||||
I[10-04|13:54:30.405] Block{
|
I[10-04|13:54:30.405] Finalizing commit of block with 0 txs module=consensus height=91 hash=F671D562C7B9242900A286E1882EE64E5556FE9E root=E0FBAFBF6FCED8B9786DDFEB1A0D4FA2501BADAD
|
||||||
Header{
|
I[10-04|13:54:30.405] Block{
|
||||||
ChainID: test-chain-3MNw2N
|
Header{
|
||||||
Height: 91
|
ChainID: test-chain-3MNw2N
|
||||||
Time: 2017-10-04 13:54:30.393 +0000 UTC
|
Height: 91
|
||||||
NumTxs: 0
|
Time: 2017-10-04 13:54:30.393 +0000 UTC
|
||||||
LastBlockID: F15AB8BEF9A6AAB07E457A6E16BC410546AA4DC6:1:D505DA273544
|
NumTxs: 0
|
||||||
LastCommit: 56FEF2EFDB8B37E9C6E6D635749DF3169D5F005D
|
LastBlockID: F15AB8BEF9A6AAB07E457A6E16BC410546AA4DC6:1:D505DA273544
|
||||||
Data:
|
LastCommit: 56FEF2EFDB8B37E9C6E6D635749DF3169D5F005D
|
||||||
Validators: CE25FBFF2E10C0D51AA1A07C064A96931BC8B297
|
Data:
|
||||||
App: E0FBAFBF6FCED8B9786DDFEB1A0D4FA2501BADAD
|
Validators: CE25FBFF2E10C0D51AA1A07C064A96931BC8B297
|
||||||
}#F671D562C7B9242900A286E1882EE64E5556FE9E
|
App: E0FBAFBF6FCED8B9786DDFEB1A0D4FA2501BADAD
|
||||||
Data{
|
}#F671D562C7B9242900A286E1882EE64E5556FE9E
|
||||||
|
Data{
|
||||||
|
|
||||||
}#
|
}#
|
||||||
Commit{
|
Commit{
|
||||||
BlockID: F15AB8BEF9A6AAB07E457A6E16BC410546AA4DC6:1:D505DA273544
|
BlockID: F15AB8BEF9A6AAB07E457A6E16BC410546AA4DC6:1:D505DA273544
|
||||||
Precommits: Vote{0:125B0E3C5512 90/00/2(Precommit) F15AB8BEF9A6 {/FE98E2B956F0.../}}
|
Precommits: Vote{0:125B0E3C5512 90/00/2(Precommit) F15AB8BEF9A6 {/FE98E2B956F0.../}}
|
||||||
}#56FEF2EFDB8B37E9C6E6D635749DF3169D5F005D
|
}#56FEF2EFDB8B37E9C6E6D635749DF3169D5F005D
|
||||||
}#F671D562C7B9242900A286E1882EE64E5556FE9E module=consensus
|
}#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.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] 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
|
I[10-04|13:54:30.410] Recheck txs module=mempool numtxs=0 height=91
|
||||||
|
```
|
||||||
|
|
||||||
## List of modules
|
## List of modules
|
||||||
|
|
||||||
Here is the list of modules you may encounter in Tendermint's log and a
|
Here is the list of modules you may encounter in Tendermint's log and a
|
||||||
little overview what they do.
|
little overview what they do.
|
||||||
|
|
||||||
- `abci-client` As mentioned in [Application Development Guide](./app-development.md), Tendermint acts as an ABCI
|
- `abci-client` As mentioned in [Application Development Guide](./app-development.md), Tendermint acts as an ABCI
|
||||||
client with respect to the application and maintains 3 connections:
|
client with respect to the application and maintains 3 connections:
|
||||||
mempool, consensus and query. The code used by Tendermint Core can
|
mempool, consensus and query. The code used by Tendermint Core can
|
||||||
be found [here](https://github.com/tendermint/tendermint/tree/develop/abci/client).
|
be found [here](https://github.com/tendermint/tendermint/tree/develop/abci/client).
|
||||||
- `blockchain` Provides storage, pool (a group of peers), and reactor
|
- `blockchain` Provides storage, pool (a group of peers), and reactor
|
||||||
for both storing and exchanging blocks between peers.
|
for both storing and exchanging blocks between peers.
|
||||||
- `consensus` The heart of Tendermint core, which is the
|
- `consensus` The heart of Tendermint core, which is the
|
||||||
implementation of the consensus algorithm. Includes two
|
implementation of the consensus algorithm. Includes two
|
||||||
"submodules": `wal` (write-ahead logging) for ensuring data
|
"submodules": `wal` (write-ahead logging) for ensuring data
|
||||||
integrity and `replay` to replay blocks and messages on recovery
|
integrity and `replay` to replay blocks and messages on recovery
|
||||||
from a crash.
|
from a crash.
|
||||||
- `events` Simple event notification system. The list of events can be
|
- `events` Simple event notification system. The list of events can be
|
||||||
found
|
found
|
||||||
[here](https://github.com/tendermint/tendermint/blob/master/types/events.go).
|
[here](https://github.com/tendermint/tendermint/blob/master/types/events.go).
|
||||||
You can subscribe to them by calling `subscribe` RPC method. Refer
|
You can subscribe to them by calling `subscribe` RPC method. Refer
|
||||||
to [RPC docs](./specification/rpc.md) for additional information.
|
to [RPC docs](./specification/rpc.md) for additional information.
|
||||||
- `mempool` Mempool module handles all incoming transactions, whenever
|
- `mempool` Mempool module handles all incoming transactions, whenever
|
||||||
they are coming from peers or the application.
|
they are coming from peers or the application.
|
||||||
- `p2p` Provides an abstraction around peer-to-peer communication. For
|
- `p2p` Provides an abstraction around peer-to-peer communication. For
|
||||||
more details, please check out the
|
more details, please check out the
|
||||||
[README](https://github.com/tendermint/tendermint/blob/master/p2p/README.md).
|
[README](https://github.com/tendermint/tendermint/blob/master/p2p/README.md).
|
||||||
- `rpc` [Tendermint's RPC](./specification/rpc.md).
|
- `rpc` [Tendermint's RPC](./specification/rpc.md).
|
||||||
- `rpc-server` RPC server. For implementation details, please read the
|
- `rpc-server` RPC server. For implementation details, please read the
|
||||||
[README](https://github.com/tendermint/tendermint/blob/master/rpc/lib/README.md).
|
[README](https://github.com/tendermint/tendermint/blob/master/rpc/lib/README.md).
|
||||||
- `state` Represents the latest state and execution submodule, which
|
- `state` Represents the latest state and execution submodule, which
|
||||||
executes blocks against the application.
|
executes blocks against the application.
|
||||||
- `types` A collection of the publicly exposed types and methods to
|
- `types` A collection of the publicly exposed types and methods to
|
||||||
work with them.
|
work with them.
|
||||||
|
|
|
@ -5,27 +5,29 @@ to their results.
|
||||||
|
|
||||||
Let's take a look at the `[tx_index]` config section:
|
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
|
# What indexer to use for transactions
|
||||||
#
|
#
|
||||||
# Options:
|
# Options:
|
||||||
# 1) "null" (default)
|
# 1) "null" (default)
|
||||||
# 2) "kv" - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend).
|
# 2) "kv" - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend).
|
||||||
indexer = "kv"
|
indexer = "kv"
|
||||||
|
|
||||||
# Comma-separated list of tags to index (by default the only tag is tx hash)
|
# 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
|
# 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
|
# bloat. This is, of course, depends on the indexer's DB and the volume of
|
||||||
# transactions.
|
# transactions.
|
||||||
index_tags = ""
|
index_tags = ""
|
||||||
|
|
||||||
# When set to true, tells indexer to index all tags. Note this may be not
|
# 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
|
# desirable (see the comment above). IndexTags has a precedence over
|
||||||
# IndexAllTags (i.e. when given both, IndexTags will be indexed).
|
# IndexAllTags (i.e. when given both, IndexTags will be indexed).
|
||||||
index_all_tags = false
|
index_all_tags = false
|
||||||
|
```
|
||||||
|
|
||||||
By default, Tendermint will index all transactions by their respective
|
By default, Tendermint will index all transactions by their respective
|
||||||
hashes using an embedded simple indexer. Note, we are planning to add
|
hashes using an embedded simple indexer. Note, we are planning to add
|
||||||
|
@ -39,15 +41,17 @@ pairs of UTF-8 encoded strings (e.g. "account.owner": "Bob", "balance":
|
||||||
|
|
||||||
Example:
|
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")},
|
tags := []cmn.KVPair{
|
||||||
{[]byte("account.address"), []byte("0xdeadbeef")},
|
{[]byte("account.name"), []byte("igor")},
|
||||||
{[]byte("tx.amount"), []byte("7")},
|
{[]byte("account.address"), []byte("0xdeadbeef")},
|
||||||
}
|
{[]byte("tx.amount"), []byte("7")},
|
||||||
return types.ResponseDeliverTx{Code: code.CodeTypeOK, Tags: tags}
|
|
||||||
}
|
}
|
||||||
|
return types.ResponseDeliverTx{Code: code.CodeTypeOK, Tags: tags}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
If you want Tendermint to only index transactions by "account.name" tag,
|
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
|
in the config set `tx_index.index_tags="account.name"`. If you to index
|
||||||
|
@ -55,9 +59,9 @@ all tags, set `index_all_tags=true`
|
||||||
|
|
||||||
Note, there are a few predefined tags:
|
Note, there are a few predefined tags:
|
||||||
|
|
||||||
- `tm.event` (event type)
|
- `tm.event` (event type)
|
||||||
- `tx.hash` (transaction's hash)
|
- `tx.hash` (transaction's hash)
|
||||||
- `tx.height` (height of the block transaction was committed in)
|
- `tx.height` (height of the block transaction was committed in)
|
||||||
|
|
||||||
Tendermint will throw a warning if you try to use any of the above keys.
|
Tendermint will throw a warning if you try to use any of the above keys.
|
||||||
|
|
||||||
|
@ -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
|
You can query the transaction results by calling `/tx_search` RPC
|
||||||
endpoint:
|
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)
|
Check out [API docs](https://tendermint.github.io/slate/?shell#txsearch)
|
||||||
for more information on query syntax and other options.
|
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
|
Clients can subscribe to transactions with the given tags via Websocket
|
||||||
by providing a query to `/subscribe` RPC endpoint.
|
by providing a query to `/subscribe` RPC endpoint.
|
||||||
|
|
||||||
{
|
```
|
||||||
"jsonrpc": "2.0",
|
{
|
||||||
"method": "subscribe",
|
"jsonrpc": "2.0",
|
||||||
"id": "0",
|
"method": "subscribe",
|
||||||
"params": {
|
"id": "0",
|
||||||
"query": "account.name='igor'"
|
"params": {
|
||||||
}
|
"query": "account.name='igor'"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Check out [API docs](https://tendermint.github.io/slate/#subscribe) for
|
Check out [API docs](https://tendermint.github.io/slate/#subscribe) for
|
||||||
more information on query syntax and other options.
|
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.
|
configuration, service discovery, locking, leader-election, and so on.
|
||||||
|
|
||||||
Tendermint is in essence similar software, but with two key differences:
|
Tendermint is in essence similar software, but with two key differences:
|
||||||
|
|
||||||
- It is Byzantine Fault Tolerant, meaning it can only tolerate up to a
|
- It is Byzantine Fault Tolerant, meaning it can only tolerate up to a
|
||||||
1/3 of failures, but those failures can include arbitrary behaviour -
|
1/3 of failures, but those failures can include arbitrary behaviour -
|
||||||
including hacking and malicious attacks. - It does not specify a
|
including hacking and malicious attacks. - It does not specify a
|
||||||
particular application, like a fancy key-value store. Instead, it
|
particular application, like a fancy key-value store. Instead, it
|
||||||
focuses on arbitrary state machine replication, so developers can build
|
focuses on arbitrary state machine replication, so developers can build
|
||||||
the application logic that's right for them, from key-value store to
|
the application logic that's right for them, from key-value store to
|
||||||
cryptocurrency to e-voting platform and beyond.
|
cryptocurrency to e-voting platform and beyond.
|
||||||
|
|
||||||
The layout of this Tendermint website content is also ripped directly
|
The layout of this Tendermint website content is also ripped directly
|
||||||
and without shame from [consul.io](https://www.consul.io/) and the other
|
and without shame from [consul.io](https://www.consul.io/) and the other
|
||||||
|
@ -167,16 +168,16 @@ maintains a fully audited Unspent Transaction Output (UTXO) database. If
|
||||||
one wanted to create a Bitcoin-like system on top of ABCI, Tendermint
|
one wanted to create a Bitcoin-like system on top of ABCI, Tendermint
|
||||||
Core would be responsible for
|
Core would be responsible for
|
||||||
|
|
||||||
- Sharing blocks and transactions between nodes
|
- Sharing blocks and transactions between nodes
|
||||||
- Establishing a canonical/immutable order of transactions
|
- Establishing a canonical/immutable order of transactions
|
||||||
(the blockchain)
|
(the blockchain)
|
||||||
|
|
||||||
The application will be responsible for
|
The application will be responsible for
|
||||||
|
|
||||||
- Maintaining the UTXO database
|
- Maintaining the UTXO database
|
||||||
- Validating cryptographic signatures of transactions
|
- Validating cryptographic signatures of transactions
|
||||||
- Preventing transactions from spending non-existent transactions
|
- Preventing transactions from spending non-existent transactions
|
||||||
- Allowing clients to query the UTXO database.
|
- Allowing clients to query the UTXO database.
|
||||||
|
|
||||||
Tendermint is able to decompose the blockchain design by offering a very
|
Tendermint is able to decompose the blockchain design by offering a very
|
||||||
simple API (ie. the ABCI) between the application process and consensus
|
simple API (ie. the ABCI) between the application process and consensus
|
||||||
|
@ -242,14 +243,14 @@ Java, C++, Python, or Go. Game programmers and blockchain developers are
|
||||||
already familiar with creating deterministic programs by avoiding
|
already familiar with creating deterministic programs by avoiding
|
||||||
sources of non-determinism such as:
|
sources of non-determinism such as:
|
||||||
|
|
||||||
- random number generators (without deterministic seeding)
|
- random number generators (without deterministic seeding)
|
||||||
- race conditions on threads (or avoiding threads altogether)
|
- race conditions on threads (or avoiding threads altogether)
|
||||||
- the system clock
|
- the system clock
|
||||||
- uninitialized memory (in unsafe programming languages like C
|
- uninitialized memory (in unsafe programming languages like C
|
||||||
or C++)
|
or C++)
|
||||||
- [floating point
|
- [floating point
|
||||||
arithmetic](http://gafferongames.com/networking-for-game-programmers/floating-point-determinism/)
|
arithmetic](http://gafferongames.com/networking-for-game-programmers/floating-point-determinism/)
|
||||||
- language features that are random (e.g. map iteration in Go)
|
- language features that are random (e.g. map iteration in Go)
|
||||||
|
|
||||||
While programmers can avoid non-determinism by being careful, it is also
|
While programmers can avoid non-determinism by being careful, it is also
|
||||||
possible to create a special linter or static analyzer for each language
|
possible to create a special linter or static analyzer for each language
|
||||||
|
@ -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
|
followed in the flow diagram. Once a validator precommits a block, it is
|
||||||
locked on that block. Then,
|
locked on that block. Then,
|
||||||
|
|
||||||
1) it must prevote for the block it is locked on
|
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
|
2. it can only unlock, and precommit for a new block, if there is a
|
||||||
polka for that block in a later round
|
polka for that block in a later round
|
||||||
|
|
||||||
## Stake
|
## 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,21 +49,25 @@ 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
|
necessary info: whenever the node is syncing or not, what height it is
|
||||||
on, etc.
|
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
|
`dump_consensus_state` will give you a detailed overview of the
|
||||||
consensus state (proposer, lastest validators, peers states). From it,
|
consensus state (proposer, lastest validators, peers states). From it,
|
||||||
you should be able to figure out why, for example, the network had
|
you should be able to figure out why, for example, the network had
|
||||||
halted.
|
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
|
There is a reduced version of this endpoint - `consensus_state`, which
|
||||||
returns just the votes seen at the current height.
|
returns just the votes seen at the current height.
|
||||||
|
|
||||||
- [Github Issues](https://github.com/tendermint/tendermint/issues)
|
- [Github Issues](https://github.com/tendermint/tendermint/issues)
|
||||||
- [StackOverflow
|
- [StackOverflow
|
||||||
questions](https://stackoverflow.com/questions/tagged/tendermint)
|
questions](https://stackoverflow.com/questions/tagged/tendermint)
|
||||||
|
|
||||||
## Monitoring Tendermint
|
## Monitoring Tendermint
|
||||||
|
|
||||||
|
@ -107,18 +111,18 @@ programs](https://golang.org/pkg/os/signal/#hdr-Default_behavior_of_signals_in_G
|
||||||
While actual specs vary depending on the load and validators count,
|
While actual specs vary depending on the load and validators count,
|
||||||
minimal requirements are:
|
minimal requirements are:
|
||||||
|
|
||||||
- 1GB RAM
|
- 1GB RAM
|
||||||
- 25GB of disk space
|
- 25GB of disk space
|
||||||
- 1.4 GHz CPU
|
- 1.4 GHz CPU
|
||||||
|
|
||||||
SSD disks are preferable for applications with high transaction
|
SSD disks are preferable for applications with high transaction
|
||||||
throughput.
|
throughput.
|
||||||
|
|
||||||
Recommended:
|
Recommended:
|
||||||
|
|
||||||
- 2GB RAM
|
- 2GB RAM
|
||||||
- 100GB SSD
|
- 100GB SSD
|
||||||
- x64 2.0 GHz 2v CPU
|
- x64 2.0 GHz 2v CPU
|
||||||
|
|
||||||
While for now, Tendermint stores all the history and it may require
|
While for now, Tendermint stores all the history and it may require
|
||||||
significant disk space over time, we are planning to implement state
|
significant disk space over time, we are planning to implement state
|
||||||
|
@ -145,21 +149,23 @@ Cosmos network.
|
||||||
|
|
||||||
## Configuration parameters
|
## Configuration parameters
|
||||||
|
|
||||||
- `p2p.flush_throttle_timeout` `p2p.max_packet_msg_payload_size`
|
- `p2p.flush_throttle_timeout` `p2p.max_packet_msg_payload_size`
|
||||||
`p2p.send_rate` `p2p.recv_rate`
|
`p2p.send_rate` `p2p.recv_rate`
|
||||||
|
|
||||||
If you are going to use Tendermint in a private domain and you have a
|
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
|
private high-speed network among your peers, it makes sense to lower
|
||||||
flush throttle timeout and increase other params.
|
flush throttle timeout and increase other params.
|
||||||
|
|
||||||
[p2p]
|
```
|
||||||
|
[p2p]
|
||||||
|
|
||||||
send_rate=20000000 # 2MB/s
|
send_rate=20000000 # 2MB/s
|
||||||
recv_rate=20000000 # 2MB/s
|
recv_rate=20000000 # 2MB/s
|
||||||
flush_throttle_timeout=10
|
flush_throttle_timeout=10
|
||||||
max_packet_msg_payload_size=10240 # 10KB
|
max_packet_msg_payload_size=10240 # 10KB
|
||||||
|
```
|
||||||
|
|
||||||
- `mempool.recheck`
|
- `mempool.recheck`
|
||||||
|
|
||||||
After every block, Tendermint rechecks every transaction left in the
|
After every block, Tendermint rechecks every transaction left in the
|
||||||
mempool to see if transactions committed in that block affected the
|
mempool to see if transactions committed in that block affected the
|
||||||
|
@ -167,13 +173,13 @@ application state, so some of the transactions left may become invalid.
|
||||||
If that does not apply to your application, you can disable it by
|
If that does not apply to your application, you can disable it by
|
||||||
setting `mempool.recheck=false`.
|
setting `mempool.recheck=false`.
|
||||||
|
|
||||||
- `mempool.broadcast`
|
- `mempool.broadcast`
|
||||||
|
|
||||||
Setting this to false will stop the mempool from relaying transactions
|
Setting this to false will stop the mempool from relaying transactions
|
||||||
to other peers until they are included in a block. It means only the
|
to other peers until they are included in a block. It means only the
|
||||||
peer you send the tx to will see it until it is included in a block.
|
peer you send the tx to will see it until it is included in a block.
|
||||||
|
|
||||||
- `consensus.skip_timeout_commit`
|
- `consensus.skip_timeout_commit`
|
||||||
|
|
||||||
We want `skip_timeout_commit=false` when there is economics on the line
|
We want `skip_timeout_commit=false` when there is economics on the line
|
||||||
because proposers should wait to hear for more votes. But if you don't
|
because proposers should wait to hear for more votes. But if you don't
|
||||||
|
@ -182,22 +188,22 @@ be kept false by default for public deployments (e.g. [Cosmos
|
||||||
Hub](https://cosmos.network/intro/hub)) while for enterprise
|
Hub](https://cosmos.network/intro/hub)) while for enterprise
|
||||||
applications, setting it to true is not a problem.
|
applications, setting it to true is not a problem.
|
||||||
|
|
||||||
- `consensus.peer_gossip_sleep_duration`
|
- `consensus.peer_gossip_sleep_duration`
|
||||||
|
|
||||||
You can try to reduce the time your node sleeps before checking if
|
You can try to reduce the time your node sleeps before checking if
|
||||||
theres something to send its peers.
|
theres something to send its peers.
|
||||||
|
|
||||||
- `consensus.timeout_commit`
|
- `consensus.timeout_commit`
|
||||||
|
|
||||||
You can also try lowering `timeout_commit` (time we sleep before
|
You can also try lowering `timeout_commit` (time we sleep before
|
||||||
proposing the next block).
|
proposing the next block).
|
||||||
|
|
||||||
- `consensus.max_block_size_txs`
|
- `consensus.max_block_size_txs`
|
||||||
|
|
||||||
By default, the maximum number of transactions per a block is 10_000.
|
By default, the maximum number of transactions per a block is 10_000.
|
||||||
Feel free to change it to suit your needs.
|
Feel free to change it to suit your needs.
|
||||||
|
|
||||||
- `p2p.addr_book_strict`
|
- `p2p.addr_book_strict`
|
||||||
|
|
||||||
By default, Tendermint checks whenever a peer's address is routable before
|
By default, Tendermint checks whenever a peer's address is routable before
|
||||||
saving it to the address book. The address is considered as routable if the IP
|
saving it to the address book. The address is considered as routable if the IP
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
|
|
||||||
Tendermint Core can be configured via a TOML file in
|
Tendermint Core can be configured via a TOML file in
|
||||||
`$TMHOME/config/config.toml`. Some of these parameters can be overridden by
|
`$TMHOME/config/config.toml`. Some of these parameters can be overridden by
|
||||||
command-line flags. For most users, the options in the `##### main
|
command-line flags. For most users, the options in the `##### main base configuration options #####` are intended to be modified while
|
||||||
base configuration options #####` are intended to be modified while
|
|
||||||
config options further below are intended for advance power users.
|
config options further below are intended for advance power users.
|
||||||
|
|
||||||
## Options
|
## 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
|
You can subscribe to any of the events above by calling `subscribe` RPC
|
||||||
method via Websocket.
|
method via Websocket.
|
||||||
|
|
||||||
{
|
```
|
||||||
"jsonrpc": "2.0",
|
{
|
||||||
"method": "subscribe",
|
"jsonrpc": "2.0",
|
||||||
"id": "0",
|
"method": "subscribe",
|
||||||
"params": {
|
"id": "0",
|
||||||
"query": "tm.event='NewBlock'"
|
"params": {
|
||||||
}
|
"query": "tm.event='NewBlock'"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Check out [API docs](https://tendermint.github.io/slate/#subscribe) for
|
Check out [API docs](https://tendermint.github.io/slate/#subscribe) for
|
||||||
more information on query syntax and other options.
|
more information on query syntax and other options.
|
||||||
|
|
|
@ -12,15 +12,15 @@ script](https://github.com/tendermint/tendermint/blob/develop/networks/remote/in
|
||||||
that can be run on a fresh DO droplet and will automatically spin up a 4
|
that can be run on a fresh DO droplet and will automatically spin up a 4
|
||||||
node testnet. The script more or less does everything described below.
|
node testnet. The script more or less does everything described below.
|
||||||
|
|
||||||
- Install [Terraform](https://www.terraform.io/downloads.html) and
|
- Install [Terraform](https://www.terraform.io/downloads.html) and
|
||||||
[Ansible](http://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html)
|
[Ansible](http://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html)
|
||||||
on a Linux machine.
|
on a Linux machine.
|
||||||
- Create a [DigitalOcean API
|
- Create a [DigitalOcean API
|
||||||
token](https://cloud.digitalocean.com/settings/api/tokens) with read
|
token](https://cloud.digitalocean.com/settings/api/tokens) with read
|
||||||
and write capability.
|
and write capability.
|
||||||
- Install the python dopy package (`pip install dopy`)
|
- Install the python dopy package (`pip install dopy`)
|
||||||
- Create SSH keys (`ssh-keygen`)
|
- Create SSH keys (`ssh-keygen`)
|
||||||
- Set environment variables:
|
- Set environment variables:
|
||||||
|
|
||||||
```
|
```
|
||||||
export DO_API_TOKEN="abcdef01234567890abcdef01234567890"
|
export DO_API_TOKEN="abcdef01234567890abcdef01234567890"
|
||||||
|
@ -44,6 +44,7 @@ then:
|
||||||
terraform init
|
terraform init
|
||||||
terraform apply -var DO_API_TOKEN="$DO_API_TOKEN" -var SSH_KEY_FILE="$SSH_KEY_FILE"
|
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.
|
and you will get a list of IP addresses that belong to your droplets.
|
||||||
|
|
||||||
With the droplets created and running, let's setup Ansible.
|
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:
|
Initialize the root directory by running:
|
||||||
|
|
||||||
tendermint init
|
```
|
||||||
|
tendermint init
|
||||||
|
```
|
||||||
|
|
||||||
This will create a new private key (`priv_validator.json`), and a
|
This will create a new private key (`priv_validator.json`), and a
|
||||||
genesis file (`genesis.json`) containing the associated public key, in
|
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:
|
For more elaborate initialization, see the tesnet command:
|
||||||
|
|
||||||
tendermint testnet --help
|
```
|
||||||
|
tendermint testnet --help
|
||||||
|
```
|
||||||
|
|
||||||
## Run
|
## Run
|
||||||
|
|
||||||
To run a Tendermint node, use
|
To run a Tendermint node, use
|
||||||
|
|
||||||
tendermint node
|
```
|
||||||
|
tendermint node
|
||||||
|
```
|
||||||
|
|
||||||
By default, Tendermint will try to connect to an ABCI application on
|
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
|
[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
|
installed, run it in another window. If you don't, kill Tendermint and
|
||||||
run an in-process version of the `kvstore` app:
|
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
|
After a few seconds you should see blocks start streaming in. Note that
|
||||||
blocks are produced regularly, even if there are no transactions. See
|
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
|
Tendermint supports in-process versions of the `counter`, `kvstore` and
|
||||||
`nil` apps that ship as examples with `abci-cli`. It's easy to compile
|
`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
|
`--proxy_app` flag to specify the address of the socket it is listening
|
||||||
on, for instance:
|
on, for instance:
|
||||||
|
|
||||||
tendermint node --proxy_app=/var/run/abci.sock
|
```
|
||||||
|
tendermint node --proxy_app=/var/run/abci.sock
|
||||||
|
```
|
||||||
|
|
||||||
## Transactions
|
## Transactions
|
||||||
|
|
||||||
To send a transaction, use `curl` to make requests to the Tendermint RPC
|
To send a transaction, use `curl` to make requests to the Tendermint RPC
|
||||||
server, for example:
|
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:
|
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:
|
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
|
Visit http://localhost:26657> in your browser to see the list of other
|
||||||
endpoints. Some take no arguments (like `/status`), while others specify
|
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:
|
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\].
|
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
|
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:
|
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:
|
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\].
|
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`:
|
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\].
|
which sends a 4 byte transaction: \[01 02 03 04\].
|
||||||
|
|
||||||
With `POST` (using `json`), the raw hex must be `base64` encoded:
|
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\].
|
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`
|
To reset a blockchain, stop the node, remove the `~/.tendermint/data`
|
||||||
directory and run
|
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
|
This final step is necessary to reset the `priv_validator.json`, which
|
||||||
otherwise prevents you from making conflicting votes in the consensus
|
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
|
transactions or the app hash changes, run Tendermint with this
|
||||||
additional flag:
|
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:
|
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`.
|
empty blocks requires the config option to be set to `false`.
|
||||||
|
|
||||||
The block interval setting allows for a delay (in seconds) between the
|
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`:
|
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
|
With this setting, empty blocks will be produced every 5s if no block
|
||||||
has been produced otherwise, regardless of the value of
|
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
|
Since there are multiple phases to processing a transaction, we offer
|
||||||
multiple endpoints to broadcast a transaction:
|
multiple endpoints to broadcast a transaction:
|
||||||
|
|
||||||
/broadcast_tx_async
|
```
|
||||||
/broadcast_tx_sync
|
/broadcast_tx_async
|
||||||
/broadcast_tx_commit
|
/broadcast_tx_sync
|
||||||
|
/broadcast_tx_commit
|
||||||
|
```
|
||||||
|
|
||||||
These correspond to no-processing, processing through the mempool, and
|
These correspond to no-processing, processing through the mempool, and
|
||||||
processing through a block, respectively. That is, `broadcast_tx_async`,
|
processing through a block, respectively. That is, `broadcast_tx_async`,
|
||||||
|
@ -208,38 +244,42 @@ When `tendermint init` is run, both a `genesis.json` and
|
||||||
`priv_validator.json` are created in `~/.tendermint/config`. The
|
`priv_validator.json` are created in `~/.tendermint/config`. The
|
||||||
`genesis.json` might look like:
|
`genesis.json` might look like:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"validators" : [
|
||||||
{
|
{
|
||||||
"validators" : [
|
|
||||||
{
|
|
||||||
"pub_key" : {
|
|
||||||
"value" : "h3hk+QE8c6QLTySp8TcfzclJw/BG79ziGB/pIA+DfPE=",
|
|
||||||
"type" : "tendermint/PubKeyEd25519"
|
|
||||||
},
|
|
||||||
"power" : 10,
|
|
||||||
"name" : ""
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"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",
|
|
||||||
"pub_key" : {
|
"pub_key" : {
|
||||||
"value" : "h3hk+QE8c6QLTySp8TcfzclJw/BG79ziGB/pIA+DfPE=",
|
"value" : "h3hk+QE8c6QLTySp8TcfzclJw/BG79ziGB/pIA+DfPE=",
|
||||||
"type" : "tendermint/PubKeyEd25519"
|
"type" : "tendermint/PubKeyEd25519"
|
||||||
},
|
},
|
||||||
"last_height" : "0",
|
"power" : 10,
|
||||||
"priv_key" : {
|
"name" : ""
|
||||||
"value" : "JPivl82x+LfVkp8i3ztoTjY6c6GJ4pBxQexErOCyhwqHeGT5ATxzpAtPJKnxNx/NyUnD8Ebv3OIYH+kgD4N88Q==",
|
|
||||||
"type" : "tendermint/PrivKeyEd25519"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"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",
|
||||||
|
"pub_key" : {
|
||||||
|
"value" : "h3hk+QE8c6QLTySp8TcfzclJw/BG79ziGB/pIA+DfPE=",
|
||||||
|
"type" : "tendermint/PubKeyEd25519"
|
||||||
|
},
|
||||||
|
"last_height" : "0",
|
||||||
|
"priv_key" : {
|
||||||
|
"value" : "JPivl82x+LfVkp8i3ztoTjY6c6GJ4pBxQexErOCyhwqHeGT5ATxzpAtPJKnxNx/NyUnD8Ebv3OIYH+kgD4N88Q==",
|
||||||
|
"type" : "tendermint/PrivKeyEd25519"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
The `priv_validator.json` actually contains a private key, and should
|
The `priv_validator.json` actually contains a private key, and should
|
||||||
thus be kept absolutely secret; for now we work with the plain text.
|
thus be kept absolutely secret; for now we work with the plain text.
|
||||||
|
@ -272,6 +312,7 @@ with the consensus protocol.
|
||||||
### Peers
|
### Peers
|
||||||
|
|
||||||
#### Seed
|
#### Seed
|
||||||
|
|
||||||
A seed node is a node who relays the addresses of other peers which they know
|
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
|
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
|
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.
|
from you after sending you some addresses.
|
||||||
|
|
||||||
#### Persistent Peer
|
#### Persistent Peer
|
||||||
|
|
||||||
Persistent peers are people you want to be constantly connected with. If you
|
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
|
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
|
another address from the address book. On restarts you will always try to
|
||||||
|
@ -302,12 +344,16 @@ persistent connections with.
|
||||||
|
|
||||||
For example,
|
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
|
Alternatively, you can use the `/dial_seeds` endpoint of the RPC to
|
||||||
specify seeds for a running node to connect 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
|
Note, with PeX enabled, you
|
||||||
should not need seeds after the first start.
|
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
|
`config.toml` or the `/dial_peers` RPC endpoint to do it without
|
||||||
stopping Tendermint core instance.
|
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
|
### Adding a Non-Validator
|
||||||
|
|
||||||
|
@ -338,51 +387,57 @@ before starting the network. For instance, we could make a new
|
||||||
|
|
||||||
We can generate a new `priv_validator.json` with the command:
|
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
|
Now we can update our genesis file. For instance, if the new
|
||||||
`priv_validator.json` looks like:
|
`priv_validator.json` looks like:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"address" : "5AF49D2A2D4F5AD4C7C8C4CC2FB020131E9C4902",
|
||||||
|
"pub_key" : {
|
||||||
|
"value" : "l9X9+fjkeBzDfPGbUM7AMIRE6uJN78zN5+lk5OYotek=",
|
||||||
|
"type" : "tendermint/PubKeyEd25519"
|
||||||
|
},
|
||||||
|
"priv_key" : {
|
||||||
|
"value" : "EDJY9W6zlAw+su6ITgTKg2nTZcHAH1NMTW5iwlgmNDuX1f35+OR4HMN88ZtQzsAwhETq4k3vzM3n6WTk5ii16Q==",
|
||||||
|
"type" : "tendermint/PrivKeyEd25519"
|
||||||
|
},
|
||||||
|
"last_step" : 0,
|
||||||
|
"last_round" : "0",
|
||||||
|
"last_height" : "0"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
then the new `genesis.json` will be:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"validators" : [
|
||||||
|
{
|
||||||
|
"pub_key" : {
|
||||||
|
"value" : "h3hk+QE8c6QLTySp8TcfzclJw/BG79ziGB/pIA+DfPE=",
|
||||||
|
"type" : "tendermint/PubKeyEd25519"
|
||||||
|
},
|
||||||
|
"power" : 10,
|
||||||
|
"name" : ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"address" : "5AF49D2A2D4F5AD4C7C8C4CC2FB020131E9C4902",
|
|
||||||
"pub_key" : {
|
"pub_key" : {
|
||||||
"value" : "l9X9+fjkeBzDfPGbUM7AMIRE6uJN78zN5+lk5OYotek=",
|
"value" : "l9X9+fjkeBzDfPGbUM7AMIRE6uJN78zN5+lk5OYotek=",
|
||||||
"type" : "tendermint/PubKeyEd25519"
|
"type" : "tendermint/PubKeyEd25519"
|
||||||
},
|
},
|
||||||
"priv_key" : {
|
"power" : 10,
|
||||||
"value" : "EDJY9W6zlAw+su6ITgTKg2nTZcHAH1NMTW5iwlgmNDuX1f35+OR4HMN88ZtQzsAwhETq4k3vzM3n6WTk5ii16Q==",
|
"name" : ""
|
||||||
"type" : "tendermint/PrivKeyEd25519"
|
|
||||||
},
|
|
||||||
"last_step" : 0,
|
|
||||||
"last_round" : "0",
|
|
||||||
"last_height" : "0"
|
|
||||||
}
|
|
||||||
|
|
||||||
then the new `genesis.json` will be:
|
|
||||||
|
|
||||||
{
|
|
||||||
"validators" : [
|
|
||||||
{
|
|
||||||
"pub_key" : {
|
|
||||||
"value" : "h3hk+QE8c6QLTySp8TcfzclJw/BG79ziGB/pIA+DfPE=",
|
|
||||||
"type" : "tendermint/PubKeyEd25519"
|
|
||||||
},
|
|
||||||
"power" : 10,
|
|
||||||
"name" : ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pub_key" : {
|
|
||||||
"value" : "l9X9+fjkeBzDfPGbUM7AMIRE6uJN78zN5+lk5OYotek=",
|
|
||||||
"type" : "tendermint/PubKeyEd25519"
|
|
||||||
},
|
|
||||||
"power" : 10,
|
|
||||||
"name" : ""
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"app_hash" : "",
|
|
||||||
"chain_id" : "test-chain-rDlYSN",
|
|
||||||
"genesis_time" : "0001-01-01T00:00:00Z"
|
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"app_hash" : "",
|
||||||
|
"chain_id" : "test-chain-rDlYSN",
|
||||||
|
"genesis_time" : "0001-01-01T00:00:00Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Update the `genesis.json` in `~/.tendermint/config`. Copy the genesis
|
Update the `genesis.json` in `~/.tendermint/config`. Copy the genesis
|
||||||
file and the new `priv_validator.json` to the `~/.tendermint/config` on
|
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