@ -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 |