@ -0,0 +1,10 @@ | |||
# Pending | |||
BREAKING CHANGES: | |||
- [types] CanonicalTime uses nanoseconds instead of clipping to ms | |||
- breaks serialization/signing of all messages with a timestamp | |||
IMPROVEMENTS: | |||
- [blockchain] Improve fast-sync logic | |||
- tweak params | |||
- only process one block at a time to avoid starving |
@ -1,174 +0,0 @@ | |||
GOTOOLS = \ | |||
github.com/mitchellh/gox \ | |||
github.com/golang/dep/cmd/dep \ | |||
gopkg.in/alecthomas/gometalinter.v2 \ | |||
github.com/gogo/protobuf/protoc-gen-gogo \ | |||
github.com/gogo/protobuf/gogoproto | |||
GOTOOLS_CHECK = gox dep gometalinter.v2 protoc protoc-gen-gogo | |||
PACKAGES=$(shell go list ./... | grep -v '/vendor/') | |||
INCLUDE = -I=. -I=${GOPATH}/src -I=${GOPATH}/src/github.com/gogo/protobuf/protobuf | |||
all: check get_vendor_deps protoc build test install metalinter | |||
check: check_tools | |||
######################################## | |||
### Build | |||
protoc: | |||
## If you get the following error, | |||
## "error while loading shared libraries: libprotobuf.so.14: cannot open shared object file: No such file or directory" | |||
## See https://stackoverflow.com/a/25518702 | |||
protoc $(INCLUDE) --gogo_out=plugins=grpc:. types/*.proto | |||
@echo "--> adding nolint declarations to protobuf generated files" | |||
@awk '/package types/ { print "//nolint: gas"; print; next }1' types/types.pb.go > types/types.pb.go.new | |||
@mv types/types.pb.go.new types/types.pb.go | |||
build: | |||
@go build -i ./cmd/... | |||
dist: | |||
@bash scripts/dist.sh | |||
@bash scripts/publish.sh | |||
install: | |||
@go install ./cmd/... | |||
######################################## | |||
### Tools & dependencies | |||
check_tools: | |||
@# https://stackoverflow.com/a/25668869 | |||
@echo "Found tools: $(foreach tool,$(GOTOOLS_CHECK),\ | |||
$(if $(shell which $(tool)),$(tool),$(error "No $(tool) in PATH")))" | |||
get_tools: | |||
@echo "--> Installing tools" | |||
go get -u -v $(GOTOOLS) | |||
@gometalinter.v2 --install | |||
get_protoc: | |||
@# https://github.com/google/protobuf/releases | |||
curl -L https://github.com/google/protobuf/releases/download/v3.4.1/protobuf-cpp-3.4.1.tar.gz | tar xvz && \ | |||
cd protobuf-3.4.1 && \ | |||
DIST_LANG=cpp ./configure && \ | |||
make && \ | |||
make install && \ | |||
cd .. && \ | |||
rm -rf protobuf-3.4.1 | |||
update_tools: | |||
@echo "--> Updating tools" | |||
@go get -u $(GOTOOLS) | |||
get_vendor_deps: | |||
@rm -rf vendor/ | |||
@echo "--> Running dep ensure" | |||
@dep ensure | |||
######################################## | |||
### Testing | |||
test: | |||
@find . -path ./vendor -prune -o -name "*.sock" -exec rm {} \; | |||
@echo "==> Running go test" | |||
@go test $(PACKAGES) | |||
test_race: | |||
@find . -path ./vendor -prune -o -name "*.sock" -exec rm {} \; | |||
@echo "==> Running go test --race" | |||
@go test -v -race $(PACKAGES) | |||
### three tests tested by Jenkins | |||
test_cover: | |||
@ bash tests/test_cover.sh | |||
test_apps: | |||
# test the counter using a go test script | |||
@ bash tests/test_app/test.sh | |||
test_cli: | |||
# test the cli against the examples in the tutorial at: | |||
# http://tendermint.readthedocs.io/projects/tools/en/master/abci-cli.html | |||
# | |||
# XXX: if this test fails, fix it and update the docs at: | |||
# https://github.com/tendermint/tendermint/blob/develop/docs/abci-cli.rst | |||
@ bash tests/test_cli/test.sh | |||
######################################## | |||
### Formatting, linting, and vetting | |||
fmt: | |||
@go fmt ./... | |||
metalinter: | |||
@echo "==> Running linter" | |||
gometalinter.v2 --vendor --deadline=600s --disable-all \ | |||
--enable=maligned \ | |||
--enable=deadcode \ | |||
--enable=goconst \ | |||
--enable=goimports \ | |||
--enable=gosimple \ | |||
--enable=ineffassign \ | |||
--enable=megacheck \ | |||
--enable=misspell \ | |||
--enable=staticcheck \ | |||
--enable=safesql \ | |||
--enable=structcheck \ | |||
--enable=unconvert \ | |||
--enable=unused \ | |||
--enable=varcheck \ | |||
--enable=vetshadow \ | |||
./... | |||
#--enable=gas \ | |||
#--enable=dupl \ | |||
#--enable=errcheck \ | |||
#--enable=gocyclo \ | |||
#--enable=golint \ <== comments on anything exported | |||
#--enable=gotype \ | |||
#--enable=interfacer \ | |||
#--enable=unparam \ | |||
#--enable=vet \ | |||
metalinter_all: | |||
protoc $(INCLUDE) --lint_out=. types/*.proto | |||
gometalinter.v2 --vendor --deadline=600s --enable-all --disable=lll ./... | |||
######################################## | |||
### Docker | |||
DEVDOC_SAVE = docker commit `docker ps -a -n 1 -q` devdoc:local | |||
docker_build: | |||
docker build -t "tendermint/abci-dev" -f Dockerfile.develop . | |||
docker_run: | |||
docker run -it -v "$(CURDIR):/go/src/github.com/tendermint/abci" -w "/go/src/github.com/tendermint/abci" "tendermint/abci-dev" /bin/bash | |||
docker_run_rm: | |||
docker run -it --rm -v "$(CURDIR):/go/src/github.com/tendermint/abci" -w "/go/src/github.com/tendermint/abci" "tendermint/abci-dev" /bin/bash | |||
devdoc_init: | |||
docker run -it -v "$(CURDIR):/go/src/github.com/tendermint/abci" -w "/go/src/github.com/tendermint/abci" tendermint/devdoc echo | |||
# TODO make this safer | |||
$(call DEVDOC_SAVE) | |||
devdoc: | |||
docker run -it -v "$(CURDIR):/go/src/github.com/tendermint/abci" -w "/go/src/github.com/tendermint/abci" devdoc:local bash | |||
devdoc_save: | |||
# TODO make this safer | |||
$(call DEVDOC_SAVE) | |||
devdoc_clean: | |||
docker rmi $$(docker images -f "dangling=true" -q) | |||
# To avoid unintended conflicts with file names, always add to .PHONY | |||
# unless there is a reason not to. | |||
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html | |||
.PHONY: check protoc build dist install check_tools get_tools get_protoc update_tools get_vendor_deps test test_race fmt metalinter metalinter_all docker_build docker_run docker_run_rm devdoc_init devdoc devdoc_save devdoc_clean |
@ -1,12 +0,0 @@ | |||
FROM golang:1.9.2 | |||
RUN apt-get update && apt-get install -y --no-install-recommends \ | |||
zip \ | |||
&& rm -rf /var/lib/apt/lists/* | |||
# We want to ensure that release builds never have any cgo dependencies so we | |||
# switch that off at the highest level. | |||
ENV CGO_ENABLED 0 | |||
RUN mkdir -p $GOPATH/src/github.com/tendermint/abci | |||
WORKDIR $GOPATH/src/github.com/tendermint/abci |
@ -1,52 +0,0 @@ | |||
#!/usr/bin/env bash | |||
set -e | |||
REPO_NAME="abci" | |||
# Get the version from the environment, or try to figure it out. | |||
if [ -z $VERSION ]; then | |||
VERSION=$(awk -F\" '/Version =/ { print $2; exit }' < version/version.go) | |||
fi | |||
if [ -z "$VERSION" ]; then | |||
echo "Please specify a version." | |||
exit 1 | |||
fi | |||
echo "==> Building version $VERSION..." | |||
# Get the parent directory of where this script is. | |||
SOURCE="${BASH_SOURCE[0]}" | |||
while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done | |||
DIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )" | |||
# Change into that dir because we expect that. | |||
cd "$DIR" | |||
# Delete the old dir | |||
echo "==> Removing old directory..." | |||
rm -rf build/pkg | |||
mkdir -p build/pkg | |||
# Do a hermetic build inside a Docker container. | |||
docker build -t tendermint/${REPO_NAME}-builder scripts/${REPO_NAME}-builder/ | |||
docker run --rm -e "BUILD_TAGS=$BUILD_TAGS" -v "$(pwd)":/go/src/github.com/tendermint/${REPO_NAME} tendermint/${REPO_NAME}-builder ./scripts/dist_build.sh | |||
# Add $REPO_NAME and $VERSION prefix to package name. | |||
rm -rf ./build/dist | |||
mkdir -p ./build/dist | |||
for FILENAME in $(find ./build/pkg -mindepth 1 -maxdepth 1 -type f); do | |||
FILENAME=$(basename "$FILENAME") | |||
cp "./build/pkg/${FILENAME}" "./build/dist/${REPO_NAME}_${VERSION}_${FILENAME}" | |||
done | |||
# Make the checksums. | |||
pushd ./build/dist | |||
shasum -a256 ./* > "./${REPO_NAME}_${VERSION}_SHA256SUMS" | |||
popd | |||
# Done | |||
echo | |||
echo "==> Results:" | |||
ls -hl ./build/dist | |||
exit 0 |
@ -1,53 +0,0 @@ | |||
#!/usr/bin/env bash | |||
set -e | |||
# Get the parent directory of where this script is. | |||
SOURCE="${BASH_SOURCE[0]}" | |||
while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done | |||
DIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )" | |||
# Change into that dir because we expect that. | |||
cd "$DIR" | |||
# Get the git commit | |||
GIT_COMMIT="$(git rev-parse --short HEAD)" | |||
GIT_DESCRIBE="$(git describe --tags --always)" | |||
GIT_IMPORT="github.com/tendermint/abci/version" | |||
# Determine the arch/os combos we're building for | |||
XC_ARCH=${XC_ARCH:-"386 amd64 arm"} | |||
XC_OS=${XC_OS:-"solaris darwin freebsd linux windows"} | |||
# Make sure build tools are available. | |||
make get_tools | |||
# Get VENDORED dependencies | |||
make get_vendor_deps | |||
BINARY="abci-cli" | |||
# Build! | |||
echo "==> Building..." | |||
"$(which gox)" \ | |||
-os="${XC_OS}" \ | |||
-arch="${XC_ARCH}" \ | |||
-osarch="!darwin/arm !solaris/amd64 !freebsd/amd64" \ | |||
-ldflags "-X ${GIT_IMPORT}.GitCommit='${GIT_COMMIT}' -X ${GIT_IMPORT}.GitDescribe='${GIT_DESCRIBE}'" \ | |||
-output "build/pkg/{{.OS}}_{{.Arch}}/$BINARY" \ | |||
-tags="${BUILD_TAGS}" \ | |||
github.com/tendermint/abci/cmd/$BINARY | |||
# Zip all the files. | |||
echo "==> Packaging..." | |||
for PLATFORM in $(find ./build/pkg -mindepth 1 -maxdepth 1 -type d); do | |||
OSARCH=$(basename "${PLATFORM}") | |||
echo "--> ${OSARCH}" | |||
pushd "$PLATFORM" >/dev/null 2>&1 | |||
zip "../${OSARCH}.zip" ./* | |||
popd >/dev/null 2>&1 | |||
done | |||
exit 0 |
@ -1,7 +0,0 @@ | |||
#! /bin/bash | |||
# Get the version from the environment, or try to figure it out. | |||
if [ -z $VERSION ]; then | |||
VERSION=$(awk -F\" '/Version =/ { print $2; exit }' < version/version.go) | |||
fi | |||
aws s3 cp --recursive build/dist s3://tendermint/binaries/abci/v${VERSION} --acl public-read |
@ -1,294 +0,0 @@ | |||
ABCI Specification | |||
================== | |||
NOTE: this file has moved to `specification.md <./specification.md>`__. It is left to prevent link breakages for the forseable future. It can safely be deleted in a few months. | |||
Message Types | |||
~~~~~~~~~~~~~ | |||
ABCI requests/responses are defined as simple Protobuf messages in `this | |||
schema | |||
file <https://github.com/tendermint/abci/blob/master/types/types.proto>`__. | |||
TendermintCore sends the requests, and the ABCI application sends the | |||
responses. Here, we provide an overview of the messages types and how they | |||
are used by Tendermint. Then we describe each request-response pair as a | |||
function with arguments and return values, and add some notes on usage. | |||
Some messages (``Echo, Info, InitChain, BeginBlock, EndBlock, Commit``), don't | |||
return errors because an error would indicate a critical failure in the | |||
application and there's nothing Tendermint can do. The problem should be | |||
addressed and both Tendermint and the application restarted. All other | |||
messages (``SetOption, Query, CheckTx, DeliverTx``) return an | |||
application-specific response ``Code uint32``, where only ``0`` is reserved for | |||
``OK``. | |||
Some messages (``SetOption, Query, CheckTx, DeliverTx``) return | |||
non-deterministic data in the form of ``Info`` and ``Log``. The ``Log`` is | |||
intended for the literal output from the application's logger, while the | |||
``Info`` is any additional info that should be returned. | |||
The first time a new blockchain is started, Tendermint calls ``InitChain``. | |||
From then on, the Block Execution Sequence that causes the committed state to | |||
be updated is as follows: | |||
``BeginBlock, [DeliverTx], EndBlock, Commit`` | |||
where one ``DeliverTx`` is called for each transaction in the block. | |||
Cryptographic commitments to the results of DeliverTx, EndBlock, and | |||
Commit are included in the header of the next block. | |||
Tendermint opens three connections to the application to handle the different message | |||
types: | |||
- ``Consensus Connection - InitChain, BeginBlock, DeliverTx, EndBlock, Commit`` | |||
- ``Mempool Connection - CheckTx`` | |||
- ``Info Connection - Info, SetOption, Query`` | |||
The ``Flush`` message is used on every connection, and the ``Echo`` message | |||
is only used for debugging. | |||
Note that messages may be sent concurrently across all connections - | |||
a typical application will thus maintain a distinct state for each | |||
connection. They may be referred to as the ``DeliverTx state``, the | |||
``CheckTx state``, and the ``Commit state`` respectively. | |||
See below for more details on the message types and how they are used. | |||
Echo | |||
^^^^ | |||
- **Arguments**: | |||
- ``Message (string)``: A string to echo back | |||
- **Returns**: | |||
- ``Message (string)``: The input string | |||
- **Usage**: | |||
- Echo a string to test an abci client/server implementation | |||
Flush | |||
^^^^^ | |||
- **Usage**: | |||
- Signals that messages queued on the client should be flushed to | |||
the server. It is called periodically by the client implementation | |||
to ensure asynchronous requests are actually sent, and is called | |||
immediately to make a synchronous request, which returns when the | |||
Flush response comes back. | |||
Info | |||
^^^^ | |||
- **Arguments**: | |||
- ``Version (string)``: The Tendermint version | |||
- **Returns**: | |||
- ``Data (string)``: Some arbitrary information | |||
- ``Version (Version)``: Version information | |||
- ``LastBlockHeight (int64)``: Latest block for which the app has | |||
called Commit | |||
- ``LastBlockAppHash ([]byte)``: Latest result of Commit | |||
- **Usage**: | |||
- Return information about the application state. | |||
- Used to sync Tendermint with the application during a handshake that | |||
happens on startup. | |||
- Tendermint expects ``LastBlockAppHash`` and ``LastBlockHeight`` to be | |||
updated during ``Commit``, ensuring that ``Commit`` is never called twice | |||
for the same block height. | |||
SetOption | |||
^^^^^^^^^ | |||
- **Arguments**: | |||
- ``Key (string)``: Key to set | |||
- ``Value (string)``: Value to set for key | |||
- **Returns**: | |||
- ``Code (uint32)``: Response code | |||
- ``Log (string)``: The output of the application's logger. May be non-deterministic. | |||
- ``Info (string)``: Additional information. May be non-deterministic. | |||
- **Usage**: | |||
- Set non-consensus critical application specific options. | |||
- e.g. Key="min-fee", Value="100fermion" could set the minimum fee required for CheckTx | |||
(but not DeliverTx - that would be consensus critical). | |||
InitChain | |||
^^^^^^^^^ | |||
- **Arguments**: | |||
- ``Validators ([]Validator)``: Initial genesis validators | |||
- ``AppStateBytes ([]byte)``: Serialized initial application state | |||
- **Usage**: | |||
- Called once upon genesis. | |||
Query | |||
^^^^^ | |||
- **Arguments**: | |||
- ``Data ([]byte)``: Raw query bytes. Can be used with or in lieu of | |||
Path. | |||
- ``Path (string)``: Path of request, like an HTTP GET path. Can be | |||
used with or in liue of Data. | |||
- Apps MUST interpret '/store' as a query by key on the underlying | |||
store. The key SHOULD be specified in the Data field. | |||
- Apps SHOULD allow queries over specific types like '/accounts/...' | |||
or '/votes/...' | |||
- ``Height (int64)``: The block height for which you want the query | |||
(default=0 returns data for the latest committed block). Note that | |||
this is the height of the block containing the application's | |||
Merkle root hash, which represents the state as it was after | |||
committing the block at Height-1 | |||
- ``Prove (bool)``: Return Merkle proof with response if possible | |||
- **Returns**: | |||
- ``Code (uint32)``: Response code. | |||
- ``Log (string)``: The output of the application's logger. May be non-deterministic. | |||
- ``Info (string)``: Additional information. May be non-deterministic. | |||
- ``Index (int64)``: The index of the key in the tree. | |||
- ``Key ([]byte)``: The key of the matching data. | |||
- ``Value ([]byte)``: The value of the matching data. | |||
- ``Proof ([]byte)``: Proof for the data, if requested. | |||
- ``Height (int64)``: The block height from which data was derived. | |||
Note that this is the height of the block containing the | |||
application's Merkle root hash, which represents the state as it | |||
was after committing the block at Height-1 | |||
- **Usage**: | |||
- Query for data from the application at current or past height. | |||
- Optionally return Merkle proof. | |||
BeginBlock | |||
^^^^^^^^^^ | |||
- **Arguments**: | |||
- ``Hash ([]byte)``: The block's hash. This can be derived from the | |||
block header. | |||
- ``Header (struct{})``: The block header | |||
- ``AbsentValidators ([]int32)``: List of indices of validators not | |||
included in the LastCommit | |||
- ``ByzantineValidators ([]Evidence)``: List of evidence of | |||
validators that acted maliciously | |||
- **Usage**: | |||
- Signals the beginning of a new block. Called prior to any DeliverTxs. | |||
- The header is expected to at least contain the Height. | |||
- The ``AbsentValidators`` and ``ByzantineValidators`` can be used to | |||
determine rewards and punishments for the validators. | |||
CheckTx | |||
^^^^^^^ | |||
- **Arguments**: | |||
- ``Tx ([]byte)``: The request transaction bytes | |||
- **Returns**: | |||
- ``Code (uint32)``: Response code | |||
- ``Data ([]byte)``: Result bytes, if any. | |||
- ``Log (string)``: The output of the application's logger. May be non-deterministic. | |||
- ``Info (string)``: Additional information. May be non-deterministic. | |||
- ``GasWanted (int64)``: Amount of gas request for transaction. | |||
- ``GasUsed (int64)``: Amount of gas consumed by transaction. | |||
- ``Tags ([]cmn.KVPair)``: Key-Value tags for filtering and indexing transactions (eg. by account). | |||
- ``Fee (cmn.KI64Pair)``: Fee paid for the transaction. | |||
- **Usage**: Validate a mempool transaction, prior to broadcasting or | |||
proposing. CheckTx should perform stateful but light-weight checks | |||
of the validity of the transaction (like checking signatures and account balances), | |||
but need not execute in full (like running a smart contract). | |||
Tendermint runs CheckTx and DeliverTx concurrently with eachother, | |||
though on distinct ABCI connections - the mempool connection and the consensus | |||
connection, respectively. | |||
The application should maintain a separate state to support CheckTx. | |||
This state can be reset to the latest committed state during ``Commit``, | |||
where Tendermint ensures the mempool is locked and not sending new ``CheckTx``. | |||
After ``Commit``, the mempool will rerun CheckTx on all remaining | |||
transactions, throwing out any that are no longer valid. | |||
Keys and values in Tags must be UTF-8 encoded strings (e.g. "account.owner": "Bob", "balance": "100.0", "date": "2018-01-02") | |||
DeliverTx | |||
^^^^^^^^^ | |||
- **Arguments**: | |||
- ``Tx ([]byte)``: The request transaction bytes. | |||
- **Returns**: | |||
- ``Code (uint32)``: Response code. | |||
- ``Data ([]byte)``: Result bytes, if any. | |||
- ``Log (string)``: The output of the application's logger. May be non-deterministic. | |||
- ``Info (string)``: Additional information. May be non-deterministic. | |||
- ``GasWanted (int64)``: Amount of gas requested for transaction. | |||
- ``GasUsed (int64)``: Amount of gas consumed by transaction. | |||
- ``Tags ([]cmn.KVPair)``: Key-Value tags for filtering and indexing transactions (eg. by account). | |||
- ``Fee (cmn.KI64Pair)``: Fee paid for the transaction. | |||
- **Usage**: | |||
- Deliver a transaction to be executed in full by the application. If the transaction is valid, | |||
returns CodeType.OK. | |||
- Keys and values in Tags must be UTF-8 encoded strings (e.g. "account.owner": "Bob", "balance": "100.0", "time": "2018-01-02T12:30:00Z") | |||
EndBlock | |||
^^^^^^^^ | |||
- **Arguments**: | |||
- ``Height (int64)``: Height of the block just executed. | |||
- **Returns**: | |||
- ``ValidatorUpdates ([]Validator)``: Changes to validator set (set | |||
voting power to 0 to remove). | |||
- ``ConsensusParamUpdates (ConsensusParams)``: Changes to | |||
consensus-critical time, size, and other parameters. | |||
- **Usage**: | |||
- Signals the end of a block. | |||
- Called prior to each Commit, after all transactions. | |||
- Validator set and consensus params are updated with the result. | |||
- Validator pubkeys are expected to be go-wire encoded. | |||
Commit | |||
^^^^^^ | |||
- **Returns**: | |||
- ``Data ([]byte)``: The Merkle root hash | |||
- **Usage**: | |||
- Persist the application state. | |||
- Return a Merkle root hash of the application state. | |||
- It's critical that all application instances return the same hash. If not, | |||
they will not be able to agree on the next block, because the hash is | |||
included in the next block! |
@ -1,13 +0,0 @@ | |||
#!/usr/bin/env bash | |||
set -e | |||
echo "" > coverage.txt | |||
echo "==> Running unit tests" | |||
for d in $(go list ./... | grep -v vendor); do | |||
go test -race -coverprofile=profile.out -covermode=atomic "$d" | |||
if [ -f profile.out ]; then | |||
cat profile.out >> coverage.txt | |||
rm profile.out | |||
fi | |||
done |
@ -1,137 +0,0 @@ | |||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. | |||
[[projects]] | |||
branch = "master" | |||
name = "github.com/btcsuite/btcd" | |||
packages = ["btcec"] | |||
revision = "86fed781132ac890ee03e906e4ecd5d6fa180c64" | |||
[[projects]] | |||
branch = "master" | |||
name = "github.com/btcsuite/btcutil" | |||
packages = ["base58"] | |||
revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4" | |||
[[projects]] | |||
name = "github.com/davecgh/go-spew" | |||
packages = ["spew"] | |||
revision = "346938d642f2ec3594ed81d874461961cd0faa76" | |||
version = "v1.1.0" | |||
[[projects]] | |||
name = "github.com/go-kit/kit" | |||
packages = [ | |||
"log", | |||
"log/level", | |||
"log/term" | |||
] | |||
revision = "4dc7be5d2d12881735283bcab7352178e190fc71" | |||
version = "v0.6.0" | |||
[[projects]] | |||
name = "github.com/go-logfmt/logfmt" | |||
packages = ["."] | |||
revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5" | |||
version = "v0.3.0" | |||
[[projects]] | |||
name = "github.com/go-stack/stack" | |||
packages = ["."] | |||
revision = "259ab82a6cad3992b4e21ff5cac294ccb06474bc" | |||
version = "v1.7.0" | |||
[[projects]] | |||
name = "github.com/gogo/protobuf" | |||
packages = [ | |||
"gogoproto", | |||
"proto", | |||
"protoc-gen-gogo/descriptor" | |||
] | |||
revision = "1adfc126b41513cc696b209667c8656ea7aac67c" | |||
version = "v1.0.0" | |||
[[projects]] | |||
branch = "master" | |||
name = "github.com/kr/logfmt" | |||
packages = ["."] | |||
revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0" | |||
[[projects]] | |||
name = "github.com/pkg/errors" | |||
packages = ["."] | |||
revision = "645ef00459ed84a119197bfb8d8205042c6df63d" | |||
version = "v0.8.0" | |||
[[projects]] | |||
name = "github.com/pmezard/go-difflib" | |||
packages = ["difflib"] | |||
revision = "792786c7400a136282c1664665ae0a8db921c6c2" | |||
version = "v1.0.0" | |||
[[projects]] | |||
name = "github.com/stretchr/testify" | |||
packages = [ | |||
"assert", | |||
"require" | |||
] | |||
revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686" | |||
version = "v1.2.2" | |||
[[projects]] | |||
branch = "master" | |||
name = "github.com/tendermint/ed25519" | |||
packages = [ | |||
".", | |||
"edwards25519", | |||
"extra25519" | |||
] | |||
revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057" | |||
[[projects]] | |||
name = "github.com/tendermint/go-amino" | |||
packages = ["."] | |||
revision = "2106ca61d91029c931fd54968c2bb02dc96b1412" | |||
version = "0.10.1" | |||
[[projects]] | |||
name = "github.com/tendermint/tmlibs" | |||
packages = [ | |||
"common", | |||
"log", | |||
"test" | |||
] | |||
revision = "692f1d86a6e2c0efa698fd1e4541b68c74ffaf38" | |||
version = "v0.8.4" | |||
[[projects]] | |||
branch = "master" | |||
name = "golang.org/x/crypto" | |||
packages = [ | |||
"bcrypt", | |||
"blowfish", | |||
"chacha20poly1305", | |||
"hkdf", | |||
"internal/chacha20", | |||
"internal/subtle", | |||
"nacl/secretbox", | |||
"openpgp/armor", | |||
"openpgp/errors", | |||
"poly1305", | |||
"ripemd160", | |||
"salsa20/salsa" | |||
] | |||
revision = "7f39a6fea4fe9364fb61e1def6a268a51b4f3a06" | |||
[[projects]] | |||
branch = "master" | |||
name = "golang.org/x/sys" | |||
packages = ["cpu"] | |||
revision = "ad87a3a340fa7f3bed189293fbfa7a9b7e021ae1" | |||
[solve-meta] | |||
analyzer-name = "dep" | |||
analyzer-version = 1 | |||
inputs-digest = "027b22b86396a971d5d5c1d298947f531f39743975d65a22e98601140aa1b1a1" | |||
solver-name = "gps-cdcl" | |||
solver-version = 1 |
@ -1,49 +0,0 @@ | |||
# Gopkg.toml example | |||
# | |||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md | |||
# for detailed Gopkg.toml documentation. | |||
# | |||
# required = ["github.com/user/thing/cmd/thing"] | |||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] | |||
# | |||
# [[constraint]] | |||
# name = "github.com/user/project" | |||
# version = "1.0.0" | |||
# | |||
# [[constraint]] | |||
# name = "github.com/user/project2" | |||
# branch = "dev" | |||
# source = "github.com/myfork/project2" | |||
# | |||
# [[override]] | |||
# name = "github.com/x/y" | |||
# version = "2.4.0" | |||
# | |||
# [prune] | |||
# non-go = false | |||
# go-tests = true | |||
# unused-packages = true | |||
[[constraint]] | |||
name = "github.com/btcsuite/btcutil" | |||
branch = "master" | |||
[[constraint]] | |||
name = "github.com/stretchr/testify" | |||
version = "1.2.1" | |||
[[constraint]] | |||
name = "github.com/tendermint/ed25519" | |||
branch = "master" | |||
[[constraint]] | |||
name = "github.com/tendermint/go-amino" | |||
version = "0.10.0-rc2" | |||
[[constraint]] | |||
name = "github.com/tendermint/tmlibs" | |||
version = "0.8.1" | |||
[prune] | |||
go-tests = true | |||
unused-packages = true |
@ -1,37 +0,0 @@ | |||
package crypto | |||
import ( | |||
amino "github.com/tendermint/go-amino" | |||
) | |||
var cdc = amino.NewCodec() | |||
func init() { | |||
// NOTE: It's important that there be no conflicts here, | |||
// as that would change the canonical representations, | |||
// and therefore change the address. | |||
// TODO: Add feature to go-amino to ensure that there | |||
// are no conflicts. | |||
RegisterAmino(cdc) | |||
} | |||
// RegisterAmino registers all crypto related types in the given (amino) codec. | |||
func RegisterAmino(cdc *amino.Codec) { | |||
cdc.RegisterInterface((*PubKey)(nil), nil) | |||
cdc.RegisterConcrete(PubKeyEd25519{}, | |||
"tendermint/PubKeyEd25519", nil) | |||
cdc.RegisterConcrete(PubKeySecp256k1{}, | |||
"tendermint/PubKeySecp256k1", nil) | |||
cdc.RegisterInterface((*PrivKey)(nil), nil) | |||
cdc.RegisterConcrete(PrivKeyEd25519{}, | |||
"tendermint/PrivKeyEd25519", nil) | |||
cdc.RegisterConcrete(PrivKeySecp256k1{}, | |||
"tendermint/PrivKeySecp256k1", nil) | |||
cdc.RegisterInterface((*Signature)(nil), nil) | |||
cdc.RegisterConcrete(SignatureEd25519{}, | |||
"tendermint/SignatureEd25519", nil) | |||
cdc.RegisterConcrete(SignatureSecp256k1{}, | |||
"tendermint/SignatureSecp256k1", nil) | |||
} |
@ -0,0 +1,36 @@ | |||
package crypto | |||
import ( | |||
cmn "github.com/tendermint/tendermint/libs/common" | |||
) | |||
type PrivKey interface { | |||
Bytes() []byte | |||
Sign(msg []byte) (Signature, error) | |||
PubKey() PubKey | |||
Equals(PrivKey) bool | |||
} | |||
// An address is a []byte, but hex-encoded even in JSON. | |||
// []byte leaves us the option to change the address length. | |||
// Use an alias so Unmarshal methods (with ptr receivers) are available too. | |||
type Address = cmn.HexBytes | |||
type PubKey interface { | |||
Address() Address | |||
Bytes() []byte | |||
VerifyBytes(msg []byte, sig Signature) bool | |||
Equals(PubKey) bool | |||
} | |||
type Signature interface { | |||
Bytes() []byte | |||
IsZero() bool | |||
Equals(Signature) bool | |||
} | |||
type Symmetric interface { | |||
Keygen() []byte | |||
Encrypt(plaintext []byte, secret []byte) (ciphertext []byte) | |||
Decrypt(ciphertext []byte, secret []byte) (plaintext []byte, err error) | |||
} |
@ -0,0 +1,227 @@ | |||
package ed25519 | |||
import ( | |||
"bytes" | |||
"crypto/subtle" | |||
"fmt" | |||
"github.com/tendermint/ed25519" | |||
"github.com/tendermint/ed25519/extra25519" | |||
amino "github.com/tendermint/go-amino" | |||
"github.com/tendermint/tendermint/crypto" | |||
"github.com/tendermint/tendermint/crypto/tmhash" | |||
cmn "github.com/tendermint/tendermint/libs/common" | |||
) | |||
//------------------------------------- | |||
var _ crypto.PrivKey = PrivKeyEd25519{} | |||
const ( | |||
Ed25519PrivKeyAminoRoute = "tendermint/PrivKeyEd25519" | |||
Ed25519PubKeyAminoRoute = "tendermint/PubKeyEd25519" | |||
Ed25519SignatureAminoRoute = "tendermint/SignatureEd25519" | |||
) | |||
var cdc = amino.NewCodec() | |||
func init() { | |||
cdc.RegisterInterface((*crypto.PubKey)(nil), nil) | |||
cdc.RegisterConcrete(PubKeyEd25519{}, | |||
Ed25519PubKeyAminoRoute, nil) | |||
cdc.RegisterInterface((*crypto.PrivKey)(nil), nil) | |||
cdc.RegisterConcrete(PrivKeyEd25519{}, | |||
Ed25519PrivKeyAminoRoute, nil) | |||
cdc.RegisterInterface((*crypto.Signature)(nil), nil) | |||
cdc.RegisterConcrete(SignatureEd25519{}, | |||
Ed25519SignatureAminoRoute, nil) | |||
} | |||
// PrivKeyEd25519 implements crypto.PrivKey. | |||
type PrivKeyEd25519 [64]byte | |||
// Bytes marshals the privkey using amino encoding. | |||
func (privKey PrivKeyEd25519) Bytes() []byte { | |||
return cdc.MustMarshalBinaryBare(privKey) | |||
} | |||
// Sign produces a signature on the provided message. | |||
func (privKey PrivKeyEd25519) Sign(msg []byte) (crypto.Signature, error) { | |||
privKeyBytes := [64]byte(privKey) | |||
signatureBytes := ed25519.Sign(&privKeyBytes, msg) | |||
return SignatureEd25519(*signatureBytes), nil | |||
} | |||
// PubKey gets the corresponding public key from the private key. | |||
func (privKey PrivKeyEd25519) PubKey() crypto.PubKey { | |||
privKeyBytes := [64]byte(privKey) | |||
initialized := false | |||
// If the latter 32 bytes of the privkey are all zero, compute the pubkey | |||
// otherwise privkey is initialized and we can use the cached value inside | |||
// of the private key. | |||
for _, v := range privKeyBytes[32:] { | |||
if v != 0 { | |||
initialized = true | |||
break | |||
} | |||
} | |||
if initialized { | |||
var pubkeyBytes [PubKeyEd25519Size]byte | |||
copy(pubkeyBytes[:], privKeyBytes[32:]) | |||
return PubKeyEd25519(pubkeyBytes) | |||
} | |||
pubBytes := *ed25519.MakePublicKey(&privKeyBytes) | |||
return PubKeyEd25519(pubBytes) | |||
} | |||
// Equals - you probably don't need to use this. | |||
// Runs in constant time based on length of the keys. | |||
func (privKey PrivKeyEd25519) Equals(other crypto.PrivKey) bool { | |||
if otherEd, ok := other.(PrivKeyEd25519); ok { | |||
return subtle.ConstantTimeCompare(privKey[:], otherEd[:]) == 1 | |||
} else { | |||
return false | |||
} | |||
} | |||
// ToCurve25519 takes a private key and returns its representation on | |||
// Curve25519. Curve25519 is birationally equivalent to Edwards25519, | |||
// which Ed25519 uses internally. This method is intended for use in | |||
// an X25519 Diffie Hellman key exchange. | |||
func (privKey PrivKeyEd25519) ToCurve25519() *[PubKeyEd25519Size]byte { | |||
keyCurve25519 := new([32]byte) | |||
privKeyBytes := [64]byte(privKey) | |||
extra25519.PrivateKeyToCurve25519(keyCurve25519, &privKeyBytes) | |||
return keyCurve25519 | |||
} | |||
// GenPrivKey generates a new ed25519 private key. | |||
// It uses OS randomness in conjunction with the current global random seed | |||
// in tendermint/libs/common to generate the private key. | |||
func GenPrivKey() PrivKeyEd25519 { | |||
privKey := new([64]byte) | |||
copy(privKey[:32], crypto.CRandBytes(32)) | |||
// ed25519.MakePublicKey(privKey) alters the last 32 bytes of privKey. | |||
// It places the pubkey in the last 32 bytes of privKey, and returns the | |||
// public key. | |||
ed25519.MakePublicKey(privKey) | |||
return PrivKeyEd25519(*privKey) | |||
} | |||
// GenPrivKeyFromSecret hashes the secret with SHA2, and uses | |||
// that 32 byte output to create the private key. | |||
// NOTE: secret should be the output of a KDF like bcrypt, | |||
// if it's derived from user input. | |||
func GenPrivKeyFromSecret(secret []byte) PrivKeyEd25519 { | |||
privKey32 := crypto.Sha256(secret) // Not Ripemd160 because we want 32 bytes. | |||
privKey := new([64]byte) | |||
copy(privKey[:32], privKey32) | |||
// ed25519.MakePublicKey(privKey) alters the last 32 bytes of privKey. | |||
// It places the pubkey in the last 32 bytes of privKey, and returns the | |||
// public key. | |||
ed25519.MakePublicKey(privKey) | |||
return PrivKeyEd25519(*privKey) | |||
} | |||
//------------------------------------- | |||
var _ crypto.PubKey = PubKeyEd25519{} | |||
// PubKeyEd25519Size is the number of bytes in an Ed25519 signature. | |||
const PubKeyEd25519Size = 32 | |||
// PubKeyEd25519 implements crypto.PubKey for the Ed25519 signature scheme. | |||
type PubKeyEd25519 [PubKeyEd25519Size]byte | |||
// Address is the SHA256-20 of the raw pubkey bytes. | |||
func (pubKey PubKeyEd25519) Address() crypto.Address { | |||
return crypto.Address(tmhash.Sum(pubKey[:])) | |||
} | |||
// Bytes marshals the PubKey using amino encoding. | |||
func (pubKey PubKeyEd25519) Bytes() []byte { | |||
bz, err := cdc.MarshalBinaryBare(pubKey) | |||
if err != nil { | |||
panic(err) | |||
} | |||
return bz | |||
} | |||
func (pubKey PubKeyEd25519) VerifyBytes(msg []byte, sig_ crypto.Signature) bool { | |||
// make sure we use the same algorithm to sign | |||
sig, ok := sig_.(SignatureEd25519) | |||
if !ok { | |||
return false | |||
} | |||
pubKeyBytes := [PubKeyEd25519Size]byte(pubKey) | |||
sigBytes := [SignatureEd25519Size]byte(sig) | |||
return ed25519.Verify(&pubKeyBytes, msg, &sigBytes) | |||
} | |||
// ToCurve25519 takes a public key and returns its representation on | |||
// Curve25519. Curve25519 is birationally equivalent to Edwards25519, | |||
// which Ed25519 uses internally. This method is intended for use in | |||
// an X25519 Diffie Hellman key exchange. | |||
// | |||
// If there is an error, then this function returns nil. | |||
func (pubKey PubKeyEd25519) ToCurve25519() *[PubKeyEd25519Size]byte { | |||
keyCurve25519, pubKeyBytes := new([PubKeyEd25519Size]byte), [PubKeyEd25519Size]byte(pubKey) | |||
ok := extra25519.PublicKeyToCurve25519(keyCurve25519, &pubKeyBytes) | |||
if !ok { | |||
return nil | |||
} | |||
return keyCurve25519 | |||
} | |||
func (pubKey PubKeyEd25519) String() string { | |||
return fmt.Sprintf("PubKeyEd25519{%X}", pubKey[:]) | |||
} | |||
// nolint: golint | |||
func (pubKey PubKeyEd25519) Equals(other crypto.PubKey) bool { | |||
if otherEd, ok := other.(PubKeyEd25519); ok { | |||
return bytes.Equal(pubKey[:], otherEd[:]) | |||
} else { | |||
return false | |||
} | |||
} | |||
//------------------------------------- | |||
var _ crypto.Signature = SignatureEd25519{} | |||
// Size of an Edwards25519 signature. Namely the size of a compressed | |||
// Edwards25519 point, and a field element. Both of which are 32 bytes. | |||
const SignatureEd25519Size = 64 | |||
// SignatureEd25519 implements crypto.Signature | |||
type SignatureEd25519 [SignatureEd25519Size]byte | |||
func (sig SignatureEd25519) Bytes() []byte { | |||
bz, err := cdc.MarshalBinaryBare(sig) | |||
if err != nil { | |||
panic(err) | |||
} | |||
return bz | |||
} | |||
func (sig SignatureEd25519) IsZero() bool { return len(sig) == 0 } | |||
func (sig SignatureEd25519) String() string { return fmt.Sprintf("/%X.../", cmn.Fingerprint(sig[:])) } | |||
func (sig SignatureEd25519) Equals(other crypto.Signature) bool { | |||
if otherEd, ok := other.(SignatureEd25519); ok { | |||
return subtle.ConstantTimeCompare(sig[:], otherEd[:]) == 1 | |||
} else { | |||
return false | |||
} | |||
} | |||
func SignatureEd25519FromBytes(data []byte) crypto.Signature { | |||
var sig SignatureEd25519 | |||
copy(sig[:], data) | |||
return sig | |||
} |
@ -0,0 +1,31 @@ | |||
package ed25519_test | |||
import ( | |||
"testing" | |||
"github.com/stretchr/testify/assert" | |||
"github.com/stretchr/testify/require" | |||
"github.com/tendermint/tendermint/crypto" | |||
"github.com/tendermint/tendermint/crypto/ed25519" | |||
) | |||
func TestSignAndValidateEd25519(t *testing.T) { | |||
privKey := ed25519.GenPrivKey() | |||
pubKey := privKey.PubKey() | |||
msg := crypto.CRandBytes(128) | |||
sig, err := privKey.Sign(msg) | |||
require.Nil(t, err) | |||
// Test the signature | |||
assert.True(t, pubKey.VerifyBytes(msg, sig)) | |||
// Mutate the signature, just one bit. | |||
// TODO: Replace this with a much better fuzzer, tendermint/ed25519/issues/10 | |||
sigEd := sig.(ed25519.SignatureEd25519) | |||
sigEd[7] ^= byte(0x01) | |||
sig = sigEd | |||
assert.False(t, pubKey.VerifyBytes(msg, sig)) | |||
} |
@ -0,0 +1,57 @@ | |||
package cryptoAmino | |||
import ( | |||
amino "github.com/tendermint/go-amino" | |||
"github.com/tendermint/tendermint/crypto" | |||
"github.com/tendermint/tendermint/crypto/ed25519" | |||
"github.com/tendermint/tendermint/crypto/secp256k1" | |||
) | |||
var cdc = amino.NewCodec() | |||
func init() { | |||
// NOTE: It's important that there be no conflicts here, | |||
// as that would change the canonical representations, | |||
// and therefore change the address. | |||
// TODO: Remove above note when | |||
// https://github.com/tendermint/go-amino/issues/9 | |||
// is resolved | |||
RegisterAmino(cdc) | |||
} | |||
// RegisterAmino registers all crypto related types in the given (amino) codec. | |||
func RegisterAmino(cdc *amino.Codec) { | |||
// These are all written here instead of | |||
cdc.RegisterInterface((*crypto.PubKey)(nil), nil) | |||
cdc.RegisterConcrete(ed25519.PubKeyEd25519{}, | |||
"tendermint/PubKeyEd25519", nil) | |||
cdc.RegisterConcrete(secp256k1.PubKeySecp256k1{}, | |||
"tendermint/PubKeySecp256k1", nil) | |||
cdc.RegisterInterface((*crypto.PrivKey)(nil), nil) | |||
cdc.RegisterConcrete(ed25519.PrivKeyEd25519{}, | |||
"tendermint/PrivKeyEd25519", nil) | |||
cdc.RegisterConcrete(secp256k1.PrivKeySecp256k1{}, | |||
"tendermint/PrivKeySecp256k1", nil) | |||
cdc.RegisterInterface((*crypto.Signature)(nil), nil) | |||
cdc.RegisterConcrete(ed25519.SignatureEd25519{}, | |||
"tendermint/SignatureEd25519", nil) | |||
cdc.RegisterConcrete(secp256k1.SignatureSecp256k1{}, | |||
"tendermint/SignatureSecp256k1", nil) | |||
} | |||
func PrivKeyFromBytes(privKeyBytes []byte) (privKey crypto.PrivKey, err error) { | |||
err = cdc.UnmarshalBinaryBare(privKeyBytes, &privKey) | |||
return | |||
} | |||
func PubKeyFromBytes(pubKeyBytes []byte) (pubKey crypto.PubKey, err error) { | |||
err = cdc.UnmarshalBinaryBare(pubKeyBytes, &pubKey) | |||
return | |||
} | |||
func SignatureFromBytes(pubKeyBytes []byte) (pubKey crypto.Signature, err error) { | |||
err = cdc.UnmarshalBinaryBare(pubKeyBytes, &pubKey) | |||
return | |||
} |
@ -1,164 +0,0 @@ | |||
package crypto | |||
import ( | |||
"crypto/subtle" | |||
secp256k1 "github.com/btcsuite/btcd/btcec" | |||
"github.com/tendermint/ed25519" | |||
"github.com/tendermint/ed25519/extra25519" | |||
) | |||
func PrivKeyFromBytes(privKeyBytes []byte) (privKey PrivKey, err error) { | |||
err = cdc.UnmarshalBinaryBare(privKeyBytes, &privKey) | |||
return | |||
} | |||
//---------------------------------------- | |||
type PrivKey interface { | |||
Bytes() []byte | |||
Sign(msg []byte) (Signature, error) | |||
PubKey() PubKey | |||
Equals(PrivKey) bool | |||
} | |||
//------------------------------------- | |||
var _ PrivKey = PrivKeyEd25519{} | |||
// Implements PrivKey | |||
type PrivKeyEd25519 [64]byte | |||
func (privKey PrivKeyEd25519) Bytes() []byte { | |||
return cdc.MustMarshalBinaryBare(privKey) | |||
} | |||
func (privKey PrivKeyEd25519) Sign(msg []byte) (Signature, error) { | |||
privKeyBytes := [64]byte(privKey) | |||
signatureBytes := ed25519.Sign(&privKeyBytes, msg) | |||
return SignatureEd25519(*signatureBytes), nil | |||
} | |||
func (privKey PrivKeyEd25519) PubKey() PubKey { | |||
privKeyBytes := [64]byte(privKey) | |||
pubBytes := *ed25519.MakePublicKey(&privKeyBytes) | |||
return PubKeyEd25519(pubBytes) | |||
} | |||
// Equals - you probably don't need to use this. | |||
// Runs in constant time based on length of the keys. | |||
func (privKey PrivKeyEd25519) Equals(other PrivKey) bool { | |||
if otherEd, ok := other.(PrivKeyEd25519); ok { | |||
return subtle.ConstantTimeCompare(privKey[:], otherEd[:]) == 1 | |||
} else { | |||
return false | |||
} | |||
} | |||
func (privKey PrivKeyEd25519) ToCurve25519() *[32]byte { | |||
keyCurve25519 := new([32]byte) | |||
privKeyBytes := [64]byte(privKey) | |||
extra25519.PrivateKeyToCurve25519(keyCurve25519, &privKeyBytes) | |||
return keyCurve25519 | |||
} | |||
// Deterministically generates new priv-key bytes from key. | |||
func (privKey PrivKeyEd25519) Generate(index int) PrivKeyEd25519 { | |||
bz, err := cdc.MarshalBinaryBare(struct { | |||
PrivKey [64]byte | |||
Index int | |||
}{privKey, index}) | |||
if err != nil { | |||
panic(err) | |||
} | |||
newBytes := Sha256(bz) | |||
newKey := new([64]byte) | |||
copy(newKey[:32], newBytes) | |||
ed25519.MakePublicKey(newKey) | |||
return PrivKeyEd25519(*newKey) | |||
} | |||
func GenPrivKeyEd25519() PrivKeyEd25519 { | |||
privKeyBytes := new([64]byte) | |||
copy(privKeyBytes[:32], CRandBytes(32)) | |||
ed25519.MakePublicKey(privKeyBytes) | |||
return PrivKeyEd25519(*privKeyBytes) | |||
} | |||
// NOTE: secret should be the output of a KDF like bcrypt, | |||
// if it's derived from user input. | |||
func GenPrivKeyEd25519FromSecret(secret []byte) PrivKeyEd25519 { | |||
privKey32 := Sha256(secret) // Not Ripemd160 because we want 32 bytes. | |||
privKeyBytes := new([64]byte) | |||
copy(privKeyBytes[:32], privKey32) | |||
ed25519.MakePublicKey(privKeyBytes) | |||
return PrivKeyEd25519(*privKeyBytes) | |||
} | |||
//------------------------------------- | |||
var _ PrivKey = PrivKeySecp256k1{} | |||
// Implements PrivKey | |||
type PrivKeySecp256k1 [32]byte | |||
func (privKey PrivKeySecp256k1) Bytes() []byte { | |||
return cdc.MustMarshalBinaryBare(privKey) | |||
} | |||
func (privKey PrivKeySecp256k1) Sign(msg []byte) (Signature, error) { | |||
priv__, _ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey[:]) | |||
sig__, err := priv__.Sign(Sha256(msg)) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return SignatureSecp256k1(sig__.Serialize()), nil | |||
} | |||
func (privKey PrivKeySecp256k1) PubKey() PubKey { | |||
_, pub__ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey[:]) | |||
var pub PubKeySecp256k1 | |||
copy(pub[:], pub__.SerializeCompressed()) | |||
return pub | |||
} | |||
// Equals - you probably don't need to use this. | |||
// Runs in constant time based on length of the keys. | |||
func (privKey PrivKeySecp256k1) Equals(other PrivKey) bool { | |||
if otherSecp, ok := other.(PrivKeySecp256k1); ok { | |||
return subtle.ConstantTimeCompare(privKey[:], otherSecp[:]) == 1 | |||
} else { | |||
return false | |||
} | |||
} | |||
/* | |||
// Deterministically generates new priv-key bytes from key. | |||
func (key PrivKeySecp256k1) Generate(index int) PrivKeySecp256k1 { | |||
newBytes := cdc.BinarySha256(struct { | |||
PrivKey [64]byte | |||
Index int | |||
}{key, index}) | |||
var newKey [64]byte | |||
copy(newKey[:], newBytes) | |||
return PrivKeySecp256k1(newKey) | |||
} | |||
*/ | |||
func GenPrivKeySecp256k1() PrivKeySecp256k1 { | |||
privKeyBytes := [32]byte{} | |||
copy(privKeyBytes[:], CRandBytes(32)) | |||
priv, _ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKeyBytes[:]) | |||
copy(privKeyBytes[:], priv.Serialize()) | |||
return PrivKeySecp256k1(privKeyBytes) | |||
} | |||
// NOTE: secret should be the output of a KDF like bcrypt, | |||
// if it's derived from user input. | |||
func GenPrivKeySecp256k1FromSecret(secret []byte) PrivKeySecp256k1 { | |||
privKey32 := Sha256(secret) // Not Ripemd160 because we want 32 bytes. | |||
priv, _ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey32) | |||
privKeyBytes := [32]byte{} | |||
copy(privKeyBytes[:], priv.Serialize()) | |||
return PrivKeySecp256k1(privKeyBytes) | |||
} |
@ -1,60 +0,0 @@ | |||
package crypto_test | |||
import ( | |||
"testing" | |||
"github.com/stretchr/testify/assert" | |||
"github.com/tendermint/tendermint/crypto" | |||
) | |||
func TestGeneratePrivKey(t *testing.T) { | |||
testPriv := crypto.GenPrivKeyEd25519() | |||
testGenerate := testPriv.Generate(1) | |||
signBytes := []byte("something to sign") | |||
pub := testGenerate.PubKey() | |||
sig, err := testGenerate.Sign(signBytes) | |||
assert.NoError(t, err) | |||
assert.True(t, pub.VerifyBytes(signBytes, sig)) | |||
} | |||
/* | |||
type BadKey struct { | |||
PrivKeyEd25519 | |||
} | |||
func TestReadPrivKey(t *testing.T) { | |||
assert, require := assert.New(t), require.New(t) | |||
// garbage in, garbage out | |||
garbage := []byte("hjgewugfbiewgofwgewr") | |||
XXX This test wants to register BadKey globally to crypto, | |||
but we don't want to support that. | |||
_, err := PrivKeyFromBytes(garbage) | |||
require.Error(err) | |||
edKey := GenPrivKeyEd25519() | |||
badKey := BadKey{edKey} | |||
cases := []struct { | |||
key PrivKey | |||
valid bool | |||
}{ | |||
{edKey, true}, | |||
{badKey, false}, | |||
} | |||
for i, tc := range cases { | |||
data := tc.key.Bytes() | |||
fmt.Println(">>>", data) | |||
key, err := PrivKeyFromBytes(data) | |||
fmt.Printf("!!! %#v\n", key, err) | |||
if tc.valid { | |||
assert.NoError(err, "%d", i) | |||
assert.Equal(tc.key, key, "%d", i) | |||
} else { | |||
assert.Error(err, "%d: %#v", i, key) | |||
} | |||
} | |||
} | |||
*/ |
@ -1,153 +0,0 @@ | |||
package crypto | |||
import ( | |||
"bytes" | |||
"crypto/sha256" | |||
"fmt" | |||
"golang.org/x/crypto/ripemd160" | |||
secp256k1 "github.com/btcsuite/btcd/btcec" | |||
"github.com/tendermint/ed25519" | |||
"github.com/tendermint/ed25519/extra25519" | |||
cmn "github.com/tendermint/tendermint/libs/common" | |||
"github.com/tendermint/tendermint/crypto/tmhash" | |||
) | |||
// An address is a []byte, but hex-encoded even in JSON. | |||
// []byte leaves us the option to change the address length. | |||
// Use an alias so Unmarshal methods (with ptr receivers) are available too. | |||
type Address = cmn.HexBytes | |||
func PubKeyFromBytes(pubKeyBytes []byte) (pubKey PubKey, err error) { | |||
err = cdc.UnmarshalBinaryBare(pubKeyBytes, &pubKey) | |||
return | |||
} | |||
//---------------------------------------- | |||
type PubKey interface { | |||
Address() Address | |||
Bytes() []byte | |||
VerifyBytes(msg []byte, sig Signature) bool | |||
Equals(PubKey) bool | |||
} | |||
//------------------------------------- | |||
var _ PubKey = PubKeyEd25519{} | |||
const PubKeyEd25519Size = 32 | |||
// Implements PubKeyInner | |||
type PubKeyEd25519 [PubKeyEd25519Size]byte | |||
// Address is the SHA256-20 of the raw pubkey bytes. | |||
func (pubKey PubKeyEd25519) Address() Address { | |||
return Address(tmhash.Sum(pubKey[:])) | |||
} | |||
func (pubKey PubKeyEd25519) Bytes() []byte { | |||
bz, err := cdc.MarshalBinaryBare(pubKey) | |||
if err != nil { | |||
panic(err) | |||
} | |||
return bz | |||
} | |||
func (pubKey PubKeyEd25519) VerifyBytes(msg []byte, sig_ Signature) bool { | |||
// make sure we use the same algorithm to sign | |||
sig, ok := sig_.(SignatureEd25519) | |||
if !ok { | |||
return false | |||
} | |||
pubKeyBytes := [PubKeyEd25519Size]byte(pubKey) | |||
sigBytes := [SignatureEd25519Size]byte(sig) | |||
return ed25519.Verify(&pubKeyBytes, msg, &sigBytes) | |||
} | |||
// For use with golang/crypto/nacl/box | |||
// If error, returns nil. | |||
func (pubKey PubKeyEd25519) ToCurve25519() *[PubKeyEd25519Size]byte { | |||
keyCurve25519, pubKeyBytes := new([PubKeyEd25519Size]byte), [PubKeyEd25519Size]byte(pubKey) | |||
ok := extra25519.PublicKeyToCurve25519(keyCurve25519, &pubKeyBytes) | |||
if !ok { | |||
return nil | |||
} | |||
return keyCurve25519 | |||
} | |||
func (pubKey PubKeyEd25519) String() string { | |||
return fmt.Sprintf("PubKeyEd25519{%X}", pubKey[:]) | |||
} | |||
func (pubKey PubKeyEd25519) Equals(other PubKey) bool { | |||
if otherEd, ok := other.(PubKeyEd25519); ok { | |||
return bytes.Equal(pubKey[:], otherEd[:]) | |||
} else { | |||
return false | |||
} | |||
} | |||
//------------------------------------- | |||
var _ PubKey = PubKeySecp256k1{} | |||
const PubKeySecp256k1Size = 33 | |||
// Implements PubKey. | |||
// Compressed pubkey (just the x-cord), | |||
// prefixed with 0x02 or 0x03, depending on the y-cord. | |||
type PubKeySecp256k1 [PubKeySecp256k1Size]byte | |||
// Implements Bitcoin style addresses: RIPEMD160(SHA256(pubkey)) | |||
func (pubKey PubKeySecp256k1) Address() Address { | |||
hasherSHA256 := sha256.New() | |||
hasherSHA256.Write(pubKey[:]) // does not error | |||
sha := hasherSHA256.Sum(nil) | |||
hasherRIPEMD160 := ripemd160.New() | |||
hasherRIPEMD160.Write(sha) // does not error | |||
return Address(hasherRIPEMD160.Sum(nil)) | |||
} | |||
func (pubKey PubKeySecp256k1) Bytes() []byte { | |||
bz, err := cdc.MarshalBinaryBare(pubKey) | |||
if err != nil { | |||
panic(err) | |||
} | |||
return bz | |||
} | |||
func (pubKey PubKeySecp256k1) VerifyBytes(msg []byte, sig_ Signature) bool { | |||
// and assert same algorithm to sign and verify | |||
sig, ok := sig_.(SignatureSecp256k1) | |||
if !ok { | |||
return false | |||
} | |||
pub__, err := secp256k1.ParsePubKey(pubKey[:], secp256k1.S256()) | |||
if err != nil { | |||
return false | |||
} | |||
sig__, err := secp256k1.ParseDERSignature(sig[:], secp256k1.S256()) | |||
if err != nil { | |||
return false | |||
} | |||
return sig__.Verify(Sha256(msg), pub__) | |||
} | |||
func (pubKey PubKeySecp256k1) String() string { | |||
return fmt.Sprintf("PubKeySecp256k1{%X}", pubKey[:]) | |||
} | |||
func (pubKey PubKeySecp256k1) Equals(other PubKey) bool { | |||
if otherSecp, ok := other.(PubKeySecp256k1); ok { | |||
return bytes.Equal(pubKey[:], otherSecp[:]) | |||
} else { | |||
return false | |||
} | |||
} |
@ -1,50 +0,0 @@ | |||
package crypto | |||
import ( | |||
"encoding/hex" | |||
"testing" | |||
"github.com/btcsuite/btcutil/base58" | |||
"github.com/stretchr/testify/assert" | |||
"github.com/stretchr/testify/require" | |||
) | |||
type keyData struct { | |||
priv string | |||
pub string | |||
addr string | |||
} | |||
var secpDataTable = []keyData{ | |||
{ | |||
priv: "a96e62ed3955e65be32703f12d87b6b5cf26039ecfa948dc5107a495418e5330", | |||
pub: "02950e1cdfcb133d6024109fd489f734eeb4502418e538c28481f22bce276f248c", | |||
addr: "1CKZ9Nx4zgds8tU7nJHotKSDr4a9bYJCa3", | |||
}, | |||
} | |||
func TestPubKeySecp256k1Address(t *testing.T) { | |||
for _, d := range secpDataTable { | |||
privB, _ := hex.DecodeString(d.priv) | |||
pubB, _ := hex.DecodeString(d.pub) | |||
addrBbz, _, _ := base58.CheckDecode(d.addr) | |||
addrB := Address(addrBbz) | |||
var priv PrivKeySecp256k1 | |||
copy(priv[:], privB) | |||
pubKey := priv.PubKey() | |||
pubT, _ := pubKey.(PubKeySecp256k1) | |||
pub := pubT[:] | |||
addr := pubKey.Address() | |||
assert.Equal(t, pub, pubB, "Expected pub keys to match") | |||
assert.Equal(t, addr, addrB, "Expected addresses to match") | |||
} | |||
} | |||
func TestPubKeyInvalidDataProperReturnsEmpty(t *testing.T) { | |||
pk, err := PubKeyFromBytes([]byte("foo")) | |||
require.NotNil(t, err, "expecting a non-nil error") | |||
require.Nil(t, pk, "expecting an empty public key on error") | |||
} |
@ -0,0 +1,198 @@ | |||
package secp256k1 | |||
import ( | |||
"bytes" | |||
"crypto/sha256" | |||
"crypto/subtle" | |||
"fmt" | |||
secp256k1 "github.com/btcsuite/btcd/btcec" | |||
amino "github.com/tendermint/go-amino" | |||
"github.com/tendermint/tendermint/crypto" | |||
"github.com/tendermint/tendermint/libs/common" | |||
"golang.org/x/crypto/ripemd160" | |||
) | |||
//------------------------------------- | |||
const ( | |||
Secp256k1PrivKeyAminoRoute = "tendermint/PrivKeySecp256k1" | |||
Secp256k1PubKeyAminoRoute = "tendermint/PubKeySecp256k1" | |||
Secp256k1SignatureAminoRoute = "tendermint/SignatureSecp256k1" | |||
) | |||
var cdc = amino.NewCodec() | |||
func init() { | |||
cdc.RegisterInterface((*crypto.PubKey)(nil), nil) | |||
cdc.RegisterConcrete(PubKeySecp256k1{}, | |||
Secp256k1PubKeyAminoRoute, nil) | |||
cdc.RegisterInterface((*crypto.PrivKey)(nil), nil) | |||
cdc.RegisterConcrete(PrivKeySecp256k1{}, | |||
Secp256k1PrivKeyAminoRoute, nil) | |||
cdc.RegisterInterface((*crypto.Signature)(nil), nil) | |||
cdc.RegisterConcrete(SignatureSecp256k1{}, | |||
Secp256k1SignatureAminoRoute, nil) | |||
} | |||
//------------------------------------- | |||
var _ crypto.PrivKey = PrivKeySecp256k1{} | |||
// PrivKeySecp256k1 implements PrivKey. | |||
type PrivKeySecp256k1 [32]byte | |||
// Bytes marshalls the private key using amino encoding. | |||
func (privKey PrivKeySecp256k1) Bytes() []byte { | |||
return cdc.MustMarshalBinaryBare(privKey) | |||
} | |||
// Sign creates an ECDSA signature on curve Secp256k1, using SHA256 on the msg. | |||
func (privKey PrivKeySecp256k1) Sign(msg []byte) (crypto.Signature, error) { | |||
priv, _ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey[:]) | |||
sig, err := priv.Sign(crypto.Sha256(msg)) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return SignatureSecp256k1(sig.Serialize()), nil | |||
} | |||
// PubKey performs the point-scalar multiplication from the privKey on the | |||
// generator point to get the pubkey. | |||
func (privKey PrivKeySecp256k1) PubKey() crypto.PubKey { | |||
_, pubkeyObject := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey[:]) | |||
var pubkeyBytes PubKeySecp256k1 | |||
copy(pubkeyBytes[:], pubkeyObject.SerializeCompressed()) | |||
return pubkeyBytes | |||
} | |||
// Equals - you probably don't need to use this. | |||
// Runs in constant time based on length of the keys. | |||
func (privKey PrivKeySecp256k1) Equals(other crypto.PrivKey) bool { | |||
if otherSecp, ok := other.(PrivKeySecp256k1); ok { | |||
return subtle.ConstantTimeCompare(privKey[:], otherSecp[:]) == 1 | |||
} | |||
return false | |||
} | |||
// GenPrivKey generates a new ECDSA private key on curve secp256k1 private key. | |||
// It uses OS randomness in conjunction with the current global random seed | |||
// in tendermint/libs/common to generate the private key. | |||
func GenPrivKey() PrivKeySecp256k1 { | |||
privKeyBytes := [32]byte{} | |||
copy(privKeyBytes[:], crypto.CRandBytes(32)) | |||
// crypto.CRandBytes is guaranteed to be 32 bytes long, so it can be | |||
// casted to PrivKeySecp256k1. | |||
return PrivKeySecp256k1(privKeyBytes) | |||
} | |||
// GenPrivKeySecp256k1 hashes the secret with SHA2, and uses | |||
// that 32 byte output to create the private key. | |||
// NOTE: secret should be the output of a KDF like bcrypt, | |||
// if it's derived from user input. | |||
func GenPrivKeySecp256k1(secret []byte) PrivKeySecp256k1 { | |||
privKey32 := sha256.Sum256(secret) | |||
// sha256.Sum256() is guaranteed to be 32 bytes long, so it can be | |||
// casted to PrivKeySecp256k1. | |||
return PrivKeySecp256k1(privKey32) | |||
} | |||
//------------------------------------- | |||
var _ crypto.PubKey = PubKeySecp256k1{} | |||
// PubKeySecp256k1Size is comprised of 32 bytes for one field element | |||
// (the x-coordinate), plus one byte for the parity of the y-coordinate. | |||
const PubKeySecp256k1Size = 33 | |||
// PubKeySecp256k1 implements crypto.PubKey. | |||
// It is the compressed form of the pubkey. The first byte depends is a 0x02 byte | |||
// if the y-coordinate is the lexicographically largest of the two associated with | |||
// the x-coordinate. Otherwise the first byte is a 0x03. | |||
// This prefix is followed with the x-coordinate. | |||
type PubKeySecp256k1 [PubKeySecp256k1Size]byte | |||
// Address returns a Bitcoin style addresses: RIPEMD160(SHA256(pubkey)) | |||
func (pubKey PubKeySecp256k1) Address() crypto.Address { | |||
hasherSHA256 := sha256.New() | |||
hasherSHA256.Write(pubKey[:]) // does not error | |||
sha := hasherSHA256.Sum(nil) | |||
hasherRIPEMD160 := ripemd160.New() | |||
hasherRIPEMD160.Write(sha) // does not error | |||
return crypto.Address(hasherRIPEMD160.Sum(nil)) | |||
} | |||
// Bytes returns the pubkey marshalled with amino encoding. | |||
func (pubKey PubKeySecp256k1) Bytes() []byte { | |||
bz, err := cdc.MarshalBinaryBare(pubKey) | |||
if err != nil { | |||
panic(err) | |||
} | |||
return bz | |||
} | |||
func (pubKey PubKeySecp256k1) VerifyBytes(msg []byte, interfaceSig crypto.Signature) bool { | |||
// and assert same algorithm to sign and verify | |||
sig, ok := interfaceSig.(SignatureSecp256k1) | |||
if !ok { | |||
return false | |||
} | |||
pub, err := secp256k1.ParsePubKey(pubKey[:], secp256k1.S256()) | |||
if err != nil { | |||
return false | |||
} | |||
parsedSig, err := secp256k1.ParseDERSignature(sig[:], secp256k1.S256()) | |||
if err != nil { | |||
return false | |||
} | |||
return parsedSig.Verify(crypto.Sha256(msg), pub) | |||
} | |||
func (pubKey PubKeySecp256k1) String() string { | |||
return fmt.Sprintf("PubKeySecp256k1{%X}", pubKey[:]) | |||
} | |||
func (pubKey PubKeySecp256k1) Equals(other crypto.PubKey) bool { | |||
if otherSecp, ok := other.(PubKeySecp256k1); ok { | |||
return bytes.Equal(pubKey[:], otherSecp[:]) | |||
} | |||
return false | |||
} | |||
//------------------------------------- | |||
var _ crypto.Signature = SignatureSecp256k1{} | |||
// SignatureSecp256k1 implements crypto.Signature | |||
type SignatureSecp256k1 []byte | |||
func (sig SignatureSecp256k1) Bytes() []byte { | |||
bz, err := cdc.MarshalBinaryBare(sig) | |||
if err != nil { | |||
panic(err) | |||
} | |||
return bz | |||
} | |||
func (sig SignatureSecp256k1) IsZero() bool { return len(sig) == 0 } | |||
func (sig SignatureSecp256k1) String() string { | |||
return fmt.Sprintf("/%X.../", common.Fingerprint(sig[:])) | |||
} | |||
func (sig SignatureSecp256k1) Equals(other crypto.Signature) bool { | |||
if otherSecp, ok := other.(SignatureSecp256k1); ok { | |||
return subtle.ConstantTimeCompare(sig[:], otherSecp[:]) == 1 | |||
} else { | |||
return false | |||
} | |||
} | |||
func SignatureSecp256k1FromBytes(data []byte) crypto.Signature { | |||
sig := make(SignatureSecp256k1, len(data)) | |||
copy(sig[:], data) | |||
return sig | |||
} |
@ -0,0 +1,88 @@ | |||
package secp256k1_test | |||
import ( | |||
"encoding/hex" | |||
"testing" | |||
"github.com/btcsuite/btcutil/base58" | |||
"github.com/stretchr/testify/assert" | |||
"github.com/stretchr/testify/require" | |||
"github.com/tendermint/tendermint/crypto" | |||
"github.com/tendermint/tendermint/crypto/secp256k1" | |||
underlyingSecp256k1 "github.com/btcsuite/btcd/btcec" | |||
) | |||
type keyData struct { | |||
priv string | |||
pub string | |||
addr string | |||
} | |||
var secpDataTable = []keyData{ | |||
{ | |||
priv: "a96e62ed3955e65be32703f12d87b6b5cf26039ecfa948dc5107a495418e5330", | |||
pub: "02950e1cdfcb133d6024109fd489f734eeb4502418e538c28481f22bce276f248c", | |||
addr: "1CKZ9Nx4zgds8tU7nJHotKSDr4a9bYJCa3", | |||
}, | |||
} | |||
func TestPubKeySecp256k1Address(t *testing.T) { | |||
for _, d := range secpDataTable { | |||
privB, _ := hex.DecodeString(d.priv) | |||
pubB, _ := hex.DecodeString(d.pub) | |||
addrBbz, _, _ := base58.CheckDecode(d.addr) | |||
addrB := crypto.Address(addrBbz) | |||
var priv secp256k1.PrivKeySecp256k1 | |||
copy(priv[:], privB) | |||
pubKey := priv.PubKey() | |||
pubT, _ := pubKey.(secp256k1.PubKeySecp256k1) | |||
pub := pubT[:] | |||
addr := pubKey.Address() | |||
assert.Equal(t, pub, pubB, "Expected pub keys to match") | |||
assert.Equal(t, addr, addrB, "Expected addresses to match") | |||
} | |||
} | |||
func TestSignAndValidateSecp256k1(t *testing.T) { | |||
privKey := secp256k1.GenPrivKey() | |||
pubKey := privKey.PubKey() | |||
msg := crypto.CRandBytes(128) | |||
sig, err := privKey.Sign(msg) | |||
require.Nil(t, err) | |||
assert.True(t, pubKey.VerifyBytes(msg, sig)) | |||
// Mutate the signature, just one bit. | |||
sigEd := sig.(secp256k1.SignatureSecp256k1) | |||
sigEd[3] ^= byte(0x01) | |||
sig = sigEd | |||
assert.False(t, pubKey.VerifyBytes(msg, sig)) | |||
} | |||
// This test is intended to justify the removal of calls to the underlying library | |||
// in creating the privkey. | |||
func TestSecp256k1LoadPrivkeyAndSerializeIsIdentity(t *testing.T) { | |||
numberOfTests := 256 | |||
for i := 0; i < numberOfTests; i++ { | |||
// Seed the test case with some random bytes | |||
privKeyBytes := [32]byte{} | |||
copy(privKeyBytes[:], crypto.CRandBytes(32)) | |||
// This function creates a private and public key in the underlying libraries format. | |||
// The private key is basically calling new(big.Int).SetBytes(pk), which removes leading zero bytes | |||
priv, _ := underlyingSecp256k1.PrivKeyFromBytes(underlyingSecp256k1.S256(), privKeyBytes[:]) | |||
// this takes the bytes returned by `(big int).Bytes()`, and if the length is less than 32 bytes, | |||
// pads the bytes from the left with zero bytes. Therefore these two functions composed | |||
// result in the identity function on privKeyBytes, hence the following equality check | |||
// always returning true. | |||
serializedBytes := priv.Serialize() | |||
require.Equal(t, privKeyBytes[:], serializedBytes) | |||
} | |||
} |
@ -1,90 +0,0 @@ | |||
package crypto | |||
import ( | |||
"fmt" | |||
"crypto/subtle" | |||
. "github.com/tendermint/tendermint/libs/common" | |||
) | |||
func SignatureFromBytes(pubKeyBytes []byte) (pubKey Signature, err error) { | |||
err = cdc.UnmarshalBinaryBare(pubKeyBytes, &pubKey) | |||
return | |||
} | |||
//---------------------------------------- | |||
type Signature interface { | |||
Bytes() []byte | |||
IsZero() bool | |||
Equals(Signature) bool | |||
} | |||
//------------------------------------- | |||
var _ Signature = SignatureEd25519{} | |||
const SignatureEd25519Size = 64 | |||
// Implements Signature | |||
type SignatureEd25519 [SignatureEd25519Size]byte | |||
func (sig SignatureEd25519) Bytes() []byte { | |||
bz, err := cdc.MarshalBinaryBare(sig) | |||
if err != nil { | |||
panic(err) | |||
} | |||
return bz | |||
} | |||
func (sig SignatureEd25519) IsZero() bool { return len(sig) == 0 } | |||
func (sig SignatureEd25519) String() string { return fmt.Sprintf("/%X.../", Fingerprint(sig[:])) } | |||
func (sig SignatureEd25519) Equals(other Signature) bool { | |||
if otherEd, ok := other.(SignatureEd25519); ok { | |||
return subtle.ConstantTimeCompare(sig[:], otherEd[:]) == 1 | |||
} else { | |||
return false | |||
} | |||
} | |||
func SignatureEd25519FromBytes(data []byte) Signature { | |||
var sig SignatureEd25519 | |||
copy(sig[:], data) | |||
return sig | |||
} | |||
//------------------------------------- | |||
var _ Signature = SignatureSecp256k1{} | |||
// Implements Signature | |||
type SignatureSecp256k1 []byte | |||
func (sig SignatureSecp256k1) Bytes() []byte { | |||
bz, err := cdc.MarshalBinaryBare(sig) | |||
if err != nil { | |||
panic(err) | |||
} | |||
return bz | |||
} | |||
func (sig SignatureSecp256k1) IsZero() bool { return len(sig) == 0 } | |||
func (sig SignatureSecp256k1) String() string { return fmt.Sprintf("/%X.../", Fingerprint(sig[:])) } | |||
func (sig SignatureSecp256k1) Equals(other Signature) bool { | |||
if otherSecp, ok := other.(SignatureSecp256k1); ok { | |||
return subtle.ConstantTimeCompare(sig[:], otherSecp[:]) == 1 | |||
} else { | |||
return false | |||
} | |||
} | |||
func SignatureSecp256k1FromBytes(data []byte) Signature { | |||
sig := make(SignatureSecp256k1, len(data)) | |||
copy(sig[:], data) | |||
return sig | |||
} |
@ -1,46 +0,0 @@ | |||
package crypto | |||
import ( | |||
"testing" | |||
"github.com/stretchr/testify/assert" | |||
"github.com/stretchr/testify/require" | |||
) | |||
func TestSignAndValidateEd25519(t *testing.T) { | |||
privKey := GenPrivKeyEd25519() | |||
pubKey := privKey.PubKey() | |||
msg := CRandBytes(128) | |||
sig, err := privKey.Sign(msg) | |||
require.Nil(t, err) | |||
// Test the signature | |||
assert.True(t, pubKey.VerifyBytes(msg, sig)) | |||
// Mutate the signature, just one bit. | |||
sigEd := sig.(SignatureEd25519) | |||
sigEd[7] ^= byte(0x01) | |||
sig = sigEd | |||
assert.False(t, pubKey.VerifyBytes(msg, sig)) | |||
} | |||
func TestSignAndValidateSecp256k1(t *testing.T) { | |||
privKey := GenPrivKeySecp256k1() | |||
pubKey := privKey.PubKey() | |||
msg := CRandBytes(128) | |||
sig, err := privKey.Sign(msg) | |||
require.Nil(t, err) | |||
assert.True(t, pubKey.VerifyBytes(msg, sig)) | |||
// Mutate the signature, just one bit. | |||
sigEd := sig.(SignatureSecp256k1) | |||
sigEd[3] ^= byte(0x01) | |||
sig = sigEd | |||
assert.False(t, pubKey.VerifyBytes(msg, sig)) | |||
} |
@ -0,0 +1,21 @@ | |||
# Documentation Maintenance Overview | |||
The documentation found in this directory is hosted at: | |||
- https://tendermint.com/docs/ | |||
and built using [VuePress](https://vuepress.vuejs.org/) from the tendermint website repo: | |||
- https://github.com/tendermint/tendermint.com | |||
which has a [configuration file](https://github.com/tendermint/tendermint.com/blob/develop/docs/.vuepress/config.js) for displaying | |||
the Table of Contents that lists all the documentation. | |||
Under the hood, Jenkins listens for changes in ./docs then pushes a `docs-staging` branch to the tendermint.com repo with the latest documentation. That branch must be manually PR'd to `develop` then `master` for staging then production. This process should happen in synchrony with a release. | |||
The `README.md` in this directory is the landing page for | |||
website documentation and the following folders are intentionally | |||
ommitted: | |||
- `architecture/` ==> contains Architecture Design Records | |||
- `spec/` ==> contains the detailed specification |
@ -1,324 +0,0 @@ | |||
# ABCI Specification | |||
## Message Types | |||
ABCI requests/responses are defined as simple Protobuf messages in [this | |||
schema file](https://github.com/tendermint/tendermint/blob/develop/abci/types/types.proto). | |||
TendermintCore sends the requests, and the ABCI application sends the | |||
responses. Here, we provide an overview of the messages types and how | |||
they are used by Tendermint. Then we describe each request-response pair | |||
as a function with arguments and return values, and add some notes on | |||
usage. | |||
Some messages (`Echo, Info, InitChain, BeginBlock, EndBlock, Commit`), | |||
don't return errors because an error would indicate a critical failure | |||
in the application and there's nothing Tendermint can do. The problem | |||
should be addressed and both Tendermint and the application restarted. | |||
All other messages (`SetOption, Query, CheckTx, DeliverTx`) return an | |||
application-specific response `Code uint32`, where only `0` is reserved | |||
for `OK`. | |||
Some messages (`SetOption, Query, CheckTx, DeliverTx`) return | |||
non-deterministic data in the form of `Info` and `Log`. The `Log` is | |||
intended for the literal output from the application's logger, while the | |||
`Info` is any additional info that should be returned. | |||
The first time a new blockchain is started, Tendermint calls | |||
`InitChain`. From then on, the Block Execution Sequence that causes the | |||
committed state to be updated is as follows: | |||
`BeginBlock, [DeliverTx], EndBlock, Commit` | |||
where one `DeliverTx` is called for each transaction in the block. | |||
Cryptographic commitments to the results of DeliverTx, EndBlock, and | |||
Commit are included in the header of the next block. | |||
Tendermint opens three connections to the application to handle the | |||
different message types: | |||
- `Consensus Connection - InitChain, BeginBlock, DeliverTx, EndBlock, Commit` | |||
- `Mempool Connection - CheckTx` | |||
- `Info Connection - Info, SetOption, Query` | |||
The `Flush` message is used on every connection, and the `Echo` message | |||
is only used for debugging. | |||
Note that messages may be sent concurrently across all connections -a | |||
typical application will thus maintain a distinct state for each | |||
connection. They may be referred to as the `DeliverTx state`, the | |||
`CheckTx state`, and the `Commit state` respectively. | |||
See below for more details on the message types and how they are used. | |||
## Request/Response Messages | |||
### Echo | |||
- **Request**: | |||
- `Message (string)`: A string to echo back | |||
- **Response**: | |||
- `Message (string)`: The input string | |||
- **Usage**: | |||
- Echo a string to test an abci client/server implementation | |||
### Flush | |||
- **Usage**: | |||
- Signals that messages queued on the client should be flushed to | |||
the server. It is called periodically by the client | |||
implementation to ensure asynchronous requests are actually | |||
sent, and is called immediately to make a synchronous request, | |||
which returns when the Flush response comes back. | |||
### Info | |||
- **Request**: | |||
- `Version (string)`: The Tendermint version | |||
- **Response**: | |||
- `Data (string)`: Some arbitrary information | |||
- `Version (Version)`: Version information | |||
- `LastBlockHeight (int64)`: Latest block for which the app has | |||
called Commit | |||
- `LastBlockAppHash ([]byte)`: Latest result of Commit | |||
- **Usage**: | |||
- Return information about the application state. | |||
- Used to sync Tendermint with the application during a handshake | |||
that happens on startup. | |||
- Tendermint expects `LastBlockAppHash` and `LastBlockHeight` to | |||
be updated during `Commit`, ensuring that `Commit` is never | |||
called twice for the same block height. | |||
### SetOption | |||
- **Request**: | |||
- `Key (string)`: Key to set | |||
- `Value (string)`: Value to set for key | |||
- **Response**: | |||
- `Code (uint32)`: Response code | |||
- `Log (string)`: The output of the application's logger. May | |||
be non-deterministic. | |||
- `Info (string)`: Additional information. May | |||
be non-deterministic. | |||
- **Usage**: | |||
- Set non-consensus critical application specific options. | |||
- e.g. Key="min-fee", Value="100fermion" could set the minimum fee | |||
required for CheckTx (but not DeliverTx - that would be | |||
consensus critical). | |||
### InitChain | |||
- **Request**: | |||
- `Validators ([]Validator)`: Initial genesis validators | |||
- `AppStateBytes ([]byte)`: Serialized initial application state | |||
- **Response**: | |||
- `ConsensusParams (ConsensusParams)`: Initial | |||
consensus-critical parameters. | |||
- `Validators ([]Validator)`: Initial validator set. | |||
- **Usage**: | |||
- Called once upon genesis. | |||
### Query | |||
- **Request**: | |||
- `Data ([]byte)`: Raw query bytes. Can be used with or in lieu | |||
of Path. | |||
- `Path (string)`: Path of request, like an HTTP GET path. Can be | |||
used with or in liue of Data. | |||
- Apps MUST interpret '/store' as a query by key on the | |||
underlying store. The key SHOULD be specified in the Data field. | |||
- Apps SHOULD allow queries over specific types like | |||
'/accounts/...' or '/votes/...' | |||
- `Height (int64)`: The block height for which you want the query | |||
(default=0 returns data for the latest committed block). Note | |||
that this is the height of the block containing the | |||
application's Merkle root hash, which represents the state as it | |||
was after committing the block at Height-1 | |||
- `Prove (bool)`: Return Merkle proof with response if possible | |||
- **Response**: | |||
- `Code (uint32)`: Response code. | |||
- `Log (string)`: The output of the application's logger. May | |||
be non-deterministic. | |||
- `Info (string)`: Additional information. May | |||
be non-deterministic. | |||
- `Index (int64)`: The index of the key in the tree. | |||
- `Key ([]byte)`: The key of the matching data. | |||
- `Value ([]byte)`: The value of the matching data. | |||
- `Proof ([]byte)`: Proof for the data, if requested. | |||
- `Height (int64)`: The block height from which data was derived. | |||
Note that this is the height of the block containing the | |||
application's Merkle root hash, which represents the state as it | |||
was after committing the block at Height-1 | |||
- **Usage**: | |||
- Query for data from the application at current or past height. | |||
- Optionally return Merkle proof. | |||
### BeginBlock | |||
- **Request**: | |||
- `Hash ([]byte)`: The block's hash. This can be derived from the | |||
block header. | |||
- `Header (struct{})`: The block header | |||
- `Validators ([]SigningValidator)`: List of validators in the current validator | |||
set and whether or not they signed a vote in the LastCommit | |||
- `ByzantineValidators ([]Evidence)`: List of evidence of | |||
validators that acted maliciously | |||
- **Response**: | |||
- `Tags ([]cmn.KVPair)`: Key-Value tags for filtering and indexing | |||
- **Usage**: | |||
- Signals the beginning of a new block. Called prior to | |||
any DeliverTxs. | |||
- The header is expected to at least contain the Height. | |||
- The `Validators` and `ByzantineValidators` can be used to | |||
determine rewards and punishments for the validators. | |||
### CheckTx | |||
- **Request**: | |||
- `Tx ([]byte)`: The request transaction bytes | |||
- **Response**: | |||
- `Code (uint32)`: Response code | |||
- `Data ([]byte)`: Result bytes, if any. | |||
- `Log (string)`: The output of the application's logger. May | |||
be non-deterministic. | |||
- `Info (string)`: Additional information. May | |||
be non-deterministic. | |||
- `GasWanted (int64)`: Amount of gas request for transaction. | |||
- `GasUsed (int64)`: Amount of gas consumed by transaction. | |||
- `Tags ([]cmn.KVPair)`: Key-Value tags for filtering and indexing | |||
transactions (eg. by account). | |||
- `Fee (cmn.KI64Pair)`: Fee paid for the transaction. | |||
- **Usage**: Validate a mempool transaction, prior to broadcasting | |||
or proposing. CheckTx should perform stateful but light-weight | |||
checks of the validity of the transaction (like checking signatures | |||
and account balances), but need not execute in full (like running a | |||
smart contract). | |||
Tendermint runs CheckTx and DeliverTx concurrently with eachother, | |||
though on distinct ABCI connections - the mempool connection and the | |||
consensus connection, respectively. | |||
The application should maintain a separate state to support CheckTx. | |||
This state can be reset to the latest committed state during | |||
`Commit`, where Tendermint ensures the mempool is locked and not | |||
sending new `CheckTx`. After `Commit`, the mempool will rerun | |||
CheckTx on all remaining transactions, throwing out any that are no | |||
longer valid. | |||
Keys and values in Tags must be UTF-8 encoded strings (e.g. | |||
"account.owner": "Bob", "balance": "100.0", "date": "2018-01-02") | |||
### DeliverTx | |||
- **Request**: | |||
- `Tx ([]byte)`: The request transaction bytes. | |||
- **Response**: | |||
- `Code (uint32)`: Response code. | |||
- `Data ([]byte)`: Result bytes, if any. | |||
- `Log (string)`: The output of the application's logger. May | |||
be non-deterministic. | |||
- `Info (string)`: Additional information. May | |||
be non-deterministic. | |||
- `GasWanted (int64)`: Amount of gas requested for transaction. | |||
- `GasUsed (int64)`: Amount of gas consumed by transaction. | |||
- `Tags ([]cmn.KVPair)`: Key-Value tags for filtering and indexing | |||
transactions (eg. by account). | |||
- `Fee (cmn.KI64Pair)`: Fee paid for the transaction. | |||
- **Usage**: | |||
- Deliver a transaction to be executed in full by the application. | |||
If the transaction is valid, returns CodeType.OK. | |||
- Keys and values in Tags must be UTF-8 encoded strings (e.g. | |||
"account.owner": "Bob", "balance": "100.0", | |||
"time": "2018-01-02T12:30:00Z") | |||
### EndBlock | |||
- **Request**: | |||
- `Height (int64)`: Height of the block just executed. | |||
- **Response**: | |||
- `ValidatorUpdates ([]Validator)`: Changes to validator set (set | |||
voting power to 0 to remove). | |||
- `ConsensusParamUpdates (ConsensusParams)`: Changes to | |||
consensus-critical time, size, and other parameters. | |||
- `Tags ([]cmn.KVPair)`: Key-Value tags for filtering and indexing | |||
- **Usage**: | |||
- Signals the end of a block. | |||
- Called prior to each Commit, after all transactions. | |||
- Validator set and consensus params are updated with the result. | |||
- Validator pubkeys are expected to be go-wire encoded. | |||
### Commit | |||
- **Response**: | |||
- `Data ([]byte)`: The Merkle root hash | |||
- **Usage**: | |||
- Persist the application state. | |||
- Return a Merkle root hash of the application state. | |||
- It's critical that all application instances return the | |||
same hash. If not, they will not be able to agree on the next | |||
block, because the hash is included in the next block! | |||
## Data Messages | |||
### Header | |||
- **Fields**: | |||
- `ChainID (string)`: ID of the blockchain | |||
- `Height (int64)`: Height of the block in the chain | |||
- `Time (int64)`: Unix time of the block | |||
- `NumTxs (int32)`: Number of transactions in the block | |||
- `TotalTxs (int64)`: Total number of transactions in the blockchain until | |||
now | |||
- `LastBlockHash ([]byte)`: Hash of the previous (parent) block | |||
- `ValidatorsHash ([]byte)`: Hash of the validator set for this block | |||
- `AppHash ([]byte)`: Data returned by the last call to `Commit` - typically the | |||
Merkle root of the application state after executing the previous block's | |||
transactions | |||
- `Proposer (Validator)`: Original proposer for the block | |||
- **Usage**: | |||
- Provided in RequestBeginBlock | |||
- Provides important context about the current state of the blockchain - | |||
especially height and time. | |||
- Provides the proposer of the current block, for use in proposer-based | |||
reward mechanisms. | |||
### Validator | |||
- **Fields**: | |||
- `Address ([]byte)`: Address of the validator (hash of the public key) | |||
- `PubKey (PubKey)`: Public key of the validator | |||
- `Power (int64)`: Voting power of the validator | |||
- **Usage**: | |||
- Provides all identifying information about the validator | |||
### SigningValidator | |||
- **Fields**: | |||
- `Validator (Validator)`: A validator | |||
- `SignedLastBlock (bool)`: Indicated whether or not the validator signed | |||
the last block | |||
- **Usage**: | |||
- Indicates whether a validator signed the last block, allowing for rewards | |||
based on validator availability | |||
### PubKey | |||
- **Fields**: | |||
- `Type (string)`: Type of the public key. A simple string like `"ed25519"`. | |||
In the future, may indicate a serialization algorithm to parse the `Data`, | |||
for instance `"amino"`. | |||
- `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 | |||
encoded public key. | |||
- **Usage**: | |||
- A generic and extensible typed public key | |||
### Evidence | |||
- **Fields**: | |||
- `Type (string)`: Type of the evidence. A hierarchical path like | |||
"duplicate/vote". | |||
- `Validator (Validator`: The offending validator | |||
- `Height (int64)`: Height when the offense was committed | |||
- `Time (int64)`: Unix time of the block at height `Height` | |||
- `TotalVotingPower (int64)`: Total voting power of the validator set at | |||
height `Height` |
@ -0,0 +1,325 @@ | |||
# ABCI Specification | |||
## Message Types | |||
ABCI requests/responses are defined as simple Protobuf messages in [this | |||
schema file](https://github.com/tendermint/tendermint/blob/master/abci/types/types.proto). | |||
TendermintCore sends the requests, and the ABCI application sends the | |||
responses. Here, we provide an overview of the messages types and how | |||
they are used by Tendermint. Then we describe each request-response pair | |||
as a function with arguments and return values, and add some notes on | |||
usage. | |||
Some messages (`Echo, Info, InitChain, BeginBlock, EndBlock, Commit`), | |||
don't return errors because an error would indicate a critical failure | |||
in the application and there's nothing Tendermint can do. The problem | |||
should be addressed and both Tendermint and the application restarted. | |||
All other messages (`SetOption, Query, CheckTx, DeliverTx`) return an | |||
application-specific response `Code uint32`, where only `0` is reserved | |||
for `OK`. | |||
Some messages (`SetOption, Query, CheckTx, DeliverTx`) return | |||
non-deterministic data in the form of `Info` and `Log`. The `Log` is | |||
intended for the literal output from the application's logger, while the | |||
`Info` is any additional info that should be returned. | |||
The first time a new blockchain is started, Tendermint calls | |||
`InitChain`. From then on, the Block Execution Sequence that causes the | |||
committed state to be updated is as follows: | |||
`BeginBlock, [DeliverTx], EndBlock, Commit` | |||
where one `DeliverTx` is called for each transaction in the block. | |||
Cryptographic commitments to the results of DeliverTx, EndBlock, and | |||
Commit are included in the header of the next block. | |||
Tendermint opens three connections to the application to handle the | |||
different message types: | |||
- `Consensus Connection - InitChain, BeginBlock, DeliverTx, EndBlock, Commit` | |||
- `Mempool Connection - CheckTx` | |||
- `Info Connection - Info, SetOption, Query` | |||
The `Flush` message is used on every connection, and the `Echo` message | |||
is only used for debugging. | |||
Note that messages may be sent concurrently across all connections -a | |||
typical application will thus maintain a distinct state for each | |||
connection. They may be referred to as the `DeliverTx state`, the | |||
`CheckTx state`, and the `Commit state` respectively. | |||
See below for more details on the message types and how they are used. | |||
## Request/Response Messages | |||
### Echo | |||
- **Request**: | |||
- `Message (string)`: A string to echo back | |||
- **Response**: | |||
- `Message (string)`: The input string | |||
- **Usage**: | |||
- Echo a string to test an abci client/server implementation | |||
### Flush | |||
- **Usage**: | |||
- Signals that messages queued on the client should be flushed to | |||
the server. It is called periodically by the client | |||
implementation to ensure asynchronous requests are actually | |||
sent, and is called immediately to make a synchronous request, | |||
which returns when the Flush response comes back. | |||
### Info | |||
- **Request**: | |||
- `Version (string)`: The Tendermint version | |||
- **Response**: | |||
- `Data (string)`: Some arbitrary information | |||
- `Version (Version)`: Version information | |||
- `LastBlockHeight (int64)`: Latest block for which the app has | |||
called Commit | |||
- `LastBlockAppHash ([]byte)`: Latest result of Commit | |||
- **Usage**: | |||
- Return information about the application state. | |||
- Used to sync Tendermint with the application during a handshake | |||
that happens on startup. | |||
- Tendermint expects `LastBlockAppHash` and `LastBlockHeight` to | |||
be updated during `Commit`, ensuring that `Commit` is never | |||
called twice for the same block height. | |||
### SetOption | |||
- **Request**: | |||
- `Key (string)`: Key to set | |||
- `Value (string)`: Value to set for key | |||
- **Response**: | |||
- `Code (uint32)`: Response code | |||
- `Log (string)`: The output of the application's logger. May | |||
be non-deterministic. | |||
- `Info (string)`: Additional information. May | |||
be non-deterministic. | |||
- **Usage**: | |||
- Set non-consensus critical application specific options. | |||
- e.g. Key="min-fee", Value="100fermion" could set the minimum fee | |||
required for CheckTx (but not DeliverTx - that would be | |||
consensus critical). | |||
### InitChain | |||
- **Request**: | |||
- `Validators ([]Validator)`: Initial genesis validators | |||
- `AppStateBytes ([]byte)`: Serialized initial application state | |||
- **Response**: | |||
- `ConsensusParams (ConsensusParams)`: Initial | |||
consensus-critical parameters. | |||
- `Validators ([]Validator)`: Initial validator set. | |||
- **Usage**: | |||
- Called once upon genesis. | |||
### Query | |||
- **Request**: | |||
- `Data ([]byte)`: Raw query bytes. Can be used with or in lieu | |||
of Path. | |||
- `Path (string)`: Path of request, like an HTTP GET path. Can be | |||
used with or in liue of Data. | |||
- Apps MUST interpret '/store' as a query by key on the | |||
underlying store. The key SHOULD be specified in the Data field. | |||
- Apps SHOULD allow queries over specific types like | |||
'/accounts/...' or '/votes/...' | |||
- `Height (int64)`: The block height for which you want the query | |||
(default=0 returns data for the latest committed block). Note | |||
that this is the height of the block containing the | |||
application's Merkle root hash, which represents the state as it | |||
was after committing the block at Height-1 | |||
- `Prove (bool)`: Return Merkle proof with response if possible | |||
- **Response**: | |||
- `Code (uint32)`: Response code. | |||
- `Log (string)`: The output of the application's logger. May | |||
be non-deterministic. | |||
- `Info (string)`: Additional information. May | |||
be non-deterministic. | |||
- `Index (int64)`: The index of the key in the tree. | |||
- `Key ([]byte)`: The key of the matching data. | |||
- `Value ([]byte)`: The value of the matching data. | |||
- `Proof ([]byte)`: Proof for the data, if requested. | |||
- `Height (int64)`: The block height from which data was derived. | |||
Note that this is the height of the block containing the | |||
application's Merkle root hash, which represents the state as it | |||
was after committing the block at Height-1 | |||
- **Usage**: | |||
- Query for data from the application at current or past height. | |||
- Optionally return Merkle proof. | |||
### BeginBlock | |||
- **Request**: | |||
- `Hash ([]byte)`: The block's hash. This can be derived from the | |||
block header. | |||
- `Header (struct{})`: The block header | |||
- `Validators ([]SigningValidator)`: List of validators in the current validator | |||
set and whether or not they signed a vote in the LastCommit | |||
- `ByzantineValidators ([]Evidence)`: List of evidence of | |||
validators that acted maliciously | |||
- **Response**: | |||
- `Tags ([]cmn.KVPair)`: Key-Value tags for filtering and indexing | |||
- **Usage**: | |||
- Signals the beginning of a new block. Called prior to | |||
any DeliverTxs. | |||
- The header is expected to at least contain the Height. | |||
- The `Validators` and `ByzantineValidators` can be used to | |||
determine rewards and punishments for the validators. | |||
### CheckTx | |||
- **Request**: | |||
- `Tx ([]byte)`: The request transaction bytes | |||
- **Response**: | |||
- `Code (uint32)`: Response code | |||
- `Data ([]byte)`: Result bytes, if any. | |||
- `Log (string)`: The output of the application's logger. May | |||
be non-deterministic. | |||
- `Info (string)`: Additional information. May | |||
be non-deterministic. | |||
- `GasWanted (int64)`: Amount of gas request for transaction. | |||
- `GasUsed (int64)`: Amount of gas consumed by transaction. | |||
- `Tags ([]cmn.KVPair)`: Key-Value tags for filtering and indexing | |||
transactions (eg. by account). | |||
- `Fee (cmn.KI64Pair)`: Fee paid for the transaction. | |||
- **Usage**: Validate a mempool transaction, prior to broadcasting | |||
or proposing. CheckTx should perform stateful but light-weight | |||
checks of the validity of the transaction (like checking signatures | |||
and account balances), but need not execute in full (like running a | |||
smart contract). | |||
Tendermint runs CheckTx and DeliverTx concurrently with eachother, | |||
though on distinct ABCI connections - the mempool connection and the | |||
consensus connection, respectively. | |||
The application should maintain a separate state to support CheckTx. | |||
This state can be reset to the latest committed state during | |||
`Commit`. Before calling Commit, Tendermint will lock and flush the mempool, | |||
ensuring that all existing CheckTx are responded to and no new ones can | |||
begin. After `Commit`, the mempool will rerun | |||
CheckTx for all remaining transactions, throwing out any that are no longer valid. | |||
Then the mempool will unlock and start sending CheckTx again. | |||
Keys and values in Tags must be UTF-8 encoded strings (e.g. | |||
"account.owner": "Bob", "balance": "100.0", "date": "2018-01-02") | |||
### DeliverTx | |||
- **Request**: | |||
- `Tx ([]byte)`: The request transaction bytes. | |||
- **Response**: | |||
- `Code (uint32)`: Response code. | |||
- `Data ([]byte)`: Result bytes, if any. | |||
- `Log (string)`: The output of the application's logger. May | |||
be non-deterministic. | |||
- `Info (string)`: Additional information. May | |||
be non-deterministic. | |||
- `GasWanted (int64)`: Amount of gas requested for transaction. | |||
- `GasUsed (int64)`: Amount of gas consumed by transaction. | |||
- `Tags ([]cmn.KVPair)`: Key-Value tags for filtering and indexing | |||
transactions (eg. by account). | |||
- `Fee (cmn.KI64Pair)`: Fee paid for the transaction. | |||
- **Usage**: | |||
- Deliver a transaction to be executed in full by the application. | |||
If the transaction is valid, returns CodeType.OK. | |||
- Keys and values in Tags must be UTF-8 encoded strings (e.g. | |||
"account.owner": "Bob", "balance": "100.0", | |||
"time": "2018-01-02T12:30:00Z") | |||
### EndBlock | |||
- **Request**: | |||
- `Height (int64)`: Height of the block just executed. | |||
- **Response**: | |||
- `ValidatorUpdates ([]Validator)`: Changes to validator set (set | |||
voting power to 0 to remove). | |||
- `ConsensusParamUpdates (ConsensusParams)`: Changes to | |||
consensus-critical time, size, and other parameters. | |||
- `Tags ([]cmn.KVPair)`: Key-Value tags for filtering and indexing | |||
- **Usage**: | |||
- Signals the end of a block. | |||
- Called prior to each Commit, after all transactions. | |||
- Validator set and consensus params are updated with the result. | |||
- Validator pubkeys are expected to be go-wire encoded. | |||
### Commit | |||
- **Response**: | |||
- `Data ([]byte)`: The Merkle root hash | |||
- **Usage**: | |||
- Persist the application state. | |||
- Return a Merkle root hash of the application state. | |||
- It's critical that all application instances return the | |||
same hash. If not, they will not be able to agree on the next | |||
block, because the hash is included in the next block! | |||
## Data Messages | |||
### Header | |||
- **Fields**: | |||
- `ChainID (string)`: ID of the blockchain | |||
- `Height (int64)`: Height of the block in the chain | |||
- `Time (int64)`: Unix time of the block | |||
- `NumTxs (int32)`: Number of transactions in the block | |||
- `TotalTxs (int64)`: Total number of transactions in the blockchain until | |||
now | |||
- `LastBlockHash ([]byte)`: Hash of the previous (parent) block | |||
- `ValidatorsHash ([]byte)`: Hash of the validator set for this block | |||
- `AppHash ([]byte)`: Data returned by the last call to `Commit` - typically the | |||
Merkle root of the application state after executing the previous block's | |||
transactions | |||
- `Proposer (Validator)`: Original proposer for the block | |||
- **Usage**: | |||
- Provided in RequestBeginBlock | |||
- Provides important context about the current state of the blockchain - | |||
especially height and time. | |||
- Provides the proposer of the current block, for use in proposer-based | |||
reward mechanisms. | |||
### Validator | |||
- **Fields**: | |||
- `Address ([]byte)`: Address of the validator (hash of the public key) | |||
- `PubKey (PubKey)`: Public key of the validator | |||
- `Power (int64)`: Voting power of the validator | |||
- **Usage**: | |||
- Provides all identifying information about the validator | |||
### SigningValidator | |||
- **Fields**: | |||
- `Validator (Validator)`: A validator | |||
- `SignedLastBlock (bool)`: Indicated whether or not the validator signed | |||
the last block | |||
- **Usage**: | |||
- Indicates whether a validator signed the last block, allowing for rewards | |||
based on validator availability | |||
### PubKey | |||
- **Fields**: | |||
- `Type (string)`: Type of the public key. A simple string like `"ed25519"`. | |||
In the future, may indicate a serialization algorithm to parse the `Data`, | |||
for instance `"amino"`. | |||
- `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 | |||
encoded public key. | |||
- **Usage**: | |||
- A generic and extensible typed public key | |||
### Evidence | |||
- **Fields**: | |||
- `Type (string)`: Type of the evidence. A hierarchical path like | |||
"duplicate/vote". | |||
- `Validator (Validator`: The offending validator | |||
- `Height (int64)`: Height when the offense was committed | |||
- `Time (int64)`: Unix time of the block at height `Height` | |||
- `TotalVotingPower (int64)`: Total voting power of the validator set at | |||
height `Height` |
@ -0,0 +1,213 @@ | |||
{ | |||
"abciApps": [ | |||
{ | |||
"name": "Cosmos SDK", | |||
"url": "https://github.com/cosmos/cosmos-sdk", | |||
"language": "Go", | |||
"author": "Cosmos", | |||
"description": | |||
"A prototypical account based crypto currency state machine supporting plugins" | |||
}, | |||
{ | |||
"name": "cb-ledger", | |||
"url": "https://github.com/block-finance/cpp-abci", | |||
"language": "C++", | |||
"author": "Block Finance", | |||
"description": | |||
"Custodian Bank Ledger, integrating central banking with the blockchains of tomorrow" | |||
}, | |||
{ | |||
"name": "Clearchain", | |||
"url": "https://github.com/tendermint/clearchain", | |||
"language": "Go", | |||
"author": "FXCLR", | |||
"description": | |||
"Application to manage a distributed ledger for money transfers that support multi-currency accounts" | |||
}, | |||
{ | |||
"name": "Ethermint", | |||
"url": "https://github.com/tendermint/ethermint", | |||
"language": "Go", | |||
"author": "Tendermint", | |||
"description": "The go-ethereum state machine run as an ABCI app" | |||
}, | |||
{ | |||
"name": "Merkle AVL Tree", | |||
"url": "https://github.com/tendermint/merkleeyes", | |||
"language": "Go", | |||
"author": "Tendermint", | |||
"description": "Tendermint IAVL tree implemented as an ABCI app" | |||
}, | |||
{ | |||
"name": "Burrow", | |||
"url": "https://github.com/hyperledger/burrow", | |||
"language": "Go", | |||
"author": "Monax Industries", | |||
"description": | |||
"Ethereum Virtual Machine augmented with native permissioning scheme and global key-value store" | |||
}, | |||
{ | |||
"name": "Merkle AVL Tree", | |||
"url": "https://github.com/jTMSP/MerkleTree", | |||
"language": "Java", | |||
"author": "jTMSP", | |||
"description": "Tendermint IAVL tree implemented as an ABCI app" | |||
}, | |||
{ | |||
"name": "TMChat", | |||
"url": "https://github.com/wolfposd/TMChat", | |||
"language": "Java", | |||
"author": "jTMSP", | |||
"description": "P2P chat using Tendermint" | |||
}, | |||
{ | |||
"name": "Comit", | |||
"url": "https://github.com/zballs/comit", | |||
"language": "Go", | |||
"author": "Zach Balder", | |||
"description": "Public service reporting and tracking" | |||
}, | |||
{ | |||
"name": "Passchain", | |||
"url": "https://github.com/trusch/passchain", | |||
"language": "Go", | |||
"author": "trusch", | |||
"description": | |||
"Tool to securely store and share passwords, tokens and other short secrets" | |||
}, | |||
{ | |||
"name": "Passwerk", | |||
"url": "https://github.com/rigelrozanski/passwerk", | |||
"language": "Go", | |||
"author": "Rigel Rozanski", | |||
"description": "Encrypted storage web-utility backed by Tendermint" | |||
}, | |||
{ | |||
"name": "py-tendermint", | |||
"url": "https://github.com/davebryson/py-tendermint", | |||
"language": "Python", | |||
"author": "Dave Bryson", | |||
"description": | |||
"A Python microframework for building blockchain applications with Tendermint" | |||
}, | |||
{ | |||
"name": "Stratumn SDK", | |||
"url": "https://github.com/stratumn/sdk", | |||
"language": "Go", | |||
"author": "Stratumn", | |||
"description": "SDK for Proof-of-Process networks" | |||
}, | |||
{ | |||
"name": "Lotion", | |||
"url": "https://github.com/keppel/lotion", | |||
"language": "Javascript", | |||
"author": "Judd Keppel", | |||
"description": | |||
"A Javascript microframework for building blockchain applications with Tendermint" | |||
}, | |||
{ | |||
"name": "Tendermint Blockchain Chat App", | |||
"url": "https://github.com/SaifRehman/tendermint-chat-app/", | |||
"language": "Javascript", | |||
"author": "Saif Rehman", | |||
"description": | |||
"This is a minimal chat application based on Tendermint using Lotion.js in 30 lines of code!. It also includes web/mobile application built using Ionic 3." | |||
}, | |||
{ | |||
"name": "BigchainDB", | |||
"url": "https://github.com/bigchaindb/bigchaindb", | |||
"language": "Python", | |||
"author": "BigchainDB GmbH and the BigchainDB community", | |||
"description": "Blockchain database" | |||
}, | |||
{ | |||
"name": "Mint", | |||
"url": "https://github.com/Hashnode/mint", | |||
"language": "Go", | |||
"author": "Hashnode", | |||
"description": "Build blockchain-powered social apps" | |||
} | |||
], | |||
"abciServers": [ | |||
{ | |||
"name": "abci", | |||
"url": "https://github.com/tendermint/abci", | |||
"language": "Go", | |||
"author": "Tendermint" | |||
}, | |||
{ | |||
"name": "js-abci", | |||
"url": "https://github.com/tendermint/js-abci", | |||
"language": "Javascript", | |||
"author": "Tendermint" | |||
}, | |||
{ | |||
"name": "cpp-tmsp", | |||
"url": "https://github.com/mdyring/cpp-tmsp", | |||
"language": "C++", | |||
"author": "Martin Dyring" | |||
}, | |||
{ | |||
"name": "jabci", | |||
"url": "https://github.com/jTendermint/jabci", | |||
"language": "Java", | |||
"author": "jTendermint" | |||
}, | |||
{ | |||
"name": "ocaml-tmsp", | |||
"url": "https://github.com/zballs/ocaml-tmsp", | |||
"language": "Ocaml", | |||
"author": "Zach Balder" | |||
}, | |||
{ | |||
"name": "abci_server", | |||
"url": "https://github.com/KrzysiekJ/abci_server", | |||
"language": "Erlang", | |||
"author": "Krzysztof Jurewicz" | |||
}, | |||
{ | |||
"name": "py-abci", | |||
"url": "https://github.com/davebryson/py-abci", | |||
"language": "Python", | |||
"author": "Dave Bryson" | |||
}, | |||
{ | |||
"name": "Spearmint", | |||
"url": "https://github.com/dennismckinnon/spearmint", | |||
"language": "Javascript", | |||
"author": "Dennis McKinnon" | |||
} | |||
], | |||
"deploymentTools": [ | |||
{ | |||
"name": "mintnet-kubernetes", | |||
"url": "https://github.com/tendermint/tools", | |||
"technology": "Docker and Kubernetes", | |||
"author": "Tendermint", | |||
"description": | |||
"Deploy a Tendermint test network using Google's kubernetes" | |||
}, | |||
{ | |||
"name": "terraforce", | |||
"url": "https://github.com/tendermint/tools", | |||
"technology": "Terraform", | |||
"author": "Tendermint", | |||
"description": | |||
"Terraform + our custom terraforce tool; deploy a production Tendermint network with load balancing over multiple AWS availability zones" | |||
}, | |||
{ | |||
"name": "ansible-tendermint", | |||
"url": "https://github.com/tendermint/tools", | |||
"technology": "Ansible", | |||
"author": "Tendermint", | |||
"description": "Ansible playbooks + Tendermint" | |||
}, | |||
{ | |||
"name": "brooklyn-tendermint", | |||
"url": "https://github.com/cloudsoft/brooklyn-tendermint", | |||
"technology": "Clocker for Apache Brooklyn ", | |||
"author": "Cloudsoft", | |||
"description": "Deploy a tendermint test network in docker containers " | |||
} | |||
] | |||
} |
@ -0,0 +1,97 @@ | |||
# Indexing Transactions | |||
Tendermint allows you to index transactions and later query or subscribe | |||
to their results. | |||
Let's take a look at the `[tx_index]` config section: | |||
``` | |||
##### transactions indexer configuration options ##### | |||
[tx_index] | |||
# What indexer to use for transactions | |||
# | |||
# Options: | |||
# 1) "null" (default) | |||
# 2) "kv" - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend). | |||
indexer = "kv" | |||
# Comma-separated list of tags to index (by default the only tag is tx hash) | |||
# | |||
# It's recommended to index only a subset of tags due to possible memory | |||
# bloat. This is, of course, depends on the indexer's DB and the volume of | |||
# transactions. | |||
index_tags = "" | |||
# When set to true, tells indexer to index all tags. Note this may be not | |||
# desirable (see the comment above). IndexTags has a precedence over | |||
# IndexAllTags (i.e. when given both, IndexTags will be indexed). | |||
index_all_tags = false | |||
``` | |||
By default, Tendermint will index all transactions by their respective | |||
hashes using an embedded simple indexer. Note, we are planning to add | |||
more options in the future (e.g., Postgresql indexer). | |||
## Adding tags | |||
In your application's `DeliverTx` method, add the `Tags` field with the | |||
pairs of UTF-8 encoded strings (e.g. "account.owner": "Bob", "balance": | |||
"100.0", "date": "2018-01-02"). | |||
Example: | |||
``` | |||
func (app *KVStoreApplication) DeliverTx(tx []byte) types.Result { | |||
... | |||
tags := []cmn.KVPair{ | |||
{[]byte("account.name"), []byte("igor")}, | |||
{[]byte("account.address"), []byte("0xdeadbeef")}, | |||
{[]byte("tx.amount"), []byte("7")}, | |||
} | |||
return types.ResponseDeliverTx{Code: code.CodeTypeOK, Tags: tags} | |||
} | |||
``` | |||
If you want Tendermint to only index transactions by "account.name" tag, | |||
in the config set `tx_index.index_tags="account.name"`. If you to index | |||
all tags, set `index_all_tags=true` | |||
Note, there are a few predefined tags: | |||
- `tm.event` (event type) | |||
- `tx.hash` (transaction's hash) | |||
- `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. | |||
## Querying transactions | |||
You can query the transaction results by calling `/tx_search` RPC | |||
endpoint: | |||
``` | |||
curl "localhost:26657/tx_search?query=\"account.name='igor'\"&prove=true" | |||
``` | |||
Check out [API docs](https://tendermint.github.io/slate/?shell#txsearch) | |||
for more information on query syntax and other options. | |||
## Subscribing to transactions | |||
Clients can subscribe to transactions with the given tags via Websocket | |||
by providing a query to `/subscribe` RPC endpoint. | |||
``` | |||
{ | |||
"jsonrpc": "2.0", | |||
"method": "subscribe", | |||
"id": "0", | |||
"params": { | |||
"query": "account.name='igor'" | |||
} | |||
} | |||
``` | |||
Check out [API docs](https://tendermint.github.io/slate/#subscribe) for | |||
more information on query syntax and other options. |
@ -1,68 +0,0 @@ | |||
# Deploy a Testnet | |||
Now that we've seen how ABCI works, and even played with a few | |||
applications on a single validator node, it's time to deploy a test | |||
network to four validator nodes. | |||
## Manual Deployments | |||
It's relatively easy to setup a Tendermint cluster manually. The only | |||
requirements for a particular Tendermint node are a private key for the | |||
validator, stored as `priv_validator.json`, a node key, stored as | |||
`node_key.json` and a list of the public keys of all validators, stored | |||
as `genesis.json`. These files should be stored in | |||
`~/.tendermint/config`, or wherever the `$TMHOME` variable might be set | |||
to. | |||
Here are the steps to setting up a testnet manually: | |||
1) Provision nodes on your cloud provider of choice | |||
2) Install Tendermint and the application of interest on all nodes | |||
3) Generate a private key and a node key for each validator using | |||
`tendermint init` | |||
4) Compile a list of public keys for each validator into a | |||
`genesis.json` file and replace the existing file with it. | |||
5) Run | |||
`tendermint node --proxy_app=kvstore --p2p.persistent_peers=< peer addresses >` | |||
on each node, where `< peer addresses >` is a comma separated list | |||
of the IP:PORT combination for each node. The default port for | |||
Tendermint is `26656`. Thus, if the IP addresses of your nodes were | |||
`192.168.0.1, 192.168.0.2, 192.168.0.3, 192.168.0.4`, the command | |||
would look like: | |||
tendermint node --proxy_app=kvstore --p2p.persistent_peers=96663a3dd0d7b9d17d4c8211b191af259621c693@192.168.0.1:26656, 429fcf25974313b95673f58d77eacdd434402665@192.168.0.2:26656, 0491d373a8e0fcf1023aaf18c51d6a1d0d4f31bd@192.168.0.3:26656, f9baeaa15fedf5e1ef7448dd60f46c01f1a9e9c4@192.168.0.4:26656 | |||
After a few seconds, all the nodes should connect to each other and | |||
start making blocks! For more information, see the Tendermint Networks | |||
section of [the guide to using Tendermint](./using-tendermint.md). | |||
But wait! Steps 3 and 4 are quite manual. Instead, use [this | |||
script](https://github.com/tendermint/tendermint/blob/develop/docs/examples/init_testnet.sh), | |||
which does the heavy lifting for you. And it gets better. | |||
Instead of the previously linked script to initialize the files required | |||
for a testnet, we have the `tendermint testnet` command. By default, | |||
running `tendermint testnet` will create all the required files, just | |||
like the script. Of course, you'll still need to manually edit some | |||
fields in the `config.toml`. Alternatively, see the available flags to | |||
auto-populate the `config.toml` with the fields that would otherwise be | |||
passed in via flags when running `tendermint node`. As you might | |||
imagine, this command is useful for manual or automated deployments. | |||
## Automated Deployments | |||
The easiest and fastest way to get a testnet up in less than 5 minutes. | |||
### Local | |||
With `docker` and `docker-compose` installed, run the command: | |||
make localnet-start | |||
from the root of the tendermint repository. This will spin up a 4-node | |||
local testnet. Review the target in the Makefile to debug any problems. | |||
### Cloud | |||
See the [next section](./terraform-and-ansible.html) for details. |
@ -1,69 +0,0 @@ | |||
#!/usr/bin/env bash | |||
# make all the files | |||
tendermint init --home ./tester/node0 | |||
tendermint init --home ./tester/node1 | |||
tendermint init --home ./tester/node2 | |||
tendermint init --home ./tester/node3 | |||
file0=./tester/node0/config/genesis.json | |||
file1=./tester/node1/config/genesis.json | |||
file2=./tester/node2/config/genesis.json | |||
file3=./tester/node3/config/genesis.json | |||
genesis_time=`cat $file0 | jq '.genesis_time'` | |||
chain_id=`cat $file0 | jq '.chain_id'` | |||
value0=`cat $file0 | jq '.validators[0].pub_key.value'` | |||
value1=`cat $file1 | jq '.validators[0].pub_key.value'` | |||
value2=`cat $file2 | jq '.validators[0].pub_key.value'` | |||
value3=`cat $file3 | jq '.validators[0].pub_key.value'` | |||
rm $file0 | |||
rm $file1 | |||
rm $file2 | |||
rm $file3 | |||
echo "{ | |||
\"genesis_time\": $genesis_time, | |||
\"chain_id\": $chain_id, | |||
\"validators\": [ | |||
{ | |||
\"pub_key\": { | |||
\"type\": \"tendermint/PubKeyEd25519\", | |||
\"value\": $value0 | |||
}, | |||
\"power:\": 10, | |||
\"name\":, \"\" | |||
}, | |||
{ | |||
\"pub_key\": { | |||
\"type\": \"tendermint/PubKeyEd25519\", | |||
\"value\": $value1 | |||
}, | |||
\"power:\": 10, | |||
\"name\":, \"\" | |||
}, | |||
{ | |||
\"pub_key\": { | |||
\"type\": \"tendermint/PubKeyEd25519\", | |||
\"value\": $value2 | |||
}, | |||
\"power:\": 10, | |||
\"name\":, \"\" | |||
}, | |||
{ | |||
\"pub_key\": { | |||
\"type\": \"tendermint/PubKeyEd25519\", | |||
\"value\": $value3 | |||
}, | |||
\"power:\": 10, | |||
\"name\":, \"\" | |||
} | |||
], | |||
\"app_hash\": \"\" | |||
}" >> $file0 | |||
cp $file0 $file1 | |||
cp $file0 $file2 | |||
cp $file2 $file3 |
@ -1,32 +0,0 @@ | |||
#!/usr/bin/env bash | |||
# XXX: this script is meant to be used only on a fresh Ubuntu 16.04 instance | |||
# and has only been tested on Digital Ocean | |||
# get and unpack golang | |||
curl -O https://storage.googleapis.com/golang/go1.10.linux-amd64.tar.gz | |||
tar -xvf go1.10.linux-amd64.tar.gz | |||
apt install make | |||
## move go and add binary to path | |||
mv go /usr/local | |||
echo "export PATH=\$PATH:/usr/local/go/bin" >> ~/.profile | |||
## create the GOPATH directory, set GOPATH and put on PATH | |||
mkdir goApps | |||
echo "export GOPATH=/root/goApps" >> ~/.profile | |||
echo "export PATH=\$PATH:\$GOPATH/bin" >> ~/.profile | |||
source ~/.profile | |||
## get the code and move into it | |||
REPO=github.com/tendermint/tendermint | |||
go get $REPO | |||
cd $GOPATH/src/$REPO | |||
## build | |||
git checkout master | |||
make get_tools | |||
make get_vendor_deps | |||
make install |
@ -1,166 +0,0 @@ | |||
# This is a TOML config file. | |||
# For more information, see https://github.com/toml-lang/toml | |||
##### main base config options ##### | |||
# TCP or UNIX socket address of the ABCI application, | |||
# or the name of an ABCI application compiled in with the Tendermint binary | |||
proxy_app = "tcp://127.0.0.1:26658" | |||
# A custom human readable name for this node | |||
moniker = "alpha" | |||
# If this node is many blocks behind the tip of the chain, FastSync | |||
# allows them to catchup quickly by downloading blocks in parallel | |||
# and verifying their commits | |||
fast_sync = true | |||
# Database backend: leveldb | memdb | |||
db_backend = "leveldb" | |||
# Database directory | |||
db_path = "data" | |||
# Output level for logging, including package level options | |||
log_level = "main:info,state:info,*:error" | |||
##### additional base config options ##### | |||
# Path to the JSON file containing the initial validator set and other meta data | |||
genesis_file = "config/genesis.json" | |||
# Path to the JSON file containing the private key to use as a validator in the consensus protocol | |||
priv_validator_file = "config/priv_validator.json" | |||
# Path to the JSON file containing the private key to use for node authentication in the p2p protocol | |||
node_key_file = "config/node_key.json" | |||
# Mechanism to connect to the ABCI application: socket | grpc | |||
abci = "socket" | |||
# TCP or UNIX socket address for the profiling server to listen on | |||
prof_laddr = "" | |||
# If true, query the ABCI app on connecting to a new peer | |||
# so the app can decide if we should keep the connection or not | |||
filter_peers = false | |||
##### advanced configuration options ##### | |||
##### rpc server configuration options ##### | |||
[rpc] | |||
# TCP or UNIX socket address for the RPC server to listen on | |||
laddr = "tcp://0.0.0.0:26657" | |||
# TCP or UNIX socket address for the gRPC server to listen on | |||
# NOTE: This server only supports /broadcast_tx_commit | |||
grpc_laddr = "" | |||
# Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool | |||
unsafe = false | |||
##### peer to peer configuration options ##### | |||
[p2p] | |||
# Address to listen for incoming connections | |||
laddr = "tcp://0.0.0.0:26656" | |||
# Comma separated list of seed nodes to connect to | |||
seeds = "" | |||
# Comma separated list of nodes to keep persistent connections to | |||
# Do not add private peers to this list if you don't want them advertised | |||
persistent_peers = "" | |||
# Path to address book | |||
addr_book_file = "config/addrbook.json" | |||
# Set true for strict address routability rules | |||
addr_book_strict = true | |||
# Time to wait before flushing messages out on the connection, in ms | |||
flush_throttle_timeout = 100 | |||
# Maximum number of peers to connect to | |||
max_num_peers = 50 | |||
# Maximum size of a message packet payload, in bytes | |||
max_packet_msg_payload_size = 1024 | |||
# Rate at which packets can be sent, in bytes/second | |||
send_rate = 512000 | |||
# Rate at which packets can be received, in bytes/second | |||
recv_rate = 512000 | |||
# Set true to enable the peer-exchange reactor | |||
pex = true | |||
# Seed mode, in which node constantly crawls the network and looks for | |||
# peers. If another node asks it for addresses, it responds and disconnects. | |||
# | |||
# Does not work if the peer-exchange reactor is disabled. | |||
seed_mode = false | |||
# Comma separated list of peer IDs to keep private (will not be gossiped to other peers) | |||
private_peer_ids = "" | |||
##### mempool configuration options ##### | |||
[mempool] | |||
recheck = true | |||
recheck_empty = true | |||
broadcast = true | |||
wal_dir = "data/mempool.wal" | |||
##### consensus configuration options ##### | |||
[consensus] | |||
wal_file = "data/cs.wal/wal" | |||
# All timeouts are in milliseconds | |||
timeout_propose = 3000 | |||
timeout_propose_delta = 500 | |||
timeout_prevote = 1000 | |||
timeout_prevote_delta = 500 | |||
timeout_precommit = 1000 | |||
timeout_precommit_delta = 500 | |||
timeout_commit = 1000 | |||
# Make progress as soon as we have all the precommits (as if TimeoutCommit = 0) | |||
skip_timeout_commit = false | |||
# BlockSize | |||
max_block_size_txs = 10000 | |||
max_block_size_bytes = 1 | |||
# EmptyBlocks mode and possible interval between empty blocks in seconds | |||
create_empty_blocks = true | |||
create_empty_blocks_interval = 0 | |||
# Reactor sleep duration parameters are in milliseconds | |||
peer_gossip_sleep_duration = 100 | |||
peer_query_maj23_sleep_duration = 2000 | |||
##### transactions indexer configuration options ##### | |||
[tx_index] | |||
# What indexer to use for transactions | |||
# | |||
# Options: | |||
# 1) "null" (default) | |||
# 2) "kv" - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend). | |||
indexer = "kv" | |||
# Comma-separated list of tags to index (by default the only tag is tx hash) | |||
# | |||
# It's recommended to index only a subset of tags due to possible memory | |||
# bloat. This is, of course, depends on the indexer's DB and the volume of | |||
# transactions. | |||
index_tags = "" | |||
# When set to true, tells indexer to index all tags. Note this may be not | |||
# desirable (see the comment above). IndexTags has a precedence over | |||
# IndexAllTags (i.e. when given both, IndexTags will be indexed). | |||
index_all_tags = false |
@ -1,39 +0,0 @@ | |||
{ | |||
"genesis_time": "0001-01-01T00:00:00Z", | |||
"chain_id": "test-chain-A2i3OZ", | |||
"validators": [ | |||
{ | |||
"pub_key": { | |||
"type": "tendermint/PubKeyEd25519", | |||
"value": "D+k4AdjnYPWbB9wmad137Bdpo/kAulOoTRQrLy/Qc4k=" | |||
}, | |||
"power": "10", | |||
"name": "" | |||
}, | |||
{ | |||
"pub_key": { | |||
"type": "tendermint/PubKeyEd25519", | |||
"value": "b56N5GCR1adcVRuENjfKw/mrm2dkhT7wNZXV/SDsKsU=" | |||
}, | |||
"power": "10", | |||
"name": "" | |||
}, | |||
{ | |||
"pub_key": { | |||
"type": "tendermint/PubKeyEd25519", | |||
"value": "IgZDpJvGA0TAamicA8ircy+RX/BkUlj6DXwM791ywIU=" | |||
}, | |||
"power": "10", | |||
"name": "" | |||
}, | |||
{ | |||
"pub_key": { | |||
"type": "tendermint/PubKeyEd25519", | |||
"value": "KGAZfxZvIZ7abbeIQ85U1ECG6+I62KSdaH8ulc0+OiU=" | |||
}, | |||
"power": "10", | |||
"name": "" | |||
} | |||
], | |||
"app_hash": "" | |||
} |
@ -1 +0,0 @@ | |||
{"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"7lY+k6EDllG8Q9gVbF5313t/ag2YGkBVKdVa0YHJ9xO5k0w3Q/hke0Z7UFT1KgVDGRUEKzwAwwjwFQUvgF0ZWg=="}} |
@ -1,14 +0,0 @@ | |||
{ | |||
"address": "122A9414774A2FCAD026201DA477EF3F41970EF0", | |||
"pub_key": { | |||
"type": "tendermint/PubKeyEd25519", | |||
"value": "D+k4AdjnYPWbB9wmad137Bdpo/kAulOoTRQrLy/Qc4k=" | |||
}, | |||
"last_height": "0", | |||
"last_round": "0", | |||
"last_step": 0, | |||
"priv_key": { | |||
"type": "tendermint/PrivKeyEd25519", | |||
"value": "YLxp3ho+kySgAnzjBptbxDzSGw2ntGZLsIHQsaVxY/cP6TgB2Odg9ZsH3CZp3XfsF2mj+QC6U6hNFCsvL9BziQ==" | |||
} | |||
} |
@ -1,166 +0,0 @@ | |||
# This is a TOML config file. | |||
# For more information, see https://github.com/toml-lang/toml | |||
##### main base config options ##### | |||
# TCP or UNIX socket address of the ABCI application, | |||
# or the name of an ABCI application compiled in with the Tendermint binary | |||
proxy_app = "tcp://127.0.0.1:26658" | |||
# A custom human readable name for this node | |||
moniker = "bravo" | |||
# If this node is many blocks behind the tip of the chain, FastSync | |||
# allows them to catchup quickly by downloading blocks in parallel | |||
# and verifying their commits | |||
fast_sync = true | |||
# Database backend: leveldb | memdb | |||
db_backend = "leveldb" | |||
# Database directory | |||
db_path = "data" | |||
# Output level for logging, including package level options | |||
log_level = "main:info,state:info,*:error" | |||
##### additional base config options ##### | |||
# Path to the JSON file containing the initial validator set and other meta data | |||
genesis_file = "config/genesis.json" | |||
# Path to the JSON file containing the private key to use as a validator in the consensus protocol | |||
priv_validator_file = "config/priv_validator.json" | |||
# Path to the JSON file containing the private key to use for node authentication in the p2p protocol | |||
node_key_file = "config/node_key.json" | |||
# Mechanism to connect to the ABCI application: socket | grpc | |||
abci = "socket" | |||
# TCP or UNIX socket address for the profiling server to listen on | |||
prof_laddr = "" | |||
# If true, query the ABCI app on connecting to a new peer | |||
# so the app can decide if we should keep the connection or not | |||
filter_peers = false | |||
##### advanced configuration options ##### | |||
##### rpc server configuration options ##### | |||
[rpc] | |||
# TCP or UNIX socket address for the RPC server to listen on | |||
laddr = "tcp://0.0.0.0:26657" | |||
# TCP or UNIX socket address for the gRPC server to listen on | |||
# NOTE: This server only supports /broadcast_tx_commit | |||
grpc_laddr = "" | |||
# Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool | |||
unsafe = false | |||
##### peer to peer configuration options ##### | |||
[p2p] | |||
# Address to listen for incoming connections | |||
laddr = "tcp://0.0.0.0:26656" | |||
# Comma separated list of seed nodes to connect to | |||
seeds = "" | |||
# Comma separated list of nodes to keep persistent connections to | |||
# Do not add private peers to this list if you don't want them advertised | |||
persistent_peers = "" | |||
# Path to address book | |||
addr_book_file = "config/addrbook.json" | |||
# Set true for strict address routability rules | |||
addr_book_strict = true | |||
# Time to wait before flushing messages out on the connection, in ms | |||
flush_throttle_timeout = 100 | |||
# Maximum number of peers to connect to | |||
max_num_peers = 50 | |||
# Maximum size of a message packet payload, in bytes | |||
max_packet_msg_payload_size = 1024 | |||
# Rate at which packets can be sent, in bytes/second | |||
send_rate = 512000 | |||
# Rate at which packets can be received, in bytes/second | |||
recv_rate = 512000 | |||
# Set true to enable the peer-exchange reactor | |||
pex = true | |||
# Seed mode, in which node constantly crawls the network and looks for | |||
# peers. If another node asks it for addresses, it responds and disconnects. | |||
# | |||
# Does not work if the peer-exchange reactor is disabled. | |||
seed_mode = false | |||
# Comma separated list of peer IDs to keep private (will not be gossiped to other peers) | |||
private_peer_ids = "" | |||
##### mempool configuration options ##### | |||
[mempool] | |||
recheck = true | |||
recheck_empty = true | |||
broadcast = true | |||
wal_dir = "data/mempool.wal" | |||
##### consensus configuration options ##### | |||
[consensus] | |||
wal_file = "data/cs.wal/wal" | |||
# All timeouts are in milliseconds | |||
timeout_propose = 3000 | |||
timeout_propose_delta = 500 | |||
timeout_prevote = 1000 | |||
timeout_prevote_delta = 500 | |||
timeout_precommit = 1000 | |||
timeout_precommit_delta = 500 | |||
timeout_commit = 1000 | |||
# Make progress as soon as we have all the precommits (as if TimeoutCommit = 0) | |||
skip_timeout_commit = false | |||
# BlockSize | |||
max_block_size_txs = 10000 | |||
max_block_size_bytes = 1 | |||
# EmptyBlocks mode and possible interval between empty blocks in seconds | |||
create_empty_blocks = true | |||
create_empty_blocks_interval = 0 | |||
# Reactor sleep duration parameters are in milliseconds | |||
peer_gossip_sleep_duration = 100 | |||
peer_query_maj23_sleep_duration = 2000 | |||
##### transactions indexer configuration options ##### | |||
[tx_index] | |||
# What indexer to use for transactions | |||
# | |||
# Options: | |||
# 1) "null" (default) | |||
# 2) "kv" - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend). | |||
indexer = "kv" | |||
# Comma-separated list of tags to index (by default the only tag is tx hash) | |||
# | |||
# It's recommended to index only a subset of tags due to possible memory | |||
# bloat. This is, of course, depends on the indexer's DB and the volume of | |||
# transactions. | |||
index_tags = "" | |||
# When set to true, tells indexer to index all tags. Note this may be not | |||
# desirable (see the comment above). IndexTags has a precedence over | |||
# IndexAllTags (i.e. when given both, IndexTags will be indexed). | |||
index_all_tags = false |
@ -1,39 +0,0 @@ | |||
{ | |||
"genesis_time": "0001-01-01T00:00:00Z", | |||
"chain_id": "test-chain-A2i3OZ", | |||
"validators": [ | |||
{ | |||
"pub_key": { | |||
"type": "tendermint/PubKeyEd25519", | |||
"value": "D+k4AdjnYPWbB9wmad137Bdpo/kAulOoTRQrLy/Qc4k=" | |||
}, | |||
"power": "10", | |||
"name": "" | |||
}, | |||
{ | |||
"pub_key": { | |||
"type": "tendermint/PubKeyEd25519", | |||
"value": "b56N5GCR1adcVRuENjfKw/mrm2dkhT7wNZXV/SDsKsU=" | |||
}, | |||
"power": "10", | |||
"name": "" | |||
}, | |||
{ | |||
"pub_key": { | |||
"type": "tendermint/PubKeyEd25519", | |||
"value": "IgZDpJvGA0TAamicA8ircy+RX/BkUlj6DXwM791ywIU=" | |||
}, | |||
"power": "10", | |||
"name": "" | |||
}, | |||
{ | |||
"pub_key": { | |||
"type": "tendermint/PubKeyEd25519", | |||
"value": "KGAZfxZvIZ7abbeIQ85U1ECG6+I62KSdaH8ulc0+OiU=" | |||
}, | |||
"power": "10", | |||
"name": "" | |||
} | |||
], | |||
"app_hash": "" | |||
} |
@ -1 +0,0 @@ | |||
{"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"H71dc/TIG7nTselfa9nG0WRArXLKYnm7P5eFCk2lk8ASKQ3sIHpbdxCSHQD/RcdHe7TiabJeuOssNPvPWiyQEQ=="}} |
@ -1,14 +0,0 @@ | |||
{ | |||
"address": "BEA1B57F5806CF9AC4D54C8CF806DED5C0F102E1", | |||
"pub_key": { | |||
"type": "tendermint/PubKeyEd25519", | |||
"value": "b56N5GCR1adcVRuENjfKw/mrm2dkhT7wNZXV/SDsKsU=" | |||
}, | |||
"last_height": "0", | |||
"last_round": "0", | |||
"last_step": 0, | |||
"priv_key": { | |||
"type": "tendermint/PrivKeyEd25519", | |||
"value": "o0IqrHSPtd5YqGefodWxpJuRzvuVBjgbH785vbMgk7Vvno3kYJHVp1xVG4Q2N8rD+aubZ2SFPvA1ldX9IOwqxQ==" | |||
} | |||
} |
@ -1,166 +0,0 @@ | |||
# This is a TOML config file. | |||
# For more information, see https://github.com/toml-lang/toml | |||
##### main base config options ##### | |||
# TCP or UNIX socket address of the ABCI application, | |||
# or the name of an ABCI application compiled in with the Tendermint binary | |||
proxy_app = "tcp://127.0.0.1:26658" | |||
# A custom human readable name for this node | |||
moniker = "charlie" | |||
# If this node is many blocks behind the tip of the chain, FastSync | |||
# allows them to catchup quickly by downloading blocks in parallel | |||
# and verifying their commits | |||
fast_sync = true | |||
# Database backend: leveldb | memdb | |||
db_backend = "leveldb" | |||
# Database directory | |||
db_path = "data" | |||
# Output level for logging, including package level options | |||
log_level = "main:info,state:info,*:error" | |||
##### additional base config options ##### | |||
# Path to the JSON file containing the initial validator set and other meta data | |||
genesis_file = "config/genesis.json" | |||
# Path to the JSON file containing the private key to use as a validator in the consensus protocol | |||
priv_validator_file = "config/priv_validator.json" | |||
# Path to the JSON file containing the private key to use for node authentication in the p2p protocol | |||
node_key_file = "config/node_key.json" | |||
# Mechanism to connect to the ABCI application: socket | grpc | |||
abci = "socket" | |||
# TCP or UNIX socket address for the profiling server to listen on | |||
prof_laddr = "" | |||
# If true, query the ABCI app on connecting to a new peer | |||
# so the app can decide if we should keep the connection or not | |||
filter_peers = false | |||
##### advanced configuration options ##### | |||
##### rpc server configuration options ##### | |||
[rpc] | |||
# TCP or UNIX socket address for the RPC server to listen on | |||
laddr = "tcp://0.0.0.0:26657" | |||
# TCP or UNIX socket address for the gRPC server to listen on | |||
# NOTE: This server only supports /broadcast_tx_commit | |||
grpc_laddr = "" | |||
# Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool | |||
unsafe = false | |||
##### peer to peer configuration options ##### | |||
[p2p] | |||
# Address to listen for incoming connections | |||
laddr = "tcp://0.0.0.0:26656" | |||
# Comma separated list of seed nodes to connect to | |||
seeds = "" | |||
# Comma separated list of nodes to keep persistent connections to | |||
# Do not add private peers to this list if you don't want them advertised | |||
persistent_peers = "" | |||
# Path to address book | |||
addr_book_file = "config/addrbook.json" | |||
# Set true for strict address routability rules | |||
addr_book_strict = true | |||
# Time to wait before flushing messages out on the connection, in ms | |||
flush_throttle_timeout = 100 | |||
# Maximum number of peers to connect to | |||
max_num_peers = 50 | |||
# Maximum size of a message packet payload, in bytes | |||
max_packet_msg_payload_size = 1024 | |||
# Rate at which packets can be sent, in bytes/second | |||
send_rate = 512000 | |||
# Rate at which packets can be received, in bytes/second | |||
recv_rate = 512000 | |||
# Set true to enable the peer-exchange reactor | |||
pex = true | |||
# Seed mode, in which node constantly crawls the network and looks for | |||
# peers. If another node asks it for addresses, it responds and disconnects. | |||
# | |||
# Does not work if the peer-exchange reactor is disabled. | |||
seed_mode = false | |||
# Comma separated list of peer IDs to keep private (will not be gossiped to other peers) | |||
private_peer_ids = "" | |||
##### mempool configuration options ##### | |||
[mempool] | |||
recheck = true | |||
recheck_empty = true | |||
broadcast = true | |||
wal_dir = "data/mempool.wal" | |||
##### consensus configuration options ##### | |||
[consensus] | |||
wal_file = "data/cs.wal/wal" | |||
# All timeouts are in milliseconds | |||
timeout_propose = 3000 | |||
timeout_propose_delta = 500 | |||
timeout_prevote = 1000 | |||
timeout_prevote_delta = 500 | |||
timeout_precommit = 1000 | |||
timeout_precommit_delta = 500 | |||
timeout_commit = 1000 | |||
# Make progress as soon as we have all the precommits (as if TimeoutCommit = 0) | |||
skip_timeout_commit = false | |||
# BlockSize | |||
max_block_size_txs = 10000 | |||
max_block_size_bytes = 1 | |||
# EmptyBlocks mode and possible interval between empty blocks in seconds | |||
create_empty_blocks = true | |||
create_empty_blocks_interval = 0 | |||
# Reactor sleep duration parameters are in milliseconds | |||
peer_gossip_sleep_duration = 100 | |||
peer_query_maj23_sleep_duration = 2000 | |||
##### transactions indexer configuration options ##### | |||
[tx_index] | |||
# What indexer to use for transactions | |||
# | |||
# Options: | |||
# 1) "null" (default) | |||
# 2) "kv" - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend). | |||
indexer = "kv" | |||
# Comma-separated list of tags to index (by default the only tag is tx hash) | |||
# | |||
# It's recommended to index only a subset of tags due to possible memory | |||
# bloat. This is, of course, depends on the indexer's DB and the volume of | |||
# transactions. | |||
index_tags = "" | |||
# When set to true, tells indexer to index all tags. Note this may be not | |||
# desirable (see the comment above). IndexTags has a precedence over | |||
# IndexAllTags (i.e. when given both, IndexTags will be indexed). | |||
index_all_tags = false |
@ -1,39 +0,0 @@ | |||
{ | |||
"genesis_time": "0001-01-01T00:00:00Z", | |||
"chain_id": "test-chain-A2i3OZ", | |||
"validators": [ | |||
{ | |||
"pub_key": { | |||
"type": "tendermint/PubKeyEd25519", | |||
"value": "D+k4AdjnYPWbB9wmad137Bdpo/kAulOoTRQrLy/Qc4k=" | |||
}, | |||
"power": "10", | |||
"name": "" | |||
}, | |||
{ | |||
"pub_key": { | |||
"type": "tendermint/PubKeyEd25519", | |||
"value": "b56N5GCR1adcVRuENjfKw/mrm2dkhT7wNZXV/SDsKsU=" | |||
}, | |||
"power": "10", | |||
"name": "" | |||
}, | |||
{ | |||
"pub_key": { | |||
"type": "tendermint/PubKeyEd25519", | |||
"value": "IgZDpJvGA0TAamicA8ircy+RX/BkUlj6DXwM791ywIU=" | |||
}, | |||
"power": "10", | |||
"name": "" | |||
}, | |||
{ | |||
"pub_key": { | |||
"type": "tendermint/PubKeyEd25519", | |||
"value": "KGAZfxZvIZ7abbeIQ85U1ECG6+I62KSdaH8ulc0+OiU=" | |||
}, | |||
"power": "10", | |||
"name": "" | |||
} | |||
], | |||
"app_hash": "" | |||
} |
@ -1 +0,0 @@ | |||
{"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"COHZ/Y2cWGWxJNkRwtpQBt5sYvOnb6Gpz0lO46XERRJFBIdSWD5x1UMGRSTmnvW1ec5G4bMdg6zUZKOZD+vVPg=="}} |
@ -1,14 +0,0 @@ | |||
{ | |||
"address": "F0AA266949FB29ADA0B679C27889ED930BD1BDA1", | |||
"pub_key": { | |||
"type": "tendermint/PubKeyEd25519", | |||
"value": "IgZDpJvGA0TAamicA8ircy+RX/BkUlj6DXwM791ywIU=" | |||
}, | |||
"last_height": "0", | |||
"last_round": "0", | |||
"last_step": 0, | |||
"priv_key": { | |||
"type": "tendermint/PrivKeyEd25519", | |||
"value": "khADeZ5K/8u/L99DFaZNRq8V5g+EHWbwfqFjhCrppaAiBkOkm8YDRMBqaJwDyKtzL5Ff8GRSWPoNfAzv3XLAhQ==" | |||
} | |||
} |
@ -1,166 +0,0 @@ | |||
# This is a TOML config file. | |||
# For more information, see https://github.com/toml-lang/toml | |||
##### main base config options ##### | |||
# TCP or UNIX socket address of the ABCI application, | |||
# or the name of an ABCI application compiled in with the Tendermint binary | |||
proxy_app = "tcp://127.0.0.1:26658" | |||
# A custom human readable name for this node | |||
moniker = "delta" | |||
# If this node is many blocks behind the tip of the chain, FastSync | |||
# allows them to catchup quickly by downloading blocks in parallel | |||
# and verifying their commits | |||
fast_sync = true | |||
# Database backend: leveldb | memdb | |||
db_backend = "leveldb" | |||
# Database directory | |||
db_path = "data" | |||
# Output level for logging, including package level options | |||
log_level = "main:info,state:info,*:error" | |||
##### additional base config options ##### | |||
# Path to the JSON file containing the initial validator set and other meta data | |||
genesis_file = "config/genesis.json" | |||
# Path to the JSON file containing the private key to use as a validator in the consensus protocol | |||
priv_validator_file = "config/priv_validator.json" | |||
# Path to the JSON file containing the private key to use for node authentication in the p2p protocol | |||
node_key_file = "config/node_key.json" | |||
# Mechanism to connect to the ABCI application: socket | grpc | |||
abci = "socket" | |||
# TCP or UNIX socket address for the profiling server to listen on | |||
prof_laddr = "" | |||
# If true, query the ABCI app on connecting to a new peer | |||
# so the app can decide if we should keep the connection or not | |||
filter_peers = false | |||
##### advanced configuration options ##### | |||
##### rpc server configuration options ##### | |||
[rpc] | |||
# TCP or UNIX socket address for the RPC server to listen on | |||
laddr = "tcp://0.0.0.0:26657" | |||
# TCP or UNIX socket address for the gRPC server to listen on | |||
# NOTE: This server only supports /broadcast_tx_commit | |||
grpc_laddr = "" | |||
# Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool | |||
unsafe = false | |||
##### peer to peer configuration options ##### | |||
[p2p] | |||
# Address to listen for incoming connections | |||
laddr = "tcp://0.0.0.0:26656" | |||
# Comma separated list of seed nodes to connect to | |||
seeds = "" | |||
# Comma separated list of nodes to keep persistent connections to | |||
# Do not add private peers to this list if you don't want them advertised | |||
persistent_peers = "" | |||
# Path to address book | |||
addr_book_file = "config/addrbook.json" | |||
# Set true for strict address routability rules | |||
addr_book_strict = true | |||
# Time to wait before flushing messages out on the connection, in ms | |||
flush_throttle_timeout = 100 | |||
# Maximum number of peers to connect to | |||
max_num_peers = 50 | |||
# Maximum size of a message packet payload, in bytes | |||
max_packet_msg_payload_size = 1024 | |||
# Rate at which packets can be sent, in bytes/second | |||
send_rate = 512000 | |||
# Rate at which packets can be received, in bytes/second | |||
recv_rate = 512000 | |||
# Set true to enable the peer-exchange reactor | |||
pex = true | |||
# Seed mode, in which node constantly crawls the network and looks for | |||
# peers. If another node asks it for addresses, it responds and disconnects. | |||
# | |||
# Does not work if the peer-exchange reactor is disabled. | |||
seed_mode = false | |||
# Comma separated list of peer IDs to keep private (will not be gossiped to other peers) | |||
private_peer_ids = "" | |||
##### mempool configuration options ##### | |||
[mempool] | |||
recheck = true | |||
recheck_empty = true | |||
broadcast = true | |||
wal_dir = "data/mempool.wal" | |||
##### consensus configuration options ##### | |||
[consensus] | |||
wal_file = "data/cs.wal/wal" | |||
# All timeouts are in milliseconds | |||
timeout_propose = 3000 | |||
timeout_propose_delta = 500 | |||
timeout_prevote = 1000 | |||
timeout_prevote_delta = 500 | |||
timeout_precommit = 1000 | |||
timeout_precommit_delta = 500 | |||
timeout_commit = 1000 | |||
# Make progress as soon as we have all the precommits (as if TimeoutCommit = 0) | |||
skip_timeout_commit = false | |||
# BlockSize | |||
max_block_size_txs = 10000 | |||
max_block_size_bytes = 1 | |||
# EmptyBlocks mode and possible interval between empty blocks in seconds | |||
create_empty_blocks = true | |||
create_empty_blocks_interval = 0 | |||
# Reactor sleep duration parameters are in milliseconds | |||
peer_gossip_sleep_duration = 100 | |||
peer_query_maj23_sleep_duration = 2000 | |||
##### transactions indexer configuration options ##### | |||
[tx_index] | |||
# What indexer to use for transactions | |||
# | |||
# Options: | |||
# 1) "null" (default) | |||
# 2) "kv" - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend). | |||
indexer = "kv" | |||
# Comma-separated list of tags to index (by default the only tag is tx hash) | |||
# | |||
# It's recommended to index only a subset of tags due to possible memory | |||
# bloat. This is, of course, depends on the indexer's DB and the volume of | |||
# transactions. | |||
index_tags = "" | |||
# When set to true, tells indexer to index all tags. Note this may be not | |||
# desirable (see the comment above). IndexTags has a precedence over | |||
# IndexAllTags (i.e. when given both, IndexTags will be indexed). | |||
index_all_tags = false |
@ -1,39 +0,0 @@ | |||
{ | |||
"genesis_time": "0001-01-01T00:00:00Z", | |||
"chain_id": "test-chain-A2i3OZ", | |||
"validators": [ | |||
{ | |||
"pub_key": { | |||
"type": "tendermint/PubKeyEd25519", | |||
"value": "D+k4AdjnYPWbB9wmad137Bdpo/kAulOoTRQrLy/Qc4k=" | |||
}, | |||
"power": "10", | |||
"name": "" | |||
}, | |||
{ | |||
"pub_key": { | |||
"type": "tendermint/PubKeyEd25519", | |||
"value": "b56N5GCR1adcVRuENjfKw/mrm2dkhT7wNZXV/SDsKsU=" | |||
}, | |||
"power": "10", | |||
"name": "" | |||
}, | |||
{ | |||
"pub_key": { | |||
"type": "tendermint/PubKeyEd25519", | |||
"value": "IgZDpJvGA0TAamicA8ircy+RX/BkUlj6DXwM791ywIU=" | |||
}, | |||
"power": "10", | |||
"name": "" | |||
}, | |||
{ | |||
"pub_key": { | |||
"type": "tendermint/PubKeyEd25519", | |||
"value": "KGAZfxZvIZ7abbeIQ85U1ECG6+I62KSdaH8ulc0+OiU=" | |||
}, | |||
"power": "10", | |||
"name": "" | |||
} | |||
], | |||
"app_hash": "" | |||
} |
@ -1 +0,0 @@ | |||
{"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"9Y9xp/tUJJ6pHTF5SUV0bGKYSdVbFtMHu+Lr8S0JBSZAwneaejnfOEU1LMKOnQ07skrDUaJcj5di3jAyjxJzqg=="}} |
@ -1,14 +0,0 @@ | |||
{ | |||
"address": "9A1A6914EB5F4FF0269C7EEEE627C27310CC64F9", | |||
"pub_key": { | |||
"type": "tendermint/PubKeyEd25519", | |||
"value": "KGAZfxZvIZ7abbeIQ85U1ECG6+I62KSdaH8ulc0+OiU=" | |||
}, | |||
"last_height": "0", | |||
"last_round": "0", | |||
"last_step": 0, | |||
"priv_key": { | |||
"type": "tendermint/PrivKeyEd25519", | |||
"value": "jb52LZ5gp+eQ8nJlFK1z06nBMp1gD8ICmyzdM1icGOgoYBl/Fm8hntptt4hDzlTUQIbr4jrYpJ1ofy6VzT46JQ==" | |||
} | |||
} |
@ -1,130 +0,0 @@ | |||
# How to read logs | |||
## Walkabout example | |||
We first create three connections (mempool, consensus and query) to the | |||
application (running `kvstore` locally in this case). | |||
I[10-04|13:54:27.364] Starting multiAppConn module=proxy impl=multiAppConn | |||
I[10-04|13:54:27.366] Starting localClient module=abci-client connection=query impl=localClient | |||
I[10-04|13:54:27.366] Starting localClient module=abci-client connection=mempool impl=localClient | |||
I[10-04|13:54:27.367] Starting localClient module=abci-client connection=consensus impl=localClient | |||
Then Tendermint Core and the application perform a handshake. | |||
I[10-04|13:54:27.367] ABCI Handshake module=consensus appHeight=90 appHash=E0FBAFBF6FCED8B9786DDFEB1A0D4FA2501BADAD | |||
I[10-04|13:54:27.368] ABCI Replay Blocks module=consensus appHeight=90 storeHeight=90 stateHeight=90 | |||
I[10-04|13:54:27.368] Completed ABCI Handshake - Tendermint and App are synced module=consensus appHeight=90 appHash=E0FBAFBF6FCED8B9786DDFEB1A0D4FA2501BADAD | |||
After that, we start a few more things like the event switch, reactors, | |||
and perform UPNP discover in order to detect the IP address. | |||
I[10-04|13:54:27.374] Starting EventSwitch module=types impl=EventSwitch | |||
I[10-04|13:54:27.375] This node is a validator module=consensus | |||
I[10-04|13:54:27.379] Starting Node module=main impl=Node | |||
I[10-04|13:54:27.381] Local listener module=p2p ip=:: port=26656 | |||
I[10-04|13:54:27.382] Getting UPNP external address module=p2p | |||
I[10-04|13:54:30.386] Could not perform UPNP discover module=p2p err="write udp4 0.0.0.0:38238->239.255.255.250:1900: i/o timeout" | |||
I[10-04|13:54:30.386] Starting DefaultListener module=p2p impl=Listener(@10.0.2.15:26656) | |||
I[10-04|13:54:30.387] Starting P2P Switch module=p2p impl="P2P Switch" | |||
I[10-04|13:54:30.387] Starting MempoolReactor module=mempool impl=MempoolReactor | |||
I[10-04|13:54:30.387] Starting BlockchainReactor module=blockchain impl=BlockchainReactor | |||
I[10-04|13:54:30.387] Starting ConsensusReactor module=consensus impl=ConsensusReactor | |||
I[10-04|13:54:30.387] ConsensusReactor module=consensus fastSync=false | |||
I[10-04|13:54:30.387] Starting ConsensusState module=consensus impl=ConsensusState | |||
I[10-04|13:54:30.387] Starting WAL module=consensus wal=/home/vagrant/.tendermint/data/cs.wal/wal impl=WAL | |||
I[10-04|13:54:30.388] Starting TimeoutTicker module=consensus impl=TimeoutTicker | |||
Notice the second row where Tendermint Core reports that "This node is a | |||
validator". It also could be just an observer (regular node). | |||
Next we replay all the messages from the WAL. | |||
I[10-04|13:54:30.390] Catchup by replaying consensus messages module=consensus height=91 | |||
I[10-04|13:54:30.390] Replay: New Step module=consensus height=91 round=0 step=RoundStepNewHeight | |||
I[10-04|13:54:30.390] Replay: Done module=consensus | |||
"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])}" | |||
Next follows a standard block creation cycle, where we enter a new | |||
round, propose a block, receive more than 2/3 of prevotes, then | |||
precommits and finally have a chance to commit a block. For details, | |||
please refer to [Consensus | |||
Overview](./introduction.md#consensus-overview) or [Byzantine Consensus | |||
Algorithm](./spec/consensus). | |||
I[10-04|13:54:30.393] enterNewRound(91/0). Current: 91/0/RoundStepNewHeight module=consensus | |||
I[10-04|13:54:30.393] enterPropose(91/0). Current: 91/0/RoundStepNewRound module=consensus | |||
I[10-04|13:54:30.393] enterPropose: Our turn to propose module=consensus proposer=125B0E3C5512F5C2B0E1109E31885C4511570C42 privValidator="PrivValidator{125B0E3C5512F5C2B0E1109E31885C4511570C42 LH:90, LR:0, LS:3}" | |||
I[10-04|13:54:30.394] Signed proposal module=consensus height=91 round=0 proposal="Proposal{91/0 1:21B79872514F (-1,:0:000000000000) {/10EDEDD7C84E.../}}" | |||
I[10-04|13:54:30.397] Received complete proposal block module=consensus height=91 hash=F671D562C7B9242900A286E1882EE64E5556FE9E | |||
I[10-04|13:54:30.397] enterPrevote(91/0). Current: 91/0/RoundStepPropose module=consensus | |||
I[10-04|13:54:30.397] enterPrevote: ProposalBlock is valid module=consensus height=91 round=0 | |||
I[10-04|13:54:30.398] Signed and pushed vote module=consensus height=91 round=0 vote="Vote{0:125B0E3C5512 91/00/1(Prevote) F671D562C7B9 {/89047FFC21D8.../}}" err=null | |||
I[10-04|13:54:30.401] Added to prevote module=consensus vote="Vote{0:125B0E3C5512 91/00/1(Prevote) F671D562C7B9 {/89047FFC21D8.../}}" prevotes="VoteSet{H:91 R:0 T:1 +2/3:F671D562C7B9242900A286E1882EE64E5556FE9E:1:21B79872514F BA{1:X} map[]}" | |||
I[10-04|13:54:30.401] enterPrecommit(91/0). Current: 91/0/RoundStepPrevote module=consensus | |||
I[10-04|13:54:30.401] enterPrecommit: +2/3 prevoted proposal block. Locking module=consensus hash=F671D562C7B9242900A286E1882EE64E5556FE9E | |||
I[10-04|13:54:30.402] Signed and pushed vote module=consensus height=91 round=0 vote="Vote{0:125B0E3C5512 91/00/2(Precommit) F671D562C7B9 {/80533478E41A.../}}" err=null | |||
I[10-04|13:54:30.404] Added to precommit module=consensus vote="Vote{0:125B0E3C5512 91/00/2(Precommit) F671D562C7B9 {/80533478E41A.../}}" precommits="VoteSet{H:91 R:0 T:2 +2/3:F671D562C7B9242900A286E1882EE64E5556FE9E:1:21B79872514F BA{1:X} map[]}" | |||
I[10-04|13:54:30.404] enterCommit(91/0). Current: 91/0/RoundStepPrecommit module=consensus | |||
I[10-04|13:54:30.405] Finalizing commit of block with 0 txs module=consensus height=91 hash=F671D562C7B9242900A286E1882EE64E5556FE9E root=E0FBAFBF6FCED8B9786DDFEB1A0D4FA2501BADAD | |||
I[10-04|13:54:30.405] Block{ | |||
Header{ | |||
ChainID: test-chain-3MNw2N | |||
Height: 91 | |||
Time: 2017-10-04 13:54:30.393 +0000 UTC | |||
NumTxs: 0 | |||
LastBlockID: F15AB8BEF9A6AAB07E457A6E16BC410546AA4DC6:1:D505DA273544 | |||
LastCommit: 56FEF2EFDB8B37E9C6E6D635749DF3169D5F005D | |||
Data: | |||
Validators: CE25FBFF2E10C0D51AA1A07C064A96931BC8B297 | |||
App: E0FBAFBF6FCED8B9786DDFEB1A0D4FA2501BADAD | |||
}#F671D562C7B9242900A286E1882EE64E5556FE9E | |||
Data{ | |||
}# | |||
Commit{ | |||
BlockID: F15AB8BEF9A6AAB07E457A6E16BC410546AA4DC6:1:D505DA273544 | |||
Precommits: Vote{0:125B0E3C5512 90/00/2(Precommit) F15AB8BEF9A6 {/FE98E2B956F0.../}} | |||
}#56FEF2EFDB8B37E9C6E6D635749DF3169D5F005D | |||
}#F671D562C7B9242900A286E1882EE64E5556FE9E module=consensus | |||
I[10-04|13:54:30.408] Executed block module=state height=91 validTxs=0 invalidTxs=0 | |||
I[10-04|13:54:30.410] Committed state module=state height=91 txs=0 hash=E0FBAFBF6FCED8B9786DDFEB1A0D4FA2501BADAD | |||
I[10-04|13:54:30.410] Recheck txs module=mempool numtxs=0 height=91 | |||
## List of modules | |||
Here is the list of modules you may encounter in Tendermint's log and a | |||
little overview what they do. | |||
- `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: | |||
mempool, consensus and query. The code used by Tendermint Core can | |||
be found [here](https://github.com/tendermint/tendermint/tree/develop/abci/client). | |||
- `blockchain` Provides storage, pool (a group of peers), and reactor | |||
for both storing and exchanging blocks between peers. | |||
- `consensus` The heart of Tendermint core, which is the | |||
implementation of the consensus algorithm. Includes two | |||
"submodules": `wal` (write-ahead logging) for ensuring data | |||
integrity and `replay` to replay blocks and messages on recovery | |||
from a crash. | |||
- `events` Simple event notification system. The list of events can be | |||
found | |||
[here](https://github.com/tendermint/tendermint/blob/master/types/events.go). | |||
You can subscribe to them by calling `subscribe` RPC method. Refer | |||
to [RPC docs](./specification/rpc.md) for additional information. | |||
- `mempool` Mempool module handles all incoming transactions, whenever | |||
they are coming from peers or the application. | |||
- `p2p` Provides an abstraction around peer-to-peer communication. For | |||
more details, please check out the | |||
[README](https://github.com/tendermint/tendermint/blob/master/p2p/README.md). | |||
- `rpc` [Tendermint's RPC](./specification/rpc.md). | |||
- `rpc-server` RPC server. For implementation details, please read the | |||
[README](https://github.com/tendermint/tendermint/blob/master/rpc/lib/README.md). | |||
- `state` Represents the latest state and execution submodule, which | |||
executes blocks against the application. | |||
- `types` A collection of the publicly exposed types and methods to | |||
work with them. |
@ -1,89 +0,0 @@ | |||
# Indexing Transactions | |||
Tendermint allows you to index transactions and later query or subscribe | |||
to their results. | |||
Let's take a look at the `[tx_index]` config section: | |||
##### transactions indexer configuration options ##### | |||
[tx_index] | |||
# What indexer to use for transactions | |||
# | |||
# Options: | |||
# 1) "null" (default) | |||
# 2) "kv" - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend). | |||
indexer = "kv" | |||
# Comma-separated list of tags to index (by default the only tag is tx hash) | |||
# | |||
# It's recommended to index only a subset of tags due to possible memory | |||
# bloat. This is, of course, depends on the indexer's DB and the volume of | |||
# transactions. | |||
index_tags = "" | |||
# When set to true, tells indexer to index all tags. Note this may be not | |||
# desirable (see the comment above). IndexTags has a precedence over | |||
# IndexAllTags (i.e. when given both, IndexTags will be indexed). | |||
index_all_tags = false | |||
By default, Tendermint will index all transactions by their respective | |||
hashes using an embedded simple indexer. Note, we are planning to add | |||
more options in the future (e.g., Postgresql indexer). | |||
## Adding tags | |||
In your application's `DeliverTx` method, add the `Tags` field with the | |||
pairs of UTF-8 encoded strings (e.g. "account.owner": "Bob", "balance": | |||
"100.0", "date": "2018-01-02"). | |||
Example: | |||
func (app *KVStoreApplication) DeliverTx(tx []byte) types.Result { | |||
... | |||
tags := []cmn.KVPair{ | |||
{[]byte("account.name"), []byte("igor")}, | |||
{[]byte("account.address"), []byte("0xdeadbeef")}, | |||
{[]byte("tx.amount"), []byte("7")}, | |||
} | |||
return types.ResponseDeliverTx{Code: code.CodeTypeOK, Tags: tags} | |||
} | |||
If you want Tendermint to only index transactions by "account.name" tag, | |||
in the config set `tx_index.index_tags="account.name"`. If you to index | |||
all tags, set `index_all_tags=true` | |||
Note, there are a few predefined tags: | |||
- `tm.event` (event type) | |||
- `tx.hash` (transaction's hash) | |||
- `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. | |||
## Querying transactions | |||
You can query the transaction results by calling `/tx_search` RPC | |||
endpoint: | |||
curl "localhost:26657/tx_search?query=\"account.name='igor'\"&prove=true" | |||
Check out [API docs](https://tendermint.github.io/slate/?shell#txsearch) | |||
for more information on query syntax and other options. | |||
## Subscribing to transactions | |||
Clients can subscribe to transactions with the given tags via Websocket | |||
by providing a query to `/subscribe` RPC endpoint. | |||
{ | |||
"jsonrpc": "2.0", | |||
"method": "subscribe", | |||
"id": "0", | |||
"params": { | |||
"query": "account.name='igor'" | |||
} | |||
} | |||
Check out [API docs](https://tendermint.github.io/slate/#subscribe) for | |||
more information on query syntax and other options. |
@ -0,0 +1,250 @@ | |||
# Interview Transcript with Tendermint core researcher, Zarko Milosevic, by Chjango | |||
**ZM**: Regarding leader election, it's round robin, but a weighted one. You | |||
take into account the amount of bonded tokens. Depending on how much weight | |||
they have of voting power, they would be elected more frequently. So we do | |||
rotate, but just the guys who are having more voting power would be elected | |||
more frequently. We are having 4 validators, and 1 of them have 2 times more | |||
voting power, they have 2 times more elected as a leader. | |||
**CC**: 2x more absolute voting power or probabilistic voting power? | |||
**ZM**: It's actually very deterministic. It's not probabilistic at all. See | |||
[Tendermint proposal election specification][1]. In Tendermint, there is no | |||
pseudorandom leader election. It's a deterministic protocol. So leader election | |||
is a built-in function in the code, so you know exactly—depending on the voting | |||
power in the validator set, you'd know who exactly would be the leader in round | |||
x, x + 1, and so on. There is nothing random there; we are not trying to hide | |||
who would be the leader. It's really well known. It's just that there is a | |||
function, it's a mathematical function, and it's just basically—it's kind of an | |||
implementation detail—it starts from the voting power, and when you are | |||
elected, you get decreased some number, and in each round you keep increasing | |||
depending on your voting power, so that you are elected after k rounds again. | |||
But knowing the validator set and the voting power, it's very simple function, | |||
you can calculate yourself to know exactly who would be next. For each round, | |||
this function will return you the leader for that round. In every round, we do | |||
this computation. It's all part of the same flow. It enforces the properties | |||
which are: proportional to your voting power, you will be elected, and we keep | |||
changing the leaders. So it can't happen to have one guy being more elected | |||
than other guys, if they have the same voting power. So one time it will be guy | |||
B, and next time it will be guy B1. So it's not random. | |||
**CC**: Assuming the validator set remains unchanged for a month, then if you | |||
run this function, are you able to know exactly who is going to go for that | |||
entire month? | |||
**ZM**: Yes. | |||
**CC**: What're the attack scenarios for this? | |||
**ZM**: This is something which is easily attacked by people who argue that | |||
Tendermint is not decentralized enough. They say that by knowing the leader, | |||
you can DDoS the leader. And by DDoSing the leader, you are able to stop the | |||
progress. Because it's true. If you would be able to DDoS the leader, the | |||
leader would not be able to propose and then effectively will not be making | |||
progress. How we are addressing this thing is Sentry Architecture. So the | |||
validator—or at least a proper validator—will never be available. You don't | |||
know the ip address of the validator. You are never able to open the connection | |||
to the validator. So validator is spawning sentry nodes and this is the single | |||
administration domain and there is only connection from validator in the sense | |||
of sentry nodes. And ip address of validator is not shared in the p2p network. | |||
It’s completely private. This is our answer to DDoS attack. By playing clever | |||
at this sentry node architecture and spawning additional sentry nodes in case, | |||
for ex your sentry nodes are being DDoS’d, bc your sentry nodes are public, | |||
then you will be able to connect to sentry nodes. this is where we will expect | |||
the validator to be clever enough that so that in case they are DDoS’d at the | |||
sentry level, they will spawn a different sentry node and then you communicate | |||
through them. We are in a sense pushing the responsibility on the validator. | |||
**CC**: So if I understand this correctly, the public identity of the validator | |||
doesn’t even matter because that entity can obfuscate where their real full | |||
nodes reside via a proxy through this sentry architecture. | |||
**ZM**: Exactly. So you do know what is the address or identity of the validator | |||
but you don’t know the network address of it; you’re not able to attack it | |||
because you don’t know where they are. They are completely obfuscated by the | |||
sentry nodes. There is now, if you really want to figure out….There is the | |||
Tendermint protocol, the structure of the protocol is not fully decentralized | |||
in the sense that the flow of information is going from the round proposer, or | |||
the round coordinator, to other nodes, and then after they receive this it’s | |||
basically like [inaudible: “O to 1”]. So by tracking where this information is | |||
coming from, you might be able to identify who are the sentry nodes behind it. | |||
So if you are doing some network analysis, you might be able to deduce | |||
something. If the thing would be completely stuck, where the validator would | |||
never change their sentry nodes or ip addresses of sentry nodes, it could be | |||
possible to deduce something. This is where economic game comes into play. We | |||
are doing an economics game there. We say that it’s a validator business. If | |||
they are not able to hide themselves well enough, they’ll be DDoS’d and they | |||
will be kicked out of the active validator set. So it’s in their interest. | |||
[Proposer Selection Procedure in Tendermint][1]. This is how it should work no | |||
matter what implementation. | |||
**CC**: Going back to the proposer, lets say the validator does get DDoS’d, then | |||
the proposer goes down. What happens? | |||
**ZM**: How the proposal mechanism works—there’s nothing special there—it goes | |||
through a sequence of rounds. Normal execution of Tendermint is that for each | |||
height, we are going through a sequence of rounds, starting from round 0, and | |||
then we are incrementing through the rounds. The nodes are moving through the | |||
rounds as part of normal procedure until they decide to commit. In case you | |||
have one proposer—the proposer of a single round—being DDoS’d, we will probably | |||
not decide in that round, because he will not be able to send his proposal. So | |||
we will go to the next round, and hopefully the next proposer will be able to | |||
communicate with the validators and then we’ll decide in the next round. | |||
**CC**: Are there timeouts between one round to another, if a round gets | |||
skipped? | |||
**ZM**: There are timeouts. It’s a bit more complex. I think we have 5 timeouts. | |||
We may be able to simplify this a bit. What is important to understand is: The | |||
only condition which needs to be satisfied so we can go to the next round is | |||
that your validator is able to communicate with more than 2/3rds of voting | |||
power. To be able to move to the next round, you need to receive more than | |||
2/3rd of voting power equivalent of pre-commit messages. | |||
We have two kinds of messages: 1) Proposal: Where the current round proposer is | |||
suggesting how the next block should look like. This is first one. Every round | |||
starts with proposer sending a proposal. And then there are two more rounds of | |||
voting, where the validator is trying to agree whether they will commit the | |||
proposal or not. And the first of such vote messages is called `pre-vote` and | |||
the second one is `pre-commit`. Now, to be able to move between steps, between | |||
a `pre-vote` and `pre-commit` step, you need to receive enough number of | |||
messages where if message is sent by validator A, then also this message has a | |||
weight, or voting power which is equal to the voting power of the validator who | |||
sent this message. Before you receive more than 2/3 of voting power messages, you are not | |||
able to move to the higher round. Only when you receive more than 2/3 of | |||
messages, you actually start the timeout. The timeout is happening only after | |||
you receive enough messages. And it happens because of the asynchrony of the | |||
message communication so you give more time to guys with this timeout to | |||
receive some messages which are maybe delayed. | |||
**CC**: In this way that you just described via the whole network gossiping | |||
before we commit a block, that is what makes Tendermint BFT deterministic in a | |||
partially synchronous setting vs Bitcoin which has synchrony assumptions | |||
whereby blocks are first mined and then gossiped to the network. | |||
**ZM**: It's true that in Bitcoin, this is where the synchrony assumption comes | |||
to play because if they're not able to communicate timely, they are not able to | |||
converge to a single longest chain. Why are they not able to decrease timeout | |||
in Bitcoin? Because if they would decrease, there would be so many forks that | |||
they won't be able to converge to a single chain. By increasing this | |||
complexity and the block time, they're able to have not so many forks. This is | |||
effectively the timing assumption—the block duration in a sense because it's | |||
enough time so that the decided block is propagated through the network before | |||
someone else start deciding on the same block and creating forks. It's very | |||
different from the consensus algorithms in a distributed computing setup where | |||
Tendermint fits. In Tendermint, where we talk about the timing dependency, they | |||
are really part of this 3-communication step protocol I just explained. We have | |||
the following assumption: If the good guys are not able to communicate timely | |||
and reliably without having message loss within a round, the Tendermint will | |||
not make progress—it will not be making blocks. So if you are in a completely | |||
asynchronous network where messages get lost or delayed unpredictably, | |||
Tendermint will not make progress, it will not create forks, but it will not | |||
decide, it will not tell you what is the next block. For termination, it's a | |||
liveness property of consensus. It's a guarantee to decide. We do need timing | |||
assumptions. Within a round, correct validators are able to communicate to each | |||
other the consensus messages, not the transactions, but consensus messages. | |||
They need to communicate in a timely and reliable fashion. But this doesn't | |||
need to hold forever. It's just that what we are assuming when we say it's a | |||
partially synchronous system, we assume that the system will be going through a | |||
period of asynchrony, where we don't have this guarantee; the messages will be | |||
delayed or some will be lost and then will not make progress for some period of | |||
time, or we're not guaranteed to make progress. And the period of synchrony | |||
where these guarantees hold. And if we think about internet, internet is best | |||
described using such a model. Sometimes when we send a message to SF to | |||
Belgrade, it takes 100 ms, sometimes it takes 300 ms, sometimes it takes 1 s. | |||
But in most cases, it takes 100 ms or less than this. | |||
There is one thing which would be really nice if you understand it. In a global | |||
wide area network, we can't make assumption on the communication unless we are | |||
very conservative about this. If you want to be very fast, then we can't make | |||
assumption and say we'll be for sure communicating with 1 ms communication | |||
delay. Because of the complexity and various congestion issues on the network, | |||
it might happen that during a short period of time, this doesn't hold. If this | |||
doesn't hold and you depend on this for correctness of your protocol, you will | |||
have a fork. So the partially synchronous protocol, most of them like | |||
Tendermint, they don't depend on the timing assumption from the internet for | |||
correctness. This is where we state: safety always. So we never make a fork no | |||
matter how bad our estimates about the internet communication delays are. We'll | |||
never make a fork, but we do make some assumptions, and these assumptions are | |||
built-in our timeouts in our protocol which are actually adaptive. So we are | |||
adapting to the current condition and this is where we're saying...We do assume | |||
some properties, or some communication delays, to eventually hold on the | |||
network. During this period, we guarantee that we will be deciding and | |||
committing blocks. And we will be doing this very fast. We will be basically on | |||
the speed of the current network. | |||
**CC**: We make liveness assumptions based on the integrity of the validator | |||
businesses, assuming they're up and running fine. | |||
**ZM**: This is where we are saying, the protocol will be live if we have at | |||
most 1/3, or a bit less than 1/3, of faulty validators. Which means that all | |||
other guys should be online and available. This is also for liveness. This is | |||
related to the condition that we are not able to make progress in rounds if we | |||
don't receive enough messages. If half of our voting power, or half of our | |||
validators are down, we don't have enough messages, so the protocol is | |||
completely blocked. It doesn't make progress in a round, which means it's not | |||
able to be signed. So it's completely critical for Tendermint that we make | |||
progress in rounds. It's like breathing. Tendermint is breathing. If there is | |||
no progress, it's dead; it's blocked, we're not able to breathe, that's why | |||
we're not able to make progress. | |||
**CC**: How does Tendermint compare to other consensus algos? | |||
**ZM**: Tendermint is a very interesting protocol. From an academic point of | |||
view, I'm convinced that there is value there. Hopefully, we prove it by | |||
publishing it on some good conference. What is novel is, if we compare first | |||
Tendermint to this existing BFT problem, it's a continuation of academic | |||
research on BFT consensus. What is novel in Tendermint is that it somehow | |||
merges consensus protocol with gossip. This is completely novel idea. | |||
Originally, in BFT, people were assuming the single administration domain, | |||
small number of nodes, local area network, 4-7 nodes max. If you look at the | |||
research paper, 99% of them have this kind of setup. Wide area was studied but | |||
there is significantly less work in wide area networks. No one studied how to | |||
scale those protocols to hundreds or thousands of nodes before blockchain. It | |||
was always a single administration domain. So in Tendermint now, you are able | |||
to reach consensus among different administration domains which are potentially | |||
hundreds of them in wide area network. The system model is potentially harder | |||
because we have more nodes and wide area network. The second thing is that: | |||
normally, in bft protocols, the protocol itself are normally designed in a way | |||
that has two phases, or two parts. The one which is called normal case, which | |||
is normally quite simple, in this normal case. In spite of some failures, which | |||
are part of the normal execution of the protocol, like for example leader | |||
crashes or leader being DDoS'd, they need to go through a quite complex | |||
protocol, which is like being called view change or leader election or | |||
whatever. These two parts of the same protocol are having quite different | |||
complexity. And most of the people only understand this normal case. In | |||
Tendermint, there is no this difference. We have only one protocol, there are | |||
not two protocols. It's always the same steps and they are much closer to the | |||
normal case than this complex view change protocol. | |||
_This is a bit too technical but this is on a high level things to remember, | |||
that: The system it addresses it's harder than the others and the algorithm | |||
complexity in Tendermint is simpler._ The initial goal of Jae and Bucky which | |||
is inspired by Raft, is that it's simpler so normal engineers could understand. | |||
**CC**: Can you expand on the termination requirement? | |||
_Important point about Liveness in Tendermint_ | |||
**ZM**: In Tendermint, we are saying, for termination, we are making assumption | |||
that the system is partially synchronous. And in a partially synchronous system | |||
model, we are able to mathematically prove that the protocol will make | |||
decisions; it will decide. | |||
**CC**: What is a persistent peer? | |||
**ZM**: It's a list of peer identities, which you will try to establish | |||
connection to them, in case connection is broken, Tendermint will automatically | |||
try to reestablish connection. These are important peers, you will really try | |||
persistently to establish connection to them. For other peers, you just drop it | |||
and try from your address book to connect to someone else. The address book is a | |||
list of peers which you discover that they exist, because we are talking about a | |||
very dynamic network—so the nodes are coming and going away—and the gossiping | |||
protocol is discovering new nodes and gossiping them around. So every node will | |||
keep the list of new nodes it discovers, and when you need to establish | |||
connection to a peer, you'll look to address book and get some addresses from | |||
there. There's categorization/ranking of nodes there. | |||
[1]: https://github.com/tendermint/tendermint/blob/master/docs/spec/reactors/consensus/proposer-selection.md |