diff --git a/abci/.circleci/config.yml b/abci/.circleci/config.yml new file mode 100644 index 000000000..ab773382b --- /dev/null +++ b/abci/.circleci/config.yml @@ -0,0 +1,139 @@ +version: 2 + +defaults: &defaults + working_directory: /go/src/github.com/tendermint/abci + docker: + - image: circleci/golang:1.10.0 + environment: + GOBIN: /tmp/workspace/bin + +jobs: + setup_dependencies: + <<: *defaults + steps: + - run: mkdir -p /tmp/workspace/bin + - run: mkdir -p /tmp/workspace/profiles + - checkout + - restore_cache: + keys: + - v1-pkg-cache + - run: + name: tools + command: | + export PATH="$GOBIN:$PATH" + make get_tools + - run: + name: dependencies + command: | + export PATH="$GOBIN:$PATH" + make get_vendor_deps + - run: + name: binaries + command: | + export PATH="$GOBIN:$PATH" + make install + - persist_to_workspace: + root: /tmp/workspace + paths: + - bin + - profiles + - save_cache: + key: v1-pkg-cache + paths: + - /go/pkg + - save_cache: + key: v1-tree-{{ .Environment.CIRCLE_SHA1 }} + paths: + - /go/src/github.com/tendermint/abci + + test_apps: + <<: *defaults + steps: + - attach_workspace: + at: /tmp/workspace + - restore_cache: + key: v1-pkg-cache + - restore_cache: + key: v1-tree-{{ .Environment.CIRCLE_SHA1 }} + - run: + name: Run apps tests + command: | + export PATH="$GOBIN:$PATH" + bash tests/test_app/test.sh + +# XXX: if this test fails, fix it and update the docs at: +# https://github.com/tendermint/tendermint/blob/develop/docs/abci-cli.rst + test_cli: + <<: *defaults + steps: + - attach_workspace: + at: /tmp/workspace + - restore_cache: + key: v1-pkg-cache + - restore_cache: + key: v1-tree-{{ .Environment.CIRCLE_SHA1 }} + - run: + name: Run cli tests + command: | + export PATH="$GOBIN:$PATH" + bash tests/test_cli/test.sh + + test_cover: + <<: *defaults + parallelism: 4 + steps: + - attach_workspace: + at: /tmp/workspace + - restore_cache: + key: v1-pkg-cache + - restore_cache: + key: v1-tree-{{ .Environment.CIRCLE_SHA1 }} + - run: + name: Run test cover + command: | + for pkg in $(go list github.com/tendermint/abci/... | grep -v /vendor/ | circleci tests split --split-by=timings); do + id=$(basename "$pkg") + go test -timeout 5m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg" + done + - persist_to_workspace: + root: /tmp/workspace + paths: + - "profiles/*" + + upload_coverage: + <<: *defaults + steps: + - attach_workspace: + at: /tmp/workspace + - restore_cache: + key: v1-tree-{{ .Environment.CIRCLE_SHA1 }} + - run: + name: gather + command: | + set -ex + + echo "mode: atomic" > coverage.txt + for prof in $(ls /tmp/workspace/profiles/); do + tail -n +2 /tmp/workspace/profiles/"$prof" >> coverage.txt + done + - run: + name: upload + command: bash <(curl -s https://codecov.io/bash) -f coverage.txt + +workflows: + version: 2 + test-suite: + jobs: + - setup_dependencies + - test_cover: + requires: + - setup_dependencies + - test_apps: + requires: + - setup_dependencies + - test_cli: + requires: + - setup_dependencies + - upload_coverage: + requires: + - test_cover diff --git a/abci/.editorconfig b/abci/.editorconfig new file mode 100644 index 000000000..82f774362 --- /dev/null +++ b/abci/.editorconfig @@ -0,0 +1,19 @@ +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[Makefile] +indent_style = tab + +[*.sh] +indent_style = tab + +[*.proto] +indent_style = space +indent_size = 2 diff --git a/abci/.gitignore b/abci/.gitignore new file mode 100644 index 000000000..421292fa4 --- /dev/null +++ b/abci/.gitignore @@ -0,0 +1,7 @@ +vendor +.glide +types/types.pb.go +*.sw[op] +abci-cli +coverage.txt +profile.out \ No newline at end of file diff --git a/abci/CHANGELOG.md b/abci/CHANGELOG.md new file mode 100644 index 000000000..63de0bd03 --- /dev/null +++ b/abci/CHANGELOG.md @@ -0,0 +1,352 @@ +# Changelog + +## 0.12.0 + +*2018-06-12* + +BREAKING CHANGES: + +- [abci-cli] Change rpc port from 46658 to 26658. +- [examples] Change rpc port from 46658 to 26658. + +## 0.11.0 + +*June 6, 2018* + +BREAKING CHANGES: + +- [example/dummy] Remove. See example/kvstore +- [types] Upgrade many messages: + - RequestInitChain takes all fields from a Genesis file + - RequestBeginBlock provides a list of all validators and whether or not + they signed + - Header: remove some fields, add proposer + - BlockID, PartSetHeader: remove + - Validator: includes address + - PubKey: new message with `type` and `data` + - Evidence: add type and more fields + +FEATURES: + +- [types] Add some fields + - ResponseInitChain includes ConsensusParams and Validators + - ResponseBeginBlock includes tags + - ResponseEndBlock includes tags + +## 0.10.3 (April 9, 2018) + +IMPROVEMENTS: + +- Update tmlibs dep + +## 0.10.2 (March 23, 2018) + +Hot fix to remove `omitempty` from `fee` and to actually run `make +protoc` + +## 0.10.1 (March 22, 2018) + +FEATURES: + +- [types] ResponseCheckTx and ResponseDeliverTx are now the same. +- [example] `dummy` is duplicated as `kvstore`. + +IMPROVEMENTS: + +- glide -> Godep +- remove pkg/errors +- improve specification.rst + +## 0.10.0 (February 20, 2018) + +BREAKING CHANGES: + +- [types] Socket messages are length prefixed with real protobuf Varint instead of `` +- [types] Drop gogo custom type magic with data.Bytes +- [types] Use `[(gogoproto.nullable)=false]` to prefer value over pointer for the types +- [types] Field re-ordering ... +- [types] KVPair: replace with common.KVPair. Add common KI64Pair too (for fees). +- [types] CheckTx/DeliverTx: updates for tags, gas, fees +- [types] Commit: Remove code and log from Commit +- [types] SetOption: Remove code +- [example/dummy] remove dependence on IAVL +- [types] IsOk/IsErr: methods removed + +FEATURES: + +- [types] SetOption/Query/CheckTx/DeliverTx: Add `info string` field to responses +- [types] RequestInitChain.AppStateBytes for app's genesis state + +IMPROVEMENTS: + +- [all] remove go-wire and go-crypto dependencies :) + +## 0.9.0 (December 28, 2017) + +BREAKING CHANGES: + - [types] Id -> ID + - [types] ResponseEndBlock: renamed Diffs field to ValidatorUpdates + - [types] changed protobuf field indices for Request and Response oneof types + +FEATURES: + - [types] ResponseEndBlock: added ConsensusParamUpdates + +BUG FIXES: + - [cmd] fix console and batch commands to use a single persistent connection + +## 0.8.0 (December 6, 2017) + +BREAKING CHANGES: + - [client] all XxxSync methods now return (ResponseXxx, error) + - [types] all methods on Application interface now take RequestXxx and return (ResponseXxx, error). + - Except `CheckTx`/`DeliverTx`, which takes a `tx []byte` argument. + - Except `Commit`, which takes no arguments. + - [types] removed Result and ResultQuery + - [types] removed CodeType - only `0 == OK` is defined here, everything else is left to convention at the application level + - [types] switched to using `gogo/protobuf` for code generation + - [types] use `customtype` feature of `gogo/protobuf` to replace `[]byte` with `data.Bytes` in all generated types :) + - this eliminates the need for additional types like ResultQuery + - [types] `pubKey` -> `pub_key` + - [types] `uint64` -> `int32` for `Header.num_txs` and `PartSetHeader.total` + - [types] `uint64` -> `int64` for everything else + - [types] ResponseSetOption includes error code + - [abci-cli] codes are printed as their number instead of a message, except for `code == 0`, which is still printed as `OK` + +FEATURES: + - [types] ResponseDeliverTx: added `tags` field + - [types] ResponseCheckTx: added `gas` and `fee` fields + - [types] RequestBeginBlock: added `absent_validators` and `byzantine_validators` fields + - [dummy] DeliverTx returns an owner tag and a key tag + - [abci-cli] added `log_level` flag to control the logger + - [abci-cli] introduce `abci-cli test` command for simple testing of ABCI server implementations via Counter application + +## 0.7.1 (November 14, 2017) + +IMPROVEMENTS: + - [cli] added version command + +BUG FIXES: + - [server] fix "Connection error module=abci-server error=EOF" + +## 0.7.0 (October 27, 2017) + +BREAKING CHANGES: + - [cli] consolidate example apps under a single `abci-cli` binary + +IMPROVEMENTS: + - [cli] use spf13/cobra instead of urfave/cli + - [dummy] use iavl instead of merkleeyes, and add support for historical queries + +BUG FIXES: + - [client] fix deadlock on StopForError + +## 0.6.0 (September 22, 2017) + +BREAKING CHANGES: + +- [types/client] app.BeginBlock takes RequestBeginBlock +- [types/client] app.InitChain takes RequestInitChain +- [types/client] app.Info takes RequestInfo + +IMPROVEMENTS: + +- various linting + +## 0.5.0 (May 18, 2017) + +BREAKING CHANGES: + +- `NewSocketClient` and `NewGRPCClient` no longer start the client automatically, and don't return errors. The caller is responsible for running `client.Start()` and checking the error. +- `NewSocketServer` and `NewGRPCServer` no longer start the server automatically, and don't return errors. The caller is responsible for running `server.Start()` and checking the error. + + +FEATURES: + +- [types] new method `func (res Result) IsSameCode(compare Result) bool` checks whether two results have the same code +- [types] new methods `func (r *ResponseCheckTx) Result() Result` and `func (r *ResponseDeliverTx) Result() Result` to convert from protobuf types (for control over json serialization) +- [types] new method `func (r *ResponseQuery) Result() *ResultQuery` and struct `ResultQuery` to convert from protobuf types (for control over json serializtion) + +IMPROVEMENTS: + +- Update imports for new `tmlibs` repository +- Use the new logger +- [abci-cli] Add flags to the query command for `path`, `height`, and `prove` +- [types] use `data.Bytes` and `json` tags in the `Result` struct + +BUG FIXES: + +## 0.4.1 (April 18, 2017) + +IMPROVEMENTS: + +- Update dependencies + +## 0.4.0 (March 6, 2017) + +BREAKING CHANGES: + +- Query takes RequestQuery and returns ResponseQuery. The request is split into `data` and `path`, +can specify a height to query the state from, and whether or not the response should come with a proof. +The response returns the corresponding key-value pair, with proof if requested. + +``` +message RequestQuery{ + bytes data = 1; + string path = 2; + uint64 height = 3; + bool prove = 4; +} + +message ResponseQuery{ + CodeType code = 1; + int64 index = 2; + bytes key = 3; + bytes value = 4; + bytes proof = 5; + uint64 height = 6; + string log = 7; +} +``` + +IMPROVEMENTS: + +- Updates to Makefile +- Various cleanup +- BaseApplication can be embedded by new apps to avoid implementing empty methods +- Drop BlockchainAware and make BeginBlock/EndBlock part of the `type Application interface` + +## 0.3.0 (January 12, 2017) + +BREAKING CHANGES: + +- TMSP is now ABCI (Application/Asynchronous/A BlockChain Interface or Atomic BroadCast Interface) +- AppendTx is now DeliverTx (conforms to the literature) +- BeginBlock takes a Header: + +``` +message RequestBeginBlock{ + bytes hash = 1; + Header header = 2; +} +``` + +- Info returns a ResponseInfo, containing last block height and app hash: + +``` +message ResponseInfo { + string data = 1; + string version = 2; + uint64 last_block_height = 3; + bytes last_block_app_hash = 4; +} +``` + +- EndBlock returns a ResponseEndBlock, containing the changed validators: + +``` +message ResponseEndBlock{ + repeated Validator diffs = 4; +} +``` + +- Hex strings are 0x-prefixed in the CLI +- Query on the Dummy app now uses hex-strings + +FEATURES: + +- New app, PersistentDummy, uses Info/BeginBlock to recover from failures and supports validator set changes +- New message types for blockchain data: + +``` +//---------------------------------------- +// Blockchain Types + +message Header { + string chain_id = 1; + uint64 height = 2; + uint64 time = 3; + uint64 num_txs = 4; + BlockID last_block_id = 5; + bytes last_commit_hash = 6; + bytes data_hash = 7; + bytes validators_hash = 8; + bytes app_hash = 9; +} + +message BlockID { + bytes hash = 1; + PartSetHeader parts = 2; +} + +message PartSetHeader { + uint64 total = 1; + bytes hash = 2; +} + +message Validator { + bytes pubKey = 1; + uint64 power = 2; +} +``` + +- Add support for Query to Counter app + +IMPROVEMENT: + +- Don't exit the tmsp-cli console on bad args + +BUG FIXES: + +- Fix parsing in the Counter app to handle invalid transactions + + +## 0.2.1 (September 12, 2016) + +IMPROVEMENTS + +- Better error handling in console + + +## 0.2.0 (July 23, 2016) + +BREAKING CHANGES: + +- Use `oneof` types in protobuf + +FEATURES: + +- GRPC support + + +## PreHistory + +##### Mar 26h, 2016 +* Introduce BeginBlock + +##### Mar 6th, 2016 + +* Added InitChain, EndBlock + +##### Feb 14th, 2016 + +* s/GetHash/Commit/g +* Document Protobuf request/response fields + +##### Jan 23th, 2016 + +* Added CheckTx/Query ABCI message types +* Added Result/Log fields to DeliverTx/CheckTx/SetOption +* Removed Listener messages +* Removed Code from ResponseSetOption and ResponseGetHash +* Made examples BigEndian + +##### Jan 12th, 2016 + +* Added "RetCodeBadNonce = 0x06" return code + +##### Jan 8th, 2016 + +* Tendermint/ABCI now comes to consensus on the order first before DeliverTx. + + + diff --git a/abci/Dockerfile.develop b/abci/Dockerfile.develop new file mode 100644 index 000000000..c6ec05f69 --- /dev/null +++ b/abci/Dockerfile.develop @@ -0,0 +1,23 @@ +FROM golang:latest + +RUN mkdir -p /go/src/github.com/tendermint/abci +WORKDIR /go/src/github.com/tendermint/abci + +COPY Makefile /go/src/github.com/tendermint/abci/ + +# see make protoc for details on ldconfig +RUN make get_protoc && ldconfig + +# killall is used in tests +RUN apt-get update && apt-get install -y \ + psmisc \ + && rm -rf /var/lib/apt/lists/* + +COPY Gopkg.toml /go/src/github.com/tendermint/abci/ +COPY Gopkg.lock /go/src/github.com/tendermint/abci/ +RUN make get_tools + +# see https://github.com/golang/dep/issues/1312 +RUN dep ensure -vendor-only + +COPY . /go/src/github.com/tendermint/abci diff --git a/abci/Gopkg.lock b/abci/Gopkg.lock new file mode 100644 index 000000000..6bc40c0bb --- /dev/null +++ b/abci/Gopkg.lock @@ -0,0 +1,213 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[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", + "jsonpb", + "proto", + "protoc-gen-gogo/descriptor", + "sortkeys", + "types" + ] + revision = "1adfc126b41513cc696b209667c8656ea7aac67c" + version = "v1.0.0" + +[[projects]] + name = "github.com/golang/protobuf" + packages = [ + "proto", + "ptypes", + "ptypes/any", + "ptypes/duration", + "ptypes/timestamp" + ] + revision = "925541529c1fa6821df4e44ce2723319eb2be768" + version = "v1.0.0" + +[[projects]] + branch = "master" + name = "github.com/golang/snappy" + packages = ["."] + revision = "553a641470496b2327abcac10b36396bd98e45c9" + +[[projects]] + name = "github.com/inconshreveable/mousetrap" + packages = ["."] + revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" + version = "v1.0" + +[[projects]] + branch = "master" + name = "github.com/jmhodges/levigo" + packages = ["."] + revision = "c42d9e0ca023e2198120196f842701bb4c55d7b9" + +[[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/spf13/cobra" + packages = ["."] + revision = "a1f051bc3eba734da4772d60e2d677f47cf93ef4" + version = "v0.0.2" + +[[projects]] + name = "github.com/spf13/pflag" + packages = ["."] + revision = "e57e3eeb33f795204c1ca35f56c44f83227c6e66" + version = "v1.0.0" + +[[projects]] + name = "github.com/stretchr/testify" + packages = [ + "assert", + "require" + ] + revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71" + version = "v1.2.1" + +[[projects]] + branch = "master" + name = "github.com/syndtr/goleveldb" + packages = [ + "leveldb", + "leveldb/cache", + "leveldb/comparer", + "leveldb/errors", + "leveldb/filter", + "leveldb/iterator", + "leveldb/journal", + "leveldb/memdb", + "leveldb/opt", + "leveldb/storage", + "leveldb/table", + "leveldb/util" + ] + revision = "714f901b98fdb3aa954b4193d8cbd64a28d80cad" + +[[projects]] + name = "github.com/tendermint/tmlibs" + packages = [ + "common", + "db", + "log" + ] + revision = "2e24b64fc121dcdf1cabceab8dc2f7257675483c" + version = "v0.8.1" + +[[projects]] + branch = "master" + name = "golang.org/x/net" + packages = [ + "context", + "http2", + "http2/hpack", + "idna", + "internal/timeseries", + "lex/httplex", + "trace" + ] + revision = "61147c48b25b599e5b561d2e9c4f3e1ef489ca41" + +[[projects]] + name = "golang.org/x/text" + packages = [ + "collate", + "collate/build", + "internal/colltab", + "internal/gen", + "internal/tag", + "internal/triegen", + "internal/ucd", + "language", + "secure/bidirule", + "transform", + "unicode/bidi", + "unicode/cldr", + "unicode/norm", + "unicode/rangetable" + ] + revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" + version = "v0.3.0" + +[[projects]] + branch = "master" + name = "google.golang.org/genproto" + packages = ["googleapis/rpc/status"] + revision = "ce84044298496ef4b54b4a0a0909ba593cc60e30" + +[[projects]] + name = "google.golang.org/grpc" + packages = [ + ".", + "balancer", + "codes", + "connectivity", + "credentials", + "grpclb/grpc_lb_v1/messages", + "grpclog", + "internal", + "keepalive", + "metadata", + "naming", + "peer", + "resolver", + "stats", + "status", + "tap", + "transport" + ] + revision = "5b3c4e850e90a4cf6a20ebd46c8b32a0a3afcb9e" + version = "v1.7.5" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "e42d4a691fb0d0db9c717394e580dd00b36ba9e185541f99fc56689338470123" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/abci/Gopkg.toml b/abci/Gopkg.toml new file mode 100644 index 000000000..3f9a207df --- /dev/null +++ b/abci/Gopkg.toml @@ -0,0 +1,56 @@ +# 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 + +# NOTE if not specified, dep automatically adds `^` to each version, +# meaning it will accept up to the next version for the first non-zero +# element in the version. +# +# So `version = "1.3.2"` means `1.3.2 <= version < 2.0.0`. +# Use `~` for only minor version bumps. + +[[constraint]] + name = "github.com/gogo/protobuf" + version = "~1.0.0" + +[[constraint]] + name = "github.com/spf13/cobra" + version = "~0.0.1" + +[[constraint]] + name = "github.com/stretchr/testify" + version = "~1.2.1" + +[[constraint]] + name = "github.com/tendermint/tmlibs" + version = "0.8.1" + +[[constraint]] + name = "google.golang.org/grpc" + version = "~1.7.3" + +[prune] + go-tests = true + unused-packages = true diff --git a/abci/LICENSE b/abci/LICENSE new file mode 100644 index 000000000..57951bb8a --- /dev/null +++ b/abci/LICENSE @@ -0,0 +1,193 @@ +Tendermint ABCI +Copyright (C) 2015 Tendermint + + + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/abci/Makefile b/abci/Makefile new file mode 100644 index 000000000..7d1c4b2e5 --- /dev/null +++ b/abci/Makefile @@ -0,0 +1,174 @@ +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 diff --git a/abci/README.md b/abci/README.md new file mode 100644 index 000000000..6de9f7069 --- /dev/null +++ b/abci/README.md @@ -0,0 +1,168 @@ +# Application BlockChain Interface (ABCI) + +[![CircleCI](https://circleci.com/gh/tendermint/abci.svg?style=svg)](https://circleci.com/gh/tendermint/abci) + +Blockchains are systems for multi-master state machine replication. +**ABCI** is an interface that defines the boundary between the replication engine (the blockchain), +and the state machine (the application). +Using a socket protocol, a consensus engine running in one process +can manage an application state running in another. + +Previously, the ABCI was referred to as TMSP. + +The community has provided a number of addtional implementations, see the [Tendermint Ecosystem](https://tendermint.com/ecosystem) + +## Specification + +A detailed description of the ABCI methods and message types is contained in: + +- [A prose specification](specification.md) +- [A protobuf file](https://github.com/tendermint/abci/blob/master/types/types.proto) +- [A Go interface](https://github.com/tendermint/abci/blob/master/types/application.go). + +For more background information on ABCI, motivations, and tendermint, please visit [the documentation](http://tendermint.readthedocs.io/en/master/). +The two guides to focus on are the `Application Development Guide` and `Using ABCI-CLI`. + + +## Protocl Buffers + +To compile the protobuf file, run: + +``` +make protoc +``` + +See `protoc --help` and [the Protocol Buffers site](https://developers.google.com/protocol-buffers) +for details on compiling for other languages. Note we also include a [GRPC](http://www.grpc.io/docs) +service definition. + +## Install ABCI-CLI + +The `abci-cli` is a simple tool for debugging ABCI servers and running some +example apps. To install it: + +``` +go get github.com/tendermint/abci +cd $GOPATH/src/github.com/tendermint/abci +make get_vendor_deps +make install +``` + +## Implementation + +We provide three implementations of the ABCI in Go: + +- Golang in-process +- ABCI-socket +- GRPC + +Note the GRPC version is maintained primarily to simplify onboarding and prototyping and is not receiving the same +attention to security and performance as the others + +### In Process + +The simplest implementation just uses function calls within Go. +This means ABCI applications written in Golang can be compiled with TendermintCore and run as a single binary. + +See the [examples](#examples) below for more information. + +### Socket (TSP) + +ABCI is best implemented as a streaming protocol. +The socket implementation provides for asynchronous, ordered message passing over unix or tcp. +Messages are serialized using Protobuf3 and length-prefixed with a [signed Varint](https://developers.google.com/protocol-buffers/docs/encoding?csw=1#signed-integers) + +For example, if the Protobuf3 encoded ABCI message is `0xDEADBEEF` (4 bytes), the length-prefixed message is `0x08DEADBEEF`, since `0x08` is the signed varint +encoding of `4`. If the Protobuf3 encoded ABCI message is 65535 bytes long, the length-prefixed message would be like `0xFEFF07...`. + +Note the benefit of using this `varint` encoding over the old version (where integers were encoded as `` is that +it is the standard way to encode integers in Protobuf. It is also generally shorter. + +### GRPC + +GRPC is an rpc framework native to Protocol Buffers with support in many languages. +Implementing the ABCI using GRPC can allow for faster prototyping, but is expected to be much slower than +the ordered, asynchronous socket protocol. The implementation has also not received as much testing or review. + +Note the length-prefixing used in the socket implementation does not apply for GRPC. + +## Usage + +The `abci-cli` tool wraps an ABCI client and can be used for probing/testing an ABCI server. +For instance, `abci-cli test` will run a test sequence against a listening server running the Counter application (see below). +It can also be used to run some example applications. +See [the documentation](http://tendermint.readthedocs.io/en/master/) for more details. + +### Examples + +Check out the variety of example applications in the [example directory](example/). +It also contains the code refered to by the `counter` and `kvstore` apps; these apps come +built into the `abci-cli` binary. + +#### Counter + +The `abci-cli counter` application illustrates nonce checking in transactions. It's code looks like: + +```golang +func cmdCounter(cmd *cobra.Command, args []string) error { + + app := counter.NewCounterApplication(flagSerial) + + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) + + // Start the listener + srv, err := server.NewServer(flagAddrC, flagAbci, app) + if err != nil { + return err + } + srv.SetLogger(logger.With("module", "abci-server")) + if err := srv.Start(); err != nil { + return err + } + + // Wait forever + cmn.TrapSignal(func() { + // Cleanup + srv.Stop() + }) + return nil +} +``` + +and can be found in [this file](cmd/abci-cli/abci-cli.go). + +#### kvstore + +The `abci-cli kvstore` application, which illustrates a simple key-value Merkle tree + +```golang +func cmdKVStore(cmd *cobra.Command, args []string) error { + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) + + // Create the application - in memory or persisted to disk + var app types.Application + if flagPersist == "" { + app = kvstore.NewKVStoreApplication() + } else { + app = kvstore.NewPersistentKVStoreApplication(flagPersist) + app.(*kvstore.PersistentKVStoreApplication).SetLogger(logger.With("module", "kvstore")) + } + + // Start the listener + srv, err := server.NewServer(flagAddrD, flagAbci, app) + if err != nil { + return err + } + srv.SetLogger(logger.With("module", "abci-server")) + if err := srv.Start(); err != nil { + return err + } + + // Wait forever + cmn.TrapSignal(func() { + // Cleanup + srv.Stop() + }) + return nil +} +``` diff --git a/abci/client/client.go b/abci/client/client.go new file mode 100644 index 000000000..ad0e5a7ab --- /dev/null +++ b/abci/client/client.go @@ -0,0 +1,129 @@ +package abcicli + +import ( + "fmt" + "sync" + + "github.com/tendermint/abci/types" + cmn "github.com/tendermint/tmlibs/common" +) + +const ( + dialRetryIntervalSeconds = 3 + echoRetryIntervalSeconds = 1 +) + +// Client defines an interface for an ABCI client. +// All `Async` methods return a `ReqRes` object. +// All `Sync` methods return the appropriate protobuf ResponseXxx struct and an error. +// Note these are client errors, eg. ABCI socket connectivity issues. +// Application-related errors are reflected in response via ABCI error codes and logs. +type Client interface { + cmn.Service + + SetResponseCallback(Callback) + Error() error + + FlushAsync() *ReqRes + EchoAsync(msg string) *ReqRes + InfoAsync(types.RequestInfo) *ReqRes + SetOptionAsync(types.RequestSetOption) *ReqRes + DeliverTxAsync(tx []byte) *ReqRes + CheckTxAsync(tx []byte) *ReqRes + QueryAsync(types.RequestQuery) *ReqRes + CommitAsync() *ReqRes + InitChainAsync(types.RequestInitChain) *ReqRes + BeginBlockAsync(types.RequestBeginBlock) *ReqRes + EndBlockAsync(types.RequestEndBlock) *ReqRes + + FlushSync() error + EchoSync(msg string) (*types.ResponseEcho, error) + InfoSync(types.RequestInfo) (*types.ResponseInfo, error) + SetOptionSync(types.RequestSetOption) (*types.ResponseSetOption, error) + DeliverTxSync(tx []byte) (*types.ResponseDeliverTx, error) + CheckTxSync(tx []byte) (*types.ResponseCheckTx, error) + QuerySync(types.RequestQuery) (*types.ResponseQuery, error) + CommitSync() (*types.ResponseCommit, error) + InitChainSync(types.RequestInitChain) (*types.ResponseInitChain, error) + BeginBlockSync(types.RequestBeginBlock) (*types.ResponseBeginBlock, error) + EndBlockSync(types.RequestEndBlock) (*types.ResponseEndBlock, error) +} + +//---------------------------------------- + +// NewClient returns a new ABCI client of the specified transport type. +// It returns an error if the transport is not "socket" or "grpc" +func NewClient(addr, transport string, mustConnect bool) (client Client, err error) { + switch transport { + case "socket": + client = NewSocketClient(addr, mustConnect) + case "grpc": + client = NewGRPCClient(addr, mustConnect) + default: + err = fmt.Errorf("Unknown abci transport %s", transport) + } + return +} + +//---------------------------------------- + +type Callback func(*types.Request, *types.Response) + +//---------------------------------------- + +type ReqRes struct { + *types.Request + *sync.WaitGroup + *types.Response // Not set atomically, so be sure to use WaitGroup. + + mtx sync.Mutex + done bool // Gets set to true once *after* WaitGroup.Done(). + cb func(*types.Response) // A single callback that may be set. +} + +func NewReqRes(req *types.Request) *ReqRes { + return &ReqRes{ + Request: req, + WaitGroup: waitGroup1(), + Response: nil, + + done: false, + cb: nil, + } +} + +// Sets the callback for this ReqRes atomically. +// If reqRes is already done, calls cb immediately. +// NOTE: reqRes.cb should not change if reqRes.done. +// NOTE: only one callback is supported. +func (reqRes *ReqRes) SetCallback(cb func(res *types.Response)) { + reqRes.mtx.Lock() + + if reqRes.done { + reqRes.mtx.Unlock() + cb(reqRes.Response) + return + } + + defer reqRes.mtx.Unlock() + reqRes.cb = cb +} + +func (reqRes *ReqRes) GetCallback() func(*types.Response) { + reqRes.mtx.Lock() + defer reqRes.mtx.Unlock() + return reqRes.cb +} + +// NOTE: it should be safe to read reqRes.cb without locks after this. +func (reqRes *ReqRes) SetDone() { + reqRes.mtx.Lock() + reqRes.done = true + reqRes.mtx.Unlock() +} + +func waitGroup1() (wg *sync.WaitGroup) { + wg = &sync.WaitGroup{} + wg.Add(1) + return +} diff --git a/abci/client/grpc_client.go b/abci/client/grpc_client.go new file mode 100644 index 000000000..0f405a9c4 --- /dev/null +++ b/abci/client/grpc_client.go @@ -0,0 +1,301 @@ +package abcicli + +import ( + "fmt" + "net" + "sync" + "time" + + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" + + "github.com/tendermint/abci/types" + cmn "github.com/tendermint/tmlibs/common" +) + +var _ Client = (*grpcClient)(nil) + +// A stripped copy of the remoteClient that makes +// synchronous calls using grpc +type grpcClient struct { + cmn.BaseService + mustConnect bool + + client types.ABCIApplicationClient + + mtx sync.Mutex + addr string + err error + resCb func(*types.Request, *types.Response) // listens to all callbacks +} + +func NewGRPCClient(addr string, mustConnect bool) *grpcClient { + cli := &grpcClient{ + addr: addr, + mustConnect: mustConnect, + } + cli.BaseService = *cmn.NewBaseService(nil, "grpcClient", cli) + return cli +} + +func dialerFunc(addr string, timeout time.Duration) (net.Conn, error) { + return cmn.Connect(addr) +} + +func (cli *grpcClient) OnStart() error { + if err := cli.BaseService.OnStart(); err != nil { + return err + } +RETRY_LOOP: + for { + conn, err := grpc.Dial(cli.addr, grpc.WithInsecure(), grpc.WithDialer(dialerFunc)) + if err != nil { + if cli.mustConnect { + return err + } + cli.Logger.Error(fmt.Sprintf("abci.grpcClient failed to connect to %v. Retrying...\n", cli.addr)) + time.Sleep(time.Second * dialRetryIntervalSeconds) + continue RETRY_LOOP + } + + cli.Logger.Info("Dialed server. Waiting for echo.", "addr", cli.addr) + client := types.NewABCIApplicationClient(conn) + + ENSURE_CONNECTED: + for { + _, err := client.Echo(context.Background(), &types.RequestEcho{"hello"}, grpc.FailFast(true)) + if err == nil { + break ENSURE_CONNECTED + } + cli.Logger.Error("Echo failed", "err", err) + time.Sleep(time.Second * echoRetryIntervalSeconds) + } + + cli.client = client + return nil + } +} + +func (cli *grpcClient) OnStop() { + cli.BaseService.OnStop() + cli.mtx.Lock() + defer cli.mtx.Unlock() + // TODO: how to close conn? its not a net.Conn and grpc doesn't expose a Close() + /*if cli.client.conn != nil { + cli.client.conn.Close() + }*/ +} + +func (cli *grpcClient) StopForError(err error) { + cli.mtx.Lock() + if !cli.IsRunning() { + return + } + + if cli.err == nil { + cli.err = err + } + cli.mtx.Unlock() + + cli.Logger.Error(fmt.Sprintf("Stopping abci.grpcClient for error: %v", err.Error())) + cli.Stop() +} + +func (cli *grpcClient) Error() error { + cli.mtx.Lock() + defer cli.mtx.Unlock() + return cli.err +} + +// Set listener for all responses +// NOTE: callback may get internally generated flush responses. +func (cli *grpcClient) SetResponseCallback(resCb Callback) { + cli.mtx.Lock() + defer cli.mtx.Unlock() + cli.resCb = resCb +} + +//---------------------------------------- +// GRPC calls are synchronous, but some callbacks expect to be called asynchronously +// (eg. the mempool expects to be able to lock to remove bad txs from cache). +// To accommodate, we finish each call in its own go-routine, +// which is expensive, but easy - if you want something better, use the socket protocol! +// maybe one day, if people really want it, we use grpc streams, +// but hopefully not :D + +func (cli *grpcClient) EchoAsync(msg string) *ReqRes { + req := types.ToRequestEcho(msg) + res, err := cli.client.Echo(context.Background(), req.GetEcho(), grpc.FailFast(true)) + if err != nil { + cli.StopForError(err) + } + return cli.finishAsyncCall(req, &types.Response{&types.Response_Echo{res}}) +} + +func (cli *grpcClient) FlushAsync() *ReqRes { + req := types.ToRequestFlush() + res, err := cli.client.Flush(context.Background(), req.GetFlush(), grpc.FailFast(true)) + if err != nil { + cli.StopForError(err) + } + return cli.finishAsyncCall(req, &types.Response{&types.Response_Flush{res}}) +} + +func (cli *grpcClient) InfoAsync(params types.RequestInfo) *ReqRes { + req := types.ToRequestInfo(params) + res, err := cli.client.Info(context.Background(), req.GetInfo(), grpc.FailFast(true)) + if err != nil { + cli.StopForError(err) + } + return cli.finishAsyncCall(req, &types.Response{&types.Response_Info{res}}) +} + +func (cli *grpcClient) SetOptionAsync(params types.RequestSetOption) *ReqRes { + req := types.ToRequestSetOption(params) + res, err := cli.client.SetOption(context.Background(), req.GetSetOption(), grpc.FailFast(true)) + if err != nil { + cli.StopForError(err) + } + return cli.finishAsyncCall(req, &types.Response{&types.Response_SetOption{res}}) +} + +func (cli *grpcClient) DeliverTxAsync(tx []byte) *ReqRes { + req := types.ToRequestDeliverTx(tx) + res, err := cli.client.DeliverTx(context.Background(), req.GetDeliverTx(), grpc.FailFast(true)) + if err != nil { + cli.StopForError(err) + } + return cli.finishAsyncCall(req, &types.Response{&types.Response_DeliverTx{res}}) +} + +func (cli *grpcClient) CheckTxAsync(tx []byte) *ReqRes { + req := types.ToRequestCheckTx(tx) + res, err := cli.client.CheckTx(context.Background(), req.GetCheckTx(), grpc.FailFast(true)) + if err != nil { + cli.StopForError(err) + } + return cli.finishAsyncCall(req, &types.Response{&types.Response_CheckTx{res}}) +} + +func (cli *grpcClient) QueryAsync(params types.RequestQuery) *ReqRes { + req := types.ToRequestQuery(params) + res, err := cli.client.Query(context.Background(), req.GetQuery(), grpc.FailFast(true)) + if err != nil { + cli.StopForError(err) + } + return cli.finishAsyncCall(req, &types.Response{&types.Response_Query{res}}) +} + +func (cli *grpcClient) CommitAsync() *ReqRes { + req := types.ToRequestCommit() + res, err := cli.client.Commit(context.Background(), req.GetCommit(), grpc.FailFast(true)) + if err != nil { + cli.StopForError(err) + } + return cli.finishAsyncCall(req, &types.Response{&types.Response_Commit{res}}) +} + +func (cli *grpcClient) InitChainAsync(params types.RequestInitChain) *ReqRes { + req := types.ToRequestInitChain(params) + res, err := cli.client.InitChain(context.Background(), req.GetInitChain(), grpc.FailFast(true)) + if err != nil { + cli.StopForError(err) + } + return cli.finishAsyncCall(req, &types.Response{&types.Response_InitChain{res}}) +} + +func (cli *grpcClient) BeginBlockAsync(params types.RequestBeginBlock) *ReqRes { + req := types.ToRequestBeginBlock(params) + res, err := cli.client.BeginBlock(context.Background(), req.GetBeginBlock(), grpc.FailFast(true)) + if err != nil { + cli.StopForError(err) + } + return cli.finishAsyncCall(req, &types.Response{&types.Response_BeginBlock{res}}) +} + +func (cli *grpcClient) EndBlockAsync(params types.RequestEndBlock) *ReqRes { + req := types.ToRequestEndBlock(params) + res, err := cli.client.EndBlock(context.Background(), req.GetEndBlock(), grpc.FailFast(true)) + if err != nil { + cli.StopForError(err) + } + return cli.finishAsyncCall(req, &types.Response{&types.Response_EndBlock{res}}) +} + +func (cli *grpcClient) finishAsyncCall(req *types.Request, res *types.Response) *ReqRes { + reqres := NewReqRes(req) + reqres.Response = res // Set response + reqres.Done() // Release waiters + reqres.SetDone() // so reqRes.SetCallback will run the callback + + // go routine for callbacks + go func() { + // Notify reqRes listener if set + if cb := reqres.GetCallback(); cb != nil { + cb(res) + } + + // Notify client listener if set + if cli.resCb != nil { + cli.resCb(reqres.Request, res) + } + }() + return reqres +} + +//---------------------------------------- + +func (cli *grpcClient) FlushSync() error { + return nil +} + +func (cli *grpcClient) EchoSync(msg string) (*types.ResponseEcho, error) { + reqres := cli.EchoAsync(msg) + // StopForError should already have been called if error is set + return reqres.Response.GetEcho(), cli.Error() +} + +func (cli *grpcClient) InfoSync(req types.RequestInfo) (*types.ResponseInfo, error) { + reqres := cli.InfoAsync(req) + return reqres.Response.GetInfo(), cli.Error() +} + +func (cli *grpcClient) SetOptionSync(req types.RequestSetOption) (*types.ResponseSetOption, error) { + reqres := cli.SetOptionAsync(req) + return reqres.Response.GetSetOption(), cli.Error() +} + +func (cli *grpcClient) DeliverTxSync(tx []byte) (*types.ResponseDeliverTx, error) { + reqres := cli.DeliverTxAsync(tx) + return reqres.Response.GetDeliverTx(), cli.Error() +} + +func (cli *grpcClient) CheckTxSync(tx []byte) (*types.ResponseCheckTx, error) { + reqres := cli.CheckTxAsync(tx) + return reqres.Response.GetCheckTx(), cli.Error() +} + +func (cli *grpcClient) QuerySync(req types.RequestQuery) (*types.ResponseQuery, error) { + reqres := cli.QueryAsync(req) + return reqres.Response.GetQuery(), cli.Error() +} + +func (cli *grpcClient) CommitSync() (*types.ResponseCommit, error) { + reqres := cli.CommitAsync() + return reqres.Response.GetCommit(), cli.Error() +} + +func (cli *grpcClient) InitChainSync(params types.RequestInitChain) (*types.ResponseInitChain, error) { + reqres := cli.InitChainAsync(params) + return reqres.Response.GetInitChain(), cli.Error() +} + +func (cli *grpcClient) BeginBlockSync(params types.RequestBeginBlock) (*types.ResponseBeginBlock, error) { + reqres := cli.BeginBlockAsync(params) + return reqres.Response.GetBeginBlock(), cli.Error() +} + +func (cli *grpcClient) EndBlockSync(params types.RequestEndBlock) (*types.ResponseEndBlock, error) { + reqres := cli.EndBlockAsync(params) + return reqres.Response.GetEndBlock(), cli.Error() +} diff --git a/abci/client/local_client.go b/abci/client/local_client.go new file mode 100644 index 000000000..64bf5fe08 --- /dev/null +++ b/abci/client/local_client.go @@ -0,0 +1,230 @@ +package abcicli + +import ( + "sync" + + types "github.com/tendermint/abci/types" + cmn "github.com/tendermint/tmlibs/common" +) + +var _ Client = (*localClient)(nil) + +type localClient struct { + cmn.BaseService + mtx *sync.Mutex + types.Application + Callback +} + +func NewLocalClient(mtx *sync.Mutex, app types.Application) *localClient { + if mtx == nil { + mtx = new(sync.Mutex) + } + cli := &localClient{ + mtx: mtx, + Application: app, + } + cli.BaseService = *cmn.NewBaseService(nil, "localClient", cli) + return cli +} + +func (app *localClient) SetResponseCallback(cb Callback) { + app.mtx.Lock() + defer app.mtx.Unlock() + app.Callback = cb +} + +// TODO: change types.Application to include Error()? +func (app *localClient) Error() error { + return nil +} + +func (app *localClient) FlushAsync() *ReqRes { + // Do nothing + return newLocalReqRes(types.ToRequestFlush(), nil) +} + +func (app *localClient) EchoAsync(msg string) *ReqRes { + return app.callback( + types.ToRequestEcho(msg), + types.ToResponseEcho(msg), + ) +} + +func (app *localClient) InfoAsync(req types.RequestInfo) *ReqRes { + app.mtx.Lock() + res := app.Application.Info(req) + app.mtx.Unlock() + return app.callback( + types.ToRequestInfo(req), + types.ToResponseInfo(res), + ) +} + +func (app *localClient) SetOptionAsync(req types.RequestSetOption) *ReqRes { + app.mtx.Lock() + res := app.Application.SetOption(req) + app.mtx.Unlock() + return app.callback( + types.ToRequestSetOption(req), + types.ToResponseSetOption(res), + ) +} + +func (app *localClient) DeliverTxAsync(tx []byte) *ReqRes { + app.mtx.Lock() + res := app.Application.DeliverTx(tx) + app.mtx.Unlock() + return app.callback( + types.ToRequestDeliverTx(tx), + types.ToResponseDeliverTx(res), + ) +} + +func (app *localClient) CheckTxAsync(tx []byte) *ReqRes { + app.mtx.Lock() + res := app.Application.CheckTx(tx) + app.mtx.Unlock() + return app.callback( + types.ToRequestCheckTx(tx), + types.ToResponseCheckTx(res), + ) +} + +func (app *localClient) QueryAsync(req types.RequestQuery) *ReqRes { + app.mtx.Lock() + res := app.Application.Query(req) + app.mtx.Unlock() + return app.callback( + types.ToRequestQuery(req), + types.ToResponseQuery(res), + ) +} + +func (app *localClient) CommitAsync() *ReqRes { + app.mtx.Lock() + res := app.Application.Commit() + app.mtx.Unlock() + return app.callback( + types.ToRequestCommit(), + types.ToResponseCommit(res), + ) +} + +func (app *localClient) InitChainAsync(req types.RequestInitChain) *ReqRes { + app.mtx.Lock() + res := app.Application.InitChain(req) + reqRes := app.callback( + types.ToRequestInitChain(req), + types.ToResponseInitChain(res), + ) + app.mtx.Unlock() + return reqRes +} + +func (app *localClient) BeginBlockAsync(req types.RequestBeginBlock) *ReqRes { + app.mtx.Lock() + res := app.Application.BeginBlock(req) + app.mtx.Unlock() + return app.callback( + types.ToRequestBeginBlock(req), + types.ToResponseBeginBlock(res), + ) +} + +func (app *localClient) EndBlockAsync(req types.RequestEndBlock) *ReqRes { + app.mtx.Lock() + res := app.Application.EndBlock(req) + app.mtx.Unlock() + return app.callback( + types.ToRequestEndBlock(req), + types.ToResponseEndBlock(res), + ) +} + +//------------------------------------------------------- + +func (app *localClient) FlushSync() error { + return nil +} + +func (app *localClient) EchoSync(msg string) (*types.ResponseEcho, error) { + return &types.ResponseEcho{msg}, nil +} + +func (app *localClient) InfoSync(req types.RequestInfo) (*types.ResponseInfo, error) { + app.mtx.Lock() + res := app.Application.Info(req) + app.mtx.Unlock() + return &res, nil +} + +func (app *localClient) SetOptionSync(req types.RequestSetOption) (*types.ResponseSetOption, error) { + app.mtx.Lock() + res := app.Application.SetOption(req) + app.mtx.Unlock() + return &res, nil +} + +func (app *localClient) DeliverTxSync(tx []byte) (*types.ResponseDeliverTx, error) { + app.mtx.Lock() + res := app.Application.DeliverTx(tx) + app.mtx.Unlock() + return &res, nil +} + +func (app *localClient) CheckTxSync(tx []byte) (*types.ResponseCheckTx, error) { + app.mtx.Lock() + res := app.Application.CheckTx(tx) + app.mtx.Unlock() + return &res, nil +} + +func (app *localClient) QuerySync(req types.RequestQuery) (*types.ResponseQuery, error) { + app.mtx.Lock() + res := app.Application.Query(req) + app.mtx.Unlock() + return &res, nil +} + +func (app *localClient) CommitSync() (*types.ResponseCommit, error) { + app.mtx.Lock() + res := app.Application.Commit() + app.mtx.Unlock() + return &res, nil +} + +func (app *localClient) InitChainSync(req types.RequestInitChain) (*types.ResponseInitChain, error) { + app.mtx.Lock() + res := app.Application.InitChain(req) + app.mtx.Unlock() + return &res, nil +} + +func (app *localClient) BeginBlockSync(req types.RequestBeginBlock) (*types.ResponseBeginBlock, error) { + app.mtx.Lock() + res := app.Application.BeginBlock(req) + app.mtx.Unlock() + return &res, nil +} + +func (app *localClient) EndBlockSync(req types.RequestEndBlock) (*types.ResponseEndBlock, error) { + app.mtx.Lock() + res := app.Application.EndBlock(req) + app.mtx.Unlock() + return &res, nil +} + +//------------------------------------------------------- + +func (app *localClient) callback(req *types.Request, res *types.Response) *ReqRes { + app.Callback(req, res) + return newLocalReqRes(req, res) +} + +func newLocalReqRes(req *types.Request, res *types.Response) *ReqRes { + reqRes := NewReqRes(req) + reqRes.Response = res + reqRes.SetDone() + return reqRes +} diff --git a/abci/client/socket_client.go b/abci/client/socket_client.go new file mode 100644 index 000000000..5c0101685 --- /dev/null +++ b/abci/client/socket_client.go @@ -0,0 +1,399 @@ +package abcicli + +import ( + "bufio" + "container/list" + "errors" + "fmt" + "net" + "reflect" + "sync" + "time" + + "github.com/tendermint/abci/types" + cmn "github.com/tendermint/tmlibs/common" +) + +const reqQueueSize = 256 // TODO make configurable +// const maxResponseSize = 1048576 // 1MB TODO make configurable +const flushThrottleMS = 20 // Don't wait longer than... + +var _ Client = (*socketClient)(nil) + +// This is goroutine-safe, but users should beware that +// the application in general is not meant to be interfaced +// with concurrent callers. +type socketClient struct { + cmn.BaseService + + reqQueue chan *ReqRes + flushTimer *cmn.ThrottleTimer + mustConnect bool + + mtx sync.Mutex + addr string + conn net.Conn + err error + reqSent *list.List + resCb func(*types.Request, *types.Response) // listens to all callbacks + +} + +func NewSocketClient(addr string, mustConnect bool) *socketClient { + cli := &socketClient{ + reqQueue: make(chan *ReqRes, reqQueueSize), + flushTimer: cmn.NewThrottleTimer("socketClient", flushThrottleMS), + mustConnect: mustConnect, + + addr: addr, + reqSent: list.New(), + resCb: nil, + } + cli.BaseService = *cmn.NewBaseService(nil, "socketClient", cli) + return cli +} + +func (cli *socketClient) OnStart() error { + if err := cli.BaseService.OnStart(); err != nil { + return err + } + + var err error + var conn net.Conn +RETRY_LOOP: + for { + conn, err = cmn.Connect(cli.addr) + if err != nil { + if cli.mustConnect { + return err + } + cli.Logger.Error(fmt.Sprintf("abci.socketClient failed to connect to %v. Retrying...", cli.addr)) + time.Sleep(time.Second * dialRetryIntervalSeconds) + continue RETRY_LOOP + } + cli.conn = conn + + go cli.sendRequestsRoutine(conn) + go cli.recvResponseRoutine(conn) + + return nil + } +} + +func (cli *socketClient) OnStop() { + cli.BaseService.OnStop() + + cli.mtx.Lock() + defer cli.mtx.Unlock() + if cli.conn != nil { + cli.conn.Close() + } + + cli.flushQueue() +} + +// Stop the client and set the error +func (cli *socketClient) StopForError(err error) { + if !cli.IsRunning() { + return + } + + cli.mtx.Lock() + if cli.err == nil { + cli.err = err + } + cli.mtx.Unlock() + + cli.Logger.Error(fmt.Sprintf("Stopping abci.socketClient for error: %v", err.Error())) + cli.Stop() +} + +func (cli *socketClient) Error() error { + cli.mtx.Lock() + defer cli.mtx.Unlock() + return cli.err +} + +// Set listener for all responses +// NOTE: callback may get internally generated flush responses. +func (cli *socketClient) SetResponseCallback(resCb Callback) { + cli.mtx.Lock() + defer cli.mtx.Unlock() + cli.resCb = resCb +} + +//---------------------------------------- + +func (cli *socketClient) sendRequestsRoutine(conn net.Conn) { + + w := bufio.NewWriter(conn) + for { + select { + case <-cli.flushTimer.Ch: + select { + case cli.reqQueue <- NewReqRes(types.ToRequestFlush()): + default: + // Probably will fill the buffer, or retry later. + } + case <-cli.Quit(): + return + case reqres := <-cli.reqQueue: + cli.willSendReq(reqres) + err := types.WriteMessage(reqres.Request, w) + if err != nil { + cli.StopForError(fmt.Errorf("Error writing msg: %v", err)) + return + } + // cli.Logger.Debug("Sent request", "requestType", reflect.TypeOf(reqres.Request), "request", reqres.Request) + if _, ok := reqres.Request.Value.(*types.Request_Flush); ok { + err = w.Flush() + if err != nil { + cli.StopForError(fmt.Errorf("Error flushing writer: %v", err)) + return + } + } + } + } +} + +func (cli *socketClient) recvResponseRoutine(conn net.Conn) { + + r := bufio.NewReader(conn) // Buffer reads + for { + var res = &types.Response{} + err := types.ReadMessage(r, res) + if err != nil { + cli.StopForError(err) + return + } + switch r := res.Value.(type) { + case *types.Response_Exception: + // XXX After setting cli.err, release waiters (e.g. reqres.Done()) + cli.StopForError(errors.New(r.Exception.Error)) + return + default: + // cli.Logger.Debug("Received response", "responseType", reflect.TypeOf(res), "response", res) + err := cli.didRecvResponse(res) + if err != nil { + cli.StopForError(err) + return + } + } + } +} + +func (cli *socketClient) willSendReq(reqres *ReqRes) { + cli.mtx.Lock() + defer cli.mtx.Unlock() + cli.reqSent.PushBack(reqres) +} + +func (cli *socketClient) didRecvResponse(res *types.Response) error { + cli.mtx.Lock() + defer cli.mtx.Unlock() + + // Get the first ReqRes + next := cli.reqSent.Front() + if next == nil { + return fmt.Errorf("Unexpected result type %v when nothing expected", reflect.TypeOf(res.Value)) + } + reqres := next.Value.(*ReqRes) + if !resMatchesReq(reqres.Request, res) { + return fmt.Errorf("Unexpected result type %v when response to %v expected", + reflect.TypeOf(res.Value), reflect.TypeOf(reqres.Request.Value)) + } + + reqres.Response = res // Set response + reqres.Done() // Release waiters + cli.reqSent.Remove(next) // Pop first item from linked list + + // Notify reqRes listener if set + if cb := reqres.GetCallback(); cb != nil { + cb(res) + } + + // Notify client listener if set + if cli.resCb != nil { + cli.resCb(reqres.Request, res) + } + + return nil +} + +//---------------------------------------- + +func (cli *socketClient) EchoAsync(msg string) *ReqRes { + return cli.queueRequest(types.ToRequestEcho(msg)) +} + +func (cli *socketClient) FlushAsync() *ReqRes { + return cli.queueRequest(types.ToRequestFlush()) +} + +func (cli *socketClient) InfoAsync(req types.RequestInfo) *ReqRes { + return cli.queueRequest(types.ToRequestInfo(req)) +} + +func (cli *socketClient) SetOptionAsync(req types.RequestSetOption) *ReqRes { + return cli.queueRequest(types.ToRequestSetOption(req)) +} + +func (cli *socketClient) DeliverTxAsync(tx []byte) *ReqRes { + return cli.queueRequest(types.ToRequestDeliverTx(tx)) +} + +func (cli *socketClient) CheckTxAsync(tx []byte) *ReqRes { + return cli.queueRequest(types.ToRequestCheckTx(tx)) +} + +func (cli *socketClient) QueryAsync(req types.RequestQuery) *ReqRes { + return cli.queueRequest(types.ToRequestQuery(req)) +} + +func (cli *socketClient) CommitAsync() *ReqRes { + return cli.queueRequest(types.ToRequestCommit()) +} + +func (cli *socketClient) InitChainAsync(req types.RequestInitChain) *ReqRes { + return cli.queueRequest(types.ToRequestInitChain(req)) +} + +func (cli *socketClient) BeginBlockAsync(req types.RequestBeginBlock) *ReqRes { + return cli.queueRequest(types.ToRequestBeginBlock(req)) +} + +func (cli *socketClient) EndBlockAsync(req types.RequestEndBlock) *ReqRes { + return cli.queueRequest(types.ToRequestEndBlock(req)) +} + +//---------------------------------------- + +func (cli *socketClient) FlushSync() error { + reqRes := cli.queueRequest(types.ToRequestFlush()) + if err := cli.Error(); err != nil { + return err + } + reqRes.Wait() // NOTE: if we don't flush the queue, its possible to get stuck here + return cli.Error() +} + +func (cli *socketClient) EchoSync(msg string) (*types.ResponseEcho, error) { + reqres := cli.queueRequest(types.ToRequestEcho(msg)) + cli.FlushSync() + return reqres.Response.GetEcho(), cli.Error() +} + +func (cli *socketClient) InfoSync(req types.RequestInfo) (*types.ResponseInfo, error) { + reqres := cli.queueRequest(types.ToRequestInfo(req)) + cli.FlushSync() + return reqres.Response.GetInfo(), cli.Error() +} + +func (cli *socketClient) SetOptionSync(req types.RequestSetOption) (*types.ResponseSetOption, error) { + reqres := cli.queueRequest(types.ToRequestSetOption(req)) + cli.FlushSync() + return reqres.Response.GetSetOption(), cli.Error() +} + +func (cli *socketClient) DeliverTxSync(tx []byte) (*types.ResponseDeliverTx, error) { + reqres := cli.queueRequest(types.ToRequestDeliverTx(tx)) + cli.FlushSync() + return reqres.Response.GetDeliverTx(), cli.Error() +} + +func (cli *socketClient) CheckTxSync(tx []byte) (*types.ResponseCheckTx, error) { + reqres := cli.queueRequest(types.ToRequestCheckTx(tx)) + cli.FlushSync() + return reqres.Response.GetCheckTx(), cli.Error() +} + +func (cli *socketClient) QuerySync(req types.RequestQuery) (*types.ResponseQuery, error) { + reqres := cli.queueRequest(types.ToRequestQuery(req)) + cli.FlushSync() + return reqres.Response.GetQuery(), cli.Error() +} + +func (cli *socketClient) CommitSync() (*types.ResponseCommit, error) { + reqres := cli.queueRequest(types.ToRequestCommit()) + cli.FlushSync() + return reqres.Response.GetCommit(), cli.Error() +} + +func (cli *socketClient) InitChainSync(req types.RequestInitChain) (*types.ResponseInitChain, error) { + reqres := cli.queueRequest(types.ToRequestInitChain(req)) + cli.FlushSync() + return reqres.Response.GetInitChain(), cli.Error() +} + +func (cli *socketClient) BeginBlockSync(req types.RequestBeginBlock) (*types.ResponseBeginBlock, error) { + reqres := cli.queueRequest(types.ToRequestBeginBlock(req)) + cli.FlushSync() + return reqres.Response.GetBeginBlock(), cli.Error() +} + +func (cli *socketClient) EndBlockSync(req types.RequestEndBlock) (*types.ResponseEndBlock, error) { + reqres := cli.queueRequest(types.ToRequestEndBlock(req)) + cli.FlushSync() + return reqres.Response.GetEndBlock(), cli.Error() +} + +//---------------------------------------- + +func (cli *socketClient) queueRequest(req *types.Request) *ReqRes { + reqres := NewReqRes(req) + + // TODO: set cli.err if reqQueue times out + cli.reqQueue <- reqres + + // Maybe auto-flush, or unset auto-flush + switch req.Value.(type) { + case *types.Request_Flush: + cli.flushTimer.Unset() + default: + cli.flushTimer.Set() + } + + return reqres +} + +func (cli *socketClient) flushQueue() { +LOOP: + for { + select { + case reqres := <-cli.reqQueue: + reqres.Done() + default: + break LOOP + } + } +} + +//---------------------------------------- + +func resMatchesReq(req *types.Request, res *types.Response) (ok bool) { + switch req.Value.(type) { + case *types.Request_Echo: + _, ok = res.Value.(*types.Response_Echo) + case *types.Request_Flush: + _, ok = res.Value.(*types.Response_Flush) + case *types.Request_Info: + _, ok = res.Value.(*types.Response_Info) + case *types.Request_SetOption: + _, ok = res.Value.(*types.Response_SetOption) + case *types.Request_DeliverTx: + _, ok = res.Value.(*types.Response_DeliverTx) + case *types.Request_CheckTx: + _, ok = res.Value.(*types.Response_CheckTx) + case *types.Request_Commit: + _, ok = res.Value.(*types.Response_Commit) + case *types.Request_Query: + _, ok = res.Value.(*types.Response_Query) + case *types.Request_InitChain: + _, ok = res.Value.(*types.Response_InitChain) + case *types.Request_BeginBlock: + _, ok = res.Value.(*types.Response_BeginBlock) + case *types.Request_EndBlock: + _, ok = res.Value.(*types.Response_EndBlock) + } + return ok +} diff --git a/abci/client/socket_client_test.go b/abci/client/socket_client_test.go new file mode 100644 index 000000000..814d5a68c --- /dev/null +++ b/abci/client/socket_client_test.go @@ -0,0 +1,28 @@ +package abcicli_test + +import ( + "errors" + "testing" + "time" + + "github.com/tendermint/abci/client" +) + +func TestSocketClientStopForErrorDeadlock(t *testing.T) { + c := abcicli.NewSocketClient(":80", false) + err := errors.New("foo-tendermint") + + // See Issue https://github.com/tendermint/abci/issues/114 + doneChan := make(chan bool) + go func() { + defer close(doneChan) + c.StopForError(err) + c.StopForError(err) + }() + + select { + case <-doneChan: + case <-time.After(time.Second * 4): + t.Fatalf("Test took too long, potential deadlock still exists") + } +} diff --git a/abci/cmd/abci-cli/abci-cli.go b/abci/cmd/abci-cli/abci-cli.go new file mode 100644 index 000000000..b773c990b --- /dev/null +++ b/abci/cmd/abci-cli/abci-cli.go @@ -0,0 +1,765 @@ +package main + +import ( + "bufio" + "encoding/hex" + "errors" + "fmt" + "io" + "os" + "strings" + + "github.com/spf13/cobra" + + cmn "github.com/tendermint/tmlibs/common" + "github.com/tendermint/tmlibs/log" + + abcicli "github.com/tendermint/abci/client" + "github.com/tendermint/abci/example/code" + "github.com/tendermint/abci/example/counter" + "github.com/tendermint/abci/example/kvstore" + "github.com/tendermint/abci/server" + servertest "github.com/tendermint/abci/tests/server" + "github.com/tendermint/abci/types" + "github.com/tendermint/abci/version" +) + +// client is a global variable so it can be reused by the console +var ( + client abcicli.Client + logger log.Logger +) + +// flags +var ( + // global + flagAddress string + flagAbci string + flagVerbose bool // for the println output + flagLogLevel string // for the logger + + // query + flagPath string + flagHeight int + flagProve bool + + // counter + flagSerial bool + + // kvstore + flagPersist string +) + +var RootCmd = &cobra.Command{ + Use: "abci-cli", + Short: "the ABCI CLI tool wraps an ABCI client", + Long: "the ABCI CLI tool wraps an ABCI client and is used for testing ABCI servers", + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + + switch cmd.Use { + case "counter", "kvstore", "dummy": // for the examples apps, don't pre-run + return nil + case "version": // skip running for version command + return nil + } + + if logger == nil { + allowLevel, err := log.AllowLevel(flagLogLevel) + if err != nil { + return err + } + logger = log.NewFilter(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), allowLevel) + } + if client == nil { + var err error + client, err = abcicli.NewClient(flagAddress, flagAbci, false) + if err != nil { + return err + } + client.SetLogger(logger.With("module", "abci-client")) + if err := client.Start(); err != nil { + return err + } + } + return nil + }, +} + +// Structure for data passed to print response. +type response struct { + // generic abci response + Data []byte + Code uint32 + Info string + Log string + + Query *queryResponse +} + +type queryResponse struct { + Key []byte + Value []byte + Height int64 + Proof []byte +} + +func Execute() error { + addGlobalFlags() + addCommands() + return RootCmd.Execute() +} + +func addGlobalFlags() { + RootCmd.PersistentFlags().StringVarP(&flagAddress, "address", "", "tcp://0.0.0.0:26658", "address of application socket") + RootCmd.PersistentFlags().StringVarP(&flagAbci, "abci", "", "socket", "either socket or grpc") + RootCmd.PersistentFlags().BoolVarP(&flagVerbose, "verbose", "v", false, "print the command and results as if it were a console session") + RootCmd.PersistentFlags().StringVarP(&flagLogLevel, "log_level", "", "debug", "set the logger level") +} + +func addQueryFlags() { + queryCmd.PersistentFlags().StringVarP(&flagPath, "path", "", "/store", "path to prefix query with") + queryCmd.PersistentFlags().IntVarP(&flagHeight, "height", "", 0, "height to query the blockchain at") + queryCmd.PersistentFlags().BoolVarP(&flagProve, "prove", "", false, "whether or not to return a merkle proof of the query result") +} + +func addCounterFlags() { + counterCmd.PersistentFlags().BoolVarP(&flagSerial, "serial", "", false, "enforce incrementing (serial) transactions") +} + +func addDummyFlags() { + dummyCmd.PersistentFlags().StringVarP(&flagPersist, "persist", "", "", "directory to use for a database") +} + +func addKVStoreFlags() { + kvstoreCmd.PersistentFlags().StringVarP(&flagPersist, "persist", "", "", "directory to use for a database") +} + +func addCommands() { + RootCmd.AddCommand(batchCmd) + RootCmd.AddCommand(consoleCmd) + RootCmd.AddCommand(echoCmd) + RootCmd.AddCommand(infoCmd) + RootCmd.AddCommand(setOptionCmd) + RootCmd.AddCommand(deliverTxCmd) + RootCmd.AddCommand(checkTxCmd) + RootCmd.AddCommand(commitCmd) + RootCmd.AddCommand(versionCmd) + RootCmd.AddCommand(testCmd) + addQueryFlags() + RootCmd.AddCommand(queryCmd) + + // examples + addCounterFlags() + RootCmd.AddCommand(counterCmd) + // deprecated, left for backwards compatibility + addDummyFlags() + RootCmd.AddCommand(dummyCmd) + // replaces dummy, see issue #196 + addKVStoreFlags() + RootCmd.AddCommand(kvstoreCmd) +} + +var batchCmd = &cobra.Command{ + Use: "batch", + Short: "run a batch of abci commands against an application", + Long: `run a batch of abci commands against an application + +This command is run by piping in a file containing a series of commands +you'd like to run: + + abci-cli batch < example.file + +where example.file looks something like: + + set_option serial on + check_tx 0x00 + check_tx 0xff + deliver_tx 0x00 + check_tx 0x00 + deliver_tx 0x01 + deliver_tx 0x04 + info +`, + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + return cmdBatch(cmd, args) + }, +} + +var consoleCmd = &cobra.Command{ + Use: "console", + Short: "start an interactive ABCI console for multiple commands", + Long: `start an interactive ABCI console for multiple commands + +This command opens an interactive console for running any of the other commands +without opening a new connection each time +`, + Args: cobra.ExactArgs(0), + ValidArgs: []string{"echo", "info", "set_option", "deliver_tx", "check_tx", "commit", "query"}, + RunE: func(cmd *cobra.Command, args []string) error { + return cmdConsole(cmd, args) + }, +} + +var echoCmd = &cobra.Command{ + Use: "echo", + Short: "have the application echo a message", + Long: "have the application echo a message", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + return cmdEcho(cmd, args) + }, +} +var infoCmd = &cobra.Command{ + Use: "info", + Short: "get some info about the application", + Long: "get some info about the application", + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + return cmdInfo(cmd, args) + }, +} +var setOptionCmd = &cobra.Command{ + Use: "set_option", + Short: "set an option on the application", + Long: "set an option on the application", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + return cmdSetOption(cmd, args) + }, +} + +var deliverTxCmd = &cobra.Command{ + Use: "deliver_tx", + Short: "deliver a new transaction to the application", + Long: "deliver a new transaction to the application", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + return cmdDeliverTx(cmd, args) + }, +} + +var checkTxCmd = &cobra.Command{ + Use: "check_tx", + Short: "validate a transaction", + Long: "validate a transaction", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + return cmdCheckTx(cmd, args) + }, +} + +var commitCmd = &cobra.Command{ + Use: "commit", + Short: "commit the application state and return the Merkle root hash", + Long: "commit the application state and return the Merkle root hash", + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + return cmdCommit(cmd, args) + }, +} + +var versionCmd = &cobra.Command{ + Use: "version", + Short: "print ABCI console version", + Long: "print ABCI console version", + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Println(version.Version) + return nil + }, +} + +var queryCmd = &cobra.Command{ + Use: "query", + Short: "query the application state", + Long: "query the application state", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + return cmdQuery(cmd, args) + }, +} + +var counterCmd = &cobra.Command{ + Use: "counter", + Short: "ABCI demo example", + Long: "ABCI demo example", + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + return cmdCounter(cmd, args) + }, +} + +// deprecated, left for backwards compatibility +var dummyCmd = &cobra.Command{ + Use: "dummy", + Deprecated: "use: [abci-cli kvstore] instead", + Short: "ABCI demo example", + Long: "ABCI demo example", + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + return cmdKVStore(cmd, args) + }, +} + +var kvstoreCmd = &cobra.Command{ + Use: "kvstore", + Short: "ABCI demo example", + Long: "ABCI demo example", + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + return cmdKVStore(cmd, args) + }, +} + +var testCmd = &cobra.Command{ + Use: "test", + Short: "run integration tests", + Long: "run integration tests", + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + return cmdTest(cmd, args) + }, +} + +// Generates new Args array based off of previous call args to maintain flag persistence +func persistentArgs(line []byte) []string { + + // generate the arguments to run from original os.Args + // to maintain flag arguments + args := os.Args + args = args[:len(args)-1] // remove the previous command argument + + if len(line) > 0 { // prevents introduction of extra space leading to argument parse errors + args = append(args, strings.Split(string(line), " ")...) + } + return args +} + +//-------------------------------------------------------------------------------- + +func compose(fs []func() error) error { + if len(fs) == 0 { + return nil + } else { + err := fs[0]() + if err == nil { + return compose(fs[1:]) + } else { + return err + } + } +} + +func cmdTest(cmd *cobra.Command, args []string) error { + return compose( + []func() error{ + func() error { return servertest.InitChain(client) }, + func() error { return servertest.SetOption(client, "serial", "on") }, + func() error { return servertest.Commit(client, nil) }, + func() error { return servertest.DeliverTx(client, []byte("abc"), code.CodeTypeBadNonce, nil) }, + func() error { return servertest.Commit(client, nil) }, + func() error { return servertest.DeliverTx(client, []byte{0x00}, code.CodeTypeOK, nil) }, + func() error { return servertest.Commit(client, []byte{0, 0, 0, 0, 0, 0, 0, 1}) }, + func() error { return servertest.DeliverTx(client, []byte{0x00}, code.CodeTypeBadNonce, nil) }, + func() error { return servertest.DeliverTx(client, []byte{0x01}, code.CodeTypeOK, nil) }, + func() error { return servertest.DeliverTx(client, []byte{0x00, 0x02}, code.CodeTypeOK, nil) }, + func() error { return servertest.DeliverTx(client, []byte{0x00, 0x03}, code.CodeTypeOK, nil) }, + func() error { return servertest.DeliverTx(client, []byte{0x00, 0x00, 0x04}, code.CodeTypeOK, nil) }, + func() error { + return servertest.DeliverTx(client, []byte{0x00, 0x00, 0x06}, code.CodeTypeBadNonce, nil) + }, + func() error { return servertest.Commit(client, []byte{0, 0, 0, 0, 0, 0, 0, 5}) }, + }) +} + +func cmdBatch(cmd *cobra.Command, args []string) error { + bufReader := bufio.NewReader(os.Stdin) + for { + + line, more, err := bufReader.ReadLine() + if more { + return errors.New("Input line is too long") + } else if err == io.EOF { + break + } else if len(line) == 0 { + continue + } else if err != nil { + return err + } + + cmdArgs := persistentArgs(line) + if err := muxOnCommands(cmd, cmdArgs); err != nil { + return err + } + fmt.Println() + } + return nil +} + +func cmdConsole(cmd *cobra.Command, args []string) error { + for { + fmt.Printf("> ") + bufReader := bufio.NewReader(os.Stdin) + line, more, err := bufReader.ReadLine() + if more { + return errors.New("Input is too long") + } else if err != nil { + return err + } + + pArgs := persistentArgs(line) + if err := muxOnCommands(cmd, pArgs); err != nil { + return err + } + } + return nil +} + +func muxOnCommands(cmd *cobra.Command, pArgs []string) error { + if len(pArgs) < 2 { + return errors.New("expecting persistent args of the form: abci-cli [command] <...>") + } + + // TODO: this parsing is fragile + args := []string{} + for i := 0; i < len(pArgs); i++ { + arg := pArgs[i] + + // check for flags + if strings.HasPrefix(arg, "-") { + // if it has an equal, we can just skip + if strings.Contains(arg, "=") { + continue + } + // if its a boolean, we can just skip + _, err := cmd.Flags().GetBool(strings.TrimLeft(arg, "-")) + if err == nil { + continue + } + + // otherwise, we need to skip the next one too + i += 1 + continue + } + + // append the actual arg + args = append(args, arg) + } + var subCommand string + var actualArgs []string + if len(args) > 1 { + subCommand = args[1] + } + if len(args) > 2 { + actualArgs = args[2:] + } + cmd.Use = subCommand // for later print statements ... + + switch strings.ToLower(subCommand) { + case "check_tx": + return cmdCheckTx(cmd, actualArgs) + case "commit": + return cmdCommit(cmd, actualArgs) + case "deliver_tx": + return cmdDeliverTx(cmd, actualArgs) + case "echo": + return cmdEcho(cmd, actualArgs) + case "info": + return cmdInfo(cmd, actualArgs) + case "query": + return cmdQuery(cmd, actualArgs) + case "set_option": + return cmdSetOption(cmd, actualArgs) + default: + return cmdUnimplemented(cmd, pArgs) + } +} + +func cmdUnimplemented(cmd *cobra.Command, args []string) error { + // TODO: Print out all the sub-commands available + msg := "unimplemented command" + if err := cmd.Help(); err != nil { + msg = err.Error() + } + if len(args) > 0 { + msg += fmt.Sprintf(" args: [%s]", strings.Join(args, " ")) + } + printResponse(cmd, args, response{ + Code: codeBad, + Log: msg, + }) + return nil +} + +// Have the application echo a message +func cmdEcho(cmd *cobra.Command, args []string) error { + msg := "" + if len(args) > 0 { + msg = args[0] + } + res, err := client.EchoSync(msg) + if err != nil { + return err + } + printResponse(cmd, args, response{ + Data: []byte(res.Message), + }) + return nil +} + +// Get some info from the application +func cmdInfo(cmd *cobra.Command, args []string) error { + var version string + if len(args) == 1 { + version = args[0] + } + res, err := client.InfoSync(types.RequestInfo{version}) + if err != nil { + return err + } + printResponse(cmd, args, response{ + Data: []byte(res.Data), + }) + return nil +} + +const codeBad uint32 = 10 + +// Set an option on the application +func cmdSetOption(cmd *cobra.Command, args []string) error { + if len(args) < 2 { + printResponse(cmd, args, response{ + Code: codeBad, + Log: "want at least arguments of the form: ", + }) + return nil + } + + key, val := args[0], args[1] + _, err := client.SetOptionSync(types.RequestSetOption{key, val}) + if err != nil { + return err + } + printResponse(cmd, args, response{Log: "OK (SetOption doesn't return anything.)"}) // NOTE: Nothing to show... + return nil +} + +// Append a new tx to application +func cmdDeliverTx(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + printResponse(cmd, args, response{ + Code: codeBad, + Log: "want the tx", + }) + return nil + } + txBytes, err := stringOrHexToBytes(args[0]) + if err != nil { + return err + } + res, err := client.DeliverTxSync(txBytes) + if err != nil { + return err + } + printResponse(cmd, args, response{ + Code: res.Code, + Data: res.Data, + Info: res.Info, + Log: res.Log, + }) + return nil +} + +// Validate a tx +func cmdCheckTx(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + printResponse(cmd, args, response{ + Code: codeBad, + Info: "want the tx", + }) + return nil + } + txBytes, err := stringOrHexToBytes(args[0]) + if err != nil { + return err + } + res, err := client.CheckTxSync(txBytes) + if err != nil { + return err + } + printResponse(cmd, args, response{ + Code: res.Code, + Data: res.Data, + Info: res.Info, + Log: res.Log, + }) + return nil +} + +// Get application Merkle root hash +func cmdCommit(cmd *cobra.Command, args []string) error { + res, err := client.CommitSync() + if err != nil { + return err + } + printResponse(cmd, args, response{ + Data: res.Data, + }) + return nil +} + +// Query application state +func cmdQuery(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + printResponse(cmd, args, response{ + Code: codeBad, + Info: "want the query", + Log: "", + }) + return nil + } + queryBytes, err := stringOrHexToBytes(args[0]) + if err != nil { + return err + } + + resQuery, err := client.QuerySync(types.RequestQuery{ + Data: queryBytes, + Path: flagPath, + Height: int64(flagHeight), + Prove: flagProve, + }) + if err != nil { + return err + } + printResponse(cmd, args, response{ + Code: resQuery.Code, + Info: resQuery.Info, + Log: resQuery.Log, + Query: &queryResponse{ + Key: resQuery.Key, + Value: resQuery.Value, + Height: resQuery.Height, + Proof: resQuery.Proof, + }, + }) + return nil +} + +func cmdCounter(cmd *cobra.Command, args []string) error { + + app := counter.NewCounterApplication(flagSerial) + + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) + + // Start the listener + srv, err := server.NewServer(flagAddress, flagAbci, app) + if err != nil { + return err + } + srv.SetLogger(logger.With("module", "abci-server")) + if err := srv.Start(); err != nil { + return err + } + + // Wait forever + cmn.TrapSignal(func() { + // Cleanup + srv.Stop() + }) + return nil +} + +func cmdKVStore(cmd *cobra.Command, args []string) error { + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) + + // Create the application - in memory or persisted to disk + var app types.Application + if flagPersist == "" { + app = kvstore.NewKVStoreApplication() + } else { + app = kvstore.NewPersistentKVStoreApplication(flagPersist) + app.(*kvstore.PersistentKVStoreApplication).SetLogger(logger.With("module", "kvstore")) + } + + // Start the listener + srv, err := server.NewServer(flagAddress, flagAbci, app) + if err != nil { + return err + } + srv.SetLogger(logger.With("module", "abci-server")) + if err := srv.Start(); err != nil { + return err + } + + // Wait forever + cmn.TrapSignal(func() { + // Cleanup + srv.Stop() + }) + return nil +} + +//-------------------------------------------------------------------------------- + +func printResponse(cmd *cobra.Command, args []string, rsp response) { + + if flagVerbose { + fmt.Println(">", cmd.Use, strings.Join(args, " ")) + } + + // Always print the status code. + if rsp.Code == types.CodeTypeOK { + fmt.Printf("-> code: OK\n") + } else { + fmt.Printf("-> code: %d\n", rsp.Code) + + } + + if len(rsp.Data) != 0 { + // Do no print this line when using the commit command + // because the string comes out as gibberish + if cmd.Use != "commit" { + fmt.Printf("-> data: %s\n", rsp.Data) + } + fmt.Printf("-> data.hex: 0x%X\n", rsp.Data) + } + if rsp.Log != "" { + fmt.Printf("-> log: %s\n", rsp.Log) + } + + if rsp.Query != nil { + fmt.Printf("-> height: %d\n", rsp.Query.Height) + if rsp.Query.Key != nil { + fmt.Printf("-> key: %s\n", rsp.Query.Key) + fmt.Printf("-> key.hex: %X\n", rsp.Query.Key) + } + if rsp.Query.Value != nil { + fmt.Printf("-> value: %s\n", rsp.Query.Value) + fmt.Printf("-> value.hex: %X\n", rsp.Query.Value) + } + if rsp.Query.Proof != nil { + fmt.Printf("-> proof: %X\n", rsp.Query.Proof) + } + } +} + +// NOTE: s is interpreted as a string unless prefixed with 0x +func stringOrHexToBytes(s string) ([]byte, error) { + if len(s) > 2 && strings.ToLower(s[:2]) == "0x" { + b, err := hex.DecodeString(s[2:]) + if err != nil { + err = fmt.Errorf("Error decoding hex argument: %s", err.Error()) + return nil, err + } + return b, nil + } + + if !strings.HasPrefix(s, "\"") || !strings.HasSuffix(s, "\"") { + err := fmt.Errorf("Invalid string arg: \"%s\". Must be quoted or a \"0x\"-prefixed hex string", s) + return nil, err + } + + return []byte(s[1 : len(s)-1]), nil +} diff --git a/abci/cmd/abci-cli/main.go b/abci/cmd/abci-cli/main.go new file mode 100644 index 000000000..a927e7ed8 --- /dev/null +++ b/abci/cmd/abci-cli/main.go @@ -0,0 +1,14 @@ +package main + +import ( + "fmt" + "os" +) + +func main() { + err := Execute() + if err != nil { + fmt.Print(err) + os.Exit(1) + } +} diff --git a/abci/example/code/code.go b/abci/example/code/code.go new file mode 100644 index 000000000..94e9d015e --- /dev/null +++ b/abci/example/code/code.go @@ -0,0 +1,9 @@ +package code + +// Return codes for the examples +const ( + CodeTypeOK uint32 = 0 + CodeTypeEncodingError uint32 = 1 + CodeTypeBadNonce uint32 = 2 + CodeTypeUnauthorized uint32 = 3 +) diff --git a/abci/example/counter/counter.go b/abci/example/counter/counter.go new file mode 100644 index 000000000..a6d5df6ab --- /dev/null +++ b/abci/example/counter/counter.go @@ -0,0 +1,104 @@ +package counter + +import ( + "encoding/binary" + "fmt" + + "github.com/tendermint/abci/example/code" + "github.com/tendermint/abci/types" + cmn "github.com/tendermint/tmlibs/common" +) + +type CounterApplication struct { + types.BaseApplication + + hashCount int + txCount int + serial bool +} + +func NewCounterApplication(serial bool) *CounterApplication { + return &CounterApplication{serial: serial} +} + +func (app *CounterApplication) Info(req types.RequestInfo) types.ResponseInfo { + return types.ResponseInfo{Data: cmn.Fmt("{\"hashes\":%v,\"txs\":%v}", app.hashCount, app.txCount)} +} + +func (app *CounterApplication) SetOption(req types.RequestSetOption) types.ResponseSetOption { + key, value := req.Key, req.Value + if key == "serial" && value == "on" { + app.serial = true + } else { + /* + TODO Panic and have the ABCI server pass an exception. + The client can call SetOptionSync() and get an `error`. + return types.ResponseSetOption{ + Error: cmn.Fmt("Unknown key (%s) or value (%s)", key, value), + } + */ + return types.ResponseSetOption{} + } + + return types.ResponseSetOption{} +} + +func (app *CounterApplication) DeliverTx(tx []byte) types.ResponseDeliverTx { + if app.serial { + if len(tx) > 8 { + return types.ResponseDeliverTx{ + Code: code.CodeTypeEncodingError, + Log: fmt.Sprintf("Max tx size is 8 bytes, got %d", len(tx))} + } + tx8 := make([]byte, 8) + copy(tx8[len(tx8)-len(tx):], tx) + txValue := binary.BigEndian.Uint64(tx8) + if txValue != uint64(app.txCount) { + return types.ResponseDeliverTx{ + Code: code.CodeTypeBadNonce, + Log: fmt.Sprintf("Invalid nonce. Expected %v, got %v", app.txCount, txValue)} + } + } + app.txCount++ + return types.ResponseDeliverTx{Code: code.CodeTypeOK} +} + +func (app *CounterApplication) CheckTx(tx []byte) types.ResponseCheckTx { + if app.serial { + if len(tx) > 8 { + return types.ResponseCheckTx{ + Code: code.CodeTypeEncodingError, + Log: fmt.Sprintf("Max tx size is 8 bytes, got %d", len(tx))} + } + tx8 := make([]byte, 8) + copy(tx8[len(tx8)-len(tx):], tx) + txValue := binary.BigEndian.Uint64(tx8) + if txValue < uint64(app.txCount) { + return types.ResponseCheckTx{ + Code: code.CodeTypeBadNonce, + Log: fmt.Sprintf("Invalid nonce. Expected >= %v, got %v", app.txCount, txValue)} + } + } + return types.ResponseCheckTx{Code: code.CodeTypeOK} +} + +func (app *CounterApplication) Commit() (resp types.ResponseCommit) { + app.hashCount++ + if app.txCount == 0 { + return types.ResponseCommit{} + } + hash := make([]byte, 8) + binary.BigEndian.PutUint64(hash, uint64(app.txCount)) + return types.ResponseCommit{Data: hash} +} + +func (app *CounterApplication) Query(reqQuery types.RequestQuery) types.ResponseQuery { + switch reqQuery.Path { + case "hash": + return types.ResponseQuery{Value: []byte(cmn.Fmt("%v", app.hashCount))} + case "tx": + return types.ResponseQuery{Value: []byte(cmn.Fmt("%v", app.txCount))} + default: + return types.ResponseQuery{Log: cmn.Fmt("Invalid query path. Expected hash or tx, got %v", reqQuery.Path)} + } +} diff --git a/abci/example/example.go b/abci/example/example.go new file mode 100644 index 000000000..ee491c1b5 --- /dev/null +++ b/abci/example/example.go @@ -0,0 +1,3 @@ +package example + +// so the go tool doesn't return errors about no buildable go files ... diff --git a/abci/example/example_test.go b/abci/example/example_test.go new file mode 100644 index 000000000..3fc38f492 --- /dev/null +++ b/abci/example/example_test.go @@ -0,0 +1,154 @@ +package example + +import ( + "fmt" + "net" + "reflect" + "testing" + "time" + + "google.golang.org/grpc" + + "golang.org/x/net/context" + + cmn "github.com/tendermint/tmlibs/common" + "github.com/tendermint/tmlibs/log" + + abcicli "github.com/tendermint/abci/client" + "github.com/tendermint/abci/example/code" + "github.com/tendermint/abci/example/kvstore" + abciserver "github.com/tendermint/abci/server" + "github.com/tendermint/abci/types" +) + +func TestKVStore(t *testing.T) { + fmt.Println("### Testing KVStore") + testStream(t, kvstore.NewKVStoreApplication()) +} + +func TestBaseApp(t *testing.T) { + fmt.Println("### Testing BaseApp") + testStream(t, types.NewBaseApplication()) +} + +func TestGRPC(t *testing.T) { + fmt.Println("### Testing GRPC") + testGRPCSync(t, types.NewGRPCApplication(types.NewBaseApplication())) +} + +func testStream(t *testing.T, app types.Application) { + numDeliverTxs := 200000 + + // Start the listener + server := abciserver.NewSocketServer("unix://test.sock", app) + server.SetLogger(log.TestingLogger().With("module", "abci-server")) + if err := server.Start(); err != nil { + t.Fatalf("Error starting socket server: %v", err.Error()) + } + defer server.Stop() + + // Connect to the socket + client := abcicli.NewSocketClient("unix://test.sock", false) + client.SetLogger(log.TestingLogger().With("module", "abci-client")) + if err := client.Start(); err != nil { + t.Fatalf("Error starting socket client: %v", err.Error()) + } + defer client.Stop() + + done := make(chan struct{}) + counter := 0 + client.SetResponseCallback(func(req *types.Request, res *types.Response) { + // Process response + switch r := res.Value.(type) { + case *types.Response_DeliverTx: + counter++ + if r.DeliverTx.Code != code.CodeTypeOK { + t.Error("DeliverTx failed with ret_code", r.DeliverTx.Code) + } + if counter > numDeliverTxs { + t.Fatalf("Too many DeliverTx responses. Got %d, expected %d", counter, numDeliverTxs) + } + if counter == numDeliverTxs { + go func() { + time.Sleep(time.Second * 2) // Wait for a bit to allow counter overflow + close(done) + }() + return + } + case *types.Response_Flush: + // ignore + default: + t.Error("Unexpected response type", reflect.TypeOf(res.Value)) + } + }) + + // Write requests + for counter := 0; counter < numDeliverTxs; counter++ { + // Send request + reqRes := client.DeliverTxAsync([]byte("test")) + _ = reqRes + // check err ? + + // Sometimes send flush messages + if counter%123 == 0 { + client.FlushAsync() + // check err ? + } + } + + // Send final flush message + client.FlushAsync() + + <-done +} + +//------------------------- +// test grpc + +func dialerFunc(addr string, timeout time.Duration) (net.Conn, error) { + return cmn.Connect(addr) +} + +func testGRPCSync(t *testing.T, app *types.GRPCApplication) { + numDeliverTxs := 2000 + + // Start the listener + server := abciserver.NewGRPCServer("unix://test.sock", app) + server.SetLogger(log.TestingLogger().With("module", "abci-server")) + if err := server.Start(); err != nil { + t.Fatalf("Error starting GRPC server: %v", err.Error()) + } + defer server.Stop() + + // Connect to the socket + conn, err := grpc.Dial("unix://test.sock", grpc.WithInsecure(), grpc.WithDialer(dialerFunc)) + if err != nil { + t.Fatalf("Error dialing GRPC server: %v", err.Error()) + } + defer conn.Close() + + client := types.NewABCIApplicationClient(conn) + + // Write requests + for counter := 0; counter < numDeliverTxs; counter++ { + // Send request + response, err := client.DeliverTx(context.Background(), &types.RequestDeliverTx{[]byte("test")}) + if err != nil { + t.Fatalf("Error in GRPC DeliverTx: %v", err.Error()) + } + counter++ + if response.Code != code.CodeTypeOK { + t.Error("DeliverTx failed with ret_code", response.Code) + } + if counter > numDeliverTxs { + t.Fatal("Too many DeliverTx responses") + } + t.Log("response", counter) + if counter == numDeliverTxs { + go func() { + time.Sleep(time.Second * 2) // Wait for a bit to allow counter overflow + }() + } + + } +} diff --git a/abci/example/js/.gitignore b/abci/example/js/.gitignore new file mode 100644 index 000000000..3c3629e64 --- /dev/null +++ b/abci/example/js/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/abci/example/js/README.md b/abci/example/js/README.md new file mode 100644 index 000000000..1bef9cbf5 --- /dev/null +++ b/abci/example/js/README.md @@ -0,0 +1 @@ +This example has been moved here: https://github.com/tendermint/js-abci/tree/master/example diff --git a/abci/example/kvstore/README.md b/abci/example/kvstore/README.md new file mode 100644 index 000000000..e988eadb0 --- /dev/null +++ b/abci/example/kvstore/README.md @@ -0,0 +1,31 @@ +# KVStore + +There are two app's here: the KVStoreApplication and the PersistentKVStoreApplication. + +## KVStoreApplication + +The KVStoreApplication is a simple merkle key-value store. +Transactions of the form `key=value` are stored as key-value pairs in the tree. +Transactions without an `=` sign set the value to the key. +The app has no replay protection (other than what the mempool provides). + +## PersistentKVStoreApplication + +The PersistentKVStoreApplication wraps the KVStoreApplication +and provides two additional features: + +1) persistence of state across app restarts (using Tendermint's ABCI-Handshake mechanism) +2) validator set changes + +The state is persisted in leveldb along with the last block committed, +and the Handshake allows any necessary blocks to be replayed. +Validator set changes are effected using the following transaction format: + +``` +val:pubkey1/power1,addr2/power2,addr3/power3" +``` + +where `power1` is the new voting power for the validator with `pubkey1` (possibly a new one). +There is no sybil protection against new validators joining. +Validators can be removed by setting their power to `0`. + diff --git a/abci/example/kvstore/helpers.go b/abci/example/kvstore/helpers.go new file mode 100644 index 000000000..63bc31a63 --- /dev/null +++ b/abci/example/kvstore/helpers.go @@ -0,0 +1,38 @@ +package kvstore + +import ( + "github.com/tendermint/abci/types" + cmn "github.com/tendermint/tmlibs/common" +) + +// RandVal creates one random validator, with a key derived +// from the input value +func RandVal(i int) types.Validator { + addr := cmn.RandBytes(20) + pubkey := cmn.RandBytes(32) + power := cmn.RandUint16() + 1 + v := types.Ed25519Validator(pubkey, int64(power)) + v.Address = addr + return v +} + +// RandVals returns a list of cnt validators for initializing +// the application. Note that the keys are deterministically +// derived from the index in the array, while the power is +// random (Change this if not desired) +func RandVals(cnt int) []types.Validator { + res := make([]types.Validator, cnt) + for i := 0; i < cnt; i++ { + res[i] = RandVal(i) + } + return res +} + +// InitKVStore initializes the kvstore app with some data, +// which allows tests to pass and is fine as long as you +// don't make any tx that modify the validator state +func InitKVStore(app *PersistentKVStoreApplication) { + app.InitChain(types.RequestInitChain{ + Validators: RandVals(1), + }) +} diff --git a/abci/example/kvstore/kvstore.go b/abci/example/kvstore/kvstore.go new file mode 100644 index 000000000..4ccbc56b0 --- /dev/null +++ b/abci/example/kvstore/kvstore.go @@ -0,0 +1,126 @@ +package kvstore + +import ( + "bytes" + "encoding/binary" + "encoding/json" + "fmt" + + "github.com/tendermint/abci/example/code" + "github.com/tendermint/abci/types" + cmn "github.com/tendermint/tmlibs/common" + dbm "github.com/tendermint/tmlibs/db" +) + +var ( + stateKey = []byte("stateKey") + kvPairPrefixKey = []byte("kvPairKey:") +) + +type State struct { + db dbm.DB + Size int64 `json:"size"` + Height int64 `json:"height"` + AppHash []byte `json:"app_hash"` +} + +func loadState(db dbm.DB) State { + stateBytes := db.Get(stateKey) + var state State + if len(stateBytes) != 0 { + err := json.Unmarshal(stateBytes, &state) + if err != nil { + panic(err) + } + } + state.db = db + return state +} + +func saveState(state State) { + stateBytes, err := json.Marshal(state) + if err != nil { + panic(err) + } + state.db.Set(stateKey, stateBytes) +} + +func prefixKey(key []byte) []byte { + return append(kvPairPrefixKey, key...) +} + +//--------------------------------------------------- + +var _ types.Application = (*KVStoreApplication)(nil) + +type KVStoreApplication struct { + types.BaseApplication + + state State +} + +func NewKVStoreApplication() *KVStoreApplication { + state := loadState(dbm.NewMemDB()) + return &KVStoreApplication{state: state} +} + +func (app *KVStoreApplication) Info(req types.RequestInfo) (resInfo types.ResponseInfo) { + return types.ResponseInfo{Data: fmt.Sprintf("{\"size\":%v}", app.state.Size)} +} + +// tx is either "key=value" or just arbitrary bytes +func (app *KVStoreApplication) DeliverTx(tx []byte) types.ResponseDeliverTx { + var key, value []byte + parts := bytes.Split(tx, []byte("=")) + if len(parts) == 2 { + key, value = parts[0], parts[1] + } else { + key, value = tx, tx + } + app.state.db.Set(prefixKey(key), value) + app.state.Size += 1 + + tags := []cmn.KVPair{ + {[]byte("app.creator"), []byte("jae")}, + {[]byte("app.key"), key}, + } + return types.ResponseDeliverTx{Code: code.CodeTypeOK, Tags: tags} +} + +func (app *KVStoreApplication) CheckTx(tx []byte) types.ResponseCheckTx { + return types.ResponseCheckTx{Code: code.CodeTypeOK} +} + +func (app *KVStoreApplication) Commit() types.ResponseCommit { + // Using a memdb - just return the big endian size of the db + appHash := make([]byte, 8) + binary.PutVarint(appHash, app.state.Size) + app.state.AppHash = appHash + app.state.Height += 1 + saveState(app.state) + return types.ResponseCommit{Data: appHash} +} + +func (app *KVStoreApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) { + if reqQuery.Prove { + value := app.state.db.Get(prefixKey(reqQuery.Data)) + resQuery.Index = -1 // TODO make Proof return index + resQuery.Key = reqQuery.Data + resQuery.Value = value + if value != nil { + resQuery.Log = "exists" + } else { + resQuery.Log = "does not exist" + } + return + } else { + value := app.state.db.Get(prefixKey(reqQuery.Data)) + resQuery.Value = value + if value != nil { + resQuery.Log = "exists" + } else { + resQuery.Log = "does not exist" + } + return + } +} diff --git a/abci/example/kvstore/kvstore_test.go b/abci/example/kvstore/kvstore_test.go new file mode 100644 index 000000000..ff3e49208 --- /dev/null +++ b/abci/example/kvstore/kvstore_test.go @@ -0,0 +1,310 @@ +package kvstore + +import ( + "bytes" + "io/ioutil" + "sort" + "testing" + + "github.com/stretchr/testify/require" + + cmn "github.com/tendermint/tmlibs/common" + "github.com/tendermint/tmlibs/log" + + abcicli "github.com/tendermint/abci/client" + "github.com/tendermint/abci/example/code" + abciserver "github.com/tendermint/abci/server" + "github.com/tendermint/abci/types" +) + +func testKVStore(t *testing.T, app types.Application, tx []byte, key, value string) { + ar := app.DeliverTx(tx) + require.False(t, ar.IsErr(), ar) + // repeating tx doesn't raise error + ar = app.DeliverTx(tx) + require.False(t, ar.IsErr(), ar) + + // make sure query is fine + resQuery := app.Query(types.RequestQuery{ + Path: "/store", + Data: []byte(key), + }) + require.Equal(t, code.CodeTypeOK, resQuery.Code) + require.Equal(t, value, string(resQuery.Value)) + + // make sure proof is fine + resQuery = app.Query(types.RequestQuery{ + Path: "/store", + Data: []byte(key), + Prove: true, + }) + require.EqualValues(t, code.CodeTypeOK, resQuery.Code) + require.Equal(t, value, string(resQuery.Value)) +} + +func TestKVStoreKV(t *testing.T) { + kvstore := NewKVStoreApplication() + key := "abc" + value := key + tx := []byte(key) + testKVStore(t, kvstore, tx, key, value) + + value = "def" + tx = []byte(key + "=" + value) + testKVStore(t, kvstore, tx, key, value) +} + +func TestPersistentKVStoreKV(t *testing.T) { + dir, err := ioutil.TempDir("/tmp", "abci-kvstore-test") // TODO + if err != nil { + t.Fatal(err) + } + kvstore := NewPersistentKVStoreApplication(dir) + key := "abc" + value := key + tx := []byte(key) + testKVStore(t, kvstore, tx, key, value) + + value = "def" + tx = []byte(key + "=" + value) + testKVStore(t, kvstore, tx, key, value) +} + +func TestPersistentKVStoreInfo(t *testing.T) { + dir, err := ioutil.TempDir("/tmp", "abci-kvstore-test") // TODO + if err != nil { + t.Fatal(err) + } + kvstore := NewPersistentKVStoreApplication(dir) + InitKVStore(kvstore) + height := int64(0) + + resInfo := kvstore.Info(types.RequestInfo{}) + if resInfo.LastBlockHeight != height { + t.Fatalf("expected height of %d, got %d", height, resInfo.LastBlockHeight) + } + + // make and apply block + height = int64(1) + hash := []byte("foo") + header := types.Header{ + Height: int64(height), + } + kvstore.BeginBlock(types.RequestBeginBlock{hash, header, nil, nil}) + kvstore.EndBlock(types.RequestEndBlock{header.Height}) + kvstore.Commit() + + resInfo = kvstore.Info(types.RequestInfo{}) + if resInfo.LastBlockHeight != height { + t.Fatalf("expected height of %d, got %d", height, resInfo.LastBlockHeight) + } + +} + +// add a validator, remove a validator, update a validator +func TestValUpdates(t *testing.T) { + dir, err := ioutil.TempDir("/tmp", "abci-kvstore-test") // TODO + if err != nil { + t.Fatal(err) + } + kvstore := NewPersistentKVStoreApplication(dir) + + // init with some validators + total := 10 + nInit := 5 + vals := RandVals(total) + // iniitalize with the first nInit + kvstore.InitChain(types.RequestInitChain{ + Validators: vals[:nInit], + }) + + vals1, vals2 := vals[:nInit], kvstore.Validators() + valsEqual(t, vals1, vals2) + + var v1, v2, v3 types.Validator + + // add some validators + v1, v2 = vals[nInit], vals[nInit+1] + diff := []types.Validator{v1, v2} + tx1 := MakeValSetChangeTx(v1.PubKey, v1.Power) + tx2 := MakeValSetChangeTx(v2.PubKey, v2.Power) + + makeApplyBlock(t, kvstore, 1, diff, tx1, tx2) + + vals1, vals2 = vals[:nInit+2], kvstore.Validators() + valsEqual(t, vals1, vals2) + + // remove some validators + v1, v2, v3 = vals[nInit-2], vals[nInit-1], vals[nInit] + v1.Power = 0 + v2.Power = 0 + v3.Power = 0 + diff = []types.Validator{v1, v2, v3} + tx1 = MakeValSetChangeTx(v1.PubKey, v1.Power) + tx2 = MakeValSetChangeTx(v2.PubKey, v2.Power) + tx3 := MakeValSetChangeTx(v3.PubKey, v3.Power) + + makeApplyBlock(t, kvstore, 2, diff, tx1, tx2, tx3) + + vals1 = append(vals[:nInit-2], vals[nInit+1]) + vals2 = kvstore.Validators() + valsEqual(t, vals1, vals2) + + // update some validators + v1 = vals[0] + if v1.Power == 5 { + v1.Power = 6 + } else { + v1.Power = 5 + } + diff = []types.Validator{v1} + tx1 = MakeValSetChangeTx(v1.PubKey, v1.Power) + + makeApplyBlock(t, kvstore, 3, diff, tx1) + + vals1 = append([]types.Validator{v1}, vals1[1:]...) + vals2 = kvstore.Validators() + valsEqual(t, vals1, vals2) + +} + +func makeApplyBlock(t *testing.T, kvstore types.Application, heightInt int, diff []types.Validator, txs ...[]byte) { + // make and apply block + height := int64(heightInt) + hash := []byte("foo") + header := types.Header{ + Height: height, + } + + kvstore.BeginBlock(types.RequestBeginBlock{hash, header, nil, nil}) + for _, tx := range txs { + if r := kvstore.DeliverTx(tx); r.IsErr() { + t.Fatal(r) + } + } + resEndBlock := kvstore.EndBlock(types.RequestEndBlock{header.Height}) + kvstore.Commit() + + valsEqual(t, diff, resEndBlock.ValidatorUpdates) + +} + +// order doesn't matter +func valsEqual(t *testing.T, vals1, vals2 []types.Validator) { + if len(vals1) != len(vals2) { + t.Fatalf("vals dont match in len. got %d, expected %d", len(vals2), len(vals1)) + } + sort.Sort(types.Validators(vals1)) + sort.Sort(types.Validators(vals2)) + for i, v1 := range vals1 { + v2 := vals2[i] + if !bytes.Equal(v1.PubKey.Data, v2.PubKey.Data) || + v1.Power != v2.Power { + t.Fatalf("vals dont match at index %d. got %X/%d , expected %X/%d", i, v2.PubKey, v2.Power, v1.PubKey, v1.Power) + } + } +} + +func makeSocketClientServer(app types.Application, name string) (abcicli.Client, cmn.Service, error) { + // Start the listener + socket := cmn.Fmt("unix://%s.sock", name) + logger := log.TestingLogger() + + server := abciserver.NewSocketServer(socket, app) + server.SetLogger(logger.With("module", "abci-server")) + if err := server.Start(); err != nil { + return nil, nil, err + } + + // Connect to the socket + client := abcicli.NewSocketClient(socket, false) + client.SetLogger(logger.With("module", "abci-client")) + if err := client.Start(); err != nil { + server.Stop() + return nil, nil, err + } + + return client, server, nil +} + +func makeGRPCClientServer(app types.Application, name string) (abcicli.Client, cmn.Service, error) { + // Start the listener + socket := cmn.Fmt("unix://%s.sock", name) + logger := log.TestingLogger() + + gapp := types.NewGRPCApplication(app) + server := abciserver.NewGRPCServer(socket, gapp) + server.SetLogger(logger.With("module", "abci-server")) + if err := server.Start(); err != nil { + return nil, nil, err + } + + client := abcicli.NewGRPCClient(socket, true) + client.SetLogger(logger.With("module", "abci-client")) + if err := client.Start(); err != nil { + server.Stop() + return nil, nil, err + } + return client, server, nil +} + +func TestClientServer(t *testing.T) { + // set up socket app + kvstore := NewKVStoreApplication() + client, server, err := makeSocketClientServer(kvstore, "kvstore-socket") + require.Nil(t, err) + defer server.Stop() + defer client.Stop() + + runClientTests(t, client) + + // set up grpc app + kvstore = NewKVStoreApplication() + gclient, gserver, err := makeGRPCClientServer(kvstore, "kvstore-grpc") + require.Nil(t, err) + defer gserver.Stop() + defer gclient.Stop() + + runClientTests(t, gclient) +} + +func runClientTests(t *testing.T, client abcicli.Client) { + // run some tests.... + key := "abc" + value := key + tx := []byte(key) + testClient(t, client, tx, key, value) + + value = "def" + tx = []byte(key + "=" + value) + testClient(t, client, tx, key, value) +} + +func testClient(t *testing.T, app abcicli.Client, tx []byte, key, value string) { + ar, err := app.DeliverTxSync(tx) + require.NoError(t, err) + require.False(t, ar.IsErr(), ar) + // repeating tx doesn't raise error + ar, err = app.DeliverTxSync(tx) + require.NoError(t, err) + require.False(t, ar.IsErr(), ar) + + // make sure query is fine + resQuery, err := app.QuerySync(types.RequestQuery{ + Path: "/store", + Data: []byte(key), + }) + require.Nil(t, err) + require.Equal(t, code.CodeTypeOK, resQuery.Code) + require.Equal(t, value, string(resQuery.Value)) + + // make sure proof is fine + resQuery, err = app.QuerySync(types.RequestQuery{ + Path: "/store", + Data: []byte(key), + Prove: true, + }) + require.Nil(t, err) + require.Equal(t, code.CodeTypeOK, resQuery.Code) + require.Equal(t, value, string(resQuery.Value)) +} diff --git a/abci/example/kvstore/persistent_kvstore.go b/abci/example/kvstore/persistent_kvstore.go new file mode 100644 index 000000000..02f7ce74a --- /dev/null +++ b/abci/example/kvstore/persistent_kvstore.go @@ -0,0 +1,200 @@ +package kvstore + +import ( + "bytes" + "encoding/hex" + "fmt" + "strconv" + "strings" + + "github.com/tendermint/abci/example/code" + "github.com/tendermint/abci/types" + cmn "github.com/tendermint/tmlibs/common" + dbm "github.com/tendermint/tmlibs/db" + "github.com/tendermint/tmlibs/log" +) + +const ( + ValidatorSetChangePrefix string = "val:" +) + +//----------------------------------------- + +var _ types.Application = (*PersistentKVStoreApplication)(nil) + +type PersistentKVStoreApplication struct { + app *KVStoreApplication + + // validator set + ValUpdates []types.Validator + + logger log.Logger +} + +func NewPersistentKVStoreApplication(dbDir string) *PersistentKVStoreApplication { + name := "kvstore" + db, err := dbm.NewGoLevelDB(name, dbDir) + if err != nil { + panic(err) + } + + state := loadState(db) + + return &PersistentKVStoreApplication{ + app: &KVStoreApplication{state: state}, + logger: log.NewNopLogger(), + } +} + +func (app *PersistentKVStoreApplication) SetLogger(l log.Logger) { + app.logger = l +} + +func (app *PersistentKVStoreApplication) Info(req types.RequestInfo) types.ResponseInfo { + res := app.app.Info(req) + res.LastBlockHeight = app.app.state.Height + res.LastBlockAppHash = app.app.state.AppHash + return res +} + +func (app *PersistentKVStoreApplication) SetOption(req types.RequestSetOption) types.ResponseSetOption { + return app.app.SetOption(req) +} + +// tx is either "val:pubkey/power" or "key=value" or just arbitrary bytes +func (app *PersistentKVStoreApplication) DeliverTx(tx []byte) types.ResponseDeliverTx { + // if it starts with "val:", update the validator set + // format is "val:pubkey/power" + if isValidatorTx(tx) { + // update validators in the merkle tree + // and in app.ValUpdates + return app.execValidatorTx(tx) + } + + // otherwise, update the key-value store + return app.app.DeliverTx(tx) +} + +func (app *PersistentKVStoreApplication) CheckTx(tx []byte) types.ResponseCheckTx { + return app.app.CheckTx(tx) +} + +// Commit will panic if InitChain was not called +func (app *PersistentKVStoreApplication) Commit() types.ResponseCommit { + return app.app.Commit() +} + +func (app *PersistentKVStoreApplication) Query(reqQuery types.RequestQuery) types.ResponseQuery { + return app.app.Query(reqQuery) +} + +// Save the validators in the merkle tree +func (app *PersistentKVStoreApplication) InitChain(req types.RequestInitChain) types.ResponseInitChain { + for _, v := range req.Validators { + r := app.updateValidator(v) + if r.IsErr() { + app.logger.Error("Error updating validators", "r", r) + } + } + return types.ResponseInitChain{} +} + +// Track the block hash and header information +func (app *PersistentKVStoreApplication) BeginBlock(req types.RequestBeginBlock) types.ResponseBeginBlock { + // reset valset changes + app.ValUpdates = make([]types.Validator, 0) + return types.ResponseBeginBlock{} +} + +// Update the validator set +func (app *PersistentKVStoreApplication) EndBlock(req types.RequestEndBlock) types.ResponseEndBlock { + return types.ResponseEndBlock{ValidatorUpdates: app.ValUpdates} +} + +//--------------------------------------------- +// update validators + +func (app *PersistentKVStoreApplication) Validators() (validators []types.Validator) { + itr := app.app.state.db.Iterator(nil, nil) + for ; itr.Valid(); itr.Next() { + if isValidatorTx(itr.Key()) { + validator := new(types.Validator) + err := types.ReadMessage(bytes.NewBuffer(itr.Value()), validator) + if err != nil { + panic(err) + } + validators = append(validators, *validator) + } + } + return +} + +func MakeValSetChangeTx(pubkey types.PubKey, power int64) []byte { + return []byte(cmn.Fmt("val:%X/%d", pubkey.Data, power)) +} + +func isValidatorTx(tx []byte) bool { + return strings.HasPrefix(string(tx), ValidatorSetChangePrefix) +} + +// format is "val:pubkey/power" +// pubkey is raw 32-byte ed25519 key +func (app *PersistentKVStoreApplication) execValidatorTx(tx []byte) types.ResponseDeliverTx { + tx = tx[len(ValidatorSetChangePrefix):] + + //get the pubkey and power + pubKeyAndPower := strings.Split(string(tx), "/") + if len(pubKeyAndPower) != 2 { + return types.ResponseDeliverTx{ + Code: code.CodeTypeEncodingError, + Log: fmt.Sprintf("Expected 'pubkey/power'. Got %v", pubKeyAndPower)} + } + pubkeyS, powerS := pubKeyAndPower[0], pubKeyAndPower[1] + + // decode the pubkey + pubkey, err := hex.DecodeString(pubkeyS) + if err != nil { + return types.ResponseDeliverTx{ + Code: code.CodeTypeEncodingError, + Log: fmt.Sprintf("Pubkey (%s) is invalid hex", pubkeyS)} + } + + // decode the power + power, err := strconv.ParseInt(powerS, 10, 64) + if err != nil { + return types.ResponseDeliverTx{ + Code: code.CodeTypeEncodingError, + Log: fmt.Sprintf("Power (%s) is not an int", powerS)} + } + + // update + return app.updateValidator(types.Ed25519Validator(pubkey, int64(power))) +} + +// add, update, or remove a validator +func (app *PersistentKVStoreApplication) updateValidator(v types.Validator) types.ResponseDeliverTx { + key := []byte("val:" + string(v.PubKey.Data)) + if v.Power == 0 { + // remove validator + if !app.app.state.db.Has(key) { + return types.ResponseDeliverTx{ + Code: code.CodeTypeUnauthorized, + Log: fmt.Sprintf("Cannot remove non-existent validator %X", key)} + } + app.app.state.db.Delete(key) + } else { + // add or update validator + value := bytes.NewBuffer(make([]byte, 0)) + if err := types.WriteMessage(&v, value); err != nil { + return types.ResponseDeliverTx{ + Code: code.CodeTypeEncodingError, + Log: fmt.Sprintf("Error encoding validator: %v", err)} + } + app.app.state.db.Set(key, value.Bytes()) + } + + // we only update the changes array if we successfully updated the tree + app.ValUpdates = append(app.ValUpdates, v) + + return types.ResponseDeliverTx{Code: code.CodeTypeOK} +} diff --git a/abci/example/python/abci/__init__.py b/abci/example/python/abci/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/abci/example/python/abci/msg.py b/abci/example/python/abci/msg.py new file mode 100644 index 000000000..7329f5852 --- /dev/null +++ b/abci/example/python/abci/msg.py @@ -0,0 +1,50 @@ +from wire import decode_string + +# map type_byte to message name +message_types = { + 0x01: "echo", + 0x02: "flush", + 0x03: "info", + 0x04: "set_option", + 0x21: "deliver_tx", + 0x22: "check_tx", + 0x23: "commit", + 0x24: "add_listener", + 0x25: "rm_listener", +} + +# return the decoded arguments of abci messages + +class RequestDecoder(): + + def __init__(self, reader): + self.reader = reader + + def echo(self): + return decode_string(self.reader) + + def flush(self): + return + + def info(self): + return + + def set_option(self): + return decode_string(self.reader), decode_string(self.reader) + + def deliver_tx(self): + return decode_string(self.reader) + + def check_tx(self): + return decode_string(self.reader) + + def commit(self): + return + + def add_listener(self): + # TODO + return + + def rm_listener(self): + # TODO + return diff --git a/abci/example/python/abci/reader.py b/abci/example/python/abci/reader.py new file mode 100644 index 000000000..6c0dad94e --- /dev/null +++ b/abci/example/python/abci/reader.py @@ -0,0 +1,56 @@ + +# Simple read() method around a bytearray + + +class BytesBuffer(): + + def __init__(self, b): + self.buf = b + self.readCount = 0 + + def count(self): + return self.readCount + + def reset_count(self): + self.readCount = 0 + + def size(self): + return len(self.buf) + + def peek(self): + return self.buf[0] + + def write(self, b): + # b should be castable to byte array + self.buf += bytearray(b) + + def read(self, n): + if len(self.buf) < n: + print "reader err: buf less than n" + # TODO: exception + return + self.readCount += n + r = self.buf[:n] + self.buf = self.buf[n:] + return r + +# Buffer bytes off a tcp connection and read them off in chunks + + +class ConnReader(): + + def __init__(self, conn): + self.conn = conn + self.buf = bytearray() + + # blocking + def read(self, n): + while n > len(self.buf): + moreBuf = self.conn.recv(1024) + if not moreBuf: + raise IOError("dead connection") + self.buf = self.buf + bytearray(moreBuf) + + r = self.buf[:n] + self.buf = self.buf[n:] + return r diff --git a/abci/example/python/abci/server.py b/abci/example/python/abci/server.py new file mode 100644 index 000000000..40d50896c --- /dev/null +++ b/abci/example/python/abci/server.py @@ -0,0 +1,202 @@ +import socket +import select +import sys + +from wire import decode_varint, encode +from reader import BytesBuffer +from msg import RequestDecoder, message_types + +# hold the asyncronous state of a connection +# ie. we may not get enough bytes on one read to decode the message + +class Connection(): + + def __init__(self, fd, app): + self.fd = fd + self.app = app + self.recBuf = BytesBuffer(bytearray()) + self.resBuf = BytesBuffer(bytearray()) + self.msgLength = 0 + self.decoder = RequestDecoder(self.recBuf) + self.inProgress = False # are we in the middle of a message + + def recv(this): + data = this.fd.recv(1024) + if not data: # what about len(data) == 0 + raise IOError("dead connection") + this.recBuf.write(data) + +# ABCI server responds to messges by calling methods on the app + +class ABCIServer(): + + def __init__(self, app, port=5410): + self.app = app + # map conn file descriptors to (app, reqBuf, resBuf, msgDecoder) + self.appMap = {} + + self.port = port + self.listen_backlog = 10 + + self.listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.listener.setblocking(0) + self.listener.bind(('', port)) + + self.listener.listen(self.listen_backlog) + + self.shutdown = False + + self.read_list = [self.listener] + self.write_list = [] + + def handle_new_connection(self, r): + new_fd, new_addr = r.accept() + new_fd.setblocking(0) # non-blocking + self.read_list.append(new_fd) + self.write_list.append(new_fd) + print 'new connection to', new_addr + + self.appMap[new_fd] = Connection(new_fd, self.app) + + def handle_conn_closed(self, r): + self.read_list.remove(r) + self.write_list.remove(r) + r.close() + print "connection closed" + + def handle_recv(self, r): + # app, recBuf, resBuf, conn + conn = self.appMap[r] + while True: + try: + print "recv loop" + # check if we need more data first + if conn.inProgress: + if (conn.msgLength == 0 or conn.recBuf.size() < conn.msgLength): + conn.recv() + else: + if conn.recBuf.size() == 0: + conn.recv() + + conn.inProgress = True + + # see if we have enough to get the message length + if conn.msgLength == 0: + ll = conn.recBuf.peek() + if conn.recBuf.size() < 1 + ll: + # we don't have enough bytes to read the length yet + return + print "decoding msg length" + conn.msgLength = decode_varint(conn.recBuf) + + # see if we have enough to decode the message + if conn.recBuf.size() < conn.msgLength: + return + + # now we can decode the message + + # first read the request type and get the particular msg + # decoder + typeByte = conn.recBuf.read(1) + typeByte = int(typeByte[0]) + resTypeByte = typeByte + 0x10 + req_type = message_types[typeByte] + + if req_type == "flush": + # messages are length prefixed + conn.resBuf.write(encode(1)) + conn.resBuf.write([resTypeByte]) + conn.fd.send(str(conn.resBuf.buf)) + conn.msgLength = 0 + conn.inProgress = False + conn.resBuf = BytesBuffer(bytearray()) + return + + decoder = getattr(conn.decoder, req_type) + + print "decoding args" + req_args = decoder() + print "got args", req_args + + # done decoding message + conn.msgLength = 0 + conn.inProgress = False + + req_f = getattr(conn.app, req_type) + if req_args is None: + res = req_f() + elif isinstance(req_args, tuple): + res = req_f(*req_args) + else: + res = req_f(req_args) + + if isinstance(res, tuple): + res, ret_code = res + else: + ret_code = res + res = None + + print "called", req_type, "ret code:", ret_code + if ret_code != 0: + print "non-zero retcode:", ret_code + + if req_type in ("echo", "info"): # these dont return a ret code + enc = encode(res) + # messages are length prefixed + conn.resBuf.write(encode(len(enc) + 1)) + conn.resBuf.write([resTypeByte]) + conn.resBuf.write(enc) + else: + enc, encRet = encode(res), encode(ret_code) + # messages are length prefixed + conn.resBuf.write(encode(len(enc) + len(encRet) + 1)) + conn.resBuf.write([resTypeByte]) + conn.resBuf.write(encRet) + conn.resBuf.write(enc) + except TypeError as e: + print "TypeError on reading from connection:", e + self.handle_conn_closed(r) + return + except ValueError as e: + print "ValueError on reading from connection:", e + self.handle_conn_closed(r) + return + except IOError as e: + print "IOError on reading from connection:", e + self.handle_conn_closed(r) + return + except Exception as e: + # sys.exc_info()[0] # TODO better + print "error reading from connection", str(e) + self.handle_conn_closed(r) + return + + def main_loop(self): + while not self.shutdown: + r_list, w_list, _ = select.select( + self.read_list, self.write_list, [], 2.5) + + for r in r_list: + if (r == self.listener): + try: + self.handle_new_connection(r) + # undo adding to read list ... + except NameError as e: + print "Could not connect due to NameError:", e + except TypeError as e: + print "Could not connect due to TypeError:", e + except: + print "Could not connect due to unexpected error:", sys.exc_info()[0] + else: + self.handle_recv(r) + + def handle_shutdown(self): + for r in self.read_list: + r.close() + for w in self.write_list: + try: + w.close() + except Exception as e: + print(e) # TODO: add logging + self.shutdown = True diff --git a/abci/example/python/abci/wire.py b/abci/example/python/abci/wire.py new file mode 100644 index 000000000..1a07e89f1 --- /dev/null +++ b/abci/example/python/abci/wire.py @@ -0,0 +1,115 @@ + +# the decoder works off a reader +# the encoder returns bytearray + + +def hex2bytes(h): + return bytearray(h.decode('hex')) + + +def bytes2hex(b): + if type(b) in (str, unicode): + return "".join([hex(ord(c))[2:].zfill(2) for c in b]) + else: + return bytes2hex(b.decode()) + + +# expects uvarint64 (no crazy big nums!) +def uvarint_size(i): + if i == 0: + return 0 + for j in xrange(1, 8): + if i < 1 << j * 8: + return j + return 8 + +# expects i < 2**size + + +def encode_big_endian(i, size): + if size == 0: + return bytearray() + return encode_big_endian(i / 256, size - 1) + bytearray([i % 256]) + + +def decode_big_endian(reader, size): + if size == 0: + return 0 + firstByte = reader.read(1)[0] + return firstByte * (256 ** (size - 1)) + decode_big_endian(reader, size - 1) + +# ints are max 16 bytes long + + +def encode_varint(i): + negate = False + if i < 0: + negate = True + i = -i + size = uvarint_size(i) + if size == 0: + return bytearray([0]) + big_end = encode_big_endian(i, size) + if negate: + size += 0xF0 + return bytearray([size]) + big_end + +# returns the int and whats left of the byte array + + +def decode_varint(reader): + size = reader.read(1)[0] + if size == 0: + return 0 + + negate = True if size > int(0xF0) else False + if negate: + size = size - 0xF0 + i = decode_big_endian(reader, size) + if negate: + i = i * (-1) + return i + + +def encode_string(s): + size = encode_varint(len(s)) + return size + bytearray(s) + + +def decode_string(reader): + length = decode_varint(reader) + return str(reader.read(length)) + + +def encode_list(s): + b = bytearray() + map(b.extend, map(encode, s)) + return encode_varint(len(s)) + b + + +def encode(s): + if s is None: + return bytearray() + if isinstance(s, int): + return encode_varint(s) + elif isinstance(s, str): + return encode_string(s) + elif isinstance(s, list): + return encode_list(s) + else: + print "UNSUPPORTED TYPE!", type(s), s + + +if __name__ == '__main__': + ns = [100, 100, 1000, 256] + ss = [2, 5, 5, 2] + bs = map(encode_big_endian, ns, ss) + ds = map(decode_big_endian, bs, ss) + print ns + print [i[0] for i in ds] + + ss = ["abc", "hi there jim", "ok now what"] + e = map(encode_string, ss) + d = map(decode_string, e) + print ss + print [i[0] for i in d] diff --git a/abci/example/python/app.py b/abci/example/python/app.py new file mode 100644 index 000000000..1c041be6c --- /dev/null +++ b/abci/example/python/app.py @@ -0,0 +1,82 @@ +import sys + +from abci.wire import hex2bytes, decode_big_endian, encode_big_endian +from abci.server import ABCIServer +from abci.reader import BytesBuffer + + +class CounterApplication(): + + def __init__(self): + sys.exit("The python example is out of date. Upgrading the Python examples is currently left as an exercise to you.") + self.hashCount = 0 + self.txCount = 0 + self.serial = False + + def echo(self, msg): + return msg, 0 + + def info(self): + return ["hashes:%d, txs:%d" % (self.hashCount, self.txCount)], 0 + + def set_option(self, key, value): + if key == "serial" and value == "on": + self.serial = True + return 0 + + def deliver_tx(self, txBytes): + if self.serial: + txByteArray = bytearray(txBytes) + if len(txBytes) >= 2 and txBytes[:2] == "0x": + txByteArray = hex2bytes(txBytes[2:]) + txValue = decode_big_endian( + BytesBuffer(txByteArray), len(txBytes)) + if txValue != self.txCount: + return None, 6 + self.txCount += 1 + return None, 0 + + def check_tx(self, txBytes): + if self.serial: + txByteArray = bytearray(txBytes) + if len(txBytes) >= 2 and txBytes[:2] == "0x": + txByteArray = hex2bytes(txBytes[2:]) + txValue = decode_big_endian( + BytesBuffer(txByteArray), len(txBytes)) + if txValue < self.txCount: + return 6 + return 0 + + def commit(self): + self.hashCount += 1 + if self.txCount == 0: + return "", 0 + h = encode_big_endian(self.txCount, 8) + h.reverse() + return str(h), 0 + + def add_listener(self): + return 0 + + def rm_listener(self): + return 0 + + def event(self): + return + + +if __name__ == '__main__': + l = len(sys.argv) + if l == 1: + port = 26658 + elif l == 2: + port = int(sys.argv[1]) + else: + print "too many arguments" + quit() + + print 'ABCI Demo APP (Python)' + + app = CounterApplication() + server = ABCIServer(app, port) + server.main_loop() diff --git a/abci/example/python3/abci/__init__.py b/abci/example/python3/abci/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/abci/example/python3/abci/msg.py b/abci/example/python3/abci/msg.py new file mode 100644 index 000000000..807c4b6b0 --- /dev/null +++ b/abci/example/python3/abci/msg.py @@ -0,0 +1,50 @@ +from .wire import decode_string + +# map type_byte to message name +message_types = { + 0x01: "echo", + 0x02: "flush", + 0x03: "info", + 0x04: "set_option", + 0x21: "deliver_tx", + 0x22: "check_tx", + 0x23: "commit", + 0x24: "add_listener", + 0x25: "rm_listener", +} + +# return the decoded arguments of abci messages + +class RequestDecoder(): + + def __init__(self, reader): + self.reader = reader + + def echo(self): + return decode_string(self.reader) + + def flush(self): + return + + def info(self): + return + + def set_option(self): + return decode_string(self.reader), decode_string(self.reader) + + def deliver_tx(self): + return decode_string(self.reader) + + def check_tx(self): + return decode_string(self.reader) + + def commit(self): + return + + def add_listener(self): + # TODO + return + + def rm_listener(self): + # TODO + return diff --git a/abci/example/python3/abci/reader.py b/abci/example/python3/abci/reader.py new file mode 100644 index 000000000..c016ac604 --- /dev/null +++ b/abci/example/python3/abci/reader.py @@ -0,0 +1,56 @@ + +# Simple read() method around a bytearray + + +class BytesBuffer(): + + def __init__(self, b): + self.buf = b + self.readCount = 0 + + def count(self): + return self.readCount + + def reset_count(self): + self.readCount = 0 + + def size(self): + return len(self.buf) + + def peek(self): + return self.buf[0] + + def write(self, b): + # b should be castable to byte array + self.buf += bytearray(b) + + def read(self, n): + if len(self.buf) < n: + print("reader err: buf less than n") + # TODO: exception + return + self.readCount += n + r = self.buf[:n] + self.buf = self.buf[n:] + return r + +# Buffer bytes off a tcp connection and read them off in chunks + + +class ConnReader(): + + def __init__(self, conn): + self.conn = conn + self.buf = bytearray() + + # blocking + def read(self, n): + while n > len(self.buf): + moreBuf = self.conn.recv(1024) + if not moreBuf: + raise IOError("dead connection") + self.buf = self.buf + bytearray(moreBuf) + + r = self.buf[:n] + self.buf = self.buf[n:] + return r diff --git a/abci/example/python3/abci/server.py b/abci/example/python3/abci/server.py new file mode 100644 index 000000000..04063262d --- /dev/null +++ b/abci/example/python3/abci/server.py @@ -0,0 +1,196 @@ +import socket +import select +import sys +import logging + +from .wire import decode_varint, encode +from .reader import BytesBuffer +from .msg import RequestDecoder, message_types + +# hold the asyncronous state of a connection +# ie. we may not get enough bytes on one read to decode the message + +logger = logging.getLogger(__name__) + +class Connection(): + + def __init__(self, fd, app): + self.fd = fd + self.app = app + self.recBuf = BytesBuffer(bytearray()) + self.resBuf = BytesBuffer(bytearray()) + self.msgLength = 0 + self.decoder = RequestDecoder(self.recBuf) + self.inProgress = False # are we in the middle of a message + + def recv(this): + data = this.fd.recv(1024) + if not data: # what about len(data) == 0 + raise IOError("dead connection") + this.recBuf.write(data) + +# ABCI server responds to messges by calling methods on the app + +class ABCIServer(): + + def __init__(self, app, port=5410): + self.app = app + # map conn file descriptors to (app, reqBuf, resBuf, msgDecoder) + self.appMap = {} + + self.port = port + self.listen_backlog = 10 + + self.listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.listener.setblocking(0) + self.listener.bind(('', port)) + + self.listener.listen(self.listen_backlog) + + self.shutdown = False + + self.read_list = [self.listener] + self.write_list = [] + + def handle_new_connection(self, r): + new_fd, new_addr = r.accept() + new_fd.setblocking(0) # non-blocking + self.read_list.append(new_fd) + self.write_list.append(new_fd) + print('new connection to', new_addr) + + self.appMap[new_fd] = Connection(new_fd, self.app) + + def handle_conn_closed(self, r): + self.read_list.remove(r) + self.write_list.remove(r) + r.close() + print("connection closed") + + def handle_recv(self, r): + # app, recBuf, resBuf, conn + conn = self.appMap[r] + while True: + try: + print("recv loop") + # check if we need more data first + if conn.inProgress: + if (conn.msgLength == 0 or conn.recBuf.size() < conn.msgLength): + conn.recv() + else: + if conn.recBuf.size() == 0: + conn.recv() + + conn.inProgress = True + + # see if we have enough to get the message length + if conn.msgLength == 0: + ll = conn.recBuf.peek() + if conn.recBuf.size() < 1 + ll: + # we don't have enough bytes to read the length yet + return + print("decoding msg length") + conn.msgLength = decode_varint(conn.recBuf) + + # see if we have enough to decode the message + if conn.recBuf.size() < conn.msgLength: + return + + # now we can decode the message + + # first read the request type and get the particular msg + # decoder + typeByte = conn.recBuf.read(1) + typeByte = int(typeByte[0]) + resTypeByte = typeByte + 0x10 + req_type = message_types[typeByte] + + if req_type == "flush": + # messages are length prefixed + conn.resBuf.write(encode(1)) + conn.resBuf.write([resTypeByte]) + conn.fd.send(conn.resBuf.buf) + conn.msgLength = 0 + conn.inProgress = False + conn.resBuf = BytesBuffer(bytearray()) + return + + decoder = getattr(conn.decoder, req_type) + + print("decoding args") + req_args = decoder() + print("got args", req_args) + + # done decoding message + conn.msgLength = 0 + conn.inProgress = False + + req_f = getattr(conn.app, req_type) + if req_args is None: + res = req_f() + elif isinstance(req_args, tuple): + res = req_f(*req_args) + else: + res = req_f(req_args) + + if isinstance(res, tuple): + res, ret_code = res + else: + ret_code = res + res = None + + print("called", req_type, "ret code:", ret_code, 'res:', res) + if ret_code != 0: + print("non-zero retcode:", ret_code) + + if req_type in ("echo", "info"): # these dont return a ret code + enc = encode(res) + # messages are length prefixed + conn.resBuf.write(encode(len(enc) + 1)) + conn.resBuf.write([resTypeByte]) + conn.resBuf.write(enc) + else: + enc, encRet = encode(res), encode(ret_code) + # messages are length prefixed + conn.resBuf.write(encode(len(enc) + len(encRet) + 1)) + conn.resBuf.write([resTypeByte]) + conn.resBuf.write(encRet) + conn.resBuf.write(enc) + except IOError as e: + print("IOError on reading from connection:", e) + self.handle_conn_closed(r) + return + except Exception as e: + logger.exception("error reading from connection") + self.handle_conn_closed(r) + return + + def main_loop(self): + while not self.shutdown: + r_list, w_list, _ = select.select( + self.read_list, self.write_list, [], 2.5) + + for r in r_list: + if (r == self.listener): + try: + self.handle_new_connection(r) + # undo adding to read list ... + except NameError as e: + print("Could not connect due to NameError:", e) + except TypeError as e: + print("Could not connect due to TypeError:", e) + except: + print("Could not connect due to unexpected error:", sys.exc_info()[0]) + else: + self.handle_recv(r) + + def handle_shutdown(self): + for r in self.read_list: + r.close() + for w in self.write_list: + try: + w.close() + except Exception as e: + print(e) # TODO: add logging + self.shutdown = True diff --git a/abci/example/python3/abci/wire.py b/abci/example/python3/abci/wire.py new file mode 100644 index 000000000..72f5fab8b --- /dev/null +++ b/abci/example/python3/abci/wire.py @@ -0,0 +1,119 @@ + +# the decoder works off a reader +# the encoder returns bytearray + + +def hex2bytes(h): + return bytearray(h.decode('hex')) + + +def bytes2hex(b): + if type(b) in (str, str): + return "".join([hex(ord(c))[2:].zfill(2) for c in b]) + else: + return bytes2hex(b.decode()) + + +# expects uvarint64 (no crazy big nums!) +def uvarint_size(i): + if i == 0: + return 0 + for j in range(1, 8): + if i < 1 << j * 8: + return j + return 8 + +# expects i < 2**size + + +def encode_big_endian(i, size): + if size == 0: + return bytearray() + return encode_big_endian(i // 256, size - 1) + bytearray([i % 256]) + + +def decode_big_endian(reader, size): + if size == 0: + return 0 + firstByte = reader.read(1)[0] + return firstByte * (256 ** (size - 1)) + decode_big_endian(reader, size - 1) + +# ints are max 16 bytes long + + +def encode_varint(i): + negate = False + if i < 0: + negate = True + i = -i + size = uvarint_size(i) + if size == 0: + return bytearray([0]) + big_end = encode_big_endian(i, size) + if negate: + size += 0xF0 + return bytearray([size]) + big_end + +# returns the int and whats left of the byte array + + +def decode_varint(reader): + size = reader.read(1)[0] + if size == 0: + return 0 + + negate = True if size > int(0xF0) else False + if negate: + size = size - 0xF0 + i = decode_big_endian(reader, size) + if negate: + i = i * (-1) + return i + + +def encode_string(s): + size = encode_varint(len(s)) + return size + bytearray(s, 'utf8') + + +def decode_string(reader): + length = decode_varint(reader) + raw_data = reader.read(length) + return raw_data.decode() + + +def encode_list(s): + b = bytearray() + list(map(b.extend, list(map(encode, s)))) + return encode_varint(len(s)) + b + + +def encode(s): + print('encoding', repr(s)) + if s is None: + return bytearray() + if isinstance(s, int): + return encode_varint(s) + elif isinstance(s, str): + return encode_string(s) + elif isinstance(s, list): + return encode_list(s) + elif isinstance(s, bytearray): + return encode_string(s) + else: + print("UNSUPPORTED TYPE!", type(s), s) + + +if __name__ == '__main__': + ns = [100, 100, 1000, 256] + ss = [2, 5, 5, 2] + bs = list(map(encode_big_endian, ns, ss)) + ds = list(map(decode_big_endian, bs, ss)) + print(ns) + print([i[0] for i in ds]) + + ss = ["abc", "hi there jim", "ok now what"] + e = list(map(encode_string, ss)) + d = list(map(decode_string, e)) + print(ss) + print([i[0] for i in d]) diff --git a/abci/example/python3/app.py b/abci/example/python3/app.py new file mode 100644 index 000000000..9f051b1e2 --- /dev/null +++ b/abci/example/python3/app.py @@ -0,0 +1,82 @@ +import sys + +from abci.wire import hex2bytes, decode_big_endian, encode_big_endian +from abci.server import ABCIServer +from abci.reader import BytesBuffer + + +class CounterApplication(): + + def __init__(self): + sys.exit("The python example is out of date. Upgrading the Python examples is currently left as an exercise to you.") + self.hashCount = 0 + self.txCount = 0 + self.serial = False + + def echo(self, msg): + return msg, 0 + + def info(self): + return ["hashes:%d, txs:%d" % (self.hashCount, self.txCount)], 0 + + def set_option(self, key, value): + if key == "serial" and value == "on": + self.serial = True + return 0 + + def deliver_tx(self, txBytes): + if self.serial: + txByteArray = bytearray(txBytes) + if len(txBytes) >= 2 and txBytes[:2] == "0x": + txByteArray = hex2bytes(txBytes[2:]) + txValue = decode_big_endian( + BytesBuffer(txByteArray), len(txBytes)) + if txValue != self.txCount: + return None, 6 + self.txCount += 1 + return None, 0 + + def check_tx(self, txBytes): + if self.serial: + txByteArray = bytearray(txBytes) + if len(txBytes) >= 2 and txBytes[:2] == "0x": + txByteArray = hex2bytes(txBytes[2:]) + txValue = decode_big_endian( + BytesBuffer(txByteArray), len(txBytes)) + if txValue < self.txCount: + return 6 + return 0 + + def commit(self): + self.hashCount += 1 + if self.txCount == 0: + return "", 0 + h = encode_big_endian(self.txCount, 8) + h.reverse() + return h.decode(), 0 + + def add_listener(self): + return 0 + + def rm_listener(self): + return 0 + + def event(self): + return + + +if __name__ == '__main__': + l = len(sys.argv) + if l == 1: + port = 26658 + elif l == 2: + port = int(sys.argv[1]) + else: + print("too many arguments") + quit() + + print('ABCI Demo APP (Python)') + + app = CounterApplication() + server = ABCIServer(app, port) + server.main_loop() diff --git a/abci/scripts/abci-builder/Dockerfile b/abci/scripts/abci-builder/Dockerfile new file mode 100644 index 000000000..1182085b4 --- /dev/null +++ b/abci/scripts/abci-builder/Dockerfile @@ -0,0 +1,12 @@ +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 diff --git a/abci/scripts/dist.sh b/abci/scripts/dist.sh new file mode 100755 index 000000000..d94ce20f7 --- /dev/null +++ b/abci/scripts/dist.sh @@ -0,0 +1,52 @@ +#!/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 diff --git a/abci/scripts/dist_build.sh b/abci/scripts/dist_build.sh new file mode 100755 index 000000000..c45c752ef --- /dev/null +++ b/abci/scripts/dist_build.sh @@ -0,0 +1,53 @@ +#!/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 diff --git a/abci/scripts/publish.sh b/abci/scripts/publish.sh new file mode 100644 index 000000000..715f6c11b --- /dev/null +++ b/abci/scripts/publish.sh @@ -0,0 +1,7 @@ +#! /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 diff --git a/abci/server/grpc_server.go b/abci/server/grpc_server.go new file mode 100644 index 000000000..e7dad15cf --- /dev/null +++ b/abci/server/grpc_server.go @@ -0,0 +1,57 @@ +package server + +import ( + "net" + + "google.golang.org/grpc" + + "github.com/tendermint/abci/types" + cmn "github.com/tendermint/tmlibs/common" +) + +type GRPCServer struct { + cmn.BaseService + + proto string + addr string + listener net.Listener + server *grpc.Server + + app types.ABCIApplicationServer +} + +// NewGRPCServer returns a new gRPC ABCI server +func NewGRPCServer(protoAddr string, app types.ABCIApplicationServer) cmn.Service { + proto, addr := cmn.ProtocolAndAddress(protoAddr) + s := &GRPCServer{ + proto: proto, + addr: addr, + listener: nil, + app: app, + } + s.BaseService = *cmn.NewBaseService(nil, "ABCIServer", s) + return s +} + +// OnStart starts the gRPC service +func (s *GRPCServer) OnStart() error { + if err := s.BaseService.OnStart(); err != nil { + return err + } + ln, err := net.Listen(s.proto, s.addr) + if err != nil { + return err + } + s.Logger.Info("Listening", "proto", s.proto, "addr", s.addr) + s.listener = ln + s.server = grpc.NewServer() + types.RegisterABCIApplicationServer(s.server, s.app) + go s.server.Serve(s.listener) + return nil +} + +// OnStop stops the gRPC server +func (s *GRPCServer) OnStop() { + s.BaseService.OnStop() + s.server.Stop() +} diff --git a/abci/server/server.go b/abci/server/server.go new file mode 100644 index 000000000..0bbd65e37 --- /dev/null +++ b/abci/server/server.go @@ -0,0 +1,31 @@ +/* +Package server is used to start a new ABCI server. + +It contains two server implementation: + * gRPC server + * socket server + +*/ + +package server + +import ( + "fmt" + + "github.com/tendermint/abci/types" + cmn "github.com/tendermint/tmlibs/common" +) + +func NewServer(protoAddr, transport string, app types.Application) (cmn.Service, error) { + var s cmn.Service + var err error + switch transport { + case "socket": + s = NewSocketServer(protoAddr, app) + case "grpc": + s = NewGRPCServer(protoAddr, types.NewGRPCApplication(app)) + default: + err = fmt.Errorf("Unknown server type %s", transport) + } + return s, err +} diff --git a/abci/server/socket_server.go b/abci/server/socket_server.go new file mode 100644 index 000000000..eb26dc353 --- /dev/null +++ b/abci/server/socket_server.go @@ -0,0 +1,226 @@ +package server + +import ( + "bufio" + "fmt" + "io" + "net" + "sync" + + "github.com/tendermint/abci/types" + cmn "github.com/tendermint/tmlibs/common" +) + +// var maxNumberConnections = 2 + +type SocketServer struct { + cmn.BaseService + + proto string + addr string + listener net.Listener + + connsMtx sync.Mutex + conns map[int]net.Conn + nextConnID int + + appMtx sync.Mutex + app types.Application +} + +func NewSocketServer(protoAddr string, app types.Application) cmn.Service { + proto, addr := cmn.ProtocolAndAddress(protoAddr) + s := &SocketServer{ + proto: proto, + addr: addr, + listener: nil, + app: app, + conns: make(map[int]net.Conn), + } + s.BaseService = *cmn.NewBaseService(nil, "ABCIServer", s) + return s +} + +func (s *SocketServer) OnStart() error { + if err := s.BaseService.OnStart(); err != nil { + return err + } + ln, err := net.Listen(s.proto, s.addr) + if err != nil { + return err + } + s.listener = ln + go s.acceptConnectionsRoutine() + return nil +} + +func (s *SocketServer) OnStop() { + s.BaseService.OnStop() + if err := s.listener.Close(); err != nil { + s.Logger.Error("Error closing listener", "err", err) + } + + s.connsMtx.Lock() + defer s.connsMtx.Unlock() + for id, conn := range s.conns { + delete(s.conns, id) + if err := conn.Close(); err != nil { + s.Logger.Error("Error closing connection", "id", id, "conn", conn, "err", err) + } + } +} + +func (s *SocketServer) addConn(conn net.Conn) int { + s.connsMtx.Lock() + defer s.connsMtx.Unlock() + + connID := s.nextConnID + s.nextConnID++ + s.conns[connID] = conn + + return connID +} + +// deletes conn even if close errs +func (s *SocketServer) rmConn(connID int) error { + s.connsMtx.Lock() + defer s.connsMtx.Unlock() + + conn, ok := s.conns[connID] + if !ok { + return fmt.Errorf("Connection %d does not exist", connID) + } + + delete(s.conns, connID) + return conn.Close() +} + +func (s *SocketServer) acceptConnectionsRoutine() { + for { + // Accept a connection + s.Logger.Info("Waiting for new connection...") + conn, err := s.listener.Accept() + if err != nil { + if !s.IsRunning() { + return // Ignore error from listener closing. + } + s.Logger.Error("Failed to accept connection: " + err.Error()) + continue + } + + s.Logger.Info("Accepted a new connection") + + connID := s.addConn(conn) + + closeConn := make(chan error, 2) // Push to signal connection closed + responses := make(chan *types.Response, 1000) // A channel to buffer responses + + // Read requests from conn and deal with them + go s.handleRequests(closeConn, conn, responses) + // Pull responses from 'responses' and write them to conn. + go s.handleResponses(closeConn, conn, responses) + + // Wait until signal to close connection + go s.waitForClose(closeConn, connID) + } +} + +func (s *SocketServer) waitForClose(closeConn chan error, connID int) { + err := <-closeConn + if err == io.EOF { + s.Logger.Error("Connection was closed by client") + } else if err != nil { + s.Logger.Error("Connection error", "error", err) + } else { + // never happens + s.Logger.Error("Connection was closed.") + } + + // Close the connection + if err := s.rmConn(connID); err != nil { + s.Logger.Error("Error in closing connection", "error", err) + } +} + +// Read requests from conn and deal with them +func (s *SocketServer) handleRequests(closeConn chan error, conn net.Conn, responses chan<- *types.Response) { + var count int + var bufReader = bufio.NewReader(conn) + for { + + var req = &types.Request{} + err := types.ReadMessage(bufReader, req) + if err != nil { + if err == io.EOF { + closeConn <- err + } else { + closeConn <- fmt.Errorf("Error reading message: %v", err.Error()) + } + return + } + s.appMtx.Lock() + count++ + s.handleRequest(req, responses) + s.appMtx.Unlock() + } +} + +func (s *SocketServer) handleRequest(req *types.Request, responses chan<- *types.Response) { + switch r := req.Value.(type) { + case *types.Request_Echo: + responses <- types.ToResponseEcho(r.Echo.Message) + case *types.Request_Flush: + responses <- types.ToResponseFlush() + case *types.Request_Info: + res := s.app.Info(*r.Info) + responses <- types.ToResponseInfo(res) + case *types.Request_SetOption: + res := s.app.SetOption(*r.SetOption) + responses <- types.ToResponseSetOption(res) + case *types.Request_DeliverTx: + res := s.app.DeliverTx(r.DeliverTx.Tx) + responses <- types.ToResponseDeliverTx(res) + case *types.Request_CheckTx: + res := s.app.CheckTx(r.CheckTx.Tx) + responses <- types.ToResponseCheckTx(res) + case *types.Request_Commit: + res := s.app.Commit() + responses <- types.ToResponseCommit(res) + case *types.Request_Query: + res := s.app.Query(*r.Query) + responses <- types.ToResponseQuery(res) + case *types.Request_InitChain: + res := s.app.InitChain(*r.InitChain) + responses <- types.ToResponseInitChain(res) + case *types.Request_BeginBlock: + res := s.app.BeginBlock(*r.BeginBlock) + responses <- types.ToResponseBeginBlock(res) + case *types.Request_EndBlock: + res := s.app.EndBlock(*r.EndBlock) + responses <- types.ToResponseEndBlock(res) + default: + responses <- types.ToResponseException("Unknown request") + } +} + +// Pull responses from 'responses' and write them to conn. +func (s *SocketServer) handleResponses(closeConn chan error, conn net.Conn, responses <-chan *types.Response) { + var count int + var bufWriter = bufio.NewWriter(conn) + for { + var res = <-responses + err := types.WriteMessage(res, bufWriter) + if err != nil { + closeConn <- fmt.Errorf("Error writing message: %v", err.Error()) + return + } + if _, ok := res.Value.(*types.Response_Flush); ok { + err = bufWriter.Flush() + if err != nil { + closeConn <- fmt.Errorf("Error flushing write buffer: %v", err.Error()) + return + } + } + count++ + } +} diff --git a/abci/specification.md b/abci/specification.md new file mode 100644 index 000000000..25b9d7d0e --- /dev/null +++ b/abci/specification.md @@ -0,0 +1,324 @@ +# ABCI Specification + +## 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. + +## 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` diff --git a/abci/specification.rst b/abci/specification.rst new file mode 100644 index 000000000..8d8530def --- /dev/null +++ b/abci/specification.rst @@ -0,0 +1,294 @@ +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 `__. +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! diff --git a/abci/tests/benchmarks/blank.go b/abci/tests/benchmarks/blank.go new file mode 100644 index 000000000..20f08f14b --- /dev/null +++ b/abci/tests/benchmarks/blank.go @@ -0,0 +1 @@ +package benchmarks diff --git a/abci/tests/benchmarks/parallel/parallel.go b/abci/tests/benchmarks/parallel/parallel.go new file mode 100644 index 000000000..cc68ceadc --- /dev/null +++ b/abci/tests/benchmarks/parallel/parallel.go @@ -0,0 +1,55 @@ +package main + +import ( + "bufio" + "fmt" + "log" + + "github.com/tendermint/abci/types" + cmn "github.com/tendermint/tmlibs/common" +) + +func main() { + + conn, err := cmn.Connect("unix://test.sock") + if err != nil { + log.Fatal(err.Error()) + } + + // Read a bunch of responses + go func() { + counter := 0 + for { + var res = &types.Response{} + err := types.ReadMessage(conn, res) + if err != nil { + log.Fatal(err.Error()) + } + counter++ + if counter%1000 == 0 { + fmt.Println("Read", counter) + } + } + }() + + // Write a bunch of requests + counter := 0 + for i := 0; ; i++ { + var bufWriter = bufio.NewWriter(conn) + var req = types.ToRequestEcho("foobar") + + err := types.WriteMessage(req, bufWriter) + if err != nil { + log.Fatal(err.Error()) + } + err = bufWriter.Flush() + if err != nil { + log.Fatal(err.Error()) + } + + counter++ + if counter%1000 == 0 { + fmt.Println("Write", counter) + } + } +} diff --git a/abci/tests/benchmarks/simple/simple.go b/abci/tests/benchmarks/simple/simple.go new file mode 100644 index 000000000..ef5bab86b --- /dev/null +++ b/abci/tests/benchmarks/simple/simple.go @@ -0,0 +1,69 @@ +package main + +import ( + "bufio" + "fmt" + "log" + "net" + "reflect" + + "github.com/tendermint/abci/types" + cmn "github.com/tendermint/tmlibs/common" +) + +func main() { + + conn, err := cmn.Connect("unix://test.sock") + if err != nil { + log.Fatal(err.Error()) + } + + // Make a bunch of requests + counter := 0 + for i := 0; ; i++ { + req := types.ToRequestEcho("foobar") + _, err := makeRequest(conn, req) + if err != nil { + log.Fatal(err.Error()) + } + counter++ + if counter%1000 == 0 { + fmt.Println(counter) + } + } +} + +func makeRequest(conn net.Conn, req *types.Request) (*types.Response, error) { + var bufWriter = bufio.NewWriter(conn) + + // Write desired request + err := types.WriteMessage(req, bufWriter) + if err != nil { + return nil, err + } + err = types.WriteMessage(types.ToRequestFlush(), bufWriter) + if err != nil { + return nil, err + } + err = bufWriter.Flush() + if err != nil { + return nil, err + } + + // Read desired response + var res = &types.Response{} + err = types.ReadMessage(conn, res) + if err != nil { + return nil, err + } + var resFlush = &types.Response{} + err = types.ReadMessage(conn, resFlush) + if err != nil { + return nil, err + } + if _, ok := resFlush.Value.(*types.Response_Flush); !ok { + return nil, fmt.Errorf("Expected flush response but got something else: %v", reflect.TypeOf(resFlush)) + } + + return res, nil +} diff --git a/abci/tests/client_server_test.go b/abci/tests/client_server_test.go new file mode 100644 index 000000000..1f05c9a71 --- /dev/null +++ b/abci/tests/client_server_test.go @@ -0,0 +1,27 @@ +package tests + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + abciclient "github.com/tendermint/abci/client" + "github.com/tendermint/abci/example/kvstore" + abciserver "github.com/tendermint/abci/server" +) + +func TestClientServerNoAddrPrefix(t *testing.T) { + addr := "localhost:26658" + transport := "socket" + app := kvstore.NewKVStoreApplication() + + server, err := abciserver.NewServer(addr, transport, app) + assert.NoError(t, err, "expected no error on NewServer") + err = server.Start() + assert.NoError(t, err, "expected no error on server.Start") + + client, err := abciclient.NewClient(addr, transport, true) + assert.NoError(t, err, "expected no error on NewClient") + err = client.Start() + assert.NoError(t, err, "expected no error on client.Start") +} diff --git a/abci/tests/server/client.go b/abci/tests/server/client.go new file mode 100644 index 000000000..5ef978c73 --- /dev/null +++ b/abci/tests/server/client.go @@ -0,0 +1,96 @@ +package testsuite + +import ( + "bytes" + "errors" + "fmt" + + abcicli "github.com/tendermint/abci/client" + "github.com/tendermint/abci/types" + cmn "github.com/tendermint/tmlibs/common" +) + +func InitChain(client abcicli.Client) error { + total := 10 + vals := make([]types.Validator, total) + for i := 0; i < total; i++ { + pubkey := cmn.RandBytes(33) + power := cmn.RandInt() + vals[i] = types.Ed25519Validator(pubkey, int64(power)) + } + _, err := client.InitChainSync(types.RequestInitChain{ + Validators: vals, + }) + if err != nil { + fmt.Printf("Failed test: InitChain - %v\n", err) + return err + } + fmt.Println("Passed test: InitChain") + return nil +} + +func SetOption(client abcicli.Client, key, value string) error { + _, err := client.SetOptionSync(types.RequestSetOption{Key: key, Value: value}) + if err != nil { + fmt.Println("Failed test: SetOption") + fmt.Printf("error while setting %v=%v: \nerror: %v\n", key, value, err) + return err + } + fmt.Println("Passed test: SetOption") + return nil +} + +func Commit(client abcicli.Client, hashExp []byte) error { + res, err := client.CommitSync() + data := res.Data + if err != nil { + fmt.Println("Failed test: Commit") + fmt.Printf("error while committing: %v\n", err) + return err + } + if !bytes.Equal(data, hashExp) { + fmt.Println("Failed test: Commit") + fmt.Printf("Commit hash was unexpected. Got %X expected %X\n", data, hashExp) + return errors.New("CommitTx failed") + } + fmt.Println("Passed test: Commit") + return nil +} + +func DeliverTx(client abcicli.Client, txBytes []byte, codeExp uint32, dataExp []byte) error { + res, _ := client.DeliverTxSync(txBytes) + code, data, log := res.Code, res.Data, res.Log + if code != codeExp { + fmt.Println("Failed test: DeliverTx") + fmt.Printf("DeliverTx response code was unexpected. Got %v expected %v. Log: %v\n", + code, codeExp, log) + return errors.New("DeliverTx error") + } + if !bytes.Equal(data, dataExp) { + fmt.Println("Failed test: DeliverTx") + fmt.Printf("DeliverTx response data was unexpected. Got %X expected %X\n", + data, dataExp) + return errors.New("DeliverTx error") + } + fmt.Println("Passed test: DeliverTx") + return nil +} + +func CheckTx(client abcicli.Client, txBytes []byte, codeExp uint32, dataExp []byte) error { + res, _ := client.CheckTxSync(txBytes) + code, data, log := res.Code, res.Data, res.Log + if code != codeExp { + fmt.Println("Failed test: CheckTx") + fmt.Printf("CheckTx response code was unexpected. Got %v expected %v. Log: %v\n", + code, codeExp, log) + return errors.New("CheckTx") + } + if !bytes.Equal(data, dataExp) { + fmt.Println("Failed test: CheckTx") + fmt.Printf("CheckTx response data was unexpected. Got %X expected %X\n", + data, dataExp) + return errors.New("CheckTx") + } + fmt.Println("Passed test: CheckTx") + return nil +} diff --git a/abci/tests/test_app/app.go b/abci/tests/test_app/app.go new file mode 100644 index 000000000..1c308ec0d --- /dev/null +++ b/abci/tests/test_app/app.go @@ -0,0 +1,78 @@ +package main + +import ( + "bytes" + "fmt" + "os" + + abcicli "github.com/tendermint/abci/client" + "github.com/tendermint/abci/types" + "github.com/tendermint/tmlibs/log" +) + +func startClient(abciType string) abcicli.Client { + // Start client + client, err := abcicli.NewClient("tcp://127.0.0.1:26658", abciType, true) + if err != nil { + panic(err.Error()) + } + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) + client.SetLogger(logger.With("module", "abcicli")) + if err := client.Start(); err != nil { + panicf("connecting to abci_app: %v", err.Error()) + } + + return client +} + +func setOption(client abcicli.Client, key, value string) { + _, err := client.SetOptionSync(types.RequestSetOption{key, value}) + if err != nil { + panicf("setting %v=%v: \nerr: %v", key, value, err) + } +} + +func commit(client abcicli.Client, hashExp []byte) { + res, err := client.CommitSync() + if err != nil { + panicf("client error: %v", err) + } + if !bytes.Equal(res.Data, hashExp) { + panicf("Commit hash was unexpected. Got %X expected %X", res.Data, hashExp) + } +} + +func deliverTx(client abcicli.Client, txBytes []byte, codeExp uint32, dataExp []byte) { + res, err := client.DeliverTxSync(txBytes) + if err != nil { + panicf("client error: %v", err) + } + if res.Code != codeExp { + panicf("DeliverTx response code was unexpected. Got %v expected %v. Log: %v", res.Code, codeExp, res.Log) + } + if !bytes.Equal(res.Data, dataExp) { + panicf("DeliverTx response data was unexpected. Got %X expected %X", res.Data, dataExp) + } +} + +/*func checkTx(client abcicli.Client, txBytes []byte, codeExp uint32, dataExp []byte) { + res, err := client.CheckTxSync(txBytes) + if err != nil { + panicf("client error: %v", err) + } + if res.IsErr() { + panicf("checking tx %X: %v\nlog: %v", txBytes, res.Log) + } + if res.Code != codeExp { + panicf("CheckTx response code was unexpected. Got %v expected %v. Log: %v", + res.Code, codeExp, res.Log) + } + if !bytes.Equal(res.Data, dataExp) { + panicf("CheckTx response data was unexpected. Got %X expected %X", + res.Data, dataExp) + } +}*/ + +func panicf(format string, a ...interface{}) { + panic(fmt.Sprintf(format, a...)) +} diff --git a/abci/tests/test_app/main.go b/abci/tests/test_app/main.go new file mode 100644 index 000000000..95a7515d4 --- /dev/null +++ b/abci/tests/test_app/main.go @@ -0,0 +1,84 @@ +package main + +import ( + "fmt" + "log" + "os" + "os/exec" + "time" + + "github.com/tendermint/abci/example/code" + "github.com/tendermint/abci/types" +) + +var abciType string + +func init() { + abciType = os.Getenv("ABCI") + if abciType == "" { + abciType = "socket" + } +} + +func main() { + testCounter() +} + +const ( + maxABCIConnectTries = 10 +) + +func ensureABCIIsUp(typ string, n int) error { + var err error + cmdString := "abci-cli echo hello" + if typ == "grpc" { + cmdString = "abci-cli --abci grpc echo hello" + } + + for i := 0; i < n; i++ { + cmd := exec.Command("bash", "-c", cmdString) // nolint: gas + _, err = cmd.CombinedOutput() + if err == nil { + break + } + <-time.After(500 * time.Millisecond) + } + return err +} + +func testCounter() { + abciApp := os.Getenv("ABCI_APP") + if abciApp == "" { + panic("No ABCI_APP specified") + } + + fmt.Printf("Running %s test with abci=%s\n", abciApp, abciType) + cmd := exec.Command("bash", "-c", fmt.Sprintf("abci-cli %s", abciApp)) // nolint: gas + cmd.Stdout = os.Stdout + if err := cmd.Start(); err != nil { + log.Fatalf("starting %q err: %v", abciApp, err) + } + defer cmd.Wait() + defer cmd.Process.Kill() + + if err := ensureABCIIsUp(abciType, maxABCIConnectTries); err != nil { + log.Fatalf("echo failed: %v", err) + } + + client := startClient(abciType) + defer client.Stop() + + setOption(client, "serial", "on") + commit(client, nil) + deliverTx(client, []byte("abc"), code.CodeTypeBadNonce, nil) + commit(client, nil) + deliverTx(client, []byte{0x00}, types.CodeTypeOK, nil) + commit(client, []byte{0, 0, 0, 0, 0, 0, 0, 1}) + deliverTx(client, []byte{0x00}, code.CodeTypeBadNonce, nil) + deliverTx(client, []byte{0x01}, types.CodeTypeOK, nil) + deliverTx(client, []byte{0x00, 0x02}, types.CodeTypeOK, nil) + deliverTx(client, []byte{0x00, 0x03}, types.CodeTypeOK, nil) + deliverTx(client, []byte{0x00, 0x00, 0x04}, types.CodeTypeOK, nil) + deliverTx(client, []byte{0x00, 0x00, 0x06}, code.CodeTypeBadNonce, nil) + commit(client, []byte{0, 0, 0, 0, 0, 0, 0, 5}) +} diff --git a/abci/tests/test_app/test.sh b/abci/tests/test_app/test.sh new file mode 100755 index 000000000..230c94163 --- /dev/null +++ b/abci/tests/test_app/test.sh @@ -0,0 +1,27 @@ +#! /bin/bash +set -e + +# These tests spawn the counter app and server by execing the ABCI_APP command and run some simple client tests against it + +# Get the 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" + +echo "RUN COUNTER OVER SOCKET" +# test golang counter +ABCI_APP="counter" go run ./*.go +echo "----------------------" + + +echo "RUN COUNTER OVER GRPC" +# test golang counter via grpc +ABCI_APP="counter --abci=grpc" ABCI="grpc" go run ./*.go +echo "----------------------" + +# test nodejs counter +# TODO: fix node app +#ABCI_APP="node $GOPATH/src/github.com/tendermint/js-abci/example/app.js" go test -test.run TestCounter diff --git a/abci/tests/test_cli/ex1.abci b/abci/tests/test_cli/ex1.abci new file mode 100644 index 000000000..e909266ec --- /dev/null +++ b/abci/tests/test_cli/ex1.abci @@ -0,0 +1,10 @@ +echo hello +info +commit +deliver_tx "abc" +info +commit +query "abc" +deliver_tx "def=xyz" +commit +query "def" diff --git a/abci/tests/test_cli/ex1.abci.out b/abci/tests/test_cli/ex1.abci.out new file mode 100644 index 000000000..5d4c196dc --- /dev/null +++ b/abci/tests/test_cli/ex1.abci.out @@ -0,0 +1,47 @@ +> echo hello +-> code: OK +-> data: hello +-> data.hex: 0x68656C6C6F + +> info +-> code: OK +-> data: {"size":0} +-> data.hex: 0x7B2273697A65223A307D + +> commit +-> code: OK +-> data.hex: 0x0000000000000000 + +> deliver_tx "abc" +-> code: OK + +> info +-> code: OK +-> data: {"size":1} +-> data.hex: 0x7B2273697A65223A317D + +> commit +-> code: OK +-> data.hex: 0x0200000000000000 + +> query "abc" +-> code: OK +-> log: exists +-> height: 0 +-> value: abc +-> value.hex: 616263 + +> deliver_tx "def=xyz" +-> code: OK + +> commit +-> code: OK +-> data.hex: 0x0400000000000000 + +> query "def" +-> code: OK +-> log: exists +-> height: 0 +-> value: xyz +-> value.hex: 78797A + diff --git a/abci/tests/test_cli/ex2.abci b/abci/tests/test_cli/ex2.abci new file mode 100644 index 000000000..3b435f22a --- /dev/null +++ b/abci/tests/test_cli/ex2.abci @@ -0,0 +1,8 @@ +set_option serial on +check_tx 0x00 +check_tx 0xff +deliver_tx 0x00 +check_tx 0x00 +deliver_tx 0x01 +deliver_tx 0x04 +info diff --git a/abci/tests/test_cli/ex2.abci.out b/abci/tests/test_cli/ex2.abci.out new file mode 100644 index 000000000..5bceb85d8 --- /dev/null +++ b/abci/tests/test_cli/ex2.abci.out @@ -0,0 +1,29 @@ +> set_option serial on +-> code: OK +-> log: OK (SetOption doesn't return anything.) + +> check_tx 0x00 +-> code: OK + +> check_tx 0xff +-> code: OK + +> deliver_tx 0x00 +-> code: OK + +> check_tx 0x00 +-> code: 2 +-> log: Invalid nonce. Expected >= 1, got 0 + +> deliver_tx 0x01 +-> code: OK + +> deliver_tx 0x04 +-> code: 2 +-> log: Invalid nonce. Expected 2, got 4 + +> info +-> code: OK +-> data: {"hashes":0,"txs":2} +-> data.hex: 0x7B22686173686573223A302C22747873223A327D + diff --git a/abci/tests/test_cli/test.sh b/abci/tests/test_cli/test.sh new file mode 100755 index 000000000..ce074f513 --- /dev/null +++ b/abci/tests/test_cli/test.sh @@ -0,0 +1,42 @@ +#! /bin/bash +set -e + +# Get the root directory. +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" || exit + +function testExample() { + N=$1 + INPUT=$2 + APP="$3 $4" + + echo "Example $N: $APP" + $APP &> /dev/null & + sleep 2 + abci-cli --log_level=error --verbose batch < "$INPUT" > "${INPUT}.out.new" + killall "$3" + + pre=$(shasum < "${INPUT}.out") + post=$(shasum < "${INPUT}.out.new") + + if [[ "$pre" != "$post" ]]; then + echo "You broke the tutorial" + echo "Got:" + cat "${INPUT}.out.new" + echo "Expected:" + cat "${INPUT}.out" + exit 1 + fi + + rm "${INPUT}".out.new +} + +testExample 1 tests/test_cli/ex1.abci abci-cli kvstore +testExample 2 tests/test_cli/ex2.abci abci-cli counter + +echo "" +echo "PASS" diff --git a/abci/tests/test_cover.sh b/abci/tests/test_cover.sh new file mode 100755 index 000000000..abbbbe563 --- /dev/null +++ b/abci/tests/test_cover.sh @@ -0,0 +1,13 @@ +#!/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 diff --git a/abci/tests/tests.go b/abci/tests/tests.go new file mode 100644 index 000000000..ca8701d29 --- /dev/null +++ b/abci/tests/tests.go @@ -0,0 +1 @@ +package tests diff --git a/abci/types/application.go b/abci/types/application.go new file mode 100644 index 000000000..ef1bc92e5 --- /dev/null +++ b/abci/types/application.go @@ -0,0 +1,138 @@ +package types // nolint: goimports + +import ( + context "golang.org/x/net/context" +) + +// Application is an interface that enables any finite, deterministic state machine +// to be driven by a blockchain-based replication engine via the ABCI. +// All methods take a RequestXxx argument and return a ResponseXxx argument, +// except CheckTx/DeliverTx, which take `tx []byte`, and `Commit`, which takes nothing. +type Application interface { + // Info/Query Connection + Info(RequestInfo) ResponseInfo // Return application info + SetOption(RequestSetOption) ResponseSetOption // Set application option + Query(RequestQuery) ResponseQuery // Query for state + + // Mempool Connection + CheckTx(tx []byte) ResponseCheckTx // Validate a tx for the mempool + + // Consensus Connection + InitChain(RequestInitChain) ResponseInitChain // Initialize blockchain with validators and other info from TendermintCore + BeginBlock(RequestBeginBlock) ResponseBeginBlock // Signals the beginning of a block + DeliverTx(tx []byte) ResponseDeliverTx // Deliver a tx for full processing + EndBlock(RequestEndBlock) ResponseEndBlock // Signals the end of a block, returns changes to the validator set + Commit() ResponseCommit // Commit the state and return the application Merkle root hash +} + +//------------------------------------------------------- +// BaseApplication is a base form of Application + +var _ Application = (*BaseApplication)(nil) + +type BaseApplication struct { +} + +func NewBaseApplication() *BaseApplication { + return &BaseApplication{} +} + +func (BaseApplication) Info(req RequestInfo) ResponseInfo { + return ResponseInfo{} +} + +func (BaseApplication) SetOption(req RequestSetOption) ResponseSetOption { + return ResponseSetOption{} +} + +func (BaseApplication) DeliverTx(tx []byte) ResponseDeliverTx { + return ResponseDeliverTx{Code: CodeTypeOK} +} + +func (BaseApplication) CheckTx(tx []byte) ResponseCheckTx { + return ResponseCheckTx{Code: CodeTypeOK} +} + +func (BaseApplication) Commit() ResponseCommit { + return ResponseCommit{} +} + +func (BaseApplication) Query(req RequestQuery) ResponseQuery { + return ResponseQuery{Code: CodeTypeOK} +} + +func (BaseApplication) InitChain(req RequestInitChain) ResponseInitChain { + return ResponseInitChain{} +} + +func (BaseApplication) BeginBlock(req RequestBeginBlock) ResponseBeginBlock { + return ResponseBeginBlock{} +} + +func (BaseApplication) EndBlock(req RequestEndBlock) ResponseEndBlock { + return ResponseEndBlock{} +} + +//------------------------------------------------------- + +// GRPCApplication is a GRPC wrapper for Application +type GRPCApplication struct { + app Application +} + +func NewGRPCApplication(app Application) *GRPCApplication { + return &GRPCApplication{app} +} + +func (app *GRPCApplication) Echo(ctx context.Context, req *RequestEcho) (*ResponseEcho, error) { + return &ResponseEcho{req.Message}, nil +} + +func (app *GRPCApplication) Flush(ctx context.Context, req *RequestFlush) (*ResponseFlush, error) { + return &ResponseFlush{}, nil +} + +func (app *GRPCApplication) Info(ctx context.Context, req *RequestInfo) (*ResponseInfo, error) { + res := app.app.Info(*req) + return &res, nil +} + +func (app *GRPCApplication) SetOption(ctx context.Context, req *RequestSetOption) (*ResponseSetOption, error) { + res := app.app.SetOption(*req) + return &res, nil +} + +func (app *GRPCApplication) DeliverTx(ctx context.Context, req *RequestDeliverTx) (*ResponseDeliverTx, error) { + res := app.app.DeliverTx(req.Tx) + return &res, nil +} + +func (app *GRPCApplication) CheckTx(ctx context.Context, req *RequestCheckTx) (*ResponseCheckTx, error) { + res := app.app.CheckTx(req.Tx) + return &res, nil +} + +func (app *GRPCApplication) Query(ctx context.Context, req *RequestQuery) (*ResponseQuery, error) { + res := app.app.Query(*req) + return &res, nil +} + +func (app *GRPCApplication) Commit(ctx context.Context, req *RequestCommit) (*ResponseCommit, error) { + res := app.app.Commit() + return &res, nil +} + +func (app *GRPCApplication) InitChain(ctx context.Context, req *RequestInitChain) (*ResponseInitChain, error) { + res := app.app.InitChain(*req) + return &res, nil +} + +func (app *GRPCApplication) BeginBlock(ctx context.Context, req *RequestBeginBlock) (*ResponseBeginBlock, error) { + res := app.app.BeginBlock(*req) + return &res, nil +} + +func (app *GRPCApplication) EndBlock(ctx context.Context, req *RequestEndBlock) (*ResponseEndBlock, error) { + res := app.app.EndBlock(*req) + return &res, nil +} diff --git a/abci/types/messages.go b/abci/types/messages.go new file mode 100644 index 000000000..52e4b6758 --- /dev/null +++ b/abci/types/messages.go @@ -0,0 +1,210 @@ +package types + +import ( + "bufio" + "encoding/binary" + "io" + + "github.com/gogo/protobuf/proto" +) + +const ( + maxMsgSize = 104857600 // 100MB +) + +// WriteMessage writes a varint length-delimited protobuf message. +func WriteMessage(msg proto.Message, w io.Writer) error { + bz, err := proto.Marshal(msg) + if err != nil { + return err + } + return encodeByteSlice(w, bz) +} + +// ReadMessage reads a varint length-delimited protobuf message. +func ReadMessage(r io.Reader, msg proto.Message) error { + return readProtoMsg(r, msg, maxMsgSize) +} + +func readProtoMsg(r io.Reader, msg proto.Message, maxSize int) error { + // binary.ReadVarint takes an io.ByteReader, eg. a bufio.Reader + reader, ok := r.(*bufio.Reader) + if !ok { + reader = bufio.NewReader(r) + } + length64, err := binary.ReadVarint(reader) + if err != nil { + return err + } + length := int(length64) + if length < 0 || length > maxSize { + return io.ErrShortBuffer + } + buf := make([]byte, length) + if _, err := io.ReadFull(reader, buf); err != nil { + return err + } + return proto.Unmarshal(buf, msg) +} + +//----------------------------------------------------------------------- +// NOTE: we copied wire.EncodeByteSlice from go-wire rather than keep +// go-wire as a dep + +func encodeByteSlice(w io.Writer, bz []byte) (err error) { + err = encodeVarint(w, int64(len(bz))) + if err != nil { + return + } + _, err = w.Write(bz) + return +} + +func encodeVarint(w io.Writer, i int64) (err error) { + var buf [10]byte + n := binary.PutVarint(buf[:], i) + _, err = w.Write(buf[0:n]) + return +} + +//---------------------------------------- + +func ToRequestEcho(message string) *Request { + return &Request{ + Value: &Request_Echo{&RequestEcho{message}}, + } +} + +func ToRequestFlush() *Request { + return &Request{ + Value: &Request_Flush{&RequestFlush{}}, + } +} + +func ToRequestInfo(req RequestInfo) *Request { + return &Request{ + Value: &Request_Info{&req}, + } +} + +func ToRequestSetOption(req RequestSetOption) *Request { + return &Request{ + Value: &Request_SetOption{&req}, + } +} + +func ToRequestDeliverTx(tx []byte) *Request { + return &Request{ + Value: &Request_DeliverTx{&RequestDeliverTx{tx}}, + } +} + +func ToRequestCheckTx(tx []byte) *Request { + return &Request{ + Value: &Request_CheckTx{&RequestCheckTx{tx}}, + } +} + +func ToRequestCommit() *Request { + return &Request{ + Value: &Request_Commit{&RequestCommit{}}, + } +} + +func ToRequestQuery(req RequestQuery) *Request { + return &Request{ + Value: &Request_Query{&req}, + } +} + +func ToRequestInitChain(req RequestInitChain) *Request { + return &Request{ + Value: &Request_InitChain{&req}, + } +} + +func ToRequestBeginBlock(req RequestBeginBlock) *Request { + return &Request{ + Value: &Request_BeginBlock{&req}, + } +} + +func ToRequestEndBlock(req RequestEndBlock) *Request { + return &Request{ + Value: &Request_EndBlock{&req}, + } +} + +//---------------------------------------- + +func ToResponseException(errStr string) *Response { + return &Response{ + Value: &Response_Exception{&ResponseException{errStr}}, + } +} + +func ToResponseEcho(message string) *Response { + return &Response{ + Value: &Response_Echo{&ResponseEcho{message}}, + } +} + +func ToResponseFlush() *Response { + return &Response{ + Value: &Response_Flush{&ResponseFlush{}}, + } +} + +func ToResponseInfo(res ResponseInfo) *Response { + return &Response{ + Value: &Response_Info{&res}, + } +} + +func ToResponseSetOption(res ResponseSetOption) *Response { + return &Response{ + Value: &Response_SetOption{&res}, + } +} + +func ToResponseDeliverTx(res ResponseDeliverTx) *Response { + return &Response{ + Value: &Response_DeliverTx{&res}, + } +} + +func ToResponseCheckTx(res ResponseCheckTx) *Response { + return &Response{ + Value: &Response_CheckTx{&res}, + } +} + +func ToResponseCommit(res ResponseCommit) *Response { + return &Response{ + Value: &Response_Commit{&res}, + } +} + +func ToResponseQuery(res ResponseQuery) *Response { + return &Response{ + Value: &Response_Query{&res}, + } +} + +func ToResponseInitChain(res ResponseInitChain) *Response { + return &Response{ + Value: &Response_InitChain{&res}, + } +} + +func ToResponseBeginBlock(res ResponseBeginBlock) *Response { + return &Response{ + Value: &Response_BeginBlock{&res}, + } +} + +func ToResponseEndBlock(res ResponseEndBlock) *Response { + return &Response{ + Value: &Response_EndBlock{&res}, + } +} diff --git a/abci/types/messages_test.go b/abci/types/messages_test.go new file mode 100644 index 000000000..21d3595f0 --- /dev/null +++ b/abci/types/messages_test.go @@ -0,0 +1,104 @@ +package types + +import ( + "bytes" + "encoding/json" + "strings" + "testing" + + "github.com/gogo/protobuf/proto" + "github.com/stretchr/testify/assert" + cmn "github.com/tendermint/tmlibs/common" +) + +func TestMarshalJSON(t *testing.T) { + b, err := json.Marshal(&ResponseDeliverTx{}) + assert.Nil(t, err) + // Do not include empty fields. + assert.False(t, strings.Contains(string(b), "code")) + + r1 := ResponseCheckTx{ + Code: 1, + Data: []byte("hello"), + GasWanted: 43, + Tags: []cmn.KVPair{ + {[]byte("pho"), []byte("bo")}, + }, + } + b, err = json.Marshal(&r1) + assert.Nil(t, err) + + var r2 ResponseCheckTx + err = json.Unmarshal(b, &r2) + assert.Nil(t, err) + assert.Equal(t, r1, r2) +} + +func TestWriteReadMessageSimple(t *testing.T) { + cases := []proto.Message{ + &RequestEcho{ + Message: "Hello", + }, + } + + for _, c := range cases { + buf := new(bytes.Buffer) + err := WriteMessage(c, buf) + assert.Nil(t, err) + + msg := new(RequestEcho) + err = ReadMessage(buf, msg) + assert.Nil(t, err) + + assert.Equal(t, c, msg) + } +} + +func TestWriteReadMessage(t *testing.T) { + cases := []proto.Message{ + &Header{ + NumTxs: 4, + }, + // TODO: add the rest + } + + for _, c := range cases { + buf := new(bytes.Buffer) + err := WriteMessage(c, buf) + assert.Nil(t, err) + + msg := new(Header) + err = ReadMessage(buf, msg) + assert.Nil(t, err) + + assert.Equal(t, c, msg) + } +} + +func TestWriteReadMessage2(t *testing.T) { + phrase := "hello-world" + cases := []proto.Message{ + &ResponseCheckTx{ + Data: []byte(phrase), + Log: phrase, + GasWanted: 10, + Tags: []cmn.KVPair{ + cmn.KVPair{[]byte("abc"), []byte("def")}, + }, + // Fee: cmn.KI64Pair{ + }, + // TODO: add the rest + } + + for _, c := range cases { + buf := new(bytes.Buffer) + err := WriteMessage(c, buf) + assert.Nil(t, err) + + msg := new(ResponseCheckTx) + err = ReadMessage(buf, msg) + assert.Nil(t, err) + + assert.Equal(t, c, msg) + } +} diff --git a/abci/types/protoreplace/protoreplace.go b/abci/types/protoreplace/protoreplace.go new file mode 100644 index 000000000..3ea0c73da --- /dev/null +++ b/abci/types/protoreplace/protoreplace.go @@ -0,0 +1,55 @@ +// +build ignore + +package main + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "os/exec" + "regexp" + "strings" +) + +// This script replaces most `[]byte` with `data.Bytes` in a `.pb.go` file. +// It was written before we realized we could use `gogo/protobuf` to achieve +// this more natively. So it's here for safe keeping in case we ever need to +// abandon `gogo/protobuf`. + +func main() { + bytePattern := regexp.MustCompile("[[][]]byte") + const oldPath = "types/types.pb.go" + const tmpPath = "types/types.pb.new" + content, err := ioutil.ReadFile(oldPath) + if err != nil { + panic("cannot read " + oldPath) + os.Exit(1) + } + lines := bytes.Split(content, []byte("\n")) + outFile, _ := os.Create(tmpPath) + wroteImport := false + for _, line_bytes := range lines { + line := string(line_bytes) + gotPackageLine := strings.HasPrefix(line, "package ") + writeImportTime := strings.HasPrefix(line, "import ") + containsDescriptor := strings.Contains(line, "Descriptor") + containsByteArray := strings.Contains(line, "[]byte") + if containsByteArray && !containsDescriptor { + line = string(bytePattern.ReplaceAll([]byte(line), []byte("data.Bytes"))) + } + if writeImportTime && !wroteImport { + wroteImport = true + fmt.Fprintf(outFile, "import \"github.com/tendermint/go-wire/data\"\n") + + } + if gotPackageLine { + fmt.Fprintf(outFile, "%s\n", "//nolint: gas") + } + fmt.Fprintf(outFile, "%s\n", line) + } + outFile.Close() + os.Remove(oldPath) + os.Rename(tmpPath, oldPath) + exec.Command("goimports", "-w", oldPath) +} diff --git a/abci/types/pubkey.go b/abci/types/pubkey.go new file mode 100644 index 000000000..e5cd5fbf3 --- /dev/null +++ b/abci/types/pubkey.go @@ -0,0 +1,16 @@ +package types + +const ( + PubKeyEd25519 = "ed25519" +) + +func Ed25519Validator(pubkey []byte, power int64) Validator { + return Validator{ + // Address: + PubKey: PubKey{ + Type: PubKeyEd25519, + Data: pubkey, + }, + Power: power, + } +} diff --git a/abci/types/result.go b/abci/types/result.go new file mode 100644 index 000000000..dbf409f4c --- /dev/null +++ b/abci/types/result.go @@ -0,0 +1,121 @@ +package types + +import ( + "bytes" + "encoding/json" + + "github.com/gogo/protobuf/jsonpb" +) + +const ( + CodeTypeOK uint32 = 0 +) + +// IsOK returns true if Code is OK. +func (r ResponseCheckTx) IsOK() bool { + return r.Code == CodeTypeOK +} + +// IsErr returns true if Code is something other than OK. +func (r ResponseCheckTx) IsErr() bool { + return r.Code != CodeTypeOK +} + +// IsOK returns true if Code is OK. +func (r ResponseDeliverTx) IsOK() bool { + return r.Code == CodeTypeOK +} + +// IsErr returns true if Code is something other than OK. +func (r ResponseDeliverTx) IsErr() bool { + return r.Code != CodeTypeOK +} + +// IsOK returns true if Code is OK. +func (r ResponseQuery) IsOK() bool { + return r.Code == CodeTypeOK +} + +// IsErr returns true if Code is something other than OK. +func (r ResponseQuery) IsErr() bool { + return r.Code != CodeTypeOK +} + +//--------------------------------------------------------------------------- +// override JSON marshalling so we dont emit defaults (ie. disable omitempty) +// note we need Unmarshal functions too because protobuf had the bright idea +// to marshal int64->string. cool. cool, cool, cool: https://developers.google.com/protocol-buffers/docs/proto3#json + +var ( + jsonpbMarshaller = jsonpb.Marshaler{ + EnumsAsInts: true, + EmitDefaults: false, + } + jsonpbUnmarshaller = jsonpb.Unmarshaler{} +) + +func (r *ResponseSetOption) MarshalJSON() ([]byte, error) { + s, err := jsonpbMarshaller.MarshalToString(r) + return []byte(s), err +} + +func (r *ResponseSetOption) UnmarshalJSON(b []byte) error { + reader := bytes.NewBuffer(b) + return jsonpbUnmarshaller.Unmarshal(reader, r) +} + +func (r *ResponseCheckTx) MarshalJSON() ([]byte, error) { + s, err := jsonpbMarshaller.MarshalToString(r) + return []byte(s), err +} + +func (r *ResponseCheckTx) UnmarshalJSON(b []byte) error { + reader := bytes.NewBuffer(b) + return jsonpbUnmarshaller.Unmarshal(reader, r) +} + +func (r *ResponseDeliverTx) MarshalJSON() ([]byte, error) { + s, err := jsonpbMarshaller.MarshalToString(r) + return []byte(s), err +} + +func (r *ResponseDeliverTx) UnmarshalJSON(b []byte) error { + reader := bytes.NewBuffer(b) + return jsonpbUnmarshaller.Unmarshal(reader, r) +} + +func (r *ResponseQuery) MarshalJSON() ([]byte, error) { + s, err := jsonpbMarshaller.MarshalToString(r) + return []byte(s), err +} + +func (r *ResponseQuery) UnmarshalJSON(b []byte) error { + reader := bytes.NewBuffer(b) + return jsonpbUnmarshaller.Unmarshal(reader, r) +} + +func (r *ResponseCommit) MarshalJSON() ([]byte, error) { + s, err := jsonpbMarshaller.MarshalToString(r) + return []byte(s), err +} + +func (r *ResponseCommit) UnmarshalJSON(b []byte) error { + reader := bytes.NewBuffer(b) + return jsonpbUnmarshaller.Unmarshal(reader, r) +} + +// Some compile time assertions to ensure we don't +// have accidental runtime surprises later on. + +// jsonEncodingRoundTripper ensures that asserted +// interfaces implement both MarshalJSON and UnmarshalJSON +type jsonRoundTripper interface { + json.Marshaler + json.Unmarshaler +} + +var _ jsonRoundTripper = (*ResponseCommit)(nil) +var _ jsonRoundTripper = (*ResponseQuery)(nil) +var _ jsonRoundTripper = (*ResponseDeliverTx)(nil) +var _ jsonRoundTripper = (*ResponseCheckTx)(nil) +var _ jsonRoundTripper = (*ResponseSetOption)(nil) diff --git a/abci/types/types.pb.go b/abci/types/types.pb.go new file mode 100644 index 000000000..a6b806fe6 --- /dev/null +++ b/abci/types/types.pb.go @@ -0,0 +1,2455 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: types/types.proto + +/* +Package types is a generated protocol buffer package. + +It is generated from these files: + types/types.proto + +It has these top-level messages: + Request + RequestEcho + RequestFlush + RequestInfo + RequestSetOption + RequestInitChain + RequestQuery + RequestBeginBlock + RequestCheckTx + RequestDeliverTx + RequestEndBlock + RequestCommit + Response + ResponseException + ResponseEcho + ResponseFlush + ResponseInfo + ResponseSetOption + ResponseInitChain + ResponseQuery + ResponseBeginBlock + ResponseCheckTx + ResponseDeliverTx + ResponseEndBlock + ResponseCommit + ConsensusParams + BlockSize + TxSize + BlockGossip + Header + Validator + SigningValidator + PubKey + Evidence +*/ +//nolint: gas +package types + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" +import _ "github.com/gogo/protobuf/gogoproto" +import common "github.com/tendermint/tmlibs/common" + +import context "golang.org/x/net/context" +import grpc "google.golang.org/grpc" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + +type Request struct { + // Types that are valid to be assigned to Value: + // *Request_Echo + // *Request_Flush + // *Request_Info + // *Request_SetOption + // *Request_InitChain + // *Request_Query + // *Request_BeginBlock + // *Request_CheckTx + // *Request_DeliverTx + // *Request_EndBlock + // *Request_Commit + Value isRequest_Value `protobuf_oneof:"value"` +} + +func (m *Request) Reset() { *m = Request{} } +func (m *Request) String() string { return proto.CompactTextString(m) } +func (*Request) ProtoMessage() {} +func (*Request) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{0} } + +type isRequest_Value interface { + isRequest_Value() +} + +type Request_Echo struct { + Echo *RequestEcho `protobuf:"bytes,2,opt,name=echo,oneof"` +} +type Request_Flush struct { + Flush *RequestFlush `protobuf:"bytes,3,opt,name=flush,oneof"` +} +type Request_Info struct { + Info *RequestInfo `protobuf:"bytes,4,opt,name=info,oneof"` +} +type Request_SetOption struct { + SetOption *RequestSetOption `protobuf:"bytes,5,opt,name=set_option,json=setOption,oneof"` +} +type Request_InitChain struct { + InitChain *RequestInitChain `protobuf:"bytes,6,opt,name=init_chain,json=initChain,oneof"` +} +type Request_Query struct { + Query *RequestQuery `protobuf:"bytes,7,opt,name=query,oneof"` +} +type Request_BeginBlock struct { + BeginBlock *RequestBeginBlock `protobuf:"bytes,8,opt,name=begin_block,json=beginBlock,oneof"` +} +type Request_CheckTx struct { + CheckTx *RequestCheckTx `protobuf:"bytes,9,opt,name=check_tx,json=checkTx,oneof"` +} +type Request_DeliverTx struct { + DeliverTx *RequestDeliverTx `protobuf:"bytes,19,opt,name=deliver_tx,json=deliverTx,oneof"` +} +type Request_EndBlock struct { + EndBlock *RequestEndBlock `protobuf:"bytes,11,opt,name=end_block,json=endBlock,oneof"` +} +type Request_Commit struct { + Commit *RequestCommit `protobuf:"bytes,12,opt,name=commit,oneof"` +} + +func (*Request_Echo) isRequest_Value() {} +func (*Request_Flush) isRequest_Value() {} +func (*Request_Info) isRequest_Value() {} +func (*Request_SetOption) isRequest_Value() {} +func (*Request_InitChain) isRequest_Value() {} +func (*Request_Query) isRequest_Value() {} +func (*Request_BeginBlock) isRequest_Value() {} +func (*Request_CheckTx) isRequest_Value() {} +func (*Request_DeliverTx) isRequest_Value() {} +func (*Request_EndBlock) isRequest_Value() {} +func (*Request_Commit) isRequest_Value() {} + +func (m *Request) GetValue() isRequest_Value { + if m != nil { + return m.Value + } + return nil +} + +func (m *Request) GetEcho() *RequestEcho { + if x, ok := m.GetValue().(*Request_Echo); ok { + return x.Echo + } + return nil +} + +func (m *Request) GetFlush() *RequestFlush { + if x, ok := m.GetValue().(*Request_Flush); ok { + return x.Flush + } + return nil +} + +func (m *Request) GetInfo() *RequestInfo { + if x, ok := m.GetValue().(*Request_Info); ok { + return x.Info + } + return nil +} + +func (m *Request) GetSetOption() *RequestSetOption { + if x, ok := m.GetValue().(*Request_SetOption); ok { + return x.SetOption + } + return nil +} + +func (m *Request) GetInitChain() *RequestInitChain { + if x, ok := m.GetValue().(*Request_InitChain); ok { + return x.InitChain + } + return nil +} + +func (m *Request) GetQuery() *RequestQuery { + if x, ok := m.GetValue().(*Request_Query); ok { + return x.Query + } + return nil +} + +func (m *Request) GetBeginBlock() *RequestBeginBlock { + if x, ok := m.GetValue().(*Request_BeginBlock); ok { + return x.BeginBlock + } + return nil +} + +func (m *Request) GetCheckTx() *RequestCheckTx { + if x, ok := m.GetValue().(*Request_CheckTx); ok { + return x.CheckTx + } + return nil +} + +func (m *Request) GetDeliverTx() *RequestDeliverTx { + if x, ok := m.GetValue().(*Request_DeliverTx); ok { + return x.DeliverTx + } + return nil +} + +func (m *Request) GetEndBlock() *RequestEndBlock { + if x, ok := m.GetValue().(*Request_EndBlock); ok { + return x.EndBlock + } + return nil +} + +func (m *Request) GetCommit() *RequestCommit { + if x, ok := m.GetValue().(*Request_Commit); ok { + return x.Commit + } + return nil +} + +// XXX_OneofFuncs is for the internal use of the proto package. +func (*Request) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { + return _Request_OneofMarshaler, _Request_OneofUnmarshaler, _Request_OneofSizer, []interface{}{ + (*Request_Echo)(nil), + (*Request_Flush)(nil), + (*Request_Info)(nil), + (*Request_SetOption)(nil), + (*Request_InitChain)(nil), + (*Request_Query)(nil), + (*Request_BeginBlock)(nil), + (*Request_CheckTx)(nil), + (*Request_DeliverTx)(nil), + (*Request_EndBlock)(nil), + (*Request_Commit)(nil), + } +} + +func _Request_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { + m := msg.(*Request) + // value + switch x := m.Value.(type) { + case *Request_Echo: + _ = b.EncodeVarint(2<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Echo); err != nil { + return err + } + case *Request_Flush: + _ = b.EncodeVarint(3<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Flush); err != nil { + return err + } + case *Request_Info: + _ = b.EncodeVarint(4<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Info); err != nil { + return err + } + case *Request_SetOption: + _ = b.EncodeVarint(5<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.SetOption); err != nil { + return err + } + case *Request_InitChain: + _ = b.EncodeVarint(6<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.InitChain); err != nil { + return err + } + case *Request_Query: + _ = b.EncodeVarint(7<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Query); err != nil { + return err + } + case *Request_BeginBlock: + _ = b.EncodeVarint(8<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.BeginBlock); err != nil { + return err + } + case *Request_CheckTx: + _ = b.EncodeVarint(9<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.CheckTx); err != nil { + return err + } + case *Request_DeliverTx: + _ = b.EncodeVarint(19<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.DeliverTx); err != nil { + return err + } + case *Request_EndBlock: + _ = b.EncodeVarint(11<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.EndBlock); err != nil { + return err + } + case *Request_Commit: + _ = b.EncodeVarint(12<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Commit); err != nil { + return err + } + case nil: + default: + return fmt.Errorf("Request.Value has unexpected type %T", x) + } + return nil +} + +func _Request_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { + m := msg.(*Request) + switch tag { + case 2: // value.echo + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(RequestEcho) + err := b.DecodeMessage(msg) + m.Value = &Request_Echo{msg} + return true, err + case 3: // value.flush + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(RequestFlush) + err := b.DecodeMessage(msg) + m.Value = &Request_Flush{msg} + return true, err + case 4: // value.info + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(RequestInfo) + err := b.DecodeMessage(msg) + m.Value = &Request_Info{msg} + return true, err + case 5: // value.set_option + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(RequestSetOption) + err := b.DecodeMessage(msg) + m.Value = &Request_SetOption{msg} + return true, err + case 6: // value.init_chain + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(RequestInitChain) + err := b.DecodeMessage(msg) + m.Value = &Request_InitChain{msg} + return true, err + case 7: // value.query + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(RequestQuery) + err := b.DecodeMessage(msg) + m.Value = &Request_Query{msg} + return true, err + case 8: // value.begin_block + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(RequestBeginBlock) + err := b.DecodeMessage(msg) + m.Value = &Request_BeginBlock{msg} + return true, err + case 9: // value.check_tx + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(RequestCheckTx) + err := b.DecodeMessage(msg) + m.Value = &Request_CheckTx{msg} + return true, err + case 19: // value.deliver_tx + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(RequestDeliverTx) + err := b.DecodeMessage(msg) + m.Value = &Request_DeliverTx{msg} + return true, err + case 11: // value.end_block + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(RequestEndBlock) + err := b.DecodeMessage(msg) + m.Value = &Request_EndBlock{msg} + return true, err + case 12: // value.commit + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(RequestCommit) + err := b.DecodeMessage(msg) + m.Value = &Request_Commit{msg} + return true, err + default: + return false, nil + } +} + +func _Request_OneofSizer(msg proto.Message) (n int) { + m := msg.(*Request) + // value + switch x := m.Value.(type) { + case *Request_Echo: + s := proto.Size(x.Echo) + n += proto.SizeVarint(2<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Request_Flush: + s := proto.Size(x.Flush) + n += proto.SizeVarint(3<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Request_Info: + s := proto.Size(x.Info) + n += proto.SizeVarint(4<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Request_SetOption: + s := proto.Size(x.SetOption) + n += proto.SizeVarint(5<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Request_InitChain: + s := proto.Size(x.InitChain) + n += proto.SizeVarint(6<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Request_Query: + s := proto.Size(x.Query) + n += proto.SizeVarint(7<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Request_BeginBlock: + s := proto.Size(x.BeginBlock) + n += proto.SizeVarint(8<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Request_CheckTx: + s := proto.Size(x.CheckTx) + n += proto.SizeVarint(9<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Request_DeliverTx: + s := proto.Size(x.DeliverTx) + n += proto.SizeVarint(19<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Request_EndBlock: + s := proto.Size(x.EndBlock) + n += proto.SizeVarint(11<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Request_Commit: + s := proto.Size(x.Commit) + n += proto.SizeVarint(12<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case nil: + default: + panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) + } + return n +} + +type RequestEcho struct { + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` +} + +func (m *RequestEcho) Reset() { *m = RequestEcho{} } +func (m *RequestEcho) String() string { return proto.CompactTextString(m) } +func (*RequestEcho) ProtoMessage() {} +func (*RequestEcho) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{1} } + +func (m *RequestEcho) GetMessage() string { + if m != nil { + return m.Message + } + return "" +} + +type RequestFlush struct { +} + +func (m *RequestFlush) Reset() { *m = RequestFlush{} } +func (m *RequestFlush) String() string { return proto.CompactTextString(m) } +func (*RequestFlush) ProtoMessage() {} +func (*RequestFlush) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{2} } + +type RequestInfo struct { + Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` +} + +func (m *RequestInfo) Reset() { *m = RequestInfo{} } +func (m *RequestInfo) String() string { return proto.CompactTextString(m) } +func (*RequestInfo) ProtoMessage() {} +func (*RequestInfo) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{3} } + +func (m *RequestInfo) GetVersion() string { + if m != nil { + return m.Version + } + return "" +} + +// nondeterministic +type RequestSetOption struct { + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *RequestSetOption) Reset() { *m = RequestSetOption{} } +func (m *RequestSetOption) String() string { return proto.CompactTextString(m) } +func (*RequestSetOption) ProtoMessage() {} +func (*RequestSetOption) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{4} } + +func (m *RequestSetOption) GetKey() string { + if m != nil { + return m.Key + } + return "" +} + +func (m *RequestSetOption) GetValue() string { + if m != nil { + return m.Value + } + return "" +} + +type RequestInitChain struct { + Time int64 `protobuf:"varint,1,opt,name=time,proto3" json:"time,omitempty"` + ChainId string `protobuf:"bytes,2,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + ConsensusParams *ConsensusParams `protobuf:"bytes,3,opt,name=consensus_params,json=consensusParams" json:"consensus_params,omitempty"` + Validators []Validator `protobuf:"bytes,4,rep,name=validators" json:"validators"` + AppStateBytes []byte `protobuf:"bytes,5,opt,name=app_state_bytes,json=appStateBytes,proto3" json:"app_state_bytes,omitempty"` +} + +func (m *RequestInitChain) Reset() { *m = RequestInitChain{} } +func (m *RequestInitChain) String() string { return proto.CompactTextString(m) } +func (*RequestInitChain) ProtoMessage() {} +func (*RequestInitChain) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{5} } + +func (m *RequestInitChain) GetTime() int64 { + if m != nil { + return m.Time + } + return 0 +} + +func (m *RequestInitChain) GetChainId() string { + if m != nil { + return m.ChainId + } + return "" +} + +func (m *RequestInitChain) GetConsensusParams() *ConsensusParams { + if m != nil { + return m.ConsensusParams + } + return nil +} + +func (m *RequestInitChain) GetValidators() []Validator { + if m != nil { + return m.Validators + } + return nil +} + +func (m *RequestInitChain) GetAppStateBytes() []byte { + if m != nil { + return m.AppStateBytes + } + return nil +} + +type RequestQuery struct { + Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` + Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` + Height int64 `protobuf:"varint,3,opt,name=height,proto3" json:"height,omitempty"` + Prove bool `protobuf:"varint,4,opt,name=prove,proto3" json:"prove,omitempty"` +} + +func (m *RequestQuery) Reset() { *m = RequestQuery{} } +func (m *RequestQuery) String() string { return proto.CompactTextString(m) } +func (*RequestQuery) ProtoMessage() {} +func (*RequestQuery) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{6} } + +func (m *RequestQuery) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +func (m *RequestQuery) GetPath() string { + if m != nil { + return m.Path + } + return "" +} + +func (m *RequestQuery) GetHeight() int64 { + if m != nil { + return m.Height + } + return 0 +} + +func (m *RequestQuery) GetProve() bool { + if m != nil { + return m.Prove + } + return false +} + +type RequestBeginBlock struct { + Hash []byte `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` + Header Header `protobuf:"bytes,2,opt,name=header" json:"header"` + Validators []SigningValidator `protobuf:"bytes,3,rep,name=validators" json:"validators"` + ByzantineValidators []Evidence `protobuf:"bytes,4,rep,name=byzantine_validators,json=byzantineValidators" json:"byzantine_validators"` +} + +func (m *RequestBeginBlock) Reset() { *m = RequestBeginBlock{} } +func (m *RequestBeginBlock) String() string { return proto.CompactTextString(m) } +func (*RequestBeginBlock) ProtoMessage() {} +func (*RequestBeginBlock) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{7} } + +func (m *RequestBeginBlock) GetHash() []byte { + if m != nil { + return m.Hash + } + return nil +} + +func (m *RequestBeginBlock) GetHeader() Header { + if m != nil { + return m.Header + } + return Header{} +} + +func (m *RequestBeginBlock) GetValidators() []SigningValidator { + if m != nil { + return m.Validators + } + return nil +} + +func (m *RequestBeginBlock) GetByzantineValidators() []Evidence { + if m != nil { + return m.ByzantineValidators + } + return nil +} + +type RequestCheckTx struct { + Tx []byte `protobuf:"bytes,1,opt,name=tx,proto3" json:"tx,omitempty"` +} + +func (m *RequestCheckTx) Reset() { *m = RequestCheckTx{} } +func (m *RequestCheckTx) String() string { return proto.CompactTextString(m) } +func (*RequestCheckTx) ProtoMessage() {} +func (*RequestCheckTx) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{8} } + +func (m *RequestCheckTx) GetTx() []byte { + if m != nil { + return m.Tx + } + return nil +} + +type RequestDeliverTx struct { + Tx []byte `protobuf:"bytes,1,opt,name=tx,proto3" json:"tx,omitempty"` +} + +func (m *RequestDeliverTx) Reset() { *m = RequestDeliverTx{} } +func (m *RequestDeliverTx) String() string { return proto.CompactTextString(m) } +func (*RequestDeliverTx) ProtoMessage() {} +func (*RequestDeliverTx) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{9} } + +func (m *RequestDeliverTx) GetTx() []byte { + if m != nil { + return m.Tx + } + return nil +} + +type RequestEndBlock struct { + Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` +} + +func (m *RequestEndBlock) Reset() { *m = RequestEndBlock{} } +func (m *RequestEndBlock) String() string { return proto.CompactTextString(m) } +func (*RequestEndBlock) ProtoMessage() {} +func (*RequestEndBlock) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{10} } + +func (m *RequestEndBlock) GetHeight() int64 { + if m != nil { + return m.Height + } + return 0 +} + +type RequestCommit struct { +} + +func (m *RequestCommit) Reset() { *m = RequestCommit{} } +func (m *RequestCommit) String() string { return proto.CompactTextString(m) } +func (*RequestCommit) ProtoMessage() {} +func (*RequestCommit) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{11} } + +type Response struct { + // Types that are valid to be assigned to Value: + // *Response_Exception + // *Response_Echo + // *Response_Flush + // *Response_Info + // *Response_SetOption + // *Response_InitChain + // *Response_Query + // *Response_BeginBlock + // *Response_CheckTx + // *Response_DeliverTx + // *Response_EndBlock + // *Response_Commit + Value isResponse_Value `protobuf_oneof:"value"` +} + +func (m *Response) Reset() { *m = Response{} } +func (m *Response) String() string { return proto.CompactTextString(m) } +func (*Response) ProtoMessage() {} +func (*Response) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{12} } + +type isResponse_Value interface { + isResponse_Value() +} + +type Response_Exception struct { + Exception *ResponseException `protobuf:"bytes,1,opt,name=exception,oneof"` +} +type Response_Echo struct { + Echo *ResponseEcho `protobuf:"bytes,2,opt,name=echo,oneof"` +} +type Response_Flush struct { + Flush *ResponseFlush `protobuf:"bytes,3,opt,name=flush,oneof"` +} +type Response_Info struct { + Info *ResponseInfo `protobuf:"bytes,4,opt,name=info,oneof"` +} +type Response_SetOption struct { + SetOption *ResponseSetOption `protobuf:"bytes,5,opt,name=set_option,json=setOption,oneof"` +} +type Response_InitChain struct { + InitChain *ResponseInitChain `protobuf:"bytes,6,opt,name=init_chain,json=initChain,oneof"` +} +type Response_Query struct { + Query *ResponseQuery `protobuf:"bytes,7,opt,name=query,oneof"` +} +type Response_BeginBlock struct { + BeginBlock *ResponseBeginBlock `protobuf:"bytes,8,opt,name=begin_block,json=beginBlock,oneof"` +} +type Response_CheckTx struct { + CheckTx *ResponseCheckTx `protobuf:"bytes,9,opt,name=check_tx,json=checkTx,oneof"` +} +type Response_DeliverTx struct { + DeliverTx *ResponseDeliverTx `protobuf:"bytes,10,opt,name=deliver_tx,json=deliverTx,oneof"` +} +type Response_EndBlock struct { + EndBlock *ResponseEndBlock `protobuf:"bytes,11,opt,name=end_block,json=endBlock,oneof"` +} +type Response_Commit struct { + Commit *ResponseCommit `protobuf:"bytes,12,opt,name=commit,oneof"` +} + +func (*Response_Exception) isResponse_Value() {} +func (*Response_Echo) isResponse_Value() {} +func (*Response_Flush) isResponse_Value() {} +func (*Response_Info) isResponse_Value() {} +func (*Response_SetOption) isResponse_Value() {} +func (*Response_InitChain) isResponse_Value() {} +func (*Response_Query) isResponse_Value() {} +func (*Response_BeginBlock) isResponse_Value() {} +func (*Response_CheckTx) isResponse_Value() {} +func (*Response_DeliverTx) isResponse_Value() {} +func (*Response_EndBlock) isResponse_Value() {} +func (*Response_Commit) isResponse_Value() {} + +func (m *Response) GetValue() isResponse_Value { + if m != nil { + return m.Value + } + return nil +} + +func (m *Response) GetException() *ResponseException { + if x, ok := m.GetValue().(*Response_Exception); ok { + return x.Exception + } + return nil +} + +func (m *Response) GetEcho() *ResponseEcho { + if x, ok := m.GetValue().(*Response_Echo); ok { + return x.Echo + } + return nil +} + +func (m *Response) GetFlush() *ResponseFlush { + if x, ok := m.GetValue().(*Response_Flush); ok { + return x.Flush + } + return nil +} + +func (m *Response) GetInfo() *ResponseInfo { + if x, ok := m.GetValue().(*Response_Info); ok { + return x.Info + } + return nil +} + +func (m *Response) GetSetOption() *ResponseSetOption { + if x, ok := m.GetValue().(*Response_SetOption); ok { + return x.SetOption + } + return nil +} + +func (m *Response) GetInitChain() *ResponseInitChain { + if x, ok := m.GetValue().(*Response_InitChain); ok { + return x.InitChain + } + return nil +} + +func (m *Response) GetQuery() *ResponseQuery { + if x, ok := m.GetValue().(*Response_Query); ok { + return x.Query + } + return nil +} + +func (m *Response) GetBeginBlock() *ResponseBeginBlock { + if x, ok := m.GetValue().(*Response_BeginBlock); ok { + return x.BeginBlock + } + return nil +} + +func (m *Response) GetCheckTx() *ResponseCheckTx { + if x, ok := m.GetValue().(*Response_CheckTx); ok { + return x.CheckTx + } + return nil +} + +func (m *Response) GetDeliverTx() *ResponseDeliverTx { + if x, ok := m.GetValue().(*Response_DeliverTx); ok { + return x.DeliverTx + } + return nil +} + +func (m *Response) GetEndBlock() *ResponseEndBlock { + if x, ok := m.GetValue().(*Response_EndBlock); ok { + return x.EndBlock + } + return nil +} + +func (m *Response) GetCommit() *ResponseCommit { + if x, ok := m.GetValue().(*Response_Commit); ok { + return x.Commit + } + return nil +} + +// XXX_OneofFuncs is for the internal use of the proto package. +func (*Response) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { + return _Response_OneofMarshaler, _Response_OneofUnmarshaler, _Response_OneofSizer, []interface{}{ + (*Response_Exception)(nil), + (*Response_Echo)(nil), + (*Response_Flush)(nil), + (*Response_Info)(nil), + (*Response_SetOption)(nil), + (*Response_InitChain)(nil), + (*Response_Query)(nil), + (*Response_BeginBlock)(nil), + (*Response_CheckTx)(nil), + (*Response_DeliverTx)(nil), + (*Response_EndBlock)(nil), + (*Response_Commit)(nil), + } +} + +func _Response_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { + m := msg.(*Response) + // value + switch x := m.Value.(type) { + case *Response_Exception: + _ = b.EncodeVarint(1<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Exception); err != nil { + return err + } + case *Response_Echo: + _ = b.EncodeVarint(2<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Echo); err != nil { + return err + } + case *Response_Flush: + _ = b.EncodeVarint(3<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Flush); err != nil { + return err + } + case *Response_Info: + _ = b.EncodeVarint(4<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Info); err != nil { + return err + } + case *Response_SetOption: + _ = b.EncodeVarint(5<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.SetOption); err != nil { + return err + } + case *Response_InitChain: + _ = b.EncodeVarint(6<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.InitChain); err != nil { + return err + } + case *Response_Query: + _ = b.EncodeVarint(7<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Query); err != nil { + return err + } + case *Response_BeginBlock: + _ = b.EncodeVarint(8<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.BeginBlock); err != nil { + return err + } + case *Response_CheckTx: + _ = b.EncodeVarint(9<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.CheckTx); err != nil { + return err + } + case *Response_DeliverTx: + _ = b.EncodeVarint(10<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.DeliverTx); err != nil { + return err + } + case *Response_EndBlock: + _ = b.EncodeVarint(11<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.EndBlock); err != nil { + return err + } + case *Response_Commit: + _ = b.EncodeVarint(12<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Commit); err != nil { + return err + } + case nil: + default: + return fmt.Errorf("Response.Value has unexpected type %T", x) + } + return nil +} + +func _Response_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { + m := msg.(*Response) + switch tag { + case 1: // value.exception + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(ResponseException) + err := b.DecodeMessage(msg) + m.Value = &Response_Exception{msg} + return true, err + case 2: // value.echo + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(ResponseEcho) + err := b.DecodeMessage(msg) + m.Value = &Response_Echo{msg} + return true, err + case 3: // value.flush + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(ResponseFlush) + err := b.DecodeMessage(msg) + m.Value = &Response_Flush{msg} + return true, err + case 4: // value.info + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(ResponseInfo) + err := b.DecodeMessage(msg) + m.Value = &Response_Info{msg} + return true, err + case 5: // value.set_option + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(ResponseSetOption) + err := b.DecodeMessage(msg) + m.Value = &Response_SetOption{msg} + return true, err + case 6: // value.init_chain + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(ResponseInitChain) + err := b.DecodeMessage(msg) + m.Value = &Response_InitChain{msg} + return true, err + case 7: // value.query + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(ResponseQuery) + err := b.DecodeMessage(msg) + m.Value = &Response_Query{msg} + return true, err + case 8: // value.begin_block + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(ResponseBeginBlock) + err := b.DecodeMessage(msg) + m.Value = &Response_BeginBlock{msg} + return true, err + case 9: // value.check_tx + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(ResponseCheckTx) + err := b.DecodeMessage(msg) + m.Value = &Response_CheckTx{msg} + return true, err + case 10: // value.deliver_tx + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(ResponseDeliverTx) + err := b.DecodeMessage(msg) + m.Value = &Response_DeliverTx{msg} + return true, err + case 11: // value.end_block + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(ResponseEndBlock) + err := b.DecodeMessage(msg) + m.Value = &Response_EndBlock{msg} + return true, err + case 12: // value.commit + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(ResponseCommit) + err := b.DecodeMessage(msg) + m.Value = &Response_Commit{msg} + return true, err + default: + return false, nil + } +} + +func _Response_OneofSizer(msg proto.Message) (n int) { + m := msg.(*Response) + // value + switch x := m.Value.(type) { + case *Response_Exception: + s := proto.Size(x.Exception) + n += proto.SizeVarint(1<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Response_Echo: + s := proto.Size(x.Echo) + n += proto.SizeVarint(2<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Response_Flush: + s := proto.Size(x.Flush) + n += proto.SizeVarint(3<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Response_Info: + s := proto.Size(x.Info) + n += proto.SizeVarint(4<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Response_SetOption: + s := proto.Size(x.SetOption) + n += proto.SizeVarint(5<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Response_InitChain: + s := proto.Size(x.InitChain) + n += proto.SizeVarint(6<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Response_Query: + s := proto.Size(x.Query) + n += proto.SizeVarint(7<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Response_BeginBlock: + s := proto.Size(x.BeginBlock) + n += proto.SizeVarint(8<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Response_CheckTx: + s := proto.Size(x.CheckTx) + n += proto.SizeVarint(9<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Response_DeliverTx: + s := proto.Size(x.DeliverTx) + n += proto.SizeVarint(10<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Response_EndBlock: + s := proto.Size(x.EndBlock) + n += proto.SizeVarint(11<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Response_Commit: + s := proto.Size(x.Commit) + n += proto.SizeVarint(12<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case nil: + default: + panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) + } + return n +} + +// nondeterministic +type ResponseException struct { + Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` +} + +func (m *ResponseException) Reset() { *m = ResponseException{} } +func (m *ResponseException) String() string { return proto.CompactTextString(m) } +func (*ResponseException) ProtoMessage() {} +func (*ResponseException) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{13} } + +func (m *ResponseException) GetError() string { + if m != nil { + return m.Error + } + return "" +} + +type ResponseEcho struct { + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` +} + +func (m *ResponseEcho) Reset() { *m = ResponseEcho{} } +func (m *ResponseEcho) String() string { return proto.CompactTextString(m) } +func (*ResponseEcho) ProtoMessage() {} +func (*ResponseEcho) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{14} } + +func (m *ResponseEcho) GetMessage() string { + if m != nil { + return m.Message + } + return "" +} + +type ResponseFlush struct { +} + +func (m *ResponseFlush) Reset() { *m = ResponseFlush{} } +func (m *ResponseFlush) String() string { return proto.CompactTextString(m) } +func (*ResponseFlush) ProtoMessage() {} +func (*ResponseFlush) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{15} } + +type ResponseInfo struct { + Data string `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` + Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` + LastBlockHeight int64 `protobuf:"varint,3,opt,name=last_block_height,json=lastBlockHeight,proto3" json:"last_block_height,omitempty"` + LastBlockAppHash []byte `protobuf:"bytes,4,opt,name=last_block_app_hash,json=lastBlockAppHash,proto3" json:"last_block_app_hash,omitempty"` +} + +func (m *ResponseInfo) Reset() { *m = ResponseInfo{} } +func (m *ResponseInfo) String() string { return proto.CompactTextString(m) } +func (*ResponseInfo) ProtoMessage() {} +func (*ResponseInfo) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{16} } + +func (m *ResponseInfo) GetData() string { + if m != nil { + return m.Data + } + return "" +} + +func (m *ResponseInfo) GetVersion() string { + if m != nil { + return m.Version + } + return "" +} + +func (m *ResponseInfo) GetLastBlockHeight() int64 { + if m != nil { + return m.LastBlockHeight + } + return 0 +} + +func (m *ResponseInfo) GetLastBlockAppHash() []byte { + if m != nil { + return m.LastBlockAppHash + } + return nil +} + +// nondeterministic +type ResponseSetOption struct { + Code uint32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` + // bytes data = 2; + Log string `protobuf:"bytes,3,opt,name=log,proto3" json:"log,omitempty"` + Info string `protobuf:"bytes,4,opt,name=info,proto3" json:"info,omitempty"` +} + +func (m *ResponseSetOption) Reset() { *m = ResponseSetOption{} } +func (m *ResponseSetOption) String() string { return proto.CompactTextString(m) } +func (*ResponseSetOption) ProtoMessage() {} +func (*ResponseSetOption) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{17} } + +func (m *ResponseSetOption) GetCode() uint32 { + if m != nil { + return m.Code + } + return 0 +} + +func (m *ResponseSetOption) GetLog() string { + if m != nil { + return m.Log + } + return "" +} + +func (m *ResponseSetOption) GetInfo() string { + if m != nil { + return m.Info + } + return "" +} + +type ResponseInitChain struct { + ConsensusParams *ConsensusParams `protobuf:"bytes,1,opt,name=consensus_params,json=consensusParams" json:"consensus_params,omitempty"` + Validators []Validator `protobuf:"bytes,2,rep,name=validators" json:"validators"` +} + +func (m *ResponseInitChain) Reset() { *m = ResponseInitChain{} } +func (m *ResponseInitChain) String() string { return proto.CompactTextString(m) } +func (*ResponseInitChain) ProtoMessage() {} +func (*ResponseInitChain) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{18} } + +func (m *ResponseInitChain) GetConsensusParams() *ConsensusParams { + if m != nil { + return m.ConsensusParams + } + return nil +} + +func (m *ResponseInitChain) GetValidators() []Validator { + if m != nil { + return m.Validators + } + return nil +} + +type ResponseQuery struct { + Code uint32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` + // bytes data = 2; // use "value" instead. + Log string `protobuf:"bytes,3,opt,name=log,proto3" json:"log,omitempty"` + Info string `protobuf:"bytes,4,opt,name=info,proto3" json:"info,omitempty"` + Index int64 `protobuf:"varint,5,opt,name=index,proto3" json:"index,omitempty"` + Key []byte `protobuf:"bytes,6,opt,name=key,proto3" json:"key,omitempty"` + Value []byte `protobuf:"bytes,7,opt,name=value,proto3" json:"value,omitempty"` + Proof []byte `protobuf:"bytes,8,opt,name=proof,proto3" json:"proof,omitempty"` + Height int64 `protobuf:"varint,9,opt,name=height,proto3" json:"height,omitempty"` +} + +func (m *ResponseQuery) Reset() { *m = ResponseQuery{} } +func (m *ResponseQuery) String() string { return proto.CompactTextString(m) } +func (*ResponseQuery) ProtoMessage() {} +func (*ResponseQuery) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{19} } + +func (m *ResponseQuery) GetCode() uint32 { + if m != nil { + return m.Code + } + return 0 +} + +func (m *ResponseQuery) GetLog() string { + if m != nil { + return m.Log + } + return "" +} + +func (m *ResponseQuery) GetInfo() string { + if m != nil { + return m.Info + } + return "" +} + +func (m *ResponseQuery) GetIndex() int64 { + if m != nil { + return m.Index + } + return 0 +} + +func (m *ResponseQuery) GetKey() []byte { + if m != nil { + return m.Key + } + return nil +} + +func (m *ResponseQuery) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +func (m *ResponseQuery) GetProof() []byte { + if m != nil { + return m.Proof + } + return nil +} + +func (m *ResponseQuery) GetHeight() int64 { + if m != nil { + return m.Height + } + return 0 +} + +type ResponseBeginBlock struct { + Tags []common.KVPair `protobuf:"bytes,1,rep,name=tags" json:"tags,omitempty"` +} + +func (m *ResponseBeginBlock) Reset() { *m = ResponseBeginBlock{} } +func (m *ResponseBeginBlock) String() string { return proto.CompactTextString(m) } +func (*ResponseBeginBlock) ProtoMessage() {} +func (*ResponseBeginBlock) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{20} } + +func (m *ResponseBeginBlock) GetTags() []common.KVPair { + if m != nil { + return m.Tags + } + return nil +} + +type ResponseCheckTx struct { + Code uint32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` + Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` + Log string `protobuf:"bytes,3,opt,name=log,proto3" json:"log,omitempty"` + Info string `protobuf:"bytes,4,opt,name=info,proto3" json:"info,omitempty"` + GasWanted int64 `protobuf:"varint,5,opt,name=gas_wanted,json=gasWanted,proto3" json:"gas_wanted,omitempty"` + GasUsed int64 `protobuf:"varint,6,opt,name=gas_used,json=gasUsed,proto3" json:"gas_used,omitempty"` + Tags []common.KVPair `protobuf:"bytes,7,rep,name=tags" json:"tags,omitempty"` + Fee common.KI64Pair `protobuf:"bytes,8,opt,name=fee" json:"fee"` +} + +func (m *ResponseCheckTx) Reset() { *m = ResponseCheckTx{} } +func (m *ResponseCheckTx) String() string { return proto.CompactTextString(m) } +func (*ResponseCheckTx) ProtoMessage() {} +func (*ResponseCheckTx) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{21} } + +func (m *ResponseCheckTx) GetCode() uint32 { + if m != nil { + return m.Code + } + return 0 +} + +func (m *ResponseCheckTx) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +func (m *ResponseCheckTx) GetLog() string { + if m != nil { + return m.Log + } + return "" +} + +func (m *ResponseCheckTx) GetInfo() string { + if m != nil { + return m.Info + } + return "" +} + +func (m *ResponseCheckTx) GetGasWanted() int64 { + if m != nil { + return m.GasWanted + } + return 0 +} + +func (m *ResponseCheckTx) GetGasUsed() int64 { + if m != nil { + return m.GasUsed + } + return 0 +} + +func (m *ResponseCheckTx) GetTags() []common.KVPair { + if m != nil { + return m.Tags + } + return nil +} + +func (m *ResponseCheckTx) GetFee() common.KI64Pair { + if m != nil { + return m.Fee + } + return common.KI64Pair{} +} + +type ResponseDeliverTx struct { + Code uint32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` + Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` + Log string `protobuf:"bytes,3,opt,name=log,proto3" json:"log,omitempty"` + Info string `protobuf:"bytes,4,opt,name=info,proto3" json:"info,omitempty"` + GasWanted int64 `protobuf:"varint,5,opt,name=gas_wanted,json=gasWanted,proto3" json:"gas_wanted,omitempty"` + GasUsed int64 `protobuf:"varint,6,opt,name=gas_used,json=gasUsed,proto3" json:"gas_used,omitempty"` + Tags []common.KVPair `protobuf:"bytes,7,rep,name=tags" json:"tags,omitempty"` + Fee common.KI64Pair `protobuf:"bytes,8,opt,name=fee" json:"fee"` +} + +func (m *ResponseDeliverTx) Reset() { *m = ResponseDeliverTx{} } +func (m *ResponseDeliverTx) String() string { return proto.CompactTextString(m) } +func (*ResponseDeliverTx) ProtoMessage() {} +func (*ResponseDeliverTx) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{22} } + +func (m *ResponseDeliverTx) GetCode() uint32 { + if m != nil { + return m.Code + } + return 0 +} + +func (m *ResponseDeliverTx) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +func (m *ResponseDeliverTx) GetLog() string { + if m != nil { + return m.Log + } + return "" +} + +func (m *ResponseDeliverTx) GetInfo() string { + if m != nil { + return m.Info + } + return "" +} + +func (m *ResponseDeliverTx) GetGasWanted() int64 { + if m != nil { + return m.GasWanted + } + return 0 +} + +func (m *ResponseDeliverTx) GetGasUsed() int64 { + if m != nil { + return m.GasUsed + } + return 0 +} + +func (m *ResponseDeliverTx) GetTags() []common.KVPair { + if m != nil { + return m.Tags + } + return nil +} + +func (m *ResponseDeliverTx) GetFee() common.KI64Pair { + if m != nil { + return m.Fee + } + return common.KI64Pair{} +} + +type ResponseEndBlock struct { + ValidatorUpdates []Validator `protobuf:"bytes,1,rep,name=validator_updates,json=validatorUpdates" json:"validator_updates"` + ConsensusParamUpdates *ConsensusParams `protobuf:"bytes,2,opt,name=consensus_param_updates,json=consensusParamUpdates" json:"consensus_param_updates,omitempty"` + Tags []common.KVPair `protobuf:"bytes,3,rep,name=tags" json:"tags,omitempty"` +} + +func (m *ResponseEndBlock) Reset() { *m = ResponseEndBlock{} } +func (m *ResponseEndBlock) String() string { return proto.CompactTextString(m) } +func (*ResponseEndBlock) ProtoMessage() {} +func (*ResponseEndBlock) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{23} } + +func (m *ResponseEndBlock) GetValidatorUpdates() []Validator { + if m != nil { + return m.ValidatorUpdates + } + return nil +} + +func (m *ResponseEndBlock) GetConsensusParamUpdates() *ConsensusParams { + if m != nil { + return m.ConsensusParamUpdates + } + return nil +} + +func (m *ResponseEndBlock) GetTags() []common.KVPair { + if m != nil { + return m.Tags + } + return nil +} + +type ResponseCommit struct { + // reserve 1 + Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` +} + +func (m *ResponseCommit) Reset() { *m = ResponseCommit{} } +func (m *ResponseCommit) String() string { return proto.CompactTextString(m) } +func (*ResponseCommit) ProtoMessage() {} +func (*ResponseCommit) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{24} } + +func (m *ResponseCommit) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +// ConsensusParams contains all consensus-relevant parameters +// that can be adjusted by the abci app +type ConsensusParams struct { + BlockSize *BlockSize `protobuf:"bytes,1,opt,name=block_size,json=blockSize" json:"block_size,omitempty"` + TxSize *TxSize `protobuf:"bytes,2,opt,name=tx_size,json=txSize" json:"tx_size,omitempty"` + BlockGossip *BlockGossip `protobuf:"bytes,3,opt,name=block_gossip,json=blockGossip" json:"block_gossip,omitempty"` +} + +func (m *ConsensusParams) Reset() { *m = ConsensusParams{} } +func (m *ConsensusParams) String() string { return proto.CompactTextString(m) } +func (*ConsensusParams) ProtoMessage() {} +func (*ConsensusParams) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{25} } + +func (m *ConsensusParams) GetBlockSize() *BlockSize { + if m != nil { + return m.BlockSize + } + return nil +} + +func (m *ConsensusParams) GetTxSize() *TxSize { + if m != nil { + return m.TxSize + } + return nil +} + +func (m *ConsensusParams) GetBlockGossip() *BlockGossip { + if m != nil { + return m.BlockGossip + } + return nil +} + +// BlockSize contain limits on the block size. +type BlockSize struct { + MaxBytes int32 `protobuf:"varint,1,opt,name=max_bytes,json=maxBytes,proto3" json:"max_bytes,omitempty"` + MaxTxs int32 `protobuf:"varint,2,opt,name=max_txs,json=maxTxs,proto3" json:"max_txs,omitempty"` + MaxGas int64 `protobuf:"varint,3,opt,name=max_gas,json=maxGas,proto3" json:"max_gas,omitempty"` +} + +func (m *BlockSize) Reset() { *m = BlockSize{} } +func (m *BlockSize) String() string { return proto.CompactTextString(m) } +func (*BlockSize) ProtoMessage() {} +func (*BlockSize) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{26} } + +func (m *BlockSize) GetMaxBytes() int32 { + if m != nil { + return m.MaxBytes + } + return 0 +} + +func (m *BlockSize) GetMaxTxs() int32 { + if m != nil { + return m.MaxTxs + } + return 0 +} + +func (m *BlockSize) GetMaxGas() int64 { + if m != nil { + return m.MaxGas + } + return 0 +} + +// TxSize contain limits on the tx size. +type TxSize struct { + MaxBytes int32 `protobuf:"varint,1,opt,name=max_bytes,json=maxBytes,proto3" json:"max_bytes,omitempty"` + MaxGas int64 `protobuf:"varint,2,opt,name=max_gas,json=maxGas,proto3" json:"max_gas,omitempty"` +} + +func (m *TxSize) Reset() { *m = TxSize{} } +func (m *TxSize) String() string { return proto.CompactTextString(m) } +func (*TxSize) ProtoMessage() {} +func (*TxSize) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{27} } + +func (m *TxSize) GetMaxBytes() int32 { + if m != nil { + return m.MaxBytes + } + return 0 +} + +func (m *TxSize) GetMaxGas() int64 { + if m != nil { + return m.MaxGas + } + return 0 +} + +// BlockGossip determine consensus critical +// elements of how blocks are gossiped +type BlockGossip struct { + // Note: must not be 0 + BlockPartSizeBytes int32 `protobuf:"varint,1,opt,name=block_part_size_bytes,json=blockPartSizeBytes,proto3" json:"block_part_size_bytes,omitempty"` +} + +func (m *BlockGossip) Reset() { *m = BlockGossip{} } +func (m *BlockGossip) String() string { return proto.CompactTextString(m) } +func (*BlockGossip) ProtoMessage() {} +func (*BlockGossip) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{28} } + +func (m *BlockGossip) GetBlockPartSizeBytes() int32 { + if m != nil { + return m.BlockPartSizeBytes + } + return 0 +} + +// just the minimum the app might need +type Header struct { + // basics + ChainID string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + Height int64 `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"` + Time int64 `protobuf:"varint,3,opt,name=time,proto3" json:"time,omitempty"` + // txs + NumTxs int32 `protobuf:"varint,4,opt,name=num_txs,json=numTxs,proto3" json:"num_txs,omitempty"` + TotalTxs int64 `protobuf:"varint,5,opt,name=total_txs,json=totalTxs,proto3" json:"total_txs,omitempty"` + // hashes + LastBlockHash []byte `protobuf:"bytes,6,opt,name=last_block_hash,json=lastBlockHash,proto3" json:"last_block_hash,omitempty"` + ValidatorsHash []byte `protobuf:"bytes,7,opt,name=validators_hash,json=validatorsHash,proto3" json:"validators_hash,omitempty"` + AppHash []byte `protobuf:"bytes,8,opt,name=app_hash,json=appHash,proto3" json:"app_hash,omitempty"` + // consensus + Proposer Validator `protobuf:"bytes,9,opt,name=proposer" json:"proposer"` +} + +func (m *Header) Reset() { *m = Header{} } +func (m *Header) String() string { return proto.CompactTextString(m) } +func (*Header) ProtoMessage() {} +func (*Header) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{29} } + +func (m *Header) GetChainID() string { + if m != nil { + return m.ChainID + } + return "" +} + +func (m *Header) GetHeight() int64 { + if m != nil { + return m.Height + } + return 0 +} + +func (m *Header) GetTime() int64 { + if m != nil { + return m.Time + } + return 0 +} + +func (m *Header) GetNumTxs() int32 { + if m != nil { + return m.NumTxs + } + return 0 +} + +func (m *Header) GetTotalTxs() int64 { + if m != nil { + return m.TotalTxs + } + return 0 +} + +func (m *Header) GetLastBlockHash() []byte { + if m != nil { + return m.LastBlockHash + } + return nil +} + +func (m *Header) GetValidatorsHash() []byte { + if m != nil { + return m.ValidatorsHash + } + return nil +} + +func (m *Header) GetAppHash() []byte { + if m != nil { + return m.AppHash + } + return nil +} + +func (m *Header) GetProposer() Validator { + if m != nil { + return m.Proposer + } + return Validator{} +} + +// Validator +type Validator struct { + Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + PubKey PubKey `protobuf:"bytes,2,opt,name=pub_key,json=pubKey" json:"pub_key"` + Power int64 `protobuf:"varint,3,opt,name=power,proto3" json:"power,omitempty"` +} + +func (m *Validator) Reset() { *m = Validator{} } +func (m *Validator) String() string { return proto.CompactTextString(m) } +func (*Validator) ProtoMessage() {} +func (*Validator) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{30} } + +func (m *Validator) GetAddress() []byte { + if m != nil { + return m.Address + } + return nil +} + +func (m *Validator) GetPubKey() PubKey { + if m != nil { + return m.PubKey + } + return PubKey{} +} + +func (m *Validator) GetPower() int64 { + if m != nil { + return m.Power + } + return 0 +} + +// Validator with an extra bool +type SigningValidator struct { + Validator Validator `protobuf:"bytes,1,opt,name=validator" json:"validator"` + SignedLastBlock bool `protobuf:"varint,2,opt,name=signed_last_block,json=signedLastBlock,proto3" json:"signed_last_block,omitempty"` +} + +func (m *SigningValidator) Reset() { *m = SigningValidator{} } +func (m *SigningValidator) String() string { return proto.CompactTextString(m) } +func (*SigningValidator) ProtoMessage() {} +func (*SigningValidator) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{31} } + +func (m *SigningValidator) GetValidator() Validator { + if m != nil { + return m.Validator + } + return Validator{} +} + +func (m *SigningValidator) GetSignedLastBlock() bool { + if m != nil { + return m.SignedLastBlock + } + return false +} + +type PubKey struct { + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` +} + +func (m *PubKey) Reset() { *m = PubKey{} } +func (m *PubKey) String() string { return proto.CompactTextString(m) } +func (*PubKey) ProtoMessage() {} +func (*PubKey) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{32} } + +func (m *PubKey) GetType() string { + if m != nil { + return m.Type + } + return "" +} + +func (m *PubKey) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +type Evidence struct { + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + Validator Validator `protobuf:"bytes,2,opt,name=validator" json:"validator"` + Height int64 `protobuf:"varint,3,opt,name=height,proto3" json:"height,omitempty"` + Time int64 `protobuf:"varint,4,opt,name=time,proto3" json:"time,omitempty"` + TotalVotingPower int64 `protobuf:"varint,5,opt,name=total_voting_power,json=totalVotingPower,proto3" json:"total_voting_power,omitempty"` +} + +func (m *Evidence) Reset() { *m = Evidence{} } +func (m *Evidence) String() string { return proto.CompactTextString(m) } +func (*Evidence) ProtoMessage() {} +func (*Evidence) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{33} } + +func (m *Evidence) GetType() string { + if m != nil { + return m.Type + } + return "" +} + +func (m *Evidence) GetValidator() Validator { + if m != nil { + return m.Validator + } + return Validator{} +} + +func (m *Evidence) GetHeight() int64 { + if m != nil { + return m.Height + } + return 0 +} + +func (m *Evidence) GetTime() int64 { + if m != nil { + return m.Time + } + return 0 +} + +func (m *Evidence) GetTotalVotingPower() int64 { + if m != nil { + return m.TotalVotingPower + } + return 0 +} + +func init() { + proto.RegisterType((*Request)(nil), "types.Request") + proto.RegisterType((*RequestEcho)(nil), "types.RequestEcho") + proto.RegisterType((*RequestFlush)(nil), "types.RequestFlush") + proto.RegisterType((*RequestInfo)(nil), "types.RequestInfo") + proto.RegisterType((*RequestSetOption)(nil), "types.RequestSetOption") + proto.RegisterType((*RequestInitChain)(nil), "types.RequestInitChain") + proto.RegisterType((*RequestQuery)(nil), "types.RequestQuery") + proto.RegisterType((*RequestBeginBlock)(nil), "types.RequestBeginBlock") + proto.RegisterType((*RequestCheckTx)(nil), "types.RequestCheckTx") + proto.RegisterType((*RequestDeliverTx)(nil), "types.RequestDeliverTx") + proto.RegisterType((*RequestEndBlock)(nil), "types.RequestEndBlock") + proto.RegisterType((*RequestCommit)(nil), "types.RequestCommit") + proto.RegisterType((*Response)(nil), "types.Response") + proto.RegisterType((*ResponseException)(nil), "types.ResponseException") + proto.RegisterType((*ResponseEcho)(nil), "types.ResponseEcho") + proto.RegisterType((*ResponseFlush)(nil), "types.ResponseFlush") + proto.RegisterType((*ResponseInfo)(nil), "types.ResponseInfo") + proto.RegisterType((*ResponseSetOption)(nil), "types.ResponseSetOption") + proto.RegisterType((*ResponseInitChain)(nil), "types.ResponseInitChain") + proto.RegisterType((*ResponseQuery)(nil), "types.ResponseQuery") + proto.RegisterType((*ResponseBeginBlock)(nil), "types.ResponseBeginBlock") + proto.RegisterType((*ResponseCheckTx)(nil), "types.ResponseCheckTx") + proto.RegisterType((*ResponseDeliverTx)(nil), "types.ResponseDeliverTx") + proto.RegisterType((*ResponseEndBlock)(nil), "types.ResponseEndBlock") + proto.RegisterType((*ResponseCommit)(nil), "types.ResponseCommit") + proto.RegisterType((*ConsensusParams)(nil), "types.ConsensusParams") + proto.RegisterType((*BlockSize)(nil), "types.BlockSize") + proto.RegisterType((*TxSize)(nil), "types.TxSize") + proto.RegisterType((*BlockGossip)(nil), "types.BlockGossip") + proto.RegisterType((*Header)(nil), "types.Header") + proto.RegisterType((*Validator)(nil), "types.Validator") + proto.RegisterType((*SigningValidator)(nil), "types.SigningValidator") + proto.RegisterType((*PubKey)(nil), "types.PubKey") + proto.RegisterType((*Evidence)(nil), "types.Evidence") +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// Client API for ABCIApplication service + +type ABCIApplicationClient interface { + Echo(ctx context.Context, in *RequestEcho, opts ...grpc.CallOption) (*ResponseEcho, error) + Flush(ctx context.Context, in *RequestFlush, opts ...grpc.CallOption) (*ResponseFlush, error) + Info(ctx context.Context, in *RequestInfo, opts ...grpc.CallOption) (*ResponseInfo, error) + SetOption(ctx context.Context, in *RequestSetOption, opts ...grpc.CallOption) (*ResponseSetOption, error) + DeliverTx(ctx context.Context, in *RequestDeliverTx, opts ...grpc.CallOption) (*ResponseDeliverTx, error) + CheckTx(ctx context.Context, in *RequestCheckTx, opts ...grpc.CallOption) (*ResponseCheckTx, error) + Query(ctx context.Context, in *RequestQuery, opts ...grpc.CallOption) (*ResponseQuery, error) + Commit(ctx context.Context, in *RequestCommit, opts ...grpc.CallOption) (*ResponseCommit, error) + InitChain(ctx context.Context, in *RequestInitChain, opts ...grpc.CallOption) (*ResponseInitChain, error) + BeginBlock(ctx context.Context, in *RequestBeginBlock, opts ...grpc.CallOption) (*ResponseBeginBlock, error) + EndBlock(ctx context.Context, in *RequestEndBlock, opts ...grpc.CallOption) (*ResponseEndBlock, error) +} + +type aBCIApplicationClient struct { + cc *grpc.ClientConn +} + +func NewABCIApplicationClient(cc *grpc.ClientConn) ABCIApplicationClient { + return &aBCIApplicationClient{cc} +} + +func (c *aBCIApplicationClient) Echo(ctx context.Context, in *RequestEcho, opts ...grpc.CallOption) (*ResponseEcho, error) { + out := new(ResponseEcho) + err := grpc.Invoke(ctx, "/types.ABCIApplication/Echo", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *aBCIApplicationClient) Flush(ctx context.Context, in *RequestFlush, opts ...grpc.CallOption) (*ResponseFlush, error) { + out := new(ResponseFlush) + err := grpc.Invoke(ctx, "/types.ABCIApplication/Flush", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *aBCIApplicationClient) Info(ctx context.Context, in *RequestInfo, opts ...grpc.CallOption) (*ResponseInfo, error) { + out := new(ResponseInfo) + err := grpc.Invoke(ctx, "/types.ABCIApplication/Info", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *aBCIApplicationClient) SetOption(ctx context.Context, in *RequestSetOption, opts ...grpc.CallOption) (*ResponseSetOption, error) { + out := new(ResponseSetOption) + err := grpc.Invoke(ctx, "/types.ABCIApplication/SetOption", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *aBCIApplicationClient) DeliverTx(ctx context.Context, in *RequestDeliverTx, opts ...grpc.CallOption) (*ResponseDeliverTx, error) { + out := new(ResponseDeliverTx) + err := grpc.Invoke(ctx, "/types.ABCIApplication/DeliverTx", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *aBCIApplicationClient) CheckTx(ctx context.Context, in *RequestCheckTx, opts ...grpc.CallOption) (*ResponseCheckTx, error) { + out := new(ResponseCheckTx) + err := grpc.Invoke(ctx, "/types.ABCIApplication/CheckTx", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *aBCIApplicationClient) Query(ctx context.Context, in *RequestQuery, opts ...grpc.CallOption) (*ResponseQuery, error) { + out := new(ResponseQuery) + err := grpc.Invoke(ctx, "/types.ABCIApplication/Query", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *aBCIApplicationClient) Commit(ctx context.Context, in *RequestCommit, opts ...grpc.CallOption) (*ResponseCommit, error) { + out := new(ResponseCommit) + err := grpc.Invoke(ctx, "/types.ABCIApplication/Commit", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *aBCIApplicationClient) InitChain(ctx context.Context, in *RequestInitChain, opts ...grpc.CallOption) (*ResponseInitChain, error) { + out := new(ResponseInitChain) + err := grpc.Invoke(ctx, "/types.ABCIApplication/InitChain", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *aBCIApplicationClient) BeginBlock(ctx context.Context, in *RequestBeginBlock, opts ...grpc.CallOption) (*ResponseBeginBlock, error) { + out := new(ResponseBeginBlock) + err := grpc.Invoke(ctx, "/types.ABCIApplication/BeginBlock", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *aBCIApplicationClient) EndBlock(ctx context.Context, in *RequestEndBlock, opts ...grpc.CallOption) (*ResponseEndBlock, error) { + out := new(ResponseEndBlock) + err := grpc.Invoke(ctx, "/types.ABCIApplication/EndBlock", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for ABCIApplication service + +type ABCIApplicationServer interface { + Echo(context.Context, *RequestEcho) (*ResponseEcho, error) + Flush(context.Context, *RequestFlush) (*ResponseFlush, error) + Info(context.Context, *RequestInfo) (*ResponseInfo, error) + SetOption(context.Context, *RequestSetOption) (*ResponseSetOption, error) + DeliverTx(context.Context, *RequestDeliverTx) (*ResponseDeliverTx, error) + CheckTx(context.Context, *RequestCheckTx) (*ResponseCheckTx, error) + Query(context.Context, *RequestQuery) (*ResponseQuery, error) + Commit(context.Context, *RequestCommit) (*ResponseCommit, error) + InitChain(context.Context, *RequestInitChain) (*ResponseInitChain, error) + BeginBlock(context.Context, *RequestBeginBlock) (*ResponseBeginBlock, error) + EndBlock(context.Context, *RequestEndBlock) (*ResponseEndBlock, error) +} + +func RegisterABCIApplicationServer(s *grpc.Server, srv ABCIApplicationServer) { + s.RegisterService(&_ABCIApplication_serviceDesc, srv) +} + +func _ABCIApplication_Echo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RequestEcho) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ABCIApplicationServer).Echo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/types.ABCIApplication/Echo", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ABCIApplicationServer).Echo(ctx, req.(*RequestEcho)) + } + return interceptor(ctx, in, info, handler) +} + +func _ABCIApplication_Flush_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RequestFlush) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ABCIApplicationServer).Flush(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/types.ABCIApplication/Flush", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ABCIApplicationServer).Flush(ctx, req.(*RequestFlush)) + } + return interceptor(ctx, in, info, handler) +} + +func _ABCIApplication_Info_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RequestInfo) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ABCIApplicationServer).Info(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/types.ABCIApplication/Info", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ABCIApplicationServer).Info(ctx, req.(*RequestInfo)) + } + return interceptor(ctx, in, info, handler) +} + +func _ABCIApplication_SetOption_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RequestSetOption) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ABCIApplicationServer).SetOption(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/types.ABCIApplication/SetOption", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ABCIApplicationServer).SetOption(ctx, req.(*RequestSetOption)) + } + return interceptor(ctx, in, info, handler) +} + +func _ABCIApplication_DeliverTx_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RequestDeliverTx) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ABCIApplicationServer).DeliverTx(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/types.ABCIApplication/DeliverTx", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ABCIApplicationServer).DeliverTx(ctx, req.(*RequestDeliverTx)) + } + return interceptor(ctx, in, info, handler) +} + +func _ABCIApplication_CheckTx_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RequestCheckTx) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ABCIApplicationServer).CheckTx(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/types.ABCIApplication/CheckTx", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ABCIApplicationServer).CheckTx(ctx, req.(*RequestCheckTx)) + } + return interceptor(ctx, in, info, handler) +} + +func _ABCIApplication_Query_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RequestQuery) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ABCIApplicationServer).Query(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/types.ABCIApplication/Query", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ABCIApplicationServer).Query(ctx, req.(*RequestQuery)) + } + return interceptor(ctx, in, info, handler) +} + +func _ABCIApplication_Commit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RequestCommit) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ABCIApplicationServer).Commit(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/types.ABCIApplication/Commit", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ABCIApplicationServer).Commit(ctx, req.(*RequestCommit)) + } + return interceptor(ctx, in, info, handler) +} + +func _ABCIApplication_InitChain_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RequestInitChain) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ABCIApplicationServer).InitChain(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/types.ABCIApplication/InitChain", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ABCIApplicationServer).InitChain(ctx, req.(*RequestInitChain)) + } + return interceptor(ctx, in, info, handler) +} + +func _ABCIApplication_BeginBlock_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RequestBeginBlock) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ABCIApplicationServer).BeginBlock(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/types.ABCIApplication/BeginBlock", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ABCIApplicationServer).BeginBlock(ctx, req.(*RequestBeginBlock)) + } + return interceptor(ctx, in, info, handler) +} + +func _ABCIApplication_EndBlock_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RequestEndBlock) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ABCIApplicationServer).EndBlock(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/types.ABCIApplication/EndBlock", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ABCIApplicationServer).EndBlock(ctx, req.(*RequestEndBlock)) + } + return interceptor(ctx, in, info, handler) +} + +var _ABCIApplication_serviceDesc = grpc.ServiceDesc{ + ServiceName: "types.ABCIApplication", + HandlerType: (*ABCIApplicationServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Echo", + Handler: _ABCIApplication_Echo_Handler, + }, + { + MethodName: "Flush", + Handler: _ABCIApplication_Flush_Handler, + }, + { + MethodName: "Info", + Handler: _ABCIApplication_Info_Handler, + }, + { + MethodName: "SetOption", + Handler: _ABCIApplication_SetOption_Handler, + }, + { + MethodName: "DeliverTx", + Handler: _ABCIApplication_DeliverTx_Handler, + }, + { + MethodName: "CheckTx", + Handler: _ABCIApplication_CheckTx_Handler, + }, + { + MethodName: "Query", + Handler: _ABCIApplication_Query_Handler, + }, + { + MethodName: "Commit", + Handler: _ABCIApplication_Commit_Handler, + }, + { + MethodName: "InitChain", + Handler: _ABCIApplication_InitChain_Handler, + }, + { + MethodName: "BeginBlock", + Handler: _ABCIApplication_BeginBlock_Handler, + }, + { + MethodName: "EndBlock", + Handler: _ABCIApplication_EndBlock_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "types/types.proto", +} + +func init() { proto.RegisterFile("types/types.proto", fileDescriptorTypes) } + +var fileDescriptorTypes = []byte{ + // 1846 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x58, 0xcd, 0x6e, 0x1b, 0xc9, + 0x11, 0x16, 0xff, 0x39, 0xa5, 0x1f, 0xd2, 0x2d, 0xdb, 0xa2, 0xb9, 0x08, 0x6c, 0x0c, 0x02, 0xaf, + 0x9c, 0xd5, 0x8a, 0x89, 0x76, 0x6d, 0xd8, 0xbb, 0xc9, 0x22, 0x92, 0xd6, 0x59, 0x0a, 0x9b, 0x1f, + 0x65, 0xec, 0x75, 0x80, 0x5c, 0x88, 0x26, 0xa7, 0x45, 0x0e, 0x4c, 0xce, 0xcc, 0x4e, 0x37, 0xb5, + 0x94, 0x6f, 0xb9, 0x2f, 0x72, 0xcd, 0x39, 0x2f, 0x90, 0x43, 0x80, 0xbc, 0x42, 0x90, 0x97, 0x88, + 0x0f, 0x49, 0x4e, 0x79, 0x89, 0x04, 0x55, 0xdd, 0xf3, 0xab, 0xa1, 0xe1, 0x38, 0xc7, 0xbd, 0x48, + 0x5d, 0x5d, 0x55, 0x3d, 0x5d, 0xc5, 0xaa, 0xaf, 0xaa, 0x1a, 0x6e, 0xa8, 0xab, 0x50, 0xc8, 0x01, + 0xfd, 0x3d, 0x0c, 0xa3, 0x40, 0x05, 0xac, 0x41, 0x44, 0xff, 0xc3, 0xa9, 0xa7, 0x66, 0xcb, 0xf1, + 0xe1, 0x24, 0x58, 0x0c, 0xa6, 0xc1, 0x34, 0x18, 0x10, 0x77, 0xbc, 0xbc, 0x20, 0x8a, 0x08, 0x5a, + 0x69, 0xad, 0xfe, 0x20, 0x23, 0xae, 0x84, 0xef, 0x8a, 0x68, 0xe1, 0xf9, 0x6a, 0xa0, 0x16, 0x73, + 0x6f, 0x2c, 0x07, 0x93, 0x60, 0xb1, 0x08, 0xfc, 0xec, 0x67, 0xec, 0xbf, 0xd6, 0xa1, 0xe5, 0x88, + 0xaf, 0x97, 0x42, 0x2a, 0xb6, 0x0f, 0x75, 0x31, 0x99, 0x05, 0xbd, 0xea, 0xbd, 0xca, 0xfe, 0xe6, + 0x11, 0x3b, 0xd4, 0x72, 0x86, 0xfb, 0x74, 0x32, 0x0b, 0x86, 0x1b, 0x0e, 0x49, 0xb0, 0x0f, 0xa0, + 0x71, 0x31, 0x5f, 0xca, 0x59, 0xaf, 0x46, 0xa2, 0xbb, 0x79, 0xd1, 0x9f, 0x21, 0x6b, 0xb8, 0xe1, + 0x68, 0x19, 0x3c, 0xd6, 0xf3, 0x2f, 0x82, 0x5e, 0xbd, 0xec, 0xd8, 0x33, 0xff, 0x82, 0x8e, 0x45, + 0x09, 0xf6, 0x18, 0x40, 0x0a, 0x35, 0x0a, 0x42, 0xe5, 0x05, 0x7e, 0xaf, 0x41, 0xf2, 0x7b, 0x79, + 0xf9, 0x67, 0x42, 0xfd, 0x8a, 0xd8, 0xc3, 0x0d, 0xc7, 0x92, 0x31, 0x81, 0x9a, 0x9e, 0xef, 0xa9, + 0xd1, 0x64, 0xc6, 0x3d, 0xbf, 0xd7, 0x2c, 0xd3, 0x3c, 0xf3, 0x3d, 0x75, 0x8a, 0x6c, 0xd4, 0xf4, + 0x62, 0x02, 0x4d, 0xf9, 0x7a, 0x29, 0xa2, 0xab, 0x5e, 0xab, 0xcc, 0x94, 0x5f, 0x23, 0x0b, 0x4d, + 0x21, 0x19, 0xf6, 0x29, 0x6c, 0x8e, 0xc5, 0xd4, 0xf3, 0x47, 0xe3, 0x79, 0x30, 0x79, 0xd9, 0x6b, + 0x93, 0x4a, 0x2f, 0xaf, 0x72, 0x82, 0x02, 0x27, 0xc8, 0x1f, 0x6e, 0x38, 0x30, 0x4e, 0x28, 0x76, + 0x04, 0xed, 0xc9, 0x4c, 0x4c, 0x5e, 0x8e, 0xd4, 0xaa, 0x67, 0x91, 0xe6, 0xad, 0xbc, 0xe6, 0x29, + 0x72, 0x9f, 0xaf, 0x86, 0x1b, 0x4e, 0x6b, 0xa2, 0x97, 0x68, 0x97, 0x2b, 0xe6, 0xde, 0xa5, 0x88, + 0x50, 0x6b, 0xb7, 0xcc, 0xae, 0xcf, 0x35, 0x9f, 0xf4, 0x2c, 0x37, 0x26, 0xd8, 0x43, 0xb0, 0x84, + 0xef, 0x9a, 0x8b, 0x6e, 0x92, 0xe2, 0xed, 0xc2, 0x2f, 0xea, 0xbb, 0xf1, 0x35, 0xdb, 0xc2, 0xac, + 0xd9, 0x21, 0x34, 0x31, 0x4a, 0x3c, 0xd5, 0xdb, 0x22, 0x9d, 0x9b, 0x85, 0x2b, 0x12, 0x6f, 0xb8, + 0xe1, 0x18, 0xa9, 0x93, 0x16, 0x34, 0x2e, 0xf9, 0x7c, 0x29, 0xec, 0xf7, 0x61, 0x33, 0x13, 0x29, + 0xac, 0x07, 0xad, 0x85, 0x90, 0x92, 0x4f, 0x45, 0xaf, 0x72, 0xaf, 0xb2, 0x6f, 0x39, 0x31, 0x69, + 0xef, 0xc0, 0x56, 0x36, 0x4e, 0x32, 0x8a, 0x18, 0x0b, 0xa8, 0x78, 0x29, 0x22, 0x89, 0x01, 0x60, + 0x14, 0x0d, 0x69, 0x7f, 0x02, 0xdd, 0x62, 0x10, 0xb0, 0x2e, 0xd4, 0x5e, 0x8a, 0x2b, 0x23, 0x89, + 0x4b, 0x76, 0xd3, 0x5c, 0x88, 0xa2, 0xd8, 0x72, 0xcc, 0xed, 0xfe, 0x55, 0x49, 0x94, 0x93, 0x38, + 0x60, 0x0c, 0xea, 0xca, 0x5b, 0xe8, 0x0b, 0xd6, 0x1c, 0x5a, 0xb3, 0x3b, 0xf8, 0x23, 0x71, 0xcf, + 0x1f, 0x79, 0xae, 0x39, 0xa1, 0x45, 0xf4, 0x99, 0xcb, 0x8e, 0xa1, 0x3b, 0x09, 0x7c, 0x29, 0x7c, + 0xb9, 0x94, 0xa3, 0x90, 0x47, 0x7c, 0x21, 0x4d, 0xfc, 0xc7, 0x8e, 0x3d, 0x8d, 0xd9, 0xe7, 0xc4, + 0x75, 0x3a, 0x93, 0xfc, 0x06, 0x7b, 0x04, 0x70, 0xc9, 0xe7, 0x9e, 0xcb, 0x55, 0x10, 0xc9, 0x5e, + 0xfd, 0x5e, 0x6d, 0x7f, 0xf3, 0xa8, 0x6b, 0x94, 0x5f, 0xc4, 0x8c, 0x93, 0xfa, 0xdf, 0x5e, 0xdf, + 0xdd, 0x70, 0x32, 0x92, 0xec, 0x3e, 0x74, 0x78, 0x18, 0x8e, 0xa4, 0xe2, 0x4a, 0x8c, 0xc6, 0x57, + 0x4a, 0x48, 0xca, 0x8e, 0x2d, 0x67, 0x9b, 0x87, 0xe1, 0x33, 0xdc, 0x3d, 0xc1, 0x4d, 0xdb, 0x4d, + 0x7c, 0x4b, 0x81, 0x8b, 0x16, 0xba, 0x5c, 0x71, 0xb2, 0x70, 0xcb, 0xa1, 0x35, 0xee, 0x85, 0x5c, + 0xcd, 0x8c, 0x75, 0xb4, 0x66, 0xb7, 0xa1, 0x39, 0x13, 0xde, 0x74, 0xa6, 0xc8, 0xa0, 0x9a, 0x63, + 0x28, 0x74, 0x66, 0x18, 0x05, 0x97, 0x82, 0x72, 0xb7, 0xed, 0x68, 0xc2, 0xfe, 0x7b, 0x05, 0x6e, + 0x5c, 0x0b, 0x76, 0x3c, 0x77, 0xc6, 0xe5, 0x2c, 0xfe, 0x16, 0xae, 0xd9, 0x07, 0x78, 0x2e, 0x77, + 0x45, 0x64, 0x30, 0x65, 0xdb, 0xd8, 0x3a, 0xa4, 0x4d, 0x63, 0xa8, 0x11, 0x61, 0x3f, 0xc9, 0x39, + 0xa7, 0x46, 0xce, 0x89, 0x63, 0xfd, 0x99, 0x37, 0xf5, 0x3d, 0x7f, 0xfa, 0x26, 0x1f, 0x0d, 0xe1, + 0xe6, 0xf8, 0xea, 0x15, 0xf7, 0x95, 0xe7, 0x8b, 0xd1, 0x35, 0x2f, 0x77, 0xcc, 0x41, 0x4f, 0x2f, + 0x3d, 0x57, 0xf8, 0x13, 0x61, 0x0e, 0xd8, 0x4d, 0x54, 0x92, 0xa3, 0xa5, 0x7d, 0x0f, 0x76, 0xf2, + 0x19, 0xc9, 0x76, 0xa0, 0xaa, 0x56, 0xc6, 0xb2, 0xaa, 0x5a, 0xd9, 0x76, 0x12, 0x4d, 0x49, 0xf6, + 0x5d, 0x93, 0x79, 0x00, 0x9d, 0x42, 0xa2, 0x65, 0xdc, 0x5c, 0xc9, 0xba, 0xd9, 0xee, 0xc0, 0x76, + 0x2e, 0xbf, 0xec, 0x6f, 0x1b, 0xd0, 0x76, 0x84, 0x0c, 0x31, 0x7c, 0xd8, 0x63, 0xb0, 0xc4, 0x6a, + 0x22, 0x34, 0x28, 0x56, 0x0a, 0x90, 0xa3, 0x65, 0x9e, 0xc6, 0x7c, 0xc4, 0x80, 0x44, 0x98, 0x3d, + 0xc8, 0x01, 0xfa, 0x6e, 0x51, 0x29, 0x8b, 0xe8, 0x07, 0x79, 0x44, 0xbf, 0x59, 0x90, 0x2d, 0x40, + 0xfa, 0x83, 0x1c, 0xa4, 0x17, 0x0f, 0xce, 0x61, 0xfa, 0x93, 0x12, 0x4c, 0x2f, 0x5e, 0x7f, 0x0d, + 0xa8, 0x3f, 0x29, 0x01, 0xf5, 0xde, 0xb5, 0x6f, 0x95, 0xa2, 0xfa, 0x41, 0x1e, 0xd5, 0x8b, 0xe6, + 0x14, 0x60, 0xfd, 0xc7, 0x65, 0xb0, 0x7e, 0xa7, 0xa0, 0xb3, 0x16, 0xd7, 0x3f, 0xba, 0x86, 0xeb, + 0xb7, 0x0b, 0xaa, 0x25, 0xc0, 0xfe, 0x24, 0x07, 0xec, 0x50, 0x6a, 0xdb, 0x1a, 0x64, 0x7f, 0x74, + 0x1d, 0xd9, 0xf7, 0x8a, 0x3f, 0x6d, 0x19, 0xb4, 0x0f, 0x0a, 0xd0, 0x7e, 0xab, 0x78, 0xcb, 0xb5, + 0xd8, 0xfe, 0x00, 0xf3, 0xbd, 0x10, 0x69, 0x88, 0x0d, 0x22, 0x8a, 0x82, 0xc8, 0x80, 0xaf, 0x26, + 0xec, 0x7d, 0x44, 0xa0, 0x34, 0xbe, 0xde, 0x50, 0x07, 0x28, 0xe8, 0x33, 0xd1, 0x65, 0xff, 0xa1, + 0x92, 0xea, 0x52, 0x29, 0xc8, 0xa2, 0x97, 0x65, 0xd0, 0x2b, 0x53, 0x1e, 0xaa, 0xb9, 0xf2, 0xc0, + 0x7e, 0x00, 0x37, 0xe6, 0x5c, 0x2a, 0xed, 0x97, 0x51, 0x0e, 0xce, 0x3a, 0xc8, 0xd0, 0x0e, 0xd1, + 0xb8, 0xf6, 0x21, 0xec, 0x66, 0x64, 0x11, 0x5a, 0x09, 0xba, 0xea, 0x94, 0xbc, 0xdd, 0x44, 0xfa, + 0x38, 0x0c, 0x87, 0x5c, 0xce, 0xec, 0x5f, 0xa4, 0xf6, 0xa7, 0xa5, 0x87, 0x41, 0x7d, 0x12, 0xb8, + 0xda, 0xac, 0x6d, 0x87, 0xd6, 0x58, 0x8e, 0xe6, 0xc1, 0x94, 0xbe, 0x6a, 0x39, 0xb8, 0x44, 0xa9, + 0x24, 0x53, 0x2c, 0x9d, 0x12, 0xf6, 0xef, 0x2b, 0xe9, 0x79, 0x69, 0x35, 0x2a, 0x2b, 0x2f, 0x95, + 0xff, 0xa7, 0xbc, 0x54, 0xdf, 0xb6, 0xbc, 0xd8, 0x7f, 0xa9, 0xa4, 0xbf, 0x45, 0x52, 0x38, 0xde, + 0xcd, 0x38, 0x0c, 0x0b, 0xcf, 0x77, 0xc5, 0x8a, 0x52, 0xbd, 0xe6, 0x68, 0x22, 0xae, 0xd3, 0x4d, + 0x72, 0x70, 0xbe, 0x4e, 0xb7, 0x68, 0x4f, 0x13, 0xa6, 0xe0, 0x04, 0x17, 0x94, 0x83, 0x5b, 0x8e, + 0x26, 0x32, 0xb8, 0x69, 0xe5, 0x70, 0xf3, 0x1c, 0xd8, 0xf5, 0xec, 0x64, 0x9f, 0x40, 0x5d, 0xf1, + 0x29, 0x3a, 0x0f, 0xed, 0xdf, 0x39, 0xd4, 0x5d, 0xef, 0xe1, 0x97, 0x2f, 0xce, 0xb9, 0x17, 0x9d, + 0xdc, 0x46, 0xeb, 0xff, 0xfd, 0xfa, 0xee, 0x0e, 0xca, 0x1c, 0x04, 0x0b, 0x4f, 0x89, 0x45, 0xa8, + 0xae, 0x1c, 0xd2, 0xb1, 0xff, 0x53, 0x41, 0xd4, 0xce, 0x65, 0x6d, 0xa9, 0x2f, 0xe2, 0xd0, 0xac, + 0x66, 0x0a, 0xeb, 0xdb, 0xf9, 0xe7, 0x7b, 0x00, 0x53, 0x2e, 0x47, 0xdf, 0x70, 0x5f, 0x09, 0xd7, + 0x38, 0xc9, 0x9a, 0x72, 0xf9, 0x1b, 0xda, 0xc0, 0xfe, 0x03, 0xd9, 0x4b, 0x29, 0x5c, 0xf2, 0x56, + 0xcd, 0x69, 0x4d, 0xb9, 0xfc, 0x4a, 0x0a, 0x37, 0xb1, 0xab, 0xf5, 0xbf, 0xdb, 0xc5, 0xf6, 0xa1, + 0x76, 0x21, 0x84, 0x41, 0xb6, 0x6e, 0xa2, 0x7a, 0xf6, 0xe8, 0x63, 0x52, 0xd6, 0x21, 0x81, 0x22, + 0xf6, 0xef, 0xaa, 0x69, 0x70, 0xa6, 0xc5, 0xed, 0xbb, 0xe5, 0x83, 0x7f, 0x52, 0xb7, 0x98, 0x87, + 0x52, 0x76, 0x0a, 0x37, 0x92, 0x94, 0x19, 0x2d, 0x43, 0x97, 0x63, 0x17, 0x56, 0x79, 0x63, 0x8e, + 0x75, 0x13, 0x85, 0xaf, 0xb4, 0x3c, 0xfb, 0x25, 0xec, 0x15, 0x92, 0x3c, 0x39, 0xaa, 0xfa, 0xc6, + 0x5c, 0xbf, 0x95, 0xcf, 0xf5, 0xf8, 0xbc, 0xd8, 0x1f, 0xb5, 0x77, 0x88, 0xf5, 0xef, 0x63, 0x9b, + 0x93, 0x85, 0xfe, 0xb2, 0x5f, 0xd4, 0xfe, 0x63, 0x05, 0x3a, 0x85, 0xcb, 0xb0, 0x01, 0x80, 0x46, + 0x4e, 0xe9, 0xbd, 0x12, 0x06, 0xa4, 0x62, 0x1f, 0x90, 0xb3, 0x9e, 0x79, 0xaf, 0x84, 0x63, 0x8d, + 0xe3, 0x25, 0xbb, 0x0f, 0x2d, 0xb5, 0xd2, 0xd2, 0xf9, 0x46, 0xf0, 0xf9, 0x8a, 0x44, 0x9b, 0x8a, + 0xfe, 0xb3, 0x87, 0xb0, 0xa5, 0x0f, 0x9e, 0x06, 0x52, 0x7a, 0xa1, 0x69, 0x46, 0x58, 0xf6, 0xe8, + 0x2f, 0x88, 0xe3, 0x6c, 0x8e, 0x53, 0xc2, 0xfe, 0x2d, 0x58, 0xc9, 0x67, 0xd9, 0x7b, 0x60, 0x2d, + 0xf8, 0xca, 0x74, 0xc9, 0x78, 0xb7, 0x86, 0xd3, 0x5e, 0xf0, 0x15, 0x35, 0xc8, 0x6c, 0x0f, 0x5a, + 0xc8, 0x54, 0x2b, 0xed, 0xef, 0x86, 0xd3, 0x5c, 0xf0, 0xd5, 0xf3, 0x55, 0xc2, 0x98, 0x72, 0x19, + 0xb7, 0xc0, 0x0b, 0xbe, 0xfa, 0x82, 0x4b, 0xfb, 0x33, 0x68, 0xea, 0x4b, 0xbe, 0xd5, 0xc1, 0xa8, + 0x5f, 0xcd, 0xe9, 0xff, 0x14, 0x36, 0x33, 0xf7, 0x66, 0x3f, 0x82, 0x5b, 0xda, 0xc2, 0x90, 0x47, + 0x8a, 0x3c, 0x92, 0x3b, 0x90, 0x11, 0xf3, 0x9c, 0x47, 0x0a, 0x3f, 0xa9, 0x9b, 0xfa, 0x3f, 0x57, + 0xa1, 0xa9, 0x1b, 0x66, 0x76, 0x3f, 0x33, 0x9d, 0x50, 0x55, 0x3c, 0xd9, 0xfc, 0xc7, 0xeb, 0xbb, + 0x2d, 0x2a, 0x20, 0x67, 0x9f, 0xa7, 0xa3, 0x4a, 0x0a, 0x98, 0xd5, 0x5c, 0x3f, 0x1f, 0x4f, 0x3c, + 0xb5, 0xcc, 0xc4, 0xb3, 0x07, 0x2d, 0x7f, 0xb9, 0x20, 0x97, 0xd4, 0xb5, 0x4b, 0xfc, 0xe5, 0x02, + 0x5d, 0xf2, 0x1e, 0x58, 0x2a, 0x50, 0x7c, 0x4e, 0x2c, 0x9d, 0xa4, 0x6d, 0xda, 0x40, 0xe6, 0x7d, + 0xe8, 0x64, 0xab, 0x2d, 0x56, 0x4f, 0x0d, 0xee, 0xdb, 0x69, 0xad, 0xc5, 0x09, 0xe0, 0x7d, 0xe8, + 0xa4, 0x85, 0x46, 0xcb, 0x69, 0xc0, 0xdf, 0x49, 0xb7, 0x49, 0xf0, 0x0e, 0xb4, 0x93, 0x3a, 0xac, + 0xc1, 0xbf, 0xc5, 0x75, 0xf9, 0xc5, 0xc1, 0x39, 0x8c, 0x82, 0x30, 0x90, 0x22, 0x32, 0x0d, 0xd6, + 0xba, 0x84, 0x4b, 0xe4, 0x6c, 0x0f, 0xac, 0x84, 0x89, 0x4d, 0x03, 0x77, 0xdd, 0x48, 0x48, 0x69, + 0xfa, 0xf3, 0x98, 0x64, 0x07, 0xd0, 0x0a, 0x97, 0xe3, 0x11, 0xd6, 0xa6, 0x7c, 0x60, 0x9e, 0x2f, + 0xc7, 0x5f, 0x8a, 0xab, 0x78, 0x42, 0x09, 0x89, 0xa2, 0xea, 0x14, 0x7c, 0x23, 0x22, 0xe3, 0x3f, + 0x4d, 0xd8, 0x0a, 0xba, 0xc5, 0xf1, 0x84, 0x7d, 0x0c, 0x56, 0x62, 0x5f, 0x21, 0x41, 0x8a, 0x77, + 0x4e, 0x05, 0xb1, 0x85, 0x91, 0xde, 0xd4, 0x17, 0xee, 0x28, 0xf5, 0x2d, 0xdd, 0xab, 0xed, 0x74, + 0x34, 0xe3, 0xe7, 0xb1, 0x73, 0xed, 0x1f, 0x42, 0x53, 0xdf, 0x91, 0x7e, 0xd4, 0xab, 0x30, 0xee, + 0xaf, 0x68, 0x5d, 0x9a, 0xc9, 0x7f, 0xaa, 0x40, 0x3b, 0x1e, 0x7f, 0x4a, 0x95, 0x72, 0x97, 0xae, + 0xbe, 0xed, 0xa5, 0xd7, 0xcd, 0x8e, 0x71, 0xac, 0xd5, 0x33, 0xb1, 0x76, 0x00, 0x4c, 0x87, 0xd4, + 0x65, 0xa0, 0x3c, 0x7f, 0x3a, 0xd2, 0xde, 0xd4, 0xb1, 0xd5, 0x25, 0xce, 0x0b, 0x62, 0x9c, 0xe3, + 0xfe, 0xd1, 0xb7, 0x0d, 0xe8, 0x1c, 0x9f, 0x9c, 0x9e, 0x1d, 0x87, 0xe1, 0xdc, 0x9b, 0x70, 0xea, + 0xba, 0x06, 0x50, 0xa7, 0xbe, 0xb2, 0xe4, 0x75, 0xaa, 0x5f, 0x36, 0xe0, 0xb0, 0x23, 0x68, 0x50, + 0x7b, 0xc9, 0xca, 0x1e, 0xa9, 0xfa, 0xa5, 0x73, 0x0e, 0x7e, 0x44, 0x37, 0xa0, 0xd7, 0xdf, 0xaa, + 0xfa, 0x65, 0xc3, 0x0e, 0xfb, 0x0c, 0xac, 0xb4, 0x31, 0x5c, 0xf7, 0x62, 0xd5, 0x5f, 0x3b, 0xf6, + 0xa0, 0x7e, 0x5a, 0x6b, 0xd7, 0xbd, 0xef, 0xf4, 0xd7, 0xce, 0x07, 0xec, 0x31, 0xb4, 0xe2, 0x6e, + 0xa5, 0xfc, 0x4d, 0xa9, 0xbf, 0x66, 0x24, 0x41, 0xf7, 0xe8, 0x8e, 0xaf, 0xec, 0xe1, 0xab, 0x5f, + 0x3a, 0x37, 0xb1, 0x87, 0xd0, 0x34, 0x05, 0xa3, 0xf4, 0x75, 0xa8, 0x5f, 0x3e, 0x58, 0xa0, 0x91, + 0x69, 0xb7, 0xbb, 0xee, 0x71, 0xae, 0xbf, 0x76, 0xc0, 0x63, 0xc7, 0x00, 0x99, 0x2e, 0x6f, 0xed, + 0xab, 0x5b, 0x7f, 0xfd, 0xe0, 0xc6, 0x3e, 0x85, 0x76, 0x3a, 0x8c, 0x97, 0xbf, 0x86, 0xf5, 0xd7, + 0xcd, 0x52, 0xe3, 0x26, 0xbd, 0x98, 0x7e, 0xf4, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe5, 0xf3, + 0xb2, 0x34, 0xad, 0x15, 0x00, 0x00, +} diff --git a/abci/types/types.proto b/abci/types/types.proto new file mode 100644 index 000000000..b4f4b2aa6 --- /dev/null +++ b/abci/types/types.proto @@ -0,0 +1,282 @@ +syntax = "proto3"; +package types; + +// For more information on gogo.proto, see: +// https://github.com/gogo/protobuf/blob/master/extensions.md +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/tendermint/tmlibs/common/types.proto"; + +// This file is copied from http://github.com/tendermint/abci +// NOTE: When using custom types, mind the warnings. +// https://github.com/gogo/protobuf/blob/master/custom_types.md#warnings-and-issues + +//---------------------------------------- +// Request types + +message Request { + oneof value { + RequestEcho echo = 2; + RequestFlush flush = 3; + RequestInfo info = 4; + RequestSetOption set_option = 5; + RequestInitChain init_chain = 6; + RequestQuery query = 7; + RequestBeginBlock begin_block = 8; + RequestCheckTx check_tx = 9; + RequestDeliverTx deliver_tx = 19; + RequestEndBlock end_block = 11; + RequestCommit commit = 12; + } +} + +message RequestEcho { + string message = 1; +} + +message RequestFlush { +} + +message RequestInfo { + string version = 1; +} + +// nondeterministic +message RequestSetOption { + string key = 1; + string value = 2; +} + +message RequestInitChain { + int64 time = 1; + string chain_id = 2; + ConsensusParams consensus_params = 3; + repeated Validator validators = 4 [(gogoproto.nullable)=false]; + bytes app_state_bytes = 5; +} + +message RequestQuery { + bytes data = 1; + string path = 2; + int64 height = 3; + bool prove = 4; +} + +message RequestBeginBlock { + bytes hash = 1; + Header header = 2 [(gogoproto.nullable)=false]; + repeated SigningValidator validators = 3 [(gogoproto.nullable)=false]; + repeated Evidence byzantine_validators = 4 [(gogoproto.nullable)=false]; +} + +message RequestCheckTx { + bytes tx = 1; +} + +message RequestDeliverTx { + bytes tx = 1; +} + +message RequestEndBlock { + int64 height = 1; +} + +message RequestCommit { +} + +//---------------------------------------- +// Response types + +message Response { + oneof value { + ResponseException exception = 1; + ResponseEcho echo = 2; + ResponseFlush flush = 3; + ResponseInfo info = 4; + ResponseSetOption set_option = 5; + ResponseInitChain init_chain = 6; + ResponseQuery query = 7; + ResponseBeginBlock begin_block = 8; + ResponseCheckTx check_tx = 9; + ResponseDeliverTx deliver_tx = 10; + ResponseEndBlock end_block = 11; + ResponseCommit commit = 12; + } +} + +// nondeterministic +message ResponseException { + string error = 1; +} + +message ResponseEcho { + string message = 1; +} + +message ResponseFlush { +} + +message ResponseInfo { + string data = 1; + string version = 2; + int64 last_block_height = 3; + bytes last_block_app_hash = 4; +} + +// nondeterministic +message ResponseSetOption { + uint32 code = 1; + // bytes data = 2; + string log = 3; + string info = 4; +} + +message ResponseInitChain { + ConsensusParams consensus_params = 1; + repeated Validator validators = 2 [(gogoproto.nullable)=false]; +} + +message ResponseQuery { + uint32 code = 1; + // bytes data = 2; // use "value" instead. + string log = 3; // nondeterministic + string info = 4; // nondeterministic + int64 index = 5; + bytes key = 6; + bytes value = 7; + bytes proof = 8; + int64 height = 9; +} + +message ResponseBeginBlock { + repeated common.KVPair tags = 1 [(gogoproto.nullable)=false, (gogoproto.jsontag)="tags,omitempty"]; +} + +message ResponseCheckTx { + uint32 code = 1; + bytes data = 2; + string log = 3; // nondeterministic + string info = 4; // nondeterministic + int64 gas_wanted = 5; + int64 gas_used = 6; + repeated common.KVPair tags = 7 [(gogoproto.nullable)=false, (gogoproto.jsontag)="tags,omitempty"]; + common.KI64Pair fee = 8 [(gogoproto.nullable)=false]; +} + +message ResponseDeliverTx { + uint32 code = 1; + bytes data = 2; + string log = 3; // nondeterministic + string info = 4; // nondeterministic + int64 gas_wanted = 5; + int64 gas_used = 6; + repeated common.KVPair tags = 7 [(gogoproto.nullable)=false, (gogoproto.jsontag)="tags,omitempty"]; + common.KI64Pair fee = 8 [(gogoproto.nullable)=false]; +} + +message ResponseEndBlock { + repeated Validator validator_updates = 1 [(gogoproto.nullable)=false]; + ConsensusParams consensus_param_updates = 2; + repeated common.KVPair tags = 3 [(gogoproto.nullable)=false, (gogoproto.jsontag)="tags,omitempty"]; +} + +message ResponseCommit { + // reserve 1 + bytes data = 2; +} + +//---------------------------------------- +// Misc. + +// ConsensusParams contains all consensus-relevant parameters +// that can be adjusted by the abci app +message ConsensusParams { + BlockSize block_size = 1; + TxSize tx_size = 2; + BlockGossip block_gossip = 3; +} + +// BlockSize contain limits on the block size. +message BlockSize { + int32 max_bytes = 1; + int32 max_txs = 2; + int64 max_gas = 3; +} + +// TxSize contain limits on the tx size. +message TxSize { + int32 max_bytes = 1; + int64 max_gas = 2; +} + +// BlockGossip determine consensus critical +// elements of how blocks are gossiped +message BlockGossip { + // Note: must not be 0 + int32 block_part_size_bytes = 1; +} + +//---------------------------------------- +// Blockchain Types + +// just the minimum the app might need +message Header { + // basics + string chain_id = 1 [(gogoproto.customname)="ChainID"]; + int64 height = 2; + int64 time = 3; + + // txs + int32 num_txs = 4; + int64 total_txs = 5; + + // hashes + bytes last_block_hash = 6; + bytes validators_hash = 7; + bytes app_hash = 8; + + // consensus + Validator proposer = 9 [(gogoproto.nullable)=false]; +} + +// Validator +message Validator { + bytes address = 1; + PubKey pub_key = 2 [(gogoproto.nullable)=false]; + int64 power = 3; +} + +// Validator with an extra bool +message SigningValidator { + Validator validator = 1 [(gogoproto.nullable)=false]; + bool signed_last_block = 2; +} + +message PubKey { + string type = 1; + bytes data = 2; +} + +message Evidence { + string type = 1; + Validator validator = 2 [(gogoproto.nullable)=false]; + int64 height = 3; + int64 time = 4; + int64 total_voting_power = 5; +} + +//---------------------------------------- +// Service Definition + +service ABCIApplication { + rpc Echo(RequestEcho) returns (ResponseEcho) ; + rpc Flush(RequestFlush) returns (ResponseFlush); + rpc Info(RequestInfo) returns (ResponseInfo); + rpc SetOption(RequestSetOption) returns (ResponseSetOption); + rpc DeliverTx(RequestDeliverTx) returns (ResponseDeliverTx); + rpc CheckTx(RequestCheckTx) returns (ResponseCheckTx); + rpc Query(RequestQuery) returns (ResponseQuery); + rpc Commit(RequestCommit) returns (ResponseCommit); + rpc InitChain(RequestInitChain) returns (ResponseInitChain); + rpc BeginBlock(RequestBeginBlock) returns (ResponseBeginBlock); + rpc EndBlock(RequestEndBlock) returns (ResponseEndBlock); +} diff --git a/abci/types/types_test.go b/abci/types/types_test.go new file mode 100644 index 000000000..baa8155cd --- /dev/null +++ b/abci/types/types_test.go @@ -0,0 +1,31 @@ +package types + +import ( + "testing" + + asrt "github.com/stretchr/testify/assert" +) + +func TestConsensusParams(t *testing.T) { + assert := asrt.New(t) + + params := &ConsensusParams{ + BlockSize: &BlockSize{MaxGas: 12345}, + BlockGossip: &BlockGossip{BlockPartSizeBytes: 54321}, + } + var noParams *ConsensusParams // nil + + // no error with nil fields + assert.Nil(noParams.GetBlockSize()) + assert.EqualValues(noParams.GetBlockSize().GetMaxGas(), 0) + + // get values with real fields + assert.NotNil(params.GetBlockSize()) + assert.EqualValues(params.GetBlockSize().GetMaxTxs(), 0) + assert.EqualValues(params.GetBlockSize().GetMaxGas(), 12345) + assert.NotNil(params.GetBlockGossip()) + assert.EqualValues(params.GetBlockGossip().GetBlockPartSizeBytes(), 54321) + assert.Nil(params.GetTxSize()) + assert.EqualValues(params.GetTxSize().GetMaxBytes(), 0) + +} diff --git a/abci/types/util.go b/abci/types/util.go new file mode 100644 index 000000000..0924ab5ff --- /dev/null +++ b/abci/types/util.go @@ -0,0 +1,59 @@ +package types + +import ( + "bytes" + "encoding/json" + "sort" + + cmn "github.com/tendermint/tmlibs/common" +) + +//------------------------------------------------------------------------------ + +// Validators is a list of validators that implements the Sort interface +type Validators []Validator + +var _ sort.Interface = (Validators)(nil) + +// All these methods for Validators: +// Len, Less and Swap +// are for Validators to implement sort.Interface +// which will be used by the sort package. +// See Issue https://github.com/tendermint/abci/issues/212 + +func (v Validators) Len() int { + return len(v) +} + +// XXX: doesn't distinguish same validator with different power +func (v Validators) Less(i, j int) bool { + return bytes.Compare(v[i].PubKey.Data, v[j].PubKey.Data) <= 0 +} + +func (v Validators) Swap(i, j int) { + v1 := v[i] + v[i] = v[j] + v[j] = v1 +} + +func ValidatorsString(vs Validators) string { + s := make([]validatorPretty, len(vs)) + for i, v := range vs { + s[i] = validatorPretty{ + Address: v.Address, + PubKey: v.PubKey.Data, + Power: v.Power, + } + } + b, err := json.Marshal(s) + if err != nil { + panic(err.Error()) + } + return string(b) +} + +type validatorPretty struct { + Address cmn.HexBytes `json:"address"` + PubKey []byte `json:"pub_key"` + Power int64 `json:"power"` +} diff --git a/abci/version/version.go b/abci/version/version.go new file mode 100644 index 000000000..7223a86ad --- /dev/null +++ b/abci/version/version.go @@ -0,0 +1,9 @@ +package version + +// NOTE: we should probably be versioning the ABCI and the abci-cli separately + +const Maj = "0" +const Min = "12" +const Fix = "0" + +const Version = "0.12.0"