Compare commits

..

17 Commits

Author SHA1 Message Date
William Banfield
3db35a54dd config: update config to reflect simple-priority queue 2022-07-14 16:06:09 -04:00
kuniseichi
b71ec8c83f doc: fix typos in quick-start.md. (#8990) 2022-07-13 07:04:17 -07:00
dependabot[bot]
b421138e53 build(deps): Bump google.golang.org/grpc from 1.47.0 to 1.48.0 (#8993)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.47.0 to 1.48.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/grpc/grpc-go/releases">google.golang.org/grpc's releases</a>.</em></p>
<blockquote>
<h2>Release 1.48.0</h2>
<h1>Bug Fixes</h1>
<ul>
<li>xds/priority: fix bug that could prevent higher priorities from receiving config updates (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5417">#5417</a>)</li>
<li>RLS load balancer: don't propagate the status code returned on control plane RPCs to data plane RPCs (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5400">#5400</a>)</li>
</ul>
<h1>New Features</h1>
<ul>
<li>stats: add support for multiple stats handlers in a single client or server (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5347">#5347</a>)</li>
<li>gcp/observability: add experimental OpenCensus tracing/metrics support (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5372">#5372</a>)</li>
<li>xds: enable aggregate and logical DNS clusters by default (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5380">#5380</a>)</li>
<li>credentials/google (for xds): support xdstp C2P cluster names (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5399">#5399</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="6417495520"><code>6417495</code></a> Change version to 1.48.0 (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5482">#5482</a>)</li>
<li><a href="5770b1dea5"><code>5770b1d</code></a> xds: drop localities with zero weight at the xdsClient layer (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5476">#5476</a>)</li>
<li><a href="423cd8e3ad"><code>423cd8e</code></a> interop: update proto to make vet happy (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5475">#5475</a>)</li>
<li><a href="c9b16c884c"><code>c9b16c8</code></a> transport: remove unused <code>bufWriter.onFlush()</code> (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5464">#5464</a>)</li>
<li><a href="755bf5a191"><code>755bf5a</code></a> fix typo in the binary log (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5467">#5467</a>)</li>
<li><a href="15739b5c88"><code>15739b5</code></a> health: split imports into healthpb and healthgrpc (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5466">#5466</a>)</li>
<li><a href="c075d2011c"><code>c075d20</code></a> interop client: provide new flag, --soak_min_time_ms_between_rpcs (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5421">#5421</a>)</li>
<li><a href="4b750055a5"><code>4b75005</code></a> clusterresolver: merge P(p)arseConfig functions (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5462">#5462</a>)</li>
<li><a href="d883f3d5fa"><code>d883f3d</code></a> test/xds: fail only when state changes to something other than READY and IDLE...</li>
<li><a href="c6ee1c7144"><code>c6ee1c7</code></a> xdsclient: only include nodeID in error strings, not the whole nodeProto (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5461">#5461</a>)</li>
<li>Additional commits viewable in <a href="https://github.com/grpc/grpc-go/compare/v1.47.0...v1.48.0">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=google.golang.org/grpc&package-manager=go_modules&previous-version=1.47.0&new-version=1.48.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>
2022-07-13 13:49:52 +00:00
Sam Kleinman
e9239e9ca8 p2p: switch default queue implementation (#8976) 2022-07-12 12:20:17 +00:00
dependabot[bot]
136b62762f build(deps): Bump github.com/prometheus/common from 0.35.0 to 0.36.0 (#8980)
Bumps [github.com/prometheus/common](https://github.com/prometheus/common) from 0.35.0 to 0.36.0.
- [Release notes](https://github.com/prometheus/common/releases)
- [Commits](https://github.com/prometheus/common/compare/v0.35.0...v0.36.0)

---
updated-dependencies:
- dependency-name: github.com/prometheus/common
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-12 08:00:55 -04:00
Sam Kleinman
d5fb82e414 p2p: make p2p.Channel an interface (#8967)
This is (#8446) pulled from the `main/libp2p` branch but without any
of the libp2p content, and is perhaps the easiest first step to enable
pluggability at the peer layer, and makes it possible hoist shims
(including for, say 0.34) into tendermint without touching the reactors.
2022-07-11 20:22:40 +00:00
Rootul Patel
6902fa9282 Fix punctuation (#8972) 2022-07-11 07:14:54 -07:00
Sam Kleinman
61ce384d75 p2p: make peer gossiping coinflip safer (#8949)
Closes #8948
2022-07-08 14:06:57 +00:00
Sam Kleinman
636320f901 p2p: delete cruft (#8958)
I think the decision in #8806 is that we shouldn't do this yet, so I think it's best to just drop this.
2022-07-07 16:29:50 +00:00
Sam Kleinman
d1a16e8ff0 p2p: simpler priority queue (#8929) 2022-07-07 12:13:52 -04:00
Callum Waters
27c523dccb mempool: return error when mempool is full and inbound tx is rejected (#8942)
Addresses: https://github.com/tendermint/tendermint/issues/8928
2022-07-06 18:17:56 +00:00
yihuang
be6d74e657 Work around indexing problem for duplicate transactions (forward port: #8625) (#8945)
Port the bug fix terra-money#76 to upstream. This is critical for ethermint json-rpc to work.

fix: prevent duplicate tx index if it succeeded before
fix: use CodeTypeOk instead of 0
fix: handle duplicate txs within the same block
Co-authored-by: jess jesse@soob.co

ref: #5281

Co-authored-by: M. J. Fromberger <fromberger@interchain.io>
2022-07-06 14:05:48 -04:00
samricotta
70e7372c4a added name to CO (#8947)
Co-authored-by: Samantha Ricotta <samantharicotta@Samanthas-MacBook-Pro.local>
2022-07-06 14:30:45 +02:00
Sergio Mena
2b5329ae47 Typos in spec (#8939) 2022-07-06 00:02:29 +02:00
Hernán Vanzetto
3cde9a0bbc abci-cli: Add process_proposal command to abci-cli (#8901)
* Add `process_proposal` command to abci-cli

* Added process proposal to the 'tutorial' examples

* Added entry in CHANGELOG_PENDING.md

* Allow empty blocks in PrepareProposal, ProcessProposal, and FinalizeBlock

* Fix minimum arguments

* Add tests for empty block

* Updated abci-cli doc

Co-authored-by: Sergio Mena <sergio@informal.systems>
Co-authored-by: Jasmina Malicevic <jasmina.dustinac@gmail.com>
2022-07-05 15:08:58 +02:00
dependabot[bot]
d8de2d85c0 build(deps): Bump github.com/libp2p/go-buffer-pool from 0.0.2 to 0.1.0 (#8932) 2022-07-05 12:12:15 +02:00
Sergio Mena
331860c2a8 Restore Commit to the ABCI++ spec, and other late modifications (#8796)
* Added VoteExtensionsEnableHeight

* Fix reference to `modified`

* Removed old pseudo-code, now included in spec

* Removed markdown warnings in abci++_basic_concepts_002_draft.md

* Restored `Commit` in the Methods section

* Addressed remaining markdown warnings

* Revisited intro and basic concepts section

* Extra pass at all spec sections to recover Commit, and other ABCI++ spec modifications

* Fixed links

* make proto-gen

* Remove _primes_ from spec notation

* Update proto/tendermint/abci/types.proto

Co-authored-by: Callum Waters <cmwaters19@gmail.com>

* Update spec/abci++/abci++_tmint_expected_behavior_002_draft.md

Co-authored-by: Callum Waters <cmwaters19@gmail.com>

* Addressed @cmwaters' comments

* Addressed @angbrav's and @mpoke's comments on spec

* make proto-gen

* Fix MD anchor reference

* Clarify throughout the spec when `ProcessProposal` and `VerifyVoteExtension` are called

* Update spec/abci++/abci++_app_requirements_002_draft.md

Co-authored-by: M. J. Fromberger <fromberger@interchain.io>

* Update spec/abci++/abci++_app_requirements_002_draft.md

Co-authored-by: M. J. Fromberger <fromberger@interchain.io>

* Update spec/abci++/abci++_app_requirements_002_draft.md

Co-authored-by: William Banfield <4561443+williambanfield@users.noreply.github.com>

* Update spec/abci++/abci++_basic_concepts_002_draft.md

Co-authored-by: William Banfield <4561443+williambanfield@users.noreply.github.com>

* Update spec/abci++/abci++_basic_concepts_002_draft.md

Co-authored-by: M. J. Fromberger <fromberger@interchain.io>

* Update spec/abci++/abci++_basic_concepts_002_draft.md

Co-authored-by: William Banfield <4561443+williambanfield@users.noreply.github.com>

* Update spec/abci++/abci++_methods_002_draft.md

Co-authored-by: M. J. Fromberger <fromberger@interchain.io>

* Update spec/abci++/abci++_tmint_expected_behavior_002_draft.md

Co-authored-by: William Banfield <4561443+williambanfield@users.noreply.github.com>

* Addresed comments

* Renamed 'draft' files

* Adatped links to new filenames

* Fixed links and minor cosmetic changes

* Renamed 'byzantine_validators' to 'misbehavior' in ABCI++ spec and protobufs

* make proto-gen

* Renamed 'byzantine_validators' to 'misbehavior' in the code

* Fixed link

* Update spec/abci++/abci++_basic_concepts.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_basic_concepts.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_basic_concepts.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_basic_concepts.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_basic_concepts.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_basic_concepts.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_basic_concepts.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_basic_concepts.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_basic_concepts.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_basic_concepts.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_basic_concepts.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_basic_concepts.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_basic_concepts.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_basic_concepts.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_basic_concepts.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_basic_concepts.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_basic_concepts.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_basic_concepts.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_basic_concepts.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_basic_concepts.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_basic_concepts.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_basic_concepts.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_methods.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_methods.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_methods.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_methods.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_methods.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_methods.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_methods.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_methods.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_methods.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_methods.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_methods.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_methods.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_methods.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_methods.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_methods.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_methods.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_methods.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_methods.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_methods.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_methods.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_methods.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_methods.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_methods.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_methods.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_methods.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_methods.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_methods.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Update spec/abci++/abci++_methods.md

Co-authored-by: Daniel <daniel.cason@usi.ch>

* Addressed @cason's comments

* Clarified conditions for `ProcessProposal` call at proposer

Co-authored-by: Callum Waters <cmwaters19@gmail.com>
Co-authored-by: M. J. Fromberger <fromberger@interchain.io>
Co-authored-by: William Banfield <4561443+williambanfield@users.noreply.github.com>
Co-authored-by: Daniel <daniel.cason@usi.ch>
2022-07-04 16:25:48 +02:00
74 changed files with 1880 additions and 2502 deletions

2
.github/CODEOWNERS vendored
View File

@@ -7,7 +7,7 @@
# global owners are only requested if there isn't a more specific
# codeowner specified below. For this reason, the global codeowners
# are often repeated in package-level definitions.
* @ebuchman @cmwaters @tychoish @williambanfield @creachadair @sergio-mena @jmalicevic @thanethomson @ancazamfir
* @ebuchman @cmwaters @tychoish @williambanfield @creachadair @sergio-mena @jmalicevic @thanethomson @ancazamfir @samricotta
# Spec related changes can be approved by the protocol design team
/spec @josef-widder @milosevic @cason @sergio-mena @jmalicevic

View File

@@ -33,6 +33,7 @@ Special thanks to external contributors on this release:
- [abci] \#8664 Move `app_hash` parameter from `Commit` to `FinalizeBlock`. (@sergio-mena)
- [abci] \#8656 Added cli command for `PrepareProposal`. (@jmalicevic)
- [sink/psql] \#8637 tx_results emitted from psql sink are now json encoded, previously they were protobuf encoded
- [abci] \#8901 Added cli command for `ProcessProposal`. (@hvanz)
- P2P Protocol
@@ -100,3 +101,4 @@ Special thanks to external contributors on this release:
- [cli] \#8276 scmigrate: ensure target key is correctly renamed. (@creachadair)
- [cli] \#8294 keymigrate: ensure block hash keys are correctly translated. (@creachadair)
- [cli] \#8352 keymigrate: ensure transaction hash keys are correctly translated. (@creachadair)
- (indexer) \#8625 Fix overriding tx index of duplicated txs.

View File

@@ -17,9 +17,9 @@ by Tendermint itself. Right now, we return a regular error when this happens.
#### ABCI++
For information on how ABCI++ works, see the
[Specification](https://github.com/tendermint/tendermint/blob/master/spec/abci%2B%2B/README.md).
[Specification](spec/abci%2B%2B/README.md).
In particular, the simplest way to upgrade your application is described
[here](https://github.com/tendermint/tendermint/blob/master/spec/abci%2B%2B/abci++_tmint_expected_behavior_002_draft.md#adapting-existing-applications-that-use-abci).
[here](spec/abci%2B%2B/abci++_tmint_expected_behavior.md#adapting-existing-applications-that-use-abci).
#### Moving the `app_hash` parameter

View File

@@ -79,10 +79,11 @@ func RootCmmand(logger log.Logger) *cobra.Command {
// Structure for data passed to print response.
type response struct {
// generic abci response
Data []byte
Code uint32
Info string
Log string
Data []byte
Code uint32
Info string
Log string
Status int32
Query *queryResponse
}
@@ -132,6 +133,7 @@ func addCommands(cmd *cobra.Command, logger log.Logger) {
cmd.AddCommand(versionCmd)
cmd.AddCommand(testCmd)
cmd.AddCommand(prepareProposalCmd)
cmd.AddCommand(processProposalCmd)
cmd.AddCommand(getQueryCmd())
// examples
@@ -172,7 +174,7 @@ 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", "query", "check_tx", "prepare_proposal", "finalize_block", "commit"},
ValidArgs: []string{"echo", "info", "query", "check_tx", "prepare_proposal", "process_proposal", "finalize_block", "commit"},
RunE: cmdConsole,
}
@@ -195,7 +197,7 @@ var finalizeBlockCmd = &cobra.Command{
Use: "finalize_block",
Short: "deliver a block of transactions to the application",
Long: "deliver a block of transactions to the application",
Args: cobra.MinimumNArgs(1),
Args: cobra.MinimumNArgs(0),
RunE: cmdFinalizeBlock,
}
@@ -230,10 +232,18 @@ var prepareProposalCmd = &cobra.Command{
Use: "prepare_proposal",
Short: "prepare proposal",
Long: "prepare proposal",
Args: cobra.MinimumNArgs(1),
Args: cobra.MinimumNArgs(0),
RunE: cmdPrepareProposal,
}
var processProposalCmd = &cobra.Command{
Use: "process_proposal",
Short: "process proposal",
Long: "process proposal",
Args: cobra.MinimumNArgs(0),
RunE: cmdProcessProposal,
}
func getQueryCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "query",
@@ -352,6 +362,11 @@ func cmdTest(cmd *cobra.Command, args []string) error {
types.TxRecord_UNMODIFIED,
}, nil)
},
func() error {
return servertest.ProcessProposal(ctx, client, [][]byte{
{0x01},
}, types.ResponseProcessProposal_ACCEPT)
},
})
}
@@ -454,6 +469,8 @@ func muxOnCommands(cmd *cobra.Command, pArgs []string) error {
return cmdQuery(cmd, actualArgs)
case "prepare_proposal":
return cmdPrepareProposal(cmd, actualArgs)
case "process_proposal":
return cmdProcessProposal(cmd, actualArgs)
default:
return cmdUnimplemented(cmd, pArgs)
}
@@ -517,13 +534,6 @@ const codeBad uint32 = 10
// Append new txs to application
func cmdFinalizeBlock(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
printResponse(cmd, args, response{
Code: codeBad,
Log: "Must provide at least one transaction",
})
return nil
}
txs := make([][]byte, len(args))
for i, arg := range args {
txBytes, err := stringOrHexToBytes(arg)
@@ -633,15 +643,8 @@ func inTxArray(txByteArray [][]byte, tx []byte) bool {
}
return false
}
func cmdPrepareProposal(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
printResponse(cmd, args, response{
Code: codeBad,
Info: "Must provide at least one transaction",
Log: "Must provide at least one transaction",
})
return nil
}
txsBytesArray := make([][]byte, len(args))
for i, arg := range args {
@@ -682,6 +685,30 @@ func cmdPrepareProposal(cmd *cobra.Command, args []string) error {
return nil
}
func cmdProcessProposal(cmd *cobra.Command, args []string) error {
txsBytesArray := make([][]byte, len(args))
for i, arg := range args {
txBytes, err := stringOrHexToBytes(arg)
if err != nil {
return err
}
txsBytesArray[i] = txBytes
}
res, err := client.ProcessProposal(cmd.Context(), &types.RequestProcessProposal{
Txs: txsBytesArray,
})
if err != nil {
return err
}
printResponse(cmd, args, response{
Status: int32(res.Status),
})
return nil
}
func makeKVStoreCmd(logger log.Logger) func(*cobra.Command, []string) error {
return func(cmd *cobra.Command, args []string) error {
// Create the application - in memory or persisted to disk
@@ -739,6 +766,9 @@ func printResponse(cmd *cobra.Command, args []string, rsps ...response) {
if rsp.Log != "" {
fmt.Printf("-> log: %s\n", rsp.Log)
}
if cmd.Use == "process_proposal" {
fmt.Printf("-> status: %s\n", types.ResponseProcessProposal_ProposalStatus_name[rsp.Status])
}
if rsp.Query != nil {
fmt.Printf("-> height: %d\n", rsp.Query.Height)

View File

@@ -175,7 +175,7 @@ func (app *Application) FinalizeBlock(_ context.Context, req *types.RequestFinal
app.ValUpdates = make([]types.ValidatorUpdate, 0)
// Punish validators who committed equivocation.
for _, ev := range req.ByzantineValidators {
for _, ev := range req.Misbehavior {
if ev.Type == types.MisbehaviorType_DUPLICATE_VOTE {
addr := string(ev.Validator.Address)
if pubKey, ok := app.valAddrToPubKeyMap[addr]; ok {
@@ -293,7 +293,7 @@ func (app *Application) PrepareProposal(_ context.Context, req *types.RequestPre
func (*Application) ProcessProposal(_ context.Context, req *types.RequestProcessProposal) (*types.ResponseProcessProposal, error) {
for _, tx := range req.Txs {
if len(tx) == 0 {
if len(tx) == 0 || isPrepareTx(tx) {
return &types.ResponseProcessProposal{Status: types.ResponseProcessProposal_REJECT}, nil
}
}

View File

@@ -83,6 +83,19 @@ func PrepareProposal(ctx context.Context, client abciclient.Client, txBytes [][]
fmt.Println("Passed test: PrepareProposal")
return nil
}
func ProcessProposal(ctx context.Context, client abciclient.Client, txBytes [][]byte, statusExp types.ResponseProcessProposal_ProposalStatus) error {
res, _ := client.ProcessProposal(ctx, &types.RequestProcessProposal{Txs: txBytes})
if res.Status != statusExp {
fmt.Println("Failed test: ProcessProposal")
fmt.Printf("ProcessProposal response status was unexpected. Got %v expected %v.",
res.Status, statusExp)
return errors.New("ProcessProposal error")
}
fmt.Println("Passed test: ProcessProposal")
return nil
}
func CheckTx(ctx context.Context, client abciclient.Client, txBytes []byte, codeExp uint32, dataExp []byte) error {
res, _ := client.CheckTx(ctx, &types.RequestCheckTx{Tx: txBytes})
code, data := res.Code, res.Data

View File

@@ -1,6 +1,7 @@
echo hello
info
prepare_proposal "abc"
process_proposal "abc"
finalize_block "abc"
commit
info
@@ -8,4 +9,10 @@ query "abc"
finalize_block "def=xyz" "ghi=123"
commit
query "def"
prepare_proposal "preparedef"
prepare_proposal "preparedef"
process_proposal "def"
process_proposal "preparedef"
prepare_proposal
process_proposal
finalize_block
commit

View File

@@ -12,6 +12,10 @@
-> code: OK
-> log: Succeeded. Tx: abc action: UNMODIFIED
> process_proposal "abc"
-> code: OK
-> status: ACCEPT
> finalize_block "abc"
-> code: OK
-> code: OK
@@ -58,3 +62,24 @@
-> code: OK
-> log: Succeeded. Tx: preparedef action: REMOVED
> process_proposal "def"
-> code: OK
-> status: ACCEPT
> process_proposal "preparedef"
-> code: OK
-> status: REJECT
> prepare_proposal
> process_proposal
-> code: OK
-> status: ACCEPT
> finalize_block
-> code: OK
-> data.hex: 0x0600000000000000
> commit
-> code: OK

View File

@@ -1120,12 +1120,12 @@ type RequestPrepareProposal struct {
MaxTxBytes int64 `protobuf:"varint,1,opt,name=max_tx_bytes,json=maxTxBytes,proto3" json:"max_tx_bytes,omitempty"`
// txs is an array of transactions that will be included in a block,
// sent to the app for possible modifications.
Txs [][]byte `protobuf:"bytes,2,rep,name=txs,proto3" json:"txs,omitempty"`
LocalLastCommit ExtendedCommitInfo `protobuf:"bytes,3,opt,name=local_last_commit,json=localLastCommit,proto3" json:"local_last_commit"`
ByzantineValidators []Misbehavior `protobuf:"bytes,4,rep,name=byzantine_validators,json=byzantineValidators,proto3" json:"byzantine_validators"`
Height int64 `protobuf:"varint,5,opt,name=height,proto3" json:"height,omitempty"`
Time time.Time `protobuf:"bytes,6,opt,name=time,proto3,stdtime" json:"time"`
NextValidatorsHash []byte `protobuf:"bytes,7,opt,name=next_validators_hash,json=nextValidatorsHash,proto3" json:"next_validators_hash,omitempty"`
Txs [][]byte `protobuf:"bytes,2,rep,name=txs,proto3" json:"txs,omitempty"`
LocalLastCommit ExtendedCommitInfo `protobuf:"bytes,3,opt,name=local_last_commit,json=localLastCommit,proto3" json:"local_last_commit"`
Misbehavior []Misbehavior `protobuf:"bytes,4,rep,name=misbehavior,proto3" json:"misbehavior"`
Height int64 `protobuf:"varint,5,opt,name=height,proto3" json:"height,omitempty"`
Time time.Time `protobuf:"bytes,6,opt,name=time,proto3,stdtime" json:"time"`
NextValidatorsHash []byte `protobuf:"bytes,7,opt,name=next_validators_hash,json=nextValidatorsHash,proto3" json:"next_validators_hash,omitempty"`
// address of the public key of the validator proposing the block.
ProposerAddress []byte `protobuf:"bytes,8,opt,name=proposer_address,json=proposerAddress,proto3" json:"proposer_address,omitempty"`
}
@@ -1184,9 +1184,9 @@ func (m *RequestPrepareProposal) GetLocalLastCommit() ExtendedCommitInfo {
return ExtendedCommitInfo{}
}
func (m *RequestPrepareProposal) GetByzantineValidators() []Misbehavior {
func (m *RequestPrepareProposal) GetMisbehavior() []Misbehavior {
if m != nil {
return m.ByzantineValidators
return m.Misbehavior
}
return nil
}
@@ -1220,9 +1220,9 @@ func (m *RequestPrepareProposal) GetProposerAddress() []byte {
}
type RequestProcessProposal struct {
Txs [][]byte `protobuf:"bytes,1,rep,name=txs,proto3" json:"txs,omitempty"`
ProposedLastCommit CommitInfo `protobuf:"bytes,2,opt,name=proposed_last_commit,json=proposedLastCommit,proto3" json:"proposed_last_commit"`
ByzantineValidators []Misbehavior `protobuf:"bytes,3,rep,name=byzantine_validators,json=byzantineValidators,proto3" json:"byzantine_validators"`
Txs [][]byte `protobuf:"bytes,1,rep,name=txs,proto3" json:"txs,omitempty"`
ProposedLastCommit CommitInfo `protobuf:"bytes,2,opt,name=proposed_last_commit,json=proposedLastCommit,proto3" json:"proposed_last_commit"`
Misbehavior []Misbehavior `protobuf:"bytes,3,rep,name=misbehavior,proto3" json:"misbehavior"`
// hash is the merkle root hash of the fields of the proposed block.
Hash []byte `protobuf:"bytes,4,opt,name=hash,proto3" json:"hash,omitempty"`
Height int64 `protobuf:"varint,5,opt,name=height,proto3" json:"height,omitempty"`
@@ -1279,9 +1279,9 @@ func (m *RequestProcessProposal) GetProposedLastCommit() CommitInfo {
return CommitInfo{}
}
func (m *RequestProcessProposal) GetByzantineValidators() []Misbehavior {
func (m *RequestProcessProposal) GetMisbehavior() []Misbehavior {
if m != nil {
return m.ByzantineValidators
return m.Misbehavior
}
return nil
}
@@ -1444,10 +1444,10 @@ func (m *RequestVerifyVoteExtension) GetVoteExtension() []byte {
}
type RequestFinalizeBlock struct {
Txs [][]byte `protobuf:"bytes,1,rep,name=txs,proto3" json:"txs,omitempty"`
DecidedLastCommit CommitInfo `protobuf:"bytes,2,opt,name=decided_last_commit,json=decidedLastCommit,proto3" json:"decided_last_commit"`
ByzantineValidators []Misbehavior `protobuf:"bytes,3,rep,name=byzantine_validators,json=byzantineValidators,proto3" json:"byzantine_validators"`
// hash is the merkle root hash of the fields of the proposed block.
Txs [][]byte `protobuf:"bytes,1,rep,name=txs,proto3" json:"txs,omitempty"`
DecidedLastCommit CommitInfo `protobuf:"bytes,2,opt,name=decided_last_commit,json=decidedLastCommit,proto3" json:"decided_last_commit"`
Misbehavior []Misbehavior `protobuf:"bytes,3,rep,name=misbehavior,proto3" json:"misbehavior"`
// hash is the merkle root hash of the fields of the decided block.
Hash []byte `protobuf:"bytes,4,opt,name=hash,proto3" json:"hash,omitempty"`
Height int64 `protobuf:"varint,5,opt,name=height,proto3" json:"height,omitempty"`
Time time.Time `protobuf:"bytes,6,opt,name=time,proto3,stdtime" json:"time"`
@@ -1503,9 +1503,9 @@ func (m *RequestFinalizeBlock) GetDecidedLastCommit() CommitInfo {
return CommitInfo{}
}
func (m *RequestFinalizeBlock) GetByzantineValidators() []Misbehavior {
func (m *RequestFinalizeBlock) GetMisbehavior() []Misbehavior {
if m != nil {
return m.ByzantineValidators
return m.Misbehavior
}
return nil
}
@@ -2381,7 +2381,6 @@ func (m *ResponseDeliverTx) GetCodespace() string {
}
type ResponseCommit struct {
// reserve 1
RetainHeight int64 `protobuf:"varint,3,opt,name=retain_height,json=retainHeight,proto3" json:"retain_height,omitempty"`
}
@@ -3829,211 +3828,211 @@ func init() {
func init() { proto.RegisterFile("tendermint/abci/types.proto", fileDescriptor_252557cfdd89a31a) }
var fileDescriptor_252557cfdd89a31a = []byte{
// 3253 bytes of a gzipped FileDescriptorProto
// 3257 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5a, 0xcd, 0x73, 0x23, 0xd5,
0x11, 0xd7, 0xe8, 0x5b, 0xad, 0xaf, 0xf1, 0xb3, 0x59, 0xb4, 0x62, 0xd7, 0x36, 0x43, 0x01, 0xcb,
0x02, 0x36, 0xf1, 0x66, 0x61, 0xc9, 0x42, 0x28, 0x5b, 0xd6, 0x46, 0xf6, 0x7a, 0x6d, 0x33, 0x96,
0x4d, 0x91, 0x0f, 0x86, 0xb1, 0xf4, 0x6c, 0x0d, 0x2b, 0x69, 0x86, 0x99, 0x91, 0x91, 0x39, 0x26,
0xc5, 0x85, 0x43, 0x8a, 0x4b, 0x2a, 0x49, 0x55, 0xb8, 0x25, 0x55, 0xc9, 0x7f, 0x90, 0x5c, 0x72,
0xca, 0x81, 0x43, 0x0e, 0x9c, 0x52, 0x39, 0x91, 0x14, 0xdc, 0xf8, 0x07, 0x72, 0x4b, 0xa5, 0xde,
0xc7, 0x7c, 0x49, 0x33, 0xfa, 0x00, 0x8a, 0xaa, 0x54, 0x71, 0x9b, 0xd7, 0xd3, 0xdd, 0xef, 0xab,
0x5f, 0x77, 0xff, 0xfa, 0x3d, 0x78, 0xcc, 0xc6, 0xfd, 0x36, 0x36, 0x7b, 0x5a, 0xdf, 0x5e, 0x57,
0x4f, 0x5b, 0xda, 0xba, 0x7d, 0x69, 0x60, 0x6b, 0xcd, 0x30, 0x75, 0x5b, 0x47, 0x65, 0xef, 0xe7,
0x1a, 0xf9, 0x59, 0xbd, 0xee, 0xe3, 0x6e, 0x99, 0x97, 0x86, 0xad, 0xaf, 0x1b, 0xa6, 0xae, 0x9f,
0x31, 0xfe, 0xea, 0xb5, 0xf1, 0xdf, 0x0f, 0xf1, 0x25, 0xd7, 0x16, 0x10, 0xa6, 0xbd, 0xac, 0x1b,
0xaa, 0xa9, 0xf6, 0x9c, 0xdf, 0x2b, 0xe7, 0xba, 0x7e, 0xde, 0xc5, 0xeb, 0xb4, 0x75, 0x3a, 0x38,
0x5b, 0xb7, 0xb5, 0x1e, 0xb6, 0x6c, 0xb5, 0x67, 0x70, 0x86, 0xa5, 0x73, 0xfd, 0x5c, 0xa7, 0x9f,
0xeb, 0xe4, 0x8b, 0x51, 0xa5, 0xbf, 0xe4, 0x20, 0x23, 0xe3, 0x77, 0x07, 0xd8, 0xb2, 0xd1, 0x06,
0x24, 0x71, 0xab, 0xa3, 0x57, 0x84, 0x55, 0xe1, 0x46, 0x7e, 0xe3, 0xda, 0xda, 0xc8, 0xf0, 0xd7,
0x38, 0x5f, 0xbd, 0xd5, 0xd1, 0x1b, 0x31, 0x99, 0xf2, 0xa2, 0xdb, 0x90, 0x3a, 0xeb, 0x0e, 0xac,
0x4e, 0x25, 0x4e, 0x85, 0xae, 0x47, 0x09, 0xdd, 0x23, 0x4c, 0x8d, 0x98, 0xcc, 0xb8, 0x49, 0x57,
0x5a, 0xff, 0x4c, 0xaf, 0x24, 0x26, 0x77, 0xb5, 0xd3, 0x3f, 0xa3, 0x5d, 0x11, 0x5e, 0xb4, 0x05,
0xa0, 0xf5, 0x35, 0x5b, 0x69, 0x75, 0x54, 0xad, 0x5f, 0x49, 0x52, 0xc9, 0xc7, 0xa3, 0x25, 0x35,
0xbb, 0x46, 0x18, 0x1b, 0x31, 0x39, 0xa7, 0x39, 0x0d, 0x32, 0xdc, 0x77, 0x07, 0xd8, 0xbc, 0xac,
0xa4, 0x26, 0x0f, 0xf7, 0x75, 0xc2, 0x44, 0x86, 0x4b, 0xb9, 0xd1, 0x2b, 0x90, 0x6d, 0x75, 0x70,
0xeb, 0xa1, 0x62, 0x0f, 0x2b, 0x19, 0x2a, 0xb9, 0x12, 0x25, 0x59, 0x23, 0x7c, 0xcd, 0x61, 0x23,
0x26, 0x67, 0x5a, 0xec, 0x13, 0xdd, 0x81, 0x74, 0x4b, 0xef, 0xf5, 0x34, 0xbb, 0x02, 0x54, 0x76,
0x39, 0x52, 0x96, 0x72, 0x35, 0x62, 0x32, 0xe7, 0x47, 0xfb, 0x50, 0xea, 0x6a, 0x96, 0xad, 0x58,
0x7d, 0xd5, 0xb0, 0x3a, 0xba, 0x6d, 0x55, 0xf2, 0x54, 0xc3, 0x93, 0x51, 0x1a, 0xf6, 0x34, 0xcb,
0x3e, 0x72, 0x98, 0x1b, 0x31, 0xb9, 0xd8, 0xf5, 0x13, 0x88, 0x3e, 0xfd, 0xec, 0x0c, 0x9b, 0xae,
0xc2, 0x4a, 0x61, 0xb2, 0xbe, 0x03, 0xc2, 0xed, 0xc8, 0x13, 0x7d, 0xba, 0x9f, 0x80, 0x7e, 0x02,
0x8b, 0x5d, 0x5d, 0x6d, 0xbb, 0xea, 0x94, 0x56, 0x67, 0xd0, 0x7f, 0x58, 0x29, 0x52, 0xa5, 0xcf,
0x44, 0x0e, 0x52, 0x57, 0xdb, 0x8e, 0x8a, 0x1a, 0x11, 0x68, 0xc4, 0xe4, 0x85, 0xee, 0x28, 0x11,
0xbd, 0x05, 0x4b, 0xaa, 0x61, 0x74, 0x2f, 0x47, 0xb5, 0x97, 0xa8, 0xf6, 0x9b, 0x51, 0xda, 0x37,
0x89, 0xcc, 0xa8, 0x7a, 0xa4, 0x8e, 0x51, 0x51, 0x13, 0x44, 0xc3, 0xc4, 0x86, 0x6a, 0x62, 0xc5,
0x30, 0x75, 0x43, 0xb7, 0xd4, 0x6e, 0xa5, 0x4c, 0x75, 0x3f, 0x1d, 0xa5, 0xfb, 0x90, 0xf1, 0x1f,
0x72, 0xf6, 0x46, 0x4c, 0x2e, 0x1b, 0x41, 0x12, 0xd3, 0xaa, 0xb7, 0xb0, 0x65, 0x79, 0x5a, 0xc5,
0x69, 0x5a, 0x29, 0x7f, 0x50, 0x6b, 0x80, 0x84, 0xea, 0x90, 0xc7, 0x43, 0x22, 0xae, 0x5c, 0xe8,
0x36, 0xae, 0x2c, 0x50, 0x85, 0x52, 0xe4, 0x09, 0xa5, 0xac, 0x27, 0xba, 0x8d, 0x1b, 0x31, 0x19,
0xb0, 0xdb, 0x42, 0x2a, 0x3c, 0x72, 0x81, 0x4d, 0xed, 0xec, 0x92, 0xaa, 0x51, 0xe8, 0x1f, 0x4b,
0xd3, 0xfb, 0x15, 0x44, 0x15, 0x3e, 0x1b, 0xa5, 0xf0, 0x84, 0x0a, 0x11, 0x15, 0x75, 0x47, 0xa4,
0x11, 0x93, 0x17, 0x2f, 0xc6, 0xc9, 0xc4, 0xc4, 0xce, 0xb4, 0xbe, 0xda, 0xd5, 0xde, 0xc7, 0xca,
0x69, 0x57, 0x6f, 0x3d, 0xac, 0x2c, 0x4e, 0x36, 0xb1, 0x7b, 0x9c, 0x7b, 0x8b, 0x30, 0x13, 0x13,
0x3b, 0xf3, 0x13, 0xb6, 0x32, 0x90, 0xba, 0x50, 0xbb, 0x03, 0xbc, 0x9b, 0xcc, 0xa6, 0xc5, 0xcc,
0x6e, 0x32, 0x9b, 0x15, 0x73, 0xbb, 0xc9, 0x6c, 0x4e, 0x04, 0xe9, 0x69, 0xc8, 0xfb, 0x5c, 0x12,
0xaa, 0x40, 0xa6, 0x87, 0x2d, 0x4b, 0x3d, 0xc7, 0xd4, 0x83, 0xe5, 0x64, 0xa7, 0x29, 0x95, 0xa0,
0xe0, 0x77, 0x43, 0xd2, 0x47, 0x82, 0x2b, 0x49, 0x3c, 0x0c, 0x91, 0xbc, 0xc0, 0x26, 0x5d, 0x08,
0x2e, 0xc9, 0x9b, 0xe8, 0x09, 0x28, 0xd2, 0x49, 0x28, 0xce, 0x7f, 0xe2, 0xe6, 0x92, 0x72, 0x81,
0x12, 0x4f, 0x38, 0xd3, 0x0a, 0xe4, 0x8d, 0x0d, 0xc3, 0x65, 0x49, 0x50, 0x16, 0x30, 0x36, 0x0c,
0x87, 0xe1, 0x71, 0x28, 0x90, 0x19, 0xbb, 0x1c, 0x49, 0xda, 0x49, 0x9e, 0xd0, 0x38, 0x8b, 0xf4,
0xf7, 0x38, 0x88, 0xa3, 0xae, 0x0b, 0xdd, 0x81, 0x24, 0xf1, 0xe2, 0xdc, 0x21, 0x57, 0xd7, 0x98,
0x8b, 0x5f, 0x73, 0x5c, 0xfc, 0x5a, 0xd3, 0x71, 0xf1, 0x5b, 0xd9, 0x4f, 0x3e, 0x5b, 0x89, 0x7d,
0xf4, 0xaf, 0x15, 0x41, 0xa6, 0x12, 0xe8, 0x2a, 0x71, 0x58, 0xaa, 0xd6, 0x57, 0xb4, 0x36, 0x1d,
0x72, 0x8e, 0x78, 0x23, 0x55, 0xeb, 0xef, 0xb4, 0xd1, 0x1e, 0x88, 0x2d, 0xbd, 0x6f, 0xe1, 0xbe,
0x35, 0xb0, 0x14, 0x16, 0x42, 0xb8, 0x1b, 0x0e, 0x38, 0x53, 0x16, 0xc8, 0x6a, 0x0e, 0xe7, 0x21,
0x65, 0x94, 0xcb, 0xad, 0x20, 0x01, 0xdd, 0x03, 0xb8, 0x50, 0xbb, 0x5a, 0x5b, 0xb5, 0x75, 0xd3,
0xaa, 0x24, 0x57, 0x13, 0x37, 0xf2, 0x1b, 0xab, 0x63, 0x5b, 0x7d, 0xe2, 0xb0, 0x1c, 0x1b, 0x6d,
0xd5, 0xc6, 0x5b, 0x49, 0x32, 0x5c, 0xd9, 0x27, 0x89, 0x9e, 0x82, 0xb2, 0x6a, 0x18, 0x8a, 0x65,
0xab, 0x36, 0x56, 0x4e, 0x2f, 0x6d, 0x6c, 0x51, 0x17, 0x5d, 0x90, 0x8b, 0xaa, 0x61, 0x1c, 0x11,
0xea, 0x16, 0x21, 0xa2, 0x27, 0xa1, 0x44, 0xbc, 0xb9, 0xa6, 0x76, 0x95, 0x0e, 0xd6, 0xce, 0x3b,
0x76, 0x25, 0xbd, 0x2a, 0xdc, 0x48, 0xc8, 0x45, 0x4e, 0x6d, 0x50, 0xa2, 0xd4, 0x76, 0x77, 0x9c,
0x7a, 0x72, 0x84, 0x20, 0xd9, 0x56, 0x6d, 0x95, 0xae, 0x64, 0x41, 0xa6, 0xdf, 0x84, 0x66, 0xa8,
0x76, 0x87, 0xaf, 0x0f, 0xfd, 0x46, 0x57, 0x20, 0xcd, 0xd5, 0x26, 0xa8, 0x5a, 0xde, 0x42, 0x4b,
0x90, 0x32, 0x4c, 0xfd, 0x02, 0xd3, 0xad, 0xcb, 0xca, 0xac, 0x21, 0xc9, 0x50, 0x0a, 0x7a, 0x7d,
0x54, 0x82, 0xb8, 0x3d, 0xe4, 0xbd, 0xc4, 0xed, 0x21, 0x7a, 0x01, 0x92, 0x64, 0x21, 0x69, 0x1f,
0xa5, 0x90, 0x38, 0xc7, 0xe5, 0x9a, 0x97, 0x06, 0x96, 0x29, 0xa7, 0x54, 0x86, 0x62, 0x20, 0x1a,
0x48, 0x57, 0x60, 0x29, 0xcc, 0xb9, 0x4b, 0x1d, 0x97, 0x1e, 0x70, 0xd2, 0xe8, 0x36, 0x64, 0x5d,
0xef, 0xce, 0x0c, 0xe7, 0xea, 0x58, 0xb7, 0x0e, 0xb3, 0xec, 0xb2, 0x12, 0x8b, 0x21, 0x1b, 0xd0,
0x51, 0x79, 0x2c, 0x2f, 0xc8, 0x19, 0xd5, 0x30, 0x1a, 0xaa, 0xd5, 0x91, 0xde, 0x86, 0x4a, 0x94,
0xe7, 0xf6, 0x2d, 0x98, 0x40, 0xcd, 0xde, 0x59, 0xb0, 0x2b, 0x90, 0x3e, 0xd3, 0xcd, 0x9e, 0x6a,
0x53, 0x65, 0x45, 0x99, 0xb7, 0xc8, 0x42, 0x32, 0x2f, 0x9e, 0xa0, 0x64, 0xd6, 0x90, 0x14, 0xb8,
0x1a, 0xe9, 0xbd, 0x89, 0x88, 0xd6, 0x6f, 0x63, 0xb6, 0xac, 0x45, 0x99, 0x35, 0x3c, 0x45, 0x6c,
0xb0, 0xac, 0x41, 0xba, 0xb5, 0xe8, 0x5c, 0xa9, 0xfe, 0x9c, 0xcc, 0x5b, 0xd2, 0x9f, 0x12, 0x70,
0x25, 0xdc, 0x87, 0xa3, 0x55, 0x28, 0xf4, 0xd4, 0xa1, 0x62, 0x0f, 0xb9, 0xd9, 0x09, 0x74, 0xe3,
0xa1, 0xa7, 0x0e, 0x9b, 0x43, 0x66, 0x73, 0x22, 0x24, 0xec, 0xa1, 0x55, 0x89, 0xaf, 0x26, 0x6e,
0x14, 0x64, 0xf2, 0x89, 0x8e, 0x61, 0xa1, 0xab, 0xb7, 0xd4, 0xae, 0xd2, 0x55, 0x2d, 0x5b, 0xe1,
0xc1, 0x9d, 0x1d, 0xa2, 0x27, 0xc6, 0x16, 0x9b, 0x79, 0x63, 0xdc, 0x66, 0xfb, 0x49, 0x1c, 0x0e,
0xb7, 0xff, 0x32, 0xd5, 0xb1, 0xa7, 0x3a, 0x5b, 0x8d, 0x8e, 0x61, 0xe9, 0xf4, 0xf2, 0x7d, 0xb5,
0x6f, 0x6b, 0x7d, 0xac, 0x8c, 0x1d, 0xab, 0x71, 0xeb, 0x79, 0xa0, 0x59, 0xa7, 0xb8, 0xa3, 0x5e,
0x68, 0xba, 0xc9, 0x55, 0x2e, 0xba, 0xf2, 0x27, 0xde, 0xd9, 0xf2, 0xf6, 0x28, 0x15, 0x30, 0x6a,
0xc7, 0xbd, 0xa4, 0xe7, 0x76, 0x2f, 0x2f, 0xc0, 0x52, 0x1f, 0x0f, 0x6d, 0xdf, 0x18, 0x99, 0xe1,
0x64, 0xe8, 0x5e, 0x20, 0xf2, 0xcf, 0xeb, 0x9f, 0xd8, 0x10, 0x7a, 0x86, 0x86, 0x45, 0x43, 0xb7,
0xb0, 0xa9, 0xa8, 0xed, 0xb6, 0x89, 0x2d, 0xab, 0x92, 0xa5, 0xdc, 0x65, 0x87, 0xbe, 0xc9, 0xc8,
0xd2, 0x6f, 0xfd, 0x7b, 0x15, 0x0c, 0x83, 0x7c, 0x27, 0x04, 0x6f, 0x27, 0x8e, 0x60, 0x89, 0xcb,
0xb7, 0x03, 0x9b, 0xc1, 0xd2, 0xd1, 0xc7, 0xc6, 0x0f, 0xdc, 0xe8, 0x26, 0x20, 0x47, 0x7c, 0x86,
0x7d, 0x48, 0x7c, 0xbd, 0x7d, 0x40, 0x90, 0xa4, 0xab, 0x94, 0x64, 0x4e, 0x88, 0x7c, 0xff, 0xbf,
0xed, 0xcd, 0x6b, 0xb0, 0x30, 0x96, 0x63, 0xb8, 0xf3, 0x12, 0x42, 0xe7, 0x15, 0xf7, 0xcf, 0x4b,
0xfa, 0x9d, 0x00, 0xd5, 0xe8, 0xa4, 0x22, 0x54, 0xd5, 0xb3, 0xb0, 0xe0, 0xce, 0xc5, 0x1d, 0x1f,
0x3b, 0xf5, 0xa2, 0xfb, 0x83, 0x0f, 0x30, 0xd2, 0x81, 0x3f, 0x09, 0xa5, 0x91, 0x94, 0x87, 0xed,
0x42, 0xf1, 0xc2, 0xdf, 0xbf, 0xf4, 0xab, 0x84, 0xeb, 0x55, 0x03, 0x79, 0x49, 0x88, 0xe5, 0xbd,
0x0e, 0x8b, 0x6d, 0xdc, 0xd2, 0xda, 0x5f, 0xd5, 0xf0, 0x16, 0xb8, 0xf4, 0x77, 0x76, 0x37, 0x83,
0xdd, 0xfd, 0x12, 0x20, 0x2b, 0x63, 0xcb, 0x20, 0xd9, 0x07, 0xda, 0x82, 0x1c, 0x1e, 0xb6, 0xb0,
0x61, 0x3b, 0x09, 0x5b, 0x78, 0x2a, 0xcc, 0xb8, 0xeb, 0x0e, 0x27, 0x01, 0x82, 0xae, 0x18, 0xba,
0xc5, 0xb1, 0x6e, 0x34, 0x6c, 0xe5, 0xe2, 0x7e, 0xb0, 0xfb, 0xa2, 0x03, 0x76, 0x13, 0x91, 0x38,
0x8e, 0x49, 0x8d, 0xa0, 0xdd, 0x5b, 0x1c, 0xed, 0x26, 0xa7, 0x74, 0x16, 0x80, 0xbb, 0xb5, 0x00,
0xdc, 0x4d, 0x4d, 0x99, 0x66, 0x04, 0xde, 0x7d, 0xd1, 0xc1, 0xbb, 0xe9, 0x29, 0x23, 0x1e, 0x01,
0xbc, 0xaf, 0xfa, 0x00, 0x6f, 0x96, 0x8a, 0xae, 0x46, 0x8a, 0x86, 0x20, 0xde, 0x97, 0x5d, 0xc4,
0x9b, 0x8f, 0x44, 0xcb, 0x5c, 0x78, 0x14, 0xf2, 0x1e, 0x8c, 0x41, 0x5e, 0x06, 0x51, 0x9f, 0x8a,
0x54, 0x31, 0x05, 0xf3, 0x1e, 0x8c, 0x61, 0xde, 0xe2, 0x14, 0x85, 0x53, 0x40, 0xef, 0x4f, 0xc3,
0x41, 0x6f, 0x34, 0x2c, 0xe5, 0xc3, 0x9c, 0x0d, 0xf5, 0x2a, 0x11, 0xa8, 0xb7, 0x1c, 0x89, 0xd0,
0x98, 0xfa, 0x99, 0x61, 0xef, 0x71, 0x08, 0xec, 0x65, 0x00, 0xf5, 0x46, 0xa4, 0xf2, 0x19, 0x70,
0xef, 0x71, 0x08, 0xee, 0x5d, 0x98, 0xaa, 0x76, 0x2a, 0xf0, 0xbd, 0x17, 0x04, 0xbe, 0x28, 0x22,
0xc7, 0xf2, 0x4e, 0x7b, 0x04, 0xf2, 0x3d, 0x8d, 0x42, 0xbe, 0x0c, 0x9d, 0x3e, 0x17, 0xa9, 0x71,
0x0e, 0xe8, 0x7b, 0x30, 0x06, 0x7d, 0x97, 0xa6, 0x58, 0xda, 0xec, 0xd8, 0x37, 0x23, 0x66, 0x19,
0xea, 0xdd, 0x4d, 0x66, 0x41, 0xcc, 0x4b, 0xcf, 0x90, 0x40, 0x3c, 0xe2, 0xe1, 0x48, 0x4e, 0x8c,
0x4d, 0x53, 0x37, 0x39, 0x8a, 0x65, 0x0d, 0xe9, 0x06, 0xc1, 0x42, 0x9e, 0x37, 0x9b, 0x80, 0x93,
0x29, 0xf6, 0xf0, 0x79, 0x30, 0xe9, 0xcf, 0x82, 0x27, 0x4b, 0x91, 0xb2, 0x1f, 0x47, 0xe5, 0x38,
0x8e, 0xf2, 0xa1, 0xe7, 0x78, 0x10, 0x3d, 0xaf, 0x40, 0x9e, 0x60, 0x8a, 0x11, 0x60, 0xac, 0x1a,
0x2e, 0x30, 0xbe, 0x09, 0x0b, 0x34, 0x76, 0x32, 0x8c, 0xcd, 0x03, 0x52, 0x92, 0x06, 0xa4, 0x32,
0xf9, 0xc1, 0xd6, 0x85, 0x45, 0xa6, 0xe7, 0x61, 0xd1, 0xc7, 0xeb, 0x62, 0x15, 0x86, 0x12, 0x45,
0x97, 0x7b, 0x93, 0x83, 0x96, 0xbf, 0x09, 0xde, 0x0a, 0x79, 0x88, 0x3a, 0x0c, 0xfc, 0x0a, 0xdf,
0x10, 0xf8, 0x8d, 0x7f, 0x65, 0xf0, 0xeb, 0xc7, 0x5e, 0x89, 0x20, 0xf6, 0xfa, 0x8f, 0xe0, 0xed,
0x89, 0x0b, 0x65, 0x5b, 0x7a, 0x1b, 0x73, 0x34, 0x44, 0xbf, 0x49, 0x76, 0xd2, 0xd5, 0xcf, 0x39,
0xe6, 0x21, 0x9f, 0x84, 0xcb, 0x0d, 0x39, 0x39, 0x1e, 0x51, 0x5c, 0x20, 0xc5, 0x42, 0x3e, 0x07,
0x52, 0x22, 0x24, 0x1e, 0x62, 0x16, 0x20, 0x0a, 0x32, 0xf9, 0x24, 0x7c, 0xd4, 0xec, 0x78, 0xe8,
0x66, 0x0d, 0x74, 0x07, 0x72, 0xb4, 0x58, 0xad, 0xe8, 0x86, 0xc5, 0x63, 0x42, 0x20, 0xcb, 0x61,
0x15, 0xeb, 0xb5, 0x43, 0xc2, 0x73, 0x60, 0x58, 0x72, 0xd6, 0xe0, 0x5f, 0xbe, 0x5c, 0x23, 0x17,
0xc8, 0x35, 0xae, 0x41, 0x8e, 0x8c, 0xde, 0x32, 0xd4, 0x16, 0xa6, 0xa5, 0xd1, 0x9c, 0xec, 0x11,
0xa4, 0x4f, 0x04, 0x28, 0x8f, 0x84, 0x98, 0xd0, 0xb9, 0x3b, 0x26, 0x19, 0xf7, 0x41, 0xfb, 0xeb,
0x00, 0xe7, 0xaa, 0xa5, 0xbc, 0xa7, 0xf6, 0x6d, 0xdc, 0xe6, 0xd3, 0xcd, 0x9d, 0xab, 0xd6, 0x1b,
0x94, 0x10, 0xec, 0x38, 0x3b, 0xd2, 0xb1, 0x0f, 0x43, 0xe6, 0xfc, 0x18, 0x12, 0x55, 0x21, 0x6b,
0x98, 0x9a, 0x6e, 0x6a, 0xf6, 0x25, 0x1d, 0x6d, 0x42, 0x76, 0xdb, 0xbb, 0xc9, 0x6c, 0x42, 0x4c,
0xee, 0x26, 0xb3, 0x49, 0x31, 0xe5, 0x16, 0xaa, 0xd8, 0x91, 0xcd, 0x8b, 0x05, 0xe9, 0x83, 0xb8,
0x67, 0x8b, 0xdb, 0xb8, 0xab, 0x5d, 0x60, 0x73, 0x8e, 0xc9, 0xcc, 0xb6, 0xb9, 0xcb, 0x21, 0x53,
0xf6, 0x51, 0xc8, 0xe8, 0x49, 0x6b, 0x60, 0xe1, 0x36, 0x2f, 0x99, 0xb8, 0x6d, 0xd4, 0x80, 0x34,
0xbe, 0xc0, 0x7d, 0xdb, 0xaa, 0x64, 0xa8, 0x0d, 0x5f, 0x19, 0xc7, 0xb0, 0xe4, 0xf7, 0x56, 0x85,
0x58, 0xee, 0x97, 0x9f, 0xad, 0x88, 0x8c, 0xfb, 0x39, 0xbd, 0xa7, 0xd9, 0xb8, 0x67, 0xd8, 0x97,
0x32, 0x97, 0x9f, 0xbc, 0xb2, 0xd2, 0x6d, 0x28, 0x05, 0xe3, 0x3e, 0x7a, 0x02, 0x8a, 0x26, 0xb6,
0x55, 0xad, 0xaf, 0x04, 0xb2, 0xf6, 0x02, 0x23, 0xf2, 0x62, 0xce, 0x21, 0x3c, 0x12, 0x1a, 0xeb,
0xd1, 0x4b, 0x90, 0xf3, 0xd2, 0x04, 0x81, 0x0e, 0x7d, 0x42, 0xad, 0xc3, 0xe3, 0x95, 0xfe, 0x2a,
0x78, 0x2a, 0x83, 0xd5, 0x93, 0x3a, 0xa4, 0x4d, 0x6c, 0x0d, 0xba, 0xac, 0x9e, 0x51, 0xda, 0x78,
0x7e, 0xb6, 0x2c, 0x81, 0x50, 0x07, 0x5d, 0x5b, 0xe6, 0xc2, 0xd2, 0x5b, 0x90, 0x66, 0x14, 0x94,
0x87, 0xcc, 0xf1, 0xfe, 0xfd, 0xfd, 0x83, 0x37, 0xf6, 0xc5, 0x18, 0x02, 0x48, 0x6f, 0xd6, 0x6a,
0xf5, 0xc3, 0xa6, 0x28, 0xa0, 0x1c, 0xa4, 0x36, 0xb7, 0x0e, 0xe4, 0xa6, 0x18, 0x27, 0x64, 0xb9,
0xbe, 0x5b, 0xaf, 0x35, 0xc5, 0x04, 0x5a, 0x80, 0x22, 0xfb, 0x56, 0xee, 0x1d, 0xc8, 0x0f, 0x36,
0x9b, 0x62, 0xd2, 0x47, 0x3a, 0xaa, 0xef, 0x6f, 0xd7, 0x65, 0x31, 0x25, 0x7d, 0x0f, 0xae, 0x46,
0xe6, 0x15, 0x5e, 0x69, 0x44, 0xf0, 0x95, 0x46, 0xa4, 0xdf, 0xc4, 0x09, 0xf2, 0x8a, 0x4a, 0x16,
0xd0, 0xee, 0xc8, 0xc4, 0x37, 0xe6, 0xc8, 0x34, 0x46, 0x66, 0x4f, 0xc0, 0x96, 0x89, 0xcf, 0xb0,
0xdd, 0xea, 0xb0, 0xe4, 0x85, 0xf9, 0xc6, 0xa2, 0x5c, 0xe4, 0x54, 0x2a, 0x64, 0x31, 0xb6, 0x77,
0x70, 0xcb, 0x56, 0xd8, 0x09, 0x63, 0x40, 0x27, 0x47, 0xd8, 0x08, 0xf5, 0x88, 0x11, 0xa5, 0xb7,
0xe7, 0x5a, 0xcb, 0x1c, 0xa4, 0xe4, 0x7a, 0x53, 0x7e, 0x53, 0x4c, 0x20, 0x04, 0x25, 0xfa, 0xa9,
0x1c, 0xed, 0x6f, 0x1e, 0x1e, 0x35, 0x0e, 0xc8, 0x5a, 0x2e, 0x42, 0xd9, 0x59, 0x4b, 0x87, 0x98,
0x92, 0xfe, 0x11, 0x87, 0x47, 0x23, 0x52, 0x1d, 0x74, 0x07, 0xc0, 0x1e, 0x2a, 0x26, 0x6e, 0xe9,
0x66, 0x3b, 0xda, 0xc8, 0x9a, 0x43, 0x99, 0x72, 0xc8, 0x39, 0x9b, 0x7f, 0x59, 0x13, 0x2a, 0x6a,
0xe8, 0x15, 0xae, 0x94, 0xcc, 0xca, 0x81, 0x77, 0xd7, 0x43, 0x0a, 0x47, 0xb8, 0x45, 0x14, 0xd3,
0xb5, 0xa5, 0x8a, 0x29, 0x3f, 0x7a, 0xe0, 0x07, 0xc4, 0x03, 0x1a, 0x54, 0x66, 0x2e, 0xbd, 0xfa,
0x20, 0x33, 0x23, 0x58, 0xe8, 0x4d, 0x78, 0x74, 0x24, 0x26, 0xba, 0x4a, 0x53, 0xb3, 0x86, 0xc6,
0x47, 0x82, 0xa1, 0x91, 0xab, 0x96, 0x7e, 0x9f, 0xf0, 0x2f, 0x6c, 0x30, 0xb3, 0x3b, 0x80, 0xb4,
0x65, 0xab, 0xf6, 0xc0, 0xe2, 0x06, 0xf7, 0xd2, 0xac, 0x69, 0xe2, 0x9a, 0xf3, 0x71, 0x44, 0xc5,
0x65, 0xae, 0xe6, 0xbb, 0xf5, 0xb6, 0x88, 0x83, 0x0d, 0x2e, 0x4e, 0xf4, 0x91, 0xf1, 0x7c, 0x4e,
0x5c, 0xba, 0x0b, 0x68, 0x3c, 0x81, 0x0e, 0x29, 0x99, 0x08, 0x61, 0x25, 0x93, 0x3f, 0x08, 0xf0,
0xd8, 0x84, 0x64, 0x19, 0xbd, 0x3e, 0xb2, 0xcf, 0x2f, 0xcf, 0x93, 0x6a, 0xaf, 0x31, 0x5a, 0x70,
0xa7, 0xa5, 0x5b, 0x50, 0xf0, 0xd3, 0x67, 0x9b, 0xe4, 0x97, 0x71, 0xcf, 0xe7, 0x07, 0x6b, 0x3b,
0x5e, 0xf8, 0x13, 0xbe, 0x66, 0xf8, 0x0b, 0xda, 0x59, 0x7c, 0x4e, 0x3b, 0x3b, 0x0a, 0xb3, 0xb3,
0xc4, 0x5c, 0x59, 0xe5, 0x5c, 0xd6, 0x96, 0xfc, 0x7a, 0xd6, 0x16, 0x38, 0x70, 0xa9, 0x60, 0xda,
0xfa, 0x26, 0x80, 0x57, 0xf0, 0x22, 0x01, 0xc9, 0xd4, 0x07, 0xfd, 0x36, 0xb5, 0x80, 0x94, 0xcc,
0x1a, 0xe8, 0x36, 0xa4, 0x88, 0x25, 0x39, 0xeb, 0x34, 0xee, 0x54, 0x89, 0x25, 0xf8, 0x0a, 0x66,
0x8c, 0x5b, 0xd2, 0x00, 0x8d, 0x57, 0xd4, 0x23, 0xba, 0x78, 0x35, 0xd8, 0xc5, 0xe3, 0x91, 0xb5,
0xf9, 0xf0, 0xae, 0xde, 0x87, 0x14, 0xdd, 0x79, 0x92, 0x70, 0xd1, 0x6b, 0x1c, 0x0e, 0x7b, 0xc8,
0x37, 0xfa, 0x19, 0x80, 0x6a, 0xdb, 0xa6, 0x76, 0x3a, 0xf0, 0x3a, 0x58, 0x09, 0xb7, 0x9c, 0x4d,
0x87, 0x6f, 0xeb, 0x1a, 0x37, 0xa1, 0x25, 0x4f, 0xd4, 0x67, 0x46, 0x3e, 0x85, 0xd2, 0x3e, 0x94,
0x82, 0xb2, 0x4e, 0xa2, 0xce, 0xc6, 0x10, 0x4c, 0xd4, 0x19, 0xee, 0xe2, 0x89, 0xba, 0x9b, 0xe6,
0x27, 0xd8, 0x5d, 0x15, 0x6d, 0x48, 0xff, 0x15, 0xa0, 0xe0, 0x37, 0xbc, 0x6f, 0x38, 0xfd, 0x9c,
0x92, 0x71, 0x5f, 0x1d, 0xcb, 0x3e, 0x33, 0xe7, 0xaa, 0x75, 0xfc, 0x6d, 0x26, 0x9f, 0x1f, 0x08,
0x90, 0x75, 0x27, 0x1f, 0xbc, 0xb6, 0x0a, 0xdc, 0xf3, 0xb1, 0xb5, 0x8b, 0xfb, 0xef, 0x9a, 0xd8,
0xad, 0x5e, 0xc2, 0xbd, 0xd5, 0xbb, 0xeb, 0xe6, 0x4a, 0x51, 0x15, 0x3d, 0xff, 0x4a, 0x73, 0x9b,
0x72, 0x52, 0xc3, 0x5f, 0xf3, 0x71, 0x90, 0x24, 0x01, 0xfd, 0x00, 0xd2, 0x6a, 0xcb, 0xad, 0x63,
0x96, 0x42, 0x0a, 0x7c, 0x0e, 0xeb, 0x5a, 0x73, 0xb8, 0x49, 0x39, 0x65, 0x2e, 0xc1, 0x47, 0x15,
0x77, 0x46, 0x25, 0xbd, 0x46, 0xf4, 0x32, 0x9e, 0xa0, 0x47, 0x2c, 0x01, 0x1c, 0xef, 0x3f, 0x38,
0xd8, 0xde, 0xb9, 0xb7, 0x53, 0xdf, 0xe6, 0xd9, 0xd2, 0xf6, 0x76, 0x7d, 0x5b, 0x8c, 0x13, 0x3e,
0xb9, 0xfe, 0xe0, 0xe0, 0xa4, 0xbe, 0x2d, 0x26, 0xa4, 0xbb, 0x90, 0x73, 0xbd, 0x0a, 0x41, 0xf5,
0x4e, 0x4d, 0x56, 0xe0, 0x67, 0x9b, 0x97, 0xd8, 0x97, 0x20, 0x65, 0xe8, 0xef, 0xf1, 0x2b, 0xb6,
0x84, 0xcc, 0x1a, 0x52, 0x1b, 0xca, 0x23, 0x2e, 0x09, 0xdd, 0x85, 0x8c, 0x31, 0x38, 0x55, 0x1c,
0xa3, 0x1d, 0xa9, 0x60, 0x3b, 0x78, 0x71, 0x70, 0xda, 0xd5, 0x5a, 0xf7, 0xf1, 0xa5, 0xb3, 0x4c,
0xc6, 0xe0, 0xf4, 0x3e, 0xb3, 0x6d, 0xd6, 0x4b, 0xdc, 0xdf, 0xcb, 0x05, 0x64, 0x9d, 0xa3, 0x8a,
0x7e, 0x08, 0x39, 0xd7, 0xdb, 0xb9, 0x57, 0xe4, 0x91, 0x6e, 0x92, 0xab, 0xf7, 0x44, 0xd0, 0x4d,
0x58, 0xb0, 0xb4, 0xf3, 0xbe, 0x53, 0xbf, 0x67, 0x15, 0x9b, 0x38, 0x3d, 0x33, 0x65, 0xf6, 0x63,
0xcf, 0x29, 0x2a, 0x90, 0x20, 0x27, 0x8e, 0xfa, 0x8a, 0x6f, 0x73, 0x00, 0x21, 0xc1, 0x38, 0x11,
0x16, 0x8c, 0x7f, 0x11, 0x87, 0xbc, 0xef, 0x56, 0x00, 0x7d, 0xdf, 0xe7, 0xb8, 0x4a, 0x21, 0x51,
0xc4, 0xc7, 0xeb, 0xdd, 0x41, 0x07, 0x27, 0x16, 0x9f, 0x7f, 0x62, 0x51, 0x97, 0x30, 0xce, 0xe5,
0x42, 0x72, 0xee, 0xcb, 0x85, 0xe7, 0x00, 0xd9, 0xba, 0xad, 0x76, 0x95, 0x0b, 0xdd, 0xd6, 0xfa,
0xe7, 0x0a, 0x33, 0x0d, 0xe6, 0x66, 0x44, 0xfa, 0xe7, 0x84, 0xfe, 0x38, 0xa4, 0x56, 0xf2, 0x73,
0x01, 0xb2, 0x2e, 0xa2, 0x9b, 0xf7, 0x86, 0xfa, 0x0a, 0xa4, 0x39, 0x68, 0x61, 0x57, 0xd4, 0xbc,
0x15, 0x7a, 0x8b, 0x52, 0x85, 0x6c, 0x0f, 0xdb, 0x2a, 0xf5, 0x99, 0x2c, 0x02, 0xba, 0xed, 0x9b,
0x2f, 0x43, 0xde, 0x77, 0xbb, 0x4f, 0xdc, 0xe8, 0x7e, 0xfd, 0x0d, 0x31, 0x56, 0xcd, 0x7c, 0xf8,
0xf1, 0x6a, 0x62, 0x1f, 0xbf, 0x47, 0x4e, 0x98, 0x5c, 0xaf, 0x35, 0xea, 0xb5, 0xfb, 0xa2, 0x50,
0xcd, 0x7f, 0xf8, 0xf1, 0x6a, 0x46, 0xc6, 0xb4, 0x80, 0x7e, 0xf3, 0x3e, 0x94, 0x47, 0x36, 0x26,
0x78, 0xa0, 0x11, 0x94, 0xb6, 0x8f, 0x0f, 0xf7, 0x76, 0x6a, 0x9b, 0xcd, 0xba, 0x72, 0x72, 0xd0,
0xac, 0x8b, 0x02, 0x7a, 0x14, 0x16, 0xf7, 0x76, 0x7e, 0xd4, 0x68, 0x2a, 0xb5, 0xbd, 0x9d, 0xfa,
0x7e, 0x53, 0xd9, 0x6c, 0x36, 0x37, 0x6b, 0xf7, 0xc5, 0xf8, 0xc6, 0x1f, 0xf3, 0x50, 0xde, 0xdc,
0xaa, 0xed, 0x10, 0xd8, 0xa6, 0xb5, 0x54, 0xea, 0x1e, 0x6a, 0x90, 0xa4, 0xa5, 0xc0, 0x89, 0x6f,
0xfc, 0xaa, 0x93, 0x6f, 0x45, 0xd0, 0x3d, 0x48, 0xd1, 0x2a, 0x21, 0x9a, 0xfc, 0xe8, 0xaf, 0x3a,
0xe5, 0x9a, 0x84, 0x0c, 0x86, 0x1e, 0xa7, 0x89, 0xaf, 0x00, 0xab, 0x93, 0x6f, 0x4d, 0xd0, 0x1e,
0x64, 0x9c, 0x22, 0xd1, 0xb4, 0xa7, 0x79, 0xd5, 0xa9, 0x57, 0x19, 0x64, 0x6a, 0xac, 0xd8, 0x36,
0xf9, 0x81, 0x60, 0x75, 0xca, 0x7d, 0x0a, 0xda, 0x81, 0x34, 0x2f, 0x74, 0x4c, 0x79, 0xf3, 0x57,
0x9d, 0x76, 0x43, 0x82, 0x64, 0xc8, 0x79, 0x65, 0xcc, 0xe9, 0xcf, 0x1e, 0xab, 0x33, 0x5c, 0x15,
0xa1, 0xb7, 0xa0, 0x18, 0x2c, 0xa8, 0xcc, 0xf6, 0xae, 0xb0, 0x3a, 0xe3, 0x5d, 0x0c, 0xd1, 0x1f,
0xac, 0xae, 0xcc, 0xf6, 0xce, 0xb0, 0x3a, 0xe3, 0xd5, 0x0c, 0x7a, 0x07, 0x16, 0xc6, 0xab, 0x1f,
0xb3, 0x3f, 0x3b, 0xac, 0xce, 0x71, 0x59, 0x83, 0x7a, 0x80, 0x42, 0xaa, 0x26, 0x73, 0xbc, 0x42,
0xac, 0xce, 0x73, 0x77, 0x83, 0xda, 0x50, 0x1e, 0xad, 0x44, 0xcc, 0xfa, 0x2a, 0xb1, 0x3a, 0xf3,
0x3d, 0x0e, 0xeb, 0x25, 0x08, 0xcb, 0x67, 0x7d, 0xa5, 0x58, 0x9d, 0xf9, 0x5a, 0x07, 0x1d, 0x03,
0xf8, 0x60, 0xe5, 0x0c, 0xaf, 0x16, 0xab, 0xb3, 0x5c, 0xf0, 0x20, 0x03, 0x16, 0xc3, 0xf0, 0xe6,
0x3c, 0x8f, 0x18, 0xab, 0x73, 0xdd, 0xfb, 0x10, 0x7b, 0x0e, 0x22, 0xc7, 0xd9, 0x1e, 0x35, 0x56,
0x67, 0xbc, 0x00, 0xda, 0xaa, 0x7f, 0xf2, 0xf9, 0xb2, 0xf0, 0xe9, 0xe7, 0xcb, 0xc2, 0xbf, 0x3f,
0x5f, 0x16, 0x3e, 0xfa, 0x62, 0x39, 0xf6, 0xe9, 0x17, 0xcb, 0xb1, 0x7f, 0x7e, 0xb1, 0x1c, 0xfb,
0xf1, 0xb3, 0xe7, 0x9a, 0xdd, 0x19, 0x9c, 0xae, 0xb5, 0xf4, 0xde, 0xba, 0xff, 0x1d, 0x78, 0xd8,
0xeb, 0xf3, 0xd3, 0x34, 0x0d, 0xa8, 0xb7, 0xfe, 0x17, 0x00, 0x00, 0xff, 0xff, 0x4c, 0xb4, 0x7f,
0x53, 0x9d, 0x2e, 0x00, 0x00,
0x11, 0xd7, 0xe8, 0x5b, 0x2d, 0x4b, 0x1a, 0x3f, 0x9b, 0x45, 0x2b, 0x76, 0x6d, 0x33, 0x14, 0xb0,
0x2c, 0x60, 0x13, 0x6f, 0x16, 0x96, 0x2c, 0x84, 0x92, 0x65, 0x6d, 0x64, 0xaf, 0xd7, 0x36, 0x63,
0xd9, 0x14, 0xf9, 0x60, 0x18, 0x4b, 0xcf, 0xd6, 0xb0, 0x92, 0x66, 0x98, 0x19, 0x19, 0x99, 0x63,
0x12, 0xaa, 0x52, 0x1c, 0x52, 0xdc, 0xc2, 0x21, 0xdc, 0x92, 0xaa, 0xfc, 0x09, 0xc9, 0x25, 0xa7,
0x1c, 0x38, 0xe4, 0xc0, 0x29, 0x95, 0x13, 0x49, 0xc1, 0x8d, 0x7f, 0x20, 0xb7, 0x54, 0xea, 0x7d,
0xcc, 0x97, 0x34, 0xa3, 0x0f, 0xa0, 0xa8, 0x4a, 0x15, 0xb7, 0x79, 0x3d, 0xdd, 0xfd, 0xbe, 0xfa,
0x75, 0xf7, 0xaf, 0xdf, 0x83, 0xc7, 0x6c, 0xdc, 0x6f, 0x63, 0xb3, 0xa7, 0xf5, 0xed, 0x0d, 0xf5,
0xb4, 0xa5, 0x6d, 0xd8, 0x97, 0x06, 0xb6, 0xd6, 0x0d, 0x53, 0xb7, 0x75, 0x54, 0xf2, 0x7e, 0xae,
0x93, 0x9f, 0x95, 0xeb, 0x3e, 0xee, 0x96, 0x79, 0x69, 0xd8, 0xfa, 0x86, 0x61, 0xea, 0xfa, 0x19,
0xe3, 0xaf, 0x5c, 0x1b, 0xff, 0xfd, 0x10, 0x5f, 0x72, 0x6d, 0x01, 0x61, 0xda, 0xcb, 0x86, 0xa1,
0x9a, 0x6a, 0xcf, 0xf9, 0xbd, 0x7a, 0xae, 0xeb, 0xe7, 0x5d, 0xbc, 0x41, 0x5b, 0xa7, 0x83, 0xb3,
0x0d, 0x5b, 0xeb, 0x61, 0xcb, 0x56, 0x7b, 0x06, 0x67, 0x58, 0x3e, 0xd7, 0xcf, 0x75, 0xfa, 0xb9,
0x41, 0xbe, 0x18, 0x55, 0xfa, 0x4b, 0x0e, 0x32, 0x32, 0x7e, 0x77, 0x80, 0x2d, 0x1b, 0x6d, 0x42,
0x12, 0xb7, 0x3a, 0x7a, 0x59, 0x58, 0x13, 0x6e, 0xe4, 0x37, 0xaf, 0xad, 0x8f, 0x0c, 0x7f, 0x9d,
0xf3, 0xd5, 0x5b, 0x1d, 0xbd, 0x11, 0x93, 0x29, 0x2f, 0xba, 0x0d, 0xa9, 0xb3, 0xee, 0xc0, 0xea,
0x94, 0xe3, 0x54, 0xe8, 0x7a, 0x94, 0xd0, 0x3d, 0xc2, 0xd4, 0x88, 0xc9, 0x8c, 0x9b, 0x74, 0xa5,
0xf5, 0xcf, 0xf4, 0x72, 0x62, 0x72, 0x57, 0x3b, 0xfd, 0x33, 0xda, 0x15, 0xe1, 0x45, 0x5b, 0x00,
0x5a, 0x5f, 0xb3, 0x95, 0x56, 0x47, 0xd5, 0xfa, 0xe5, 0x24, 0x95, 0x7c, 0x3c, 0x5a, 0x52, 0xb3,
0x6b, 0x84, 0xb1, 0x11, 0x93, 0x73, 0x9a, 0xd3, 0x20, 0xc3, 0x7d, 0x77, 0x80, 0xcd, 0xcb, 0x72,
0x6a, 0xf2, 0x70, 0x5f, 0x27, 0x4c, 0x64, 0xb8, 0x94, 0x1b, 0xbd, 0x02, 0xd9, 0x56, 0x07, 0xb7,
0x1e, 0x2a, 0xf6, 0xb0, 0x9c, 0xa1, 0x92, 0xab, 0x51, 0x92, 0x35, 0xc2, 0xd7, 0x1c, 0x36, 0x62,
0x72, 0xa6, 0xc5, 0x3e, 0xd1, 0x1d, 0x48, 0xb7, 0xf4, 0x5e, 0x4f, 0xb3, 0xcb, 0x40, 0x65, 0x57,
0x22, 0x65, 0x29, 0x57, 0x23, 0x26, 0x73, 0x7e, 0xb4, 0x0f, 0xc5, 0xae, 0x66, 0xd9, 0x8a, 0xd5,
0x57, 0x0d, 0xab, 0xa3, 0xdb, 0x56, 0x39, 0x4f, 0x35, 0x3c, 0x19, 0xa5, 0x61, 0x4f, 0xb3, 0xec,
0x23, 0x87, 0xb9, 0x11, 0x93, 0x0b, 0x5d, 0x3f, 0x81, 0xe8, 0xd3, 0xcf, 0xce, 0xb0, 0xe9, 0x2a,
0x2c, 0x2f, 0x4c, 0xd6, 0x77, 0x40, 0xb8, 0x1d, 0x79, 0xa2, 0x4f, 0xf7, 0x13, 0xd0, 0xcf, 0x60,
0xa9, 0xab, 0xab, 0x6d, 0x57, 0x9d, 0xd2, 0xea, 0x0c, 0xfa, 0x0f, 0xcb, 0x05, 0xaa, 0xf4, 0x99,
0xc8, 0x41, 0xea, 0x6a, 0xdb, 0x51, 0x51, 0x23, 0x02, 0x8d, 0x98, 0xbc, 0xd8, 0x1d, 0x25, 0xa2,
0xb7, 0x60, 0x59, 0x35, 0x8c, 0xee, 0xe5, 0xa8, 0xf6, 0x22, 0xd5, 0x7e, 0x33, 0x4a, 0x7b, 0x95,
0xc8, 0x8c, 0xaa, 0x47, 0xea, 0x18, 0x15, 0x35, 0x41, 0x34, 0x4c, 0x6c, 0xa8, 0x26, 0x56, 0x0c,
0x53, 0x37, 0x74, 0x4b, 0xed, 0x96, 0x4b, 0x54, 0xf7, 0xd3, 0x51, 0xba, 0x0f, 0x19, 0xff, 0x21,
0x67, 0x6f, 0xc4, 0xe4, 0x92, 0x11, 0x24, 0x31, 0xad, 0x7a, 0x0b, 0x5b, 0x96, 0xa7, 0x55, 0x9c,
0xa6, 0x95, 0xf2, 0x07, 0xb5, 0x06, 0x48, 0xa8, 0x0e, 0x79, 0x3c, 0x24, 0xe2, 0xca, 0x85, 0x6e,
0xe3, 0xf2, 0x22, 0x55, 0x28, 0x45, 0x9e, 0x50, 0xca, 0x7a, 0xa2, 0xdb, 0xb8, 0x11, 0x93, 0x01,
0xbb, 0x2d, 0xa4, 0xc2, 0x23, 0x17, 0xd8, 0xd4, 0xce, 0x2e, 0xa9, 0x1a, 0x85, 0xfe, 0xb1, 0x34,
0xbd, 0x5f, 0x46, 0x54, 0xe1, 0xb3, 0x51, 0x0a, 0x4f, 0xa8, 0x10, 0x51, 0x51, 0x77, 0x44, 0x1a,
0x31, 0x79, 0xe9, 0x62, 0x9c, 0x4c, 0x4c, 0xec, 0x4c, 0xeb, 0xab, 0x5d, 0xed, 0x7d, 0xac, 0x9c,
0x76, 0xf5, 0xd6, 0xc3, 0xf2, 0xd2, 0x64, 0x13, 0xbb, 0xc7, 0xb9, 0xb7, 0x08, 0x33, 0x31, 0xb1,
0x33, 0x3f, 0x61, 0x2b, 0x03, 0xa9, 0x0b, 0xb5, 0x3b, 0xc0, 0xbb, 0xc9, 0x6c, 0x5a, 0xcc, 0xec,
0x26, 0xb3, 0x59, 0x31, 0xb7, 0x9b, 0xcc, 0xe6, 0x44, 0x90, 0x9e, 0x86, 0xbc, 0xcf, 0x25, 0xa1,
0x32, 0x64, 0x7a, 0xd8, 0xb2, 0xd4, 0x73, 0x4c, 0x3d, 0x58, 0x4e, 0x76, 0x9a, 0x52, 0x11, 0x16,
0xfc, 0x6e, 0x48, 0xfa, 0x48, 0x70, 0x25, 0x89, 0x87, 0x21, 0x92, 0x17, 0xd8, 0xa4, 0x0b, 0xc1,
0x25, 0x79, 0x13, 0x3d, 0x01, 0x05, 0x3a, 0x09, 0xc5, 0xf9, 0x4f, 0xdc, 0x5c, 0x52, 0x5e, 0xa0,
0xc4, 0x13, 0xce, 0xb4, 0x0a, 0x79, 0x63, 0xd3, 0x70, 0x59, 0x12, 0x94, 0x05, 0x8c, 0x4d, 0xc3,
0x61, 0x78, 0x1c, 0x16, 0xc8, 0x8c, 0x5d, 0x8e, 0x24, 0xed, 0x24, 0x4f, 0x68, 0x9c, 0x45, 0xfa,
0x7b, 0x1c, 0xc4, 0x51, 0xd7, 0x85, 0xee, 0x40, 0x92, 0x78, 0x71, 0xee, 0x90, 0x2b, 0xeb, 0xcc,
0xc5, 0xaf, 0x3b, 0x2e, 0x7e, 0xbd, 0xe9, 0xb8, 0xf8, 0xad, 0xec, 0xa7, 0x9f, 0xaf, 0xc6, 0x3e,
0xfa, 0xd7, 0xaa, 0x20, 0x53, 0x09, 0x74, 0x95, 0x38, 0x2c, 0x55, 0xeb, 0x2b, 0x5a, 0x9b, 0x0e,
0x39, 0x47, 0xbc, 0x91, 0xaa, 0xf5, 0x77, 0xda, 0x68, 0x0f, 0xc4, 0x96, 0xde, 0xb7, 0x70, 0xdf,
0x1a, 0x58, 0x0a, 0x0b, 0x21, 0xdc, 0x0d, 0x07, 0x9c, 0x29, 0x0b, 0x64, 0x35, 0x87, 0xf3, 0x90,
0x32, 0xca, 0xa5, 0x56, 0x90, 0x80, 0xee, 0x01, 0x5c, 0xa8, 0x5d, 0xad, 0xad, 0xda, 0xba, 0x69,
0x95, 0x93, 0x6b, 0x89, 0x1b, 0xf9, 0xcd, 0xb5, 0xb1, 0xad, 0x3e, 0x71, 0x58, 0x8e, 0x8d, 0xb6,
0x6a, 0xe3, 0xad, 0x24, 0x19, 0xae, 0xec, 0x93, 0x44, 0x4f, 0x41, 0x49, 0x35, 0x0c, 0xc5, 0xb2,
0x55, 0x1b, 0x2b, 0xa7, 0x97, 0x36, 0xb6, 0xa8, 0x8b, 0x5e, 0x90, 0x0b, 0xaa, 0x61, 0x1c, 0x11,
0xea, 0x16, 0x21, 0xa2, 0x27, 0xa1, 0x48, 0xbc, 0xb9, 0xa6, 0x76, 0x95, 0x0e, 0xd6, 0xce, 0x3b,
0x76, 0x39, 0xbd, 0x26, 0xdc, 0x48, 0xc8, 0x05, 0x4e, 0x6d, 0x50, 0xa2, 0xd4, 0x76, 0x77, 0x9c,
0x7a, 0x72, 0x84, 0x20, 0xd9, 0x56, 0x6d, 0x95, 0xae, 0xe4, 0x82, 0x4c, 0xbf, 0x09, 0xcd, 0x50,
0xed, 0x0e, 0x5f, 0x1f, 0xfa, 0x8d, 0xae, 0x40, 0x9a, 0xab, 0x4d, 0x50, 0xb5, 0xbc, 0x85, 0x96,
0x21, 0x65, 0x98, 0xfa, 0x05, 0xa6, 0x5b, 0x97, 0x95, 0x59, 0x43, 0x92, 0xa1, 0x18, 0xf4, 0xfa,
0xa8, 0x08, 0x71, 0x7b, 0xc8, 0x7b, 0x89, 0xdb, 0x43, 0xf4, 0x02, 0x24, 0xc9, 0x42, 0xd2, 0x3e,
0x8a, 0x21, 0x71, 0x8e, 0xcb, 0x35, 0x2f, 0x0d, 0x2c, 0x53, 0x4e, 0xa9, 0x04, 0x85, 0x40, 0x34,
0x90, 0xae, 0xc0, 0x72, 0x98, 0x73, 0x97, 0x3a, 0x2e, 0x3d, 0xe0, 0xa4, 0xd1, 0x6d, 0xc8, 0xba,
0xde, 0x9d, 0x19, 0xce, 0xd5, 0xb1, 0x6e, 0x1d, 0x66, 0xd9, 0x65, 0x25, 0x16, 0x43, 0x36, 0xa0,
0xa3, 0xf2, 0x58, 0xbe, 0x20, 0x67, 0x54, 0xc3, 0x68, 0xa8, 0x56, 0x47, 0x7a, 0x1b, 0xca, 0x51,
0x9e, 0xdb, 0xb7, 0x60, 0x02, 0x35, 0x7b, 0x67, 0xc1, 0xae, 0x40, 0xfa, 0x4c, 0x37, 0x7b, 0xaa,
0x4d, 0x95, 0x15, 0x64, 0xde, 0x22, 0x0b, 0xc9, 0xbc, 0x78, 0x82, 0x92, 0x59, 0x43, 0x52, 0xe0,
0x6a, 0xa4, 0xf7, 0x26, 0x22, 0x5a, 0xbf, 0x8d, 0xd9, 0xb2, 0x16, 0x64, 0xd6, 0xf0, 0x14, 0xb1,
0xc1, 0xb2, 0x06, 0xe9, 0xd6, 0xa2, 0x73, 0xa5, 0xfa, 0x73, 0x32, 0x6f, 0x49, 0x1f, 0x27, 0xe0,
0x4a, 0xb8, 0x0f, 0x47, 0x6b, 0xb0, 0xd0, 0x53, 0x87, 0x8a, 0x3d, 0xe4, 0x66, 0x27, 0xd0, 0x8d,
0x87, 0x9e, 0x3a, 0x6c, 0x0e, 0x99, 0xcd, 0x89, 0x90, 0xb0, 0x87, 0x56, 0x39, 0xbe, 0x96, 0xb8,
0xb1, 0x20, 0x93, 0x4f, 0x74, 0x0c, 0x8b, 0x5d, 0xbd, 0xa5, 0x76, 0x95, 0xae, 0x6a, 0xd9, 0x0a,
0x0f, 0xee, 0xec, 0x10, 0x3d, 0x31, 0xb6, 0xd8, 0xcc, 0x1b, 0xe3, 0x36, 0xdb, 0x4f, 0xe2, 0x70,
0xb8, 0xfd, 0x97, 0xa8, 0x8e, 0x3d, 0xd5, 0xd9, 0x6a, 0xb4, 0x0d, 0xf9, 0x9e, 0x66, 0x9d, 0xe2,
0x8e, 0x7a, 0xa1, 0xe9, 0x26, 0x3f, 0x4d, 0xe3, 0x46, 0xf3, 0xc0, 0xe3, 0xe1, 0x9a, 0xfc, 0x62,
0xbe, 0x2d, 0x49, 0x05, 0x6c, 0xd8, 0xf1, 0x26, 0xe9, 0xb9, 0xbd, 0xc9, 0x0b, 0xb0, 0xdc, 0xc7,
0x43, 0x5b, 0xf1, 0xce, 0x2b, 0xb3, 0x93, 0x0c, 0x5d, 0x7a, 0x44, 0xfe, 0xb9, 0x27, 0xdc, 0x22,
0x26, 0x83, 0x9e, 0xa1, 0x51, 0xd0, 0xd0, 0x2d, 0x6c, 0x2a, 0x6a, 0xbb, 0x6d, 0x62, 0xcb, 0x2a,
0x67, 0x29, 0x77, 0xc9, 0xa1, 0x57, 0x19, 0x59, 0xfa, 0x8d, 0x7f, 0x6b, 0x82, 0x51, 0x8f, 0x2f,
0xbc, 0xe0, 0x2d, 0xfc, 0x11, 0x2c, 0x73, 0xf9, 0x76, 0x60, 0xed, 0x59, 0xf6, 0xf9, 0xd8, 0xf8,
0xf9, 0x1a, 0x5d, 0x73, 0xe4, 0x88, 0x47, 0x2f, 0x7b, 0xe2, 0xeb, 0x2d, 0x3b, 0x82, 0x24, 0x5d,
0x94, 0x24, 0x73, 0x31, 0xe4, 0xfb, 0xff, 0x6d, 0x2b, 0x5e, 0x83, 0xc5, 0xb1, 0x0c, 0xc2, 0x9d,
0x97, 0x10, 0x3a, 0xaf, 0xb8, 0x7f, 0x5e, 0xd2, 0xef, 0x05, 0xa8, 0x44, 0xa7, 0x0c, 0xa1, 0xaa,
0x9e, 0x85, 0x45, 0x77, 0x2e, 0xee, 0xf8, 0xd8, 0x99, 0x16, 0xdd, 0x1f, 0x7c, 0x80, 0x91, 0xee,
0xf9, 0x49, 0x28, 0x8e, 0x24, 0x34, 0x6c, 0x17, 0x0a, 0x17, 0xfe, 0xfe, 0xa5, 0x5f, 0x27, 0x5c,
0x9f, 0x19, 0xc8, 0x3a, 0x42, 0x0c, 0xed, 0x75, 0x58, 0x6a, 0xe3, 0x96, 0xd6, 0xfe, 0xba, 0x76,
0xb6, 0xc8, 0xa5, 0xbf, 0x37, 0xb3, 0x71, 0x33, 0xfb, 0x2d, 0x40, 0x56, 0xc6, 0x96, 0x41, 0x52,
0x09, 0xb4, 0x05, 0x39, 0x3c, 0x6c, 0x61, 0xc3, 0x76, 0xb2, 0xaf, 0xf0, 0xbc, 0x96, 0x71, 0xd7,
0x1d, 0x4e, 0x82, 0xea, 0x5c, 0x31, 0x74, 0x8b, 0x03, 0xd7, 0x68, 0x0c, 0xca, 0xc5, 0xfd, 0xc8,
0xf5, 0x45, 0x07, 0xb9, 0x26, 0x22, 0x41, 0x19, 0x93, 0x1a, 0x81, 0xae, 0xb7, 0x38, 0x74, 0x4d,
0x4e, 0xe9, 0x2c, 0x80, 0x5d, 0x6b, 0x01, 0xec, 0x9a, 0x9a, 0x32, 0xcd, 0x08, 0xf0, 0xfa, 0xa2,
0x03, 0x5e, 0xd3, 0x53, 0x46, 0x3c, 0x82, 0x5e, 0x5f, 0xf5, 0xa1, 0xd7, 0x2c, 0x15, 0x5d, 0x8b,
0x14, 0x0d, 0x81, 0xaf, 0x2f, 0xbb, 0xf0, 0x35, 0x1f, 0x09, 0x7d, 0xb9, 0xf0, 0x28, 0x7e, 0x3d,
0x18, 0xc3, 0xaf, 0x0c, 0x6f, 0x3e, 0x15, 0xa9, 0x62, 0x0a, 0x80, 0x3d, 0x18, 0x03, 0xb0, 0x85,
0x29, 0x0a, 0xa7, 0x20, 0xd8, 0x9f, 0x87, 0x23, 0xd8, 0x68, 0x8c, 0xc9, 0x87, 0x39, 0x1b, 0x84,
0x55, 0x22, 0x20, 0x6c, 0x29, 0x12, 0x6e, 0x31, 0xf5, 0x33, 0x63, 0xd8, 0xe3, 0x10, 0x0c, 0xcb,
0xd0, 0xe6, 0x8d, 0x48, 0xe5, 0x33, 0x80, 0xd8, 0xe3, 0x10, 0x10, 0xbb, 0x38, 0x55, 0xed, 0x54,
0x14, 0x7b, 0x2f, 0x88, 0x62, 0x51, 0x44, 0xc2, 0xe4, 0x9d, 0xf6, 0x08, 0x18, 0x7b, 0x1a, 0x05,
0x63, 0x19, 0xd4, 0x7c, 0x2e, 0x52, 0xe3, 0x1c, 0x38, 0xf6, 0x60, 0x0c, 0xc7, 0x2e, 0x4f, 0xb1,
0xb4, 0xd9, 0x81, 0x6c, 0x46, 0xcc, 0x32, 0x08, 0xbb, 0x9b, 0xcc, 0x82, 0x98, 0x97, 0x9e, 0x21,
0x71, 0x77, 0xc4, 0xc3, 0x91, 0x04, 0x17, 0x9b, 0xa6, 0x6e, 0x72, 0x48, 0xca, 0x1a, 0xd2, 0x0d,
0x02, 0x6c, 0x3c, 0x6f, 0x36, 0x01, 0xf4, 0x52, 0x20, 0xe1, 0xf3, 0x60, 0xd2, 0x9f, 0x05, 0x4f,
0x96, 0xc2, 0x5e, 0x3f, 0x28, 0xca, 0x71, 0x50, 0xe4, 0x83, 0xc2, 0xf1, 0x20, 0x14, 0x5e, 0x85,
0x3c, 0x01, 0x08, 0x23, 0x28, 0x57, 0x35, 0x5c, 0x94, 0x7b, 0x13, 0x16, 0x69, 0xa8, 0x64, 0x80,
0x99, 0x07, 0xa4, 0x24, 0x0d, 0x48, 0x25, 0xf2, 0x83, 0xad, 0x0b, 0x8b, 0x4c, 0xcf, 0xc3, 0x92,
0x8f, 0xd7, 0x05, 0x1e, 0x0c, 0xf2, 0x89, 0x2e, 0x77, 0x95, 0x23, 0x90, 0xbf, 0x09, 0xde, 0x0a,
0x79, 0xf0, 0x38, 0x0c, 0xc9, 0x0a, 0xdf, 0x12, 0x92, 0x8d, 0x7f, 0x6d, 0x24, 0xeb, 0x07, 0x52,
0x89, 0x20, 0x90, 0xfa, 0x8f, 0xe0, 0xed, 0x89, 0x8b, 0x4b, 0x5b, 0x7a, 0x1b, 0x73, 0x68, 0x43,
0xbf, 0x49, 0x32, 0xd2, 0xd5, 0xcf, 0x39, 0x80, 0x21, 0x9f, 0x84, 0xcb, 0x0d, 0x39, 0x39, 0x1e,
0x51, 0x5c, 0x54, 0xc4, 0x42, 0x3e, 0x47, 0x45, 0x22, 0x24, 0x1e, 0x62, 0x16, 0x20, 0x16, 0x64,
0xf2, 0x49, 0xf8, 0xa8, 0xd9, 0xf1, 0xd0, 0xcd, 0x1a, 0xe8, 0x0e, 0xe4, 0x68, 0xe5, 0x59, 0xd1,
0x0d, 0x8b, 0xc7, 0x84, 0x40, 0x52, 0xc3, 0xca, 0xcf, 0xeb, 0x87, 0x84, 0xe7, 0xc0, 0xb0, 0xe4,
0xac, 0xc1, 0xbf, 0x7c, 0xb9, 0x46, 0x2e, 0x90, 0x6b, 0x5c, 0x83, 0x1c, 0x19, 0xbd, 0x65, 0xa8,
0x2d, 0x4c, 0xeb, 0x9c, 0x39, 0xd9, 0x23, 0x48, 0x9f, 0x0a, 0x50, 0x1a, 0x09, 0x31, 0xa1, 0x73,
0x77, 0x4c, 0x32, 0xee, 0xc3, 0xe9, 0xd7, 0x01, 0xce, 0x55, 0x4b, 0x79, 0x4f, 0xed, 0xdb, 0xb8,
0xcd, 0xa7, 0x9b, 0x3b, 0x57, 0xad, 0x37, 0x28, 0x21, 0xd8, 0x71, 0x76, 0xa4, 0x63, 0x1f, 0x20,
0xcc, 0xf9, 0x01, 0x21, 0xaa, 0x40, 0xd6, 0x30, 0x35, 0xdd, 0xd4, 0xec, 0x4b, 0x3a, 0xda, 0x84,
0xec, 0xb6, 0x77, 0x93, 0xd9, 0x84, 0x98, 0xdc, 0x4d, 0x66, 0x93, 0x62, 0xca, 0xad, 0x3a, 0xb1,
0x23, 0x9b, 0x17, 0x17, 0xa4, 0x0f, 0xe2, 0x9e, 0x2d, 0x6e, 0xe3, 0xae, 0x76, 0x81, 0xcd, 0x39,
0x26, 0x33, 0xdb, 0xe6, 0xae, 0x84, 0x4c, 0xd9, 0x47, 0x21, 0xa3, 0x27, 0xad, 0x81, 0x85, 0xdb,
0xbc, 0xfe, 0xe1, 0xb6, 0x51, 0x03, 0xd2, 0xf8, 0x02, 0xf7, 0x6d, 0xab, 0x9c, 0xa1, 0x36, 0x7c,
0x65, 0x1c, 0x90, 0x92, 0xdf, 0x5b, 0x65, 0x62, 0xb9, 0x5f, 0x7d, 0xbe, 0x2a, 0x32, 0xee, 0xe7,
0xf4, 0x9e, 0x66, 0xe3, 0x9e, 0x61, 0x5f, 0xca, 0x5c, 0x7e, 0xf2, 0xca, 0x4a, 0x55, 0x28, 0x06,
0xe3, 0x3e, 0x7a, 0x02, 0x0a, 0x26, 0xb6, 0x55, 0xad, 0xaf, 0x04, 0x92, 0xf4, 0x05, 0x46, 0x64,
0x27, 0x7f, 0x37, 0x99, 0x15, 0xc4, 0xf8, 0x6e, 0x32, 0x1b, 0x17, 0x13, 0xd2, 0x21, 0x3c, 0x12,
0x1a, 0xf7, 0xd1, 0x4b, 0x90, 0xf3, 0x52, 0x06, 0x81, 0x4e, 0x63, 0x42, 0x11, 0xc3, 0xe3, 0x95,
0xfe, 0x2a, 0x78, 0x2a, 0x83, 0x65, 0x91, 0x3a, 0xa4, 0x4d, 0x6c, 0x0d, 0xba, 0xac, 0x50, 0x51,
0xdc, 0x7c, 0x7e, 0xb6, 0x8c, 0x81, 0x50, 0x07, 0x5d, 0x5b, 0xe6, 0xc2, 0xd2, 0x5b, 0x90, 0x66,
0x14, 0x94, 0x87, 0xcc, 0xf1, 0xfe, 0xfd, 0xfd, 0x83, 0x37, 0xf6, 0xc5, 0x18, 0x02, 0x48, 0x57,
0x6b, 0xb5, 0xfa, 0x61, 0x53, 0x14, 0x50, 0x0e, 0x52, 0xd5, 0xad, 0x03, 0xb9, 0x29, 0xc6, 0x09,
0x59, 0xae, 0xef, 0xd6, 0x6b, 0x4d, 0x31, 0x81, 0x16, 0xa1, 0xc0, 0xbe, 0x95, 0x7b, 0x07, 0xf2,
0x83, 0x6a, 0x53, 0x4c, 0xfa, 0x48, 0x47, 0xf5, 0xfd, 0xed, 0xba, 0x2c, 0xa6, 0xa4, 0x1f, 0xc0,
0xd5, 0xc8, 0x1c, 0xc3, 0xab, 0x79, 0x08, 0xbe, 0x9a, 0x87, 0xf4, 0x71, 0x9c, 0x80, 0xae, 0xa8,
0xc4, 0x01, 0xed, 0x8e, 0x4c, 0x7c, 0x73, 0x8e, 0xac, 0x63, 0x64, 0xf6, 0x04, 0x67, 0x99, 0xf8,
0x0c, 0xdb, 0xad, 0x0e, 0x4b, 0x64, 0x98, 0x9f, 0x2c, 0xc8, 0x05, 0x4e, 0xa5, 0x42, 0x16, 0x63,
0x7b, 0x07, 0xb7, 0x6c, 0x85, 0x9d, 0x36, 0x8b, 0x82, 0x9d, 0x1c, 0x61, 0x23, 0xd4, 0x23, 0x46,
0x94, 0xde, 0x9e, 0x6b, 0x2d, 0x73, 0x90, 0x92, 0xeb, 0x4d, 0xf9, 0x4d, 0x31, 0x81, 0x10, 0x14,
0xe9, 0xa7, 0x72, 0xb4, 0x5f, 0x3d, 0x3c, 0x6a, 0x1c, 0x90, 0xb5, 0x5c, 0x82, 0x92, 0xb3, 0x96,
0x0e, 0x31, 0x25, 0xfd, 0x23, 0x0e, 0x8f, 0x46, 0xa4, 0x3d, 0xe8, 0x0e, 0x80, 0x3d, 0x54, 0x4c,
0xdc, 0xd2, 0xcd, 0x76, 0xb4, 0x91, 0x35, 0x87, 0x32, 0xe5, 0x90, 0x73, 0x36, 0xff, 0xb2, 0x26,
0x94, 0xca, 0xd0, 0x2b, 0x5c, 0x29, 0x99, 0x95, 0xc5, 0x21, 0xde, 0xf5, 0x90, 0x8a, 0x10, 0x6e,
0x11, 0xc5, 0x74, 0x6d, 0xa9, 0x62, 0xca, 0x8f, 0x1e, 0xf8, 0xb1, 0xf0, 0x80, 0x06, 0x98, 0x99,
0x6b, 0xaa, 0x3e, 0xb4, 0xcc, 0x08, 0x16, 0x7a, 0x13, 0x1e, 0x1d, 0x89, 0x8f, 0xae, 0xd2, 0xd4,
0xac, 0x61, 0xf2, 0x91, 0x60, 0x98, 0xe4, 0xaa, 0xa5, 0x3f, 0x24, 0xfc, 0x0b, 0x1b, 0xcc, 0xf2,
0x0e, 0x20, 0x6d, 0xd9, 0xaa, 0x3d, 0xb0, 0xb8, 0xc1, 0xbd, 0x34, 0x6b, 0xca, 0xb8, 0xee, 0x7c,
0x1c, 0x51, 0x71, 0x99, 0xab, 0xf9, 0x7e, 0xbd, 0x2d, 0xe9, 0x36, 0x14, 0x83, 0x8b, 0x13, 0x7d,
0x64, 0x3c, 0x9f, 0x13, 0x97, 0xee, 0x02, 0x1a, 0x4f, 0xa6, 0x43, 0xaa, 0x25, 0x42, 0x58, 0xb5,
0xe4, 0x8f, 0x02, 0x3c, 0x36, 0x21, 0x71, 0x46, 0xaf, 0x8f, 0xec, 0xf3, 0xcb, 0xf3, 0xa4, 0xdd,
0xeb, 0x8c, 0x16, 0xdc, 0x69, 0xe9, 0x16, 0x2c, 0xf8, 0xe9, 0xb3, 0x4d, 0xf2, 0xab, 0xb8, 0xe7,
0xf3, 0x83, 0x65, 0x1d, 0x2f, 0x14, 0x0a, 0xdf, 0x30, 0x14, 0x06, 0xed, 0x2c, 0x3e, 0xa7, 0x9d,
0x1d, 0x85, 0xd9, 0x59, 0x62, 0xae, 0x0c, 0x73, 0x2e, 0x6b, 0x4b, 0x7e, 0x33, 0x6b, 0x0b, 0x1c,
0xb8, 0x54, 0x30, 0x85, 0x7d, 0x13, 0xc0, 0xab, 0x75, 0x91, 0x80, 0x64, 0xea, 0x83, 0x7e, 0x9b,
0x5a, 0x40, 0x4a, 0x66, 0x0d, 0x74, 0x1b, 0x52, 0xc4, 0x92, 0x9c, 0x75, 0x1a, 0x77, 0xaa, 0xc4,
0x12, 0x7c, 0xb5, 0x32, 0xc6, 0x2d, 0x69, 0x80, 0xc6, 0x4b, 0xe5, 0x11, 0x5d, 0xbc, 0x1a, 0xec,
0xe2, 0xf1, 0xc8, 0xa2, 0x7b, 0x78, 0x57, 0xef, 0x43, 0x8a, 0xee, 0x3c, 0x49, 0xbe, 0xe8, 0xfd,
0x0c, 0x87, 0x40, 0xe4, 0x1b, 0xfd, 0x02, 0x40, 0xb5, 0x6d, 0x53, 0x3b, 0x1d, 0x78, 0x1d, 0xac,
0x86, 0x5b, 0x4e, 0xd5, 0xe1, 0xdb, 0xba, 0xc6, 0x4d, 0x68, 0xd9, 0x13, 0xf5, 0x99, 0x91, 0x4f,
0xa1, 0xb4, 0x0f, 0xc5, 0xa0, 0xac, 0x93, 0xb4, 0xb3, 0x31, 0x04, 0x93, 0x76, 0x86, 0xc1, 0x78,
0xd2, 0xee, 0xa6, 0xfc, 0x09, 0x76, 0x09, 0x45, 0x1b, 0xd2, 0x7f, 0x05, 0x58, 0xf0, 0x1b, 0xde,
0xb7, 0x9c, 0x8a, 0x4e, 0xc9, 0xbe, 0xaf, 0x8e, 0x65, 0xa2, 0x99, 0x73, 0xd5, 0x3a, 0xfe, 0x2e,
0x13, 0xd1, 0x0f, 0x04, 0xc8, 0xba, 0x93, 0x0f, 0xde, 0x47, 0x05, 0x2e, 0xf0, 0xd8, 0xda, 0xc5,
0xfd, 0x97, 0x48, 0xec, 0xba, 0x2e, 0xe1, 0x5e, 0xd7, 0xdd, 0x75, 0x73, 0xa5, 0xa8, 0xea, 0x9e,
0x7f, 0xa5, 0xb9, 0x4d, 0x39, 0xa9, 0xe1, 0xef, 0xf8, 0x38, 0x48, 0x92, 0x80, 0x7e, 0x04, 0x69,
0xb5, 0xe5, 0xd6, 0x34, 0x8b, 0x21, 0xc5, 0x3e, 0x87, 0x75, 0xbd, 0x39, 0xac, 0x52, 0x4e, 0x99,
0x4b, 0xf0, 0x51, 0xc5, 0x9d, 0x51, 0x49, 0xaf, 0x11, 0xbd, 0x8c, 0x27, 0xe8, 0x11, 0x8b, 0x00,
0xc7, 0xfb, 0x0f, 0x0e, 0xb6, 0x77, 0xee, 0xed, 0xd4, 0xb7, 0x79, 0xb6, 0xb4, 0xbd, 0x5d, 0xdf,
0x16, 0xe3, 0x84, 0x4f, 0xae, 0x3f, 0x38, 0x38, 0xa9, 0x6f, 0x8b, 0x09, 0xe9, 0x2e, 0xe4, 0x5c,
0xaf, 0x42, 0x10, 0xbe, 0x53, 0x9f, 0x15, 0xf8, 0xd9, 0xe6, 0xd5, 0xf5, 0x65, 0x48, 0x19, 0xfa,
0x7b, 0xfc, 0xee, 0x2c, 0x21, 0xb3, 0x86, 0xd4, 0x86, 0xd2, 0x88, 0x4b, 0x42, 0x77, 0x21, 0x63,
0x0c, 0x4e, 0x15, 0xc7, 0x68, 0x47, 0xaa, 0xd8, 0x0e, 0x76, 0x1c, 0x9c, 0x76, 0xb5, 0xd6, 0x7d,
0x7c, 0xe9, 0x2c, 0x93, 0x31, 0x38, 0xbd, 0xcf, 0x6c, 0x9b, 0xf5, 0x12, 0xf7, 0xf7, 0x72, 0x01,
0x59, 0xe7, 0xa8, 0xa2, 0x1f, 0x43, 0xce, 0xf5, 0x76, 0xee, 0xdd, 0x77, 0xa4, 0x9b, 0xe4, 0xea,
0x3d, 0x11, 0x74, 0x13, 0x16, 0x2d, 0xed, 0xbc, 0xef, 0x94, 0xee, 0x59, 0xf5, 0x26, 0x4e, 0xcf,
0x4c, 0x89, 0xfd, 0xd8, 0x73, 0x0a, 0x0c, 0x24, 0xc8, 0x89, 0xa3, 0xbe, 0xe2, 0xbb, 0x1c, 0x40,
0x48, 0x30, 0x4e, 0x84, 0x05, 0xe3, 0x5f, 0xc5, 0x21, 0xef, 0xbb, 0x19, 0x40, 0x3f, 0xf4, 0x39,
0xae, 0x62, 0x48, 0x14, 0xf1, 0xf1, 0x7a, 0x97, 0xcb, 0xc1, 0x89, 0xc5, 0xe7, 0x9f, 0x58, 0xd4,
0xfd, 0x8b, 0x73, 0xd1, 0x90, 0x9c, 0xfb, 0xa2, 0xe1, 0x39, 0x40, 0xb6, 0x6e, 0xab, 0x5d, 0xe5,
0x42, 0xb7, 0xb5, 0xfe, 0xb9, 0xc2, 0x4c, 0x83, 0xb9, 0x19, 0x91, 0xfe, 0x39, 0xa1, 0x3f, 0x0e,
0xa9, 0x95, 0xfc, 0x52, 0x80, 0xac, 0x8b, 0xe8, 0xe6, 0xbd, 0x7a, 0xbe, 0x02, 0x69, 0x0e, 0x5a,
0xd8, 0xdd, 0x33, 0x6f, 0x85, 0xde, 0xa8, 0x54, 0x20, 0xdb, 0xc3, 0xb6, 0x4a, 0x7d, 0x26, 0x8b,
0x80, 0x6e, 0xfb, 0xe6, 0xcb, 0x90, 0xf7, 0x5d, 0xdb, 0x13, 0x37, 0xba, 0x5f, 0x7f, 0x43, 0x8c,
0x55, 0x32, 0x1f, 0x7e, 0xb2, 0x96, 0xd8, 0xc7, 0xef, 0x91, 0x13, 0x26, 0xd7, 0x6b, 0x8d, 0x7a,
0xed, 0xbe, 0x28, 0x54, 0xf2, 0x1f, 0x7e, 0xb2, 0x96, 0x91, 0x31, 0x2d, 0xa6, 0xdf, 0xbc, 0x0f,
0xa5, 0x91, 0x8d, 0x09, 0x1e, 0x68, 0x04, 0xc5, 0xed, 0xe3, 0xc3, 0xbd, 0x9d, 0x5a, 0xb5, 0x59,
0x57, 0x4e, 0x0e, 0x9a, 0x75, 0x51, 0x40, 0x8f, 0xc2, 0xd2, 0xde, 0xce, 0x4f, 0x1a, 0x4d, 0xa5,
0xb6, 0xb7, 0x53, 0xdf, 0x6f, 0x2a, 0xd5, 0x66, 0xb3, 0x5a, 0xbb, 0x2f, 0xc6, 0x37, 0xff, 0x94,
0x87, 0x52, 0x75, 0xab, 0xb6, 0x43, 0x60, 0x9b, 0xd6, 0x52, 0xa9, 0x7b, 0xa8, 0x41, 0x92, 0x96,
0x05, 0x27, 0x3e, 0xde, 0xab, 0x4c, 0xbe, 0x21, 0x41, 0xf7, 0x20, 0x45, 0x2b, 0x86, 0x68, 0xf2,
0x6b, 0xbe, 0xca, 0x94, 0x2b, 0x13, 0x32, 0x18, 0x7a, 0x9c, 0x26, 0x3e, 0xef, 0xab, 0x4c, 0xbe,
0x41, 0x41, 0x7b, 0x90, 0x71, 0x0a, 0x46, 0xd3, 0xde, 0xdc, 0x55, 0xa6, 0x5e, 0x6b, 0x90, 0xa9,
0xb1, 0xc2, 0xdb, 0xe4, 0x97, 0x7f, 0x95, 0x29, 0x77, 0x2b, 0x68, 0x07, 0xd2, 0xbc, 0xe8, 0x31,
0xe5, 0x31, 0x5f, 0x65, 0xda, 0x6d, 0x09, 0x92, 0x21, 0xe7, 0x95, 0x34, 0xa7, 0xbf, 0x67, 0xac,
0xcc, 0x70, 0x6d, 0x84, 0xde, 0x82, 0x42, 0xb0, 0xa0, 0x32, 0xdb, 0x83, 0xc1, 0xca, 0x8c, 0xf7,
0x32, 0x44, 0x7f, 0xb0, 0xba, 0x32, 0xdb, 0x03, 0xc2, 0xca, 0x8c, 0xd7, 0x34, 0xe8, 0x1d, 0x58,
0x1c, 0xaf, 0x7e, 0xcc, 0xfe, 0x9e, 0xb0, 0x32, 0xc7, 0xc5, 0x0d, 0xea, 0x01, 0x0a, 0xa9, 0x9a,
0xcc, 0xf1, 0xbc, 0xb0, 0x32, 0xcf, 0x3d, 0x0e, 0x6a, 0x43, 0x69, 0xb4, 0x12, 0x31, 0xeb, 0x73,
0xc3, 0xca, 0xcc, 0x77, 0x3a, 0xac, 0x97, 0x20, 0x2c, 0x9f, 0xf5, 0xf9, 0x61, 0x65, 0xe6, 0x2b,
0x1e, 0x74, 0x0c, 0xe0, 0x83, 0x95, 0x33, 0x3c, 0x47, 0xac, 0xcc, 0x72, 0xd9, 0x83, 0x0c, 0x58,
0x0a, 0xc3, 0x9b, 0xf3, 0xbc, 0x4e, 0xac, 0xcc, 0x75, 0x07, 0x44, 0xec, 0x39, 0x88, 0x1c, 0x67,
0x7b, 0xad, 0x58, 0x99, 0xf1, 0x32, 0x68, 0xab, 0xfe, 0xe9, 0x17, 0x2b, 0xc2, 0x67, 0x5f, 0xac,
0x08, 0xff, 0xfe, 0x62, 0x45, 0xf8, 0xe8, 0xcb, 0x95, 0xd8, 0x67, 0x5f, 0xae, 0xc4, 0xfe, 0xf9,
0xe5, 0x4a, 0xec, 0xa7, 0xcf, 0x9e, 0x6b, 0x76, 0x67, 0x70, 0xba, 0xde, 0xd2, 0x7b, 0x1b, 0xfe,
0x07, 0xde, 0x61, 0xcf, 0xca, 0x4f, 0xd3, 0x34, 0xa0, 0xde, 0xfa, 0x5f, 0x00, 0x00, 0x00, 0xff,
0xff, 0xdc, 0x85, 0x19, 0x4e, 0x76, 0x2e, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@@ -5510,10 +5509,10 @@ func (m *RequestPrepareProposal) MarshalToSizedBuffer(dAtA []byte) (int, error)
i--
dAtA[i] = 0x28
}
if len(m.ByzantineValidators) > 0 {
for iNdEx := len(m.ByzantineValidators) - 1; iNdEx >= 0; iNdEx-- {
if len(m.Misbehavior) > 0 {
for iNdEx := len(m.Misbehavior) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.ByzantineValidators[iNdEx].MarshalToSizedBuffer(dAtA[:i])
size, err := m.Misbehavior[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
@@ -5605,10 +5604,10 @@ func (m *RequestProcessProposal) MarshalToSizedBuffer(dAtA []byte) (int, error)
i--
dAtA[i] = 0x22
}
if len(m.ByzantineValidators) > 0 {
for iNdEx := len(m.ByzantineValidators) - 1; iNdEx >= 0; iNdEx-- {
if len(m.Misbehavior) > 0 {
for iNdEx := len(m.Misbehavior) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.ByzantineValidators[iNdEx].MarshalToSizedBuffer(dAtA[:i])
size, err := m.Misbehavior[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
@@ -5779,10 +5778,10 @@ func (m *RequestFinalizeBlock) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i--
dAtA[i] = 0x22
}
if len(m.ByzantineValidators) > 0 {
for iNdEx := len(m.ByzantineValidators) - 1; iNdEx >= 0; iNdEx-- {
if len(m.Misbehavior) > 0 {
for iNdEx := len(m.Misbehavior) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.ByzantineValidators[iNdEx].MarshalToSizedBuffer(dAtA[:i])
size, err := m.Misbehavior[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
@@ -8145,8 +8144,8 @@ func (m *RequestPrepareProposal) Size() (n int) {
}
l = m.LocalLastCommit.Size()
n += 1 + l + sovTypes(uint64(l))
if len(m.ByzantineValidators) > 0 {
for _, e := range m.ByzantineValidators {
if len(m.Misbehavior) > 0 {
for _, e := range m.Misbehavior {
l = e.Size()
n += 1 + l + sovTypes(uint64(l))
}
@@ -8181,8 +8180,8 @@ func (m *RequestProcessProposal) Size() (n int) {
}
l = m.ProposedLastCommit.Size()
n += 1 + l + sovTypes(uint64(l))
if len(m.ByzantineValidators) > 0 {
for _, e := range m.ByzantineValidators {
if len(m.Misbehavior) > 0 {
for _, e := range m.Misbehavior {
l = e.Size()
n += 1 + l + sovTypes(uint64(l))
}
@@ -8261,8 +8260,8 @@ func (m *RequestFinalizeBlock) Size() (n int) {
}
l = m.DecidedLastCommit.Size()
n += 1 + l + sovTypes(uint64(l))
if len(m.ByzantineValidators) > 0 {
for _, e := range m.ByzantineValidators {
if len(m.Misbehavior) > 0 {
for _, e := range m.Misbehavior {
l = e.Size()
n += 1 + l + sovTypes(uint64(l))
}
@@ -11139,7 +11138,7 @@ func (m *RequestPrepareProposal) Unmarshal(dAtA []byte) error {
iNdEx = postIndex
case 4:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ByzantineValidators", wireType)
return fmt.Errorf("proto: wrong wireType = %d for field Misbehavior", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
@@ -11166,8 +11165,8 @@ func (m *RequestPrepareProposal) Unmarshal(dAtA []byte) error {
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.ByzantineValidators = append(m.ByzantineValidators, Misbehavior{})
if err := m.ByzantineValidators[len(m.ByzantineValidators)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
m.Misbehavior = append(m.Misbehavior, Misbehavior{})
if err := m.Misbehavior[len(m.Misbehavior)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
@@ -11408,7 +11407,7 @@ func (m *RequestProcessProposal) Unmarshal(dAtA []byte) error {
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ByzantineValidators", wireType)
return fmt.Errorf("proto: wrong wireType = %d for field Misbehavior", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
@@ -11435,8 +11434,8 @@ func (m *RequestProcessProposal) Unmarshal(dAtA []byte) error {
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.ByzantineValidators = append(m.ByzantineValidators, Misbehavior{})
if err := m.ByzantineValidators[len(m.ByzantineValidators)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
m.Misbehavior = append(m.Misbehavior, Misbehavior{})
if err := m.Misbehavior[len(m.Misbehavior)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
@@ -11985,7 +11984,7 @@ func (m *RequestFinalizeBlock) Unmarshal(dAtA []byte) error {
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ByzantineValidators", wireType)
return fmt.Errorf("proto: wrong wireType = %d for field Misbehavior", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
@@ -12012,8 +12011,8 @@ func (m *RequestFinalizeBlock) Unmarshal(dAtA []byte) error {
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.ByzantineValidators = append(m.ByzantineValidators, Misbehavior{})
if err := m.ByzantineValidators[len(m.ByzantineValidators)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
m.Misbehavior = append(m.Misbehavior, Misbehavior{})
if err := m.Misbehavior[len(m.Misbehavior)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex

View File

@@ -659,8 +659,8 @@ type P2PConfig struct { //nolint: maligned
DialTimeout time.Duration `mapstructure:"dial-timeout"`
// Makes it possible to configure which queue backend the p2p
// layer uses. Options are: "fifo" and "priority",
// with the default being "priority".
// layer uses. Options are: "fifo" and "simple-priority", and "priority",
// with the default being "simple-priority".
QueueType string `mapstructure:"queue-type"`
}
@@ -685,7 +685,7 @@ func DefaultP2PConfig() *P2PConfig {
PexReactor: true,
HandshakeTimeout: 20 * time.Second,
DialTimeout: 3 * time.Second,
QueueType: "priority",
QueueType: "simple-priority",
}
}

View File

@@ -282,7 +282,9 @@ pprof-laddr = "{{ .RPC.PprofListenAddress }}"
#######################################################
[p2p]
# Select the p2p internal queue
# Select the p2p internal queue.
# Options are: "fifo" and "simple-priority", and "priority",
# with the default being "simple-priority".
queue-type = "{{ .P2P.QueueType }}"
# Address to listen for incoming connections

View File

@@ -208,6 +208,33 @@ Try running these commands:
-> key.hex: 646566
-> value: xyz
-> value.hex: 78797A
> prepare_proposal "preparedef"
-> code: OK
-> log: Succeeded. Tx: def action: ADDED
-> code: OK
-> log: Succeeded. Tx: preparedef action: REMOVED
> process_proposal "def"
-> code: OK
-> status: ACCEPT
> process_proposal "preparedef"
-> code: OK
-> status: REJECT
> prepare_proposal
> process_proposal
-> code: OK
-> status: ACCEPT
> finalize_block
-> code: OK
-> data.hex: 0x0600000000000000
> commit
-> code: OK
```
Note that if we do `finalize_block "abc" ...` it will store `(abc, abc)`, but if

View File

@@ -106,10 +106,10 @@ Next, use the `tendermint testnet` command to create four directories of config
Before you can start the network, you'll need peers identifiers (IPs are not enough and can change). We'll refer to them as ID1, ID2, ID3, ID4.
```sh
tendermint show_node_id --home ./mytestnet/node0
tendermint show_node_id --home ./mytestnet/node1
tendermint show_node_id --home ./mytestnet/node2
tendermint show_node_id --home ./mytestnet/node3
tendermint show-node-id --home ./mytestnet/node0
tendermint show-node-id --home ./mytestnet/node1
tendermint show-node-id --home ./mytestnet/node2
tendermint show-node-id --home ./mytestnet/node3
```
Finally, from each machine, run:

View File

@@ -103,9 +103,9 @@ Another example of a cryptocurrency application built on Tendermint is
to Tendermint, but is more opinionated about how the state is managed,
and requires that all application behaviour runs in potentially many
docker containers, modules it calls "chaincode". It uses an
implementation of [PBFT](http://pmg.csail.mit.edu/papers/osdi99.pdf).
implementation of [PBFT](http://pmg.csail.mit.edu/papers/osdi99.pdf)
from a team at IBM that is augmented to handle potentially non-deterministic
chaincode It is possible to implement this docker-based behaviour as a ABCI app
chaincode. It is possible to implement this docker-based behaviour as a ABCI app
in Tendermint, though extending Tendermint to handle non-determinism remains
for future work.

View File

@@ -3,7 +3,7 @@
## Changelog
- 2020-01-11: initialized
- 2021-02-11: Migrate RFC to tendermint repo (Originally [RFC 004](https://github.com/tendermint/spec/pull/254))
- 2022-02-11: Migrate RFC to tendermint repo (Originally [RFC 004](https://github.com/tendermint/spec/pull/254))
## Author(s)

View File

@@ -213,4 +213,4 @@ documentation](https://hub.cosmos.network/main/governance/submitting.html#sendin
If the application does not implement a way to update the consensus parameters
programatically, then the application itself must be updated to do so. More information on updating
the consensus parameters via ABCI can be found in the [FinalizeBlock documentation](https://github.com/tendermint/tendermint/blob/master/spec/abci++/abci++_methods_002_draft.md#finalizeblock).
the consensus parameters via ABCI can be found in the [FinalizeBlock documentation](../../../spec/abci++/abci%2B%2B_methods.md#finalizeblock).

6
go.mod
View File

@@ -17,7 +17,7 @@ require (
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/lib/pq v1.10.6
github.com/libp2p/go-buffer-pool v0.0.2
github.com/libp2p/go-buffer-pool v0.1.0
github.com/mroth/weightedrand v0.4.1
github.com/oasisprotocol/curve25519-voi v0.0.0-20210609091139-0a56a4bca00b
github.com/ory/dockertest v3.3.5+incompatible
@@ -32,7 +32,7 @@ require (
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e
golang.org/x/net v0.0.0-20220617184016-355a448f1bc9
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29
google.golang.org/grpc v1.47.0
google.golang.org/grpc v1.48.0
pgregory.net/rapid v0.4.7
)
@@ -240,6 +240,6 @@ require (
require (
github.com/creachadair/tomledit v0.0.22
github.com/prometheus/client_model v0.2.0
github.com/prometheus/common v0.35.0
github.com/prometheus/common v0.36.0
github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca
)

12
go.sum
View File

@@ -717,8 +717,8 @@ github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs=
github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs=
github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM=
github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8=
github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg=
github.com/lufeee/execinquery v1.0.0 h1:1XUTuLIVPDlFvUU3LXmmZwHDsolsxXnY67lzhpeqe0I=
github.com/lufeee/execinquery v1.0.0/go.mod h1:EC7DrEKView09ocscGHC+apXMIaorh4xqSxS/dy8SbM=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
@@ -938,8 +938,8 @@ github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB8
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.35.0 h1:Eyr+Pw2VymWejHqCugNaQXkAi6KayVNxaHeu6khmFBE=
github.com/prometheus/common v0.35.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
github.com/prometheus/common v0.36.0 h1:78hJTing+BLYLjhXE+Z2BubeEymH5Lr0/Mt8FKkxxYo=
github.com/prometheus/common v0.36.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
@@ -1815,8 +1815,8 @@ google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ5
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8=
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w=
google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=

View File

@@ -135,7 +135,7 @@ func (r *Reactor) OnStart(ctx context.Context) error {
if err != nil {
return err
}
r.chCreator = func(context.Context, *conn.ChannelDescriptor) (*p2p.Channel, error) { return blockSyncCh, nil }
r.chCreator = func(context.Context, *conn.ChannelDescriptor) (p2p.Channel, error) { return blockSyncCh, nil }
state, err := r.stateStore.Load()
if err != nil {
@@ -183,7 +183,7 @@ func (r *Reactor) OnStop() {
// respondToPeer loads a block and sends it to the requesting peer, if we have it.
// Otherwise, we'll respond saying we do not have it.
func (r *Reactor) respondToPeer(ctx context.Context, msg *bcproto.BlockRequest, peerID types.NodeID, blockSyncCh *p2p.Channel) error {
func (r *Reactor) respondToPeer(ctx context.Context, msg *bcproto.BlockRequest, peerID types.NodeID, blockSyncCh p2p.Channel) error {
block := r.store.LoadBlock(msg.Height)
if block == nil {
r.logger.Info("peer requesting a block we do not have", "peer", peerID, "height", msg.Height)
@@ -223,7 +223,7 @@ func (r *Reactor) respondToPeer(ctx context.Context, msg *bcproto.BlockRequest,
// handleMessage handles an Envelope sent from a peer on a specific p2p Channel.
// It will handle errors and any possible panics gracefully. A caller can handle
// any error returned by sending a PeerError on the respective channel.
func (r *Reactor) handleMessage(ctx context.Context, envelope *p2p.Envelope, blockSyncCh *p2p.Channel) (err error) {
func (r *Reactor) handleMessage(ctx context.Context, envelope *p2p.Envelope, blockSyncCh p2p.Channel) (err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("panic in processing message: %v", e)
@@ -298,7 +298,7 @@ func (r *Reactor) handleMessage(ctx context.Context, envelope *p2p.Envelope, blo
// message execution will result in a PeerError being sent on the BlockSyncChannel.
// When the reactor is stopped, we will catch the signal and close the p2p Channel
// gracefully.
func (r *Reactor) processBlockSyncCh(ctx context.Context, blockSyncCh *p2p.Channel) {
func (r *Reactor) processBlockSyncCh(ctx context.Context, blockSyncCh p2p.Channel) {
iter := blockSyncCh.Receive(ctx)
for iter.Next(ctx) {
envelope := iter.Envelope()
@@ -319,7 +319,7 @@ func (r *Reactor) processBlockSyncCh(ctx context.Context, blockSyncCh *p2p.Chann
}
// processPeerUpdate processes a PeerUpdate.
func (r *Reactor) processPeerUpdate(ctx context.Context, peerUpdate p2p.PeerUpdate, blockSyncCh *p2p.Channel) {
func (r *Reactor) processPeerUpdate(ctx context.Context, peerUpdate p2p.PeerUpdate, blockSyncCh p2p.Channel) {
r.logger.Debug("received peer update", "peer", peerUpdate.NodeID, "status", peerUpdate.Status)
// XXX: Pool#RedoRequest can sometimes give us an empty peer.
@@ -354,7 +354,7 @@ func (r *Reactor) processPeerUpdate(ctx context.Context, peerUpdate p2p.PeerUpda
// processPeerUpdates initiates a blocking process where we listen for and handle
// PeerUpdate messages. When the reactor is stopped, we will catch the signal and
// close the p2p PeerUpdatesCh gracefully.
func (r *Reactor) processPeerUpdates(ctx context.Context, peerUpdates *p2p.PeerUpdates, blockSyncCh *p2p.Channel) {
func (r *Reactor) processPeerUpdates(ctx context.Context, peerUpdates *p2p.PeerUpdates, blockSyncCh p2p.Channel) {
for {
select {
case <-ctx.Done():
@@ -396,7 +396,7 @@ func (r *Reactor) SwitchToBlockSync(ctx context.Context, state sm.State) error {
return nil
}
func (r *Reactor) requestRoutine(ctx context.Context, blockSyncCh *p2p.Channel) {
func (r *Reactor) requestRoutine(ctx context.Context, blockSyncCh p2p.Channel) {
statusUpdateTicker := time.NewTicker(statusUpdateIntervalSeconds * time.Second)
defer statusUpdateTicker.Stop()
@@ -438,7 +438,7 @@ func (r *Reactor) requestRoutine(ctx context.Context, blockSyncCh *p2p.Channel)
// do.
//
// NOTE: Don't sleep in the FOR_LOOP or otherwise slow it down!
func (r *Reactor) poolRoutine(ctx context.Context, stateSynced bool, blockSyncCh *p2p.Channel) {
func (r *Reactor) poolRoutine(ctx context.Context, stateSynced bool, blockSyncCh p2p.Channel) {
var (
trySyncTicker = time.NewTicker(trySyncIntervalMS * time.Millisecond)
switchToConsensusTicker = time.NewTicker(switchToConsensusIntervalSeconds * time.Second)

View File

@@ -37,7 +37,7 @@ type reactorTestSuite struct {
reactors map[types.NodeID]*Reactor
app map[types.NodeID]abciclient.Client
blockSyncChannels map[types.NodeID]*p2p.Channel
blockSyncChannels map[types.NodeID]p2p.Channel
peerChans map[types.NodeID]chan p2p.PeerUpdate
peerUpdates map[types.NodeID]*p2p.PeerUpdates
}
@@ -64,7 +64,7 @@ func setup(
nodes: make([]types.NodeID, 0, numNodes),
reactors: make(map[types.NodeID]*Reactor, numNodes),
app: make(map[types.NodeID]abciclient.Client, numNodes),
blockSyncChannels: make(map[types.NodeID]*p2p.Channel, numNodes),
blockSyncChannels: make(map[types.NodeID]p2p.Channel, numNodes),
peerChans: make(map[types.NodeID]chan p2p.PeerUpdate, numNodes),
peerUpdates: make(map[types.NodeID]*p2p.PeerUpdates, numNodes),
}
@@ -177,7 +177,7 @@ func (rts *reactorTestSuite) addNode(
rts.peerUpdates[nodeID] = p2p.NewPeerUpdates(rts.peerChans[nodeID], 1)
rts.network.Nodes[nodeID].PeerManager.Register(ctx, rts.peerUpdates[nodeID])
chCreator := func(ctx context.Context, chdesc *p2p.ChannelDescriptor) (*p2p.Channel, error) {
chCreator := func(ctx context.Context, chdesc *p2p.ChannelDescriptor) (p2p.Channel, error) {
return rts.blockSyncChannels[nodeID], nil
}

View File

@@ -107,7 +107,7 @@ func invalidDoPrevoteFunc(
round int32,
cs *State,
r *Reactor,
voteCh *p2p.Channel,
voteCh p2p.Channel,
pv types.PrivValidator,
) {
// routine to:

View File

@@ -165,10 +165,10 @@ func NewReactor(
}
type channelBundle struct {
state *p2p.Channel
data *p2p.Channel
vote *p2p.Channel
votSet *p2p.Channel
state p2p.Channel
data p2p.Channel
vote p2p.Channel
votSet p2p.Channel
}
// OnStart starts separate go routines for each p2p Channel and listens for
@@ -310,14 +310,14 @@ func (r *Reactor) GetPeerState(peerID types.NodeID) (*PeerState, bool) {
return ps, ok
}
func (r *Reactor) broadcastNewRoundStepMessage(ctx context.Context, rs *cstypes.RoundState, stateCh *p2p.Channel) error {
func (r *Reactor) broadcastNewRoundStepMessage(ctx context.Context, rs *cstypes.RoundState, stateCh p2p.Channel) error {
return stateCh.Send(ctx, p2p.Envelope{
Broadcast: true,
Message: makeRoundStepMessage(rs),
})
}
func (r *Reactor) broadcastNewValidBlockMessage(ctx context.Context, rs *cstypes.RoundState, stateCh *p2p.Channel) error {
func (r *Reactor) broadcastNewValidBlockMessage(ctx context.Context, rs *cstypes.RoundState, stateCh p2p.Channel) error {
psHeader := rs.ProposalBlockParts.Header()
return stateCh.Send(ctx, p2p.Envelope{
Broadcast: true,
@@ -331,7 +331,7 @@ func (r *Reactor) broadcastNewValidBlockMessage(ctx context.Context, rs *cstypes
})
}
func (r *Reactor) broadcastHasVoteMessage(ctx context.Context, vote *types.Vote, stateCh *p2p.Channel) error {
func (r *Reactor) broadcastHasVoteMessage(ctx context.Context, vote *types.Vote, stateCh p2p.Channel) error {
return stateCh.Send(ctx, p2p.Envelope{
Broadcast: true,
Message: &tmcons.HasVote{
@@ -346,7 +346,7 @@ func (r *Reactor) broadcastHasVoteMessage(ctx context.Context, vote *types.Vote,
// subscribeToBroadcastEvents subscribes for new round steps and votes using the
// internal pubsub defined in the consensus state to broadcast them to peers
// upon receiving.
func (r *Reactor) subscribeToBroadcastEvents(ctx context.Context, stateCh *p2p.Channel) {
func (r *Reactor) subscribeToBroadcastEvents(ctx context.Context, stateCh p2p.Channel) {
onStopCh := r.state.getOnStopCh()
err := r.state.evsw.AddListenerForEvent(
@@ -403,7 +403,7 @@ func makeRoundStepMessage(rs *cstypes.RoundState) *tmcons.NewRoundStep {
}
}
func (r *Reactor) sendNewRoundStepMessage(ctx context.Context, peerID types.NodeID, stateCh *p2p.Channel) error {
func (r *Reactor) sendNewRoundStepMessage(ctx context.Context, peerID types.NodeID, stateCh p2p.Channel) error {
return stateCh.Send(ctx, p2p.Envelope{
To: peerID,
Message: makeRoundStepMessage(r.getRoundState()),
@@ -433,7 +433,7 @@ func (r *Reactor) getRoundState() *cstypes.RoundState {
return r.rs
}
func (r *Reactor) gossipDataForCatchup(ctx context.Context, rs *cstypes.RoundState, prs *cstypes.PeerRoundState, ps *PeerState, dataCh *p2p.Channel) {
func (r *Reactor) gossipDataForCatchup(ctx context.Context, rs *cstypes.RoundState, prs *cstypes.PeerRoundState, ps *PeerState, dataCh p2p.Channel) {
logger := r.logger.With("height", prs.Height).With("peer", ps.peerID)
if index, ok := prs.ProposalBlockParts.Not().PickRandom(); ok {
@@ -497,7 +497,7 @@ func (r *Reactor) gossipDataForCatchup(ctx context.Context, rs *cstypes.RoundSta
time.Sleep(r.state.config.PeerGossipSleepDuration)
}
func (r *Reactor) gossipDataRoutine(ctx context.Context, ps *PeerState, dataCh *p2p.Channel) {
func (r *Reactor) gossipDataRoutine(ctx context.Context, ps *PeerState, dataCh p2p.Channel) {
logger := r.logger.With("peer", ps.peerID)
timer := time.NewTimer(0)
@@ -632,7 +632,7 @@ OUTER_LOOP:
// pickSendVote picks a vote and sends it to the peer. It will return true if
// there is a vote to send and false otherwise.
func (r *Reactor) pickSendVote(ctx context.Context, ps *PeerState, votes types.VoteSetReader, voteCh *p2p.Channel) (bool, error) {
func (r *Reactor) pickSendVote(ctx context.Context, ps *PeerState, votes types.VoteSetReader, voteCh p2p.Channel) (bool, error) {
vote, ok := ps.PickVoteToSend(votes)
if !ok {
return false, nil
@@ -660,7 +660,7 @@ func (r *Reactor) gossipVotesForHeight(
rs *cstypes.RoundState,
prs *cstypes.PeerRoundState,
ps *PeerState,
voteCh *p2p.Channel,
voteCh p2p.Channel,
) (bool, error) {
logger := r.logger.With("height", prs.Height).With("peer", ps.peerID)
@@ -732,7 +732,7 @@ func (r *Reactor) gossipVotesForHeight(
return false, nil
}
func (r *Reactor) gossipVotesRoutine(ctx context.Context, ps *PeerState, voteCh *p2p.Channel) {
func (r *Reactor) gossipVotesRoutine(ctx context.Context, ps *PeerState, voteCh p2p.Channel) {
logger := r.logger.With("peer", ps.peerID)
timer := time.NewTimer(0)
@@ -804,7 +804,7 @@ func (r *Reactor) gossipVotesRoutine(ctx context.Context, ps *PeerState, voteCh
// NOTE: `queryMaj23Routine` has a simple crude design since it only comes
// into play for liveness when there's a signature DDoS attack happening.
func (r *Reactor) queryMaj23Routine(ctx context.Context, ps *PeerState, stateCh *p2p.Channel) {
func (r *Reactor) queryMaj23Routine(ctx context.Context, ps *PeerState, stateCh p2p.Channel) {
timer := time.NewTimer(0)
defer timer.Stop()
@@ -1015,7 +1015,7 @@ func (r *Reactor) processPeerUpdate(ctx context.Context, peerUpdate p2p.PeerUpda
// If we fail to find the peer state for the envelope sender, we perform a no-op
// and return. This can happen when we process the envelope after the peer is
// removed.
func (r *Reactor) handleStateMessage(ctx context.Context, envelope *p2p.Envelope, msgI Message, voteSetCh *p2p.Channel) error {
func (r *Reactor) handleStateMessage(ctx context.Context, envelope *p2p.Envelope, msgI Message, voteSetCh p2p.Channel) error {
ps, ok := r.GetPeerState(envelope.From)
if !ok || ps == nil {
r.logger.Debug("failed to find peer state", "peer", envelope.From, "ch_id", "StateChannel")

View File

@@ -46,10 +46,10 @@ type reactorTestSuite struct {
reactors map[types.NodeID]*Reactor
subs map[types.NodeID]eventbus.Subscription
blocksyncSubs map[types.NodeID]eventbus.Subscription
stateChannels map[types.NodeID]*p2p.Channel
dataChannels map[types.NodeID]*p2p.Channel
voteChannels map[types.NodeID]*p2p.Channel
voteSetBitsChannels map[types.NodeID]*p2p.Channel
stateChannels map[types.NodeID]p2p.Channel
dataChannels map[types.NodeID]p2p.Channel
voteChannels map[types.NodeID]p2p.Channel
voteSetBitsChannels map[types.NodeID]p2p.Channel
}
func chDesc(chID p2p.ChannelID, size int) *p2p.ChannelDescriptor {
@@ -86,7 +86,7 @@ func setup(
t.Cleanup(cancel)
chCreator := func(nodeID types.NodeID) p2p.ChannelCreator {
return func(ctx context.Context, desc *p2p.ChannelDescriptor) (*p2p.Channel, error) {
return func(ctx context.Context, desc *p2p.ChannelDescriptor) (p2p.Channel, error) {
switch desc.ID {
case StateChannel:
return rts.stateChannels[nodeID], nil

View File

@@ -159,7 +159,7 @@ func (r *Reactor) handleMessage(ctx context.Context, envelope *p2p.Envelope) (er
// processEvidenceCh implements a blocking event loop where we listen for p2p
// Envelope messages from the evidenceCh.
func (r *Reactor) processEvidenceCh(ctx context.Context, evidenceCh *p2p.Channel) {
func (r *Reactor) processEvidenceCh(ctx context.Context, evidenceCh p2p.Channel) {
iter := evidenceCh.Receive(ctx)
for iter.Next(ctx) {
envelope := iter.Envelope()
@@ -186,7 +186,7 @@ func (r *Reactor) processEvidenceCh(ctx context.Context, evidenceCh *p2p.Channel
// connects/disconnects frequently from the broadcasting peer(s).
//
// REF: https://github.com/tendermint/tendermint/issues/4727
func (r *Reactor) processPeerUpdate(ctx context.Context, peerUpdate p2p.PeerUpdate, evidenceCh *p2p.Channel) {
func (r *Reactor) processPeerUpdate(ctx context.Context, peerUpdate p2p.PeerUpdate, evidenceCh p2p.Channel) {
r.logger.Debug("received peer update", "peer", peerUpdate.NodeID, "status", peerUpdate.Status)
r.mtx.Lock()
@@ -227,7 +227,7 @@ func (r *Reactor) processPeerUpdate(ctx context.Context, peerUpdate p2p.PeerUpda
// processPeerUpdates initiates a blocking process where we listen for and handle
// PeerUpdate messages. When the reactor is stopped, we will catch the signal and
// close the p2p PeerUpdatesCh gracefully.
func (r *Reactor) processPeerUpdates(ctx context.Context, peerUpdates *p2p.PeerUpdates, evidenceCh *p2p.Channel) {
func (r *Reactor) processPeerUpdates(ctx context.Context, peerUpdates *p2p.PeerUpdates, evidenceCh p2p.Channel) {
for {
select {
case peerUpdate := <-peerUpdates.Updates():
@@ -249,7 +249,7 @@ func (r *Reactor) processPeerUpdates(ctx context.Context, peerUpdates *p2p.PeerU
// that the peer has already received or may not be ready for.
//
// REF: https://github.com/tendermint/tendermint/issues/4727
func (r *Reactor) broadcastEvidenceLoop(ctx context.Context, peerID types.NodeID, evidenceCh *p2p.Channel) {
func (r *Reactor) broadcastEvidenceLoop(ctx context.Context, peerID types.NodeID, evidenceCh p2p.Channel) {
var next *clist.CElement
defer func() {

View File

@@ -38,7 +38,7 @@ type reactorTestSuite struct {
logger log.Logger
reactors map[types.NodeID]*evidence.Reactor
pools map[types.NodeID]*evidence.Pool
evidenceChannels map[types.NodeID]*p2p.Channel
evidenceChannels map[types.NodeID]p2p.Channel
peerUpdates map[types.NodeID]*p2p.PeerUpdates
peerChans map[types.NodeID]chan p2p.PeerUpdate
nodes []*p2ptest.Node
@@ -96,7 +96,7 @@ func setup(ctx context.Context, t *testing.T, stateStores []sm.Store) *reactorTe
rts.network.Nodes[nodeID].PeerManager.Register(ctx, pu)
rts.nodes = append(rts.nodes, rts.network.Nodes[nodeID])
chCreator := func(ctx context.Context, chdesc *p2p.ChannelDescriptor) (*p2p.Channel, error) {
chCreator := func(ctx context.Context, chdesc *p2p.ChannelDescriptor) (p2p.Channel, error) {
return rts.evidenceChannels[nodeID], nil
}

View File

@@ -540,7 +540,7 @@ func (txmp *TxMempool) initTxCallback(wtx *WrappedTx, res *abci.ResponseCheckTx,
)
if len(evictTxs) == 0 {
// No room for the new incoming transaction so we just remove it from
// the cache.
// the cache and return an error to the user.
txmp.cache.Remove(wtx.tx)
txmp.logger.Error(
"rejected incoming good transaction; mempool full",
@@ -548,7 +548,7 @@ func (txmp *TxMempool) initTxCallback(wtx *WrappedTx, res *abci.ResponseCheckTx,
"err", err.Error(),
)
txmp.metrics.RejectedTxs.Add(1)
return nil
return err
}
// evict an existing transaction(s)

View File

@@ -194,7 +194,7 @@ func (r *Reactor) handleMessage(ctx context.Context, envelope *p2p.Envelope) (er
// processMempoolCh implements a blocking event loop where we listen for p2p
// Envelope messages from the mempoolCh.
func (r *Reactor) processMempoolCh(ctx context.Context, mempoolCh *p2p.Channel) {
func (r *Reactor) processMempoolCh(ctx context.Context, mempoolCh p2p.Channel) {
iter := mempoolCh.Receive(ctx)
for iter.Next(ctx) {
envelope := iter.Envelope()
@@ -215,7 +215,7 @@ func (r *Reactor) processMempoolCh(ctx context.Context, mempoolCh *p2p.Channel)
// goroutine or not. If not, we start one for the newly added peer. For down or
// removed peers, we remove the peer from the mempool peer ID set and signal to
// stop the tx broadcasting goroutine.
func (r *Reactor) processPeerUpdate(ctx context.Context, peerUpdate p2p.PeerUpdate, mempoolCh *p2p.Channel) {
func (r *Reactor) processPeerUpdate(ctx context.Context, peerUpdate p2p.PeerUpdate, mempoolCh p2p.Channel) {
r.logger.Debug("received peer update", "peer", peerUpdate.NodeID, "status", peerUpdate.Status)
r.mtx.Lock()
@@ -264,7 +264,7 @@ func (r *Reactor) processPeerUpdate(ctx context.Context, peerUpdate p2p.PeerUpda
// processPeerUpdates initiates a blocking process where we listen for and handle
// PeerUpdate messages. When the reactor is stopped, we will catch the signal and
// close the p2p PeerUpdatesCh gracefully.
func (r *Reactor) processPeerUpdates(ctx context.Context, peerUpdates *p2p.PeerUpdates, mempoolCh *p2p.Channel) {
func (r *Reactor) processPeerUpdates(ctx context.Context, peerUpdates *p2p.PeerUpdates, mempoolCh p2p.Channel) {
for {
select {
case <-ctx.Done():
@@ -275,7 +275,7 @@ func (r *Reactor) processPeerUpdates(ctx context.Context, peerUpdates *p2p.PeerU
}
}
func (r *Reactor) broadcastTxRoutine(ctx context.Context, peerID types.NodeID, mempoolCh *p2p.Channel) {
func (r *Reactor) broadcastTxRoutine(ctx context.Context, peerID types.NodeID, mempoolCh p2p.Channel) {
peerMempoolID := r.ids.GetForPeer(peerID)
var nextGossipTx *clist.CElement

View File

@@ -30,7 +30,7 @@ type reactorTestSuite struct {
logger log.Logger
reactors map[types.NodeID]*Reactor
mempoolChannels map[types.NodeID]*p2p.Channel
mempoolChannels map[types.NodeID]p2p.Channel
mempools map[types.NodeID]*TxMempool
kvstores map[types.NodeID]*kvstore.Application
@@ -51,7 +51,7 @@ func setupReactors(ctx context.Context, t *testing.T, logger log.Logger, numNode
logger: log.NewNopLogger().With("testCase", t.Name()),
network: p2ptest.MakeNetwork(ctx, t, p2ptest.NetworkOptions{NumNodes: numNodes}),
reactors: make(map[types.NodeID]*Reactor, numNodes),
mempoolChannels: make(map[types.NodeID]*p2p.Channel, numNodes),
mempoolChannels: make(map[types.NodeID]p2p.Channel, numNodes),
mempools: make(map[types.NodeID]*TxMempool, numNodes),
kvstores: make(map[types.NodeID]*kvstore.Application, numNodes),
peerChans: make(map[types.NodeID]chan p2p.PeerUpdate, numNodes),
@@ -75,7 +75,7 @@ func setupReactors(ctx context.Context, t *testing.T, logger log.Logger, numNode
rts.peerUpdates[nodeID] = p2p.NewPeerUpdates(rts.peerChans[nodeID], 1)
rts.network.Nodes[nodeID].PeerManager.Register(ctx, rts.peerUpdates[nodeID])
chCreator := func(ctx context.Context, chDesc *p2p.ChannelDescriptor) (*p2p.Channel, error) {
chCreator := func(ctx context.Context, chDesc *p2p.ChannelDescriptor) (p2p.Channel, error) {
return rts.mempoolChannels[nodeID], nil
}

View File

@@ -2,6 +2,7 @@ package p2p
import (
"context"
"errors"
"fmt"
"sync"
@@ -19,6 +20,10 @@ type Envelope struct {
ChannelID ChannelID
}
func (e Envelope) IsZero() bool {
return e.From == "" && e.To == "" && e.Message == nil
}
// Wrapper is a Protobuf message that can contain a variety of inner messages
// (e.g. via oneof fields). If a Channel's message type implements Wrapper, the
// Router will automatically wrap outbound messages and unwrap inbound messages,
@@ -33,6 +38,16 @@ type Wrapper interface {
Unwrap() (proto.Message, error)
}
type Channel interface {
fmt.Stringer
Err() error
Send(context.Context, Envelope) error
SendError(context.Context, PeerError) error
Receive(context.Context) *ChannelIterator
}
// PeerError is a peer error reported via Channel.Error.
//
// FIXME: This currently just disconnects the peer, which is too simplistic.
@@ -52,9 +67,9 @@ type PeerError struct {
func (pe PeerError) Error() string { return fmt.Sprintf("peer=%q: %s", pe.NodeID, pe.Err.Error()) }
func (pe PeerError) Unwrap() error { return pe.Err }
// Channel is a bidirectional channel to exchange Protobuf messages with peers.
// legacyChannel is a bidirectional channel to exchange Protobuf messages with peers.
// Each message is wrapped in an Envelope to specify its sender and receiver.
type Channel struct {
type legacyChannel struct {
ID ChannelID
inCh <-chan Envelope // inbound messages (peers to reactors)
outCh chan<- Envelope // outbound messages (reactors to peers)
@@ -65,9 +80,10 @@ type Channel struct {
// NewChannel creates a new channel. It is primarily for internal and test
// use, reactors should use Router.OpenChannel().
func NewChannel(id ChannelID, inCh <-chan Envelope, outCh chan<- Envelope, errCh chan<- PeerError) *Channel {
return &Channel{
func NewChannel(id ChannelID, name string, inCh <-chan Envelope, outCh chan<- Envelope, errCh chan<- PeerError) Channel {
return &legacyChannel{
ID: id,
name: name,
inCh: inCh,
outCh: outCh,
errCh: errCh,
@@ -76,7 +92,7 @@ func NewChannel(id ChannelID, inCh <-chan Envelope, outCh chan<- Envelope, errCh
// Send blocks until the envelope has been sent, or until ctx ends.
// An error only occurs if the context ends before the send completes.
func (ch *Channel) Send(ctx context.Context, envelope Envelope) error {
func (ch *legacyChannel) Send(ctx context.Context, envelope Envelope) error {
select {
case <-ctx.Done():
return ctx.Err()
@@ -85,9 +101,15 @@ func (ch *Channel) Send(ctx context.Context, envelope Envelope) error {
}
}
func (ch *legacyChannel) Err() error { return nil }
// SendError blocks until the given error has been sent, or ctx ends.
// An error only occurs if the context ends before the send completes.
func (ch *Channel) SendError(ctx context.Context, pe PeerError) error {
func (ch *legacyChannel) SendError(ctx context.Context, pe PeerError) error {
if errors.Is(pe.Err, context.Canceled) || errors.Is(pe.Err, context.DeadlineExceeded) {
return nil
}
select {
case <-ctx.Done():
return ctx.Err()
@@ -96,18 +118,29 @@ func (ch *Channel) SendError(ctx context.Context, pe PeerError) error {
}
}
func (ch *Channel) String() string { return fmt.Sprintf("p2p.Channel<%d:%s>", ch.ID, ch.name) }
func (ch *legacyChannel) String() string { return fmt.Sprintf("p2p.Channel<%d:%s>", ch.ID, ch.name) }
// Receive returns a new unbuffered iterator to receive messages from ch.
// The iterator runs until ctx ends.
func (ch *Channel) Receive(ctx context.Context) *ChannelIterator {
func (ch *legacyChannel) Receive(ctx context.Context) *ChannelIterator {
iter := &ChannelIterator{
pipe: make(chan Envelope), // unbuffered
}
go func() {
go func(pipe chan<- Envelope) {
defer close(iter.pipe)
iteratorWorker(ctx, ch, iter.pipe)
}()
for {
select {
case <-ctx.Done():
return
case envelope := <-ch.inCh:
select {
case <-ctx.Done():
return
case pipe <- envelope:
}
}
}
}(iter.pipe)
return iter
}
@@ -122,21 +155,6 @@ type ChannelIterator struct {
current *Envelope
}
func iteratorWorker(ctx context.Context, ch *Channel, pipe chan Envelope) {
for {
select {
case <-ctx.Done():
return
case envelope := <-ch.inCh:
select {
case <-ctx.Done():
return
case pipe <- envelope:
}
}
}
}
// Next returns true when the Envelope value has advanced, and false
// when the context is canceled or iteration should stop. If an iterator has returned false,
// it will never return true again.
@@ -175,7 +193,7 @@ func (iter *ChannelIterator) Envelope() *Envelope { return iter.current }
//
// This allows the caller to consume messages from multiple channels
// without needing to manage the concurrency separately.
func MergedChannelIterator(ctx context.Context, chs ...*Channel) *ChannelIterator {
func MergedChannelIterator(ctx context.Context, chs ...Channel) *ChannelIterator {
iter := &ChannelIterator{
pipe: make(chan Envelope), // unbuffered
}
@@ -183,10 +201,17 @@ func MergedChannelIterator(ctx context.Context, chs ...*Channel) *ChannelIterato
for _, ch := range chs {
wg.Add(1)
go func(ch *Channel) {
go func(ch Channel, pipe chan<- Envelope) {
defer wg.Done()
iteratorWorker(ctx, ch, iter.pipe)
}(ch)
iter := ch.Receive(ctx)
for iter.Next(ctx) {
select {
case <-ctx.Done():
return
case pipe <- *iter.Envelope():
}
}
}(ch, iter.pipe)
}
done := make(chan struct{})

View File

@@ -16,13 +16,13 @@ type channelInternal struct {
Error chan PeerError
}
func testChannel(size int) (*channelInternal, *Channel) {
func testChannel(size int) (*channelInternal, *legacyChannel) {
in := &channelInternal{
In: make(chan Envelope, size),
Out: make(chan Envelope, size),
Error: make(chan PeerError, size),
}
ch := &Channel{
ch := &legacyChannel{
inCh: in.In,
outCh: in.Out,
errCh: in.Error,

View File

@@ -146,8 +146,8 @@ func (n *Network) MakeChannels(
ctx context.Context,
t *testing.T,
chDesc *p2p.ChannelDescriptor,
) map[types.NodeID]*p2p.Channel {
channels := map[types.NodeID]*p2p.Channel{}
) map[types.NodeID]p2p.Channel {
channels := map[types.NodeID]p2p.Channel{}
for _, node := range n.Nodes {
channels[node.NodeID] = node.MakeChannel(ctx, t, chDesc)
}
@@ -161,8 +161,8 @@ func (n *Network) MakeChannelsNoCleanup(
ctx context.Context,
t *testing.T,
chDesc *p2p.ChannelDescriptor,
) map[types.NodeID]*p2p.Channel {
channels := map[types.NodeID]*p2p.Channel{}
) map[types.NodeID]p2p.Channel {
channels := map[types.NodeID]p2p.Channel{}
for _, node := range n.Nodes {
channels[node.NodeID] = node.MakeChannelNoCleanup(ctx, t, chDesc)
}
@@ -304,7 +304,7 @@ func (n *Node) MakeChannel(
ctx context.Context,
t *testing.T,
chDesc *p2p.ChannelDescriptor,
) *p2p.Channel {
) p2p.Channel {
ctx, cancel := context.WithCancel(ctx)
channel, err := n.Router.OpenChannel(ctx, chDesc)
require.NoError(t, err)
@@ -321,7 +321,7 @@ func (n *Node) MakeChannelNoCleanup(
ctx context.Context,
t *testing.T,
chDesc *p2p.ChannelDescriptor,
) *p2p.Channel {
) p2p.Channel {
channel, err := n.Router.OpenChannel(ctx, chDesc)
require.NoError(t, err)
return channel

View File

@@ -15,7 +15,7 @@ import (
)
// RequireEmpty requires that the given channel is empty.
func RequireEmpty(ctx context.Context, t *testing.T, channels ...*p2p.Channel) {
func RequireEmpty(ctx context.Context, t *testing.T, channels ...p2p.Channel) {
t.Helper()
ctx, cancel := context.WithTimeout(ctx, 10*time.Millisecond)
@@ -32,7 +32,7 @@ func RequireEmpty(ctx context.Context, t *testing.T, channels ...*p2p.Channel) {
}
// RequireReceive requires that the given envelope is received on the channel.
func RequireReceive(ctx context.Context, t *testing.T, channel *p2p.Channel, expect p2p.Envelope) {
func RequireReceive(ctx context.Context, t *testing.T, channel p2p.Channel, expect p2p.Envelope) {
t.Helper()
ctx, cancel := context.WithTimeout(ctx, time.Second)
@@ -54,7 +54,7 @@ func RequireReceive(ctx context.Context, t *testing.T, channel *p2p.Channel, exp
// RequireReceiveUnordered requires that the given envelopes are all received on
// the channel, ignoring order.
func RequireReceiveUnordered(ctx context.Context, t *testing.T, channel *p2p.Channel, expect []*p2p.Envelope) {
func RequireReceiveUnordered(ctx context.Context, t *testing.T, channel p2p.Channel, expect []*p2p.Envelope) {
ctx, cancel := context.WithTimeout(ctx, time.Second)
defer cancel()
@@ -75,7 +75,7 @@ func RequireReceiveUnordered(ctx context.Context, t *testing.T, channel *p2p.Cha
}
// RequireSend requires that the given envelope is sent on the channel.
func RequireSend(ctx context.Context, t *testing.T, channel *p2p.Channel, envelope p2p.Envelope) {
func RequireSend(ctx context.Context, t *testing.T, channel p2p.Channel, envelope p2p.Envelope) {
tctx, cancel := context.WithTimeout(ctx, time.Second)
defer cancel()
@@ -93,7 +93,7 @@ func RequireSend(ctx context.Context, t *testing.T, channel *p2p.Channel, envelo
func RequireSendReceive(
ctx context.Context,
t *testing.T,
channel *p2p.Channel,
channel p2p.Channel,
peerID types.NodeID,
send proto.Message,
receive proto.Message,
@@ -116,7 +116,7 @@ func RequireNoUpdates(ctx context.Context, t *testing.T, peerUpdates *p2p.PeerUp
}
// RequireError requires that the given peer error is submitted for a peer.
func RequireError(ctx context.Context, t *testing.T, channel *p2p.Channel, peerError p2p.PeerError) {
func RequireError(ctx context.Context, t *testing.T, channel p2p.Channel, peerError p2p.PeerError) {
tctx, tcancel := context.WithTimeout(ctx, time.Second)
defer tcancel()

View File

@@ -954,7 +954,7 @@ func (m *PeerManager) Advertise(peerID types.NodeID, limit uint16) []NodeAddress
}
var numAddresses int
var totalScore int
var totalAbsScore int
ranked := m.store.Ranked()
seenAddresses := map[NodeAddress]struct{}{}
scores := map[types.NodeID]int{}
@@ -965,8 +965,12 @@ func (m *PeerManager) Advertise(peerID types.NodeID, limit uint16) []NodeAddress
continue
}
score := int(peer.Score())
if score < 0 {
totalAbsScore += -score
} else {
totalAbsScore += score
}
totalScore += score
scores[peer.ID] = score
for addr := range peer.AddressInfo {
if _, ok := m.options.PrivatePeers[addr.NodeID]; !ok {
@@ -975,6 +979,8 @@ func (m *PeerManager) Advertise(peerID types.NodeID, limit uint16) []NodeAddress
}
}
meanAbsScore := (totalAbsScore + 1) / (len(scores) + 1)
var attempts uint16
var addedLastIteration bool
@@ -1023,7 +1029,7 @@ func (m *PeerManager) Advertise(peerID types.NodeID, limit uint16) []NodeAddress
// peer.
// nolint:gosec // G404: Use of weak random number generator
if numAddresses <= int(limit) || rand.Intn(totalScore+1) <= scores[peer.ID]+1 || rand.Intn((idx+1)*10) <= idx+1 {
if numAddresses <= int(limit) || rand.Intn((meanAbsScore*2)+1) <= scores[peer.ID]+1 || rand.Intn((idx+1)*10) <= idx+1 {
addresses = append(addresses, addressInfo.Address)
addedLastIteration = true
seenAddresses[addressInfo.Address] = struct{}{}
@@ -1419,47 +1425,6 @@ func (s *peerStore) Ranked() []*peerInfo {
}
sort.Slice(s.ranked, func(i, j int) bool {
return s.ranked[i].Score() > s.ranked[j].Score()
// TODO: reevaluate more wholistic sorting, perhaps as follows:
// // sort inactive peers after active peers
// if s.ranked[i].Inactive && !s.ranked[j].Inactive {
// return false
// } else if !s.ranked[i].Inactive && s.ranked[j].Inactive {
// return true
// }
// iLastDialed, iLastDialSuccess := s.ranked[i].LastDialed()
// jLastDialed, jLastDialSuccess := s.ranked[j].LastDialed()
// // sort peers who our most recent dialing attempt was
// // successful ahead of peers with recent dialing
// // failures
// switch {
// case iLastDialSuccess && jLastDialSuccess:
// // if both peers were (are?) successfully
// // connected, convey their score, but give the
// // one we dialed successfully most recently a bonus
// iScore := s.ranked[i].Score()
// jScore := s.ranked[j].Score()
// if jLastDialed.Before(iLastDialed) {
// jScore++
// } else {
// iScore++
// }
// return iScore > jScore
// case iLastDialSuccess:
// return true
// case jLastDialSuccess:
// return false
// default:
// // if both peers were not successful in their
// // most recent dialing attempt, fall back to
// // peer score.
// return s.ranked[i].Score() > s.ranked[j].Score()
// }
})
return s.ranked
}

View File

@@ -145,7 +145,7 @@ func (r *Reactor) OnStop() {}
// processPexCh implements a blocking event loop where we listen for p2p
// Envelope messages from the pexCh.
func (r *Reactor) processPexCh(ctx context.Context, pexCh *p2p.Channel) {
func (r *Reactor) processPexCh(ctx context.Context, pexCh p2p.Channel) {
incoming := make(chan *p2p.Envelope)
go func() {
defer close(incoming)
@@ -192,8 +192,7 @@ func (r *Reactor) processPexCh(ctx context.Context, pexCh *p2p.Channel) {
// A request from another peer, or a response to one of our requests.
dur, err := r.handlePexMessage(ctx, envelope, pexCh)
if err != nil {
r.logger.Error("failed to process message",
"ch_id", envelope.ChannelID, "envelope", envelope, "err", err)
r.logger.Error("failed to process message", "ch_id", envelope.ChannelID, "envelope", envelope, "err", err)
if serr := pexCh.SendError(ctx, p2p.PeerError{
NodeID: envelope.From,
Err: err,
@@ -225,7 +224,7 @@ func (r *Reactor) processPeerUpdates(ctx context.Context, peerUpdates *p2p.PeerU
// handlePexMessage handles envelopes sent from peers on the PexChannel.
// If an update was received, a new polling interval is returned; otherwise the
// duration is 0.
func (r *Reactor) handlePexMessage(ctx context.Context, envelope *p2p.Envelope, pexCh *p2p.Channel) (time.Duration, error) {
func (r *Reactor) handlePexMessage(ctx context.Context, envelope *p2p.Envelope, pexCh p2p.Channel) (time.Duration, error) {
logger := r.logger.With("peer", envelope.From)
switch msg := envelope.Message.(type) {
@@ -308,7 +307,7 @@ func (r *Reactor) processPeerUpdate(peerUpdate p2p.PeerUpdate) {
// that peer a request for more peer addresses. The chosen peer is moved into
// the requestsSent bucket so that we will not attempt to contact them again
// until they've replied or updated.
func (r *Reactor) sendRequestForPeers(ctx context.Context, pexCh *p2p.Channel) error {
func (r *Reactor) sendRequestForPeers(ctx context.Context, pexCh p2p.Channel) error {
r.mtx.Lock()
defer r.mtx.Unlock()
if len(r.availablePeers) == 0 {

View File

@@ -275,7 +275,7 @@ type singleTestReactor struct {
pexInCh chan p2p.Envelope
pexOutCh chan p2p.Envelope
pexErrCh chan p2p.PeerError
pexCh *p2p.Channel
pexCh p2p.Channel
peerCh chan p2p.PeerUpdate
manager *p2p.PeerManager
}
@@ -287,8 +287,11 @@ func setupSingle(ctx context.Context, t *testing.T) *singleTestReactor {
pexInCh := make(chan p2p.Envelope, chBuf)
pexOutCh := make(chan p2p.Envelope, chBuf)
pexErrCh := make(chan p2p.PeerError, chBuf)
chDesc := pex.ChannelDescriptor()
pexCh := p2p.NewChannel(
p2p.ChannelID(pex.PexChannel),
chDesc.ID,
chDesc.Name,
pexInCh,
pexOutCh,
pexErrCh,
@@ -299,7 +302,7 @@ func setupSingle(ctx context.Context, t *testing.T) *singleTestReactor {
peerManager, err := p2p.NewPeerManager(nodeID, dbm.NewMemDB(), p2p.PeerManagerOptions{})
require.NoError(t, err)
chCreator := func(context.Context, *p2p.ChannelDescriptor) (*p2p.Channel, error) {
chCreator := func(context.Context, *p2p.ChannelDescriptor) (p2p.Channel, error) {
return pexCh, nil
}
@@ -324,7 +327,7 @@ type reactorTestSuite struct {
logger log.Logger
reactors map[types.NodeID]*pex.Reactor
pexChannels map[types.NodeID]*p2p.Channel
pexChannels map[types.NodeID]p2p.Channel
peerChans map[types.NodeID]chan p2p.PeerUpdate
peerUpdates map[types.NodeID]*p2p.PeerUpdates
@@ -367,7 +370,7 @@ func setupNetwork(ctx context.Context, t *testing.T, opts testOptions) *reactorT
logger: log.NewNopLogger().With("testCase", t.Name()),
network: p2ptest.MakeNetwork(ctx, t, networkOpts),
reactors: make(map[types.NodeID]*pex.Reactor, realNodes),
pexChannels: make(map[types.NodeID]*p2p.Channel, opts.TotalNodes),
pexChannels: make(map[types.NodeID]p2p.Channel, opts.TotalNodes),
peerChans: make(map[types.NodeID]chan p2p.PeerUpdate, opts.TotalNodes),
peerUpdates: make(map[types.NodeID]*p2p.PeerUpdates, opts.TotalNodes),
total: opts.TotalNodes,
@@ -388,7 +391,7 @@ func setupNetwork(ctx context.Context, t *testing.T, opts testOptions) *reactorT
rts.peerUpdates[nodeID] = p2p.NewPeerUpdates(rts.peerChans[nodeID], chBuf)
rts.network.Nodes[nodeID].PeerManager.Register(ctx, rts.peerUpdates[nodeID])
chCreator := func(context.Context, *p2p.ChannelDescriptor) (*p2p.Channel, error) {
chCreator := func(context.Context, *p2p.ChannelDescriptor) (p2p.Channel, error) {
return rts.pexChannels[nodeID], nil
}
@@ -448,7 +451,7 @@ func (r *reactorTestSuite) addNodes(ctx context.Context, t *testing.T, nodes int
r.peerUpdates[nodeID] = p2p.NewPeerUpdates(r.peerChans[nodeID], r.opts.BufferSize)
r.network.Nodes[nodeID].PeerManager.Register(ctx, r.peerUpdates[nodeID])
chCreator := func(context.Context, *p2p.ChannelDescriptor) (*p2p.Channel, error) {
chCreator := func(context.Context, *p2p.ChannelDescriptor) (p2p.Channel, error) {
return r.pexChannels[nodeID], nil
}

View File

@@ -31,8 +31,16 @@ func (pq priorityQueue) get(i int) *pqEnvelope { return pq[i] }
func (pq priorityQueue) Len() int { return len(pq) }
func (pq priorityQueue) Less(i, j int) bool {
// if both elements have the same priority, prioritize based on most recent
// if both elements have the same priority, prioritize based
// on most recent and largest
if pq[i].priority == pq[j].priority {
diff := pq[i].timestamp.Sub(pq[j].timestamp)
if diff < 0 {
diff *= -1
}
if diff < 10*time.Millisecond {
return pq[i].size > pq[j].size
}
return pq[i].timestamp.After(pq[j].timestamp)
}
@@ -272,12 +280,10 @@ func (s *pqScheduler) process(ctx context.Context) {
}
func (s *pqScheduler) push(pqEnv *pqEnvelope) {
chIDStr := strconv.Itoa(int(pqEnv.envelope.ChannelID))
// enqueue the incoming Envelope
heap.Push(s.pq, pqEnv)
s.size += pqEnv.size
s.metrics.PeerQueueMsgSize.With("ch_id", chIDStr).Add(float64(pqEnv.size))
s.metrics.PeerQueueMsgSize.With("ch_id", strconv.Itoa(int(pqEnv.envelope.ChannelID))).Add(float64(pqEnv.size))
// Update the cumulative sizes by adding the Envelope's size to every
// priority less than or equal to it.

View File

@@ -68,8 +68,9 @@ type RouterOptions struct {
}
const (
queueTypeFifo = "fifo"
queueTypePriority = "priority"
queueTypeFifo = "fifo"
queueTypePriority = "priority"
queueTypeSimplePriority = "simple-priority"
)
// Validate validates router options.
@@ -77,7 +78,7 @@ func (o *RouterOptions) Validate() error {
switch o.QueueType {
case "":
o.QueueType = queueTypeFifo
case queueTypeFifo, queueTypePriority:
case queueTypeFifo, queueTypePriority, queueTypeSimplePriority:
// pass
default:
return fmt.Errorf("queue type %q is not supported", o.QueueType)
@@ -227,6 +228,9 @@ func (r *Router) createQueueFactory(ctx context.Context) (func(int) queue, error
return q
}, nil
case queueTypeSimplePriority:
return func(size int) queue { return newSimplePriorityQueue(ctx, size, r.chDescs) }, nil
default:
return nil, fmt.Errorf("cannot construct queue of type %q", r.options.QueueType)
}
@@ -235,7 +239,7 @@ func (r *Router) createQueueFactory(ctx context.Context) (func(int) queue, error
// ChannelCreator allows routers to construct their own channels,
// either by receiving a reference to Router.OpenChannel or using some
// kind shim for testing purposes.
type ChannelCreator func(context.Context, *ChannelDescriptor) (*Channel, error)
type ChannelCreator func(context.Context, *ChannelDescriptor) (Channel, error)
// OpenChannel opens a new channel for the given message type. The caller must
// close the channel when done, before stopping the Router. messageType is the
@@ -243,7 +247,7 @@ type ChannelCreator func(context.Context, *ChannelDescriptor) (*Channel, error)
// implement Wrapper to automatically (un)wrap multiple message types in a
// wrapper message. The caller may provide a size to make the channel buffered,
// which internally makes the inbound, outbound, and error channel buffered.
func (r *Router) OpenChannel(ctx context.Context, chDesc *ChannelDescriptor) (*Channel, error) {
func (r *Router) OpenChannel(ctx context.Context, chDesc *ChannelDescriptor) (Channel, error) {
r.channelMtx.Lock()
defer r.channelMtx.Unlock()
@@ -258,11 +262,10 @@ func (r *Router) OpenChannel(ctx context.Context, chDesc *ChannelDescriptor) (*C
queue := r.queueFactory(chDesc.RecvBufferCapacity)
outCh := make(chan Envelope, chDesc.RecvBufferCapacity)
errCh := make(chan PeerError, chDesc.RecvBufferCapacity)
channel := NewChannel(id, queue.dequeue(), outCh, errCh)
channel.name = chDesc.Name
channel := NewChannel(chDesc.ID, chDesc.Name, queue.dequeue(), outCh, errCh)
var wrapper Wrapper
if w, ok := messageType.(Wrapper); ok {
if w, ok := chDesc.MessageType.(Wrapper); ok {
wrapper = w
}
@@ -283,7 +286,7 @@ func (r *Router) OpenChannel(ctx context.Context, chDesc *ChannelDescriptor) (*C
queue.close()
}()
r.routeChannel(ctx, id, outCh, errCh, wrapper)
r.routeChannel(ctx, chDesc.ID, outCh, errCh, wrapper)
}()
return channel, nil
@@ -304,6 +307,9 @@ func (r *Router) routeChannel(
for {
select {
case envelope := <-outCh:
if envelope.IsZero() {
continue
}
// Mark the envelope with the channel ID to allow sendPeer() to pass
// it on to Transport.SendMessage().
envelope.ChannelID = chID

View File

@@ -26,7 +26,7 @@ import (
"github.com/tendermint/tendermint/types"
)
func echoReactor(ctx context.Context, channel *p2p.Channel) {
func echoReactor(ctx context.Context, channel p2p.Channel) {
iter := channel.Receive(ctx)
for iter.Next(ctx) {
envelope := iter.Envelope()

112
internal/p2p/rqueue.go Normal file
View File

@@ -0,0 +1,112 @@
package p2p
import (
"container/heap"
"context"
"sort"
"time"
"github.com/gogo/protobuf/proto"
)
type simpleQueue struct {
input chan Envelope
output chan Envelope
closeFn func()
closeCh <-chan struct{}
maxSize int
chDescs []*ChannelDescriptor
}
func newSimplePriorityQueue(ctx context.Context, size int, chDescs []*ChannelDescriptor) *simpleQueue {
if size%2 != 0 {
size++
}
ctx, cancel := context.WithCancel(ctx)
q := &simpleQueue{
input: make(chan Envelope, size*2),
output: make(chan Envelope, size/2),
maxSize: size * size,
closeCh: ctx.Done(),
closeFn: cancel,
}
go q.run(ctx)
return q
}
func (q *simpleQueue) enqueue() chan<- Envelope { return q.input }
func (q *simpleQueue) dequeue() <-chan Envelope { return q.output }
func (q *simpleQueue) close() { q.closeFn() }
func (q *simpleQueue) closed() <-chan struct{} { return q.closeCh }
func (q *simpleQueue) run(ctx context.Context) {
defer q.closeFn()
var chPriorities = make(map[ChannelID]uint, len(q.chDescs))
for _, chDesc := range q.chDescs {
chID := chDesc.ID
chPriorities[chID] = uint(chDesc.Priority)
}
pq := make(priorityQueue, 0, q.maxSize)
heap.Init(&pq)
ticker := time.NewTicker(10 * time.Millisecond)
// must have a buffer of exactly one because both sides of
// this channel are used in this loop, and simply signals adds
// to the heap
signal := make(chan struct{}, 1)
for {
select {
case <-ctx.Done():
return
case <-q.closeCh:
return
case e := <-q.input:
// enqueue the incoming Envelope
heap.Push(&pq, &pqEnvelope{
envelope: e,
size: uint(proto.Size(e.Message)),
priority: chPriorities[e.ChannelID],
timestamp: time.Now().UTC(),
})
select {
case signal <- struct{}{}:
default:
if len(pq) > q.maxSize {
sort.Sort(pq)
pq = pq[:q.maxSize]
}
}
case <-ticker.C:
if len(pq) > q.maxSize {
sort.Sort(pq)
pq = pq[:q.maxSize]
}
if len(pq) > 0 {
select {
case signal <- struct{}{}:
default:
}
}
case <-signal:
SEND:
for len(pq) > 0 {
select {
case <-ctx.Done():
return
case <-q.closeCh:
return
case q.output <- heap.Pop(&pq).(*pqEnvelope).envelope:
continue SEND
default:
break SEND
}
}
}
}
}

View File

@@ -0,0 +1,47 @@
package p2p
import (
"context"
"testing"
"time"
)
func TestSimpleQueue(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// set up a small queue with very small buffers so we can
// watch it shed load, then send a bunch of messages to the
// queue, most of which we'll watch it drop.
sq := newSimplePriorityQueue(ctx, 1, nil)
for i := 0; i < 100; i++ {
sq.enqueue() <- Envelope{From: "merlin"}
}
seen := 0
RETRY:
for seen <= 2 {
select {
case e := <-sq.dequeue():
if e.From != "merlin" {
continue
}
seen++
case <-time.After(10 * time.Millisecond):
break RETRY
}
}
// if we don't see any messages, then it's just broken.
if seen == 0 {
t.Errorf("seen %d messages, should have seen more than one", seen)
}
// ensure that load shedding happens: there can be at most 3
// messages that we get out of this, one that was buffered
// plus 2 that were under the cap, everything else gets
// dropped.
if seen > 3 {
t.Errorf("saw %d messages, should have seen 5 or fewer", seen)
}
}

View File

@@ -105,14 +105,14 @@ func (blockExec *BlockExecutor) CreateProposalBlock(
rpp, err := blockExec.appClient.PrepareProposal(
ctx,
&abci.RequestPrepareProposal{
MaxTxBytes: maxDataBytes,
Txs: block.Txs.ToSliceOfBytes(),
LocalLastCommit: buildExtendedCommitInfo(lastExtCommit, blockExec.store, state.InitialHeight, state.ConsensusParams.ABCI),
ByzantineValidators: block.Evidence.ToABCI(),
Height: block.Height,
Time: block.Time,
NextValidatorsHash: block.NextValidatorsHash,
ProposerAddress: block.ProposerAddress,
MaxTxBytes: maxDataBytes,
Txs: block.Txs.ToSliceOfBytes(),
LocalLastCommit: buildExtendedCommitInfo(lastExtCommit, blockExec.store, state.InitialHeight, state.ConsensusParams.ABCI),
Misbehavior: block.Evidence.ToABCI(),
Height: block.Height,
Time: block.Time,
NextValidatorsHash: block.NextValidatorsHash,
ProposerAddress: block.ProposerAddress,
},
)
if err != nil {
@@ -147,14 +147,14 @@ func (blockExec *BlockExecutor) ProcessProposal(
state State,
) (bool, error) {
resp, err := blockExec.appClient.ProcessProposal(ctx, &abci.RequestProcessProposal{
Hash: block.Header.Hash(),
Height: block.Header.Height,
Time: block.Header.Time,
Txs: block.Data.Txs.ToSliceOfBytes(),
ProposedLastCommit: buildLastCommitInfo(block, blockExec.store, state.InitialHeight),
ByzantineValidators: block.Evidence.ToABCI(),
ProposerAddress: block.ProposerAddress,
NextValidatorsHash: block.NextValidatorsHash,
Hash: block.Header.Hash(),
Height: block.Header.Height,
Time: block.Header.Time,
Txs: block.Data.Txs.ToSliceOfBytes(),
ProposedLastCommit: buildLastCommitInfo(block, blockExec.store, state.InitialHeight),
Misbehavior: block.Evidence.ToABCI(),
ProposerAddress: block.ProposerAddress,
NextValidatorsHash: block.NextValidatorsHash,
})
if err != nil {
return false, ErrInvalidBlock(err)
@@ -208,14 +208,14 @@ func (blockExec *BlockExecutor) ApplyBlock(
fBlockRes, err := blockExec.appClient.FinalizeBlock(
ctx,
&abci.RequestFinalizeBlock{
Hash: block.Hash(),
Height: block.Header.Height,
Time: block.Header.Time,
Txs: block.Txs.ToSliceOfBytes(),
DecidedLastCommit: buildLastCommitInfo(block, blockExec.store, state.InitialHeight),
ByzantineValidators: block.Evidence.ToABCI(),
ProposerAddress: block.ProposerAddress,
NextValidatorsHash: block.NextValidatorsHash,
Hash: block.Hash(),
Height: block.Header.Height,
Time: block.Header.Time,
Txs: block.Txs.ToSliceOfBytes(),
DecidedLastCommit: buildLastCommitInfo(block, blockExec.store, state.InitialHeight),
Misbehavior: block.Evidence.ToABCI(),
ProposerAddress: block.ProposerAddress,
NextValidatorsHash: block.NextValidatorsHash,
},
)
endTime := time.Now().UnixNano()
@@ -677,12 +677,12 @@ func ExecCommitBlock(
finalizeBlockResponse, err := appConn.FinalizeBlock(
ctx,
&abci.RequestFinalizeBlock{
Hash: block.Hash(),
Height: block.Height,
Time: block.Time,
Txs: block.Txs.ToSliceOfBytes(),
DecidedLastCommit: buildLastCommitInfo(block, store, initialHeight),
ByzantineValidators: block.Evidence.ToABCI(),
Hash: block.Hash(),
Height: block.Height,
Time: block.Time,
Txs: block.Txs.ToSliceOfBytes(),
DecidedLastCommit: buildLastCommitInfo(block, store, initialHeight),
Misbehavior: block.Evidence.ToABCI(),
},
)

View File

@@ -158,8 +158,8 @@ func TestFinalizeBlockDecidedLastCommit(t *testing.T) {
}
}
// TestFinalizeBlockByzantineValidators ensures we send byzantine validators list.
func TestFinalizeBlockByzantineValidators(t *testing.T) {
// TestFinalizeBlockMisbehavior ensures we send misbehavior list.
func TestFinalizeBlockMisbehavior(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
@@ -274,7 +274,7 @@ func TestFinalizeBlockByzantineValidators(t *testing.T) {
require.NoError(t, err)
// TODO check state and mempool
assert.Equal(t, abciMb, app.ByzantineValidators)
assert.Equal(t, abciMb, app.Misbehavior)
}
func TestProcessProposal(t *testing.T) {
@@ -338,11 +338,11 @@ func TestProcessProposal(t *testing.T) {
block1.Txs = txs
expectedRpp := &abci.RequestProcessProposal{
Txs: block1.Txs.ToSliceOfBytes(),
Hash: block1.Hash(),
Height: block1.Header.Height,
Time: block1.Header.Time,
ByzantineValidators: block1.Evidence.ToABCI(),
Txs: block1.Txs.ToSliceOfBytes(),
Hash: block1.Hash(),
Height: block1.Header.Height,
Time: block1.Header.Time,
Misbehavior: block1.Evidence.ToABCI(),
ProposedLastCommit: abci.CommitInfo{
Round: 0,
Votes: voteInfos,

View File

@@ -266,9 +266,9 @@ func makeRandomStateFromConsensusParams(
type testApp struct {
abci.BaseApplication
CommitVotes []abci.VoteInfo
ByzantineValidators []abci.Misbehavior
ValidatorUpdates []abci.ValidatorUpdate
CommitVotes []abci.VoteInfo
Misbehavior []abci.Misbehavior
ValidatorUpdates []abci.ValidatorUpdate
}
var _ abci.Application = (*testApp)(nil)
@@ -279,7 +279,7 @@ func (app *testApp) Info(_ context.Context, req *abci.RequestInfo) (*abci.Respon
func (app *testApp) FinalizeBlock(_ context.Context, req *abci.RequestFinalizeBlock) (*abci.ResponseFinalizeBlock, error) {
app.CommitVotes = req.DecidedLastCommit.Votes
app.ByzantineValidators = req.ByzantineValidators
app.Misbehavior = req.Misbehavior
resTxs := make([]*abci.ExecTxResult, len(req.Txs))
for i, tx := range req.Txs {

View File

@@ -4,6 +4,7 @@ import (
"context"
"time"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/internal/eventbus"
"github.com/tendermint/tendermint/internal/pubsub"
"github.com/tendermint/tendermint/libs/log"
@@ -96,7 +97,14 @@ func (is *Service) publish(msg pubsub.Message) error {
if curr.Size() != 0 {
start := time.Now()
err := sink.IndexTxEvents(curr.Ops)
var err error
curr.Ops, err = DeduplicateBatch(curr.Ops, sink)
if err != nil {
is.logger.Error("failed to deduplicate batch", "height", is.currentBlock.height, "error", err)
}
err = sink.IndexTxEvents(curr.Ops)
if err != nil {
is.logger.Error("failed to index block txs",
"height", is.currentBlock.height, "err", err)
@@ -169,3 +177,45 @@ func IndexingEnabled(sinks []EventSink) bool {
return false
}
// DeduplicateBatch consider the case of duplicate txs.
// if the current one under investigation is NOT OK, then we need to check
// whether there's a previously indexed tx.
// SKIP the current tx if the previously indexed record is found and successful.
func DeduplicateBatch(ops []*abci.TxResult, sink EventSink) ([]*abci.TxResult, error) {
result := make([]*abci.TxResult, 0, len(ops))
// keep track of successful txs in this block in order to suppress latter ones being indexed.
var successfulTxsInThisBlock = make(map[string]struct{})
for _, txResult := range ops {
hash := types.Tx(txResult.Tx).Hash()
if txResult.Result.IsOK() {
successfulTxsInThisBlock[string(hash)] = struct{}{}
} else {
// if it already appeared in current block and was successful, skip.
if _, found := successfulTxsInThisBlock[string(hash)]; found {
continue
}
// check if this tx hash is already indexed
old, err := sink.GetTxByHash(hash)
// if db op errored
// Not found is not an error
if err != nil {
return nil, err
}
// if it's already indexed in an older block and was successful, skip.
if old != nil && old.Result.Code == abci.CodeTypeOK {
continue
}
}
result = append(result, txResult)
}
return result, nil
}

View File

@@ -109,6 +109,165 @@ func TestIndexerServiceIndexesBlocks(t *testing.T) {
assert.Nil(t, teardown(t, pool))
}
func TestTxIndexDuplicatedTx(t *testing.T) {
var mockTx = types.Tx("MOCK_TX_HASH")
testCases := []struct {
name string
tx1 abci.TxResult
tx2 abci.TxResult
expSkip bool // do we expect the second tx to be skipped by tx indexer
}{
{"skip, previously successful",
abci.TxResult{
Height: 1,
Index: 0,
Tx: mockTx,
Result: abci.ExecTxResult{
Code: abci.CodeTypeOK,
},
},
abci.TxResult{
Height: 2,
Index: 0,
Tx: mockTx,
Result: abci.ExecTxResult{
Code: abci.CodeTypeOK + 1,
},
},
true,
},
{"not skip, previously unsuccessful",
abci.TxResult{
Height: 1,
Index: 0,
Tx: mockTx,
Result: abci.ExecTxResult{
Code: abci.CodeTypeOK + 1,
},
},
abci.TxResult{
Height: 2,
Index: 0,
Tx: mockTx,
Result: abci.ExecTxResult{
Code: abci.CodeTypeOK + 1,
},
},
false,
},
{"not skip, both successful",
abci.TxResult{
Height: 1,
Index: 0,
Tx: mockTx,
Result: abci.ExecTxResult{
Code: abci.CodeTypeOK,
},
},
abci.TxResult{
Height: 2,
Index: 0,
Tx: mockTx,
Result: abci.ExecTxResult{
Code: abci.CodeTypeOK,
},
},
false,
},
{"not skip, both unsuccessful",
abci.TxResult{
Height: 1,
Index: 0,
Tx: mockTx,
Result: abci.ExecTxResult{
Code: abci.CodeTypeOK + 1,
},
},
abci.TxResult{
Height: 2,
Index: 0,
Tx: mockTx,
Result: abci.ExecTxResult{
Code: abci.CodeTypeOK + 1,
},
},
false,
},
{"skip, same block, previously successful",
abci.TxResult{
Height: 1,
Index: 0,
Tx: mockTx,
Result: abci.ExecTxResult{
Code: abci.CodeTypeOK,
},
},
abci.TxResult{
Height: 1,
Index: 0,
Tx: mockTx,
Result: abci.ExecTxResult{
Code: abci.CodeTypeOK + 1,
},
},
true,
},
{"not skip, same block, previously unsuccessful",
abci.TxResult{
Height: 1,
Index: 0,
Tx: mockTx,
Result: abci.ExecTxResult{
Code: abci.CodeTypeOK + 1,
},
},
abci.TxResult{
Height: 1,
Index: 0,
Tx: mockTx,
Result: abci.ExecTxResult{
Code: abci.CodeTypeOK,
},
},
false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
sink := kv.NewEventSink(dbm.NewMemDB())
if tc.tx1.Height != tc.tx2.Height {
// index the first tx
err := sink.IndexTxEvents([]*abci.TxResult{&tc.tx1})
require.NoError(t, err)
// check if the second one should be skipped.
ops, err := indexer.DeduplicateBatch([]*abci.TxResult{&tc.tx2}, sink)
require.NoError(t, err)
if tc.expSkip {
require.Empty(t, ops)
} else {
require.Equal(t, []*abci.TxResult{&tc.tx2}, ops)
}
} else {
// same block
ops := []*abci.TxResult{&tc.tx1, &tc.tx2}
ops, err := indexer.DeduplicateBatch(ops, sink)
require.NoError(t, err)
if tc.expSkip {
// the second one is skipped
require.Equal(t, []*abci.TxResult{&tc.tx1}, ops)
} else {
require.Equal(t, []*abci.TxResult{&tc.tx1, &tc.tx2}, ops)
}
}
})
}
}
func readSchema() ([]*schema.Migration, error) {
filename := "./sink/psql/schema.sql"
contents, err := os.ReadFile(filename)

View File

@@ -26,14 +26,14 @@ var (
// NOTE: It is not the responsibility of the dispatcher to verify the light blocks.
type Dispatcher struct {
// the channel with which to send light block requests on
requestCh *p2p.Channel
requestCh p2p.Channel
mtx sync.Mutex
// all pending calls that have been dispatched and are awaiting an answer
calls map[types.NodeID]chan *types.LightBlock
}
func NewDispatcher(requestChannel *p2p.Channel) *Dispatcher {
func NewDispatcher(requestChannel p2p.Channel) *Dispatcher {
return &Dispatcher{
requestCh: requestChannel,
calls: make(map[types.NodeID]chan *types.LightBlock),

View File

@@ -24,13 +24,13 @@ type channelInternal struct {
Error chan p2p.PeerError
}
func testChannel(size int) (*channelInternal, *p2p.Channel) {
func testChannel(size int) (*channelInternal, p2p.Channel) {
in := &channelInternal{
In: make(chan p2p.Envelope, size),
Out: make(chan p2p.Envelope, size),
Error: make(chan p2p.PeerError, size),
}
return in, p2p.NewChannel(0, in.In, in.Out, in.Error)
return in, p2p.NewChannel(0, "test", in.In, in.Out, in.Error)
}
func TestDispatcherBasic(t *testing.T) {

View File

@@ -305,7 +305,7 @@ func (r *Reactor) OnStart(ctx context.Context) error {
return nil
}
go r.processChannels(ctx, map[p2p.ChannelID]*p2p.Channel{
go r.processChannels(ctx, map[p2p.ChannelID]p2p.Channel{
SnapshotChannel: snapshotCh,
ChunkChannel: chunkCh,
LightBlockChannel: blockCh,
@@ -611,7 +611,7 @@ func (r *Reactor) backfill(
// handleSnapshotMessage handles envelopes sent from peers on the
// SnapshotChannel. It returns an error only if the Envelope.Message is unknown
// for this channel. This should never be called outside of handleMessage.
func (r *Reactor) handleSnapshotMessage(ctx context.Context, envelope *p2p.Envelope, snapshotCh *p2p.Channel) error {
func (r *Reactor) handleSnapshotMessage(ctx context.Context, envelope *p2p.Envelope, snapshotCh p2p.Channel) error {
logger := r.logger.With("peer", envelope.From)
switch msg := envelope.Message.(type) {
@@ -683,7 +683,7 @@ func (r *Reactor) handleSnapshotMessage(ctx context.Context, envelope *p2p.Envel
// handleChunkMessage handles envelopes sent from peers on the ChunkChannel.
// It returns an error only if the Envelope.Message is unknown for this channel.
// This should never be called outside of handleMessage.
func (r *Reactor) handleChunkMessage(ctx context.Context, envelope *p2p.Envelope, chunkCh *p2p.Channel) error {
func (r *Reactor) handleChunkMessage(ctx context.Context, envelope *p2p.Envelope, chunkCh p2p.Channel) error {
switch msg := envelope.Message.(type) {
case *ssproto.ChunkRequest:
r.logger.Debug(
@@ -772,7 +772,7 @@ func (r *Reactor) handleChunkMessage(ctx context.Context, envelope *p2p.Envelope
return nil
}
func (r *Reactor) handleLightBlockMessage(ctx context.Context, envelope *p2p.Envelope, blockCh *p2p.Channel) error {
func (r *Reactor) handleLightBlockMessage(ctx context.Context, envelope *p2p.Envelope, blockCh p2p.Channel) error {
switch msg := envelope.Message.(type) {
case *ssproto.LightBlockRequest:
r.logger.Info("received light block request", "height", msg.Height)
@@ -829,7 +829,7 @@ func (r *Reactor) handleLightBlockMessage(ctx context.Context, envelope *p2p.Env
return nil
}
func (r *Reactor) handleParamsMessage(ctx context.Context, envelope *p2p.Envelope, paramsCh *p2p.Channel) error {
func (r *Reactor) handleParamsMessage(ctx context.Context, envelope *p2p.Envelope, paramsCh p2p.Channel) error {
switch msg := envelope.Message.(type) {
case *ssproto.ParamsRequest:
r.logger.Debug("received consensus params request", "height", msg.Height)
@@ -878,7 +878,7 @@ func (r *Reactor) handleParamsMessage(ctx context.Context, envelope *p2p.Envelop
// handleMessage handles an Envelope sent from a peer on a specific p2p Channel.
// It will handle errors and any possible panics gracefully. A caller can handle
// any error returned by sending a PeerError on the respective channel.
func (r *Reactor) handleMessage(ctx context.Context, envelope *p2p.Envelope, chans map[p2p.ChannelID]*p2p.Channel) (err error) {
func (r *Reactor) handleMessage(ctx context.Context, envelope *p2p.Envelope, chans map[p2p.ChannelID]p2p.Channel) (err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("panic in processing message: %v", e)
@@ -912,12 +912,12 @@ func (r *Reactor) handleMessage(ctx context.Context, envelope *p2p.Envelope, cha
// encountered during message execution will result in a PeerError being sent on
// the respective channel. When the reactor is stopped, we will catch the signal
// and close the p2p Channel gracefully.
func (r *Reactor) processChannels(ctx context.Context, chanTable map[p2p.ChannelID]*p2p.Channel) {
// make sure that the iterator gets cleaned up in case of error
func (r *Reactor) processChannels(ctx context.Context, chanTable map[p2p.ChannelID]p2p.Channel) {
// make sure tht the iterator gets cleaned up in case of error
ctx, cancel := context.WithCancel(ctx)
defer cancel()
chs := make([]*p2p.Channel, 0, len(chanTable))
chs := make([]p2p.Channel, 0, len(chanTable))
for key := range chanTable {
chs = append(chs, chanTable[key])
}

View File

@@ -40,22 +40,22 @@ type reactorTestSuite struct {
conn *clientmocks.Client
stateProvider *mocks.StateProvider
snapshotChannel *p2p.Channel
snapshotChannel p2p.Channel
snapshotInCh chan p2p.Envelope
snapshotOutCh chan p2p.Envelope
snapshotPeerErrCh chan p2p.PeerError
chunkChannel *p2p.Channel
chunkChannel p2p.Channel
chunkInCh chan p2p.Envelope
chunkOutCh chan p2p.Envelope
chunkPeerErrCh chan p2p.PeerError
blockChannel *p2p.Channel
blockChannel p2p.Channel
blockInCh chan p2p.Envelope
blockOutCh chan p2p.Envelope
blockPeerErrCh chan p2p.PeerError
paramsChannel *p2p.Channel
paramsChannel p2p.Channel
paramsInCh chan p2p.Envelope
paramsOutCh chan p2p.Envelope
paramsPeerErrCh chan p2p.PeerError
@@ -102,6 +102,7 @@ func setup(
rts.snapshotChannel = p2p.NewChannel(
SnapshotChannel,
"snapshot",
rts.snapshotInCh,
rts.snapshotOutCh,
rts.snapshotPeerErrCh,
@@ -109,6 +110,7 @@ func setup(
rts.chunkChannel = p2p.NewChannel(
ChunkChannel,
"chunk",
rts.chunkInCh,
rts.chunkOutCh,
rts.chunkPeerErrCh,
@@ -116,6 +118,7 @@ func setup(
rts.blockChannel = p2p.NewChannel(
LightBlockChannel,
"lightblock",
rts.blockInCh,
rts.blockOutCh,
rts.blockPeerErrCh,
@@ -123,6 +126,7 @@ func setup(
rts.paramsChannel = p2p.NewChannel(
ParamsChannel,
"params",
rts.paramsInCh,
rts.paramsOutCh,
rts.paramsPeerErrCh,
@@ -133,7 +137,7 @@ func setup(
cfg := config.DefaultStateSyncConfig()
chCreator := func(ctx context.Context, desc *p2p.ChannelDescriptor) (*p2p.Channel, error) {
chCreator := func(ctx context.Context, desc *p2p.ChannelDescriptor) (p2p.Channel, error) {
switch desc.ID {
case SnapshotChannel:
return rts.snapshotChannel, nil

View File

@@ -208,7 +208,7 @@ type stateProviderP2P struct {
sync.Mutex // light.Client is not concurrency-safe
lc *light.Client
initialHeight int64
paramsSendCh *p2p.Channel
paramsSendCh p2p.Channel
paramsRecvCh chan types.ConsensusParams
}
@@ -220,7 +220,7 @@ func NewP2PStateProvider(
initialHeight int64,
providers []lightprovider.Provider,
trustOptions light.TrustOptions,
paramsSendCh *p2p.Channel,
paramsSendCh p2p.Channel,
logger log.Logger,
) (StateProvider, error) {
if len(providers) < 2 {

View File

@@ -56,8 +56,8 @@ type syncer struct {
stateProvider StateProvider
conn abciclient.Client
snapshots *snapshotPool
snapshotCh *p2p.Channel
chunkCh *p2p.Channel
snapshotCh p2p.Channel
chunkCh p2p.Channel
tempDir string
fetchers int32
retryTimeout time.Duration

View File

@@ -109,7 +109,7 @@ message RequestPrepareProposal {
// sent to the app for possible modifications.
repeated bytes txs = 2;
ExtendedCommitInfo local_last_commit = 3 [(gogoproto.nullable) = false];
repeated Misbehavior byzantine_validators = 4 [(gogoproto.nullable) = false];
repeated Misbehavior misbehavior = 4 [(gogoproto.nullable) = false];
int64 height = 5;
google.protobuf.Timestamp time = 6 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
bytes next_validators_hash = 7;
@@ -120,7 +120,7 @@ message RequestPrepareProposal {
message RequestProcessProposal {
repeated bytes txs = 1;
CommitInfo proposed_last_commit = 2 [(gogoproto.nullable) = false];
repeated Misbehavior byzantine_validators = 3 [(gogoproto.nullable) = false];
repeated Misbehavior misbehavior = 3 [(gogoproto.nullable) = false];
// hash is the merkle root hash of the fields of the proposed block.
bytes hash = 4;
int64 height = 5;
@@ -147,8 +147,8 @@ message RequestVerifyVoteExtension {
message RequestFinalizeBlock {
repeated bytes txs = 1;
CommitInfo decided_last_commit = 2 [(gogoproto.nullable) = false];
repeated Misbehavior byzantine_validators = 3 [(gogoproto.nullable) = false];
// hash is the merkle root hash of the fields of the proposed block.
repeated Misbehavior misbehavior = 3 [(gogoproto.nullable) = false];
// hash is the merkle root hash of the fields of the decided block.
bytes hash = 4;
int64 height = 5;
google.protobuf.Timestamp time = 6 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
@@ -248,7 +248,7 @@ message ResponseDeliverTx {
}
message ResponseCommit {
// reserve 1
reserved 1, 2;
int64 retain_height = 3;
}

View File

@@ -25,14 +25,14 @@ This allows Tendermint to run with applications written in many programming lang
This specification is split as follows:
- [Overview and basic concepts](./abci++_basic_concepts_002_draft.md) - interface's overview and concepts
- [Overview and basic concepts](./abci++_basic_concepts.md) - interface's overview and concepts
needed to understand other parts of this specification.
- [Methods](./abci++_methods_002_draft.md) - complete details on all ABCI++ methods
- [Methods](./abci++_methods.md) - complete details on all ABCI++ methods
and message types.
- [Requirements for the Application](./abci++_app_requirements_002_draft.md) - formal requirements
- [Requirements for the Application](./abci++_app_requirements.md) - formal requirements
on the Application's logic to ensure Tendermint properties such as liveness. These requirements define what
Tendermint expects from the Application; second part on managing ABCI application state and related topics.
- [Tendermint's expected behavior](./abci++_tmint_expected_behavior_002_draft.md) - specification of
- [Tendermint's expected behavior](./abci++_tmint_expected_behavior.md) - specification of
how the different ABCI++ methods may be called by Tendermint. This explains what the Application
is to expect from Tendermint.
- [Client and Server](../abci/client-server.md) - for those looking to implement their

View File

@@ -1,23 +1,24 @@
---
order: 3
title: Application Requirements
title: Requirements for the Application
---
# Application Requirements
# Requirements for the Application
## Formal Requirements
This section specifies what Tendermint expects from the Application. It is structured as a set
of formal requirements that can be used for testing and verification of the Application's logic.
Let *p* and *q* be two different correct proposers in rounds *r<sub>p</sub>* and *r<sub>q</sub>*
respectively, in height *h*.
Let *p* and *q* be two correct processes.
Let *r<sub>p</sub>* (resp. *r<sub>q</sub>*) be a round of height *h* where *p* (resp. *q*) is the
proposer.
Let *s<sub>p,h-1</sub>* be *p*'s Application's state committed for height *h-1*.
Let *v<sub>p</sub>* (resp. *v<sub>q</sub>*) be the block that *p*'s (resp. *q*'s) Tendermint passes
on to the Application
via `RequestPrepareProposal` as proposer of round *r<sub>p</sub>* (resp *r<sub>q</sub>*), height *h*,
also known as the raw proposal.
Let *v'<sub>p</sub>* (resp. *v'<sub>q</sub>*) the possibly modified block *p*'s (resp. *q*'s) Application
Let *u<sub>p</sub>* (resp. *u<sub>q</sub>*) the possibly modified block *p*'s (resp. *q*'s) Application
returns via `ResponsePrepareProposal` to Tendermint, also known as the prepared proposal.
Process *p*'s prepared proposal can differ in two different rounds where *p* is the proposer.
@@ -49,6 +50,7 @@ same-block execution mode and *does not* provide values for
Full execution of blocks at `PrepareProposal` time stands on Tendermint's critical path. Thus,
Requirement 3 ensures the Application will set a value for `TimeoutPropose` such that the time it takes
to fully execute blocks in `PrepareProposal` does not interfere with Tendermint's propose timer.
Note that violation of Requirement 3 may just lead to further rounds, but will not compromise liveness.
* Requirement 4 [`PrepareProposal`, tx-size]: When *p*'s Application calls `ResponsePrepareProposal`, the
total size in bytes of the transactions returned does not exceed `RequestPrepareProposal.max_tx_bytes`.
@@ -62,7 +64,7 @@ transaction list returned by the application will never cause the resulting bloc
limit.
* Requirement 5 [`PrepareProposal`, `ProcessProposal`, coherence]: For any two correct processes *p* and *q*,
if *q*'s Tendermint calls `RequestProcessProposal` on *v'<sub>p</sub>*,
if *q*'s Tendermint calls `RequestProcessProposal` on *u<sub>p</sub>*,
*q*'s Application returns Accept in `ResponseProcessProposal`.
Requirement 5 makes sure that blocks proposed by correct processes *always* pass the correct receiving process's
@@ -75,14 +77,14 @@ serious consequences on Tendermint's liveness that this entails. Due to its crit
target for extensive testing and automated verification.
* Requirement 6 [`ProcessProposal`, determinism-1]: `ProcessProposal` is a (deterministic) function of the current
state and the block that is about to be applied. In other words, for any correct process *p*, and any arbitrary block *v'*,
if *p*'s Tendermint calls `RequestProcessProposal` on *v'* at height *h*,
then *p*'s Application's acceptance or rejection **exclusively** depends on *v'* and *s<sub>p,h-1</sub>*.
state and the block that is about to be applied. In other words, for any correct process *p*, and any arbitrary block *u*,
if *p*'s Tendermint calls `RequestProcessProposal` on *u* at height *h*,
then *p*'s Application's acceptance or rejection **exclusively** depends on *u* and *s<sub>p,h-1</sub>*.
* Requirement 7 [`ProcessProposal`, determinism-2]: For any two correct processes *p* and *q*, and any arbitrary
block *v'*,
if *p*'s (resp. *q*'s) Tendermint calls `RequestProcessProposal` on *v'* at height *h*,
then *p*'s Application accepts *v'* if and only if *q*'s Application accepts *v'*.
block *u*,
if *p*'s (resp. *q*'s) Tendermint calls `RequestProcessProposal` on *u* at height *h*,
then *p*'s Application accepts *u* if and only if *q*'s Application accepts *u*.
Note that this requirement follows from Requirement 6 and the Agreement property of consensus.
Requirements 6 and 7 ensure that all correct processes will react in the same way to a proposed block, even
@@ -97,7 +99,7 @@ of `ProcessProposal`. As a general rule `ProcessProposal` SHOULD always accept t
According to the Tendermint algorithm, a correct process can broadcast at most one precommit
message in round *r*, height *h*.
Since, as stated in the [Methods](./abci++_methods_002_draft.md#extendvote) section, `ResponseExtendVote`
Since, as stated in the [Methods](./abci++_methods.md#extendvote) section, `ResponseExtendVote`
is only called when Tendermint
is about to broadcast a non-`nil` precommit message, a correct process can only produce one vote extension
in round *r*, height *h*.
@@ -106,9 +108,9 @@ Let *e<sup>r</sup><sub>p</sub>* be the vote extension that the Application of a
Let *w<sup>r</sup><sub>p</sub>* be the proposed block that *p*'s Tendermint passes to the Application via `RequestExtendVote`
in round *r*, height *h*.
* Requirement 8 [`ExtendVote`, `VerifyVoteExtension`, coherence]: For any two correct processes *p* and *q*, if *q*
receives *e<sup>r</sup><sub>p</sub>*
from *p* in height *h*, *q*'s Application returns Accept in `ResponseVerifyVoteExtension`.
* Requirement 8 [`ExtendVote`, `VerifyVoteExtension`, coherence]: For any two different correct
processes *p* and *q*, if *q* receives *e<sup>r</sup><sub>p</sub>* from *p* in height *h*, *q*'s
Application returns Accept in `ResponseVerifyVoteExtension`.
Requirement 8 constrains the creation and handling of vote extensions in a similar way as Requirement 5
constrains the creation and handling of proposed blocks.
@@ -170,8 +172,8 @@ Finally, notice that neither `PrepareProposal` nor `ExtendVote` have determinism
requirements associated.
Indeed, `PrepareProposal` is not required to be deterministic:
* *v'<sub>p</sub>* may depend on *v<sub>p</sub>* and *s<sub>p,h-1</sub>*, but may also depend on other values or operations.
* *v<sub>p</sub> = v<sub>q</sub> &#8655; v'<sub>p</sub> = v'<sub>q</sub>*.
* *u<sub>p</sub>* may depend on *v<sub>p</sub>* and *s<sub>p,h-1</sub>*, but may also depend on other values or operations.
* *v<sub>p</sub> = v<sub>q</sub> &#8655; u<sub>p</sub> = u<sub>q</sub>*.
Likewise, `ExtendVote` can also be non-deterministic:
@@ -365,12 +367,12 @@ For more information, see Section [State Sync](#state-sync).
### Transaction Results
The Application is expected to return a list of
[`ExecTxResult`](./abci%2B%2B_methods_002_draft.md#exectxresult) in
[`ResponseFinalizeBlock`](./abci%2B%2B_methods_002_draft.md#finalizeblock). The list of transaction
[`ExecTxResult`](./abci%2B%2B_methods.md#exectxresult) in
[`ResponseFinalizeBlock`](./abci%2B%2B_methods.md#finalizeblock). The list of transaction
results must respect the same order as the list of transactions delivered via
[`RequestFinalizeBlock`](./abci%2B%2B_methods_002_draft.md#finalizeblock).
[`RequestFinalizeBlock`](./abci%2B%2B_methods.md#finalizeblock).
This section discusses the fields inside this structure, along with the fields in
[`ResponseCheckTx`](./abci%2B%2B_methods_002_draft.md#checktx),
[`ResponseCheckTx`](./abci%2B%2B_methods.md#checktx),
whose semantics are similar.
The `Info` and `Log` fields are
@@ -471,12 +473,12 @@ events took place during their execution.
### Updating the Validator Set
The application may set the validator set during
[`InitChain`](./abci%2B%2B_methods_002_draft.md#initchain), and may update it during
[`FinalizeBlock`](./abci%2B%2B_methods_002_draft.md#finalizeblock)
[`InitChain`](./abci%2B%2B_methods.md#initchain), and may update it during
[`FinalizeBlock`](./abci%2B%2B_methods.md#finalizeblock)
(next block execution mode) or
[`PrepareProposal`](./abci%2B%2B_methods_002_draft.md#prepareproposal)/[`ProcessProposal`](./abci%2B%2B_methods_002_draft.md#processproposal)
[`PrepareProposal`](./abci%2B%2B_methods.md#prepareproposal)/[`ProcessProposal`](./abci%2B%2B_methods.md#processproposal)
(same block execution mode). In all cases, a structure of type
[`ValidatorUpdate`](./abci%2B%2B_methods_002_draft.md#validatorupdate) is returned.
[`ValidatorUpdate`](./abci%2B%2B_methods.md#validatorupdate) is returned.
The `InitChain` method, used to initialize the Application, can return a list of validators.
If the list is empty, Tendermint will use the validators loaded from the genesis
@@ -510,7 +512,7 @@ Applications must ensure that
`MaxTotalVotingPower = MaxInt64 / 8`
Note the updates returned after processing the block at height `H` will only take effect
at block `H+2` (see Section [Methods](./abci%2B%2B_methods_002_draft.md)).
at block `H+2` (see Section [Methods](./abci%2B%2B_methods.md)).
### Consensus Parameters
@@ -518,10 +520,10 @@ at block `H+2` (see Section [Methods](./abci%2B%2B_methods_002_draft.md)).
They enforce certain limits in the blockchain, like the maximum size
of blocks, amount of gas used in a block, and the maximum acceptable age of
evidence. They can be set in
[`InitChain`](./abci%2B%2B_methods_002_draft.md#initchain), and updated in
[`FinalizeBlock`](./abci%2B%2B_methods_002_draft.md#finalizeblock)
[`InitChain`](./abci%2B%2B_methods.md#initchain), and updated in
[`FinalizeBlock`](./abci%2B%2B_methods.md#finalizeblock)
(next block execution mode) or
[`PrepareProposal`](./abci%2B%2B_methods_002_draft.md#prepareproposal)/[`ProcessProposal`](./abci%2B%2B_methods_002_draft.md#processproposal)
[`PrepareProposal`](./abci%2B%2B_methods.md#prepareproposal)/[`ProcessProposal`](./abci%2B%2B_methods.md#processproposal)
(same block execution model).
These parameters are deterministically set and/or updated by the Application, so
all full nodes have the same value at a given height.
@@ -672,14 +674,33 @@ node has received all precommits for a block, forgoing the remaining commit time
Setting this parameter to `false` (the default) causes Tendermint to wait
for the full commit timeout configured in `TimeoutParams.Commit`.
##### ABCIParams.VoteExtensionsEnableHeight
This parameter is either 0 or a positive height at which vote extensions
become mandatory. If the value is zero (which is the default), vote
extensions are not required. Otherwise, at all heights greater than the
configured height `H` vote extensions must be present (even if empty).
When the configured height `H` is reached, `PrepareProposal` will not
include vote extensions yet, but `ExtendVote` and `VerifyVoteExtension` will
be called. Then, when reaching height `H+1`, `PrepareProposal` will
include the vote extensions from height `H`. For all heights after `H`
* vote extensions cannot be disabled,
* they are mandatory: all precommit messages sent MUST have an extension
attached. Nevetheless, the application MAY provide 0-length
extensions.
Must always be set to a future height. Once set to a value different from
0, its value must not be changed.
#### Updating Consensus Parameters
The application may set the `ConsensusParams` during
[`InitChain`](./abci%2B%2B_methods_002_draft.md#initchain),
[`InitChain`](./abci%2B%2B_methods.md#initchain),
and update them during
[`FinalizeBlock`](./abci%2B%2B_methods_002_draft.md#finalizeblock)
[`FinalizeBlock`](./abci%2B%2B_methods.md#finalizeblock)
(next block execution mode) or
[`PrepareProposal`](./abci%2B%2B_methods_002_draft.md#prepareproposal)/[`ProcessProposal`](./abci%2B%2B_methods_002_draft.md#processproposal)
[`PrepareProposal`](./abci%2B%2B_methods.md#prepareproposal)/[`ProcessProposal`](./abci%2B%2B_methods.md#processproposal)
(same block execution mode).
If the `ConsensusParams` is empty, it will be ignored. Each field
that is not empty will be applied in full. For instance, if updating the
@@ -885,7 +906,7 @@ truncated block history - users are advised to consider the broader network impl
terms of block availability and auditability. This functionality may be added in the future.
For details on the specific ABCI calls and types, see the
[methods](abci%2B%2B_methods_002_draft.md) section.
[methods](abci%2B%2B_methods.md) section.
#### Taking Snapshots

View File

@@ -0,0 +1,468 @@
---
order: 1
title: Overview and basic concepts
---
## Outline
- [ABCI++ vs. ABCI](#abci-vs-abci)
- [Method overview](#method-overview)
- [Consensus/block execution methods](#consensusblock-execution-methods)
- [Mempool methods](#mempool-methods)
- [Info methods](#info-methods)
- [State-sync methods](#state-sync-methods)
- [Next-block execution vs. same-block execution](#next-block-execution-vs-same-block-execution)
- [Tendermint proposal timeout](#tendermint-proposal-timeout)
- [Deterministic State-Machine Replication](#deterministic-state-machine-replication)
- [Events](#events)
- [Evidence](#evidence)
- [Errors](#errors)
# Overview and basic concepts
## ABCI++ vs. ABCI
[&uparrow; Back to Outline](#outline)
The Application's main role is to execute blocks decided (a.k.a. finalized) by consensus. The
decided blocks are the main consensus's ouput to the (replicated) Application. With ABCI, the
application only interacts with consensus at *decision* time. This restricted mode of interaction
prevents numerous features for the Application, including many scalability improvements that are
now better understood than when ABCI was first written. For example, many ideas proposed to improve
scalability can be boiled down to "make the block proposers do work, so the network does not have
to". This includes optimizations such as transaction level signature aggregation, state transition
proofs, etc. Furthermore, many new security properties cannot be achieved in the current paradigm,
as the Application cannot require validators to do more than executing the transactions contained in
finalized blocks. This includes features such as threshold cryptography, and guaranteed IBC
connection attempts.
ABCI++ addresses these limitations by allowing the application to intervene at three key places of
consensus execution: (a) at the moment a new proposal is to be created, (b) at the moment a
proposal is to be validated, and (c) at the moment a (precommit) vote is sent/received. The new
interface allows block proposers to perform application-dependent work in a block through the
`PrepareProposal` method (a); validators to perform application-dependent work and checks in a
proposed block through the `ProcessProposal` method (b); and applications to require their validators
do more than just validate blocks through the `ExtendVote` and `VerifyVoteExtension` methods (c).
Furthermore, ABCI++ coalesces {`BeginBlock`, [`DeliverTx`], `EndBlock`} into `FinalizeBlock`, as a
simplified, efficient way to deliver a decided block to the Application.
## Method overview
[&uparrow; Back to Outline](#outline)
Methods can be classified into four categories: *consensus*, *mempool*, *info*, and *state-sync*.
### Consensus/block execution methods
The first time a new blockchain is started, Tendermint calls `InitChain`. From then on, method
`FinalizeBlock` is executed upon the decision of each block, resulting in an updated Application
state. During the execution of an instance of consensus, which decides the block for a given
height, and before method `FinalizeBlock` is called, methods `PrepareProposal`, `ProcessProposal`,
`ExtendVote`, and `VerifyVoteExtension` may be called several times. See
[Tendermint's expected behavior](abci++_tmint_expected_behavior.md) for details on the possible
call sequences of these methods.
- [**InitChain:**](./abci++_methods.md#initchain) This method initializes the blockchain.
Tendermint calls it once upon genesis.
- [**PrepareProposal:**](./abci++_methods.md#prepareproposal) It allows the block
proposer to perform application-dependent work in a block before proposing it.
This enables, for instance, batch optimizations to a block, which has been empirically
demonstrated to be a key component for improved performance. Method `PrepareProposal` is called
every time Tendermint is about to broadcast a Proposal message, but no previous proposal has
been locked at the Tendermint level. Tendermint gathers outstanding transactions from the
mempool, generates a block header, and uses them to create a block to propose. Then, it calls
`RequestPrepareProposal` with the newly created proposal, called *raw proposal*. The Application
can make changes to the raw proposal, such as modifying transactions, and returns the
(potentially) modified proposal, called *prepared proposal* in the `ResponsePrepareProposal`
call. The logic modifying the raw proposal can be non-deterministic.
- [**ProcessProposal:**](./abci++_methods.md#processproposal) It allows a validator to
perform application-dependent work in a proposed block. This enables features such as immediate
block execution, and allows the Application to reject invalid blocks.
Tendermint calls it when it receives a proposal and the Tendermint algorithms has not locked on a
value. The Application cannot modify the proposal at this point but can reject it if it is
invalid. If that is the case, Tendermint will prevote `nil` on the proposal, which has
strong liveness implications for Tendermint. As a general rule, the Application
SHOULD accept a prepared proposal passed via `ProcessProposal`, even if a part of
the proposal is invalid (e.g., an invalid transaction); the Application can
ignore the invalid part of the prepared proposal at block execution time.
- [**ExtendVote:**](./abci++_methods.md#extendvote) It allows applications to force their
validators to do more than just validate within consensus. `ExtendVote` allows applications to
include non-deterministic data, opaque to Tendermint, to precommit messages (the final round of
voting). The data, called *vote extension*, will be broadcast and received together with the
vote it is extending, and will be made available to the Application in the next height,
in the rounds where the local process is the proposer.
Tendermint calls `ExtendVote` when it is about to send a non-`nil` precommit message.
If the Application does not have vote extension information to provide at that time, it returns
a 0-length byte array as its vote extension.
- [**VerifyVoteExtension:**](./abci++_methods.md#verifyvoteextension) It allows
validators to validate the vote extension data attached to a precommit message. If the validation
fails, the whole precommit message will be deemed invalid and ignored by Tendermint.
This has a negative impact on Tendermint's liveness, i.e., if vote extensions repeatedly cannot be
verified by correct validators, Tendermint may not be able to finalize a block even if sufficiently
many (+2/3) validators send precommit votes for that block. Thus, `VerifyVoteExtension`
should be used with special care.
As a general rule, an Application that detects an invalid vote extension SHOULD
accept it in `ResponseVerifyVoteExtension` and ignore it in its own logic. Tendermint calls it when
a process receives a precommit message with a (possibly empty) vote extension.
- [**FinalizeBlock:**](./abci++_methods.md#finalizeblock) It delivers a decided block to the
Application. The Application must execute the transactions in the block deterministically and
update its state accordingly. Cryptographic commitments to the block and transaction results,
returned via the corresponding parameters in `ResponseFinalizeBlock`, are included in the header
of the next block. Tendermint calls it when a new block is decided.
- [**Commit:**](./abci++_methods.md#commit) Instructs the Application to persist its
state. It is a fundamental part of Tendermint's crash-recovery mechanism that ensures the
synchronization between Tendermint and the Applicatin upon recovery. Tendermint calls it just after
having persisted the data returned by `ResponseFinalizeBlock`. The Application can now discard
any state or data except the one resulting from executing the transactions in the decided block.
### Mempool methods
- [**CheckTx:**](./abci++_methods.md#checktx) This method allows the Application to validate
transactions. Validation can be stateless (e.g., checking signatures ) or stateful
(e.g., account balances). The type of validation performed is up to the application. If a
transaction passes the validation, then Tendermint adds it to the mempool; otherwise the
transaction is discarded.
Tendermint calls it when it receives a new transaction either coming from an external
user (e.g., a client) or another node. Furthermore, Tendermint can be configured to call
re-`CheckTx` on all outstanding transactions in the mempool after calling `Commit`for a block.
### Info methods
- [**Info:**](./abci++_methods.md#info) Used to sync Tendermint with the Application during a
handshake that happens upon recovery, or on startup when state-sync is used.
- [**Query:**](./abci++_methods.md#query) This method can be used to query the Application for
information about the application state.
### State-sync methods
State sync allows new nodes to rapidly bootstrap by discovering, fetching, and applying
state machine (application) snapshots instead of replaying historical blocks. For more details, see the
[state sync documentation](../p2p/messages/state-sync.md).
New nodes discover and request snapshots from other nodes in the P2P network.
A Tendermint node that receives a request for snapshots from a peer will call
`ListSnapshots` on its Application. The Application returns the list of locally available
snapshots.
Note that the list does not contain the actual snapshots but metadata about them: height at which
the snapshot was taken, application-specific verification data and more (see
[snapshot data type](./abci++_methods.md#snapshot) for more details). After receiving a
list of available snapshots from a peer, the new node can offer any of the snapshots in the list to
its local Application via the `OfferSnapshot` method. The Application can check at this point the
validity of the snapshot metadata.
Snapshots may be quite large and are thus broken into smaller "chunks" that can be
assembled into the whole snapshot. Once the Application accepts a snapshot and
begins restoring it, Tendermint will fetch snapshot "chunks" from existing nodes.
The node providing "chunks" will fetch them from its local Application using
the `LoadSnapshotChunk` method.
As the new node receives "chunks" it will apply them sequentially to the local
application with `ApplySnapshotChunk`. When all chunks have been applied, the
Application's `AppHash` is retrieved via an `Info` query.
To ensure that the sync proceeded correctly, Tendermint compares the local Application's `AppHash`
to the `AppHash` stored on the blockchain (verified via
[light client verification](../light-client/verification/README.md)).
In summary:
- [**ListSnapshots:**](./abci++_methods.md#listsnapshots) Used by nodes to discover available
snapshots on peers.
- [**OfferSnapshot:**](./abci++_methods.md#offersnapshot) When a node receives a snapshot from a
peer, Tendermint uses this method to offer the snapshot to the Application.
- [**LoadSnapshotChunk:**](./abci++_methods.md#loadsnapshotchunk) Used by Tendermint to retrieve
snapshot chunks from the Application to send to peers.
- [**ApplySnapshotChunk:**](./abci++_methods.md#applysnapshotchunk) Used by Tendermint to hand
snapshot chunks to the Application.
### Other methods
Additionally, there is a [**Flush**](./abci++_methods.md#flush) method that is called on every connection,
and an [**Echo**](./abci++_methods.md#echo) method that is used for debugging.
More details on managing state across connections can be found in the section on
[Managing Application State](./abci%2B%2B_app_requirements.md#managing-the-application-state-and-related-topics).
## Next-block execution vs. same-block execution
[&uparrow; Back to Outline](#outline)
In the original ABCI protocol, the only moment when the Application had access to a
block was after it was decided. This led to a block execution model, called *next-block
execution*, where some fields hashed in a block header refer to the execution of the
previous block, namely:
- the Merkle root of the Application's state
- the transaction results
- the consensus parameter updates
- the validator updates
With ABCI++, an Application may be configured to keep using the next-block execution model, by
executing the decided block in `FinalizeBlock`. However, the new methods introduced &mdash;
`PrepareProposal` and `ProcessProposal` &mdash; disclose the entire proposed block to the
Application, allowing for its immediate exectution. An Application implementing immediate execution
may additionally wish to store certain data resulting from the block's execution in the same block
that has just been executed. This brings about a new execution model, called
*same-block execution*. An Application implementing this execution model, upon receiving a raw
proposal via `RequestPrepareProposal` and potentially modifying its transaction list, fully
executes the resulting prepared proposal as though it was the decided block (immediate execution),
and the results of the block execution are used as follows:
- The block execution may generate a set of events. The Application should store these events and
return them back to Tendermint during the `FinalizeBlock` call if the block is finally decided.
- The Merkle root resulting from executing the prepared proposal is provided in
`ResponsePrepareProposal` and thus refers to the **current block**. Tendermint
will use it in the prepared proposal's header.
- Likewise, the transaction results from executing the prepared proposal are
provided in `ResponsePrepareProposal` and refer to the transactions in the
**current block**. Tendermint will use them to calculate the results hash
in the prepared proposal's header.
- The consensus parameter updates and validator updates are also provided in
`ResponsePrepareProposal` and reflect the result of the prepared proposal's
execution. They come into force in height H+1 (as opposed to the H+2 rule
in next-block execution model).
If the Application is configured to keep the next-block execution model, it will not
provide any data in `ResponsePrepareProposal`, other than a potentially modified
transaction list. The Application may nevertheless choose to perform immediate execution even in
next-block execution mode, however same-block execution mode *requires* immediate execution.
The long term plan is for the execution model to be set in a new boolean parameter *same_block* in
`ConsensusParams`. Once this parameter is introduced, it **must not** be changed once the
blockchain has started, unless the Application developers *really* know what they are doing.
However, modifying `ConsensusParams` structure cannot be done lightly if we are to
preserve blockchain compatibility. Therefore we need an interim solution until
soft upgrades are specified and implemented in Tendermint. This somewhat *unsafe*
solution consists in Tendermint assuming same-block execution if the Application
fills the above mentioned fields in `ResponsePrepareProposal`.
## Tendermint proposal timeout
Immediate execution requires the Application to fully execute the prepared block
before returning from `PrepareProposal`, this means that Tendermint cannot make progress
during the block execution.
This stands on Tendermint's critical path: if the Application takes a long time
executing the block, the default value of *TimeoutPropose* might not be sufficient
to accommodate the long block execution time and non-proposer nodes might time
out and prevote `nil`. The proposal, in this case, will probably be rejected and a new round will be necessary.
The Application is the best suited to provide a value for *TimeoutPropose* so
that the block execution time upon `PrepareProposal` fits well in the propose
timeout interval. Thus, the Application can adapt the value of *TimeoutPropose* at every height via
`TimeoutParams.Propose`, contained in `ConsensusParams`.
## Deterministic State-Machine Replication
[&uparrow; Back to Outline](#outline)
ABCI++ applications must implement deterministic finite-state machines to be
securely replicated by the Tendermint consensus engine. This means block execution
must be strictly deterministic: given the same
ordered set of transactions, all nodes will compute identical responses, for all
successive `FinalizeBlock` calls. This is critical because the
responses are included in the header of the next block, either via a Merkle root
or directly, so all nodes must agree on exactly what they are.
For this reason, it is recommended that application state is not exposed to any
external user or process except via the ABCI connections to a consensus engine
like Tendermint Core. The Application must only change its state based on input
from block execution (`FinalizeBlock` calls), and not through
any other kind of request. This is the only way to ensure all nodes see the same
transactions and compute the same results.
Some Applications may choose to implement immediate execution, which entails executing the blocks
that are about to be proposed (via `PrepareProposal`), and those that the Application is asked to
validate (via `ProcessProposal`). However, the state changes caused by processing those
proposed blocks must never replace the previous state until `FinalizeBlock` confirms
the block decided.
Additionally, vote extensions or the validation thereof (via `ExtendVote` or
`VerifyVoteExtension`) must *never* have side effects on the current state.
They can only be used when their data is provided in a `RequestPrepareProposal` call.
If there is some non-determinism in the state machine, consensus will eventually
fail as nodes disagree over the correct values for the block header. The
non-determinism must be fixed and the nodes restarted.
Sources of non-determinism in applications may include:
- Hardware failures
- Cosmic rays, overheating, etc.
- Node-dependent state
- Random numbers
- Time
- Underspecification
- Library version changes
- Race conditions
- Floating point numbers
- JSON or protobuf serialization
- Iterating through hash-tables/maps/dictionaries
- External Sources
- Filesystem
- Network calls (eg. some external REST API service)
See [#56](https://github.com/tendermint/abci/issues/56) for the original discussion.
Note that some methods (`Query, FinalizeBlock`) return non-deterministic data in the form
of `Info` and `Log` fields. The `Log` is intended for the literal output from the Application's
logger, while the `Info` is any additional info that should be returned. These are the only fields
that are not included in block header computations, so we don't need agreement
on them. All other fields in the `Response*` must be strictly deterministic.
## Events
[&uparrow; Back to Outline](#outline)
Method `FinalizeBlock` includes an `events` field at the top level in its
`Response*`, and one `events` field per transaction included in the block.
Applications may respond to this ABCI++ method with an event list for each executed
transaction, and a general event list for the block itself.
Events allow applications to associate metadata with transactions and blocks.
Events returned via `FinalizeBlock` do not impact Tendermint consensus in any way
and instead exist to power subscriptions and queries of Tendermint state.
An `Event` contains a `type` and a list of `EventAttributes`, which are key-value
string pairs denoting metadata about what happened during the method's (or transaction's)
execution. `Event` values can be used to index transactions and blocks according to what
happened during their execution.
Each event has a `type` which is meant to categorize the event for a particular
`Response*` or `Tx`. A `Response*` or `Tx` may contain multiple events with duplicate
`type` values, where each distinct entry is meant to categorize attributes for a
particular event. Every key and value in an event's attributes must be UTF-8
encoded strings along with the event type itself.
```protobuf
message Event {
string type = 1;
repeated EventAttribute attributes = 2;
}
```
The attributes of an `Event` consist of a `key`, a `value`, and an `index` flag. The
index flag notifies the Tendermint indexer to index the attribute. The value of
the `index` flag is non-deterministic and may vary across different nodes in the network.
```protobuf
message EventAttribute {
bytes key = 1;
bytes value = 2;
bool index = 3; // nondeterministic
}
```
Example:
```go
abci.ResponseFinalizeBlock{
// ...
Events: []abci.Event{
{
Type: "validator.provisions",
Attributes: []abci.EventAttribute{
abci.EventAttribute{Key: []byte("address"), Value: []byte("..."), Index: true},
abci.EventAttribute{Key: []byte("amount"), Value: []byte("..."), Index: true},
abci.EventAttribute{Key: []byte("balance"), Value: []byte("..."), Index: true},
},
},
{
Type: "validator.provisions",
Attributes: []abci.EventAttribute{
abci.EventAttribute{Key: []byte("address"), Value: []byte("..."), Index: true},
abci.EventAttribute{Key: []byte("amount"), Value: []byte("..."), Index: false},
abci.EventAttribute{Key: []byte("balance"), Value: []byte("..."), Index: false},
},
},
{
Type: "validator.slashed",
Attributes: []abci.EventAttribute{
abci.EventAttribute{Key: []byte("address"), Value: []byte("..."), Index: false},
abci.EventAttribute{Key: []byte("amount"), Value: []byte("..."), Index: true},
abci.EventAttribute{Key: []byte("reason"), Value: []byte("..."), Index: true},
},
},
// ...
},
}
```
## Evidence
[&uparrow; Back to Outline](#outline)
Tendermint's security model relies on the use of evidences of misbehavior. An evidence is an
irrefutable proof of malicious behavior by a network participant. It is the responsibility of
Tendermint to detect such malicious behavior. When malicious behavior is detected, Tendermint
will gossip evidences of misbehavior to other nodes and commit the evidences to
the chain once they are verified by a subset of validators. These evidences will then be
passed on to the Application through ABCI++. It is the responsibility of the
Application to handle evidence of misbehavior and exercise punishment.
There are two forms of evidence: Duplicate Vote and Light Client Attack. More
information can be found in either [data structures](../core/data_structures.md)
or [accountability](../light-client/accountability/).
EvidenceType has the following protobuf format:
```protobuf
enum EvidenceType {
UNKNOWN = 0;
DUPLICATE_VOTE = 1;
LIGHT_CLIENT_ATTACK = 2;
}
```
## Errors
[&uparrow; Back to Outline](#outline)
The `Query`, and `CheckTx` methods include a `Code` field in their `Response*`.
Field `Code` is meant to contain an application-specific response code.
A response code of `0` indicates no error. Any other response code
indicates to Tendermint that an error occurred.
These methods also return a `Codespace` string to Tendermint. This field is
used to disambiguate `Code` values returned by different domains of the
Application. The `Codespace` is a namespace for the `Code`.
Methods `Echo`, `Info`, and `InitChain` do not return errors.
An error in any of these methods represents a critical issue that Tendermint
has no reasonable way to handle. If there is an error in one
of these methods, the Application must crash to ensure that the error is safely
handled by an operator.
Method `FinalizeBlock` is a special case. It contains a number of
`Code` and `Codespace` fields as part of type `ExecTxResult`. Each of
these codes reports errors related to the transaction it is attached to.
However, `FinalizeBlock` does not return errors at the top level, so the
same considerations on critical issues made for `Echo`, `Info`, and
`InitChain` also apply here.
The handling of non-zero response codes by Tendermint is described below.
### `CheckTx`
When Tendermint receives a `ResponseCheckTx` with a non-zero `Code`, the associated
transaction will not be added to Tendermint's mempool or it will be removed if
it is already included.
### `ExecTxResult` (as part of `FinalizeBlock`)
The `ExecTxResult` type delivers transaction results from the Application to Tendermint. When
Tendermint receives a `ResponseFinalizeBlock` containing an `ExecTxResult` with a non-zero `Code`,
the response code is logged. Past `Code` values can be queried by clients. As the transaction was
part of a decided block, the `Code` does not influence Tendermint consensus.
### `Query`
When Tendermint receives a `ResponseQuery` with a non-zero `Code`, this code is
returned directly to the client that initiated the query.

View File

@@ -1,404 +0,0 @@
---
order: 1
title: Overview and basic concepts
---
## Outline
- [ABCI++ vs. ABCI](#abci-vs-abci)
- [Methods overview](#methods-overview)
- [Consensus methods](#consensus-methods)
- [Mempool methods](#mempool-methods)
- [Info methods](#info-methods)
- [State-sync methods](#state-sync-methods)
- [Next-block execution vs. same-block execution](#next-block-execution-vs-same-block-execution)
- [Tendermint timeouts](#tendermint-timeouts-in-same-block-execution)
- [Determinism](#determinism)
- [Errors](#errors)
- [Events](#events)
- [Evidence](#evidence)
# Overview and basic concepts
## ABCI++ vs. ABCI
[&uparrow; Back to Outline](#outline)
With ABCI, the application can only act at one phase in consensus, immediately after a block has been finalized. This restriction on the application prevents numerous features for the application, including many scalability improvements that are now better understood than when ABCI was first written. For example, many of the scalability proposals can be boiled down to "Make the miner / block proposers / validators do work, so the network does not have to". This includes optimizations such as tx-level signature aggregation, state transition proofs, etc. Furthermore, many new security properties cannot be achieved in the current paradigm, as the application cannot enforce validators to do more than just finalize txs. This includes features such as threshold cryptography, and guaranteed IBC connection attempts.
ABCI++ overcomes these limitations by allowing the application to intervene at three key places of the block execution. The new interface allows block proposers to perform application-dependent work in a block through the `PrepareProposal` method; validators to perform application-dependent work in a proposed block through the `ProcessProposal` method; and applications to require their validators do more than just validate blocks, e.g., validator guaranteed IBC connection attempts, through the `ExtendVote` and `VerifyVoteExtension` methods. Furthermore, ABCI++ renames {`BeginBlock`, [`DeliverTx`], `EndBlock`} to `FinalizeBlock`, as a simplified way to deliver a decided block to the Application.
## Methods overview
[&uparrow; Back to Outline](#outline)
Methods can be classified into four categories: consensus, mempool, info, and state-sync.
### Consensus/block execution methods
The first time a new blockchain is started, Tendermint calls
`InitChain`. From then on, method `FinalizeBlock` is executed at the end of each
block, resulting in an updated Application state.
During consensus execution of a block height, before method `FinalizeBlock` is
called, methods `PrepareProposal`, `ProcessProposal`, `ExtendVote`, and
`VerifyVoteExtension` may be called several times.
See [Tendermint's expected behavior](abci++_tmint_expected_behavior_002_draft.md)
for details on the possible call sequences of these methods.
* [**InitChain:**](./abci++_methods_002_draft.md#initchain) This method initializes the blockchain. Tendermint calls it once upon genesis.
* [**PrepareProposal:**](./abci++_methods_002_draft.md#prepareproposal) It allows the block proposer to perform application-dependent work in a block before using it as its proposal. This enables, for instance, batch optimizations to a block, which has been empirically demonstrated to be a key component for scaling. Method `PrepareProposal` is called every time Tendermint is about to send
a proposal message, but no previous proposal has been locked at Tendermint level.
Tendermint gathers outstanding transactions from the mempool, generates a block header, and uses
them to create a block to propose. Then, it calls `RequestPrepareProposal`
with the newly created proposal, called _raw proposal_. The Application can
make changes to the raw proposal, such as modifying transactions, and returns
the (potentially) modified proposal, called _prepared proposal_ in the
`Response*` call. The logic modifying the raw proposal can be non-deterministic.
* [**ProcessProposal:**](./abci++_methods_002_draft.md#processproposal) It allows a validator to perform application-dependent work in a proposed block. This enables features such as allowing validators to reject a block according to whether the state machine deems it valid, and changing the block execution pipeline. Tendermint calls it when it receives a proposal and it is not locked on a block. The Application cannot
modify the proposal at this point but can reject it if it realizes it is invalid.
If that is the case, Tendermint will prevote `nil` on the proposal, which has
strong liveness implications for Tendermint. As a general rule, the Application
SHOULD accept a prepared proposal passed via `ProcessProposal`, even if a part of
the proposal is invalid (e.g., an invalid transaction); the Application can
ignore the invalid part of the prepared proposal at block execution time.
* [**ExtendVote:**](./abci++_methods_002_draft.md#extendvote) It allows applications to force their validators to do more than just validate within consensus. `ExtendVote` allows applications to include non-deterministic data, opaque to Tendermint, to precommit messages (the final round of voting).
The data, called _vote extension_, will also be made available to the
application in the next height, along with the vote it is extending, in the rounds
where the local process is the proposer.
If the Application does not have vote extension information to provide, it returns a 0-length byte array as its vote extension.
Tendermint calls `ExtendVote` when is about to send a non-`nil` precommit message.
* [**VerifyVoteExtension:**](./abci++_methods_002_draft.md#verifyvoteextension) It allows validators to validate the vote extension data attached to a precommit message. If the validation fails, the precommit message will be deemed invalid and ignored
by Tendermint. This has a negative impact on Tendermint's liveness, i.e., if vote extensions repeatedly cannot be verified by correct validators, Tendermint may not be able to finalize a block even if sufficiently many (+2/3) of the validators send precommit votes for that block. Thus, `VerifyVoteExtension` should be used with special care.
As a general rule, an Application that detects an invalid vote extension SHOULD
accept it in `ResponseVerifyVoteExtension` and ignore it in its own logic. Tendermint calls it when
a process receives a precommit message with a (possibly empty) vote extension.
* [**FinalizeBlock:**](./abci++_methods_002_draft.md#finalizeblock) It delivers a decided block to the Application. The Application must execute the transactions in the block in order and update its state accordingly. Cryptographic commitments to the block and transaction results, via the corresponding
parameters in `ResponseFinalizeBlock`, are included in the header of the next block. Tendermint calls it when a new block is decided.
### Mempool methods
* [**CheckTx:**](./abci++_methods_002_draft.md#checktx) This method allows the Application to validate transactions against its current state, e.g., checking signatures and account balances. If a transaction passes the validation, then tendermint adds it to its local mempool, discarding it otherwise. Tendermint calls it when it receives a new transaction either coming from an external user or another node. Furthermore, Tendermint can be configured to re-call `CheckTx` on any decided transaction (after `FinalizeBlock`).
### Info methods
* [**Info:**](./abci++_methods_002_draft.md#info) Used to sync Tendermint with the Application during a handshake that happens on startup.
* [**Query:**](./abci++_methods_002_draft.md#query) Clients can use this method to query the Application for information about the application state.
### State-sync methods
State sync allows new nodes to rapidly bootstrap by discovering, fetching, and applying
state machine snapshots instead of replaying historical blocks. For more details, see the
[state sync section](../p2p/messages/state-sync.md).
New nodes will discover and request snapshots from other nodes in the P2P network.
A Tendermint node that receives a request for snapshots from a peer will call
`ListSnapshots` on its Application. The Application returns the list of locally avaiable snapshots.
Note that the list does not contain the actual snapshot but metadata about it: height at which the snapshot was taken, application-specific verification data and more (see [snapshot data type](./abci++_methods_002_draft.md#snapshot) for more details). After receiving a list of available snapshots from a peer, the new node can offer any of the snapshots in the list to its local Application via the `OfferSnapshot` method. The Application can check at this point the validity of the snapshot metadata.
Snapshots may be quite large and are thus broken into smaller "chunks" that can be
assembled into the whole snapshot. Once the Application accepts a snapshot and
begins restoring it, Tendermint will fetch snapshot "chunks" from existing nodes.
The node providing "chunks" will fetch them from its local Application using
the `LoadSnapshotChunk` method.
As the new node receives "chunks" it will apply them sequentially to the local
application with `ApplySnapshotChunk`. When all chunks have been applied, the
Application's `AppHash` is retrieved via an `Info` query.
To ensure that the sync proceeded correctly, Tendermint compares the local Application's `AppHash` to the `AppHash` stored on the blockchain (verified via
[light client verification](../light-client/verification/README.md)).
In summary:
* [**ListSnapshots:**](./abci++_methods_002_draft.md#listsnapshots) Used by nodes to discover available snapshots on peers.
* [**LoadSnapshotChunk:**](./abci++_methods_002_draft.md#loadsnapshotchunk) Used by Tendermint to retrieve snapshot chunks from the application to send to peers.
* [**OfferSnapshot:**](./abci++_methods_002_draft.md#offersnapshot) When a node receives a snapshot from a peer, Tendermint uses this method to offer the snapshot to the Application.
* [**ApplySnapshotChunk:**](./abci++_methods_002_draft.md#applysnapshotchunk) Used by Tendermint to hand snapshot chunks to the Application.
### Other methods
Additionally, there is a [**Flush**](./abci++_methods_002_draft.md#flush) method that is called on every connection,
and an [**Echo**](./abci++_methods_002_draft.md#echo) method that is just for debugging.
More details on managing state across connections can be found in the section on
[ABCI Applications](../abci/apps.md).
## Next-block execution vs. same-block execution
[&uparrow; Back to Outline](#outline)
In the original ABCI protocol, the only moment when the Application had access to a
block was after it was decided. This led to a block execution model, called _next-block
execution_, where some fields hashed in a block header refer to the execution of the
previous block, namely:
* the Merkle root of the Application's state
* the transaction results
* the consensus parameter updates
* the validator updates
With ABCI++, an Application may decide to keep using the next-block execution model, by doing all its processing in `FinalizeBlock`;
however the new methods introduced, `PrepareProposal` and `ProcessProposal` allow
for a new execution model, called _same-block execution_. An Application implementing
this execution model, upon receiving a raw proposal via `RequestPrepareProposal`
and potentially modifying its transaction list,
fully executes the resulting prepared proposal as though it was the decided block.
The results of the block execution are used as follows:
* The block execution may generate a set of events. The Application should store these events and return them back to Tendermint during the `FinalizeBlock` call if the block is finally decided.
* The Merkle root resulting from executing the prepared proposal is provided in
`ResponsePrepareProposal` and thus refers to the **current block**. Tendermint
will use it in the prepared proposal's header.
* likewise, the transaction results from executing the prepared proposal are
provided in `ResponsePrepareProposal` and refer to the transactions in the
**current block**. Tendermint will use them to calculate the results hash
in the prepared proposal's header.
* The consensus parameter updates and validator updates are also provided in
`ResponsePrepareProposal` and reflect the result of the prepared proposal's
execution. They come into force in height H+1 (as opposed to the H+2 rule
in next-block execution model).
If the Application decides to keep the next-block execution model, it will not
provide any data in `ResponsePrepareProposal`, other than an optionally modified
transaction list.
In the long term, the execution model will be set in a new boolean parameter
*same_block* in `ConsensusParams`.
It **must not** be changed once the blockchain has started unless the Application
developers _really_ know what they are doing.
However, modifying `ConsensusParams` structure cannot be done lightly if we are to
preserve blockchain compatibility. Therefore we need an interim solution until
soft upgrades are specified and implemented in Tendermint. This somewhat _unsafe_
solution consists in Tendermint assuming same-block execution if the Application
fills the above mentioned fields in `ResponsePrepareProposal`.
### Tendermint timeouts in same-block execution
The new same-block execution mode requires the Application to fully execute the
prepared block at `PrepareProposal` time. This execution is synchronous, so
Tendermint cannot make progress until the Application returns from `PrepareProposal`.
This stands on Tendermint's critical path: if the Application takes a long time
executing the block, the default value of _TimeoutPropose_ might not be sufficient
to accommodate the long block execution time and non-proposer processes might time
out and prevote `nil`, thus starting a further round unnecessarily.
The Application is the best suited to provide a value for _TimeoutPropose_ so
that the block execution time upon `PrepareProposal` fits well in the propose
timeout interval.
Currently, the Application can override the value of _TimeoutPropose_ via the
`config.toml` file. In the future, `ConsensusParams` will have an extra field
with the current _TimeoutPropose_ value so that the Application can adapt it at every height.
## Determinism
[&uparrow; Back to Outline](#outline)
ABCI++ applications must implement deterministic finite-state machines to be
securely replicated by the Tendermint consensus engine. This means block execution
over the Consensus Connection must be strictly deterministic: given the same
ordered set of transactions, all nodes will compute identical responses, for all
successive `FinalizeBlock` calls. This is critical because the
responses are included in the header of the next block, either via a Merkle root
or directly, so all nodes must agree on exactly what they are.
For this reason, it is recommended that application state is not exposed to any
external user or process except via the ABCI connections to a consensus engine
like Tendermint Core. The Application must only change its state based on input
from block execution (`FinalizeBlock` calls), and not through
any other kind of request. This is the only way to ensure all nodes see the same
transactions and compute the same results.
Some Applications may choose to execute the blocks that are about to be proposed
(via `PrepareProposal`), or those that the Application is asked to validate
(via `ProcessProposal`). However, the state changes caused by processing those
proposed blocks must never replace the previous state until `FinalizeBlock` confirms
the block decided.
Additionally, vote extensions or the validation thereof (via `ExtendVote` or
`VerifyVoteExtension`) must _never_ have side effects on the current state.
They can only be used when their data is provided in a `RequestPrepareProposal` call.
If there is some non-determinism in the state machine, consensus will eventually
fail as nodes disagree over the correct values for the block header. The
non-determinism must be fixed and the nodes restarted.
Sources of non-determinism in applications may include:
* Hardware failures
* Cosmic rays, overheating, etc.
* Node-dependent state
* Random numbers
* Time
* Underspecification
* Library version changes
* Race conditions
* Floating point numbers
* JSON or protobuf serialization
* Iterating through hash-tables/maps/dictionaries
* External Sources
* Filesystem
* Network calls (eg. some external REST API service)
See [#56](https://github.com/tendermint/abci/issues/56) for original discussion.
Note that some methods (`Query, CheckTx, FinalizeBlock`) return
explicitly non-deterministic data in the form of `Info` and `Log` fields. The `Log` is
intended for the literal output from the Application's logger, while the
`Info` is any additional info that should be returned. These are the only fields
that are not included in block header computations, so we don't need agreement
on them. All other fields in the `Response*` must be strictly deterministic.
## Errors
[&uparrow; Back to Outline](#outline)
The `Query`, and `CheckTx` methods include a `Code` field in their `Response*`.
The `Code` field is also included in type `TxResult`, used by
method `FinalizeBlock`'s `Response*`.
Field `Code` is meant to contain an application-specific response code.
A response code of `0` indicates no error. Any other response code
indicates to Tendermint that an error occurred.
These methods also return a `Codespace` string to Tendermint. This field is
used to disambiguate `Code` values returned by different domains of the
Application. The `Codespace` is a namespace for the `Code`.
Methods `Echo`, `Info`, and `InitChain` do not return errors.
An error in any of these methods represents a critical issue that Tendermint
has no reasonable way to handle. If there is an error in one
of these methods, the Application must crash to ensure that the error is safely
handled by an operator.
Method `FinalizeBlock` is a special case. It contains a number of
`Code` and `Codespace` fields as part of type `TxResult`. Each of
these codes reports errors related to the transaction it is attached to.
However, `FinalizeBlock` does not return errors at the top level, so the
same considerations on critical issues made for `Echo`, `Info`, and
`InitChain` also apply here.
The handling of non-zero response codes by Tendermint is described below
### `CheckTx`
When Tendermint receives a `ResponseCheckTx` with a non-zero `Code`, the associated
transaction will not be added to Tendermint's mempool or it will be removed if
it is already included.
### `TxResult` (as part of `FinalizeBlock`)
The `TxResult` type delivers transactions from Tendermint to the Application.
When Tendermint receives a `ResponseFinalizeBlock` containing a `TxResult`
with a non-zero `Code`, the response code is logged.
The transaction was already included in a block, so the `Code` does not influence
Tendermint consensus.
### `Query`
When Tendermint receives a `ResponseQuery` with a non-zero `Code`, this code is
returned directly to the client that initiated the query.
## Events
[&uparrow; Back to Outline](#outline)
Method `CheckTx` includes an `Events` field in its `Response*`.
Method `FinalizeBlock` includes an `Events` field at the top level in its
`Response*`, and one `events` field per transaction included in the block.
Applications may respond to these ABCI++ methods with a set of events.
Events allow applications to associate metadata about ABCI++ method execution with the
transactions and blocks this metadata relates to.
Events returned via these ABCI++ methods do not impact Tendermint consensus in any way
and instead exist to power subscriptions and queries of Tendermint state.
An `Event` contains a `type` and a list of `EventAttributes`, which are key-value
string pairs denoting metadata about what happened during the method's (or transaction's)
execution. `Event` values can be used to index transactions and blocks according to what
happened during their execution.
Each event has a `type` which is meant to categorize the event for a particular
`Response*` or `Tx`. A `Response*` or `Tx` may contain multiple events with duplicate
`type` values, where each distinct entry is meant to categorize attributes for a
particular event. Every key and value in an event's attributes must be UTF-8
encoded strings along with the event type itself.
```protobuf
message Event {
string type = 1;
repeated EventAttribute attributes = 2;
}
```
The attributes of an `Event` consist of a `key`, a `value`, and an `index` flag. The
index flag notifies the Tendermint indexer to index the attribute. The value of
the `index` flag is non-deterministic and may vary across different nodes in the network.
```protobuf
message EventAttribute {
bytes key = 1;
bytes value = 2;
bool index = 3; // nondeterministic
}
```
Example:
```go
abci.ResponseCheckTx{
// ...
Events: []abci.Event{
{
Type: "validator.provisions",
Attributes: []abci.EventAttribute{
abci.EventAttribute{Key: []byte("address"), Value: []byte("..."), Index: true},
abci.EventAttribute{Key: []byte("amount"), Value: []byte("..."), Index: true},
abci.EventAttribute{Key: []byte("balance"), Value: []byte("..."), Index: true},
},
},
{
Type: "validator.provisions",
Attributes: []abci.EventAttribute{
abci.EventAttribute{Key: []byte("address"), Value: []byte("..."), Index: true},
abci.EventAttribute{Key: []byte("amount"), Value: []byte("..."), Index: false},
abci.EventAttribute{Key: []byte("balance"), Value: []byte("..."), Index: false},
},
},
{
Type: "validator.slashed",
Attributes: []abci.EventAttribute{
abci.EventAttribute{Key: []byte("address"), Value: []byte("..."), Index: false},
abci.EventAttribute{Key: []byte("amount"), Value: []byte("..."), Index: true},
abci.EventAttribute{Key: []byte("reason"), Value: []byte("..."), Index: true},
},
},
// ...
},
}
```
## Evidence
[&uparrow; Back to Outline](#outline)
Tendermint's security model relies on the use of "evidence". Evidence is proof of
malicious behavior by a network participant. It is the responsibility of Tendermint
to detect such malicious behavior. When malicious behavior is detected, Tendermint
will gossip evidence of the behavior to other nodes and commit the evidence to
the chain once it is verified by all validators. This evidence will then be
passed on to the Application through ABCI++. It is the responsibility of the
Application to handle the evidence and exercise punishment.
EvidenceType has the following protobuf format:
```protobuf
enum EvidenceType {
UNKNOWN = 0;
DUPLICATE_VOTE = 1;
LIGHT_CLIENT_ATTACK = 2;
}
```
There are two forms of evidence: Duplicate Vote and Light Client Attack. More
information can be found in either [data structures](../core/data_structures.md)
or [accountability](../light-client/accountability/)

View File

@@ -9,10 +9,10 @@ This section is for those looking to implement their own ABCI Server, perhaps in
a new programming language.
You are expected to have read all previous sections of ABCI++ specification, namely
[Basic Concepts](./abci%2B%2B_basic_concepts_002_draft.md),
[Methods](./abci%2B%2B_methods_002_draft.md),
[Application Requirements](./abci%2B%2B_app_requirements_002_draft.md), and
[Expected Behavior](./abci%2B%2B_tmint_expected_behavior_002_draft.md).
[Basic Concepts](./abci%2B%2B_basic_concepts.md),
[Methods](./abci%2B%2B_methods.md),
[Application Requirements](./abci%2B%2B_app_requirements.md), and
[Expected Behavior](./abci%2B%2B_tmint_expected_behavior.md).
## Message Protocol and Synchrony
@@ -25,7 +25,7 @@ or custom protobuf types.
For more details on protobuf, see the [documentation](https://developers.google.com/protocol-buffers/docs/overview).
As of v0.36 requests are synchronous. For each of ABCI++'s four connections (see
[Connections](./abci%2B%2B_app_requirements_002_draft.md)), when Tendermint issues a request to the
[Connections](./abci%2B%2B_app_requirements.md)), when Tendermint issues a request to the
Application, it will wait for the response before continuing execution. As a side effect,
requests and responses are ordered for each connection, but not necessarily across connections.

View File

@@ -38,22 +38,21 @@ title: Methods
* **Response**:
| Name | Type | Description | Field Number |
|---------------------|--------|--------------------------------------------------|--------------|
| data | string | Some arbitrary information | 1 |
| version | string | The application software semantic version | 2 |
| app_version | uint64 | The application protocol version | 3 |
| last_block_height | int64 | Latest block for which the app has called Commit | 4 |
| last_block_app_hash | bytes | Latest result of Commit | 5 |
| Name | Type | Description | Field Number |
|---------------------|--------|-----------------------------------------------------|--------------|
| data | string | Some arbitrary information | 1 |
| version | string | The application software semantic version | 2 |
| app_version | uint64 | The application protocol version | 3 |
| last_block_height | int64 | Latest height for which the app persisted its state | 4 |
| last_block_app_hash | bytes | Latest AppHash returned by `FinalizeBlock` | 5 |
* **Usage**:
* Return information about the application state.
* Used to sync Tendermint with the application during a handshake
that happens on startup.
that happens on startup or on recovery.
* The returned `app_version` will be included in the Header of every block.
* Tendermint expects `last_block_app_hash` and `last_block_height` to
be updated during `Commit`, ensuring that `Commit` is never
called twice for the same block height.
be updated during `FinalizeBlock` and persisted during `Commit`.
> Note: Semantic version is a reference to [semantic versioning](https://semver.org/). Semantic versions in info will be displayed as X.X.x.
@@ -61,22 +60,22 @@ title: Methods
* **Request**:
| Name | Type | Description | Field Number |
|------------------|--------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------|--------------|
| time | [google.protobuf.Timestamp](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#google.protobuf.Timestamp) | Genesis time | 1 |
| chain_id | string | ID of the blockchain. | 2 |
| consensus_params | [ConsensusParams](#consensusparams) | Initial consensus-critical parameters. | 3 |
| validators | repeated [ValidatorUpdate](#validatorupdate) | Initial genesis validators, sorted by voting power. | 4 |
| app_state_bytes | bytes | Serialized initial application state. JSON bytes. | 5 |
| initial_height | int64 | Height of the initial block (typically `1`). | 6 |
| Name | Type | Description | Field Number |
|------------------|-------------------------------------------------|-----------------------------------------------------|--------------|
| time | [google.protobuf.Timestamp][protobuf-timestamp] | Genesis time | 1 |
| chain_id | string | ID of the blockchain. | 2 |
| consensus_params | [ConsensusParams](#consensusparams) | Initial consensus-critical parameters. | 3 |
| validators | repeated [ValidatorUpdate](#validatorupdate) | Initial genesis validators, sorted by voting power. | 4 |
| app_state_bytes | bytes | Serialized initial application state. JSON bytes. | 5 |
| initial_height | int64 | Height of the initial block (typically `1`). | 6 |
* **Response**:
| Name | Type | Description | Field Number |
|------------------|----------------------------------------------|-------------------------------------------------|--------------|
| Name | Type | Description | Field Number |
|------------------|----------------------------------------------|--------------------------------------------------|--------------|
| consensus_params | [ConsensusParams](#consensusparams) | Initial consensus-critical parameters (optional) | 1 |
| validators | repeated [ValidatorUpdate](#validatorupdate) | Initial validator set (optional). | 2 |
| app_hash | bytes | Initial application hash. | 3 |
| validators | repeated [ValidatorUpdate](#validatorupdate) | Initial validator set (optional). | 2 |
| app_hash | bytes | Initial application hash. | 3 |
* **Usage**:
* Called once upon genesis.
@@ -84,10 +83,10 @@ title: Methods
* If `ResponseInitChain.Validators` is not empty, it will be the initial
validator set (regardless of what is in `RequestInitChain.Validators`).
* This allows the app to decide if it wants to accept the initial validator
set proposed by tendermint (ie. in the genesis file), or if it wants to use
set proposed by Tendermint (ie. in the genesis file), or if it wants to use
a different one (perhaps computed based on some application specific
information in the genesis file).
* Both `ResponseInitChain.Validators` and `ResponseInitChain.Validators` are [ValidatorUpdate](#validatorupdate) structs.
* Both `RequestInitChain.Validators` and `ResponseInitChain.Validators` are [ValidatorUpdate](#validatorupdate) structs.
So, technically, they both are _updating_ the set of validators from the empty set.
### Query
@@ -97,7 +96,7 @@ title: Methods
| Name | Type | Description | Field Number |
|--------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------|
| data | bytes | Raw query bytes. Can be used with or in lieu of Path. | 1 |
| path | string | Path field of the request URI. Can be used with or in lieu 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/...` | 2 |
| path | string | Path field of the request URI. Can be used with or in lieu 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/...` | 2 |
| 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 | 3 |
| prove | bool | Return Merkle proof with response if possible | 4 |
@@ -145,15 +144,40 @@ title: Methods
* Technically optional - not involved in processing blocks.
* Guardian of the mempool: every node runs `CheckTx` before letting a
transaction into its local mempool.
transaction into its local mempool.
* The transaction may come from an external user or another node
* `CheckTx` validates the transaction against the current state of the application,
for example, checking signatures and account balances, but does not apply any
of the state changes described in the transaction.
not running code in a virtual machine.
* Transactions where `ResponseCheckTx.Code != 0` will be rejected - they will not be broadcast to
other nodes or included in a proposal block.
* Tendermint attributes no other value to the response code
for example, checking signatures and account balances, but does not apply any
of the state changes described in the transaction.
* Transactions where `ResponseCheckTx.Code != 0` will be rejected - they will not be broadcast
to other nodes or included in a proposal block.
Tendermint attributes no other value to the response code.
### Commit
#### Parameters and Types
* **Request**:
| Name | Type | Description | Field Number |
|--------|-------|-------------|--------------|
Commit signals the application to persist application state. It takes no parameters.
* **Response**:
| Name | Type | Description | Field Number |
|---------------|-------|------------------------------------------------------------------------|--------------|
| retain_height | int64 | Blocks below this height may be removed. Defaults to `0` (retain all). | 3 |
* **Usage**:
* Signal the Application to persist the application state.
Application is expected to persist its state at the end of this call, before calling `ResponseCommit`.
* Use `ResponseCommit.retain_height` with caution! If all nodes in the network remove historical
blocks then this data is permanently lost, and no new nodes will be able to join the network and
bootstrap. Historical blocks may also be required for other purposes, e.g. auditing, replay of
non-persisted heights, light client verification, and so on.
### ListSnapshots
@@ -269,7 +293,7 @@ title: Methods
`Snapshot.Metadata` and/or incrementally verifying contents against `AppHash`.
* When all chunks have been accepted, Tendermint will make an ABCI `Info` call to verify that
`LastBlockAppHash` and `LastBlockHeight` matches the expected values, and record the
`AppVersion` in the node state. It then switches to fast sync or consensus and joins the
`AppVersion` in the node state. It then switches to block sync or consensus and joins the
network.
* If Tendermint is unable to retrieve the next chunk after some time (e.g. because no suitable
peers are available), it will reject the snapshot and try a different one via `OfferSnapshot`.
@@ -283,16 +307,16 @@ title: Methods
* **Request**:
| Name | Type | Description | Field Number |
|-------------------------|---------------------------------------------|------------------------------------------------------------------------------------------------------------------|--------------|
| max_tx_bytes | int64 | Currently configured maximum size in bytes taken by the modified transactions. | 1 |
| txs | repeated bytes | Preliminary list of transactions that have been picked as part of the block to propose. | 2 |
| local_last_commit | [ExtendedCommitInfo](#extendedcommitinfo) | Info about the last commit, obtained locally from Tendermint's data structures. | 3 |
| byzantine_validators | repeated [Misbehavior](#misbehavior) | List of information about validators that acted incorrectly. | 4 |
| height | int64 | The height of the block that will be proposed. | 5 |
| time | [google.protobuf.Timestamp](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#google.protobuf.Timestamp) | Timestamp of the block that that will be proposed. | 6 |
| next_validators_hash | bytes | Merkle root of the next validator set. | 7 |
| proposer_address | bytes | [Address](../core/data_structures.md#address) of the validator that is creating the proposal. | 8 |
| Name | Type | Description | Field Number |
|----------------------|-------------------------------------------------|-----------------------------------------------------------------------------------------------|--------------|
| max_tx_bytes | int64 | Currently configured maximum size in bytes taken by the modified transactions. | 1 |
| txs | repeated bytes | Preliminary list of transactions that have been picked as part of the block to propose. | 2 |
| local_last_commit | [ExtendedCommitInfo](#extendedcommitinfo) | Info about the last commit, obtained locally from Tendermint's data structures. | 3 |
| misbehavior | repeated [Misbehavior](#misbehavior) | List of information about validators that misbehaved. | 4 |
| height | int64 | The height of the block that will be proposed. | 5 |
| time | [google.protobuf.Timestamp][protobuf-timestamp] | Timestamp of the block that that will be proposed. | 6 |
| next_validators_hash | bytes | Merkle root of the next validator set. | 7 |
| proposer_address | bytes | [Address](../core/data_structures.md#address) of the validator that is creating the proposal. | 8 |
* **Response**:
@@ -302,92 +326,128 @@ title: Methods
| app_hash | bytes | The Merkle root hash of the application state. | 3 |
| tx_results | repeated [ExecTxResult](#exectxresult) | List of structures containing the data resulting from executing the transactions | 4 |
| validator_updates | repeated [ValidatorUpdate](#validatorupdate) | Changes to validator set (set voting power to 0 to remove). | 5 |
| consensus_param_updates | [ConsensusParams](#consensusparams) | Changes to consensus-critical gas, size, and other parameters. | 6 |
| consensus_param_updates | [ConsensusParams](#consensusparams) | Changes to gas, size, and other consensus-related parameters. | 6 |
* **Usage**:
* The first six parameters of `RequestPrepareProposal` are the same as `RequestProcessProposal`
* `RequestPrepareProposal`'s parameters `txs`, `misbehavior`, `height`, `time`,
`next_validators_hash`, and `proposer_address` are the same as in `RequestProcessProposal`
and `RequestFinalizeBlock`.
* The height and time values match the values from the header of the proposed block.
* `RequestPrepareProposal` contains a preliminary set of transactions `txs` that Tendermint considers to be a good block proposal, called _raw proposal_. The Application can modify this set via `ResponsePrepareProposal.tx_records` (see [TxRecord](#txrecord)).
* The Application _can_ reorder, remove or add transactions to the raw proposal. Let `tx` be a transaction in `txs`:
* If the Application considers that `tx` should not be proposed in this block, e.g., there are other transactions with higher priority, then it should not include it in `tx_records`. In this case, Tendermint won't remove `tx` from the mempool. The Application should be extra-careful, as abusing this feature may cause transactions to stay forever in the mempool.
* If the Application considers that a `tx` should not be included in the proposal and removed from the mempool, then the Application should include it in `tx_records` and _mark_ it as `REMOVED`. In this case, Tendermint will remove `tx` from the mempool.
* If the Application wants to add a new transaction, then the Application should include it in `tx_records` and _mark_ it as `ADD`. In this case, Tendermint will add it to the mempool.
* The Application should be aware that removing and adding transactions may compromise _traceability_.
> Consider the following example: the Application transforms a client-submitted transaction `t1` into a second transaction `t2`, i.e., the Application asks Tendermint to remove `t1` and add `t2` to the mempool. If a client wants to eventually check what happened to `t1`, it will discover that `t_1` is not in the mempool or in a committed block, getting the wrong idea that `t_1` did not make it into a block. Note that `t_2` _will be_ in a committed block, but unless the Application tracks this information, no component will be aware of it. Thus, if the Application wants traceability, it is its responsability to support it. For instance, the Application could attach to a transformed transaction a list with the hashes of the transactions it derives from.
* Tendermint MAY include a list of transactions in `RequestPrepareProposal.txs` whose total size in bytes exceeds `RequestPrepareProposal.max_tx_bytes`.
Therefore, if the size of `RequestPrepareProposal.txs` is greater than `RequestPrepareProposal.max_tx_bytes`, the Application MUST make sure that the
`RequestPrepareProposal.max_tx_bytes` limit is respected by those transaction records returned in `ResponsePrepareProposal.tx_records` that are marked as `UNMODIFIED` or `ADDED`.
* In same-block execution mode, the Application must provide values for `ResponsePrepareProposal.app_hash`,
`ResponsePrepareProposal.tx_results`, `ResponsePrepareProposal.validator_updates`, and
* `RequestPrepareProposal.local_last_commit` is a set of the precommit votes that allowed the
decision of the previous block, together with their corresponding vote extensions.
* The `height`, `time`, and `proposer_address` values match the values from the header of the
proposed block.
* `RequestPrepareProposal` contains a preliminary set of transactions `txs` that Tendermint
retrieved from the mempool, called _raw proposal_. The Application can modify this
set via `ResponsePrepareProposal.tx_records` (see [TxRecord](#txrecord)).
* The Application _can_ modify the raw proposal: it can reorder, remove or add transactions.
Let `tx` be a transaction in `txs`:
* If the Application considers that `tx` should not be proposed in this block, e.g.,
there are other transactions with higher priority, then it should not include it in
`tx_records`. In this case, Tendermint will not remove `tx` from the mempool. The
Application should be extra-careful, as abusing this feature may cause transactions
to stay much longer than needed in the mempool.
* If the Application considers that `tx` should not be included in the proposal and
removed from the mempool, then the Application should include it in `tx_records` and
_mark_ it as `REMOVED`. In this case, Tendermint will remove `tx` from the mempool.
* If the Application wants to add a new transaction to the proposed block, then the
Application includes it in `tx_records` and _marks_ it as `ADDED`. In this case, Tendermint
will also add the transaction to the mempool.
* The Application should be aware that removing and adding transactions may compromise
_traceability_.
> Consider the following example: the Application transforms a client-submitted
transaction `t1` into a second transaction `t2`, i.e., the Application asks Tendermint
to remove `t1` and add `t2` to the mempool. If a client wants to eventually check what
happened to `t1`, it will discover that `t1` is neither in the mempool nor in a
committed block, getting the wrong idea that `t1` did not make it into a block. Note
that `t2` _will be_ in a committed block, but unless the Application tracks this
information, no component will be aware of it. Thus, if the Application wants
traceability, it is its responsability to support it. For instance, the Application
could attach to a transformed transaction a list with the hashes of the transactions it
derives from.
* Tendermint MAY include a list of transactions in `RequestPrepareProposal.txs` whose total
size in bytes exceeds `RequestPrepareProposal.max_tx_bytes`.
Therefore, if the size of `RequestPrepareProposal.txs` is greater than
`RequestPrepareProposal.max_tx_bytes`, the Application MUST remove transactions to ensure
that the `RequestPrepareProposal.max_tx_bytes` limit is respected by those transaction
records returned in `ResponsePrepareProposal.tx_records` that are marked as `UNMODIFIED` or
`ADDED`.
* In same-block execution mode, the Application must provide values for
`ResponsePrepareProposal.app_hash`, `ResponsePrepareProposal.tx_results`,
`ResponsePrepareProposal.validator_updates`, and
`ResponsePrepareProposal.consensus_param_updates`, as a result of fully executing the block.
* The values for `ResponsePrepareProposal.validator_updates`, or
`ResponsePrepareProposal.consensus_param_updates` may be empty. In this case, Tendermint will keep
the current values.
* `ResponsePrepareProposal.validator_updates`, triggered by block `H`, affect validation
for blocks `H+1`, and `H+2`. Heights following a validator update are affected in the following way:
* `H`: `NextValidatorsHash` includes the new `validator_updates` value.
* `H+1`: The validator set change takes effect and `ValidatorsHash` is updated.
* `H+2`: `local_last_commit` now includes the altered validator set.
* Height `H`: `NextValidatorsHash` includes the new `validator_updates` value.
* Height `H+1`: The validator set change takes effect and `ValidatorsHash` is updated.
* Height `H+2`: `*_last_commit` fields in `PrepareProposal`, `ProcessProposal`, and
`FinalizeBlock` now include the altered validator set.
* `ResponseFinalizeBlock.consensus_param_updates` returned for block `H` apply to the consensus
params for block `H+1` even if the change is agreed in block `H`.
For more information on the consensus parameters,
see the [application spec entry on consensus parameters](../abci/apps.md#consensus-parameters).
* It is the responsibility of the Application to set the right value for _TimeoutPropose_ so that
see the [consensus parameters](./abci%2B%2B_app_requirements.md#consensus-parameters)
section.
* It is the Application's responsibility to set the right value for _TimeoutPropose_ so that
the (synchronous) execution of the block does not cause other processes to prevote `nil` because
their propose timeout goes off.
* In next-block execution mode, Tendermint will ignore parameters `ResponsePrepareProposal.tx_results`,
* In next-block execution mode, Tendermint will ignore parameters
`ResponsePrepareProposal.app_hash`, `ResponsePrepareProposal.tx_results`,
`ResponsePrepareProposal.validator_updates`, and `ResponsePrepareProposal.consensus_param_updates`.
* As a result of executing the prepared proposal, the Application may produce header events or transaction events.
* As a result of executing the prepared proposal, the Application may produce block events or transaction events.
The Application must keep those events until a block is decided and then pass them on to Tendermint via
`ResponseFinalizeBlock`.
* Likewise, in next-block execution mode, the Application must keep all responses to executing transactions
until it can call `ResponseFinalizeBlock`.
* Likewise, in next-block execution mode, the Application must keep all responses to executing
transactions until it can call `ResponseFinalizeBlock`.
* As a sanity check, Tendermint will check the returned parameters for validity if the Application modified them.
In particular, `ResponsePrepareProposal.tx_records` will be deemed invalid if
* There is a duplicate transaction in the list.
* A new or modified transaction is marked as `UNMODIFIED` or `REMOVED`.
* An unmodified transaction is marked as `ADDED`.
* A new transaction is marked as `UNMODIFIED` or `REMOVED`.
* An existing transaction is marked as `ADDED`.
* A transaction is marked as `UNKNOWN`.
* If Tendermint fails to validate the `ResponsePrepareProposal`, Tendermint will assume the application is faulty and crash.
* If Tendermint fails to validate the `ResponsePrepareProposal`, Tendermint will assume the
Application is faulty and crash.
* The implementation of `PrepareProposal` can be non-deterministic.
#### When does Tendermint call it?
#### When does Tendermint call `PrepareProposal`?
When a validator _p_ enters Tendermint consensus round _r_, height _h_, in which _p_ is the proposer,
and _p_'s _validValue_ is `nil`:
1. _p_'s Tendermint collects outstanding transactions from the mempool
* The transactions will be collected in order of priority
* Let $C$ the list of currently collected transactions
* The collection stops when any of the following conditions are met
* the mempool is empty
* the total size of transactions $\in C$ is greater than or equal to `consensusParams.block.max_bytes`
* the sum of `GasWanted` field of transactions $\in C$ is greater than or equal to
`consensusParams.block.max_gas`
1. Tendermint collects outstanding transactions from _p_'s mempool
* the transactions will be collected in order of priority
* _p_'s Tendermint creates a block header.
2. _p_'s Tendermint calls `RequestPrepareProposal` with the newly generated block.
The call is synchronous: Tendermint's execution will block until the Application returns from the call.
3. The Application checks the block (hashes, transactions, commit info, misbehavior). Besides,
* in same-block execution mode, the Application can (and should) provide `ResponsePrepareProposal.app_hash`,
`ResponsePrepareProposal.validator_updates`, or
2. _p_'s Tendermint calls `RequestPrepareProposal` with the newly generated block, the local
commit of the previous height (with vote extensions), and any outstanding evidence of
misbehavior. The call is synchronous: Tendermint's execution will block until the Application
returns from the call.
3. The Application uses the information received (transactions, commit info, misbehavior, time) to
(potentially) modify the proposal.
* in same-block execution mode, the Application fully executes the block and provides values
for `ResponsePrepareProposal.app_hash`, `ResponsePrepareProposal.tx_results`,
`ResponsePrepareProposal.validator_updates`, and
`ResponsePrepareProposal.consensus_param_updates`.
* in "next-block execution" mode, _p_'s Tendermint will ignore the values for `ResponsePrepareProposal.app_hash`,
`ResponsePrepareProposal.validator_updates`, and `ResponsePrepareProposal.consensus_param_updates`.
* in both modes, the Application can manipulate transactions
* in next-block execution mode, _p_'s Tendermint will ignore the values for
`ResponsePrepareProposal.app_hash`, `ResponsePrepareProposal.tx_results`,
`ResponsePrepareProposal.validator_updates`, and
`ResponsePrepareProposal.consensus_param_updates`.
* in both modes, the Application can manipulate transactions:
* leave transactions untouched - `TxAction = UNMODIFIED`
* add new transactions directly to the proposal - `TxAction = ADDED`
* remove transactions (invalid) from the proposal and from the mempool - `TxAction = REMOVED`
* add new transactions (not present initially) to the proposal - `TxAction = ADDED`
* remove (invalid) transactions from the proposal and from the mempool - `TxAction = REMOVED`
* remove transactions from the proposal but not from the mempool (effectively _delaying_ them) - the
Application removes the transaction from the list
* modify transactions (e.g. aggregate them) - `TxAction = ADDED` followed by `TxAction = REMOVED`. As explained above, this compromises client traceability, unless it is implemented at the Application level.
Application does not include the transaction in `ResponsePrepareProposal.tx_records`
* modify transactions (e.g. aggregate them) - `TxAction = ADDED` followed by
`TxAction = REMOVED`. As explained above, this compromises client traceability, unless
it is implemented at the Application level.
* reorder transactions - the Application reorders transactions in the list
4. If the block is modified, the Application sets `ResponsePrepareProposal.modified` to true,
and includes the modified block in the return parameters (see the rules in section _Usage_).
The Application returns from the call.
4. The Application includes the transaction list (whether modified or not) in the return parameters
(see the rules in section _Usage_), and returns from the call.
5. _p_'s Tendermint uses the (possibly) modified block as _p_'s proposal in round _r_, height _h_.
Note that, if _p_ has a non-`nil` _validValue_, Tendermint will use it as proposal and will not call `RequestPrepareProposal`.
Note that, if _p_ has a non-`nil` _validValue_ in round _r_, height _h_, Tendermint will use it as
proposal and will not call `RequestPrepareProposal`.
### ProcessProposal
@@ -395,16 +455,16 @@ Note that, if _p_ has a non-`nil` _validValue_, Tendermint will use it as propos
* **Request**:
| Name | Type | Description | Field Number |
|----------------------|---------------------------------------------|----------------------------------------------------------------------------------------------------------------|--------------|
| txs | repeated bytes | List of transactions that have been picked as part of the proposed block. | 1 |
| proposed_last_commit | [CommitInfo](#commitinfo) | Info about the last commit, obtained from the information in the proposed block. | 2 |
| byzantine_validators | repeated [Misbehavior](#misbehavior) | List of information about validators that acted incorrectly. | 3 |
| hash | bytes | The block header's hash of the proposed block. | 4 |
| height | int64 | The height of the proposed block. | 5 |
| time | [google.protobuf.Timestamp](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#google.protobuf.Timestamp) | Timestamp included in the proposed block. | 6 |
| next_validators_hash | bytes | Merkle root of the next validator set. | 7 |
| proposer_address | bytes | [Address](../core/data_structures.md#address) of the validator that created the proposal. | 8 |
| Name | Type | Description | Field Number |
|----------------------|-------------------------------------------------|-------------------------------------------------------------------------------------------|--------------|
| txs | repeated bytes | List of transactions of the proposed block. | 1 |
| proposed_last_commit | [CommitInfo](#commitinfo) | Info about the last commit, obtained from the information in the proposed block. | 2 |
| misbehavior | repeated [Misbehavior](#misbehavior) | List of information about validators that misbehaved. | 3 |
| hash | bytes | The hash of the proposed block. | 4 |
| height | int64 | The height of the proposed block. | 5 |
| time | [google.protobuf.Timestamp][protobuf-timestamp] | Timestamp of the proposed block. | 6 |
| next_validators_hash | bytes | Merkle root of the next validator set. | 7 |
| proposer_address | bytes | [Address](../core/data_structures.md#address) of the validator that created the proposal. | 8 |
* **Response**:
@@ -414,14 +474,19 @@ Note that, if _p_ has a non-`nil` _validValue_, Tendermint will use it as propos
| app_hash | bytes | The Merkle root hash of the application state. | 2 |
| tx_results | repeated [ExecTxResult](#exectxresult) | List of structures containing the data resulting from executing the transactions. | 3 |
| validator_updates | repeated [ValidatorUpdate](#validatorupdate) | Changes to validator set (set voting power to 0 to remove). | 4 |
| consensus_param_updates | [ConsensusParams](#consensusparams) | Changes to consensus-critical gas, size, and other parameters. | 5 |
| consensus_param_updates | [ConsensusParams](#consensusparams) | Changes to gas, size, and other consensus-related parameters. | 5 |
* **Usage**:
* Contains fields from the proposed block.
* The Application may fully execute the block as though it was handling `RequestFinalizeBlock`.
However, any resulting state changes must be kept as _candidate state_,
and the Application should be ready to backtrack/discard it in case the decided block is different.
* The height and timestamp values match the values from the header of the proposed block.
* Contains all information on the proposed block needed to fully execute it.
* The Application may fully execute the block as though it was handling
`RequestFinalizeBlock`.
* However, any resulting state changes must be kept as _candidate state_,
and the Application should be ready to discard it in case another block is decided.
* `RequestProcessProposal` is also called at the proposer of a round. The reason for this is to
inform the Application of the block header's hash, which cannot be done at `PrepareProposal`
time. In this case, the call to `RequestProcessProposal` occurs right after the call to
`RequestPrepareProposal`.
* The height and time values match the values from the header of the proposed block.
* If `ResponseProcessProposal.status` is `REJECT`, Tendermint assumes the proposal received
is not valid.
* In same-block execution mode, the Application is required to fully execute the block and provide values
@@ -430,35 +495,39 @@ Note that, if _p_ has a non-`nil` _validValue_, Tendermint will use it as propos
so that Tendermint can then verify the hashes in the block's header are correct.
If the hashes mismatch, Tendermint will reject the block even if `ResponseProcessProposal.status`
was set to `ACCEPT`.
* In next-block execution mode, the Application should *not* provide values for parameters
* In next-block execution mode, the Application should _not_ provide values for parameters
`ResponseProcessProposal.app_hash`, `ResponseProcessProposal.tx_results`,
`ResponseProcessProposal.validator_updates`, and `ResponseProcessProposal.consensus_param_updates`.
* The implementation of `ProcessProposal` MUST be deterministic. Moreover, the value of
`ResponseProcessProposal.status` MUST **exclusively** depend on the parameters passed in
the call to `RequestProcessProposal`, and the last committed Application state
(see [Requirements](abci++_app_requirements_002_draft.md) section).
(see [Requirements](./abci++_app_requirements.md) section).
* Moreover, application implementors SHOULD always set `ResponseProcessProposal.status` to `ACCEPT`,
unless they _really_ know what the potential liveness implications of returning `REJECT` are.
#### When does Tendermint call it?
#### When does Tendermint call `ProcessProposal`?
When a validator _p_ enters Tendermint consensus round _r_, height _h_, in which _q_ is the proposer (possibly _p_ = _q_):
1. _p_ sets up timer `ProposeTimeout`.
2. If _p_ is the proposer, _p_ executes steps 1-6 in [PrepareProposal](#prepareproposal).
3. Upon reception of Proposal message (which contains the header) for round _r_, height _h_ from _q_, _p_'s Tendermint verifies the block header.
4. Upon reception of Proposal message, along with all the block parts, for round _r_, height _h_ from _q_, _p_'s Tendermint follows its algorithm
to check whether it should prevote for the block just received, or `nil`
5. If Tendermint should prevote for the block just received
3. Upon reception of Proposal message (which contains the header) for round _r_, height _h_ from
_q_, _p_'s Tendermint verifies the block header.
4. Upon reception of Proposal message, along with all the block parts, for round _r_, height _h_
from _q_, _p_'s Tendermint follows its algorithm to check whether it should prevote for the
proposed block, or `nil`.
5. If Tendermint should prevote for the proposed block:
1. Tendermint calls `RequestProcessProposal` with the block. The call is synchronous.
2. The Application checks/processes the proposed block, which is read-only, and returns true (_accept_) or false (_reject_) in `ResponseProcessProposal.accept`.
2. The Application checks/processes the proposed block, which is read-only, and returns
`ACCEPT` or `REJECT` in the `ResponseProcessProposal.status` field.
* The Application, depending on its needs, may call `ResponseProcessProposal`
* either after it has completely processed the block (the simpler case),
* or immediately (after doing some basic checks), and process the block asynchronously. In this case the Application will
not be able to reject the block, or force prevote/precommit `nil` afterwards.
* either after it has completely processed the block (immediate execution),
* or after doing some basic checks, and process the block asynchronously. In this case the
Application will not be able to reject the block, or force prevote/precommit `nil`
afterwards.
3. If the returned value is
* _accept_, Tendermint prevotes on this proposal for round _r_, height _h_.
* _reject_, Tendermint prevotes `nil`.
* `ACCEPT`: Tendermint prevotes on this proposal for round _r_, height _h_.
* `REJECT`: Tendermint prevotes `nil`.
### ExtendVote
@@ -473,20 +542,21 @@ When a validator _p_ enters Tendermint consensus round _r_, height _h_, in which
* **Response**:
| Name | Type | Description | Field Number |
|-------------------|-------|-----------------------------------------------|--------------|
| vote_extension | bytes | Optional information signed by by Tendermint. | 1 |
| Name | Type | Description | Field Number |
|-------------------|-------|---------------------------------------------------------|--------------|
| vote_extension | bytes | Information signed by by Tendermint. Can have 0 length. | 1 |
* **Usage**:
* `ResponseExtendVote.vote_extension` is optional information that, if present, will be signed by Tendermint and
attached to the Precommit message.
* `RequestExtendVote.hash` corresponds to the hash of a proposed block that was made available to the application
in a previous call to `ProcessProposal` or `PrepareProposal` for the current height.
* `ResponseExtendVote.vote_extension` is application-generated information that will be signed
by Tendermint and attached to the Precommit message.
* The Application may choose to use an empty vote extension (0 length).
* `RequestExtendVote.hash` corresponds to the hash of a proposed block that was made available
to the Application in a previous call to `ProcessProposal` for the current height.
* `ResponseExtendVote.vote_extension` will only be attached to a non-`nil` Precommit message. If Tendermint is to
precommit `nil`, it will not call `RequestExtendVote`.
* The Application logic that creates the extension can be non-deterministic.
#### When does Tendermint call it?
#### When does Tendermint call `ExtendVote`?
When a validator _p_ is in Tendermint consensus state _prevote_ of round _r_, height _h_, in which _q_ is the proposer; and _p_ has received
@@ -497,7 +567,7 @@ then _p_'s Tendermint locks _v_ and sends a Precommit message in the following
1. _p_'s Tendermint sets _lockedValue_ and _validValue_ to _v_, and sets _lockedRound_ and _validRound_ to _r_
2. _p_'s Tendermint calls `RequestExtendVote` with _id(v)_ (`RequestExtendVote.hash`). The call is synchronous.
3. The Application optionally returns an array of bytes, `ResponseExtendVote.extension`, which is not interpreted by Tendermint.
3. The Application returns an array of bytes, `ResponseExtendVote.extension`, which is not interpreted by Tendermint.
4. _p_'s Tendermint includes `ResponseExtendVote.extension` in a field of type [CanonicalVoteExtension](#canonicalvoteextension),
it then populates the other fields in [CanonicalVoteExtension](#canonicalvoteextension), and signs the populated
data structure.
@@ -516,12 +586,12 @@ a [CanonicalVoteExtension](#canonicalvoteextension) field in the `precommit nil`
* **Request**:
| Name | Type | Description | Field Number |
|-------------------|-------|------------------------------------------------------------------------------------------|--------------|
| hash | bytes | The header hash of the propsed block that the vote extension refers to. | 1 |
| validator_address | bytes | [Address](../core/data_structures.md#address) of the validator that signed the extension | 2 |
| height | int64 | Height of the block (for sanity check). | 3 |
| vote_extension | bytes | Application-specific information signed by Tendermint. Can have 0 length | 4 |
| Name | Type | Description | Field Number |
|-------------------|-------|-------------------------------------------------------------------------------------------|--------------|
| hash | bytes | The hash of the proposed block that the vote extension refers to. | 1 |
| validator_address | bytes | [Address](../core/data_structures.md#address) of the validator that signed the extension. | 2 |
| height | int64 | Height of the block (for sanity check). | 3 |
| vote_extension | bytes | Application-specific information signed by Tendermint. Can have 0 length. | 4 |
* **Response**:
@@ -530,33 +600,38 @@ a [CanonicalVoteExtension](#canonicalvoteextension) field in the `precommit nil`
| status | [VerifyStatus](#verifystatus) | `enum` signaling if the application accepts the vote extension | 1 |
* **Usage**:
* `RequestVerifyVoteExtension.vote_extension` can be an empty byte array. The Application's interpretation of it should be
* `RequestVerifyVoteExtension.vote_extension` can be an empty byte array. The Application's
interpretation of it should be
that the Application running at the process that sent the vote chose not to extend it.
Tendermint will always call `RequestVerifyVoteExtension`, even for 0 length vote extensions.
* `RequestVerifyVoteExtension` is not called for precommit votes sent by the local process.
* `RequestVerifyVoteExtension.hash` refers to a proposed block. There is not guarantee that
this proposed block has previously been exposed to the Application via `ProcessProposal`.
* If `ResponseVerifyVoteExtension.status` is `REJECT`, Tendermint will reject the whole received vote.
See the [Requirements](abci++_app_requirements_002_draft.md) section to understand the potential
See the [Requirements](./abci++_app_requirements.md) section to understand the potential
liveness implications of this.
* The implementation of `VerifyVoteExtension` MUST be deterministic. Moreover, the value of
`ResponseVerifyVoteExtension.status` MUST **exclusively** depend on the parameters passed in
the call to `RequestVerifyVoteExtension`, and the last committed Application state
(see [Requirements](abci++_app_requirements_002_draft.md) section).
(see [Requirements](./abci++_app_requirements.md) section).
* Moreover, application implementers SHOULD always set `ResponseVerifyVoteExtension.status` to `ACCEPT`,
unless they _really_ know what the potential liveness implications of returning `REJECT` are.
#### When does Tendermint call it?
#### When does Tendermint call `VerifyVoteExtension`?
When a validator _p_ is in Tendermint consensus round _r_, height _h_, state _prevote_ (**TODO** discuss: I think I must remove the state
from this condition, but not sure), and _p_ receives a Precommit message for round _r_, height _h_ from _q_:
When a node _p_ is in Tendermint consensus round _r_, height _h_, and _p_ receives a Precommit
message for round _r_, height _h_ from validator _q_ (_q_ &ne; _p_):
1. If the Precommit message does not contain a vote extension with a valid signature, Tendermint discards the message as invalid.
1. If the Precommit message does not contain a vote extension with a valid signature, Tendermint
discards the Precommit message as invalid.
* a 0-length vote extension is valid as long as its accompanying signature is also valid.
2. Else, _p_'s Tendermint calls `RequestVerifyVoteExtension`.
3. The Application returns _accept_ or _reject_ via `ResponseVerifyVoteExtension.status`.
3. The Application returns `ACCEPT` or `REJECT` via `ResponseVerifyVoteExtension.status`.
4. If the Application returns
* _accept_, _p_'s Tendermint will keep the received vote, together with its corresponding
* `ACCEPT`, _p_'s Tendermint will keep the received vote, together with its corresponding
vote extension in its internal data structures. It will be used to populate the [ExtendedCommitInfo](#extendedcommitinfo)
structure in calls to `RequestPrepareProposal`, in rounds of height _h + 1_ where _p_ is the proposer.
* _reject_, _p_'s Tendermint will deem the Precommit message invalid and discard it.
* `REJECT`, _p_'s Tendermint will deem the Precommit message invalid and discard it.
### FinalizeBlock
@@ -564,38 +639,38 @@ from this condition, but not sure), and _p_ receives a Precommit message for rou
* **Request**:
| Name | Type | Description | Field Number |
|----------------------|---------------------------------------------|------------------------------------------------------------------------------------------|--------------|
| txs | repeated bytes | List of transactions committed as part of the block. | 1 |
| decided_last_commit | [CommitInfo](#commitinfo) | Info about the last commit, obtained from the block that was just decided. | 2 |
| byzantine_validators | repeated [Misbehavior](#misbehavior) | List of information about validators that acted incorrectly. | 3 |
| hash | bytes | The block header's hash. Present for convenience (can be derived from the block header). | 4 |
| height | int64 | The height of the finalized block. | 5 |
| time | [google.protobuf.Timestamp](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#google.protobuf.Timestamp) | Timestamp included in the finalized block. | 6 |
| next_validators_hash | bytes | Merkle root of the next validator set. | 7 |
| proposer_address | bytes | [Address](../core/data_structures.md#address) of the validator that created the proposal.| 8 |
| Name | Type | Description | Field Number |
|----------------------|-------------------------------------------------|-------------------------------------------------------------------------------------------|--------------|
| txs | repeated bytes | List of transactions committed as part of the block. | 1 |
| decided_last_commit | [CommitInfo](#commitinfo) | Info about the last commit, obtained from the block that was just decided. | 2 |
| misbehavior | repeated [Misbehavior](#misbehavior) | List of information about validators that misbehaved. | 3 |
| hash | bytes | The block's hash. | 4 |
| height | int64 | The height of the finalized block. | 5 |
| time | [google.protobuf.Timestamp][protobuf-timestamp] | Timestamp of the finalized block. | 6 |
| next_validators_hash | bytes | Merkle root of the next validator set. | 7 |
| proposer_address | bytes | [Address](../core/data_structures.md#address) of the validator that created the proposal. | 8 |
* **Response**:
| Name | Type | Description | Field Number |
|-------------------------|-------------------------------------------------------------|----------------------------------------------------------------------------------|--------------|
| events | repeated [Event](abci++_basic_concepts_002_draft.md#events) | Type & Key-Value events for indexing | 1 |
| events | repeated [Event](abci++_basic_concepts.md#events) | Type & Key-Value events for indexing | 1 |
| tx_results | repeated [ExecTxResult](#exectxresult) | List of structures containing the data resulting from executing the transactions | 2 |
| validator_updates | repeated [ValidatorUpdate](#validatorupdate) | Changes to validator set (set voting power to 0 to remove). | 3 |
| consensus_param_updates | [ConsensusParams](#consensusparams) | Changes to consensus-critical gas, size, and other parameters. | 4 |
| consensus_param_updates | [ConsensusParams](#consensusparams) | Changes to gas, size, and other consensus-related parameters. | 4 |
| app_hash | bytes | The Merkle root hash of the application state. | 5 |
| retain_height | int64 | Blocks below this height may be removed. Defaults to `0` (retain all). | 6 |
* **Usage**:
* Contains the fields of the newly decided block.
* This method is equivalent to the call sequence `BeginBlock`, [`DeliverTx`],
`EndBlock`, `Commit` in the previous version of ABCI.
* The height and timestamp values match the values from the header of the proposed block.
* The Application can use `RequestFinalizeBlock.decided_last_commit` and `RequestFinalizeBlock.byzantine_validators`
and `EndBlock` in the previous version of ABCI.
* The height and time values match the values from the header of the proposed block.
* The Application can use `RequestFinalizeBlock.decided_last_commit` and `RequestFinalizeBlock.misbehavior`
to determine rewards and punishments for the validators.
* The application must execute the transactions in full, in the order they appear in `RequestFinalizeBlock.txs`,
before returning control to Tendermint. Alternatively, it can commit the candidate state corresponding to the same block
previously executed via `PrepareProposal` or `ProcessProposal`.
* The Application executes the transactions in `RequestFinalizeBlock.txs` deterministically,
according to the rules set up by the Application, before returning control to Tendermint.
Alternatively, it can apply the candidate state corresponding to the same block previously
executed via `PrepareProposal` or `ProcessProposal`.
* `ResponseFinalizeBlock.tx_results[i].Code == 0` only if the _i_-th transaction is fully valid.
* In next-block execution mode, the Application must provide values for `ResponseFinalizeBlock.app_hash`,
`ResponseFinalizeBlock.tx_results`, `ResponseFinalizeBlock.validator_updates`, and
@@ -605,16 +680,18 @@ from this condition, but not sure), and _p_ receives a Precommit message for rou
the current values.
* `ResponseFinalizeBlock.validator_updates`, triggered by block `H`, affect validation
for blocks `H+1`, `H+2`, and `H+3`. Heights following a validator update are affected in the following way:
- Height `H+1`: `NextValidatorsHash` includes the new `validator_updates` value.
- Height `H+2`: The validator set change takes effect and `ValidatorsHash` is updated.
- Height `H+3`: `decided_last_commit` now includes the altered validator set.
* Height `H+1`: `NextValidatorsHash` includes the new `validator_updates` value.
* Height `H+2`: The validator set change takes effect and `ValidatorsHash` is updated.
* Height `H+3`: `*_last_commit` fields in `PrepareProposal`, `ProcessProposal`, and
`FinalizeBlock` now include the altered validator set.
* `ResponseFinalizeBlock.consensus_param_updates` returned for block `H` apply to the consensus
params for block `H+1`. For more information on the consensus parameters,
see the [application spec entry on consensus parameters](../abci/apps.md#consensus-parameters).
see the [consensus parameters](./abci%2B%2B_app_requirements.md#consensus-parameters)
section.
* In same-block execution mode, Tendermint will log an error and ignore values for
`ResponseFinalizeBlock.app_hash`, `ResponseFinalizeBlock.tx_results`, `ResponseFinalizeBlock.validator_updates`,
and `ResponsePrepareProposal.consensus_param_updates`, as those must have been provided by `PrepareProposal`.
* Application is expected to persist its state at the end of this call, before calling `ResponseFinalizeBlock`.
* `ResponseFinalizeBlock.app_hash` contains an (optional) Merkle root hash of the application state.
* `ResponseFinalizeBlock.app_hash` is included
* [in next-block execution mode] as the `Header.AppHash` in the next block.
@@ -626,11 +703,7 @@ from this condition, but not sure), and _p_ receives a Precommit message for rou
of `RequestFinalizeBlock` and the previous committed state.
* Later calls to `Query` can return proofs about the application state anchored
in this Merkle root hash.
* Use `ResponseFinalizeBlock.retain_height` with caution! If all nodes in the network remove historical
blocks then this data is permanently lost, and no new nodes will be able to join the network and
bootstrap. Historical blocks may also be required for other purposes, e.g. auditing, replay of
non-persisted heights, light client verification, and so on.
* Just as `ProcessProposal`, the implementation of `FinalizeBlock` MUST be deterministic, since it is
* The implementation of `FinalizeBlock` MUST be deterministic, since it is
making the Application's state evolve in the context of state machine replication.
* Currently, Tendermint will fill up all fields in `RequestFinalizeBlock`, even if they were
already passed on to the Application via `RequestPrepareProposal` or `RequestProcessProposal`.
@@ -638,9 +711,9 @@ from this condition, but not sure), and _p_ receives a Precommit message for rou
(rather than executing the whole block). In this case the Application disregards all parameters in
`RequestFinalizeBlock` except `RequestFinalizeBlock.hash`.
#### When does Tendermint call it?
#### When does Tendermint call `FinalizeBlock`?
When a validator _p_ is in Tendermint consensus height _h_, and _p_ receives
When a node _p_ is in Tendermint consensus height _h_, and _p_ receives
* the Proposal message with block _v_ for a round _r_, along with all its block parts, from _q_,
which is the proposer of round _r_, height _h_,
@@ -649,16 +722,19 @@ When a validator _p_ is in Tendermint consensus height _h_, and _p_ receives
then _p_'s Tendermint decides block _v_ and finalizes consensus for height _h_ in the following way
1. _p_'s Tendermint persists _v_ as decision for height _h_.
2. _p_'s Tendermint locks the mempool -- no calls to checkTx on new transactions.
3. _p_'s Tendermint calls `RequestFinalizeBlock` with _id(v)_. The call is synchronous.
4. _p_'s Application processes block _v_, received in a previous call to `RequestProcessProposal`.
5. _p_'s Application commits and persists the state resulting from processing the block.
6. _p_'s Application calculates and returns the _AppHash_, along with an array of arrays of bytes representing the output of each of the transactions
7. _p_'s Tendermint hashes the array of transaction outputs and stores it in _ResultHash_
8. _p_'s Tendermint persists _AppHash_ and _ResultHash_
9. _p_'s Tendermint unlocks the mempool -- newly received transactions can now be checked.
10. _p_'s starts consensus for a new height _h+1_, round 0
1. _p_'s Tendermint persists _v_ as the decision for height _h_.
2. _p_'s Tendermint calls `RequestFinalizeBlock` with _v_'s data. The call is synchronous.
3. _p_'s Application executes block _v_.
4. _p_'s Application calculates and returns the _AppHash_, along with a list containing
the outputs of each of the transactions executed.
5. _p_'s Tendermint hashes all the transaction outputs and stores it in _ResultHash_.
6. _p_'s Tendermint persists the transaction outputs, _AppHash_, and _ResultsHash_.
7. _p_'s Tendermint locks the mempool &mdash; no calls to `CheckTx` on new transactions.
8. _p_'s Tendermint calls `RequestCommit` to instruct the Application to persist its state.
9. _p_'s Tendermint, optionally, re-checks all outstanding transactions in the mempool
against the newly persisted Application state.
10. _p_'s Tendermint unlocks the mempool &mdash; newly received transactions can now be checked.
11. _p_'s starts consensus for height _h+1_, round 0
## Data Types existing in ABCI
@@ -696,13 +772,13 @@ Most of the data structures used in ABCI are shared [common data structures](../
* **Fields**:
| Name | Type | Description | Field Number |
|--------------------|--------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------|--------------|
| type | [MisbehaviorType](#misbehaviortype) | Type of the misbehavior. An enum of possible misbehaviors. | 1 |
| validator | [Validator](#validator) | The offending validator | 2 |
| height | int64 | Height when the offense occurred | 3 |
| time | [google.protobuf.Timestamp](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#google.protobuf.Timestamp) | Time of the block that was committed at the height that the offense occurred | 4 |
| total_voting_power | int64 | Total voting power of the validator set at height `Height` | 5 |
| Name | Type | Description | Field Number |
|--------------------|-------------------------------------------------|------------------------------------------------------------------------------|--------------|
| type | [MisbehaviorType](#misbehaviortype) | Type of the misbehavior. An enum of possible misbehaviors. | 1 |
| validator | [Validator](#validator) | The offending validator | 2 |
| height | int64 | Height when the offense occurred | 3 |
| time | [google.protobuf.Timestamp][protobuf-timestamp] | Timestamp of the block that was committed at height `height` | 4 |
| total_voting_power | int64 | Total voting power of the validator set at height `height` | 5 |
#### MisbehaviorType
@@ -754,8 +830,8 @@ Most of the data structures used in ABCI are shared [common data structures](../
| height | uint64 | The height at which the snapshot was taken (after commit). | 1 |
| format | uint32 | An application-specific snapshot format, allowing applications to version their snapshot data format and make backwards-incompatible changes. Tendermint does not interpret this. | 2 |
| chunks | uint32 | The number of chunks in the snapshot. Must be at least 1 (even if empty). | 3 |
| hash | bytes | TAn arbitrary snapshot hash. Must be equal only for identical snapshots across nodes. Tendermint does not interpret the hash, it only compares them. | 3 |
| metadata | bytes | Arbitrary application metadata, for example chunk hashes or other verification data. | 3 |
| hash | bytes | An arbitrary snapshot hash. Must be equal only for identical snapshots across nodes. Tendermint does not interpret the hash, it only compares them. | 4 |
| metadata | bytes | Arbitrary application metadata, for example chunk hashes or other verification data. | 5 |
* **Usage**:
* Used for state sync snapshots, see the [state sync section](../p2p/messages/state-sync.md) for details.
@@ -823,7 +899,7 @@ Most of the data structures used in ABCI are shared [common data structures](../
| info | string | Additional information. **May be non-deterministic.** | 4 |
| gas_wanted | int64 | Amount of gas requested for transaction. | 5 |
| gas_used | int64 | Amount of gas consumed by transaction. | 6 |
| events | repeated [Event](abci++_basic_concepts_002_draft.md#events) | Type & Key-Value events for indexing transactions (e.g. by account). | 7 |
| events | repeated [Event](abci++_basic_concepts.md#events) | Type & Key-Value events for indexing transactions (e.g. by account). | 7 |
| codespace | string | Namespace for the `code`. | 8 |
### TxAction
@@ -842,7 +918,7 @@ enum TxAction {
* If `Action` is `UNMODIFIED`, Tendermint includes the transaction in the proposal. Nothing to do on the mempool.
* If `Action` is `ADDED`, Tendermint includes the transaction in the proposal. The transaction is _not_ added to the mempool.
* If `Action` is `REMOVED`, Tendermint excludes the transaction from the proposal. The transaction is also removed from the mempool if it exists,
similar to `CheckTx` returning _false_.
similar to `CheckTx` returning an error code.
### TxRecord
@@ -905,3 +981,5 @@ enum VerifyStatus {
* Tendermint is to sign the whole data structure and attach it to a Precommit message
* Upon reception, Tendermint validates the sender's signature and sanity-checks the values of `height`, `round`, and `chain_id`.
Then it sends `extension` to the Application via `RequestVerifyVoteExtension` for verification.
[protobuf-timestamp]: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#google.protobuf.Timestamp

View File

@@ -11,16 +11,20 @@ This section describes what the Application can expect from Tendermint.
The Tendermint consensus algorithm is designed to protect safety under any network conditions, as long as
less than 1/3 of validators' voting power is byzantine. Most of the time, though, the network will behave
synchronously and there will be no byzantine process. In these frequent, benign conditions:
synchronously, no process will fall behind, and there will be no byzantine process. The following describes
what will happen during a block height _h_ in these frequent, benign conditions:
* Tendermint will decide in round 0;
* Tendermint will decide in round 0, for height _h_;
* `PrepareProposal` will be called exactly once at the proposer process of round 0, height _h_;
* `ProcessProposal` will be called exactly once at all processes except the proposer of round 0, and
* `ProcessProposal` will be called exactly once at all processes, and
will return _accept_ in its `Response*`;
* `ExtendVote` will be called exactly once at all processes
* `VerifyVoteExtension` will be called _n-1_ times at each validator process, where _n_ is the number of validators; and
* `FinalizeBlock` will be finally called at all processes at the end of height _h_, conveying the same prepared
block that all calls to `PrepareProposal` and `ProcessProposal` had previously reported for height _h_.
* `ExtendVote` will be called exactly once at all processes;
* `VerifyVoteExtension` will be called exactly _n-1_ times at each validator process, where _n_ is
the number of validators, and will always return _accept_ in its `Response*`;
* `FinalizeBlock` will be called exactly once at all processes, conveying the same prepared
block that all calls to `PrepareProposal` and `ProcessProposal` had previously reported for
height _h_; and
* `Commit` will finally be called exactly once at all processes at the end of height _h_.
However, the Application logic must be ready to cope with any possible run of Tendermint for a given
height, including bad periods (byzantine proposers, network being asynchronous).
@@ -28,7 +32,7 @@ In these cases, the sequence of calls to ABCI++ methods may not be so straighfor
the Application should still be able to handle them, e.g., without crashing.
The purpose of this section is to define what these sequences look like an a precise way.
As mentioned in the [Basic Concepts](abci++_basic_concepts_002_draft.md) section, Tendermint
As mentioned in the [Basic Concepts](./abci%2B%2B_basic_concepts.md) section, Tendermint
acts as a client of ABCI++ and the Application acts as a server. Thus, it is up to Tendermint to
determine when and in which order the different ABCI++ methods will be called. A well-written
Application design should consider _any_ of these possible sequences.
@@ -46,18 +50,15 @@ state-sync = *state-sync-attempt success-sync info
state-sync-attempt = offer-snapshot *apply-chunk
success-sync = offer-snapshot 1*apply-chunk
recovery = info *consensus-replay consensus-exec
consensus-replay = decide
recovery = info consensus-exec
consensus-exec = (inf)consensus-height
consensus-height = *consensus-round decide
consensus-height = *consensus-round decide commit
consensus-round = proposer / non-proposer
proposer = prepare-proposal extend-proposer
extend-proposer = *got-vote [extend-vote] *got-vote
non-proposer = *got-vote [extend-non-proposer] *got-vote
extend-non-proposer = process-proposal *got-vote [extend-vote]
proposer = *got-vote prepare-proposal *got-vote process-proposal [extend]
extend = *got-vote extend-vote *got-vote
non-proposer = *got-vote [process-proposal] [extend]
init-chain = %s"<InitChain>"
offer-snapshot = %s"<OfferSnapshot>"
@@ -68,12 +69,10 @@ process-proposal = %s"<ProcessProposal>"
extend-vote = %s"<ExtendVote>"
got-vote = %s"<VerifyVoteExtension>"
decide = %s"<FinalizeBlock>"
commit = %s"<Commit>"
```
>**TODO** Still hesitating... introduce _n_ as total number of validators, so that we can bound the occurrences of
>`got-vote` in a round.
We have kept some of the ABCI++ methods out of the grammar, in order to keep it as clear and concise as possible.
We have kept some ABCI methods out of the grammar, in order to keep it as clear and concise as possible.
A common reason for keeping all these methods out is that they all can be called at any point in a sequence defined
by the grammar above. Other reasons depend on the method in question:
@@ -115,7 +114,7 @@ Let us now examine the grammar line by line, providing further details.
* In _state-sync_ mode, Tendermint makes one or more attempts at synchronizing the Application's state.
At the beginning of each attempt, it offers the Application a snapshot found at another process.
If the Application accepts the snapshop, at sequence of calls to `ApplySnapshotChunk` method follow
If the Application accepts the snapshot, a sequence of calls to `ApplySnapshotChunk` method follow
to provide the Application with all the snapshots needed, in order to reconstruct the state locally.
A successful attempt must provide at least one chunk via `ApplySnapshotChunk`.
At the end of a successful attempt, Tendermint calls `Info` to make sure the recontructed state's
@@ -128,12 +127,10 @@ Let us now examine the grammar line by line, providing further details.
>```
* In recovery mode, Tendermint first calls `Info` to know from which height it needs to replay decisions
to the Application. To replay a decision, Tendermint simply calls `FinalizeBlock` with the decided
block at that height. After this, Tendermint enters nomal consensus execution.
to the Application. After this, Tendermint enters nomal consensus execution.
>```abnf
>recovery = info *consensus-replay consensus-exec
>consensus-replay = decide
>recovery = info consensus-exec
>```
* The non-terminal `consensus-exec` is a key point in this grammar. It is an infinite sequence of
@@ -145,33 +142,36 @@ Let us now examine the grammar line by line, providing further details.
>consensus-exec = (inf)consensus-height
>```
* A consensus height consists of zero or more rounds before deciding via a call to `FinalizeBlock`.
In each round, the sequence of method calls depends on whether the local process is the proposer or not.
* A consensus height consists of zero or more rounds before deciding and executing via a call to
`FinalizeBlock`, followed by a call to `Commit`. In each round, the sequence of method calls
depends on whether the local process is the proposer or not. Note that, if a height contains zero
rounds, this means the process is replaying an already decided value (catch-up mode).
>```abnf
>consensus-height = *consensus-round decide
>consensus-height = *consensus-round decide commit
>consensus-round = proposer / non-proposer
>```
* If the local process is the proposer of the current round, Tendermint starts by calling `PrepareProposal`.
No calls to methods related to vote extensions (`ExtendVote`, `VerifyVoteExtension`) can be called
in the present round before `PrepareProposal`. Once `PrepareProposal` is called, calls to
`ExtendVote` and `VerifyVoteExtension` can come in any order, although the former will be called
at most once in this round.
* For every round, if the local process is the proposer of the current round, Tendermint starts by
calling `PrepareProposal`, followed by `ProcessProposal`. Then, optionally, the Application is
asked to extend its vote for that round. Calls to `VerifyVoteExtension` can come at any time: the
local process may be slightly late in the current round, or votes may come from a future round
of this height.
>```abnf
>proposer = prepare-proposal extend-proposer
>extend-proposer = *got-vote [extend-vote] *got-vote
>proposer = *got-vote prepare-proposal *got-vote process-proposal [extend]
>extend = *got-vote extend-vote *got-vote
>```
* If the local process is not the proposer of the current round, Tendermint will call `ProcessProposal`
at most once. At most one call to `ExtendVote` can occur only after `ProcessProposal` is called.
A number of calls to `VerifyVoteExtension` can occur in any order with respect to `ProcessProposal`
and `ExtendVote` throughout the round.
* Also for every round, if the local process is _not_ the proposer of the current round, Tendermint
will call `ProcessProposal` at most once. At most one call to `ExtendVote` may occur only after
`ProcessProposal` is called. A number of calls to `VerifyVoteExtension` can occur in any order
with respect to `ProcessProposal` and `ExtendVote` throughout the round. The reasons are the same
as above, namely, the process running slightly late in the current round, or votes from future
rounds of this height received.
>```abnf
>non-proposer = *got-vote [extend-non-proposer] *got-vote
>extend-non-proposer = process-proposal *got-vote [extend-vote]
>non-proposer = *got-vote [process-proposal] [extend]
>```
* Finally, the grammar describes all its terminal symbols, which denote the different ABCI++ method calls that
@@ -187,6 +187,7 @@ Let us now examine the grammar line by line, providing further details.
>extend-vote = %s"<ExtendVote>"
>got-vote = %s"<VerifyVoteExtension>"
>decide = %s"<FinalizeBlock>"
>commit = %s"<Commit>"
>```
## Adapting existing Applications that use ABCI
@@ -202,17 +203,21 @@ to undergo any changes in their implementation.
As for the new methods:
* `PrepareProposal` must create a list of [TxRecord](./abci++_methods_002_draft.md#txrecord) each containing a
transaction passed in `RequestPrepareProposal.txs`, in the same other. The field `action` must be set to `UNMODIFIED`
for all [TxRecord](./abci++_methods_002_draft.md#txrecord) elements in the list.
* `PrepareProposal` must create a list of [TxRecord](./abci++_methods.md#txrecord) each containing
a transaction passed in `RequestPrepareProposal.txs`, in the same other. The field `action` must
be set to `UNMODIFIED` for all [TxRecord](./abci++_methods.md#txrecord) elements in the list.
The Application must check whether the size of all transactions exceeds the byte limit
(`RequestPrepareProposal.max_tx_bytes`). If so, the Application must remove transactions at the end of the list
until the total byte size is at or below the limit.
(`RequestPrepareProposal.max_tx_bytes`). If so, the Application must remove transactions at the
end of the list until the total byte size is at or below the limit.
* `ProcessProposal` must set `ResponseProcessProposal.accept` to _true_ and return.
* `ExtendVote` is to set `ResponseExtendVote.extension` to an empty byte array and return.
* `VerifyVoteExtension` must set `ResponseVerifyVoteExtension.accept` to _true_ if the extension is an empty byte array
and _false_ otherwise, then return.
* `FinalizeBlock` is to coalesce the implementation of methods `BeginBlock`, `DeliverTx`, `EndBlock`, and `Commit`.
Legacy applications looking to reuse old code that implemented `DeliverTx` should wrap the legacy
`DeliverTx` logic in a loop that executes one transaction iteration per
* `VerifyVoteExtension` must set `ResponseVerifyVoteExtension.accept` to _true_ if the extension is
an empty byte array and _false_ otherwise, then return.
* `FinalizeBlock` is to coalesce the implementation of methods `BeginBlock`, `DeliverTx`, and
`EndBlock`. Legacy applications looking to reuse old code that implemented `DeliverTx` should
wrap the legacy `DeliverTx` logic in a loop that executes one transaction iteration per
transaction in `RequestFinalizeBlock.tx`.
Finally, `Commit`, which is kept in ABCI++, no longer returns the `AppHash`. It is now up to
`FinalizeBlock` to do so. Thus, a slight refactoring of the old `Commit` implementation will be
needed to move the return of `AppHash` to `FinalizeBlock`.

View File

@@ -1,156 +0,0 @@
# Tendermint v0 Markdown pseudocode
This translates the latex code for Tendermint consensus from the Tendermint paper into markdown.
### Initialization
```go
h_p 0
round_p 0
step_p is one of {propose, prevote, precommit}
decision_p Vector()
lockedRound_p -1
lockedValue_p nil
validValue_p nil
validRound_p -1
```
### StartRound(round)
```go
function startRound(round) {
round_p round
step_p propose
if proposer(h_p, round_p) = p {
if validValue_p != nil {
proposal validValue_p
} else {
proposal getValue()
}
broadcast PROPOSAL, h_p, round_p, proposal, validRound_p
} else {
schedule OnTimeoutPropose(h_p,round_p) to be executed after timeoutPropose(round_p)
}
}
```
### ReceiveProposal
In the case where the local node is not locked on any round, the following is ran:
```go
upon PROPOSAL, h_p, round_p, v, 1) from proposer(h_p, round_p) while step_p = propose do {
if valid(v) (lockedRound_p = 1 lockedValue_p = v) {
broadcast PREVOTE, h_p, round_p, id(v)
} else {
broadcast PREVOTE, h_p, round_p, nil
}
step_p prevote
}
```
In the case where the node is locked on a round, the following is ran:
```go
upon PROPOSAL, h_p, round_p, v, vr from proposer(h_p, round_p)
AND 2f + 1 PREVOTE, h_p, vr, id(v)
while step_p = propose (vr 0 vr < round_p) do {
if valid(v) (lockedRound_p vr lockedValue_p = v) {
broadcast PREVOTE, h_p, round_p, id(v)
} else {
broadcast PREVOTE, h_p, round_p, nil
}
step_p prevote
}
```
### Prevote timeout
Upon receiving 2f + 1 prevotes, setup a timeout.
```go
upon 2f + 1 PREVOTE, h_p, vr, * with step_p = prevote for the first time, do {
schedule OnTimeoutPrevote(h_p, round_p) to be executed after timeoutPrevote(round_p)
}
```
with OnTimeoutPrevote defined as:
```go
function OnTimeoutPrevote(height, round) {
if (height = h_p && round = round_p && step_p = prevote) {
broadcast PRECOMMIT, h_p, round_p, nil
step_p precommit
}
}
```
### Receiving enough prevotes to precommit
The following code is ran upon receiving 2f + 1 prevotes for the same block
```go
upon PROPOSAL, h_p, round_p, v, *
from proposer(h_p, round_p)
AND 2f + 1 PREVOTE, h_p, round_p, id(v)
while valid(v) step_p >= prevote for the first time do {
if (step_p = prevote) {
lockedValue_p v
lockedRound_p round_p
broadcast PRECOMMIT, h_p, round_p, id(v)
step_p precommit
}
validValue_p v
validRound_p round_p
}
```
And upon receiving 2f + 1 prevotes for nil:
```go
upon 2f + 1 PREVOTE, h_p, round_p, nil
while step_p = prevote do {
broadcast PRECOMMIT, h_p, round_p, nil
step_p precommit
}
```
### Precommit timeout
Upon receiving 2f + 1 precommits, setup a timeout.
```go
upon 2f + 1 PRECOMMIT, h_p, vr, * for the first time, do {
schedule OnTimeoutPrecommit(h_p, round_p) to be executed after timeoutPrecommit(round_p)
}
```
with OnTimeoutPrecommit defined as:
```go
function OnTimeoutPrecommit(height, round) {
if (height = h_p && round = round_p) {
StartRound(round_p + 1)
}
}
```
### Upon Receiving 2f + 1 precommits
The following code is ran upon receiving 2f + 1 precommits for the same block
```go
upon PROPOSAL, h_p, r, v, *
from proposer(h_p, r)
AND 2f + 1 PRECOMMIT, h_p, r, id(v)
while decision_p[h_p] = nil do {
if (valid(v)) {
decision_p[h_p] v
h_p h_p + 1
reset lockedRound_p, lockedValue_p,validRound_p and validValue_p to initial values
StartRound(0)
}
}
```
If we don't see 2f + 1 precommits for the same block, we wait until we get 2f + 1 precommits, and the timeout occurs.

View File

@@ -1,162 +0,0 @@
# Tendermint v1 Markdown pseudocode
This adds hooks for the existing ABCI to the prior pseudocode
### Initialization
```go
h_p 0
round_p 0
step_p is one of {propose, prevote, precommit}
decision_p Vector()
lockedValue_p nil
validValue_p nil
validRound_p -1
```
### StartRound(round)
```go
function startRound(round) {
round_p round
step_p propose
if proposer(h_p, round_p) = p {
if validValue_p != nil {
proposal validValue_p
} else {
txdata mempool.GetBlock()
// getBlockProposal fills in header
proposal getBlockProposal(txdata)
}
broadcast PROPOSAL, h_p, round_p, proposal, validRound_p
} else {
schedule OnTimeoutPropose(h_p,round_p) to be executed after timeoutPropose(round_p)
}
}
```
### ReceiveProposal
In the case where the local node is not locked on any round, the following is ran:
```go
upon PROPOSAL, h_p, round_p, v, 1) from proposer(h_p, round_p) while step_p = propose do {
if valid(v) (lockedRound_p = 1 lockedValue_p = v) {
broadcast PREVOTE, h_p, round_p, id(v)
} else {
broadcast PREVOTE, h_p, round_p, nil
}
step_p prevote
}
```
In the case where the node is locked on a round, the following is ran:
```go
upon PROPOSAL, h_p, round_p, v, vr
from proposer(h_p, round_p)
AND 2f + 1 PREVOTE, h_p, vr, id(v)
while step_p = propose (vr 0 vr < round_p) do {
if valid(v) (lockedRound_p vr lockedValue_p = v) {
broadcast PREVOTE, h_p, round_p, id(v)
} else {
broadcast PREVOTE, h_p, round_p, nil
}
step_p prevote
}
```
### Prevote timeout
Upon receiving 2f + 1 prevotes, setup a timeout.
```go
upon 2f + 1 PREVOTE, h_p, vr, -1
with step_p = prevote for the first time, do {
schedule OnTimeoutPrevote(h_p, round_p) to be executed after timeoutPrevote(round_p)
}
```
with OnTimeoutPrevote defined as:
```go
function OnTimeoutPrevote(height, round) {
if (height = h_p && round = round_p && step_p = prevote) {
broadcast PRECOMMIT, h_p, round_p, nil
step_p precommit
}
}
```
### Receiving enough prevotes to precommit
The following code is ran upon receiving 2f + 1 prevotes for the same block
```go
upon PROPOSAL, h_p, round_p, v, *
from proposer(h_p, round_p)
AND 2f + 1 PREVOTE, h_p, vr, id(v)
while valid(v) step_p >= prevote for the first time do {
if (step_p = prevote) {
lockedValue_p v
lockedRound_p round_p
broadcast PRECOMMIT, h_p, round_p, id(v)
step_p precommit
}
validValue_p v
validRound_p round_p
}
```
And upon receiving 2f + 1 prevotes for nil:
```go
upon 2f + 1 PREVOTE, h_p, round_p, nil
while step_p = prevote do {
broadcast PRECOMMIT, h_p, round_p, nil
step_p precommit
}
```
### Precommit timeout
Upon receiving 2f + 1 precommits, setup a timeout.
```go
upon 2f + 1 PRECOMMIT, h_p, vr, * for the first time, do {
schedule OnTimeoutPrecommit(h_p, round_p) to be executed after timeoutPrecommit(round_p)
}
```
with OnTimeoutPrecommit defined as:
```go
function OnTimeoutPrecommit(height, round) {
if (height = h_p && round = round_p) {
StartRound(round_p + 1)
}
}
```
### Upon Receiving 2f + 1 precommits
The following code is ran upon receiving 2f + 1 precommits for the same block
```go
upon PROPOSAL, h_p, r, v, *
from proposer(h_p, r)
AND 2f + 1 PRECOMMIT, h_p, r, id(v)
while decision_p[h_p] = nil do {
if (valid(v)) {
decision_p[h_p] v
h_p h_p + 1
reset lockedRound_p, lockedValue_p,validRound_p and validValue_p to initial values
ABCI.BeginBlock(v.header)
ABCI.DeliverTxs(v.data)
ABCI.EndBlock()
StartRound(0)
}
}
```
If we don't see 2f + 1 precommits for the same block, we wait until we get 2f + 1 precommits, and the timeout occurs.

View File

@@ -1,180 +0,0 @@
# Tendermint v2 Markdown pseudocode
This adds a single-threaded implementation of ABCI++,
with no optimization for splitting out verifying the header and verifying the proposal.
### Initialization
```go
h_p 0
round_p 0
step_p is one of {propose, prevote, precommit}
decision_p Vector()
lockedValue_p nil
validValue_p nil
validRound_p -1
```
### StartRound(round)
```go
function startRound(round) {
round_p round
step_p propose
if proposer(h_p, round_p) = p {
if validValue_p != nil {
proposal validValue_p
} else {
txdata mempool.GetBlock()
// getUnpreparedBlockProposal takes tx data, and fills in the unprepared header data
unpreparedProposal getUnpreparedBlockProposal(txdata)
// ABCI++: the proposer may reorder/update transactions in `unpreparedProposal`
proposal ABCI.PrepareProposal(unpreparedProposal)
}
broadcast PROPOSAL, h_p, round_p, proposal, validRound_p
} else {
schedule OnTimeoutPropose(h_p,round_p) to be executed after timeoutPropose(round_p)
}
}
```
### ReceiveProposal
In the case where the local node is not locked on any round, the following is ran:
```go
upon PROPOSAL, h_p, round_p, v, 1) from proposer(h_p, round_p) while step_p = propose do {
if valid(v) ABCI.ProcessProposal(h_p, v).accept (lockedRound_p = 1 lockedValue_p = v) {
broadcast PREVOTE, h_p, round_p, id(v)
} else {
broadcast PREVOTE, h_p, round_p, nil
// Include any slashing evidence that may be sent in the process proposal response
for evidence in ABCI.ProcessProposal(h_p, v).evidence_list {
broadcast EVIDENCE, evidence
}
}
step_p prevote
}
```
In the case where the node is locked on a round, the following is ran:
```go
upon PROPOSAL, h_p, round_p, v, vr
from proposer(h_p, round_p)
AND 2f + 1 PREVOTE, h_p, vr, id(v)
while step_p = propose (vr 0 vr < round_p) do {
if valid(v) ABCI.ProcessProposal(h_p, v).accept (lockedRound_p vr lockedValue_p = v) {
broadcast PREVOTE, h_p, round_p, id(v)
} else {
broadcast PREVOTE, h_p, round_p, nil
// Include any slashing evidence that may be sent in the process proposal response
for evidence in ABCI.ProcessProposal(h_p, v).evidence_list {
broadcast EVIDENCE, evidence
}
}
step_p prevote
}
```
### Prevote timeout
Upon receiving 2f + 1 prevotes, setup a timeout.
```go
upon 2f + 1 PREVOTE, h_p, vr, -1
with step_p = prevote for the first time, do {
schedule OnTimeoutPrevote(h_p, round_p) to be executed after timeoutPrevote(round_p)
}
```
with OnTimeoutPrevote defined as:
```go
function OnTimeoutPrevote(height, round) {
if (height = h_p && round = round_p && step_p = prevote) {
precommit_extension ABCI.ExtendVote(h_p, round_p, nil)
broadcast PRECOMMIT, h_p, round_p, nil, precommit_extension
step_p precommit
}
}
```
### Receiving enough prevotes to precommit
The following code is ran upon receiving 2f + 1 prevotes for the same block
```go
upon PROPOSAL, h_p, round_p, v, *
from proposer(h_p, round_p)
AND 2f + 1 PREVOTE, h_p, vr, id(v)
while valid(v) step_p >= prevote for the first time do {
if (step_p = prevote) {
lockedValue_p v
lockedRound_p round_p
precommit_extension ABCI.ExtendVote(h_p, round_p, id(v))
broadcast PRECOMMIT, h_p, round_p, id(v), precommit_extension
step_p precommit
}
validValue_p v
validRound_p round_p
}
```
And upon receiving 2f + 1 prevotes for nil:
```go
upon 2f + 1 PREVOTE, h_p, round_p, nil
while step_p = prevote do {
precommit_extension ABCI.ExtendVote(h_p, round_p, nil)
broadcast PRECOMMIT, h_p, round_p, nil, precommit_extension
step_p precommit
}
```
### Upon receiving a precommit
Upon receiving a precommit `precommit`, we ensure that `ABCI.VerifyVoteExtension(precommit.precommit_extension) = true`
before accepting the precommit. This is akin to how we check the signature on precommits normally, hence its not wrapped
in the syntax of methods from the paper.
### Precommit timeout
Upon receiving 2f + 1 precommits, setup a timeout.
```go
upon 2f + 1 PRECOMMIT, h_p, vr, * for the first time, do {
schedule OnTimeoutPrecommit(h_p, round_p) to be executed after timeoutPrecommit(round_p)
}
```
with OnTimeoutPrecommit defined as:
```go
function OnTimeoutPrecommit(height, round) {
if (height = h_p && round = round_p) {
StartRound(round_p + 1)
}
}
```
### Upon Receiving 2f + 1 precommits
The following code is ran upon receiving 2f + 1 precommits for the same block
```go
upon PROPOSAL, h_p, r, v, *
from proposer(h_p, r)
AND 2f + 1 PRECOMMIT, h_p, r, id(v)
while decision_p[h_p] = nil do {
if (valid(v)) {
decision_p[h_p] v
h_p h_p + 1
reset lockedRound_p, lockedValue_p,validRound_p and validValue_p to initial values
ABCI.FinalizeBlock(id(v))
StartRound(0)
}
}
```
If we don't see 2f + 1 precommits for the same block, we wait until we get 2f + 1 precommits, and the timeout occurs.

View File

@@ -1,201 +0,0 @@
# Tendermint v3 Markdown pseudocode
This is a single-threaded implementation of ABCI++,
with an optimization for the ProcessProposal phase.
Namely, processing of the header and the block data is separated into two different functions.
### Initialization
```go
h_p 0
round_p 0
step_p is one of {propose, prevote, precommit}
decision_p Vector()
lockedValue_p nil
validValue_p nil
validRound_p -1
```
### StartRound(round)
```go
function startRound(round) {
round_p round
step_p propose
if proposer(h_p, round_p) = p {
if validValue_p != nil {
proposal validValue_p
} else {
txdata mempool.GetBlock()
// getUnpreparedBlockProposal fills in header
unpreparedProposal getUnpreparedBlockProposal(txdata)
proposal ABCI.PrepareProposal(unpreparedProposal)
}
broadcast PROPOSAL, h_p, round_p, proposal, validRound_p
} else {
schedule OnTimeoutPropose(h_p,round_p) to be executed after timeoutPropose(round_p)
}
}
```
### ReceiveProposal
In the case where the local node is not locked on any round, the following is ran:
```go
upon PROPOSAL, h_p, round_p, v_header, 1) from proposer(h_p, round_p) while step_p = propose do {
prevote_nil false
// valid is Tendermints validation, ABCI.VerifyHeader is the applications
if valid(v_header) ABCI.VerifyHeader(h_p, v_header) (lockedRound_p = 1 lockedValue_p = id(v_header)) {
wait to receive proposal v corresponding to v_header
// We split up the app's header verification from the remainder of its processing of the proposal
if ABCI.ProcessProposal(h_p, v).accept {
broadcast PREVOTE, h_p, round_p, id(v)
} else {
prevote_nil true
// Include any slashing evidence that may be sent in the process proposal response
for evidence in ABCI.ProcessProposal(h_p, v).evidence_list {
broadcast EVIDENCE, evidence
}
}
} else {
prevote_nil true
}
if prevote_nil {
broadcast PREVOTE, h_p, round_p, nil
}
step_p prevote
}
```
In the case where the node is locked on a round, the following is ran:
```go
upon PROPOSAL, h_p, round_p, v_header, vr
from proposer(h_p, round_p)
AND 2f + 1 PREVOTE, h_p, vr, id(v_header)
while step_p = propose (vr 0 vr < round_p) do {
prevote_nil false
if valid(v) ABCI.VerifyHeader(h_p, v.header) (lockedRound_p vr lockedValue_p = v) {
wait to receive proposal v corresponding to v_header
// We split up the app's header verification from the remainder of its processing of the proposal
if ABCI.ProcessProposal(h_p, v).accept {
broadcast PREVOTE, h_p, round_p, id(v)
} else {
prevote_nil true
// Include any slashing evidence that may be sent in the process proposal response
for evidence in ABCI.ProcessProposal(h_p, v).evidence_list {
broadcast EVIDENCE, evidence
}
}
} else {
prevote_nil true
}
if prevote_nil {
broadcast PREVOTE, h_p, round_p, nil
}
step_p prevote
}
```
### Prevote timeout
Upon receiving 2f + 1 prevotes, setup a timeout.
```go
upon 2f + 1 PREVOTE, h_p, vr, -1
with step_p = prevote for the first time, do {
schedule OnTimeoutPrevote(h_p, round_p) to be executed after timeoutPrevote(round_p)
}
```
with OnTimeoutPrevote defined as:
```go
function OnTimeoutPrevote(height, round) {
if (height = h_p && round = round_p && step_p = prevote) {
precommit_extension ABCI.ExtendVote(h_p, round_p, nil)
broadcast PRECOMMIT, h_p, round_p, nil, precommit_extension
step_p precommit
}
}
```
### Receiving enough prevotes to precommit
The following code is ran upon receiving 2f + 1 prevotes for the same block
```go
upon PROPOSAL, h_p, round_p, v, *
from proposer(h_p, round_p)
AND 2f + 1 PREVOTE, h_p, vr, id(v)
while valid(v) step_p >= prevote for the first time do {
if (step_p = prevote) {
lockedValue_p v
lockedRound_p round_p
precommit_extension ABCI.ExtendVote(h_p, round_p, id(v))
broadcast PRECOMMIT, h_p, round_p, id(v), precommit_extension
step_p precommit
}
validValue_p v
validRound_p round_p
}
```
And upon receiving 2f + 1 prevotes for nil:
```go
upon 2f + 1 PREVOTE, h_p, round_p, nil
while step_p = prevote do {
precommit_extension ABCI.ExtendVote(h_p, round_p, nil)
broadcast PRECOMMIT, h_p, round_p, nil, precommit_extension
step_p precommit
}
```
### Upon receiving a precommit
Upon receiving a precommit `precommit`, we ensure that `ABCI.VerifyVoteExtension(precommit.precommit_extension) = true`
before accepting the precommit. This is akin to how we check the signature on precommits normally, hence its not wrapped
in the syntax of methods from the paper.
### Precommit timeout
Upon receiving 2f + 1 precommits, setup a timeout.
```go
upon 2f + 1 PRECOMMIT, h_p, vr, * for the first time, do {
schedule OnTimeoutPrecommit(h_p, round_p) to be executed after timeoutPrecommit(round_p)
}
```
with OnTimeoutPrecommit defined as:
```go
function OnTimeoutPrecommit(height, round) {
if (height = h_p && round = round_p) {
StartRound(round_p + 1)
}
}
```
### Upon Receiving 2f + 1 precommits
The following code is ran upon receiving 2f + 1 precommits for the same block
```go
upon PROPOSAL, h_p, r, v, *
from proposer(h_p, r)
AND 2f + 1 PRECOMMIT, h_p, r, id(v)
while decision_p[h_p] = nil do {
if (valid(v)) {
decision_p[h_p] v
h_p h_p + 1
reset lockedRound_p, lockedValue_p,validRound_p and validValue_p to initial values
ABCI.FinalizeBlock(id(v))
StartRound(0)
}
}
```
If we don't see 2f + 1 precommits for the same block, we wait until we get 2f + 1 precommits, and the timeout occurs.

View File

@@ -1,199 +0,0 @@
# Tendermint v4 Markdown pseudocode
This is a multi-threaded implementation of ABCI++,
where ProcessProposal starts when the proposal is received, but ends before precommitting.
### Initialization
```go
h_p 0
round_p 0
step_p is one of {propose, prevote, precommit}
decision_p Vector()
lockedValue_p nil
validValue_p nil
validRound_p -1
```
### StartRound(round)
```go
function startRound(round) {
round_p round
step_p propose
if proposer(h_p, round_p) = p {
if validValue_p != nil {
proposal validValue_p
} else {
txdata mempool.GetBlock()
// getUnpreparedBlockProposal fills in header
unpreparedProposal getUnpreparedBlockProposal(txdata)
proposal ABCI.PrepareProposal(unpreparedProposal)
}
broadcast PROPOSAL, h_p, round_p, proposal, validRound_p
} else {
schedule OnTimeoutPropose(h_p,round_p) to be executed after timeoutPropose(round_p)
}
}
```
### ReceiveProposal
In the case where the local node is not locked on any round, the following is ran:
```go
upon PROPOSAL, h_p, round_p, v, 1) from proposer(h_p, round_p) while step_p = propose do {
if valid(v) ABCI.VerifyHeader(h_p, v.header) (lockedRound_p = 1 lockedValue_p = v) {
// We fork process proposal into a parallel process
Fork ABCI.ProcessProposal(h_p, v)
broadcast PREVOTE, h_p, round_p, id(v)
} else {
broadcast PREVOTE, h_p, round_p, nil
}
step_p prevote
}
```
In the case where the node is locked on a round, the following is ran:
```go
upon PROPOSAL, h_p, round_p, v, vr
from proposer(h_p, round_p)
AND 2f + 1 PREVOTE, h_p, vr, id(v)
while step_p = propose (vr 0 vr < round_p) do {
if valid(v) ABCI.VerifyHeader(h_p, v.header) (lockedRound_p vr lockedValue_p = v) {
// We fork process proposal into a parallel process
Fork ABCI.ProcessProposal(h_p, v)
broadcast PREVOTE, h_p, round_p, id(v)
} else {
broadcast PREVOTE, h_p, round_p, nil
}
step_p prevote
}
```
### Prevote timeout
Upon receiving 2f + 1 prevotes, setup a timeout.
```go
upon 2f + 1 PREVOTE, h_p, vr, -1
with step_p = prevote for the first time, do {
schedule OnTimeoutPrevote(h_p, round_p) to be executed after timeoutPrevote(round_p)
}
```
with OnTimeoutPrevote defined as:
```go
def OnTimeoutPrevote(height, round) {
if (height = h_p && round = round_p && step_p = prevote) {
// Join the ProcessProposal, and output any evidence in case it has some.
processProposalOutput Join ABCI.ProcessProposal(h_p, v)
for evidence in processProposalOutput.evidence_list {
broadcast EVIDENCE, evidence
}
precommit_extension ABCI.ExtendVote(h_p, round_p, nil)
broadcast PRECOMMIT, h_p, round_p, nil, precommit_extension
step_p precommit
}
}
```
### Receiving enough prevotes to precommit
The following code is ran upon receiving 2f + 1 prevotes for the same block
```go
upon PROPOSAL, h_p, round_p, v, *
from proposer(h_p, round_p)
AND 2f + 1 PREVOTE, h_p, vr, id(v)
while valid(v) step_p >= prevote for the first time do {
if (step_p = prevote) {
lockedValue_p v
lockedRound_p round_p
processProposalOutput Join ABCI.ProcessProposal(h_p, v)
// If the proposal is valid precommit as before.
// If it was invalid, precommit nil.
// Note that ABCI.ProcessProposal(h_p, v).accept is deterministic for all honest nodes.
precommit_value nil
if processProposalOutput.accept {
precommit_value id(v)
}
precommit_extension ABCI.ExtendVote(h_p, round_p, precommit_value)
broadcast PRECOMMIT, h_p, round_p, precommit_value, precommit_extension
for evidence in processProposalOutput.evidence_list {
broadcast EVIDENCE, evidence
}
step_p precommit
}
validValue_p v
validRound_p round_p
}
```
And upon receiving 2f + 1 prevotes for nil:
```go
upon 2f + 1 PREVOTE, h_p, round_p, nil
while step_p = prevote do {
// Join ABCI.ProcessProposal, and broadcast any evidence if it exists.
processProposalOutput Join ABCI.ProcessProposal(h_p, v)
for evidence in processProposalOutput.evidence_list {
broadcast EVIDENCE, evidence
}
precommit_extension ABCI.ExtendVote(h_p, round_p, nil)
broadcast PRECOMMIT, h_p, round_p, nil, precommit_extension
step_p precommit
}
```
### Upon receiving a precommit
Upon receiving a precommit `precommit`, we ensure that `ABCI.VerifyVoteExtension(precommit.precommit_extension) = true`
before accepting the precommit. This is akin to how we check the signature on precommits normally, hence its not wrapped
in the syntax of methods from the paper.
### Precommit timeout
Upon receiving 2f + 1 precommits, setup a timeout.
```go
upon 2f + 1 PRECOMMIT, h_p, vr, * for the first time, do {
schedule OnTimeoutPrecommit(h_p, round_p) to be executed after timeoutPrecommit(round_p)
}
```
with OnTimeoutPrecommit defined as:
```go
def OnTimeoutPrecommit(height, round) {
if (height = h_p && round = round_p) {
StartRound(round_p + 1)
}
}
```
### Upon Receiving 2f + 1 precommits
The following code is ran upon receiving 2f + 1 precommits for the same block
```go
upon PROPOSAL, h_p, r, v, *
from proposer(h_p, r)
AND 2f + 1 PRECOMMIT, h_p, r, id(v)
while decision_p[h_p] = nil do {
if (valid(v)) {
decision_p[h_p] v
h_p h_p + 1
reset lockedRound_p, lockedValue_p,validRound_p and validValue_p to initial values
ABCI.FinalizeBlock(id(v))
StartRound(0)
}
}
```
If we don't see 2f + 1 precommits for the same block, we wait until we get 2f + 1 precommits, and the timeout occurs.

View File

@@ -1,91 +0,0 @@
## Communication between peers and components within the blocksync reactor
A newly joined or recovering node is connecting to a number of peers in order to sync up to the latest height in the blockchain. The peers do not neccessarily have to be validators but we assume that at least one of the peers is correct. The node requests the status of peers in ordeer to learn the maximum and minimum heights for which the peer has blocks. Based on this, the node decides on the maximum height it needs to sync up to. **There is no additional check** on whether the peers were lying about their heights.
Each peer has an open p2p channel. The number of total requests in flight is limited (`maxPendingRequests` initially set to `maxTotalRequesters`). Additionally, there is an upper limit on requests **per** peer (20).
Once a node receives messages via the p2p channel, they are propagated further via the reactor's go channels. This section contains details on each of the open communication channels, their capacity and when they get activated.
On startup, the reactor fires up four go routines and starts the block pool service:
1. Process requests
2. Pool routine
3. Handle p2p channel messages
4. Process peer updates
The process requests routine sends out requests and error messages to peers while all requests received through the p2p channel are processed within the `processBlockSyncCh` routine (routine 3 above).
The pool routine picks out blocks form the block pool and processes them. It also checks whether the node should switch to consensus (#2).
On peer update messages the reactor adds or removes the peer that has sent the update message (#4) .
**Note** There is currently a check for whether we have a message from an empty peer.
``` go
// XXX: Pool#RedoRequest can sometimes give us an empty peer.
if len(peerUpdate.NodeID) == 0 {
return
}
```
The pool service launches the block requesters (processes requesting blocks from particular peers). Each requester requests a block for a given height. It picks the next available peer from the pool and requets a block at a given height from it. The block pool forwards these requests via internal channels to the reactor who is the only one doing actual p2p communication with other peers.
### Communication channels
`BlockSyncCh`: a p2p channel for sending/receiving requests to/from peers.
- Channel id: 0x40
- Size of receive buffer: 1024 messages
- Size of send queue: 1000 messages
- Message size: maximum size of a block + size of proto block messages (response message prefix and key size) + size of the Extended commit.
Messages processed via the channel:
| Message name | Message fields| Description |
| --- | ---| ---|
| `BlockRequest`| `height int64, peerID types.NodeID` | request block at height `height` from peer `peerID`|
| `BlockResponse`| `block types.Block `| Send `block` to peer that requested it |
| `NoBlockResponse` |`height int64`|Indicates that a peer does not have a block at `height`|
|`StatusRequest`| `{} `| Sent to a peer to request its status|
|`StatusResponse` | `height int64, base int64`|Send to a peer the lowest and heights height of blocks within its store (`store.Height()`, `store.Base()`)|
|`HeaderRequest` | `height: int64`| Request a header from peer for verification|
|`HeaderResponse` |`header: Header`| Return the header for the corresponding height|
### Reactor channels
This section describes, per reactor component, the open channels and information they process.
#### `BlockPool`
| Channel name | Channel msg type| Description |
| --- | ---| ---|
|`requestsCh` | `BlockRequest` |The number of requests is capped by a fixed parameter `maxPendingRequestsPerPeer` (initially set to 20)|
|`errorsCh` |`peerError`| Channel buffer size is limited|
The reactor sends a p2p block request once it receives a signal via this channel from the block pool. The block pool will first pick a peer (in round robin) and assign it this particular height. Once this is done, the reactor can request a block.
#### `bpRequester`
| Channel name | Channel msg type| Description |
| --- | ---| ---|
`gotBlock` | `struct{}`| capped at 1; Here we simply register a received block and keep waiting for the reactor to terminate or a redo request. **Note**. It is not clear why we need this. |
|`redoCh`| `types.NodeID`| capped at 1 ; Signals the requester to redo a request for aparticular block after replacing the peer for this height|
When a block is received by a requester, the requester does a number of checks on the received block. Before marking the block as available, the requester verifies the following:
- that we expected a block at the particular height.
- the block came from the peer we assigned to it.
- the block is not nil
In the code there is the following ToDo listed:
` // TODO: ensure that blocks come in order for each peer.` This needs further specification.
If the checks pass, the `block` field of the requester is populated with the new block and is thus made available to the blocksync reactor.
#### `Reactor`
| Channel name | Channel msg type| Description |
| --- | ---| ---|
|`requestsCh`|`BlockRequest`|size `maxTotalRequesters`|
|`errorsCh`| `peerError`| size `maxPeerErrBuffer`|
|`didProcessCh`|`struct{}`| size `1`|
The channel is created within the pool routine of the reactor and is used to signal that the reactor should check the block pool for new blocks. A message is sent to the channel after a fixed timeout (`trySyncTicker`). As we need two blocks to verify one of them (this is more clearly defined in [verification](./verification.md), if we miss only one of them, we will not wait for the sync timer to time out, but rather try quickly again until we fetch both.
`switchToConsensusTicker`. In addition to the sync timeout, in the same routine, the reactor checks periodically whether the conditions to switch to consensus are fullfilled.

View File

@@ -1,89 +0,0 @@
### Data structures
There are four core components of the blocksync reactor: the reactor itself, a block pool, requesters and peers.
The reactor verifies received blocks, executes them against the application and commits them into the blockstore of the node. It also sends out requests to peers asking for more blocks and contains the logic to switch from blocksync to consenus.
It contains a pointer to the block pool.
```go
type Reactor struct {
service.BaseService
logger log.Logger
// immutable
initialState sm.State
// store
stateStore sm.Store
blockExec *sm.BlockExecutor
store sm.BlockStore
pool *BlockPool
consReactor consensusReactor
blockSync *atomicBool
chCreator p2p.ChannelCreator
peerEvents p2p.PeerEventSubscriber
requestsCh <-chan BlockRequest
errorsCh <-chan peerError
metrics *consensus.Metrics
eventBus *eventbus.EventBus
syncStartTime time.Time
lastTrustedBlock *TrustedBlockData
}
```
`TrustedBlockData` contains the last synced and verified block along with the commit used to verify it.
```go
type TrustedBlockData struct {
block *types.Block
commit *types.Commit
}
```
The block pool stores the last executed block (`height`), keeps track of peers connected to a node, the current height for each peer, along with the number of pending requests for each peer and assigns requests to peers (by creating `requesters`).
```go
type BlockPool {
mtx Mutex
requesters map[int64]*bpRequester
height int64
peers map[p2p.ID]*bpPeer
maxPeerHeight int64
numPending int32
store BlockStore
requestsChannel chan<- BlockRequest
errorsChannel chan<- peerError
}
```
Each requester is used to track the assignement of a request for a `block` at position `height` to a peer whose id equals to `peerID`.
```go
type bpRequester {
mtx Mutex
block *types.Block
height int64
peerID types.nodeID
redoChannel chan type.nodeID //redo may send multi-time; peerId is used to identify repeat
goBlockCh chan struct{}{}
}
```
Each `Peer` data structure stores for each peer its current `height` and number of pending requests sent to the peer (`numPending`), etc. When a block is processed, this number is decremented.
```go
type bpPeer struct {
id p2p.ID
height int64
base int64
numPending int32
timeout *time.Timer
didTimeout bool
pool *blockPool
}
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 265 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

View File

@@ -1,73 +0,0 @@
---
order: 1
parent:
title: blocksync
order: 3
---
# Blocksync
This section describes the blocksync service provided by Tendermint. The main functionality provided by it is enabling new or recovering nodes to sync up to the head of the chain quickly. Normally, in a proof-of-stake blockchain, blocks are decided when multiple nodes reach consensus on it, requiring many rounds of communication. Once a block is decided on, it is executed against the application and stored, together with the proof that at least 2/3 of the nodes have voted for it. This proof takes the shape of a `Commit` which stores the signatures of 2/3+ validators that approved the block.
It is sufficient for a syncing node to contact its peers, download old blocks in parallel, verify their commit signatures and execute them sequentially (in order) against the application. As the signatures that validated a block at height `H` are stored with the block at height `H + 1`, blocksync needs to download both blocks in order to verify the former.
We can think about the blocksync service having two modes of operation. One is as a *server*, providing syncing nodes with blocks and commits, the other as a *client* requesting blocks from peers. Each full node runs a blocksync reactor, which launches both the server and client routines.
Blocksync is implemented within a Reactor whose internal data structures keep track of peers connected to a node and keep a pool of blocks downloaded from them if the node is currently syncing. Blocks are applied one by one until a node is considered caught up (more details on the conditions can be found in the sections below).
Once caught up, the node switches to consensus and stops running blocksync as a client.
### Starting Blocksync
A node can switch to blocksync directly on start-up or after completing `state-sync`. [State sync](ToDo) is downloading only snapshots of application state along with commits. This is further speeding up the process of catching up to the latest height as a node does not download the actual blocks.
The blocksync reactor service is started at the same time as all the other services in Tendermint. But blocksync-inc is disabled (blockSync boolean flag is false) initially and thus the blockpool and the routine to process blocks from the pool are not launched until the reactor is actually activated.
The reactor is activated after state sync, where the pool and request processing routines are launched.
However, receiving messages via the p2p channel and sending status updates to other nodes is enabled regardless of whether the blocksync reactor is started. Essentially every node is running as a blocksync server as soon as it is started up.
**Note**. In the current version, if we start from state sync and blocksync is not launched before as a service, the internal channels used by the reactor will not be created. We need to be careful to launch the blocksync *service* before we call the function to switch from statesync to blocksync.
### Switching from blocksync to consensus
Ideally, the switch to consensus is done once the node considers itself caugh up or we has not advanced its height for more than a predefined amount of time (currently 60s).
We consider a node to be caught up if it is 1 height away from the maximum height reported by its peers. The reason we **do not catch up until the maximum height** (`pool.maxPeerHeight`) is that we cannot verify the block at `pool.maxPeerHeight` without the `lastCommit` of the block at `pool.maxPeerHeight + 1`.
This check is performed periodically by calling `isCaughtUp` inside `poolRoutine`.
When the node is starting from genesis, the first block does not need the vote extensions and is able to switch directly to consensus.
**Note** It is currently assumed that the Consensus reactor is already running. It is therefore not turned on by the Blocksync reactor. In case it has not been started, the Blocksync reactor simply returns.
#### **Vote extensions in v.036**
In v0.36, Tendermint introduced vote extensions, which is application defined data, attached to the votes deciding on a block. In order to actively participate in consensus, a node has to have vote extensions from the previous height. Therefore, in addition to storing and downloading commits, nodes download and send vote extensions to their peers as well.
For this reason, if the node is not starting from genesis but only after state sync, blocksync **does not** switch to consensus until it syncs at least one block. As state sync only stores a state snapshot (without vote extensions), we need to receive them from one of our peers.
### Switching to blocksync from consensus
Ideally, a node should be able to switch to blocksync even if it does not crash, but falls behind in consensus. Unfortunately, switching back to blocksync from consensus is not possible at the moment. It is expected to be handled in [Issue #129](https://github.com/tendermint/tendermint/issues/129).
## Architecture and algorithm
The Blocksync reactor is organised as a set of concurrent tasks:
- Receive routine of Blocksync Reactor
- Task for creating Requesters
- Set of Requester tasks
- A controller task.
![Blocksync Reactor Architecture Diagram](img/bc-reactor.png)
This section describes the Blocksync reactor and its internals including:
- [Data structures used](./data_structures.md)
- [Peer to peer communication pattern](./communication.md)
- [Block Verification](./verification.md)
More details on how to use the Blocksync reactor and configure it when running Tendermint can be found [here](./../../docs/tendermint-core/block-sync/README.md).

View File

@@ -1,120 +0,0 @@
## Block verification
When blocksyncing, a node is not participating in consensus. It is receiving blocks that have already been decided and committed. To avoid being fooled by malicious peers, the node has to verify the received blocks before executing the transactions and storing the block in its store.
The verification in blocksync aims to apply the same logic as the [light client verification](../light-client/verification/README.md). The safety guarantees of the light client model are provided by verifying blocks starting from an **initial trusted state** and using validators who were bonded within a **trusting period**. Once this period expires, the validators could have unbonded, and we cannot rely on them being honest anymore. In blocksync, we can be **outside** the trusting period. Therefore, the guarantees provided by blocksync verification can be divided in two groups:
- The block is within the trusting period, we can punish validators, and provide guarantees that if a block is signed by trusted validators, they are indeed correct.
- The block is outside the trusting period and we cannot guarantee that the validators are still honest. We can however make a potential attack by those validators more complicated by additionally verifying the block using other peers as *witnesses*.
A state becomes trusted once it passes the validation. The section below discusses this state in more details.
### Trusted state
Currently there are two possible ways to obtain a trusted state :
1. If the node is blocksync-ing from genesis, we have to verify the very first block and mark it as the initial trusted state.
2. The node loads the last block stored in its local block store.
**Case 1 - blocksync from genesis**
In this case we only verify that the validator hash of the block matches the validator set resulting from executing `InitChain`. We will use the initially provided validator set for verification and further verify the block against witnesses. In this scenario, this state is very likely to be outside the trusting period. We will accept this block as a trusted state and store it inside the node's block store.
It is worth noting that, running block sync from the first height is significantly slower than running statesync first. However, statesync does not keep the entire blockchain history and some operators might opt not to state sync. The reason is that, if sufficiently many nodes state sync and other nodes who have historical data fail or leave the network, the network ends up with gaps in the block history.
**Case 2 - blocksync starting from pre-existing state**
If the node is not starting from gensis, there will be pre-existing state in its local block store.
This can happen in the following two cases:
- The node is blocksyncing after completing state sync.
- The node has crashed and is now recovering.
In both of these cases, we can trust the state in the store. State sync runs the light client verification protocol. If the crash happened during consensus or sync-ing, the only blocks in the store are blocks that are verified or decided.
If we can trust this initial state at height H, we can use this fact to eliminate faulty blocks whose `lastCommit` at height H+1 does not verify the trusted block. Additionally, we can double check the validator sets that signed the block at H + 1 against the expected validator sets for this height. The expected validator sets are stored within the trusted state and returned to us upon applying the block and executing it against the application.
**Switching from statesync to blocksync**
If we switch from statesync to blocksync, the last item in the block store is not the entire block. The store holds only the verified header and the commit that verified it. Therefore, we do need to fetch the corresponding block from our peers, verify it against data stored in the store and set the `last trusted block` to this block.
To enable this, upon switching from statesync, we do not instruct the block pool to fetch blocks at height `H + 1` (where height `H` corresponds to the height of the header last verified with statesync). Instead we let it fetch the block at height H, but do not execute this block against the application (as this would fail - state is already updated to height H + 1. )
### Verifying blocks past the trusted state
<center>
<img src="img/bc-reactor-blocks.png" alt="block diagram" title="Block overview" width="800px" name="Block_overview" align="center" />
</center>
<br/>
The diagram above shows all blocks at play when verifying block at height ` H + 1` - `newBlock`, where at height `H` we have the trusted block.
At a high level, the verification algorithm is represented by the code below.
~~~golang
if !LastCommit.verify(trustedBlock) || NewBlock.validatorSet != trustedState.validatorSet {
// NewBlock is invalid, request new
request_newBlock_from_other_peer(height: h + 1)
} else {
if LastCommit2.validatorSetHash == trustedState.validatorSetHash &&
verifyBlock.validatorSetHash == trustedState.nextValidatorSetHash {
if LastCommit2.verify(NewBlock) {
if verify_block_with_peers(NewBlock) {
ApplyBlock(NewBlock)
}
}
} else
find_new_verifyBlock(height: h + 2)
}
~~~
We try to increase the trust in the NewBlock by first confirming that its `lastCommit` field indeed verifies the trusted block. We then confirm that the validator hash matches the expected hash. But to fully verify the block we need to check it against the signatures that signed it. They are stored within the `lastCommit2` of `verifyBlock` at height `H+2`. As we have no guarantees on this block, we again increase our trust in it by confirming that the validators whose signatures are in `lastCommit2` are a match for the expected validator set.
#### *Witness verification*
**Note**. This is not implemented yet.
If all these checks pass, the reactor verifies that other peers have the same block at the particular height.
In order to reduce traffic, we do not ask the peers to provide the whole block to us, rather only the header. If crosschecking the headers fails then the node requests the remainder of the block to decide on whether this peer is faulty or not.
If the block received does not verify using the same verification logic, the peer is faulty. In case the verification passes, but headers mismatch, the node will not know who to trust.
**Verification failed**
In the light client verification model, the client requests from both peers a trace of blocks in order to determine the block where the divergence happened. As the blocks being verified are within the trusted period, the light client can generate evidence and send it to full nodes to vote for it and determine which peer is faulty.
But in the block sync reactor, if verification fails, there are multiple possible scenarios:
- Abort blocksync and switch to consensus.
- Request data from more peers.
Both cases can be a potential attack on the joining node. We discuss these different attack possibilities and design shortcomings in the section below.
## Problems with current verification and reactor design
The crux of many concerns with regards to the correctness and safety of the block sync reactor lies in the fact a node connects to peers.
Currently, the only constraint a node has is that it has to connect to **a** peer. An improved design should require a node to connect to at least two peers.
We have no guarantees on the correctness of the peers we are connected to.
- Once we replace a peer due to it having timed out or not reporting a correct block, nothing prevents a node from reconnecting to it - *there is no notion of blacklisted peers*.
- If we connect to a subset of peers, they could feed the node faulty data. Eventually, when the node switches to consensus, it would realize there is something wrong, but then the node itself might be blacklisted.
- Alternatively, a node that is fed faulty data, could, upon switching to conensus, become part of a light client attack by serving as a faulty witness.
- There is no check whether the maximum height reported by peers is true or not. A slow node could report a very distant height to the node - for example 2000, when the blockchain is at height 1000 in fact. This would lead to one part of the condition to switch to consensus never being true. To prevent the node switching due to not advancing, the malicious node sends a new block very slowly. Thus the node progresses but can never participate in consensus. This issue could potentially be mitigated if, instead of taking the maximum height reported by peers, we report the lowest of their maximums. The idea is that peers should be close enough to the top of the chain in any case. But this could also open the node to an attack where they switch to consensus too early. The median would be a good compromise between the two.
- A blocksyncing node can flood peers with requests - constantly reporting that it has not synced up. At the moment, the maximum amount of requests received is limited and protects peers to some extent against this attack.
## Additional ideas and suggestions
**Performance improvement** Instead of downloading blocks before we verify them, download only headers. Once we verify those, we can download the whole block. As the headers do not contain signatures so we would need to add commits form blocks at subsequent heights as well.
**Different proposal** Instead of witness verification, verify against a light client and use backwards verification when applicable.

View File

@@ -115,7 +115,7 @@ func generateTestnet(r *rand.Rand, opt map[string]interface{}) (e2e.Manifest, er
Nodes: map[string]*e2e.ManifestNode{},
KeyType: keyType.Choose(r).(string),
Evidence: evidence.Choose(r).(int),
QueueType: "priority",
QueueType: "simple-priority",
TxSize: opt["txSize"].(int),
}

View File

@@ -4,7 +4,7 @@
evidence = 5
initial_height = 1000
initial_state = {initial01 = "a", initial02 = "b", initial03 = "c"}
queue_type = "priority"
queue_type = "simple-priority"
abci_protocol = "builtin"
[validators]

View File

@@ -355,7 +355,7 @@ func (n Node) Validate(testnet Testnet) error {
return fmt.Errorf("invalid mempool version %q", n.Mempool)
}
switch n.QueueType {
case "", "priority", "fifo":
case "", "priority", "fifo", "simple-priority":
default:
return fmt.Errorf("unsupported p2p queue type: %s", n.QueueType)
}