mirror of
https://github.com/tendermint/tendermint.git
synced 2026-08-28 03:46:33 +00:00
Merge branch 'master' into wb/rfc-017-transition
This commit is contained in:
@@ -4,6 +4,30 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: monday
|
||||
target-branch: "master"
|
||||
open-pull-requests-limit: 10
|
||||
labels:
|
||||
- T:dependencies
|
||||
- S:automerge
|
||||
|
||||
- package-ecosystem: github-actions
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: monday
|
||||
target-branch: "v0.35.x"
|
||||
open-pull-requests-limit: 10
|
||||
labels:
|
||||
- T:dependencies
|
||||
- S:automerge
|
||||
|
||||
- package-ecosystem: github-actions
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: monday
|
||||
target-branch: "v0.34.x"
|
||||
open-pull-requests-limit: 10
|
||||
labels:
|
||||
- T:dependencies
|
||||
@@ -13,6 +37,7 @@ updates:
|
||||
directory: "/docs"
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: monday
|
||||
open-pull-requests-limit: 10
|
||||
|
||||
###################################
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
# Verify that generated code is up-to-date.
|
||||
#
|
||||
# Note that we run these checks regardless whether the input files have
|
||||
# changed, because generated code can change in response to toolchain updates
|
||||
# even if no files in the repository are modified.
|
||||
name: Check generated code
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
check-mocks:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.17'
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: "Check generated mocks"
|
||||
run: |
|
||||
set -euo pipefail
|
||||
make mockery 2>/dev/null
|
||||
|
||||
if ! git diff --stat --exit-code ; then
|
||||
echo ">> ERROR:"
|
||||
echo ">>"
|
||||
echo ">> Generated mocks require update (either Mockery or source files may have changed)."
|
||||
echo ">> Ensure your tools are up-to-date, re-run 'make mockery' and update this PR."
|
||||
echo ">>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
check-proto:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.17'
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 1 # we need a .git directory to run git diff
|
||||
|
||||
- name: "Check protobuf generated code"
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# Install buf and gogo tools, so that differences that arise from
|
||||
# toolchain differences are also caught.
|
||||
readonly tools="$(mktemp -d)"
|
||||
export PATH="${PATH}:${tools}/bin"
|
||||
export GOBIN="${tools}/bin"
|
||||
|
||||
readonly base='https://github.com/bufbuild/buf/releases/latest/download'
|
||||
readonly OS="$(uname -s)" ARCH="$(uname -m)"
|
||||
curl -sSL "${base}/buf-${OS}-${ARCH}.tar.gz" \
|
||||
| tar -xzf - -C "$tools" --strip-components=1
|
||||
|
||||
go install github.com/gogo/protobuf/protoc-gen-gogofaster@latest
|
||||
|
||||
make proto-gen
|
||||
|
||||
if ! git diff --stat --exit-code ; then
|
||||
echo ">> ERROR:"
|
||||
echo ">>"
|
||||
echo ">> Protobuf generated code requires update (either tools or .proto files may have changed)."
|
||||
echo ">> Ensure your tools are up-to-date, re-run 'make proto-gen' and update this PR."
|
||||
echo ">>"
|
||||
exit 1
|
||||
fi
|
||||
@@ -39,17 +39,17 @@ jobs:
|
||||
platforms: all
|
||||
|
||||
- name: Set up Docker Build
|
||||
uses: docker/setup-buildx-action@v1.7.0
|
||||
uses: docker/setup-buildx-action@v2.0.0
|
||||
|
||||
- name: Login to DockerHub
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
uses: docker/login-action@v1.14.1
|
||||
uses: docker/login-action@v2.0.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Publish to Docker Hub
|
||||
uses: docker/build-push-action@v2.10.0
|
||||
uses: docker/build-push-action@v3.0.0
|
||||
with:
|
||||
context: .
|
||||
file: ./DOCKER/Dockerfile
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
name: Check Markdown links
|
||||
on:
|
||||
schedule:
|
||||
- cron: '* */24 * * *'
|
||||
jobs:
|
||||
markdown-link-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: creachadair/github-action-markdown-link-check@master
|
||||
with:
|
||||
folder-path: "docs"
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
**/**.go
|
||||
go.mod
|
||||
go.sum
|
||||
- uses: golangci/golangci-lint-action@v3.1.0
|
||||
- uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
# Required: the version of golangci-lint is required and
|
||||
# must be specified without patch version: we always use the
|
||||
|
||||
@@ -21,6 +21,7 @@ Special thanks to external contributors on this release:
|
||||
- [rpc] \#7982 Add new Events interface and deprecate Subscribe. (@creachadair)
|
||||
- [cli] \#8081 make the reset command safe to use by intoducing `reset-state` command. Fixed by \#8259. (@marbar3778, @cmwaters)
|
||||
- [config] \#8222 default indexer configuration to null. (@creachadair)
|
||||
- [rpc] \#8570 rework timeouts to be per-method instead of global. (@creachadair)
|
||||
|
||||
- Apps
|
||||
|
||||
|
||||
@@ -251,6 +251,21 @@ mockery:
|
||||
go generate -run="./scripts/mockery_generate.sh" ./...
|
||||
.PHONY: mockery
|
||||
|
||||
###############################################################################
|
||||
### Metrics ###
|
||||
###############################################################################
|
||||
|
||||
metrics: testdata-metrics
|
||||
go generate -run="scripts/metricsgen" ./...
|
||||
.PHONY: metrics
|
||||
|
||||
# By convention, the go tool ignores subdirectories of directories named
|
||||
# 'testdata'. This command invokes the generate command on the folder directly
|
||||
# to avoid this.
|
||||
testdata-metrics:
|
||||
ls ./scripts/metricsgen/testdata | xargs -I{} go generate -run="scripts/metricsgen" ./scripts/metricsgen/testdata/{}
|
||||
.PHONY: testdata-metrics
|
||||
|
||||
###############################################################################
|
||||
### Local testnet using docker ###
|
||||
###############################################################################
|
||||
|
||||
@@ -17,12 +17,6 @@ import (
|
||||
"github.com/tendermint/tendermint/libs/service"
|
||||
)
|
||||
|
||||
const (
|
||||
// reqQueueSize is the max number of queued async requests.
|
||||
// (memory: 256MB max assuming 1MB transactions)
|
||||
reqQueueSize = 256
|
||||
)
|
||||
|
||||
// This is goroutine-safe, but users should beware that the application in
|
||||
// general is not meant to be interfaced with concurrent callers.
|
||||
type socketClient struct {
|
||||
@@ -48,7 +42,7 @@ var _ Client = (*socketClient)(nil)
|
||||
func NewSocketClient(logger log.Logger, addr string, mustConnect bool) Client {
|
||||
cli := &socketClient{
|
||||
logger: logger,
|
||||
reqQueue: make(chan *requestAndResponse, reqQueueSize),
|
||||
reqQueue: make(chan *requestAndResponse),
|
||||
mustConnect: mustConnect,
|
||||
addr: addr,
|
||||
reqSent: list.New(),
|
||||
@@ -118,6 +112,11 @@ func (cli *socketClient) sendRequestsRoutine(ctx context.Context, conn io.Writer
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case reqres := <-cli.reqQueue:
|
||||
// N.B. We must enqueue before sending out the request, otherwise the
|
||||
// server may reply before we do it, and the receiver will fail for an
|
||||
// unsolicited reply.
|
||||
cli.trackRequest(reqres)
|
||||
|
||||
if err := types.WriteMessage(reqres.Request, bw); err != nil {
|
||||
cli.stopForError(fmt.Errorf("write to buffer: %w", err))
|
||||
return
|
||||
@@ -158,14 +157,15 @@ func (cli *socketClient) recvResponseRoutine(ctx context.Context, conn io.Reader
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *socketClient) willSendReq(reqres *requestAndResponse) {
|
||||
cli.mtx.Lock()
|
||||
defer cli.mtx.Unlock()
|
||||
|
||||
func (cli *socketClient) trackRequest(reqres *requestAndResponse) {
|
||||
// N.B. We must NOT hold the client state lock while checking this, or we
|
||||
// may deadlock with shutdown.
|
||||
if !cli.IsRunning() {
|
||||
return
|
||||
}
|
||||
|
||||
cli.mtx.Lock()
|
||||
defer cli.mtx.Unlock()
|
||||
cli.reqSent.PushBack(reqres)
|
||||
}
|
||||
|
||||
@@ -199,7 +199,6 @@ func (cli *socketClient) doRequest(ctx context.Context, req *types.Request) (*ty
|
||||
}
|
||||
|
||||
reqres := makeReqRes(req)
|
||||
cli.willSendReq(reqres)
|
||||
|
||||
select {
|
||||
case cli.reqQueue <- reqres:
|
||||
|
||||
@@ -171,7 +171,8 @@ for applications built w/ Cosmos SDK).
|
||||
// If necessary adjust global WriteTimeout to ensure it's greater than
|
||||
// TimeoutBroadcastTxCommit.
|
||||
// See https://github.com/tendermint/tendermint/issues/3435
|
||||
if cfg.WriteTimeout <= conf.RPC.TimeoutBroadcastTxCommit {
|
||||
// Note we don't need to adjust anything if the timeout is already unlimited.
|
||||
if cfg.WriteTimeout > 0 && cfg.WriteTimeout <= conf.RPC.TimeoutBroadcastTxCommit {
|
||||
cfg.WriteTimeout = conf.RPC.TimeoutBroadcastTxCommit + 1*time.Second
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -523,7 +523,7 @@ func DefaultRPCConfig() *RPCConfig {
|
||||
MaxSubscriptionClients: 100,
|
||||
MaxSubscriptionsPerClient: 5,
|
||||
ExperimentalDisableWebsocket: false, // compatible with TM v0.35 and earlier
|
||||
EventLogWindowSize: 0, // disables /events RPC by default
|
||||
EventLogWindowSize: 30 * time.Second,
|
||||
EventLogMaxItems: 0,
|
||||
|
||||
TimeoutBroadcastTxCommit: 10 * time.Second,
|
||||
|
||||
@@ -45,4 +45,4 @@ We will cover the various types of node types within Tendermint.
|
||||
|
||||
Validators are nodes that participate in the security of a network. Validators have an associated power in Tendermint, this power can represent stake in a [proof of stake](https://en.wikipedia.org/wiki/Proof_of_stake) system, reputation in [proof of authority](https://en.wikipedia.org/wiki/Proof_of_authority) or any sort of measurable unit. Running a secure and consistently online validator is crucial to a networks health. A validator must be secure and fault tolerant, it is recommended to run your validator with 2 or more sentry nodes.
|
||||
|
||||
As a validator there is the potential to have your weight reduced, this is defined by the application. Tendermint is notified by the application if a validator should have there weight increased or reduced. Application have different types of malicious behavior which lead to slashing of the validators power. Please check the documentation of the application you will be running in order to find more information.
|
||||
As a validator there is the potential to have your weight reduced, this is defined by the application. Tendermint is notified by the application if a validator should have their weight increased or reduced. Application have different types of malicious behavior which lead to slashing of the validators power. Please check the documentation of the application you will be running in order to find more information.
|
||||
|
||||
+47
-34
@@ -18,40 +18,53 @@ Listen address can be changed in the config file (see
|
||||
|
||||
The following metrics are available:
|
||||
|
||||
| **Name** | **Type** | **Tags** | **Description** |
|
||||
| -------------------------------------- | --------- | ------------- | ---------------------------------------------------------------------- |
|
||||
| abci_connection_method_timing | Histogram | method, type | Timings for each of the ABCI methods |
|
||||
| consensus_height | Gauge | | Height of the chain |
|
||||
| consensus_validators | Gauge | | Number of validators |
|
||||
| consensus_validators_power | Gauge | | Total voting power of all validators |
|
||||
| consensus_validator_power | Gauge | | Voting power of the node if in the validator set |
|
||||
| consensus_validator_last_signed_height | Gauge | | Last height the node signed a block, if the node is a validator |
|
||||
| consensus_validator_missed_blocks | Gauge | | Total amount of blocks missed for the node, if the node is a validator |
|
||||
| consensus_missing_validators | Gauge | | Number of validators who did not sign |
|
||||
| consensus_missing_validators_power | Gauge | | Total voting power of the missing validators |
|
||||
| consensus_byzantine_validators | Gauge | | Number of validators who tried to double sign |
|
||||
| consensus_byzantine_validators_power | Gauge | | Total voting power of the byzantine validators |
|
||||
| consensus_block_interval_seconds | Histogram | | Time between this and last block (Block.Header.Time) in seconds |
|
||||
| consensus_rounds | Gauge | | Number of rounds |
|
||||
| consensus_num_txs | Gauge | | Number of transactions |
|
||||
| consensus_total_txs | Gauge | | Total number of transactions committed |
|
||||
| consensus_block_parts | counter | peer_id | number of blockparts transmitted by peer |
|
||||
| consensus_latest_block_height | gauge | | /status sync_info number |
|
||||
| consensus_fast_syncing | gauge | | either 0 (not fast syncing) or 1 (syncing) |
|
||||
| consensus_state_syncing | gauge | | either 0 (not state syncing) or 1 (syncing) |
|
||||
| consensus_block_size_bytes | Gauge | | Block size in bytes |
|
||||
| evidence_pool_num_evidence | Gauge | | Number of evidence in the evidence pool
|
||||
| p2p_peers | Gauge | | Number of peers node's connected to |
|
||||
| p2p_peer_receive_bytes_total | counter | peer_id, chID | number of bytes per channel received from a given peer |
|
||||
| p2p_peer_send_bytes_total | counter | peer_id, chID | number of bytes per channel sent to a given peer |
|
||||
| p2p_peer_pending_send_bytes | gauge | peer_id | number of pending bytes to be sent to a given peer |
|
||||
| p2p_num_txs | gauge | peer_id | number of transactions submitted by each peer_id |
|
||||
| p2p_pending_send_bytes | gauge | peer_id | amount of data pending to be sent to peer |
|
||||
| mempool_size | Gauge | | Number of uncommitted transactions |
|
||||
| mempool_tx_size_bytes | histogram | | transaction sizes in bytes |
|
||||
| mempool_failed_txs | counter | | number of failed transactions |
|
||||
| mempool_recheck_times | counter | | number of transactions rechecked in the mempool |
|
||||
| state_block_processing_time | histogram | | time between BeginBlock and EndBlock in ms |
|
||||
| **Name** | **Type** | **Tags** | **Description** |
|
||||
|-----------------------------------------|-----------|-----------------|--------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| abci_connection_method_timing | Histogram | method, type | Timings for each of the ABCI methods |
|
||||
| consensus_height | Gauge | | Height of the chain |
|
||||
| consensus_validators | Gauge | | Number of validators |
|
||||
| consensus_validators_power | Gauge | | Total voting power of all validators |
|
||||
| consensus_validator_power | Gauge | | Voting power of the node if in the validator set |
|
||||
| consensus_validator_last_signed_height | Gauge | | Last height the node signed a block, if the node is a validator |
|
||||
| consensus_validator_missed_blocks | Gauge | | Total amount of blocks missed for the node, if the node is a validator |
|
||||
| consensus_missing_validators | Gauge | | Number of validators who did not sign |
|
||||
| consensus_missing_validators_power | Gauge | | Total voting power of the missing validators |
|
||||
| consensus_byzantine_validators | Gauge | | Number of validators who tried to double sign |
|
||||
| consensus_byzantine_validators_power | Gauge | | Total voting power of the byzantine validators |
|
||||
| consensus_block_interval_seconds | Histogram | | Time between this and last block (Block.Header.Time) in seconds |
|
||||
| consensus_rounds | Gauge | | Number of rounds |
|
||||
| consensus_num_txs | Gauge | | Number of transactions |
|
||||
| consensus_total_txs | Gauge | | Total number of transactions committed |
|
||||
| consensus_block_parts | Counter | peer_id | number of blockparts transmitted by peer |
|
||||
| consensus_latest_block_height | gauge | | /status sync_info number |
|
||||
| consensus_fast_syncing | gauge | | either 0 (not fast syncing) or 1 (syncing) |
|
||||
| consensus_state_syncing | gauge | | either 0 (not state syncing) or 1 (syncing) |
|
||||
| consensus_block_size_bytes | Gauge | | Block size in bytes |
|
||||
| consensus_step_duration | Histogram | step | Histogram of durations for each step in the consensus protocol |
|
||||
| consensus_block_gossip_receive_latency | Histogram | | Histogram of time taken to receive a block in seconds, measure between when a new block is first discovered to when the block is completed |
|
||||
| consensus_block_gossip_parts_received | Counter | matches_current | Number of block parts received by the node |
|
||||
| consensus_quorum_prevote_delay | Gauge | | Interval in seconds between the proposal timestamp and the timestamp of the earliest prevote that achieved a quorum |
|
||||
| consensus_full_prevote_delay | Gauge | | Interval in seconds between the proposal timestamp and the timestamp of the latest prevote in a round where all validators voted |
|
||||
| consensus_proposal_timestamp_difference | Histogram | | Difference between the timestamp in the proposal message and the local time of the validator at the time it received the message |
|
||||
| consensus_vote_extension_receive_count | Counter | status | Number of vote extensions received |
|
||||
| consensus_proposal_receive_count | Counter | status | Total number of proposals received by the node since process start |
|
||||
| consensus_proposal_create_count | Counter | | Total number of proposals created by the node since process start |
|
||||
| consensus_round_voting_power_percent | Gauge | vote_type | A value between 0 and 1.0 representing the percentage of the total voting power per vote type received within a round |
|
||||
| consensus_late_votes | Counter | vote_type | Number of votes received by the node since process start that correspond to earlier heights and rounds than this node is currently in. |
|
||||
| evidence_pool_num_evidence | Gauge | | Number of evidence in the evidence pool |
|
||||
| p2p_peers | Gauge | | Number of peers node's connected to |
|
||||
| p2p_peer_receive_bytes_total | Counter | peer_id, chID | number of bytes per channel received from a given peer |
|
||||
| p2p_peer_send_bytes_total | Counter | peer_id, chID | number of bytes per channel sent to a given peer |
|
||||
| p2p_peer_pending_send_bytes | Gauge | peer_id | number of pending bytes to be sent to a given peer |
|
||||
| p2p_num_txs | Gauge | peer_id | number of transactions submitted by each peer_id |
|
||||
| p2p_pending_send_bytes | Gauge | peer_id | amount of data pending to be sent to peer |
|
||||
| mempool_size | Gauge | | Number of uncommitted transactions |
|
||||
| mempool_tx_size_bytes | Histogram | | transaction sizes in bytes |
|
||||
| mempool_failed_txs | Counter | | number of failed transactions |
|
||||
| mempool_recheck_times | Counter | | number of transactions rechecked in the mempool |
|
||||
| state_block_processing_time | Histogram | | time between BeginBlock and EndBlock in ms |
|
||||
| state_consensus_param_updates | Counter | | number of consensus parameter updates returned by the application since process start |
|
||||
| state_validator_set_updates | Counter | | number of validator set updates returned by the application since process start |
|
||||
|
||||
## Useful queries
|
||||
|
||||
|
||||
+3
-3
@@ -53,10 +53,10 @@ sections.
|
||||
- [RFC-013: ABCI++](./rfc-013-abci++.md)
|
||||
- [RFC-014: Semantic Versioning](./rfc-014-semantic-versioning.md)
|
||||
- [RFC-015: ABCI++ Tx Mutation](./rfc-015-abci++-tx-mutation.md)
|
||||
- [RFC-016: Node Architecture](./rfc-016-node-architecture.md)
|
||||
- [RFC-017: ABCI++ Vote Extension Propagation](./rfc-017-abci++-vote-extension-propag.md)
|
||||
- [RFC-018: BLS Signature Aggregation Exploration](./rfc-018-bls-agg-exploration.md)
|
||||
- [RFC-019: Configuration File Versioning](./rfc-019-config-version.md)
|
||||
|
||||
|
||||
- [RFC-017: ABCI++ Vote Extension Propagation](./rfc-017-abci++-vote-extension-propag.md)
|
||||
- [RFC-020: Onboarding Projects](./rfc-020-onboarding-projects.rst)
|
||||
|
||||
<!-- - [RFC-NNN: Title](./rfc-NNN-title.md) -->
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 24 KiB |
@@ -31,12 +31,12 @@ not reference the new parameters. Any nodes joining the network with the newer
|
||||
version of Tendermint will have the new consensus parameters. Tendermint will need
|
||||
to handle this case so that new versions of Tendermint with new consensus parameters
|
||||
can still validate old blocks correctly without having to do anything overly complex
|
||||
or hacky.
|
||||
or hacky.
|
||||
|
||||
### Allowing Developer-Defined Values and the `EndBlock` Problem
|
||||
|
||||
When new consensus parameters are added, application developers may wish to set
|
||||
values for them so that the developer-defined values may be used as soon as the
|
||||
values for them so that the developer-defined values may be used as soon as the
|
||||
software upgrades. We do not currently have a clean mechanism for handling this.
|
||||
|
||||
Consensus parameter updates are communicated from the application to Tendermint
|
||||
@@ -51,7 +51,7 @@ can take effect is height `H+1`. As of now, height `H` must run with the default
|
||||
|
||||
### Hash Compatibility
|
||||
|
||||
This section discusses possible solutions to the problem of maintaining backwards-compatibility
|
||||
This section discusses possible solutions to the problem of maintaining backwards-compatibility
|
||||
of hashed parameters while adding new parameters.
|
||||
|
||||
#### Never Hash Defaults
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
# RFC 016: Node Architecture
|
||||
|
||||
## Changelog
|
||||
|
||||
- April 8, 2022: Initial draft (@cmwaters)
|
||||
- April 15, 2022: Incorporation of feedback
|
||||
|
||||
## Abstract
|
||||
|
||||
The `node` package is the entry point into the Tendermint codebase, used both by the command line and programatically to create the nodes that make up a network. The package has suffered the most from the evolution of the codebase, becoming bloated as developers clipped on their bits of code here and there to get whatever feature they wanted working.
|
||||
|
||||
The decisions made at the node level have the biggest impact to simplifying the protocols within them, unlocking better internal designs and making Tendermint more intuitive to use and easier to understand from the outside. Work, in minor increments, has already begun on this section of the codebase. This document exists to spark forth the necessary discourse in a few related areas that will help the team to converge on the long term makeup of the node.
|
||||
|
||||
## Discussion
|
||||
|
||||
The following is a list of points of discussion around the architecture of the node:
|
||||
|
||||
### Dependency Tree
|
||||
|
||||
The node object is currently stuffed with every component that possibly exists within Tendermint. In the constructor, all objects are built and interlaid with one another in some awkward dance. My guiding principle is that the node should only be made up of the components that it wants to have direct control of throughout its life. The node is a service which currently has the purpose of starting other services up in a particular order and stopping them all when commanded to do so. However, there are many services which are not direct dependents i.e. the mempool and evidence services should only be working when the consensus service is running. I propose to form more of a hierarchical structure of dependents which forces us to be clear about the relations that one component has to the other. More concretely, I propose the following dependency tree:
|
||||
|
||||

|
||||
|
||||
Many of the further discussion topics circle back to this representation of the node.
|
||||
|
||||
It's also important to distinguish two dimensions which may require different characteristics of the architecture. There is the starting and stopping of services and their general lifecycle management. What is the correct order of operations to starting a node for example. Then there is the question of the needs of the service during actual operation. Then there is the question of what resources each service needs access to during its operation. Some need to publish events, others need access to data stores, and so forth.
|
||||
|
||||
An alternative model and one that perhaps better suits the latter of these dimensions is the notion of an internal message passing system. Either the events bus or p2p layer could serve as a viable transport. This would essentially allow all services to communicate with any other service and could perhaps provide a solution to the coordination problem (presented below) without a centralized coordinator. The other main advantage is that such a system would be more robust to disruptions and changes to the code which may make a hierarchical structure quickly outdated and suboptimal. The addition of message routing is an added complexity to implement, will increase the degree of asynchronicity in the system and may make it harder to debug problems that are across multiple services.
|
||||
|
||||
### Coordination of State Advancing Mechanisms
|
||||
|
||||
Advancement of state in Tendermint is simply defined in heights: If the node is at height n, how does it get to height n + 1 and so on. Based on this definition we have three components that help a node to advance in height: consensus, statesync and blocksync. The way these components behave currently is very tightly coupled to one another with references passed back and forth. My guiding principle is that each of these should be able to operate completely independently of each other, e.g. a node should be able to run solely blocksync indefinitely. There have been several ideas suggested towards improving this flow. I've been leaning strongly towards a centralized system, whereby an orchestrator (in this case the node) decides what services to start and stop.
|
||||
In a decentralized message passing system, individual services make their decision based upon a "global" shared state i.e. if my height is less that 10 below the average peer height, I as consensus, should stop (knowing that blocksync has the same condition for starting). As the example illustrates, each mechanism will still need to be aware of the presence of other mechanisms.
|
||||
|
||||
Both centralized and decentralized systems rely on the communication of the nodes current height and a judgement on the height of the head of the chain. The latter, working out the head of the chain, is quite a difficult challenge as their is nothing preventing the node from acting maliciously and providing a different height. Currently both blocksync, consensus (and to a certain degree statesync), have parallel systems where peers communicate their height. This could be streamlined with the consensus (or even the p2p layer), broadcasting peer heights and either the node or the other state advancing mechanisms acting accordingly.
|
||||
|
||||
Currently, when a node starts, it turns on every service that it is attached to. This means that while a node is syncing up by requesting blocks, it is also receiving transactions and votes, as well as snapshot and block requests. This is a needless use of bandwidth. An implementation of an orchestrator, regardless of whether the system is heirachical or not, should look to be able to open and close channels dynamically and effectively broadcast which services it is running. Integrating this with service discovery may also lead to a better serivce to peers.
|
||||
|
||||
The orchestrator allows for some deal of variablity in how a node is constructed. Does it just run blocksync, shadowing the head of the chain and be highly available for querying. Does it rely on state sync at all? An important question that arises from this dynamicism is we ideally want to encourage nodes to provide as much of their resources as possible so that their is a healthy amount of providers to consumers. Do we make all services compulsory or allow for them to be disabled? Arguably it's possible that a user forks the codebase and rips out the blocksync code because they want to reduce bandwidth so this is more a question of how easy do we want to make this for users.
|
||||
|
||||
### Block Executor
|
||||
|
||||
The block executor is an important component that is currently used by both consensus and blocksync to execute transactions and update application state. Principally, I think it should be the only component that can write (and possibly even read) the block and state stores, and we should clean up other direct dependencies on the storage engine if we can. This would mean:
|
||||
|
||||
- The reactors Consensus, BlockSync and StateSync should all import the executor for advancing state ie. `ApplyBlock` and `BootstrapState`.
|
||||
- Pruning should also be a concern of the block executor as well as `FinalizeBlock` and `Commit`. This can simplify consensus to focus just on the consensus part.
|
||||
|
||||
### The Interprocess communication systems: RPC, P2P, ABCI, and Events
|
||||
|
||||
The schematic supplied above shows the relations between the different services, the node, the block executor, and the storage layer. Represented as colored dots are the components responsible for different roles of interprocess communication (IPC). These components permeate throughout the code base, seeping into most services. What can provide powerful functionality on one hand can also become a twisted vine, creating messy corner cases and convoluting the protocols themselves. A lot of the thinking around
|
||||
how we want our IPC systens to function has been summarised in this [RFC](./rfc-002-ipc-ecosystem.md). In this section, I'd like to focus the reader on the relation between the IPC and the node structure. An issue that has frequently risen is that the RPC has control of the components where it strikes me as being more logical for the component to dictate the information that is emitted/available and the knobs it wishes to expose. The RPC is also inextricably tied to the node instance and has situations where it is passed pointers directly to the storage engine and other components.
|
||||
|
||||
I am currently convinced of the approach that the p2p layer takes and would like to see other IPC components follow suit. This would mean that the RPC and events system would be constructed in the node yet would pass the adequate methods to register endpoints and topics to the sub components. For example,
|
||||
|
||||
```go
|
||||
// Methods from the RPC and event bus that would be passed into the constructor of components like "consensus"
|
||||
// NOTE: This is a hypothetical construction to convey the idea. An actual implementation may differ.
|
||||
func RegisterRoute(path string, handler func(http.ResponseWriter, *http.Request))
|
||||
|
||||
func RegisterTopic(name string) EventPublisher
|
||||
|
||||
type EventPublisher func (context.Context, types.EventData, []abci.Event)
|
||||
```
|
||||
|
||||
This would give the components control to the information they want to expose and keep all relevant logic within that package. It accomodates more to a dynamic system where services can switch on and off. Each component would also receive access to the logger and metrics system for introspection and debuggability.
|
||||
|
||||
#### IPC Rubric
|
||||
|
||||
I'd like to aim to reach a state where we as a team have either an implicit or explicit rubric which can determine, in the event of some new need to communicate information, what tool it should use for doing this. In the case of inter node communication, this is obviously the p2p stack (with perhaps the exception of the light client). Metrics and logging also have clear usage patterns. RPC and the events system are less clear. The RPC is used for debugging data and fine tuned operator control as it is for general public querying and transaction submission. The RPC is also known to have been plumbed back into the application for historical queries. The events system, similarly, is used for consuming transaction events as it is for the testing of consensus state transitions.
|
||||
|
||||
Principally, I think we should look to change our language away from what the actual transport is and more towards what it's being used for and to whom. We call it a peer to peer layer and not the underlying tcp connection. In the same way, we should look to split RPC into an operator interface (RPC Internal), a public interface (RPC External) and a bidirectional ABCI.
|
||||
|
||||
### Seperation of consumers and suppliers
|
||||
|
||||
When a service such as blocksync is turned on, it automatically begins requesting blocks to verify and apply them as it also tries to serve them to other peers catching up. We should look to distinguish these two aspects: supplying of information and consuming of information in many of these components. More concretely, I'd suggest:
|
||||
|
||||
- The blocksync and statesync service, i.e. supplying information for those trying to catch up should only start running once a node has caught up i.e. after running the blocksync and/or state sync *processes*
|
||||
- The blocksync and state sync processes have defined termination clauses that inform the orchestrator when they are done and where they finished.
|
||||
- One way of achieving this would be that every process both passes and returns the `State` object
|
||||
- In some cases, a node may specify that it wants to run blocksync indefinitely.
|
||||
- The mempool should also indicate whether it wants to receive transactions or to send them only (one-directional mempool)
|
||||
- Similarly, the light client itself only requests information whereas the light client service (currently part of state sync) can do both.
|
||||
- This distinction needs to be communicated in the p2p layer handshake itself but should also be changeable over the lifespan of the connection.
|
||||
@@ -0,0 +1,240 @@
|
||||
=======================================
|
||||
RFC 020: Tendermint Onboarding Projects
|
||||
=======================================
|
||||
|
||||
.. contents::
|
||||
:backlinks: none
|
||||
|
||||
Changelog
|
||||
---------
|
||||
|
||||
- 2022-03-30: Initial draft. (@tychoish)
|
||||
- 2022-04-25: Imported document to tendermint repository. (@tychoish)
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
This document describes a collection of projects that might be good for new
|
||||
engineers joining the Tendermint Core team. These projects mostly describe
|
||||
features that we'd be very excited to see land in the code base, but that are
|
||||
intentionally outside of the critical path of a release on the roadmap, and
|
||||
have the following properties that we think make good on-boarding projects:
|
||||
|
||||
- require relatively little context for the project or its history beyond a
|
||||
more isolated area of the code.
|
||||
|
||||
- provide exposure to different areas of the codebase, so new team members
|
||||
will have reason to explore the code base, build relationships with people
|
||||
on the team, and gain experience with more than one area of the system.
|
||||
|
||||
- be of moderate size, striking a healthy balance between trivial or
|
||||
mechanical changes (which provide little insight) and large intractable
|
||||
changes that require deeper insight than is available during onboarding to
|
||||
address well. A good size project should have natural touchpoints or
|
||||
check-ins.
|
||||
|
||||
Projects
|
||||
--------
|
||||
|
||||
Before diving into one of these projects, have a conversation about the
|
||||
project or aspects of Tendermint that you're excited to work on with your
|
||||
onboarding buddy. This will help make sure that these issues are still
|
||||
relevant, help you get any context, underatnding known pitfalls, and to
|
||||
confirm a high level approach or design (if relevant.) On-boarding buddies
|
||||
should be prepared to do some design work before someone joins the team.
|
||||
|
||||
The descriptions that follow provide some basic background and attempt to
|
||||
describe the user stories and the potential impact of these project.
|
||||
|
||||
E2E Test Systems
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Tendermint's E2E framework makes it possible to run small test networks with
|
||||
different Tendermint configurations, and make sure that the system works. The
|
||||
tests run Tendermint in a separate binary, and the system provides some very
|
||||
high level protection against making changes that could break Tendermint in
|
||||
otherwise difficult to detect ways.
|
||||
|
||||
Working on the E2E system is a good place to get introduced to the Tendermint
|
||||
codebase, particularly for developers who are newer to Go, as the E2E
|
||||
system (generator, runner, etc.) is distinct from the rest of Tendermint and
|
||||
comparatively quite small, so it may be easier to begin making changes in this
|
||||
area. At the same time, because the E2E system exercises *all* of Tendermint,
|
||||
work in this area is a good way to get introduced to various components of the
|
||||
system.
|
||||
|
||||
Configurable E2E Workloads
|
||||
++++++++++++++++++++++++++
|
||||
|
||||
All E2E tests use the same workload (e.g. generated transactions, submitted to
|
||||
different nodes in the network,) which has been tuned empirically to provide a
|
||||
gentle but consistent parallel load that all E2E tests can pass. Ideally, the
|
||||
workload generator could be configurable to have different shapes of work
|
||||
(bursty, different transaction sizes, weighted to different nodes, etc.) and
|
||||
even perhaps further parameterized within a basic shape, which would make it
|
||||
possible to use our existing test infrastructure to answer different questions
|
||||
about the performance or capability of the system.
|
||||
|
||||
The work would involve adding a new parameter to the E2E test manifest, and
|
||||
creating an option (e.g. "legacy") for the current load generation model,
|
||||
extract configurations options for the current load generation, and then
|
||||
prototype implementations of alternate load generation, and also run some
|
||||
preliminary using the tools.
|
||||
|
||||
Byzantine E2E Workloads
|
||||
+++++++++++++++++++++++
|
||||
|
||||
There are two main kinds of integration tests in Tendermint: the E2E test
|
||||
framework, and then a collection of integration tests that masquerade as
|
||||
unit-tests. While some of this expansion of test scope is (potentially)
|
||||
inevitable, the masquerading unit tests (e.g ``consensus.byzantine_test.go``)
|
||||
end up being difficult to understand, difficult to maintain, and unreliable.
|
||||
|
||||
One solution to this, would be to modify the E2E ABCI application to allow it
|
||||
to inject byzantine behavior, and then have this be a configurable aspect of
|
||||
a test network to be able to provoke Byzantine behavior in a "real" system and
|
||||
then observe that evidence is constructed. This would make it possible to
|
||||
remove the legacy tests entirely once the new tests have proven themselves.
|
||||
|
||||
Abstract Orchestration Framework
|
||||
++++++++++++++++++++++++++++++++
|
||||
|
||||
The orchestration of e2e test processes is presently done using docker
|
||||
compose, which works well, but has proven a bit limiting as all processes need
|
||||
to run on a single machine, and the log aggregation functions are confusing at
|
||||
best.
|
||||
|
||||
This project would replace the current orchestration with something more
|
||||
generic, potentially maintaining the current system, but also allowing the e2e
|
||||
tests to manage processes using k8s. There are a few "local" k8s frameworks
|
||||
(e.g. kind and k3s,) which might be able to be useful for our current testing
|
||||
model, but hopefully, we could use this new implementation with other k8s
|
||||
systems for more flexible distribute test orchestration.
|
||||
|
||||
Improve Operationalize Experience of ``run-multiple.sh``
|
||||
++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
The e2e test runner currently runs a single test, and in most cases we manage
|
||||
the test cases using a shell script that ensure cleanup of entire test
|
||||
suites. This is a bit difficult to maintain and makes reproduction of test
|
||||
cases more awkward than it should be. The e2e ``runner`` itself should provide
|
||||
equivalent functionality to ``run-multiple.sh``: ensure cleanup of test cases,
|
||||
collect and process output, and be able to manage entire suites of cases.
|
||||
|
||||
It might also be useful to implement an e2e test orchestrator that runs all
|
||||
tendermint instances in a single process, using "real" networks for faster
|
||||
feedback and iteration during development.
|
||||
|
||||
In addition to being a bit easier to maintain, having a more capable runner
|
||||
implementation would make it easier to collect data from test runs, improve
|
||||
debugability and reporting.
|
||||
|
||||
Fan-Out For CI E2E Tests
|
||||
++++++++++++++++++++++++
|
||||
|
||||
While there are some parallelism in the execution of e2e tests, each e2e test
|
||||
job must build a tendermint e2e image, which takes about 5 minutes of CPU time
|
||||
per-task, which given the size of each of the runs.
|
||||
|
||||
We'd like to be able to reduce the amount of overhead per-e2e tests while
|
||||
keeping the cycle time for working with the tests very low, while also
|
||||
maintaining a reasonable level of test coverage. This is an impossible
|
||||
tradeoff, in some ways, and the percentage of overhead at the moment is large
|
||||
enough that we can make some material progress with a moderate amount of time.
|
||||
|
||||
Most of this work has to do with modifying github actions configuration and
|
||||
e2e artifact (docker) building to reduce redundant work. Eventually, when we
|
||||
can drop the requirement for CGo storage engines, it will be possible to move
|
||||
(cross) compile tendermint locally, and then inject the binary into the docker
|
||||
container, which would reduce a lot of the build-time complexity, although we
|
||||
can move more in this direction or have runtime flags to disable CGo
|
||||
dependencies for local development.
|
||||
|
||||
Remove Panics
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
There are lots of places in the code base which can panic, and would not be
|
||||
particularly well handled. While in some cases, panics are the right answer,
|
||||
in many cases the panics were just added to simplify downstream error
|
||||
checking, and could easily be converted to errors.
|
||||
|
||||
The `Don't Panic RFC
|
||||
<https://github.com/tendermint/tendermint/blob/master/docs/rfc/rfc-008-do-not-panic.MD>`_
|
||||
covers some of the background and approach.
|
||||
|
||||
While the changes are in this project are relatively rote, this will provide
|
||||
exposure to lots of different areas of the codebase as well as insight into
|
||||
how different areas of the codebase interact with eachother, as well as
|
||||
experience with the test suites and infrastructure.
|
||||
|
||||
Implement more Expressive ABCI Applications
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Tendermint maintains two very simple ABCI applications (a KV application used
|
||||
for basic testing, and slightly more advanced test application used in the
|
||||
end-to-end tests). Writing an application would provide a new engineer with
|
||||
useful experiences using Tendermint that mirrors the expierence of downstream
|
||||
users.
|
||||
|
||||
This is more of an exploratory project, but could include providing common
|
||||
interfaces on top of Tendermint consensus for other well known protocols or
|
||||
tools (e.g. ``etcd``) or a DNS server or some other tool.
|
||||
|
||||
Self-Regulating Reactors
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Currently reactors (the internal processes that are responsible for the higher
|
||||
level behavior of Tendermint) can be started and stopped, but have no
|
||||
provision for being paused. These additional semantics may allow Tendermint to
|
||||
pause reactors (and avoid processing their messhages, etc.) and allow better
|
||||
coordination in the future.
|
||||
|
||||
While this is a big project, it's possible to break this apart into many
|
||||
smaller projects: make p2p channels pauseable, add pause/UN-pause hooks to the
|
||||
service implementation and machinery, and finally to modify the reactor
|
||||
implementations to take advantage of these additional semantics
|
||||
|
||||
This project would give an engineer some exposure to the p2p layer of the
|
||||
code, as well as to various aspects of the reactor implementations.
|
||||
|
||||
Metrics
|
||||
~~~~~~~
|
||||
|
||||
Tendermint has a metrics system that is relatively underutilized, and figuring
|
||||
out ways to capture and organize the metrics to provide value to users might
|
||||
provide an interesting set of projects for new engineers on Tendermint.
|
||||
|
||||
Convert Logs to Metrics
|
||||
+++++++++++++++++++++++
|
||||
|
||||
Because the tendermint logs tend to be quite verbose and not particularly
|
||||
actionable, most users largely ignore the logging or run at very low
|
||||
verbosity. While the log statements in the code do describe useful events,
|
||||
taken as a whole the system is not particularly tractable, and particularly at
|
||||
the Debug level, not useful. One solution to this problem is to identify log
|
||||
messages that might be (e.g. increment a counter for certian kinds of errors)
|
||||
|
||||
One approach might be to look at various logging statements, particularly
|
||||
debug statements or errors that are logged but not returned, and see if
|
||||
they're convertable to counters or other metrics.
|
||||
|
||||
Expose Metrics to Tests
|
||||
+++++++++++++++++++++++
|
||||
|
||||
The existing Tendermint test suites replace the metrics infrastructure with
|
||||
no-op implementations, which means that tests can neither verify that metrics
|
||||
are ever recorded, nor can tests use metrics to observe events in the
|
||||
system. Writing an implementation, for testing, that makes it possible to
|
||||
record metrics and provides an API for introspecting this data, as well as
|
||||
potentially writing tests that take advantage of this type, could be useful.
|
||||
|
||||
Logging Metrics
|
||||
+++++++++++++++
|
||||
|
||||
In some systems, the logging system itself can provide some interesting
|
||||
insights for operators: having metrics that track the number of messages at
|
||||
different levels as well as the total number of messages, can act as a canary
|
||||
for the system as a whole.
|
||||
|
||||
This should be achievable by adding an interceptor layer within the logging
|
||||
package itself that can add metrics to the existing system.
|
||||
@@ -2,74 +2,228 @@
|
||||
order: 7
|
||||
---
|
||||
|
||||
# Subscribing to events via Websocket
|
||||
# Subscribing to Events
|
||||
|
||||
Tendermint emits different events, which you can subscribe to via
|
||||
[Websocket](https://en.wikipedia.org/wiki/WebSocket). This can be useful
|
||||
for third-party applications (for analysis) or for inspecting state.
|
||||
A Tendermint node emits events about important state transitions during
|
||||
consensus. These events can be queried by clients via the [RPC interface][rpc]
|
||||
on nodes that enable it. The [list of supported event types][event-types] can
|
||||
be found in the tendermint/types Go package.
|
||||
|
||||
[List of events](https://godoc.org/github.com/tendermint/tendermint/types#pkg-constants)
|
||||
In Tendermint v0.36 there are two APIs to query events:
|
||||
|
||||
To connect to a node via websocket from the CLI, you can use a tool such as
|
||||
[wscat](https://github.com/websockets/wscat) and run:
|
||||
- The [**legacy streaming API**](#legacy-streaming-api), comprising the
|
||||
`subscribe`, `unsubscribe`, and `unsubscribe_all` RPC methods over websocket.
|
||||
|
||||
- The [**event log API**](#event-log-api), comprising the `events` RPC method.
|
||||
|
||||
The legacy streaming API is deprecated in Tendermint v0.36, and will be removed
|
||||
in Tendermint v0.37. Clients are strongly encouraged to migrate to the new
|
||||
event log API as soon as is practical.
|
||||
|
||||
[rpc]: https://docs.tendermint.com/master/rpc
|
||||
[event-types]: https://godoc.org/github.com/tendermint/tendermint/types#EventNewBlockValue
|
||||
|
||||
## Filter Queries
|
||||
|
||||
Event requests take a [filter query][query] parameter. A filter query is a
|
||||
string that describes a subset of available event items to return. An empty
|
||||
query matches all events; otherwise a query comprises one or more *terms*
|
||||
comparing event metadata to target values.
|
||||
|
||||
For example, to select new block events, use the term:
|
||||
|
||||
```
|
||||
tm.event = 'NewBlock'
|
||||
```
|
||||
|
||||
Multiple terms can be combined with `AND` (case matters), for example to match
|
||||
the transaction event with a given hash, use the query:
|
||||
|
||||
```
|
||||
tm.event = 'Tx' AND tx.hash = 'EA7B33F'
|
||||
```
|
||||
|
||||
Operands may be strings in single quotes (`'Tx'`), numbers (`45`), dates, or
|
||||
timestamps.
|
||||
|
||||
The comparison operators include `=`, `<`, `<=`, `>`, `>=`, and `CONTAINS` (for
|
||||
substring match). In addition, the `EXISTS` operator checks for the presence
|
||||
of an attribute regardless of its value.
|
||||
|
||||
### Attributes
|
||||
|
||||
Tendermint implicitly defines a string-valued `tm.event` attribute for all
|
||||
event types. Transaction items (type `Tx`) are also assigned `tx.hash`
|
||||
(string), giving the hash of the transaction, and and `tx.height` (number)
|
||||
giving the height of the block containing the transaction. For `NewBlock` and
|
||||
`NewBlockHeader` events, Tendermint defines a `block.height` attribute giving
|
||||
the height of the block.
|
||||
|
||||
Additional attributes can be provided by the application as [ABCI `Event`
|
||||
records][abci-event] in response to the `FinalizeBlock` request. The full name
|
||||
of the attribute in the query is formed by combining the `type` and attribute
|
||||
`key` with a period.
|
||||
|
||||
For example, given the events
|
||||
|
||||
```go
|
||||
[]abci.Event{{
|
||||
Type: "reward",
|
||||
Attributes: []abci.EventAttribute{
|
||||
{Key: "address", Value: "cosmos1xyz012pdq"},
|
||||
{Key: "amount", Value: "45.62"},
|
||||
{Key: "balance", Value: "100.390001"},
|
||||
},
|
||||
}}
|
||||
```
|
||||
|
||||
a query may refer to the names `reward.address`, `reward.amount`, and `reward.balance`, as in:
|
||||
|
||||
```
|
||||
reward.address EXISTS AND reward.balance > 45
|
||||
```
|
||||
|
||||
Certain application-specific metadata are also indexed for offline queries.
|
||||
See [Indexing transactions](../app-dev/indexing-transactions.md) for more details.
|
||||
|
||||
[query]: https://godoc.org/github.com/tendermint/tendermint/internal/pubsub/query/syntax
|
||||
[abci-event]: https://github.com/tendermint/tendermint/blob/master/proto/tendermint/abci/types.proto#L397
|
||||
|
||||
## Event Log API
|
||||
|
||||
Starting in Tendermint v0.36, when the `rpc.event-log-window-size`
|
||||
configuration is enabled, the node maintains maintains a log of all events
|
||||
within this operator-defined time window. This API supersedes the websocket
|
||||
subscription API described below.
|
||||
|
||||
Clients can query these events can by long-polling the `/events` RPC method,
|
||||
which returns the most recent items from the log that match the [request
|
||||
parameters][reqevents]. Each item returned includes a cursor that marks its
|
||||
location in the log. Cursors can be passed via the `before` and `after`
|
||||
parameters to fetch events earlier in the log.
|
||||
|
||||
For example, this request:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "events",
|
||||
"params": {
|
||||
"filter": {
|
||||
"query": "tm.event = 'Tx' AND app.key = 'applesauce'"
|
||||
},
|
||||
"maxItems": 1,
|
||||
"after": ""
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
will return a result similar to the following:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": {
|
||||
"items": [
|
||||
{
|
||||
"cursor": "16ee3d5e65be53d8-03d5",
|
||||
"event": "Tx",
|
||||
"data": {
|
||||
"type": "tendermint/event/Tx",
|
||||
"value": {
|
||||
"height": 70,
|
||||
"tx": "YXBwbGVzYXVjZT1zeXJ1cA==",
|
||||
"result": {
|
||||
"events": [
|
||||
{
|
||||
"type": "app",
|
||||
"attributes": [
|
||||
{
|
||||
"key": "creator",
|
||||
"value": "Cosmoshi Netowoko",
|
||||
"index": true
|
||||
},
|
||||
{
|
||||
"key": "key",
|
||||
"value": "applesauce",
|
||||
"index": true
|
||||
},
|
||||
{
|
||||
"key": "index_key",
|
||||
"value": "index is working",
|
||||
"index": true
|
||||
},
|
||||
{
|
||||
"key": "noindex_key",
|
||||
"value": "index is working",
|
||||
"index": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"more": false,
|
||||
"oldest": "16ee3d4c471c3b00-0001",
|
||||
"newest": "16ee3d5f2e05a4e0-0400"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `"items"` array gives the matching items (up to the requested
|
||||
`"maxResults"`) in decreasing time order (i.e., newest to oldest). In this
|
||||
case, there is only one result, but if there are additional results that were
|
||||
not returned, the `"more"` flag will be true. Calling `/events` again with the
|
||||
same query and `"after"` set to the cursor of the newest result (in this
|
||||
example, `"16ee3d5e65be53d8-03d5"`) will fetch newer results.
|
||||
|
||||
Go clients can use the [`eventstream`][eventstream] package to simplify the use
|
||||
of this method. The `eventstream.Stream` automatically handles polling for new
|
||||
events, updating the cursor, and reporting any missed events.
|
||||
|
||||
[reqevents]: https://pkg.go.dev/github.com/tendermint/tendermint@master/rpc/coretypes#RequestEvents
|
||||
[eventstream]: https://godoc.org/github.com/tendermint/tendermint/rpc/client/eventstream
|
||||
|
||||
## Legacy Streaming API
|
||||
|
||||
- **Note:** This API is deprecated in Tendermint v0.36, and will be removed in
|
||||
Tendermint v0.37. New clients and existing use should use the [event log
|
||||
API](#event-log-api) instead. See [ADR 075][adr075] for more details.
|
||||
|
||||
To subscribe to events in the streaming API, you must connect to the node RPC
|
||||
service using a [websocket][ws]. From the command line you can use a tool such
|
||||
as [wscat][wscat], for example:
|
||||
|
||||
```sh
|
||||
wscat ws://127.0.0.1:26657/websocket
|
||||
```
|
||||
|
||||
You can subscribe to any of the events above by calling the `subscribe` RPC
|
||||
method via Websocket along with a valid query.
|
||||
[ws]: https://en.wikipedia.org/wiki/WebSocket
|
||||
[wscat]: https://github.com/websockets/wscat
|
||||
|
||||
To subscribe to events, call the `subscribe` JSON-RPC method method passing in
|
||||
a [filter query][query] for the events you wish to receive:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "subscribe",
|
||||
"id": 0,
|
||||
"id": 1,
|
||||
"params": {
|
||||
"query": "tm.event='NewBlock'"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Check out [API docs](https://docs.tendermint.com/master/rpc/) for
|
||||
more information on query syntax and other options.
|
||||
The subscribe method returns an initial response confirming the subscription,
|
||||
then sends additional JSON-RPC response messages containing the matching events
|
||||
as they are published. The subscription continues until either the client
|
||||
explicitly cancels the subscription (by calling `unsubscribe` or
|
||||
`unsubscribe_all`) or until the websocket connection is terminated.
|
||||
|
||||
You can also use tags, given you had included them into DeliverTx
|
||||
response, to query transaction results. See [Indexing
|
||||
transactions](../app-dev/indexing-transactions.md) for details.
|
||||
|
||||
## ValidatorSetUpdates
|
||||
|
||||
When validator set changes, ValidatorSetUpdates event is published. The
|
||||
event carries a list of pubkey/power pairs. The list is the same
|
||||
Tendermint receives from ABCI application (see [EndBlock
|
||||
section](https://github.com/tendermint/tendermint/blob/master/spec/abci/abci.md#endblock) in
|
||||
the ABCI spec).
|
||||
|
||||
Response:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"result": {
|
||||
"query": "tm.event='ValidatorSetUpdates'",
|
||||
"data": {
|
||||
"type": "tendermint/event/ValidatorSetUpdates",
|
||||
"value": {
|
||||
"validator_updates": [
|
||||
{
|
||||
"address": "09EAD022FD25DE3A02E64B0FE9610B1417183EE4",
|
||||
"pub_key": {
|
||||
"type": "tendermint/PubKeyEd25519",
|
||||
"value": "ww0z4WaZ0Xg+YI10w43wTWbBmM3dpVza4mmSQYsd0ck="
|
||||
},
|
||||
"voting_power": "10",
|
||||
"proposer_priority": "0"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
[adr075]: https://tinyurl.com/adr075
|
||||
|
||||
@@ -16,12 +16,12 @@ require (
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
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.5
|
||||
github.com/lib/pq v1.10.6
|
||||
github.com/libp2p/go-buffer-pool v0.0.2
|
||||
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
|
||||
github.com/prometheus/client_golang v1.12.1
|
||||
github.com/prometheus/client_golang v1.12.2
|
||||
github.com/rs/cors v1.8.2
|
||||
github.com/rs/zerolog v1.26.1
|
||||
github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa
|
||||
@@ -32,7 +32,7 @@ require (
|
||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
|
||||
golang.org/x/net v0.0.0-20220412020605-290c469a71a5
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
google.golang.org/grpc v1.46.0
|
||||
google.golang.org/grpc v1.46.2
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
|
||||
pgregory.net/rapid v0.4.7
|
||||
)
|
||||
@@ -40,18 +40,26 @@ require (
|
||||
require (
|
||||
github.com/creachadair/atomicfile v0.2.6
|
||||
github.com/creachadair/taskgroup v0.3.2
|
||||
github.com/golangci/golangci-lint v1.45.2
|
||||
github.com/golangci/golangci-lint v1.46.0
|
||||
github.com/google/go-cmp v0.5.8
|
||||
github.com/vektra/mockery/v2 v2.12.2
|
||||
gotest.tools v2.2.0+incompatible
|
||||
)
|
||||
|
||||
require github.com/pelletier/go-toml/v2 v2.0.0-beta.8 // indirect
|
||||
require (
|
||||
github.com/GaijinEntertainment/go-exhaustruct/v2 v2.1.0 // indirect
|
||||
github.com/firefart/nonamedreturns v1.0.1 // indirect
|
||||
github.com/lufeee/execinquery v1.0.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.0 // indirect
|
||||
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect
|
||||
github.com/stbenjam/no-sprintf-host-port v0.1.1 // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
4d63.com/gochecknoglobals v0.1.0 // indirect
|
||||
github.com/Antonboom/errname v0.1.5 // indirect
|
||||
github.com/Antonboom/nilnil v0.1.0 // indirect
|
||||
github.com/Antonboom/errname v0.1.6 // indirect
|
||||
github.com/Antonboom/nilnil v0.1.1 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/DataDog/zstd v1.4.1 // indirect
|
||||
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect
|
||||
@@ -64,18 +72,18 @@ require (
|
||||
github.com/ashanbrown/makezero v1.1.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bkielbasa/cyclop v1.2.0 // indirect
|
||||
github.com/blizzy78/varnamelen v0.6.1 // indirect
|
||||
github.com/blizzy78/varnamelen v0.8.0 // indirect
|
||||
github.com/bombsimon/wsl/v3 v3.3.0 // indirect
|
||||
github.com/breml/bidichk v0.2.2 // indirect
|
||||
github.com/breml/errchkjson v0.2.3 // indirect
|
||||
github.com/breml/bidichk v0.2.3 // indirect
|
||||
github.com/breml/errchkjson v0.3.0 // indirect
|
||||
github.com/butuzov/ireturn v0.1.1 // indirect
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
|
||||
github.com/cespare/xxhash v1.1.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/charithe/durationcheck v0.0.9 // indirect
|
||||
github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af // indirect
|
||||
github.com/chavacava/garif v0.0.0-20220316182200-5cad0b5181d4 // indirect
|
||||
github.com/containerd/continuity v0.2.1 // indirect
|
||||
github.com/creachadair/tomledit v0.0.19
|
||||
github.com/creachadair/tomledit v0.0.22
|
||||
github.com/daixiang0/gci v0.3.3 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/denis-tingaikin/go-header v0.4.3 // indirect
|
||||
@@ -92,9 +100,9 @@ require (
|
||||
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 // indirect
|
||||
github.com/fatih/color v1.13.0 // indirect
|
||||
github.com/fatih/structtag v1.2.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
||||
github.com/fzipp/gocyclo v0.4.0 // indirect
|
||||
github.com/go-critic/go-critic v0.6.2 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
github.com/fzipp/gocyclo v0.5.1 // indirect
|
||||
github.com/go-critic/go-critic v0.6.3 // indirect
|
||||
github.com/go-toolsmith/astcast v1.0.0 // indirect
|
||||
github.com/go-toolsmith/astcopy v1.0.0 // indirect
|
||||
github.com/go-toolsmith/astequal v1.0.1 // indirect
|
||||
@@ -108,7 +116,7 @@ require (
|
||||
github.com/golang/snappy v0.0.3 // indirect
|
||||
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 // indirect
|
||||
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a // indirect
|
||||
github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613 // indirect
|
||||
github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe // indirect
|
||||
github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a // indirect
|
||||
github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 // indirect
|
||||
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca // indirect
|
||||
@@ -134,10 +142,10 @@ require (
|
||||
github.com/julz/importas v0.1.0 // indirect
|
||||
github.com/kisielk/errcheck v1.6.0 // indirect
|
||||
github.com/kisielk/gotool v1.0.0 // indirect
|
||||
github.com/kulti/thelper v0.5.1 // indirect
|
||||
github.com/kulti/thelper v0.6.2 // indirect
|
||||
github.com/kunwardeep/paralleltest v1.0.3 // indirect
|
||||
github.com/kyoh86/exportloopref v0.1.8 // indirect
|
||||
github.com/ldez/gomoddirectives v0.2.2 // indirect
|
||||
github.com/ldez/gomoddirectives v0.2.3 // indirect
|
||||
github.com/ldez/tagliatelle v0.3.1 // indirect
|
||||
github.com/leonklingele/grouper v1.1.0 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
@@ -148,37 +156,37 @@ require (
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/mbilski/exhaustivestruct v1.2.0 // indirect
|
||||
github.com/mgechev/revive v1.1.4 // indirect
|
||||
github.com/mgechev/revive v1.2.1 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/moricho/tparallel v0.2.1 // indirect
|
||||
github.com/nakabonne/nestif v0.3.1 // indirect
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 // indirect
|
||||
github.com/nishanths/exhaustive v0.7.11 // indirect
|
||||
github.com/nishanths/predeclared v0.2.1 // indirect
|
||||
github.com/nishanths/predeclared v0.2.2 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||
github.com/opencontainers/runc v1.0.3 // indirect
|
||||
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/polyfloyd/go-errorlint v0.0.0-20211125173453-6d6d39c5bb8b // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.32.1 // indirect
|
||||
github.com/prometheus/client_model v0.2.0
|
||||
github.com/prometheus/common v0.34.0
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/quasilyte/go-ruleguard v0.3.15 // indirect
|
||||
github.com/quasilyte/gogrep v0.0.0-20220103110004-ffaa07af02e3 // indirect
|
||||
github.com/quasilyte/go-ruleguard v0.3.16-0.20220213074421-6aa060fab41a // indirect
|
||||
github.com/quasilyte/gogrep v0.0.0-20220120141003-628d8b3623b5 // indirect
|
||||
github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95 // indirect
|
||||
github.com/ryancurrah/gomodguard v1.2.3 // indirect
|
||||
github.com/ryanrolds/sqlclosecheck v0.3.0 // indirect
|
||||
github.com/sanposhiho/wastedassign/v2 v2.0.6 // indirect
|
||||
github.com/securego/gosec/v2 v2.10.0 // indirect
|
||||
github.com/securego/gosec/v2 v2.11.0 // indirect
|
||||
github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/sivchari/containedctx v1.0.2 // indirect
|
||||
github.com/sivchari/tenv v1.4.7 // indirect
|
||||
github.com/sivchari/tenv v1.5.0 // indirect
|
||||
github.com/sonatard/noctx v0.0.1 // indirect
|
||||
github.com/sourcegraph/go-diff v0.6.1 // indirect
|
||||
github.com/spf13/afero v1.8.2 // indirect
|
||||
@@ -194,28 +202,28 @@ require (
|
||||
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect
|
||||
github.com/tetafro/godot v1.4.11 // indirect
|
||||
github.com/timakin/bodyclose v0.0.0-20210704033933-f49887972144 // indirect
|
||||
github.com/tomarrell/wrapcheck/v2 v2.5.0 // indirect
|
||||
github.com/tomarrell/wrapcheck/v2 v2.6.1 // indirect
|
||||
github.com/tommy-muehle/go-mnd/v2 v2.5.0 // indirect
|
||||
github.com/ultraware/funlen v0.0.3 // indirect
|
||||
github.com/ultraware/whitespace v0.0.5 // indirect
|
||||
github.com/uudashr/gocognit v1.0.5 // indirect
|
||||
github.com/yagipy/maintidx v1.0.0 // indirect
|
||||
github.com/yeya24/promlinter v0.1.1-0.20210918184747-d757024714a1 // indirect
|
||||
github.com/yeya24/promlinter v0.2.0 // indirect
|
||||
gitlab.com/bosi/decorder v0.2.1 // indirect
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect
|
||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/tools v0.1.10 // indirect
|
||||
golang.org/x/tools v0.1.11-0.20220316014157-77aa08bb151a // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect
|
||||
google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
gopkg.in/ini.v1 v1.66.4 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
honnef.co/go/tools v0.2.2 // indirect
|
||||
mvdan.cc/gofumpt v0.3.0 // indirect
|
||||
honnef.co/go/tools v0.3.1 // indirect
|
||||
mvdan.cc/gofumpt v0.3.1 // indirect
|
||||
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed // indirect
|
||||
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b // indirect
|
||||
mvdan.cc/unparam v0.0.0-20211214103731-d0ef000c54e5 // indirect
|
||||
|
||||
@@ -46,7 +46,6 @@ cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJW
|
||||
cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/firestore v1.6.0/go.mod h1:afJwI0vaXwAG54kI7A//lP/lSPDkQORQuMkv56TxEPU=
|
||||
cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
@@ -62,17 +61,17 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
|
||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Antonboom/errname v0.1.5 h1:IM+A/gz0pDhKmlt5KSNTVAvfLMb+65RxavBXpRtCUEg=
|
||||
github.com/Antonboom/errname v0.1.5/go.mod h1:DugbBstvPFQbv/5uLcRRzfrNqKE9tVdVCqWCLp6Cifo=
|
||||
github.com/Antonboom/nilnil v0.1.0 h1:DLDavmg0a6G/F4Lt9t7Enrbgb3Oph6LnDE6YVsmTt74=
|
||||
github.com/Antonboom/nilnil v0.1.0/go.mod h1:PhHLvRPSghY5Y7mX4TW+BHZQYo1A8flE5H20D3IPZBo=
|
||||
github.com/Antonboom/errname v0.1.6 h1:LzIJZlyLOCSu51o3/t2n9Ck7PcoP9wdbrdaW6J8fX24=
|
||||
github.com/Antonboom/errname v0.1.6/go.mod h1:7lz79JAnuoMNDAWE9MeeIr1/c/VpSUWatBv2FH9NYpI=
|
||||
github.com/Antonboom/nilnil v0.1.1 h1:PHhrh5ANKFWRBh7TdYmyyq2gyT2lotnvFvvFbylF81Q=
|
||||
github.com/Antonboom/nilnil v0.1.1/go.mod h1:L1jBqoWM7AOeTD+tSquifKSesRHs4ZdaxvZR+xdJEaI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
|
||||
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
@@ -83,6 +82,8 @@ github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM=
|
||||
github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
||||
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM=
|
||||
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs=
|
||||
github.com/GaijinEntertainment/go-exhaustruct/v2 v2.1.0 h1:LAPPhJ4KR5Z8aKVZF5S48csJkxL5RMKmE/98fMs1u5M=
|
||||
github.com/GaijinEntertainment/go-exhaustruct/v2 v2.1.0/go.mod h1:LGOGuvEgCfCQsy3JF2tRmpGDpzA53iZfyGEWSPwQ6/4=
|
||||
github.com/HdrHistogram/hdrhistogram-go v1.1.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
|
||||
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
|
||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||
@@ -148,14 +149,14 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB
|
||||
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
||||
github.com/bkielbasa/cyclop v1.2.0 h1:7Jmnh0yL2DjKfw28p86YTd/B4lRGcNuu12sKE35sM7A=
|
||||
github.com/bkielbasa/cyclop v1.2.0/go.mod h1:qOI0yy6A7dYC4Zgsa72Ppm9kONl0RoIlPbzot9mhmeI=
|
||||
github.com/blizzy78/varnamelen v0.6.1 h1:kttPCLzXFa+0nt++Cw9fb7GrSSM4KkyIAoX/vXsbuqA=
|
||||
github.com/blizzy78/varnamelen v0.6.1/go.mod h1:zy2Eic4qWqjrxa60jG34cfL0VXcSwzUrIx68eJPb4Q8=
|
||||
github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M=
|
||||
github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k=
|
||||
github.com/bombsimon/wsl/v3 v3.3.0 h1:Mka/+kRLoQJq7g2rggtgQsjuI/K5Efd87WX96EWFxjM=
|
||||
github.com/bombsimon/wsl/v3 v3.3.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc=
|
||||
github.com/breml/bidichk v0.2.2 h1:w7QXnpH0eCBJm55zGCTJveZEkQBt6Fs5zThIdA6qQ9Y=
|
||||
github.com/breml/bidichk v0.2.2/go.mod h1:zbfeitpevDUGI7V91Uzzuwrn4Vls8MoBMrwtt78jmso=
|
||||
github.com/breml/errchkjson v0.2.3 h1:97eGTmR/w0paL2SwfRPI1jaAZHaH/fXnxWTw2eEIqE0=
|
||||
github.com/breml/errchkjson v0.2.3/go.mod h1:jZEATw/jF69cL1iy7//Yih8yp/mXp2CBoBr9GJwCAsY=
|
||||
github.com/breml/bidichk v0.2.3 h1:qe6ggxpTfA8E75hdjWPZ581sY3a2lnl0IRxLQFelECI=
|
||||
github.com/breml/bidichk v0.2.3/go.mod h1:8u2C6DnAy0g2cEq+k/A2+tr9O1s+vHGxWn0LTc70T2A=
|
||||
github.com/breml/errchkjson v0.3.0 h1:YdDqhfqMT+I1vIxPSas44P+9Z9HzJwCeAzjB8PxP1xw=
|
||||
github.com/breml/errchkjson v0.3.0/go.mod h1:9Cogkyv9gcT8HREpzi3TiqBxCqDzo8awa92zSDFcofU=
|
||||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||
github.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c=
|
||||
github.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y=
|
||||
@@ -187,8 +188,8 @@ github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cb
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/charithe/durationcheck v0.0.9 h1:mPP4ucLrf/rKZiIG/a9IPXHGlh8p4CzgpyTy6EEutYk=
|
||||
github.com/charithe/durationcheck v0.0.9/go.mod h1:SSbRIBVfMjCi/kEB6K65XEA83D6prSM8ap1UCpNKtgg=
|
||||
github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af h1:spmv8nSH9h5oCQf40jt/ufBCt9j0/58u4G+rkeMqXGI=
|
||||
github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af/go.mod h1:Qjyv4H3//PWVzTeCezG2b9IRn6myJxJSr4TD/xo6ojU=
|
||||
github.com/chavacava/garif v0.0.0-20220316182200-5cad0b5181d4 h1:tFXjAxje9thrTF4h57Ckik+scJjTWdwAtZqZPtOT48M=
|
||||
github.com/chavacava/garif v0.0.0-20220316182200-5cad0b5181d4/go.mod h1:W8EnPSQ8Nv4fUjc/v1/8tHFqhuOJXnRub0dTfuAQktU=
|
||||
github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
@@ -229,10 +230,11 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creachadair/atomicfile v0.2.6 h1:FgYxYvGcqREApTY8Nxg8msM6P/KVKK3ob5h9FaRUTNg=
|
||||
github.com/creachadair/atomicfile v0.2.6/go.mod h1:BRq8Une6ckFneYXZQ+kO7p1ZZP3I2fzVzf28JxrIkBc=
|
||||
github.com/creachadair/command v0.0.0-20220426235536-a748effdf6a1/go.mod h1:bAM+qFQb/KwWyCc9MLC4U1jvn3XyakqP5QRkds5T6cY=
|
||||
github.com/creachadair/taskgroup v0.3.2 h1:zlfutDS+5XG40AOxcHDSThxKzns8Tnr9jnr6VqkYlkM=
|
||||
github.com/creachadair/taskgroup v0.3.2/go.mod h1:wieWwecHVzsidg2CsUnFinW1faVN4+kq+TDlRJQ0Wbk=
|
||||
github.com/creachadair/tomledit v0.0.19 h1:zbpfUtYFYFdpRjwJY9HJlto1iZ4M5YwYB6qqc37F6UM=
|
||||
github.com/creachadair/tomledit v0.0.19/go.mod h1:gvtfnSZLa+YNQD28vaPq0Nk12bRxEhmUdBzAWn+EGF4=
|
||||
github.com/creachadair/tomledit v0.0.22 h1:lRtepmrwhzDq+g1gv5ftVn5itgo7CjYbm6abKTToqJ4=
|
||||
github.com/creachadair/tomledit v0.0.22/go.mod h1:cIu/4x5L855oSRejIqr+WRFh+mv9g4fWLiUFaApYn/Y=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
|
||||
@@ -299,6 +301,8 @@ github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
|
||||
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
||||
github.com/firefart/nonamedreturns v1.0.1 h1:fSvcq6ZpK/uBAgJEGMvzErlzyM4NELLqqdTofVjVNag=
|
||||
github.com/firefart/nonamedreturns v1.0.1/go.mod h1:D3dpIBojGGNh5UfElmwPu73SwDCm+VKhHYqwlNOk2uQ=
|
||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
|
||||
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||
@@ -309,14 +313,15 @@ github.com/frankban/quicktest v1.14.2 h1:SPb1KFFmM+ybpEjPUhCCkZOM5xlovT5UbrMvWnX
|
||||
github.com/frankban/quicktest v1.14.2/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
|
||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
||||
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
github.com/fullstorydev/grpcurl v1.6.0/go.mod h1:ZQ+ayqbKMJNhzLmbpCiurTVlaK2M/3nqZCxaQ2Ze/sM=
|
||||
github.com/fzipp/gocyclo v0.4.0 h1:IykTnjwh2YLyYkGa0y92iTTEQcnyAz0r9zOo15EbJ7k=
|
||||
github.com/fzipp/gocyclo v0.4.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
|
||||
github.com/fzipp/gocyclo v0.5.1 h1:L66amyuYogbxl0j2U+vGqJXusPF2IkduvXLnYD5TFgw=
|
||||
github.com/fzipp/gocyclo v0.5.1/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-critic/go-critic v0.6.2 h1:L5SDut1N4ZfsWZY0sH4DCrsHLHnhuuWak2wa165t9gs=
|
||||
github.com/go-critic/go-critic v0.6.2/go.mod h1:td1s27kfmLpe5G/DPjlnFI7o1UCzePptwU7Az0V5iCM=
|
||||
github.com/go-critic/go-critic v0.6.3 h1:abibh5XYBTASawfTQ0rA7dVtQT+6KzpGqb/J+DxRDaw=
|
||||
github.com/go-critic/go-critic v0.6.3/go.mod h1:c6b3ZP1MQ7o6lPR7Rv3lEf7pYQUmAcx8ABHgdZCQt/k=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@@ -418,12 +423,12 @@ github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5
|
||||
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4=
|
||||
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM=
|
||||
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk=
|
||||
github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613 h1:9kfjN3AdxcbsZBf8NjltjWihK2QfBBBZuv91cMFfDHw=
|
||||
github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8=
|
||||
github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe h1:6RGUuS7EGotKx6J5HIP8ZtyMdiDscjMLfRBSPuzVVeo=
|
||||
github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe/go.mod h1:gjqyPShc/m8pEMpk0a3SeagVb0kaqvhscv+i9jI5ZhQ=
|
||||
github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a h1:iR3fYXUjHCR97qWS8ch1y9zPNsgXThGwjKPrYfqMPks=
|
||||
github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU=
|
||||
github.com/golangci/golangci-lint v1.45.2 h1:9I3PzkvscJkFAQpTQi5Ga0V4qWdJERajX1UZ7QqkW+I=
|
||||
github.com/golangci/golangci-lint v1.45.2/go.mod h1:f20dpzMmUTRp+oYnX0OGjV1Au3Jm2JeI9yLqHq1/xsI=
|
||||
github.com/golangci/golangci-lint v1.46.0 h1:uz9AtEcIP63FH+FIyuAXcQGVQO4vCUavEsMTJpPeD4s=
|
||||
github.com/golangci/golangci-lint v1.46.0/go.mod h1:IJpcNOUfx/XLRwE95FHQ6QtbhYwwqcm0H5QkwUfF4ZE=
|
||||
github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 h1:MfyDlzVjl1hoaPzPD4Gpb/QgoRfSBR0jdhwGyAWwMSA=
|
||||
github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg=
|
||||
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA=
|
||||
@@ -655,15 +660,15 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kulti/thelper v0.5.1 h1:Uf4CUekH0OvzQTFPrWkstJvXgm6pnNEtQu3HiqEkpB0=
|
||||
github.com/kulti/thelper v0.5.1/go.mod h1:vMu2Cizjy/grP+jmsvOFDx1kYP6+PD1lqg4Yu5exl2U=
|
||||
github.com/kulti/thelper v0.6.2 h1:K4xulKkwOCnT1CDms6Ex3uG1dvSMUUQe9zxgYQgbRXs=
|
||||
github.com/kulti/thelper v0.6.2/go.mod h1:DsqKShOvP40epevkFrvIwkCMNYxMeTNjdWL4dqWHZ6I=
|
||||
github.com/kunwardeep/paralleltest v1.0.3 h1:UdKIkImEAXjR1chUWLn+PNXqWUGs//7tzMeWuP7NhmI=
|
||||
github.com/kunwardeep/paralleltest v1.0.3/go.mod h1:vLydzomDFpk7yu5UX02RmP0H8QfRPOV/oFhWN85Mjb4=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/kyoh86/exportloopref v0.1.8 h1:5Ry/at+eFdkX9Vsdw3qU4YkvGtzuVfzT4X7S77LoN/M=
|
||||
github.com/kyoh86/exportloopref v0.1.8/go.mod h1:1tUcJeiioIs7VWe5gcOObrux3lb66+sBqGZrRkMwPgg=
|
||||
github.com/ldez/gomoddirectives v0.2.2 h1:p9/sXuNFArS2RLc+UpYZSI4KQwGMEDWC/LbtF5OPFVg=
|
||||
github.com/ldez/gomoddirectives v0.2.2/go.mod h1:cpgBogWITnCfRq2qGoDkKMEVSaarhdBr6g8G04uz6d0=
|
||||
github.com/ldez/gomoddirectives v0.2.3 h1:y7MBaisZVDYmKvt9/l1mjNCiSA1BVn34U0ObUcJwlhA=
|
||||
github.com/ldez/gomoddirectives v0.2.3/go.mod h1:cpgBogWITnCfRq2qGoDkKMEVSaarhdBr6g8G04uz6d0=
|
||||
github.com/ldez/tagliatelle v0.3.1 h1:3BqVVlReVUZwafJUwQ+oxbx2BEX2vUG4Yu/NOfMiKiM=
|
||||
github.com/ldez/tagliatelle v0.3.1/go.mod h1:8s6WJQwEYHbKZDsp/LjArytKOG8qaMrKQQ3mFukHs88=
|
||||
github.com/leonklingele/grouper v1.1.0 h1:tC2y/ygPbMFSBOs3DcyaEMKnnwH7eYKzohOtRrf0SAg=
|
||||
@@ -673,11 +678,12 @@ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
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.5 h1:J+gdV2cUmX7ZqL2B0lFcW0m+egaHC2V3lpO8nWxyYiQ=
|
||||
github.com/lib/pq v1.10.5/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/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
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=
|
||||
github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
@@ -718,8 +724,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
|
||||
github.com/mbilski/exhaustivestruct v1.2.0 h1:wCBmUnSYufAHO6J4AVWY6ff+oxWxsVFrwgOdMUQePUo=
|
||||
github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc=
|
||||
github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg=
|
||||
github.com/mgechev/revive v1.1.4 h1:sZOjY6GU35Kr9jKa/wsKSHgrFz8eASIB5i3tqWZMp0A=
|
||||
github.com/mgechev/revive v1.1.4/go.mod h1:ZZq2bmyssGh8MSPz3VVziqRNIMYTJXzP8MUKG90vZ9A=
|
||||
github.com/mgechev/revive v1.2.1 h1:GjFml7ZsoR0IrQ2E2YIvWFNS5GPDV7xNwvA5GM1HZC4=
|
||||
github.com/mgechev/revive v1.2.1/go.mod h1:+Ro3wqY4vakcYNtkBWdZC7dBg1xSB6sp054wWwmeFm0=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
@@ -738,8 +744,9 @@ github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eI
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
|
||||
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
|
||||
@@ -777,8 +784,8 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
|
||||
github.com/nishanths/exhaustive v0.7.11 h1:xV/WU3Vdwh5BUH4N06JNUznb6d5zhRPOnlgCrpNYNKA=
|
||||
github.com/nishanths/exhaustive v0.7.11/go.mod h1:gX+MP7DWMKJmNa1HfMozK+u04hQd3na9i0hyqf3/dOI=
|
||||
github.com/nishanths/predeclared v0.0.0-20190419143655-18a43bb90ffc/go.mod h1:62PewwiQTlm/7Rj+cxVYqZvDIUc+JjZq6GHAC1fsObQ=
|
||||
github.com/nishanths/predeclared v0.2.1 h1:1TXtjmy4f3YCFjTxRd8zcFHOmoUir+gp0ESzjFzG2sw=
|
||||
github.com/nishanths/predeclared v0.2.1/go.mod h1:HvkGJcA3naj4lOwnFXFDkFxVtSqQMB9sbB1usJ+xjQE=
|
||||
github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk=
|
||||
github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
@@ -832,10 +839,12 @@ github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
|
||||
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pelletier/go-toml/v2 v2.0.0-beta.8 h1:dy81yyLYJDwMTifq24Oi/IslOslRrDSb3jwDggjz3Z0=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pelletier/go-toml/v2 v2.0.0-beta.8/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
||||
github.com/pelletier/go-toml/v2 v2.0.0 h1:P7Bq0SaI8nsexyay5UAyDo+ICWy5MQPgEZ5+l8JQTKo=
|
||||
github.com/pelletier/go-toml/v2 v2.0.0/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
||||
github.com/performancecopilot/speed/v4 v4.0.0/go.mod h1:qxrSyuDGrTOWfV+uKRFhfxw6h/4HXRGUiZiufxo49BM=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d h1:CdDQnGF8Nq9ocOS/xlSptM1N3BbrA6/kmaep5ggwaIA=
|
||||
@@ -864,8 +873,9 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn
|
||||
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk=
|
||||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34=
|
||||
github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
@@ -878,8 +888,9 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
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 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
|
||||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
||||
github.com/prometheus/common v0.34.0 h1:RBmGO9d/FVjqHT0yUGQwBJhkwKV+wPCn7KGpvfab0uE=
|
||||
github.com/prometheus/common v0.34.0/go.mod h1:gB3sOl7P0TvJabZpLY5uQMpUqRCPPCyRLCZYc7JZTNE=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
@@ -892,19 +903,23 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T
|
||||
github.com/pseudomuto/protoc-gen-doc v1.3.2/go.mod h1:y5+P6n3iGrbKG+9O04V5ld71in3v/bX88wUwgt+U8EA=
|
||||
github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q=
|
||||
github.com/quasilyte/go-ruleguard v0.3.1-0.20210203134552-1b5a410e1cc8/go.mod h1:KsAh3x0e7Fkpgs+Q9pNLS5XpFSvYCEVl5gP9Pp1xp30=
|
||||
github.com/quasilyte/go-ruleguard v0.3.15 h1:iWYzp1z72IlXTioET0+XI6SjQdPfMGfuAiZiKznOt7g=
|
||||
github.com/quasilyte/go-ruleguard v0.3.15/go.mod h1:NhuWhnlVEM1gT1A4VJHYfy9MuYSxxwHgxWoPsn9llB4=
|
||||
github.com/quasilyte/go-ruleguard v0.3.16-0.20220213074421-6aa060fab41a h1:sWFavxtIctGrVs5SYZ5Ml1CvrDAs8Kf5kx2PI3C41dA=
|
||||
github.com/quasilyte/go-ruleguard v0.3.16-0.20220213074421-6aa060fab41a/go.mod h1:VMX+OnnSw4LicdiEGtRSD/1X8kW7GuEscjYNr4cOIT4=
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.0/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.12-0.20220101150716-969a394a9451/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.12/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.17/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.16/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.19/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
|
||||
github.com/quasilyte/go-ruleguard/rules v0.0.0-20201231183845-9e62ed36efe1/go.mod h1:7JTjp89EGyU1d6XfBiXihJNG37wB2VRkd125Q1u7Plc=
|
||||
github.com/quasilyte/go-ruleguard/rules v0.0.0-20211022131956-028d6511ab71/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50=
|
||||
github.com/quasilyte/gogrep v0.0.0-20220103110004-ffaa07af02e3 h1:P4QPNn+TK49zJjXKERt/vyPbv/mCHB/zQ4flDYOMN+M=
|
||||
github.com/quasilyte/gogrep v0.0.0-20220103110004-ffaa07af02e3/go.mod h1:wSEyW6O61xRV6zb6My3HxrQ5/8ke7NE2OayqCHa3xRM=
|
||||
github.com/quasilyte/gogrep v0.0.0-20220120141003-628d8b3623b5 h1:PDWGei+Rf2bBiuZIbZmM20J2ftEy9IeUCHA8HbQqed8=
|
||||
github.com/quasilyte/gogrep v0.0.0-20220120141003-628d8b3623b5/go.mod h1:wSEyW6O61xRV6zb6My3HxrQ5/8ke7NE2OayqCHa3xRM=
|
||||
github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95 h1:L8QM9bvf68pVdQ3bCFZMDmnt9yqcMBro1pC7F+IPYMY=
|
||||
github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0=
|
||||
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4lu7Gd+PU1fV2/qnDNfzT635KRSObncs=
|
||||
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/remyoudompheng/go-dbus v0.0.0-20121104212943-b7232d34b1d5/go.mod h1:+u151txRmLpwxBmpYn9z3d1sdJdjRPQpsXuYeY9jNls=
|
||||
github.com/remyoudompheng/go-liblzma v0.0.0-20190506200333-81bf2d431b96/go.mod h1:90HvCY7+oHHUKkbeMCiHt1WuFR2/hPJ9QrljDG+v6ls=
|
||||
github.com/remyoudompheng/go-misc v0.0.0-20190427085024-2d6ac652a50e/go.mod h1:80FQABjoFzZ2M5uEa6FUaJYEmqU2UOKojlFVak1UAwI=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
@@ -925,7 +940,6 @@ github.com/ryancurrah/gomodguard v1.2.3/go.mod h1:rYbA/4Tg5c54mV1sv4sQTP5WOPBcoL
|
||||
github.com/ryanrolds/sqlclosecheck v0.3.0 h1:AZx+Bixh8zdUBxUA1NxbxVAS78vTPq4rCb8OUZI9xFw=
|
||||
github.com/ryanrolds/sqlclosecheck v0.3.0/go.mod h1:1gREqxyTGR3lVtpngyFo3hZAgk0KCtEdgEkHwDbigdA=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sagikazarmark/crypt v0.1.0/go.mod h1:B/mN0msZuINBtQ1zZLEQcegFJJf9vnYIR88KRMEuODE=
|
||||
github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
|
||||
github.com/sagikazarmark/crypt v0.4.0/go.mod h1:ALv2SRj7GxYV4HO9elxH9nS6M9gW+xDNxqmyJ6RfDFM=
|
||||
github.com/sagikazarmark/crypt v0.5.0/go.mod h1:l+nzl7KWh51rpzp2h7t4MZWyiEWdhNpOAnclKvg+mdA=
|
||||
@@ -933,12 +947,12 @@ github.com/sanposhiho/wastedassign/v2 v2.0.6 h1:+6/hQIHKNJAUixEj6EmOngGIisyeI+T3
|
||||
github.com/sanposhiho/wastedassign/v2 v2.0.6/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
||||
github.com/securego/gosec/v2 v2.10.0 h1:l6BET4EzWtyUXCpY2v7N92v0DDCas0L7ngg3bpqbr8g=
|
||||
github.com/securego/gosec/v2 v2.10.0/go.mod h1:PVq8Ewh/nCN8l/kKC6zrGXSr7m2NmEK6ITIAWMtIaA0=
|
||||
github.com/securego/gosec/v2 v2.11.0 h1:+PDkpzR41OI2jrw1q6AdXZCbsNGNGT7pQjal0H0cArI=
|
||||
github.com/securego/gosec/v2 v2.11.0/go.mod h1:SX8bptShuG8reGC0XS09+a4H2BoWSJi+fscA+Pulbpo=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c h1:W65qqJCIOVP4jpqPQ0YvHYKwcMEMVWIzWC5iNQQfBTU=
|
||||
github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs=
|
||||
github.com/shirou/gopsutil/v3 v3.22.2/go.mod h1:WapW1AOOPlHyXr+yOyw3uYx36enocrtSoSBy0L5vUHY=
|
||||
github.com/shirou/gopsutil/v3 v3.22.4/go.mod h1:D01hZJ4pVHPpCTZ3m3T2+wDF2YAGfd+H4ifUguaQzHM=
|
||||
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
@@ -950,8 +964,8 @@ github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sivchari/containedctx v1.0.2 h1:0hLQKpgC53OVF1VT7CeoFHk9YKstur1XOgfYIc1yrHI=
|
||||
github.com/sivchari/containedctx v1.0.2/go.mod h1:PwZOeqm4/DLoJOqMSIJs3aKqXRX4YO+uXww087KZ7Bw=
|
||||
github.com/sivchari/tenv v1.4.7 h1:FdTpgRlTue5eb5nXIYgS/lyVXSjugU8UUVDwhP1NLU8=
|
||||
github.com/sivchari/tenv v1.4.7/go.mod h1:5nF+bITvkebQVanjU6IuMbvIot/7ReNsUV7I5NbprB0=
|
||||
github.com/sivchari/tenv v1.5.0 h1:wxW0mFpKI6DIb3s6m1jCDYvkWXCskrimXMuGd0K/kSQ=
|
||||
github.com/sivchari/tenv v1.5.0/go.mod h1:64yStXKSOxDfX47NlhVwND4dHwfZDdbp2Lyl018Icvg=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa h1:YJfZp12Z3AFhSBeXOlv4BO55RMwPn2NoQeDsrdWnBtY=
|
||||
@@ -989,13 +1003,14 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4=
|
||||
github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM=
|
||||
github.com/spf13/viper v1.10.1/go.mod h1:IGlFPqhNAPKRxohIzWpI5QEy4kuI7tcl5WvR+8qy1rU=
|
||||
github.com/spf13/viper v1.11.0 h1:7OX/1FS6n7jHD1zGrZTM7WtY13ZELRyosK4k93oPr44=
|
||||
github.com/spf13/viper v1.11.0/go.mod h1:djo0X/bA5+tYVoCn+C7cAYJGcVn/qYLFTG8gdUsX7Zk=
|
||||
github.com/ssgreg/nlreturn/v2 v2.2.1 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YEwQ0=
|
||||
github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I=
|
||||
github.com/stbenjam/no-sprintf-host-port v0.1.1 h1:tYugd/yrm1O0dV+ThCbaKZh195Dfm07ysF0U6JQXczc=
|
||||
github.com/stbenjam/no-sprintf-host-port v0.1.1/go.mod h1:TLhvtIvONRzdmkFiio4O8LHsN9N74I+PhRquPsxpL0I=
|
||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
|
||||
@@ -1033,13 +1048,13 @@ github.com/tetafro/godot v1.4.11 h1:BVoBIqAf/2QdbFmSwAWnaIqDivZdOV0ZRwEm6jivLKw=
|
||||
github.com/tetafro/godot v1.4.11/go.mod h1:LR3CJpxDVGlYOWn3ZZg1PgNZdTUvzsZWu8xaEohUpn8=
|
||||
github.com/timakin/bodyclose v0.0.0-20210704033933-f49887972144 h1:kl4KhGNsJIbDHS9/4U9yQo1UcPQM0kOMJHn29EoH/Ro=
|
||||
github.com/timakin/bodyclose v0.0.0-20210704033933-f49887972144/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
|
||||
github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs=
|
||||
github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8=
|
||||
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
|
||||
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tomarrell/wrapcheck/v2 v2.5.0 h1:g27SGGHNoQdvHz4KZA9o4v09RcWzylR+b1yueE5ECiw=
|
||||
github.com/tomarrell/wrapcheck/v2 v2.5.0/go.mod h1:68bQ/eJg55BROaRTbMjC7vuhL2OgfoG8bLp9ZyoBfyY=
|
||||
github.com/tomarrell/wrapcheck/v2 v2.6.1 h1:Cf4a/iwuMp9s7kKrh74GTgijRVim0wEpKjgAsT7Wctw=
|
||||
github.com/tomarrell/wrapcheck/v2 v2.6.1/go.mod h1:Eo+Opt6pyMW1b6cNllOcDSSoHO0aTJ+iF6BfCUbHltA=
|
||||
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4=
|
||||
github.com/tommy-muehle/go-mnd/v2 v2.5.0 h1:iAj0a8e6+dXSL7Liq0aXPox36FiN1dBbjA6lt9fl65s=
|
||||
github.com/tommy-muehle/go-mnd/v2 v2.5.0/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw=
|
||||
@@ -1069,8 +1084,8 @@ github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1z
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yagipy/maintidx v1.0.0 h1:h5NvIsCz+nRDapQ0exNv4aJ0yXSI0420omVANTv3GJM=
|
||||
github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsTACwgXLk=
|
||||
github.com/yeya24/promlinter v0.1.1-0.20210918184747-d757024714a1 h1:YAaOqqMTstELMMGblt6yJ/fcOt4owSYuw3IttMnKfAM=
|
||||
github.com/yeya24/promlinter v0.1.1-0.20210918184747-d757024714a1/go.mod h1:rs5vtZzeBHqqMwXqFScncpCF6u06lezhZepno9AB1Oc=
|
||||
github.com/yeya24/promlinter v0.2.0 h1:xFKDQ82orCU5jQujdaD8stOHiv8UN68BSdn2a8u8Y3o=
|
||||
github.com/yeya24/promlinter v0.2.0/go.mod h1:u54lkmBOZrpEbQQ6gox2zWKKLKu2SGe+2KOiextY+IA=
|
||||
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
|
||||
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
|
||||
@@ -1152,7 +1167,7 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220313003712-b769efc7c000/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA=
|
||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -1168,7 +1183,10 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5 h1:FR+oGxGfbQu1d+jglI3rCkjAjUnhRSZcUxr+DqlDLNo=
|
||||
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
|
||||
golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e h1:qyrTQ++p1afMkO4DPEeLGq/3oTsdlvdH4vqZUBWzUKM=
|
||||
golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
@@ -1385,11 +1403,9 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -1400,14 +1416,17 @@ golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211213223007-03aa0b5f6827/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
|
||||
golang.org/x/sys v0.0.0-20220403020550-483a9cbc67c0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220406163625-3f8b81556e12/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 h1:xHms4gcpe1YE7A3yIllJXP16CMAGuqwO2lX1mTyyRRc=
|
||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
@@ -1435,6 +1454,7 @@ golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGm
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190228203856-589c23e65e65/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190307163923-6a08e3108db3/go.mod h1:25r3+/G6/xytQM8iWZKq3Hn0kr0rgFKPUNVEL/dr3z4=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
@@ -1508,7 +1528,6 @@ golang.org/x/tools v0.0.0-20201002184944-ecd9fd270d5d/go.mod h1:z6u4i615ZeAfBE4X
|
||||
golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201028025901-8cd080b735b3/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201114224030-61ea331ec02b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
@@ -1528,8 +1547,9 @@ golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.1.9-0.20211228192929-ee1ca4ffc4da/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
|
||||
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
||||
golang.org/x/tools v0.1.11-0.20220316014157-77aa08bb151a h1:ofrrl6c6NG5/IOSx/R1cyiQxxjqlur0h/TvbUhkH0II=
|
||||
golang.org/x/tools v0.1.11-0.20220316014157-77aa08bb151a/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -1703,8 +1723,8 @@ google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ5
|
||||
google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
|
||||
google.golang.org/grpc v1.46.0 h1:oCjezcn6g6A75TGoKYBPgKmVBLexhYLM6MebdrPApP8=
|
||||
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc v1.46.2 h1:u+MLGgVf7vRdjEYZ8wDFhAVNmhkbJ5hmrA1LMWK1CAQ=
|
||||
google.golang.org/grpc v1.46.2/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=
|
||||
@@ -1733,7 +1753,6 @@ gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||
gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.66.3/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4=
|
||||
@@ -1765,10 +1784,10 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.2.2 h1:MNh1AVMyVX23VUHE2O27jm6lNj3vjO5DexS4A1xvnzk=
|
||||
honnef.co/go/tools v0.2.2/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY=
|
||||
mvdan.cc/gofumpt v0.3.0 h1:kTojdZo9AcEYbQYhGuLf/zszYthRdhDNDUi2JKTxas4=
|
||||
mvdan.cc/gofumpt v0.3.0/go.mod h1:0+VyGZWleeIj5oostkOex+nDBA0eyavuDnDusAJ8ylo=
|
||||
honnef.co/go/tools v0.3.1 h1:1kJlrWJLkaGXgcaeosRXViwviqjI7nkBvU2+sZW0AYc=
|
||||
honnef.co/go/tools v0.3.1/go.mod h1:vlRD9XErLMGT+mDuofSr0mMMquscM/1nQqtRSsh6m70=
|
||||
mvdan.cc/gofumpt v0.3.1 h1:avhhrOmv0IuvQVK7fvwV91oFSGAk5/6Po8GXTzICeu8=
|
||||
mvdan.cc/gofumpt v0.3.1/go.mod h1:w3ymliuxvzVx8DAutBnVyDqYb1Niy/yCJt/lk821YCE=
|
||||
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I=
|
||||
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
|
||||
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo=
|
||||
|
||||
+44
-16
@@ -200,16 +200,20 @@ func (pool *BlockPool) IsCaughtUp() bool {
|
||||
return pool.height >= (pool.maxPeerHeight - 1)
|
||||
}
|
||||
|
||||
// PeekTwoBlocks returns blocks at pool.height and pool.height+1.
|
||||
// We need to see the second block's Commit to validate the first block.
|
||||
// So we peek two blocks at a time.
|
||||
// PeekTwoBlocks returns blocks at pool.height and pool.height+1. We need to
|
||||
// see the second block's Commit to validate the first block. So we peek two
|
||||
// blocks at a time. We return an extended commit, containing vote extensions
|
||||
// and their associated signatures, as this is critical to consensus in ABCI++
|
||||
// as we switch from block sync to consensus mode.
|
||||
//
|
||||
// The caller will verify the commit.
|
||||
func (pool *BlockPool) PeekTwoBlocks() (first *types.Block, second *types.Block) {
|
||||
func (pool *BlockPool) PeekTwoBlocks() (first, second *types.Block, firstExtCommit *types.ExtendedCommit) {
|
||||
pool.mtx.RLock()
|
||||
defer pool.mtx.RUnlock()
|
||||
|
||||
if r := pool.requesters[pool.height]; r != nil {
|
||||
first = r.getBlock()
|
||||
firstExtCommit = r.getExtendedCommit()
|
||||
}
|
||||
if r := pool.requesters[pool.height+1]; r != nil {
|
||||
second = r.getBlock()
|
||||
@@ -218,7 +222,8 @@ func (pool *BlockPool) PeekTwoBlocks() (first *types.Block, second *types.Block)
|
||||
}
|
||||
|
||||
// PopRequest pops the first block at pool.height.
|
||||
// It must have been validated by 'second'.Commit from PeekTwoBlocks().
|
||||
// It must have been validated by the second Commit from PeekTwoBlocks.
|
||||
// TODO(thane): (?) and its corresponding ExtendedCommit.
|
||||
func (pool *BlockPool) PopRequest() {
|
||||
pool.mtx.Lock()
|
||||
defer pool.mtx.Unlock()
|
||||
@@ -262,16 +267,25 @@ func (pool *BlockPool) RedoRequest(height int64) types.NodeID {
|
||||
return peerID
|
||||
}
|
||||
|
||||
// AddBlock validates that the block comes from the peer it was expected from and calls the requester to store it.
|
||||
// AddBlock validates that the block comes from the peer it was expected from
|
||||
// and calls the requester to store it.
|
||||
//
|
||||
// This requires an extended commit at the same height as the supplied block -
|
||||
// the block contains the last commit, but we need the latest commit in case we
|
||||
// need to switch over from block sync to consensus at this height. If the
|
||||
// height of the extended commit and the height of the block do not match, we
|
||||
// do not add the block and return an error.
|
||||
// TODO: ensure that blocks come in order for each peer.
|
||||
func (pool *BlockPool) AddBlock(peerID types.NodeID, block *types.Block, blockSize int) {
|
||||
func (pool *BlockPool) AddBlock(peerID types.NodeID, block *types.Block, extCommit *types.ExtendedCommit, blockSize int) error {
|
||||
pool.mtx.Lock()
|
||||
defer pool.mtx.Unlock()
|
||||
|
||||
if extCommit != nil && block.Height != extCommit.Height {
|
||||
return fmt.Errorf("heights don't match, not adding block (block height: %d, commit height: %d)", block.Height, extCommit.Height)
|
||||
}
|
||||
|
||||
requester := pool.requesters[block.Height]
|
||||
if requester == nil {
|
||||
pool.logger.Error("peer sent us a block we didn't expect",
|
||||
"peer", peerID, "curHeight", pool.height, "blockHeight", block.Height)
|
||||
diff := pool.height - block.Height
|
||||
if diff < 0 {
|
||||
diff *= -1
|
||||
@@ -279,10 +293,10 @@ func (pool *BlockPool) AddBlock(peerID types.NodeID, block *types.Block, blockSi
|
||||
if diff > maxDiffBetweenCurrentAndReceivedBlockHeight {
|
||||
pool.sendError(errors.New("peer sent us a block we didn't expect with a height too far ahead/behind"), peerID)
|
||||
}
|
||||
return
|
||||
return fmt.Errorf("peer sent us a block we didn't expect (peer: %s, current height: %d, block height: %d)", peerID, pool.height, block.Height)
|
||||
}
|
||||
|
||||
if requester.setBlock(block, peerID) {
|
||||
if requester.setBlock(block, extCommit, peerID) {
|
||||
atomic.AddInt32(&pool.numPending, -1)
|
||||
peer := pool.peers[peerID]
|
||||
if peer != nil {
|
||||
@@ -290,9 +304,11 @@ func (pool *BlockPool) AddBlock(peerID types.NodeID, block *types.Block, blockSi
|
||||
}
|
||||
} else {
|
||||
err := errors.New("requester is different or block already exists")
|
||||
pool.logger.Error(err.Error(), "peer", peerID, "requester", requester.getPeerID(), "blockHeight", block.Height)
|
||||
pool.sendError(err, peerID)
|
||||
return fmt.Errorf("%w (peer: %s, requester: %s, block height: %d)", err, peerID, requester.getPeerID(), block.Height)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MaxPeerHeight returns the highest reported height.
|
||||
@@ -456,6 +472,7 @@ func (pool *BlockPool) debug() string {
|
||||
} else {
|
||||
str += fmt.Sprintf("H(%v):", h)
|
||||
str += fmt.Sprintf("B?(%v) ", pool.requesters[h].block != nil)
|
||||
str += fmt.Sprintf("C?(%v) ", pool.requesters[h].extCommit != nil)
|
||||
}
|
||||
}
|
||||
return str
|
||||
@@ -544,9 +561,10 @@ type bpRequester struct {
|
||||
gotBlockCh chan struct{}
|
||||
redoCh chan types.NodeID // redo may send multitime, add peerId to identify repeat
|
||||
|
||||
mtx sync.Mutex
|
||||
peerID types.NodeID
|
||||
block *types.Block
|
||||
mtx sync.Mutex
|
||||
peerID types.NodeID
|
||||
block *types.Block
|
||||
extCommit *types.ExtendedCommit
|
||||
}
|
||||
|
||||
func newBPRequester(logger log.Logger, pool *BlockPool, height int64) *bpRequester {
|
||||
@@ -572,13 +590,16 @@ func (bpr *bpRequester) OnStart(ctx context.Context) error {
|
||||
func (*bpRequester) OnStop() {}
|
||||
|
||||
// Returns true if the peer matches and block doesn't already exist.
|
||||
func (bpr *bpRequester) setBlock(block *types.Block, peerID types.NodeID) bool {
|
||||
func (bpr *bpRequester) setBlock(block *types.Block, extCommit *types.ExtendedCommit, peerID types.NodeID) bool {
|
||||
bpr.mtx.Lock()
|
||||
if bpr.block != nil || bpr.peerID != peerID {
|
||||
bpr.mtx.Unlock()
|
||||
return false
|
||||
}
|
||||
bpr.block = block
|
||||
if extCommit != nil {
|
||||
bpr.extCommit = extCommit
|
||||
}
|
||||
bpr.mtx.Unlock()
|
||||
|
||||
select {
|
||||
@@ -594,6 +615,12 @@ func (bpr *bpRequester) getBlock() *types.Block {
|
||||
return bpr.block
|
||||
}
|
||||
|
||||
func (bpr *bpRequester) getExtendedCommit() *types.ExtendedCommit {
|
||||
bpr.mtx.Lock()
|
||||
defer bpr.mtx.Unlock()
|
||||
return bpr.extCommit
|
||||
}
|
||||
|
||||
func (bpr *bpRequester) getPeerID() types.NodeID {
|
||||
bpr.mtx.Lock()
|
||||
defer bpr.mtx.Unlock()
|
||||
@@ -611,6 +638,7 @@ func (bpr *bpRequester) reset() {
|
||||
|
||||
bpr.peerID = ""
|
||||
bpr.block = nil
|
||||
bpr.extCommit = nil
|
||||
}
|
||||
|
||||
// Tells bpRequester to pick another peer and try again.
|
||||
|
||||
@@ -43,7 +43,10 @@ func (p testPeer) runInputRoutine() {
|
||||
// Request desired, pretend like we got the block immediately.
|
||||
func (p testPeer) simulateInput(input inputData) {
|
||||
block := &types.Block{Header: types.Header{Height: input.request.Height}}
|
||||
input.pool.AddBlock(input.request.PeerID, block, 123)
|
||||
extCommit := &types.ExtendedCommit{
|
||||
Height: input.request.Height,
|
||||
}
|
||||
_ = input.pool.AddBlock(input.request.PeerID, block, extCommit, 123)
|
||||
// TODO: uncommenting this creates a race which is detected by:
|
||||
// https://github.com/golang/go/blob/2bd767b1022dd3254bcec469f0ee164024726486/src/testing/testing.go#L854-L856
|
||||
// see: https://github.com/tendermint/tendermint/issues/3390#issue-418379890
|
||||
@@ -110,7 +113,7 @@ func TestBlockPoolBasic(t *testing.T) {
|
||||
if !pool.IsRunning() {
|
||||
return
|
||||
}
|
||||
first, second := pool.PeekTwoBlocks()
|
||||
first, second, _ := pool.PeekTwoBlocks()
|
||||
if first != nil && second != nil {
|
||||
pool.PopRequest()
|
||||
} else {
|
||||
@@ -164,7 +167,7 @@ func TestBlockPoolTimeout(t *testing.T) {
|
||||
if !pool.IsRunning() {
|
||||
return
|
||||
}
|
||||
first, second := pool.PeekTwoBlocks()
|
||||
first, second, _ := pool.PeekTwoBlocks()
|
||||
if first != nil && second != nil {
|
||||
pool.PopRequest()
|
||||
} else {
|
||||
|
||||
+126
-45
@@ -76,7 +76,7 @@ type Reactor struct {
|
||||
stateStore sm.Store
|
||||
|
||||
blockExec *sm.BlockExecutor
|
||||
store *store.BlockStore
|
||||
store sm.BlockStore
|
||||
pool *BlockPool
|
||||
consReactor consensusReactor
|
||||
blockSync *atomicBool
|
||||
@@ -185,25 +185,39 @@ func (r *Reactor) OnStop() {
|
||||
// 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 {
|
||||
block := r.store.LoadBlock(msg.Height)
|
||||
if block != nil {
|
||||
blockProto, err := block.ToProto()
|
||||
if err != nil {
|
||||
r.logger.Error("failed to convert msg to protobuf", "err", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if block == nil {
|
||||
r.logger.Info("peer requesting a block we do not have", "peer", peerID, "height", msg.Height)
|
||||
return blockSyncCh.Send(ctx, p2p.Envelope{
|
||||
To: peerID,
|
||||
Message: &bcproto.BlockResponse{Block: blockProto},
|
||||
Message: &bcproto.NoBlockResponse{Height: msg.Height},
|
||||
})
|
||||
}
|
||||
|
||||
r.logger.Info("peer requesting a block we do not have", "peer", peerID, "height", msg.Height)
|
||||
state, err := r.stateStore.Load()
|
||||
if err != nil {
|
||||
return fmt.Errorf("loading state: %w", err)
|
||||
}
|
||||
var extCommit *types.ExtendedCommit
|
||||
if state.ConsensusParams.ABCI.VoteExtensionsEnabled(msg.Height) {
|
||||
extCommit = r.store.LoadBlockExtendedCommit(msg.Height)
|
||||
if extCommit == nil {
|
||||
return fmt.Errorf("found block in store with no extended commit: %v", block)
|
||||
}
|
||||
}
|
||||
|
||||
blockProto, err := block.ToProto()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to convert block to protobuf: %w", err)
|
||||
}
|
||||
|
||||
return blockSyncCh.Send(ctx, p2p.Envelope{
|
||||
To: peerID,
|
||||
Message: &bcproto.NoBlockResponse{Height: msg.Height},
|
||||
To: peerID,
|
||||
Message: &bcproto.BlockResponse{
|
||||
Block: blockProto,
|
||||
ExtCommit: extCommit.ToProto(),
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// handleMessage handles an Envelope sent from a peer on a specific p2p Channel.
|
||||
@@ -236,8 +250,21 @@ func (r *Reactor) handleMessage(ctx context.Context, envelope *p2p.Envelope, blo
|
||||
"err", err)
|
||||
return err
|
||||
}
|
||||
var extCommit *types.ExtendedCommit
|
||||
if msg.ExtCommit != nil {
|
||||
var err error
|
||||
extCommit, err = types.ExtendedCommitFromProto(msg.ExtCommit)
|
||||
if err != nil {
|
||||
r.logger.Error("failed to convert extended commit from proto",
|
||||
"peer", envelope.From,
|
||||
"err", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
r.pool.AddBlock(envelope.From, block, block.Size())
|
||||
if err := r.pool.AddBlock(envelope.From, block, extCommit, block.Size()); err != nil {
|
||||
r.logger.Error("failed to add block", "err", err)
|
||||
}
|
||||
|
||||
case *bcproto.StatusRequest:
|
||||
return blockSyncCh.Send(ctx, p2p.Envelope{
|
||||
@@ -425,6 +452,8 @@ func (r *Reactor) poolRoutine(ctx context.Context, stateSynced bool, blockSyncCh
|
||||
lastRate = 0.0
|
||||
|
||||
didProcessCh = make(chan struct{}, 1)
|
||||
|
||||
initialCommitHasExtensions = (r.initialState.LastBlockHeight > 0 && r.store.LoadBlockExtendedCommit(r.initialState.LastBlockHeight) != nil)
|
||||
)
|
||||
|
||||
defer trySyncTicker.Stop()
|
||||
@@ -448,6 +477,35 @@ func (r *Reactor) poolRoutine(ctx context.Context, stateSynced bool, blockSyncCh
|
||||
)
|
||||
|
||||
switch {
|
||||
|
||||
// The case statement below is a bit confusing, so here is a breakdown
|
||||
// of its logic and purpose:
|
||||
//
|
||||
// If VoteExtensions are enabled we cannot switch to consensus without
|
||||
// the vote extension data for the previous height, i.e. state.LastBlockHeight.
|
||||
//
|
||||
// If extensions were required during state.LastBlockHeight and we have
|
||||
// sync'd at least one block, then we are guaranteed to have extensions.
|
||||
// BlockSync requires that the blocks it fetches have extensions if
|
||||
// extensions were enabled during the height.
|
||||
//
|
||||
// If extensions were required during state.LastBlockHeight and we have
|
||||
// not sync'd any blocks, then we can only transition to Consensus
|
||||
// if we already had extensions for the initial height.
|
||||
// If any of these conditions is not met, we continue the loop, looking
|
||||
// for extensions.
|
||||
case state.ConsensusParams.ABCI.VoteExtensionsEnabled(state.LastBlockHeight) &&
|
||||
(blocksSynced == 0 && !initialCommitHasExtensions):
|
||||
r.logger.Info(
|
||||
"no extended commit yet",
|
||||
"height", height,
|
||||
"last_block_height", state.LastBlockHeight,
|
||||
"initial_height", state.InitialHeight,
|
||||
"max_peer_height", r.pool.MaxPeerHeight(),
|
||||
"timeout_in", syncTimeout-time.Since(lastAdvance),
|
||||
)
|
||||
continue
|
||||
|
||||
case r.pool.IsCaughtUp():
|
||||
r.logger.Info("switching to consensus reactor", "height", height)
|
||||
|
||||
@@ -490,15 +548,20 @@ func (r *Reactor) poolRoutine(ctx context.Context, stateSynced bool, blockSyncCh
|
||||
// TODO: Uncouple from request routine.
|
||||
|
||||
// see if there are any blocks to sync
|
||||
first, second := r.pool.PeekTwoBlocks()
|
||||
if first == nil || second == nil {
|
||||
// we need both to sync the first block
|
||||
first, second, extCommit := r.pool.PeekTwoBlocks()
|
||||
if first != nil && extCommit == nil &&
|
||||
state.ConsensusParams.ABCI.VoteExtensionsEnabled(first.Height) {
|
||||
// See https://github.com/tendermint/tendermint/pull/8433#discussion_r866790631
|
||||
panic(fmt.Errorf("peeked first block without extended commit at height %d - possible node store corruption", first.Height))
|
||||
} else if first == nil || second == nil {
|
||||
// we need to have fetched two consecutive blocks in order to
|
||||
// perform blocksync verification
|
||||
continue
|
||||
} else {
|
||||
// try again quickly next loop
|
||||
didProcessCh <- struct{}{}
|
||||
}
|
||||
|
||||
// try again quickly next loop
|
||||
didProcessCh <- struct{}{}
|
||||
|
||||
firstParts, err := first.MakePartSet(types.BlockPartSizeBytes)
|
||||
if err != nil {
|
||||
r.logger.Error("failed to make ",
|
||||
@@ -517,8 +580,20 @@ func (r *Reactor) poolRoutine(ctx context.Context, stateSynced bool, blockSyncCh
|
||||
// NOTE: We can probably make this more efficient, but note that calling
|
||||
// first.Hash() doesn't verify the tx contents, so MakePartSet() is
|
||||
// currently necessary.
|
||||
if err = state.Validators.VerifyCommitLight(chainID, firstID, first.Height, second.LastCommit); err != nil {
|
||||
err = fmt.Errorf("invalid last commit: %w", err)
|
||||
// TODO(sergio): Should we also validate against the extended commit?
|
||||
err = state.Validators.VerifyCommitLight(chainID, firstID, first.Height, second.LastCommit)
|
||||
|
||||
if err == nil {
|
||||
// validate the block before we persist it
|
||||
err = r.blockExec.ValidateBlock(ctx, state, first)
|
||||
}
|
||||
if err == nil && state.ConsensusParams.ABCI.VoteExtensionsEnabled(first.Height) {
|
||||
// if vote extensions were required at this height, ensure they exist.
|
||||
err = extCommit.EnsureExtensions()
|
||||
}
|
||||
// If either of the checks failed we log the error and request for a new block
|
||||
// at that height
|
||||
if err != nil {
|
||||
r.logger.Error(
|
||||
err.Error(),
|
||||
"last_commit", second.LastCommit,
|
||||
@@ -545,37 +620,43 @@ func (r *Reactor) poolRoutine(ctx context.Context, stateSynced bool, blockSyncCh
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
r.pool.PopRequest()
|
||||
|
||||
// TODO: batch saves so we do not persist to disk every block
|
||||
if state.ConsensusParams.ABCI.VoteExtensionsEnabled(first.Height) {
|
||||
r.store.SaveBlockWithExtendedCommit(first, firstParts, extCommit)
|
||||
} else {
|
||||
r.pool.PopRequest()
|
||||
|
||||
// TODO: batch saves so we do not persist to disk every block
|
||||
// We use LastCommit here instead of extCommit. extCommit is not
|
||||
// guaranteed to be populated by the peer if extensions are not enabled.
|
||||
// Currently, the peer should provide an extCommit even if the vote extension data are absent
|
||||
// but this may change so using second.LastCommit is safer.
|
||||
r.store.SaveBlock(first, firstParts, second.LastCommit)
|
||||
}
|
||||
|
||||
var err error
|
||||
// TODO: Same thing for app - but we would need a way to get the hash
|
||||
// without persisting the state.
|
||||
state, err = r.blockExec.ApplyBlock(ctx, state, firstID, first)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to process committed block (%d:%X): %v", first.Height, first.Hash(), err))
|
||||
}
|
||||
|
||||
// TODO: Same thing for app - but we would need a way to get the hash
|
||||
// without persisting the state.
|
||||
state, err = r.blockExec.ApplyBlock(ctx, state, firstID, first)
|
||||
if err != nil {
|
||||
// TODO: This is bad, are we zombie?
|
||||
panic(fmt.Sprintf("failed to process committed block (%d:%X): %v", first.Height, first.Hash(), err))
|
||||
}
|
||||
r.metrics.RecordConsMetrics(first)
|
||||
|
||||
r.metrics.RecordConsMetrics(first)
|
||||
blocksSynced++
|
||||
|
||||
blocksSynced++
|
||||
if blocksSynced%100 == 0 {
|
||||
lastRate = 0.9*lastRate + 0.1*(100/time.Since(lastHundred).Seconds())
|
||||
r.logger.Info(
|
||||
"block sync rate",
|
||||
"height", r.pool.height,
|
||||
"max_peer_height", r.pool.MaxPeerHeight(),
|
||||
"blocks/s", lastRate,
|
||||
)
|
||||
|
||||
if blocksSynced%100 == 0 {
|
||||
lastRate = 0.9*lastRate + 0.1*(100/time.Since(lastHundred).Seconds())
|
||||
r.logger.Info(
|
||||
"block sync rate",
|
||||
"height", r.pool.height,
|
||||
"max_peer_height", r.pool.MaxPeerHeight(),
|
||||
"blocks/s", lastRate,
|
||||
)
|
||||
|
||||
lastHundred = time.Now()
|
||||
}
|
||||
lastHundred = time.Now()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,8 +40,6 @@ type reactorTestSuite struct {
|
||||
blockSyncChannels map[types.NodeID]*p2p.Channel
|
||||
peerChans map[types.NodeID]chan p2p.PeerUpdate
|
||||
peerUpdates map[types.NodeID]*p2p.PeerUpdates
|
||||
|
||||
blockSync bool
|
||||
}
|
||||
|
||||
func setup(
|
||||
@@ -69,7 +67,6 @@ func setup(
|
||||
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),
|
||||
blockSync: true,
|
||||
}
|
||||
|
||||
chDesc := &p2p.ChannelDescriptor{ID: BlockSyncChannel, MessageType: new(bcproto.Message)}
|
||||
@@ -97,21 +94,19 @@ func setup(
|
||||
return rts
|
||||
}
|
||||
|
||||
func (rts *reactorTestSuite) addNode(
|
||||
func makeReactor(
|
||||
ctx context.Context,
|
||||
t *testing.T,
|
||||
nodeID types.NodeID,
|
||||
genDoc *types.GenesisDoc,
|
||||
privVal types.PrivValidator,
|
||||
maxBlockHeight int64,
|
||||
) {
|
||||
t.Helper()
|
||||
channelCreator p2p.ChannelCreator,
|
||||
peerEvents p2p.PeerEventSubscriber) *Reactor {
|
||||
|
||||
logger := log.NewNopLogger()
|
||||
|
||||
rts.nodes = append(rts.nodes, nodeID)
|
||||
rts.app[nodeID] = proxy.New(abciclient.NewLocalClient(logger, &abci.BaseApplication{}), logger, proxy.NopMetrics())
|
||||
require.NoError(t, rts.app[nodeID].Start(ctx))
|
||||
app := proxy.New(abciclient.NewLocalClient(logger, &abci.BaseApplication{}), logger, proxy.NopMetrics())
|
||||
require.NoError(t, app.Start(ctx))
|
||||
|
||||
blockDB := dbm.NewMemDB()
|
||||
stateDB := dbm.NewMemDB()
|
||||
@@ -139,7 +134,7 @@ func (rts *reactorTestSuite) addNode(
|
||||
blockExec := sm.NewBlockExecutor(
|
||||
stateStore,
|
||||
log.NewNopLogger(),
|
||||
rts.app[nodeID],
|
||||
app,
|
||||
mp,
|
||||
sm.EmptyEvidencePool{},
|
||||
blockStore,
|
||||
@@ -147,40 +142,35 @@ func (rts *reactorTestSuite) addNode(
|
||||
sm.NopMetrics(),
|
||||
)
|
||||
|
||||
for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ {
|
||||
lastCommit := types.NewCommit(blockHeight-1, 0, types.BlockID{}, nil)
|
||||
return NewReactor(
|
||||
logger,
|
||||
stateStore,
|
||||
blockExec,
|
||||
blockStore,
|
||||
nil,
|
||||
channelCreator,
|
||||
peerEvents,
|
||||
true,
|
||||
consensus.NopMetrics(),
|
||||
nil, // eventbus, can be nil
|
||||
)
|
||||
}
|
||||
|
||||
if blockHeight > 1 {
|
||||
lastBlockMeta := blockStore.LoadBlockMeta(blockHeight - 1)
|
||||
lastBlock := blockStore.LoadBlock(blockHeight - 1)
|
||||
func (rts *reactorTestSuite) addNode(
|
||||
ctx context.Context,
|
||||
t *testing.T,
|
||||
nodeID types.NodeID,
|
||||
genDoc *types.GenesisDoc,
|
||||
privVal types.PrivValidator,
|
||||
maxBlockHeight int64,
|
||||
) {
|
||||
t.Helper()
|
||||
|
||||
vote, err := factory.MakeVote(
|
||||
ctx,
|
||||
privVal,
|
||||
lastBlock.Header.ChainID, 0,
|
||||
lastBlock.Header.Height, 0, 2,
|
||||
lastBlockMeta.BlockID,
|
||||
time.Now(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
lastCommit = types.NewCommit(
|
||||
vote.Height,
|
||||
vote.Round,
|
||||
lastBlockMeta.BlockID,
|
||||
[]types.CommitSig{vote.CommitSig()},
|
||||
)
|
||||
}
|
||||
logger := log.NewNopLogger()
|
||||
|
||||
thisBlock := sf.MakeBlock(state, blockHeight, lastCommit)
|
||||
thisParts, err := thisBlock.MakePartSet(types.BlockPartSizeBytes)
|
||||
require.NoError(t, err)
|
||||
blockID := types.BlockID{Hash: thisBlock.Hash(), PartSetHeader: thisParts.Header()}
|
||||
|
||||
state, err = blockExec.ApplyBlock(ctx, state, blockID, thisBlock)
|
||||
require.NoError(t, err)
|
||||
|
||||
blockStore.SaveBlock(thisBlock, thisParts, lastCommit)
|
||||
}
|
||||
rts.nodes = append(rts.nodes, nodeID)
|
||||
rts.app[nodeID] = proxy.New(abciclient.NewLocalClient(logger, &abci.BaseApplication{}), logger, proxy.NopMetrics())
|
||||
require.NoError(t, rts.app[nodeID].Start(ctx))
|
||||
|
||||
rts.peerChans[nodeID] = make(chan p2p.PeerUpdate)
|
||||
rts.peerUpdates[nodeID] = p2p.NewPeerUpdates(rts.peerChans[nodeID], 1)
|
||||
@@ -189,21 +179,64 @@ func (rts *reactorTestSuite) addNode(
|
||||
chCreator := func(ctx context.Context, chdesc *p2p.ChannelDescriptor) (*p2p.Channel, error) {
|
||||
return rts.blockSyncChannels[nodeID], nil
|
||||
}
|
||||
rts.reactors[nodeID] = NewReactor(
|
||||
rts.logger.With("nodeID", nodeID),
|
||||
stateStore,
|
||||
blockExec,
|
||||
blockStore,
|
||||
nil,
|
||||
chCreator,
|
||||
func(ctx context.Context) *p2p.PeerUpdates { return rts.peerUpdates[nodeID] },
|
||||
rts.blockSync,
|
||||
consensus.NopMetrics(),
|
||||
nil, // eventbus, can be nil
|
||||
)
|
||||
|
||||
require.NoError(t, rts.reactors[nodeID].Start(ctx))
|
||||
require.True(t, rts.reactors[nodeID].IsRunning())
|
||||
peerEvents := func(ctx context.Context) *p2p.PeerUpdates { return rts.peerUpdates[nodeID] }
|
||||
reactor := makeReactor(ctx, t, nodeID, genDoc, privVal, chCreator, peerEvents)
|
||||
|
||||
lastExtCommit := &types.ExtendedCommit{}
|
||||
|
||||
state, err := reactor.stateStore.Load()
|
||||
require.NoError(t, err)
|
||||
for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ {
|
||||
block, blockID, partSet, seenExtCommit := makeNextBlock(ctx, t, state, privVal, blockHeight, lastExtCommit)
|
||||
|
||||
state, err = reactor.blockExec.ApplyBlock(ctx, state, blockID, block)
|
||||
require.NoError(t, err)
|
||||
|
||||
reactor.store.SaveBlockWithExtendedCommit(block, partSet, seenExtCommit)
|
||||
lastExtCommit = seenExtCommit
|
||||
}
|
||||
|
||||
rts.reactors[nodeID] = reactor
|
||||
require.NoError(t, reactor.Start(ctx))
|
||||
require.True(t, reactor.IsRunning())
|
||||
}
|
||||
|
||||
func makeNextBlock(ctx context.Context,
|
||||
t *testing.T,
|
||||
state sm.State,
|
||||
signer types.PrivValidator,
|
||||
height int64,
|
||||
lc *types.ExtendedCommit) (*types.Block, types.BlockID, *types.PartSet, *types.ExtendedCommit) {
|
||||
|
||||
lastExtCommit := lc.Clone()
|
||||
|
||||
block := sf.MakeBlock(state, height, lastExtCommit.ToCommit())
|
||||
partSet, err := block.MakePartSet(types.BlockPartSizeBytes)
|
||||
require.NoError(t, err)
|
||||
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: partSet.Header()}
|
||||
|
||||
// Simulate a commit for the current height
|
||||
vote, err := factory.MakeVote(
|
||||
ctx,
|
||||
signer,
|
||||
block.Header.ChainID,
|
||||
0,
|
||||
block.Header.Height,
|
||||
0,
|
||||
2,
|
||||
blockID,
|
||||
time.Now(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
seenExtCommit := &types.ExtendedCommit{
|
||||
Height: vote.Height,
|
||||
Round: vote.Round,
|
||||
BlockID: blockID,
|
||||
ExtendedSignatures: []types.ExtendedCommitSig{vote.ExtendedCommitSig()},
|
||||
}
|
||||
return block, blockID, partSet, seenExtCommit
|
||||
|
||||
}
|
||||
|
||||
func (rts *reactorTestSuite) start(ctx context.Context, t *testing.T) {
|
||||
@@ -411,3 +444,35 @@ func TestReactor_BadBlockStopsPeer(t *testing.T) {
|
||||
len(rts.reactors[newNode.NodeID].pool.peers),
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
func TestReactorReceivesNoExtendedCommit(t *testing.T) {
|
||||
blockDB := dbm.NewMemDB()
|
||||
stateDB := dbm.NewMemDB()
|
||||
stateStore := sm.NewStore(stateDB)
|
||||
blockStore := store.NewBlockStore(blockDB)
|
||||
blockExec := sm.NewBlockExecutor(
|
||||
stateStore,
|
||||
log.NewNopLogger(),
|
||||
rts.app[nodeID],
|
||||
mp,
|
||||
sm.EmptyEvidencePool{},
|
||||
blockStore,
|
||||
eventbus,
|
||||
sm.NopMetrics(),
|
||||
)
|
||||
NewReactor(
|
||||
log.NewNopLogger(),
|
||||
stateStore,
|
||||
blockExec,
|
||||
blockStore,
|
||||
nil,
|
||||
chCreator,
|
||||
func(ctx context.Context) *p2p.PeerUpdates { return rts.peerUpdates[nodeID] },
|
||||
rts.blockSync,
|
||||
consensus.NopMetrics(),
|
||||
nil, // eventbus, can be nil
|
||||
)
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -178,22 +178,22 @@ func TestByzantinePrevoteEquivocation(t *testing.T) {
|
||||
lazyNodeState.decideProposal = func(ctx context.Context, height int64, round int32) {
|
||||
require.NotNil(t, lazyNodeState.privValidator)
|
||||
|
||||
var commit *types.Commit
|
||||
var extCommit *types.ExtendedCommit
|
||||
switch {
|
||||
case lazyNodeState.Height == lazyNodeState.state.InitialHeight:
|
||||
// We're creating a proposal for the first block.
|
||||
// The commit is empty, but not nil.
|
||||
commit = types.NewCommit(0, 0, types.BlockID{}, nil)
|
||||
extCommit = &types.ExtendedCommit{}
|
||||
case lazyNodeState.LastCommit.HasTwoThirdsMajority():
|
||||
// Make the commit from LastCommit
|
||||
commit = lazyNodeState.LastCommit.MakeCommit()
|
||||
extCommit = lazyNodeState.LastCommit.MakeExtendedCommit()
|
||||
default: // This shouldn't happen.
|
||||
lazyNodeState.logger.Error("enterPropose: Cannot propose anything: No commit for the previous block")
|
||||
return
|
||||
}
|
||||
|
||||
// omit the last signature in the commit
|
||||
commit.Signatures[len(commit.Signatures)-1] = types.NewCommitSigAbsent()
|
||||
extCommit.ExtendedSignatures[len(extCommit.ExtendedSignatures)-1] = types.NewExtendedCommitSigAbsent()
|
||||
|
||||
if lazyNodeState.privValidatorPubKey == nil {
|
||||
// If this node is a validator & proposer in the current round, it will
|
||||
@@ -204,7 +204,7 @@ func TestByzantinePrevoteEquivocation(t *testing.T) {
|
||||
proposerAddr := lazyNodeState.privValidatorPubKey.Address()
|
||||
|
||||
block, err := lazyNodeState.blockExec.CreateProposalBlock(
|
||||
ctx, lazyNodeState.Height, lazyNodeState.state, commit, proposerAddr, lazyNodeState.LastCommit.GetVotes())
|
||||
ctx, lazyNodeState.Height, lazyNodeState.state, extCommit, proposerAddr)
|
||||
require.NoError(t, err)
|
||||
blockParts, err := block.MakePartSet(types.BlockPartSizeBytes)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -160,7 +160,8 @@ func signVote(
|
||||
blockID types.BlockID) *types.Vote {
|
||||
|
||||
var ext []byte
|
||||
if voteType == tmproto.PrecommitType {
|
||||
// Only non-nil precommits are allowed to carry vote extensions.
|
||||
if voteType == tmproto.PrecommitType && !blockID.IsNil() {
|
||||
ext = []byte("extension")
|
||||
}
|
||||
v, err := vs.signVote(ctx, voteType, chainID, blockID, ext)
|
||||
@@ -526,10 +527,11 @@ func loadPrivValidator(t *testing.T, cfg *config.Config) *privval.FilePV {
|
||||
}
|
||||
|
||||
type makeStateArgs struct {
|
||||
config *config.Config
|
||||
logger log.Logger
|
||||
validators int
|
||||
application abci.Application
|
||||
config *config.Config
|
||||
consensusParams *types.ConsensusParams
|
||||
logger log.Logger
|
||||
validators int
|
||||
application abci.Application
|
||||
}
|
||||
|
||||
func makeState(ctx context.Context, t *testing.T, args makeStateArgs) (*State, []*validatorStub) {
|
||||
@@ -550,9 +552,13 @@ func makeState(ctx context.Context, t *testing.T, args makeStateArgs) (*State, [
|
||||
if args.logger == nil {
|
||||
args.logger = log.NewNopLogger()
|
||||
}
|
||||
c := factory.ConsensusParams()
|
||||
if args.consensusParams != nil {
|
||||
c = args.consensusParams
|
||||
}
|
||||
|
||||
state, privVals := makeGenesisState(ctx, t, args.config, genesisStateArgs{
|
||||
Params: factory.ConsensusParams(),
|
||||
Params: c,
|
||||
Validators: validators,
|
||||
})
|
||||
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
// Code generated by metricsgen. DO NOT EDIT.
|
||||
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/metrics/discard"
|
||||
prometheus "github.com/go-kit/kit/metrics/prometheus"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics {
|
||||
labels := []string{}
|
||||
for i := 0; i < len(labelsAndValues); i += 2 {
|
||||
labels = append(labels, labelsAndValues[i])
|
||||
}
|
||||
return &Metrics{
|
||||
Height: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "height",
|
||||
Help: "Height of the chain.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
ValidatorLastSignedHeight: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "validator_last_signed_height",
|
||||
Help: "Last height signed by this validator if the node is a validator.",
|
||||
}, append(labels, "validator_address")).With(labelsAndValues...),
|
||||
Rounds: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "rounds",
|
||||
Help: "Number of rounds.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
RoundDuration: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "round_duration",
|
||||
Help: "Histogram of round duration.",
|
||||
|
||||
Buckets: stdprometheus.ExponentialBucketsRange(0.1, 100, 8),
|
||||
}, labels).With(labelsAndValues...),
|
||||
Validators: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "validators",
|
||||
Help: "Number of validators.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
ValidatorsPower: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "validators_power",
|
||||
Help: "Total power of all validators.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
ValidatorPower: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "validator_power",
|
||||
Help: "Power of a validator.",
|
||||
}, append(labels, "validator_address")).With(labelsAndValues...),
|
||||
ValidatorMissedBlocks: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "validator_missed_blocks",
|
||||
Help: "Amount of blocks missed per validator.",
|
||||
}, append(labels, "validator_address")).With(labelsAndValues...),
|
||||
MissingValidators: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "missing_validators",
|
||||
Help: "Number of validators who did not sign.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
MissingValidatorsPower: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "missing_validators_power",
|
||||
Help: "Total power of the missing validators.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
ByzantineValidators: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "byzantine_validators",
|
||||
Help: "Number of validators who tried to double sign.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
ByzantineValidatorsPower: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "byzantine_validators_power",
|
||||
Help: "Total power of the byzantine validators.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
BlockIntervalSeconds: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "block_interval_seconds",
|
||||
Help: "Time between this and the last block.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
NumTxs: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "num_txs",
|
||||
Help: "Number of transactions.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
BlockSizeBytes: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "block_size_bytes",
|
||||
Help: "Size of the block.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
TotalTxs: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "total_txs",
|
||||
Help: "Total number of transactions.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
CommittedHeight: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "latest_block_height",
|
||||
Help: "The latest block height.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
BlockSyncing: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "block_syncing",
|
||||
Help: "Whether or not a node is block syncing. 1 if yes, 0 if no.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
StateSyncing: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "state_syncing",
|
||||
Help: "Whether or not a node is state syncing. 1 if yes, 0 if no.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
BlockParts: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "block_parts",
|
||||
Help: "Number of block parts transmitted by each peer.",
|
||||
}, append(labels, "peer_id")).With(labelsAndValues...),
|
||||
StepDuration: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "step_duration",
|
||||
Help: "Histogram of durations for each step in the consensus protocol.",
|
||||
|
||||
Buckets: stdprometheus.ExponentialBucketsRange(0.1, 100, 8),
|
||||
}, append(labels, "step")).With(labelsAndValues...),
|
||||
BlockGossipReceiveLatency: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "block_gossip_receive_latency",
|
||||
Help: "Histogram of time taken to receive a block in seconds, measured between when a new block is first discovered to when the block is completed.",
|
||||
|
||||
Buckets: stdprometheus.ExponentialBucketsRange(0.1, 100, 8),
|
||||
}, labels).With(labelsAndValues...),
|
||||
BlockGossipPartsReceived: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "block_gossip_parts_received",
|
||||
Help: "Number of block parts received by the node, separated by whether the part was relevant to the block the node is trying to gather or not.",
|
||||
}, append(labels, "matches_current")).With(labelsAndValues...),
|
||||
QuorumPrevoteDelay: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "quorum_prevote_delay",
|
||||
Help: "Interval in seconds between the proposal timestamp and the timestamp of the earliest prevote that achieved a quorum.",
|
||||
}, append(labels, "proposer_address")).With(labelsAndValues...),
|
||||
FullPrevoteDelay: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "full_prevote_delay",
|
||||
Help: "Interval in seconds between the proposal timestamp and the timestamp of the latest prevote in a round where all validators voted.",
|
||||
}, append(labels, "proposer_address")).With(labelsAndValues...),
|
||||
ProposalTimestampDifference: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "proposal_timestamp_difference",
|
||||
Help: "Difference between the timestamp in the proposal message and the local time of the validator at the time it received the message.",
|
||||
|
||||
Buckets: []float64{-10, -.5, -.025, 0, .1, .5, 1, 1.5, 2, 10},
|
||||
}, append(labels, "is_timely")).With(labelsAndValues...),
|
||||
VoteExtensionReceiveCount: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "vote_extension_receive_count",
|
||||
Help: "Number of vote extensions received labeled by application response status.",
|
||||
}, append(labels, "status")).With(labelsAndValues...),
|
||||
ProposalReceiveCount: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "proposal_receive_count",
|
||||
Help: "Total number of proposals received by the node since process start labeled by application response status.",
|
||||
}, append(labels, "status")).With(labelsAndValues...),
|
||||
ProposalCreateCount: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "proposal_create_count",
|
||||
Help: "Total number of proposals created by the node since process start.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
RoundVotingPowerPercent: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "round_voting_power_percent",
|
||||
Help: "A value between 0 and 1.0 representing the percentage of the total voting power per vote type received within a round.",
|
||||
}, append(labels, "vote_type")).With(labelsAndValues...),
|
||||
LateVotes: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "late_votes",
|
||||
Help: "Number of votes received by the node since process start that correspond to earlier heights and rounds than this node is currently in.",
|
||||
}, append(labels, "vote_type")).With(labelsAndValues...),
|
||||
}
|
||||
}
|
||||
|
||||
func NopMetrics() *Metrics {
|
||||
return &Metrics{
|
||||
Height: discard.NewGauge(),
|
||||
ValidatorLastSignedHeight: discard.NewGauge(),
|
||||
Rounds: discard.NewGauge(),
|
||||
RoundDuration: discard.NewHistogram(),
|
||||
Validators: discard.NewGauge(),
|
||||
ValidatorsPower: discard.NewGauge(),
|
||||
ValidatorPower: discard.NewGauge(),
|
||||
ValidatorMissedBlocks: discard.NewGauge(),
|
||||
MissingValidators: discard.NewGauge(),
|
||||
MissingValidatorsPower: discard.NewGauge(),
|
||||
ByzantineValidators: discard.NewGauge(),
|
||||
ByzantineValidatorsPower: discard.NewGauge(),
|
||||
BlockIntervalSeconds: discard.NewHistogram(),
|
||||
NumTxs: discard.NewGauge(),
|
||||
BlockSizeBytes: discard.NewHistogram(),
|
||||
TotalTxs: discard.NewGauge(),
|
||||
CommittedHeight: discard.NewGauge(),
|
||||
BlockSyncing: discard.NewGauge(),
|
||||
StateSyncing: discard.NewGauge(),
|
||||
BlockParts: discard.NewCounter(),
|
||||
StepDuration: discard.NewHistogram(),
|
||||
BlockGossipReceiveLatency: discard.NewHistogram(),
|
||||
BlockGossipPartsReceived: discard.NewCounter(),
|
||||
QuorumPrevoteDelay: discard.NewGauge(),
|
||||
FullPrevoteDelay: discard.NewGauge(),
|
||||
ProposalTimestampDifference: discard.NewHistogram(),
|
||||
VoteExtensionReceiveCount: discard.NewCounter(),
|
||||
ProposalReceiveCount: discard.NewCounter(),
|
||||
ProposalCreateCount: discard.NewCounter(),
|
||||
RoundVotingPowerPercent: discard.NewGauge(),
|
||||
LateVotes: discard.NewCounter(),
|
||||
}
|
||||
}
|
||||
+82
-230
@@ -5,13 +5,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/metrics"
|
||||
"github.com/go-kit/kit/metrics/discard"
|
||||
|
||||
cstypes "github.com/tendermint/tendermint/internal/consensus/types"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
|
||||
prometheus "github.com/go-kit/kit/metrics/prometheus"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -20,28 +17,30 @@ const (
|
||||
MetricsSubsystem = "consensus"
|
||||
)
|
||||
|
||||
//go:generate go run ../../scripts/metricsgen -struct=Metrics
|
||||
|
||||
// Metrics contains metrics exposed by this package.
|
||||
type Metrics struct {
|
||||
// Height of the chain.
|
||||
Height metrics.Gauge
|
||||
|
||||
// ValidatorLastSignedHeight of a validator.
|
||||
ValidatorLastSignedHeight metrics.Gauge
|
||||
// Last height signed by this validator if the node is a validator.
|
||||
ValidatorLastSignedHeight metrics.Gauge `metrics_labels:"validator_address"`
|
||||
|
||||
// Number of rounds.
|
||||
Rounds metrics.Gauge
|
||||
|
||||
// Histogram of round duration.
|
||||
RoundDuration metrics.Histogram
|
||||
RoundDuration metrics.Histogram `metrics_buckettype:"exprange" metrics_bucketsizes:"0.1, 100, 8"`
|
||||
|
||||
// Number of validators.
|
||||
Validators metrics.Gauge
|
||||
// Total power of all validators.
|
||||
ValidatorsPower metrics.Gauge
|
||||
// Power of a validator.
|
||||
ValidatorPower metrics.Gauge
|
||||
// Amount of blocks missed by a validator.
|
||||
ValidatorMissedBlocks metrics.Gauge
|
||||
ValidatorPower metrics.Gauge `metrics_labels:"validator_address"`
|
||||
// Amount of blocks missed per validator.
|
||||
ValidatorMissedBlocks metrics.Gauge `metrics_labels:"validator_address"`
|
||||
// Number of validators who did not sign.
|
||||
MissingValidators metrics.Gauge
|
||||
// Total power of the missing validators.
|
||||
@@ -61,27 +60,27 @@ type Metrics struct {
|
||||
// Total number of transactions.
|
||||
TotalTxs metrics.Gauge
|
||||
// The latest block height.
|
||||
CommittedHeight metrics.Gauge
|
||||
CommittedHeight metrics.Gauge `metrics_name:"latest_block_height"`
|
||||
// Whether or not a node is block syncing. 1 if yes, 0 if no.
|
||||
BlockSyncing metrics.Gauge
|
||||
// Whether or not a node is state syncing. 1 if yes, 0 if no.
|
||||
StateSyncing metrics.Gauge
|
||||
|
||||
// Number of blockparts transmitted by peer.
|
||||
BlockParts metrics.Counter
|
||||
// Number of block parts transmitted by each peer.
|
||||
BlockParts metrics.Counter `metrics_labels:"peer_id"`
|
||||
|
||||
// Histogram of step duration.
|
||||
StepDuration metrics.Histogram
|
||||
// Histogram of durations for each step in the consensus protocol.
|
||||
StepDuration metrics.Histogram `metrics_labels:"step" metrics_buckettype:"exprange" metrics_bucketsizes:"0.1, 100, 8"`
|
||||
stepStart time.Time
|
||||
|
||||
// Histogram of time taken to receive a block in seconds, measured between when a new block is first
|
||||
// discovered to when the block is completed.
|
||||
BlockGossipReceiveLatency metrics.Histogram
|
||||
BlockGossipReceiveLatency metrics.Histogram `metrics_buckettype:"exprange" metrics_bucketsizes:"0.1, 100, 8"`
|
||||
blockGossipStart time.Time
|
||||
|
||||
// Number of block parts received by the node, separated by whether the part
|
||||
// was relevant to the block the node is trying to gather or not.
|
||||
BlockGossipPartsReceived metrics.Counter
|
||||
BlockGossipPartsReceived metrics.Counter `metrics_labels:"matches_current"`
|
||||
|
||||
// QuroumPrevoteMessageDelay is the interval in seconds between the proposal
|
||||
// timestamp and the timestamp of the earliest prevote that achieved a quorum
|
||||
@@ -92,232 +91,50 @@ type Metrics struct {
|
||||
// be above 2/3 of the total voting power of the network defines the endpoint
|
||||
// the endpoint of the interval. Subtract the proposal timestamp from this endpoint
|
||||
// to obtain the quorum delay.
|
||||
QuorumPrevoteDelay metrics.Gauge
|
||||
//metrics:Interval in seconds between the proposal timestamp and the timestamp of the earliest prevote that achieved a quorum.
|
||||
QuorumPrevoteDelay metrics.Gauge `metrics_labels:"proposer_address"`
|
||||
|
||||
// FullPrevoteDelay is the interval in seconds between the proposal
|
||||
// timestamp and the timestamp of the latest prevote in a round where 100%
|
||||
// of the voting power on the network issued prevotes.
|
||||
FullPrevoteDelay metrics.Gauge
|
||||
//metrics:Interval in seconds between the proposal timestamp and the timestamp of the latest prevote in a round where all validators voted.
|
||||
FullPrevoteDelay metrics.Gauge `metrics_labels:"proposer_address"`
|
||||
|
||||
// ProposalTimestampDifference is the difference between the timestamp in
|
||||
// the proposal message and the local time of the validator at the time
|
||||
// that the validator received the message.
|
||||
ProposalTimestampDifference metrics.Histogram
|
||||
}
|
||||
//metrics:Difference between the timestamp in the proposal message and the local time of the validator at the time it received the message.
|
||||
ProposalTimestampDifference metrics.Histogram `metrics_labels:"is_timely" metrics_bucketsizes:"-10, -.5, -.025, 0, .1, .5, 1, 1.5, 2, 10"`
|
||||
|
||||
// PrometheusMetrics returns Metrics build using Prometheus client library.
|
||||
// Optionally, labels can be provided along with their values ("foo",
|
||||
// "fooValue").
|
||||
func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics {
|
||||
labels := []string{}
|
||||
for i := 0; i < len(labelsAndValues); i += 2 {
|
||||
labels = append(labels, labelsAndValues[i])
|
||||
}
|
||||
return &Metrics{
|
||||
Height: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "height",
|
||||
Help: "Height of the chain.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
Rounds: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "rounds",
|
||||
Help: "Number of rounds.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
RoundDuration: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "round_duration",
|
||||
Help: "Time spent in a round.",
|
||||
Buckets: stdprometheus.ExponentialBucketsRange(0.1, 100, 8),
|
||||
}, labels).With(labelsAndValues...),
|
||||
Validators: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "validators",
|
||||
Help: "Number of validators.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
ValidatorLastSignedHeight: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "validator_last_signed_height",
|
||||
Help: "Last signed height for a validator",
|
||||
}, append(labels, "validator_address")).With(labelsAndValues...),
|
||||
ValidatorMissedBlocks: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "validator_missed_blocks",
|
||||
Help: "Total missed blocks for a validator",
|
||||
}, append(labels, "validator_address")).With(labelsAndValues...),
|
||||
ValidatorsPower: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "validators_power",
|
||||
Help: "Total power of all validators.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
ValidatorPower: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "validator_power",
|
||||
Help: "Power of a validator",
|
||||
}, append(labels, "validator_address")).With(labelsAndValues...),
|
||||
MissingValidators: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "missing_validators",
|
||||
Help: "Number of validators who did not sign.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
MissingValidatorsPower: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "missing_validators_power",
|
||||
Help: "Total power of the missing validators.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
ByzantineValidators: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "byzantine_validators",
|
||||
Help: "Number of validators who tried to double sign.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
ByzantineValidatorsPower: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "byzantine_validators_power",
|
||||
Help: "Total power of the byzantine validators.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
BlockIntervalSeconds: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "block_interval_seconds",
|
||||
Help: "Time between this and the last block.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
NumTxs: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "num_txs",
|
||||
Help: "Number of transactions.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
BlockSizeBytes: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "block_size_bytes",
|
||||
Help: "Size of the block.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
TotalTxs: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "total_txs",
|
||||
Help: "Total number of transactions.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
CommittedHeight: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "latest_block_height",
|
||||
Help: "The latest block height.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
BlockSyncing: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "block_syncing",
|
||||
Help: "Whether or not a node is block syncing. 1 if yes, 0 if no.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
StateSyncing: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "state_syncing",
|
||||
Help: "Whether or not a node is state syncing. 1 if yes, 0 if no.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
BlockParts: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "block_parts",
|
||||
Help: "Number of blockparts transmitted by peer.",
|
||||
}, append(labels, "peer_id")).With(labelsAndValues...),
|
||||
BlockGossipReceiveLatency: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "block_gossip_receive_latency",
|
||||
Help: "Difference in seconds between when the validator learns of a new block" +
|
||||
"and when the validator receives the last piece of the block.",
|
||||
Buckets: stdprometheus.ExponentialBucketsRange(0.1, 100, 8),
|
||||
}, labels).With(labelsAndValues...),
|
||||
BlockGossipPartsReceived: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "block_gossip_parts_received",
|
||||
Help: "Number of block parts received by the node, labeled by whether the " +
|
||||
"part was relevant to the block the node was currently gathering or not.",
|
||||
}, append(labels, "matches_current")).With(labelsAndValues...),
|
||||
StepDuration: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "step_duration",
|
||||
Help: "Time spent per step.",
|
||||
Buckets: stdprometheus.ExponentialBucketsRange(0.1, 100, 8),
|
||||
}, append(labels, "step")).With(labelsAndValues...),
|
||||
QuorumPrevoteDelay: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "quorum_prevote_delay",
|
||||
Help: "Difference in seconds between the proposal timestamp and the timestamp " +
|
||||
"of the latest prevote that achieved a quorum in the prevote step.",
|
||||
}, append(labels, "proposer_address")).With(labelsAndValues...),
|
||||
FullPrevoteDelay: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "full_prevote_delay",
|
||||
Help: "Difference in seconds between the proposal timestamp and the timestamp " +
|
||||
"of the latest prevote that achieved 100% of the voting power in the prevote step.",
|
||||
}, append(labels, "proposer_address")).With(labelsAndValues...),
|
||||
ProposalTimestampDifference: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "proposal_timestamp_difference",
|
||||
Help: "Difference in seconds between the timestamp in the proposal " +
|
||||
"message and the local time when the message was received. " +
|
||||
"Only calculated when a new block is proposed.",
|
||||
Buckets: []float64{-10, -.5, -.025, 0, .1, .5, 1, 1.5, 2, 10},
|
||||
}, append(labels, "is_timely")).With(labelsAndValues...),
|
||||
}
|
||||
}
|
||||
// VoteExtensionReceiveCount is the number of vote extensions received by this
|
||||
// node. The metric is annotated by the status of the vote extension from the
|
||||
// application, either 'accepted' or 'rejected'.
|
||||
//metrics:Number of vote extensions received labeled by application response status.
|
||||
VoteExtensionReceiveCount metrics.Counter `metrics_labels:"status"`
|
||||
|
||||
// NopMetrics returns no-op Metrics.
|
||||
func NopMetrics() *Metrics {
|
||||
return &Metrics{
|
||||
Height: discard.NewGauge(),
|
||||
// ProposalReceiveCount is the total number of proposals received by this node
|
||||
// since process start.
|
||||
// The metric is annotated by the status of the proposal from the application,
|
||||
// either 'accepted' or 'rejected'.
|
||||
//metrics:Total number of proposals received by the node since process start labeled by application response status.
|
||||
ProposalReceiveCount metrics.Counter `metrics_labels:"status"`
|
||||
|
||||
ValidatorLastSignedHeight: discard.NewGauge(),
|
||||
// ProposalCreationCount is the total number of proposals created by this node
|
||||
// since process start.
|
||||
//metrics:Total number of proposals created by the node since process start.
|
||||
ProposalCreateCount metrics.Counter
|
||||
|
||||
Rounds: discard.NewGauge(),
|
||||
RoundDuration: discard.NewHistogram(),
|
||||
StepDuration: discard.NewHistogram(),
|
||||
// RoundVotingPowerPercent is the percentage of the total voting power received
|
||||
// with a round. The value begins at 0 for each round and approaches 1.0 as
|
||||
// additional voting power is observed. The metric is labeled by vote type.
|
||||
//metrics:A value between 0 and 1.0 representing the percentage of the total voting power per vote type received within a round.
|
||||
RoundVotingPowerPercent metrics.Gauge `metrics_labels:"vote_type"`
|
||||
|
||||
Validators: discard.NewGauge(),
|
||||
ValidatorsPower: discard.NewGauge(),
|
||||
ValidatorPower: discard.NewGauge(),
|
||||
ValidatorMissedBlocks: discard.NewGauge(),
|
||||
MissingValidators: discard.NewGauge(),
|
||||
MissingValidatorsPower: discard.NewGauge(),
|
||||
ByzantineValidators: discard.NewGauge(),
|
||||
ByzantineValidatorsPower: discard.NewGauge(),
|
||||
|
||||
BlockIntervalSeconds: discard.NewHistogram(),
|
||||
|
||||
NumTxs: discard.NewGauge(),
|
||||
BlockSizeBytes: discard.NewHistogram(),
|
||||
TotalTxs: discard.NewGauge(),
|
||||
CommittedHeight: discard.NewGauge(),
|
||||
BlockSyncing: discard.NewGauge(),
|
||||
StateSyncing: discard.NewGauge(),
|
||||
BlockParts: discard.NewCounter(),
|
||||
BlockGossipReceiveLatency: discard.NewHistogram(),
|
||||
BlockGossipPartsReceived: discard.NewCounter(),
|
||||
QuorumPrevoteDelay: discard.NewGauge(),
|
||||
FullPrevoteDelay: discard.NewGauge(),
|
||||
ProposalTimestampDifference: discard.NewHistogram(),
|
||||
}
|
||||
// LateVotes stores the number of votes that were received by this node that
|
||||
// correspond to earlier heights and rounds than this node is currently
|
||||
// in.
|
||||
//metrics:Number of votes received by the node since process start that correspond to earlier heights and rounds than this node is currently in.
|
||||
LateVotes metrics.Counter `metrics_labels:"vote_type"`
|
||||
}
|
||||
|
||||
// RecordConsMetrics uses for recording the block related metrics during fast-sync.
|
||||
@@ -336,10 +153,45 @@ func (m *Metrics) MarkBlockGossipComplete() {
|
||||
m.BlockGossipReceiveLatency.Observe(time.Since(m.blockGossipStart).Seconds())
|
||||
}
|
||||
|
||||
func (m *Metrics) MarkProposalProcessed(accepted bool) {
|
||||
status := "accepted"
|
||||
if !accepted {
|
||||
status = "rejected"
|
||||
}
|
||||
m.ProposalReceiveCount.With("status", status).Add(1)
|
||||
}
|
||||
|
||||
func (m *Metrics) MarkVoteExtensionReceived(accepted bool) {
|
||||
status := "accepted"
|
||||
if !accepted {
|
||||
status = "rejected"
|
||||
}
|
||||
m.VoteExtensionReceiveCount.With("status", status).Add(1)
|
||||
}
|
||||
|
||||
func (m *Metrics) MarkVoteReceived(vt tmproto.SignedMsgType, power, totalPower int64) {
|
||||
p := float64(power) / float64(totalPower)
|
||||
n := strings.ToLower(strings.TrimPrefix(vt.String(), "SIGNED_MSG_TYPE_"))
|
||||
m.RoundVotingPowerPercent.With("vote_type", n).Add(p)
|
||||
}
|
||||
|
||||
func (m *Metrics) MarkRound(r int32, st time.Time) {
|
||||
m.Rounds.Set(float64(r))
|
||||
roundTime := time.Since(st).Seconds()
|
||||
m.RoundDuration.Observe(roundTime)
|
||||
|
||||
pvt := tmproto.PrevoteType
|
||||
pvn := strings.ToLower(strings.TrimPrefix(pvt.String(), "SIGNED_MSG_TYPE_"))
|
||||
m.RoundVotingPowerPercent.With("vote_type", pvn).Set(0)
|
||||
|
||||
pct := tmproto.PrecommitType
|
||||
pcn := strings.ToLower(strings.TrimPrefix(pct.String(), "SIGNED_MSG_TYPE_"))
|
||||
m.RoundVotingPowerPercent.With("vote_type", pcn).Set(0)
|
||||
}
|
||||
|
||||
func (m *Metrics) MarkLateVote(vt tmproto.SignedMsgType) {
|
||||
n := strings.ToLower(strings.TrimPrefix(vt.String(), "SIGNED_MSG_TYPE_"))
|
||||
m.LateVotes.With("vote_type", n).Add(1)
|
||||
}
|
||||
|
||||
func (m *Metrics) MarkStep(s cstypes.RoundStepType) {
|
||||
|
||||
@@ -222,11 +222,7 @@ func (*VoteMessage) TypeTag() string { return "tendermint/Vote" }
|
||||
|
||||
// ValidateBasic checks whether the vote within the message is well-formed.
|
||||
func (m *VoteMessage) ValidateBasic() error {
|
||||
// Here we validate votes with vote extensions, since we require vote
|
||||
// extensions to be sent in precommit messages during consensus. Prevote
|
||||
// messages should never have vote extensions, and this is also validated
|
||||
// here.
|
||||
return m.Vote.ValidateWithExtension()
|
||||
return m.Vote.ValidateBasic()
|
||||
}
|
||||
|
||||
// String returns a string representation.
|
||||
|
||||
@@ -67,7 +67,7 @@ func TestMsgToProto(t *testing.T) {
|
||||
|
||||
pv := types.NewMockPV()
|
||||
vote, err := factory.MakeVote(ctx, pv, factory.DefaultTestChainID,
|
||||
0, 1, 0, 2, types.BlockID{}, time.Now())
|
||||
0, 1, 0, 2, bi, time.Now())
|
||||
require.NoError(t, err)
|
||||
pbVote := vote.ToProto()
|
||||
|
||||
|
||||
@@ -216,6 +216,8 @@ func (r *Reactor) OnStart(ctx context.Context) error {
|
||||
if err := r.state.Start(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err := r.state.updateStateFromStore(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go r.updateRoundStateRoutine(ctx)
|
||||
@@ -794,15 +796,22 @@ func (r *Reactor) gossipVotesRoutine(ctx context.Context, ps *PeerState, voteCh
|
||||
// catchup logic -- if peer is lagging by more than 1, send Commit
|
||||
blockStoreBase := r.state.blockStore.Base()
|
||||
if blockStoreBase > 0 && prs.Height != 0 && rs.Height >= prs.Height+2 && prs.Height >= blockStoreBase {
|
||||
// Load the block commit for prs.Height, which contains precommit
|
||||
// Load the block's extended commit for prs.Height, which contains precommit
|
||||
// signatures for prs.Height.
|
||||
if commit := r.state.blockStore.LoadBlockCommit(prs.Height); commit != nil {
|
||||
if ok, err := r.pickSendVote(ctx, ps, commit, voteCh); err != nil {
|
||||
return
|
||||
} else if ok {
|
||||
logger.Debug("picked Catchup commit to send", "height", prs.Height)
|
||||
continue
|
||||
}
|
||||
var ec *types.ExtendedCommit
|
||||
if r.state.state.ConsensusParams.ABCI.VoteExtensionsEnabled(prs.Height) {
|
||||
ec = r.state.blockStore.LoadBlockExtendedCommit(prs.Height)
|
||||
} else {
|
||||
ec = r.state.blockStore.LoadBlockCommit(prs.Height).WrappedExtendedCommit()
|
||||
}
|
||||
if ec == nil {
|
||||
continue
|
||||
}
|
||||
if ok, err := r.pickSendVote(ctx, ps, ec, voteCh); err != nil {
|
||||
return
|
||||
} else if ok {
|
||||
logger.Debug("picked Catchup commit to send", "height", prs.Height)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
"github.com/tendermint/tendermint/internal/test/factory"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
tmcons "github.com/tendermint/tendermint/proto/tendermint/consensus"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -600,6 +601,118 @@ func TestReactorCreatesBlockWhenEmptyBlocksFalse(t *testing.T) {
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// TestSwitchToConsensusVoteExtensions tests that the SwitchToConsensus correctly
|
||||
// checks for vote extension data when required.
|
||||
func TestSwitchToConsensusVoteExtensions(t *testing.T) {
|
||||
for _, testCase := range []struct {
|
||||
name string
|
||||
storedHeight int64
|
||||
initialRequiredHeight int64
|
||||
includeExtensions bool
|
||||
shouldPanic bool
|
||||
}{
|
||||
{
|
||||
name: "no vote extensions but not required",
|
||||
initialRequiredHeight: 0,
|
||||
storedHeight: 2,
|
||||
includeExtensions: false,
|
||||
shouldPanic: false,
|
||||
},
|
||||
{
|
||||
name: "no vote extensions but required this height",
|
||||
initialRequiredHeight: 2,
|
||||
storedHeight: 2,
|
||||
includeExtensions: false,
|
||||
shouldPanic: true,
|
||||
},
|
||||
{
|
||||
name: "no vote extensions and required in future",
|
||||
initialRequiredHeight: 3,
|
||||
storedHeight: 2,
|
||||
includeExtensions: false,
|
||||
shouldPanic: false,
|
||||
},
|
||||
{
|
||||
name: "no vote extensions and required previous height",
|
||||
initialRequiredHeight: 1,
|
||||
storedHeight: 2,
|
||||
includeExtensions: false,
|
||||
shouldPanic: true,
|
||||
},
|
||||
{
|
||||
name: "vote extensions and required previous height",
|
||||
initialRequiredHeight: 1,
|
||||
storedHeight: 2,
|
||||
includeExtensions: true,
|
||||
shouldPanic: false,
|
||||
},
|
||||
} {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*15)
|
||||
defer cancel()
|
||||
cs, vs := makeState(ctx, t, makeStateArgs{validators: 1})
|
||||
validator := vs[0]
|
||||
validator.Height = testCase.storedHeight
|
||||
|
||||
cs.state.LastBlockHeight = testCase.storedHeight
|
||||
cs.state.LastValidators = cs.state.Validators.Copy()
|
||||
cs.state.ConsensusParams.ABCI.VoteExtensionsEnableHeight = testCase.initialRequiredHeight
|
||||
|
||||
propBlock, err := cs.createProposalBlock(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Consensus is preparing to do the next height after the stored height.
|
||||
cs.Height = testCase.storedHeight + 1
|
||||
propBlock.Height = testCase.storedHeight
|
||||
blockParts, err := propBlock.MakePartSet(types.BlockPartSizeBytes)
|
||||
require.NoError(t, err)
|
||||
|
||||
var voteSet *types.VoteSet
|
||||
if testCase.includeExtensions {
|
||||
voteSet = types.NewExtendedVoteSet(cs.state.ChainID, testCase.storedHeight, 0, tmproto.PrecommitType, cs.state.Validators)
|
||||
} else {
|
||||
voteSet = types.NewVoteSet(cs.state.ChainID, testCase.storedHeight, 0, tmproto.PrecommitType, cs.state.Validators)
|
||||
}
|
||||
signedVote := signVote(ctx, t, validator, tmproto.PrecommitType, cs.state.ChainID, types.BlockID{
|
||||
Hash: propBlock.Hash(),
|
||||
PartSetHeader: blockParts.Header(),
|
||||
})
|
||||
|
||||
if !testCase.includeExtensions {
|
||||
signedVote.Extension = nil
|
||||
signedVote.ExtensionSignature = nil
|
||||
}
|
||||
|
||||
added, err := voteSet.AddVote(signedVote)
|
||||
require.NoError(t, err)
|
||||
require.True(t, added)
|
||||
|
||||
if testCase.includeExtensions {
|
||||
cs.blockStore.SaveBlockWithExtendedCommit(propBlock, blockParts, voteSet.MakeExtendedCommit())
|
||||
} else {
|
||||
cs.blockStore.SaveBlock(propBlock, blockParts, voteSet.MakeExtendedCommit().ToCommit())
|
||||
}
|
||||
reactor := NewReactor(
|
||||
log.NewNopLogger(),
|
||||
cs,
|
||||
nil,
|
||||
nil,
|
||||
cs.eventBus,
|
||||
true,
|
||||
NopMetrics(),
|
||||
)
|
||||
|
||||
if testCase.shouldPanic {
|
||||
assert.Panics(t, func() {
|
||||
reactor.SwitchToConsensus(ctx, cs.state, false)
|
||||
})
|
||||
} else {
|
||||
reactor.SwitchToConsensus(ctx, cs.state, false)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReactorRecordsVotesAndBlockParts(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
|
||||
@@ -297,7 +297,7 @@ type simulatorTestSuite struct {
|
||||
GenesisState sm.State
|
||||
Config *config.Config
|
||||
Chain []*types.Block
|
||||
Commits []*types.Commit
|
||||
ExtCommits []*types.ExtendedCommit
|
||||
CleanupFunc cleanupFunc
|
||||
|
||||
Mempool mempool.Mempool
|
||||
@@ -578,11 +578,11 @@ func setupSimulator(ctx context.Context, t *testing.T) *simulatorTestSuite {
|
||||
}
|
||||
ensureNewRound(t, newRoundCh, height+1, 0)
|
||||
|
||||
sim.Chain = make([]*types.Block, 0)
|
||||
sim.Commits = make([]*types.Commit, 0)
|
||||
sim.Chain = []*types.Block{}
|
||||
sim.ExtCommits = []*types.ExtendedCommit{}
|
||||
for i := 1; i <= numBlocks; i++ {
|
||||
sim.Chain = append(sim.Chain, css[0].blockStore.LoadBlock(int64(i)))
|
||||
sim.Commits = append(sim.Commits, css[0].blockStore.LoadBlockCommit(int64(i)))
|
||||
sim.ExtCommits = append(sim.ExtCommits, css[0].blockStore.LoadBlockExtendedCommit(int64(i)))
|
||||
}
|
||||
|
||||
return sim
|
||||
@@ -679,7 +679,7 @@ func testHandshakeReplay(
|
||||
testValidatorsChange bool,
|
||||
) {
|
||||
var chain []*types.Block
|
||||
var commits []*types.Commit
|
||||
var extCommits []*types.ExtendedCommit
|
||||
var store *mockBlockStore
|
||||
var stateDB dbm.DB
|
||||
var genesisState sm.State
|
||||
@@ -699,7 +699,7 @@ func testHandshakeReplay(
|
||||
genesisState = sim.GenesisState
|
||||
cfg = sim.Config
|
||||
chain = append([]*types.Block{}, sim.Chain...) // copy chain
|
||||
commits = sim.Commits
|
||||
extCommits = sim.ExtCommits
|
||||
store = newMockBlockStore(t, cfg, genesisState.ConsensusParams)
|
||||
} else { // test single node
|
||||
testConfig, err := ResetConfig(t.TempDir(), fmt.Sprintf("%s_%v_s", t.Name(), mode))
|
||||
@@ -718,7 +718,7 @@ func testHandshakeReplay(
|
||||
err = wal.Start(ctx)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { cancel(); wal.Wait() })
|
||||
chain, commits = makeBlockchainFromWAL(t, wal)
|
||||
chain, extCommits = makeBlockchainFromWAL(t, wal)
|
||||
pubKey, err := privVal.GetPubKey(ctx)
|
||||
require.NoError(t, err)
|
||||
stateDB, genesisState, store = stateAndStore(t, cfg, pubKey, kvstore.ProtocolVersion)
|
||||
@@ -726,7 +726,7 @@ func testHandshakeReplay(
|
||||
}
|
||||
stateStore := sm.NewStore(stateDB)
|
||||
store.chain = chain
|
||||
store.commits = commits
|
||||
store.extCommits = extCommits
|
||||
|
||||
state := genesisState.Copy()
|
||||
// run the chain through state.ApplyBlock to build up the tendermint state
|
||||
@@ -1034,7 +1034,7 @@ func (app *badApp) Commit(context.Context) (*abci.ResponseCommit, error) {
|
||||
//--------------------------
|
||||
// utils for making blocks
|
||||
|
||||
func makeBlockchainFromWAL(t *testing.T, wal WAL) ([]*types.Block, []*types.Commit) {
|
||||
func makeBlockchainFromWAL(t *testing.T, wal WAL) ([]*types.Block, []*types.ExtendedCommit) {
|
||||
t.Helper()
|
||||
var height int64
|
||||
|
||||
@@ -1047,10 +1047,10 @@ func makeBlockchainFromWAL(t *testing.T, wal WAL) ([]*types.Block, []*types.Comm
|
||||
// log.Notice("Build a blockchain by reading from the WAL")
|
||||
|
||||
var (
|
||||
blocks []*types.Block
|
||||
commits []*types.Commit
|
||||
thisBlockParts *types.PartSet
|
||||
thisBlockCommit *types.Commit
|
||||
blocks []*types.Block
|
||||
extCommits []*types.ExtendedCommit
|
||||
thisBlockParts *types.PartSet
|
||||
thisBlockExtCommit *types.ExtendedCommit
|
||||
)
|
||||
|
||||
dec := NewWALDecoder(gr)
|
||||
@@ -1082,12 +1082,12 @@ func makeBlockchainFromWAL(t *testing.T, wal WAL) ([]*types.Block, []*types.Comm
|
||||
require.Equal(t, block.Height, height+1,
|
||||
"read bad block from wal. got height %d, expected %d", block.Height, height+1)
|
||||
|
||||
commitHeight := thisBlockCommit.Height
|
||||
commitHeight := thisBlockExtCommit.Height
|
||||
require.Equal(t, commitHeight, height+1,
|
||||
"commit doesnt match. got height %d, expected %d", commitHeight, height+1)
|
||||
|
||||
blocks = append(blocks, block)
|
||||
commits = append(commits, thisBlockCommit)
|
||||
extCommits = append(extCommits, thisBlockExtCommit)
|
||||
height++
|
||||
}
|
||||
case *types.PartSetHeader:
|
||||
@@ -1097,8 +1097,12 @@ func makeBlockchainFromWAL(t *testing.T, wal WAL) ([]*types.Block, []*types.Comm
|
||||
require.NoError(t, err)
|
||||
case *types.Vote:
|
||||
if p.Type == tmproto.PrecommitType {
|
||||
thisBlockCommit = types.NewCommit(p.Height, p.Round,
|
||||
p.BlockID, []types.CommitSig{p.CommitSig()})
|
||||
thisBlockExtCommit = &types.ExtendedCommit{
|
||||
Height: p.Height,
|
||||
Round: p.Round,
|
||||
BlockID: p.BlockID,
|
||||
ExtendedSignatures: []types.ExtendedCommitSig{p.ExtendedCommitSig()},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1113,12 +1117,12 @@ func makeBlockchainFromWAL(t *testing.T, wal WAL) ([]*types.Block, []*types.Comm
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, block.Height, height+1, "read bad block from wal. got height %d, expected %d", block.Height, height+1)
|
||||
commitHeight := thisBlockCommit.Height
|
||||
commitHeight := thisBlockExtCommit.Height
|
||||
require.Equal(t, commitHeight, height+1, "commit does not match. got height %d, expected %d", commitHeight, height+1)
|
||||
|
||||
blocks = append(blocks, block)
|
||||
commits = append(commits, thisBlockCommit)
|
||||
return blocks, commits
|
||||
extCommits = append(extCommits, thisBlockExtCommit)
|
||||
return blocks, extCommits
|
||||
}
|
||||
|
||||
func readPieceFromWAL(msg *TimedWALMessage) interface{} {
|
||||
@@ -1162,14 +1166,16 @@ func stateAndStore(
|
||||
// mock block store
|
||||
|
||||
type mockBlockStore struct {
|
||||
cfg *config.Config
|
||||
params types.ConsensusParams
|
||||
chain []*types.Block
|
||||
commits []*types.Commit
|
||||
base int64
|
||||
t *testing.T
|
||||
cfg *config.Config
|
||||
params types.ConsensusParams
|
||||
chain []*types.Block
|
||||
extCommits []*types.ExtendedCommit
|
||||
base int64
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
var _ sm.BlockStore = &mockBlockStore{}
|
||||
|
||||
// TODO: NewBlockStore(db.NewMemDB) ...
|
||||
func newMockBlockStore(t *testing.T, cfg *config.Config, params types.ConsensusParams) *mockBlockStore {
|
||||
return &mockBlockStore{
|
||||
@@ -1198,20 +1204,26 @@ func (bs *mockBlockStore) LoadBlockMeta(height int64) *types.BlockMeta {
|
||||
}
|
||||
}
|
||||
func (bs *mockBlockStore) LoadBlockPart(height int64, index int) *types.Part { return nil }
|
||||
func (bs *mockBlockStore) SaveBlockWithExtendedCommit(block *types.Block, blockParts *types.PartSet, seenCommit *types.ExtendedCommit) {
|
||||
}
|
||||
func (bs *mockBlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) {
|
||||
}
|
||||
|
||||
func (bs *mockBlockStore) LoadBlockCommit(height int64) *types.Commit {
|
||||
return bs.commits[height-1]
|
||||
return bs.extCommits[height-1].ToCommit()
|
||||
}
|
||||
func (bs *mockBlockStore) LoadSeenCommit() *types.Commit {
|
||||
return bs.commits[len(bs.commits)-1]
|
||||
return bs.extCommits[len(bs.extCommits)-1].ToCommit()
|
||||
}
|
||||
func (bs *mockBlockStore) LoadBlockExtendedCommit(height int64) *types.ExtendedCommit {
|
||||
return bs.extCommits[height-1]
|
||||
}
|
||||
|
||||
func (bs *mockBlockStore) PruneBlocks(height int64) (uint64, error) {
|
||||
pruned := uint64(0)
|
||||
for i := int64(0); i < height-1; i++ {
|
||||
bs.chain[i] = nil
|
||||
bs.commits[i] = nil
|
||||
bs.extCommits[i] = nil
|
||||
pruned++
|
||||
}
|
||||
bs.base = height
|
||||
|
||||
+139
-51
@@ -122,9 +122,8 @@ type State struct {
|
||||
// store blocks and commits
|
||||
blockStore sm.BlockStore
|
||||
|
||||
stateStore sm.Store
|
||||
initialStatePopulated bool
|
||||
skipBootstrapping bool
|
||||
stateStore sm.Store
|
||||
skipBootstrapping bool
|
||||
|
||||
// create and execute blocks
|
||||
blockExec *sm.BlockExecutor
|
||||
@@ -248,9 +247,6 @@ func NewState(
|
||||
}
|
||||
|
||||
func (cs *State) updateStateFromStore() error {
|
||||
if cs.initialStatePopulated {
|
||||
return nil
|
||||
}
|
||||
state, err := cs.stateStore.Load()
|
||||
if err != nil {
|
||||
return fmt.Errorf("loading state: %w", err)
|
||||
@@ -259,6 +255,15 @@ func (cs *State) updateStateFromStore() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
eq, err := state.Equals(cs.state)
|
||||
if err != nil {
|
||||
return fmt.Errorf("comparing state: %w", err)
|
||||
}
|
||||
// if the new state is equivalent to the old state, we should not trigger a state update.
|
||||
if eq {
|
||||
return nil
|
||||
}
|
||||
|
||||
// We have no votes, so reconstruct LastCommit from SeenCommit.
|
||||
if state.LastBlockHeight > 0 {
|
||||
cs.reconstructLastCommit(state)
|
||||
@@ -266,7 +271,6 @@ func (cs *State) updateStateFromStore() error {
|
||||
|
||||
cs.updateToState(state)
|
||||
|
||||
cs.initialStatePopulated = true
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -692,27 +696,54 @@ func (cs *State) sendInternalMessage(ctx context.Context, mi msgInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
// Reconstruct LastCommit from SeenCommit, which we saved along with the block,
|
||||
// (which happens even before saving the state)
|
||||
// Reconstruct the LastCommit from either SeenCommit or the ExtendedCommit. SeenCommit
|
||||
// and ExtendedCommit are saved along with the block. If VoteExtensions are required
|
||||
// the method will panic on an absent ExtendedCommit or an ExtendedCommit without
|
||||
// extension data.
|
||||
func (cs *State) reconstructLastCommit(state sm.State) {
|
||||
extensionsEnabled := cs.state.ConsensusParams.ABCI.VoteExtensionsEnabled(state.LastBlockHeight)
|
||||
if !extensionsEnabled {
|
||||
votes, err := cs.votesFromSeenCommit(state)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to reconstruct last commit; %s", err))
|
||||
}
|
||||
cs.LastCommit = votes
|
||||
return
|
||||
}
|
||||
|
||||
votes, err := cs.votesFromExtendedCommit(state)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to reconstruct last extended commit; %s", err))
|
||||
}
|
||||
cs.LastCommit = votes
|
||||
}
|
||||
|
||||
func (cs *State) votesFromExtendedCommit(state sm.State) (*types.VoteSet, error) {
|
||||
ec := cs.blockStore.LoadBlockExtendedCommit(state.LastBlockHeight)
|
||||
if ec == nil {
|
||||
return nil, fmt.Errorf("extended commit for height %v not found", state.LastBlockHeight)
|
||||
}
|
||||
vs := ec.ToExtendedVoteSet(state.ChainID, state.LastValidators)
|
||||
if !vs.HasTwoThirdsMajority() {
|
||||
return nil, errors.New("extended commit does not have +2/3 majority")
|
||||
}
|
||||
return vs, nil
|
||||
}
|
||||
|
||||
func (cs *State) votesFromSeenCommit(state sm.State) (*types.VoteSet, error) {
|
||||
commit := cs.blockStore.LoadSeenCommit()
|
||||
if commit == nil || commit.Height != state.LastBlockHeight {
|
||||
commit = cs.blockStore.LoadBlockCommit(state.LastBlockHeight)
|
||||
}
|
||||
|
||||
if commit == nil {
|
||||
panic(fmt.Sprintf(
|
||||
"failed to reconstruct last commit; commit for height %v not found",
|
||||
state.LastBlockHeight,
|
||||
))
|
||||
return nil, fmt.Errorf("commit for height %v not found", state.LastBlockHeight)
|
||||
}
|
||||
|
||||
lastPrecommits := types.CommitToVoteSet(state.ChainID, commit, state.LastValidators)
|
||||
if !lastPrecommits.HasTwoThirdsMajority() {
|
||||
panic("failed to reconstruct last commit; does not have +2/3 maj")
|
||||
vs := commit.ToVoteSet(state.ChainID, state.LastValidators)
|
||||
if !vs.HasTwoThirdsMajority() {
|
||||
return nil, errors.New("commit does not have +2/3 majority")
|
||||
}
|
||||
|
||||
cs.LastCommit = lastPrecommits
|
||||
return vs, nil
|
||||
}
|
||||
|
||||
// Updates State and increments height to match that of state.
|
||||
@@ -814,7 +845,11 @@ func (cs *State) updateToState(state sm.State) {
|
||||
cs.ValidRound = -1
|
||||
cs.ValidBlock = nil
|
||||
cs.ValidBlockParts = nil
|
||||
cs.Votes = cstypes.NewHeightVoteSet(state.ChainID, height, validators)
|
||||
if state.ConsensusParams.ABCI.VoteExtensionsEnabled(height) {
|
||||
cs.Votes = cstypes.NewExtendedHeightVoteSet(state.ChainID, height, validators)
|
||||
} else {
|
||||
cs.Votes = cstypes.NewHeightVoteSet(state.ChainID, height, validators)
|
||||
}
|
||||
cs.CommitRound = -1
|
||||
cs.LastValidators = state.LastValidators
|
||||
cs.TriggeredTimeoutPrecommit = false
|
||||
@@ -1334,6 +1369,7 @@ func (cs *State) defaultDecideProposal(ctx context.Context, height int64, round
|
||||
} else if block == nil {
|
||||
return
|
||||
}
|
||||
cs.metrics.ProposalCreateCount.Add(1)
|
||||
blockParts, err = block.MakePartSet(types.BlockPartSizeBytes)
|
||||
if err != nil {
|
||||
cs.logger.Error("unable to create proposal block part set", "error", err)
|
||||
@@ -1400,16 +1436,17 @@ func (cs *State) createProposalBlock(ctx context.Context) (*types.Block, error)
|
||||
return nil, errors.New("entered createProposalBlock with privValidator being nil")
|
||||
}
|
||||
|
||||
var commit *types.Commit
|
||||
// TODO(sergio): wouldn't it be easier if CreateProposalBlock accepted cs.LastCommit directly?
|
||||
var lastExtCommit *types.ExtendedCommit
|
||||
switch {
|
||||
case cs.Height == cs.state.InitialHeight:
|
||||
// We're creating a proposal for the first block.
|
||||
// The commit is empty, but not nil.
|
||||
commit = types.NewCommit(0, 0, types.BlockID{}, nil)
|
||||
lastExtCommit = &types.ExtendedCommit{}
|
||||
|
||||
case cs.LastCommit.HasTwoThirdsMajority():
|
||||
// Make the commit from LastCommit
|
||||
commit = cs.LastCommit.MakeCommit()
|
||||
lastExtCommit = cs.LastCommit.MakeExtendedCommit()
|
||||
|
||||
default: // This shouldn't happen.
|
||||
cs.logger.Error("propose step; cannot propose anything without commit for the previous block")
|
||||
@@ -1425,7 +1462,7 @@ func (cs *State) createProposalBlock(ctx context.Context) (*types.Block, error)
|
||||
|
||||
proposerAddr := cs.privValidatorPubKey.Address()
|
||||
|
||||
ret, err := cs.blockExec.CreateProposalBlock(ctx, cs.Height, cs.state, commit, proposerAddr, cs.LastCommit.GetVotes())
|
||||
ret, err := cs.blockExec.CreateProposalBlock(ctx, cs.Height, cs.state, lastExtCommit, proposerAddr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -1531,6 +1568,7 @@ func (cs *State) defaultDoPrevote(ctx context.Context, height int64, round int32
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("ProcessProposal: %v", err))
|
||||
}
|
||||
cs.metrics.MarkProposalProcessed(isAppValid)
|
||||
|
||||
// Vote nil if the Application rejected the block
|
||||
if !isAppValid {
|
||||
@@ -1922,9 +1960,12 @@ func (cs *State) finalizeCommit(ctx context.Context, height int64) {
|
||||
if cs.blockStore.Height() < block.Height {
|
||||
// NOTE: the seenCommit is local justification to commit this block,
|
||||
// but may differ from the LastCommit included in the next block
|
||||
precommits := cs.Votes.Precommits(cs.CommitRound)
|
||||
seenCommit := precommits.MakeCommit()
|
||||
cs.blockStore.SaveBlock(block, blockParts, seenCommit)
|
||||
seenExtendedCommit := cs.Votes.Precommits(cs.CommitRound).MakeExtendedCommit()
|
||||
if cs.state.ConsensusParams.ABCI.VoteExtensionsEnabled(block.Height) {
|
||||
cs.blockStore.SaveBlockWithExtendedCommit(block, blockParts, seenExtendedCommit)
|
||||
} else {
|
||||
cs.blockStore.SaveBlock(block, blockParts, seenExtendedCommit.ToCommit())
|
||||
}
|
||||
} else {
|
||||
// Happens during replay if we already saved the block but didn't commit
|
||||
logger.Debug("calling finalizeCommit on already stored block", "height", block.Height)
|
||||
@@ -2026,7 +2067,7 @@ func (cs *State) RecordMetrics(height int64, block *types.Block) {
|
||||
|
||||
for i, val := range cs.LastValidators.Validators {
|
||||
commitSig := block.LastCommit.Signatures[i]
|
||||
if commitSig.Absent() {
|
||||
if commitSig.BlockIDFlag == types.BlockIDFlagAbsent {
|
||||
missingValidators++
|
||||
missingValidatorsPower += val.VotingPower
|
||||
}
|
||||
@@ -2036,7 +2077,7 @@ func (cs *State) RecordMetrics(height int64, block *types.Block) {
|
||||
"validator_address", val.Address.String(),
|
||||
}
|
||||
cs.metrics.ValidatorPower.With(label...).Set(float64(val.VotingPower))
|
||||
if commitSig.ForBlock() {
|
||||
if commitSig.BlockIDFlag == types.BlockIDFlagCommit {
|
||||
cs.metrics.ValidatorLastSignedHeight.With(label...).Set(float64(height))
|
||||
} else {
|
||||
cs.metrics.ValidatorMissedBlocks.With(label...).Add(float64(1))
|
||||
@@ -2297,6 +2338,10 @@ func (cs *State) addVote(
|
||||
"cs_height", cs.Height,
|
||||
)
|
||||
|
||||
if vote.Height < cs.Height || (vote.Height == cs.Height && vote.Round < cs.Round) {
|
||||
cs.metrics.MarkLateVote(vote.Type)
|
||||
}
|
||||
|
||||
// A precommit for the previous height?
|
||||
// These come in while we wait timeoutCommit
|
||||
if vote.Height+1 == cs.Height && vote.Type == tmproto.PrecommitType {
|
||||
@@ -2335,10 +2380,45 @@ func (cs *State) addVote(
|
||||
return
|
||||
}
|
||||
|
||||
// Verify VoteExtension if precommit
|
||||
if vote.Type == tmproto.PrecommitType {
|
||||
if err = cs.blockExec.VerifyVoteExtension(ctx, vote); err != nil {
|
||||
return false, err
|
||||
// Check to see if the chain is configured to extend votes.
|
||||
if cs.state.ConsensusParams.ABCI.VoteExtensionsEnabled(cs.Height) {
|
||||
// The chain is configured to extend votes, check that the vote is
|
||||
// not for a nil block and verify the extensions signature against the
|
||||
// corresponding public key.
|
||||
|
||||
var myAddr []byte
|
||||
if cs.privValidatorPubKey != nil {
|
||||
myAddr = cs.privValidatorPubKey.Address()
|
||||
}
|
||||
// Verify VoteExtension if precommit and not nil
|
||||
// https://github.com/tendermint/tendermint/issues/8487
|
||||
if vote.Type == tmproto.PrecommitType && !vote.BlockID.IsNil() &&
|
||||
!bytes.Equal(vote.ValidatorAddress, myAddr) { // Skip the VerifyVoteExtension call if the vote was issued by this validator.
|
||||
|
||||
// The core fields of the vote message were already validated in the
|
||||
// consensus reactor when the vote was received.
|
||||
// Here, we verify the signature of the vote extension included in the vote
|
||||
// message.
|
||||
_, val := cs.state.Validators.GetByIndex(vote.ValidatorIndex)
|
||||
if err := vote.VerifyExtension(cs.state.ChainID, val.PubKey); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
err := cs.blockExec.VerifyVoteExtension(ctx, vote)
|
||||
cs.metrics.MarkVoteExtensionReceived(err == nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Vote extensions are not enabled on the network.
|
||||
// strip the extension data from the vote in case any is present.
|
||||
//
|
||||
// TODO punish a peer if it sent a vote with an extension when the feature
|
||||
// is disabled on the network.
|
||||
// https://github.com/tendermint/tendermint/issues/8565
|
||||
if stripped := vote.StripExtension(); stripped {
|
||||
cs.logger.Error("vote included extension data but vote extensions are not enabled", "peer", peerID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2348,6 +2428,11 @@ func (cs *State) addVote(
|
||||
// Either duplicate, or error upon cs.Votes.AddByIndex()
|
||||
return
|
||||
}
|
||||
if vote.Round == cs.Round {
|
||||
vals := cs.state.Validators
|
||||
_, val := vals.GetByIndex(vote.ValidatorIndex)
|
||||
cs.metrics.MarkVoteReceived(vote.Type, val.VotingPower, vals.TotalVotingPower())
|
||||
}
|
||||
|
||||
if err := cs.eventBus.PublishEventVote(types.EventDataVote{Vote: vote}); err != nil {
|
||||
return added, err
|
||||
@@ -2482,18 +2567,18 @@ func (cs *State) signVote(
|
||||
|
||||
// If the signedMessageType is for precommit,
|
||||
// use our local precommit Timeout as the max wait time for getting a singed commit. The same goes for prevote.
|
||||
timeout := cs.voteTimeout(cs.Round)
|
||||
|
||||
switch msgType {
|
||||
case tmproto.PrecommitType:
|
||||
// if the signedMessage type is for a precommit, add VoteExtension
|
||||
ext, err := cs.blockExec.ExtendVote(ctx, vote)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
timeout := time.Second
|
||||
if msgType == tmproto.PrecommitType && !vote.BlockID.IsNil() {
|
||||
timeout = cs.voteTimeout(cs.Round)
|
||||
// if the signedMessage type is for a non-nil precommit, add
|
||||
// VoteExtension
|
||||
if cs.state.ConsensusParams.ABCI.VoteExtensionsEnabled(cs.Height) {
|
||||
ext, err := cs.blockExec.ExtendVote(ctx, vote)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vote.Extension = ext
|
||||
}
|
||||
vote.Extension = ext
|
||||
default:
|
||||
timeout = time.Second
|
||||
}
|
||||
|
||||
v := vote.ToProto()
|
||||
@@ -2533,14 +2618,17 @@ func (cs *State) signAddVote(
|
||||
|
||||
// TODO: pass pubKey to signVote
|
||||
vote, err := cs.signVote(ctx, msgType, hash, header)
|
||||
if err == nil {
|
||||
cs.sendInternalMessage(ctx, msgInfo{&VoteMessage{vote}, "", tmtime.Now()})
|
||||
cs.logger.Debug("signed and pushed vote", "height", cs.Height, "round", cs.Round, "vote", vote)
|
||||
return vote
|
||||
if err != nil {
|
||||
cs.logger.Error("failed signing vote", "height", cs.Height, "round", cs.Round, "vote", vote, "err", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
cs.logger.Error("failed signing vote", "height", cs.Height, "round", cs.Round, "vote", vote, "err", err)
|
||||
return nil
|
||||
if !cs.state.ConsensusParams.ABCI.VoteExtensionsEnabled(vote.Height) {
|
||||
// The signer will sign the extension, make sure to remove the data on the way out
|
||||
vote.StripExtension()
|
||||
}
|
||||
cs.sendInternalMessage(ctx, msgInfo{&VoteMessage{vote}, "", tmtime.Now()})
|
||||
cs.logger.Debug("signed and pushed vote", "height", cs.Height, "round", cs.Round, "vote", vote)
|
||||
return vote
|
||||
}
|
||||
|
||||
// updatePrivValidatorPubKey get's the private validator public key and
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/tendermint/tendermint/internal/eventbus"
|
||||
tmpubsub "github.com/tendermint/tendermint/internal/pubsub"
|
||||
tmquery "github.com/tendermint/tendermint/internal/pubsub/query"
|
||||
"github.com/tendermint/tendermint/internal/test/factory"
|
||||
tmbytes "github.com/tendermint/tendermint/libs/bytes"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
tmrand "github.com/tendermint/tendermint/libs/rand"
|
||||
@@ -1950,7 +1951,7 @@ func TestFinalizeBlockCalled(t *testing.T) {
|
||||
expectCalled bool
|
||||
}{
|
||||
{
|
||||
name: "finalze block called when block committed",
|
||||
name: "finalize block called when block committed",
|
||||
voteNil: false,
|
||||
expectCalled: true,
|
||||
},
|
||||
@@ -1970,11 +1971,15 @@ func TestFinalizeBlockCalled(t *testing.T) {
|
||||
Status: abci.ResponseProcessProposal_ACCEPT,
|
||||
}, nil)
|
||||
m.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{}, nil)
|
||||
m.On("VerifyVoteExtension", mock.Anything, mock.Anything).Return(&abci.ResponseVerifyVoteExtension{
|
||||
Status: abci.ResponseVerifyVoteExtension_ACCEPT,
|
||||
}, nil)
|
||||
// We only expect VerifyVoteExtension to be called on non-nil precommits.
|
||||
// https://github.com/tendermint/tendermint/issues/8487
|
||||
if !testCase.voteNil {
|
||||
m.On("ExtendVote", mock.Anything, mock.Anything).Return(&abci.ResponseExtendVote{}, nil)
|
||||
m.On("VerifyVoteExtension", mock.Anything, mock.Anything).Return(&abci.ResponseVerifyVoteExtension{
|
||||
Status: abci.ResponseVerifyVoteExtension_ACCEPT,
|
||||
}, nil)
|
||||
}
|
||||
m.On("FinalizeBlock", mock.Anything, mock.Anything).Return(&abci.ResponseFinalizeBlock{}, nil).Maybe()
|
||||
m.On("ExtendVote", mock.Anything, mock.Anything).Return(&abci.ResponseExtendVote{}, nil)
|
||||
m.On("Commit", mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe()
|
||||
|
||||
cs1, vss := makeState(ctx, t, makeStateArgs{config: config, application: m})
|
||||
@@ -2022,77 +2027,101 @@ func TestFinalizeBlockCalled(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestExtendVoteCalled tests that the vote extension methods are called at the
|
||||
// correct point in the consensus algorithm.
|
||||
func TestExtendVoteCalled(t *testing.T) {
|
||||
config := configSetup(t)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// TestExtendVoteCalledWhenEnabled tests that the vote extension methods are called at the
|
||||
// correct point in the consensus algorithm when vote extensions are enabled.
|
||||
func TestExtendVoteCalledWhenEnabled(t *testing.T) {
|
||||
for _, testCase := range []struct {
|
||||
name string
|
||||
enabled bool
|
||||
}{
|
||||
{
|
||||
name: "enabled",
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
name: "disabled",
|
||||
enabled: false,
|
||||
},
|
||||
} {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
config := configSetup(t)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
m := abcimocks.NewApplication(t)
|
||||
m.On("ProcessProposal", mock.Anything, mock.Anything).Return(&abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil)
|
||||
m.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{}, nil)
|
||||
m.On("ExtendVote", mock.Anything, mock.Anything).Return(&abci.ResponseExtendVote{
|
||||
VoteExtension: []byte("extension"),
|
||||
}, nil)
|
||||
m.On("VerifyVoteExtension", mock.Anything, mock.Anything).Return(&abci.ResponseVerifyVoteExtension{
|
||||
Status: abci.ResponseVerifyVoteExtension_ACCEPT,
|
||||
}, nil)
|
||||
m.On("Commit", mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe()
|
||||
m.On("FinalizeBlock", mock.Anything, mock.Anything).Return(&abci.ResponseFinalizeBlock{}, nil).Maybe()
|
||||
cs1, vss := makeState(ctx, t, makeStateArgs{config: config, application: m})
|
||||
height, round := cs1.Height, cs1.Round
|
||||
m := abcimocks.NewApplication(t)
|
||||
m.On("ProcessProposal", mock.Anything, mock.Anything).Return(&abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil)
|
||||
m.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{}, nil)
|
||||
if testCase.enabled {
|
||||
m.On("ExtendVote", mock.Anything, mock.Anything).Return(&abci.ResponseExtendVote{
|
||||
VoteExtension: []byte("extension"),
|
||||
}, nil)
|
||||
m.On("VerifyVoteExtension", mock.Anything, mock.Anything).Return(&abci.ResponseVerifyVoteExtension{
|
||||
Status: abci.ResponseVerifyVoteExtension_ACCEPT,
|
||||
}, nil)
|
||||
}
|
||||
m.On("Commit", mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe()
|
||||
m.On("FinalizeBlock", mock.Anything, mock.Anything).Return(&abci.ResponseFinalizeBlock{}, nil).Maybe()
|
||||
c := factory.ConsensusParams()
|
||||
if !testCase.enabled {
|
||||
c.ABCI.VoteExtensionsEnableHeight = 0
|
||||
}
|
||||
cs1, vss := makeState(ctx, t, makeStateArgs{config: config, application: m, consensusParams: c})
|
||||
height, round := cs1.Height, cs1.Round
|
||||
|
||||
proposalCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryCompleteProposal)
|
||||
newRoundCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryNewRound)
|
||||
pv1, err := cs1.privValidator.GetPubKey(ctx)
|
||||
require.NoError(t, err)
|
||||
addr := pv1.Address()
|
||||
voteCh := subscribeToVoter(ctx, t, cs1, addr)
|
||||
proposalCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryCompleteProposal)
|
||||
newRoundCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryNewRound)
|
||||
pv1, err := cs1.privValidator.GetPubKey(ctx)
|
||||
require.NoError(t, err)
|
||||
addr := pv1.Address()
|
||||
voteCh := subscribeToVoter(ctx, t, cs1, addr)
|
||||
|
||||
startTestRound(ctx, cs1, cs1.Height, round)
|
||||
ensureNewRound(t, newRoundCh, height, round)
|
||||
ensureNewProposal(t, proposalCh, height, round)
|
||||
startTestRound(ctx, cs1, cs1.Height, round)
|
||||
ensureNewRound(t, newRoundCh, height, round)
|
||||
ensureNewProposal(t, proposalCh, height, round)
|
||||
|
||||
m.AssertNotCalled(t, "ExtendVote", mock.Anything, mock.Anything)
|
||||
m.AssertNotCalled(t, "ExtendVote", mock.Anything, mock.Anything)
|
||||
|
||||
rs := cs1.GetRoundState()
|
||||
rs := cs1.GetRoundState()
|
||||
|
||||
blockID := types.BlockID{
|
||||
Hash: rs.ProposalBlock.Hash(),
|
||||
PartSetHeader: rs.ProposalBlockParts.Header(),
|
||||
}
|
||||
signAddVotes(ctx, t, cs1, tmproto.PrevoteType, config.ChainID(), blockID, vss[1:]...)
|
||||
ensurePrevoteMatch(t, voteCh, height, round, blockID.Hash)
|
||||
blockID := types.BlockID{
|
||||
Hash: rs.ProposalBlock.Hash(),
|
||||
PartSetHeader: rs.ProposalBlockParts.Header(),
|
||||
}
|
||||
signAddVotes(ctx, t, cs1, tmproto.PrevoteType, config.ChainID(), blockID, vss[1:]...)
|
||||
ensurePrevoteMatch(t, voteCh, height, round, blockID.Hash)
|
||||
|
||||
ensurePrecommit(t, voteCh, height, round)
|
||||
ensurePrecommit(t, voteCh, height, round)
|
||||
|
||||
m.AssertCalled(t, "ExtendVote", ctx, &abci.RequestExtendVote{
|
||||
Height: height,
|
||||
Hash: blockID.Hash,
|
||||
})
|
||||
if testCase.enabled {
|
||||
m.AssertCalled(t, "ExtendVote", ctx, &abci.RequestExtendVote{
|
||||
Height: height,
|
||||
Hash: blockID.Hash,
|
||||
})
|
||||
} else {
|
||||
m.AssertNotCalled(t, "ExtendVote", mock.Anything, mock.Anything)
|
||||
}
|
||||
|
||||
m.AssertCalled(t, "VerifyVoteExtension", ctx, &abci.RequestVerifyVoteExtension{
|
||||
Hash: blockID.Hash,
|
||||
ValidatorAddress: addr,
|
||||
Height: height,
|
||||
VoteExtension: []byte("extension"),
|
||||
})
|
||||
signAddVotes(ctx, t, cs1, tmproto.PrecommitType, config.ChainID(), blockID, vss[1:]...)
|
||||
ensureNewRound(t, newRoundCh, height+1, 0)
|
||||
m.AssertExpectations(t)
|
||||
signAddVotes(ctx, t, cs1, tmproto.PrecommitType, config.ChainID(), blockID, vss[1:]...)
|
||||
ensureNewRound(t, newRoundCh, height+1, 0)
|
||||
m.AssertExpectations(t)
|
||||
|
||||
// Only 3 of the vote extensions are seen, as consensus proceeds as soon as the +2/3 threshold
|
||||
// is observed by the consensus engine.
|
||||
for _, pv := range vss[:3] {
|
||||
pv, err := pv.GetPubKey(ctx)
|
||||
require.NoError(t, err)
|
||||
addr := pv.Address()
|
||||
m.AssertCalled(t, "VerifyVoteExtension", ctx, &abci.RequestVerifyVoteExtension{
|
||||
Hash: blockID.Hash,
|
||||
ValidatorAddress: addr,
|
||||
Height: height,
|
||||
VoteExtension: []byte("extension"),
|
||||
// Only 3 of the vote extensions are seen, as consensus proceeds as soon as the +2/3 threshold
|
||||
// is observed by the consensus engine.
|
||||
for _, pv := range vss[1:3] {
|
||||
pv, err := pv.GetPubKey(ctx)
|
||||
require.NoError(t, err)
|
||||
addr := pv.Address()
|
||||
if testCase.enabled {
|
||||
m.AssertCalled(t, "VerifyVoteExtension", ctx, &abci.RequestVerifyVoteExtension{
|
||||
Hash: blockID.Hash,
|
||||
ValidatorAddress: addr,
|
||||
Height: height,
|
||||
VoteExtension: []byte("extension"),
|
||||
})
|
||||
} else {
|
||||
m.AssertNotCalled(t, "VerifyVoteExtension", mock.Anything, mock.Anything)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2117,6 +2146,7 @@ func TestVerifyVoteExtensionNotCalledOnAbsentPrecommit(t *testing.T) {
|
||||
m.On("FinalizeBlock", mock.Anything, mock.Anything).Return(&abci.ResponseFinalizeBlock{}, nil).Maybe()
|
||||
cs1, vss := makeState(ctx, t, makeStateArgs{config: config, application: m})
|
||||
height, round := cs1.Height, cs1.Round
|
||||
cs1.state.ConsensusParams.ABCI.VoteExtensionsEnableHeight = cs1.Height
|
||||
|
||||
proposalCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryCompleteProposal)
|
||||
newRoundCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryNewRound)
|
||||
@@ -2134,7 +2164,7 @@ func TestVerifyVoteExtensionNotCalledOnAbsentPrecommit(t *testing.T) {
|
||||
Hash: rs.ProposalBlock.Hash(),
|
||||
PartSetHeader: rs.ProposalBlockParts.Header(),
|
||||
}
|
||||
signAddVotes(ctx, t, cs1, tmproto.PrevoteType, config.ChainID(), blockID, vss[2:]...)
|
||||
signAddVotes(ctx, t, cs1, tmproto.PrevoteType, config.ChainID(), blockID, vss...)
|
||||
ensurePrevoteMatch(t, voteCh, height, round, blockID.Hash)
|
||||
|
||||
ensurePrecommit(t, voteCh, height, round)
|
||||
@@ -2144,13 +2174,6 @@ func TestVerifyVoteExtensionNotCalledOnAbsentPrecommit(t *testing.T) {
|
||||
Hash: blockID.Hash,
|
||||
})
|
||||
|
||||
m.AssertCalled(t, "VerifyVoteExtension", mock.Anything, &abci.RequestVerifyVoteExtension{
|
||||
Hash: blockID.Hash,
|
||||
ValidatorAddress: addr,
|
||||
Height: height,
|
||||
VoteExtension: []byte("extension"),
|
||||
})
|
||||
|
||||
m.On("Commit", mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe()
|
||||
signAddVotes(ctx, t, cs1, tmproto.PrecommitType, config.ChainID(), blockID, vss[2:]...)
|
||||
ensureNewRound(t, newRoundCh, height+1, 0)
|
||||
@@ -2262,6 +2285,134 @@ func TestPrepareProposalReceivesVoteExtensions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestVoteExtensionEnableHeight tests that 'ExtensionRequireHeight' correctly
|
||||
// enforces that vote extensions be present in consensus for heights greater than
|
||||
// or equal to the configured value.
|
||||
func TestVoteExtensionEnableHeight(t *testing.T) {
|
||||
for _, testCase := range []struct {
|
||||
name string
|
||||
enableHeight int64
|
||||
hasExtension bool
|
||||
expectExtendCalled bool
|
||||
expectVerifyCalled bool
|
||||
expectSuccessfulRound bool
|
||||
}{
|
||||
{
|
||||
name: "extension present but not enabled",
|
||||
hasExtension: true,
|
||||
enableHeight: 0,
|
||||
expectExtendCalled: false,
|
||||
expectVerifyCalled: false,
|
||||
expectSuccessfulRound: true,
|
||||
},
|
||||
{
|
||||
name: "extension absent but not required",
|
||||
hasExtension: false,
|
||||
enableHeight: 0,
|
||||
expectExtendCalled: false,
|
||||
expectVerifyCalled: false,
|
||||
expectSuccessfulRound: true,
|
||||
},
|
||||
{
|
||||
name: "extension present and required",
|
||||
hasExtension: true,
|
||||
enableHeight: 1,
|
||||
expectExtendCalled: true,
|
||||
expectVerifyCalled: true,
|
||||
expectSuccessfulRound: true,
|
||||
},
|
||||
{
|
||||
name: "extension absent but required",
|
||||
hasExtension: false,
|
||||
enableHeight: 1,
|
||||
expectExtendCalled: true,
|
||||
expectVerifyCalled: false,
|
||||
expectSuccessfulRound: false,
|
||||
},
|
||||
{
|
||||
name: "extension absent but required in future height",
|
||||
hasExtension: false,
|
||||
enableHeight: 2,
|
||||
expectExtendCalled: false,
|
||||
expectVerifyCalled: false,
|
||||
expectSuccessfulRound: true,
|
||||
},
|
||||
} {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
config := configSetup(t)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
numValidators := 3
|
||||
m := abcimocks.NewApplication(t)
|
||||
m.On("ProcessProposal", mock.Anything, mock.Anything).Return(&abci.ResponseProcessProposal{
|
||||
Status: abci.ResponseProcessProposal_ACCEPT,
|
||||
}, nil)
|
||||
m.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{}, nil)
|
||||
if testCase.expectExtendCalled {
|
||||
m.On("ExtendVote", mock.Anything, mock.Anything).Return(&abci.ResponseExtendVote{}, nil)
|
||||
}
|
||||
if testCase.expectVerifyCalled {
|
||||
m.On("VerifyVoteExtension", mock.Anything, mock.Anything).Return(&abci.ResponseVerifyVoteExtension{
|
||||
Status: abci.ResponseVerifyVoteExtension_ACCEPT,
|
||||
}, nil).Times(numValidators - 1)
|
||||
}
|
||||
m.On("FinalizeBlock", mock.Anything, mock.Anything).Return(&abci.ResponseFinalizeBlock{}, nil).Maybe()
|
||||
m.On("Commit", mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe()
|
||||
c := factory.ConsensusParams()
|
||||
c.ABCI.VoteExtensionsEnableHeight = testCase.enableHeight
|
||||
cs1, vss := makeState(ctx, t, makeStateArgs{config: config, application: m, validators: numValidators, consensusParams: c})
|
||||
cs1.state.ConsensusParams.ABCI.VoteExtensionsEnableHeight = testCase.enableHeight
|
||||
height, round := cs1.Height, cs1.Round
|
||||
|
||||
timeoutCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryTimeoutPropose)
|
||||
proposalCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryCompleteProposal)
|
||||
newRoundCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryNewRound)
|
||||
pv1, err := cs1.privValidator.GetPubKey(ctx)
|
||||
require.NoError(t, err)
|
||||
addr := pv1.Address()
|
||||
voteCh := subscribeToVoter(ctx, t, cs1, addr)
|
||||
|
||||
startTestRound(ctx, cs1, cs1.Height, round)
|
||||
ensureNewRound(t, newRoundCh, height, round)
|
||||
ensureNewProposal(t, proposalCh, height, round)
|
||||
rs := cs1.GetRoundState()
|
||||
|
||||
blockID := types.BlockID{
|
||||
Hash: rs.ProposalBlock.Hash(),
|
||||
PartSetHeader: rs.ProposalBlockParts.Header(),
|
||||
}
|
||||
|
||||
// sign all of the votes
|
||||
signAddVotes(ctx, t, cs1, tmproto.PrevoteType, config.ChainID(), blockID, vss[1:]...)
|
||||
ensurePrevoteMatch(t, voteCh, height, round, rs.ProposalBlock.Hash())
|
||||
|
||||
var ext []byte
|
||||
if testCase.hasExtension {
|
||||
ext = []byte("extension")
|
||||
}
|
||||
|
||||
for _, vs := range vss[1:] {
|
||||
vote, err := vs.signVote(ctx, tmproto.PrecommitType, config.ChainID(), blockID, ext)
|
||||
if !testCase.hasExtension {
|
||||
vote.ExtensionSignature = nil
|
||||
}
|
||||
require.NoError(t, err)
|
||||
addVotes(cs1, vote)
|
||||
}
|
||||
if testCase.expectSuccessfulRound {
|
||||
ensurePrecommit(t, voteCh, height, round)
|
||||
height++
|
||||
ensureNewRound(t, newRoundCh, height, round)
|
||||
} else {
|
||||
ensureNoNewTimeout(t, timeoutCh, cs1.state.ConsensusParams.Timeout.VoteTimeout(round).Nanoseconds())
|
||||
}
|
||||
|
||||
m.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 4 vals, 3 Nil Precommits at P0
|
||||
// What we want:
|
||||
// P0 waits for timeoutPrecommit before starting next round
|
||||
|
||||
@@ -38,9 +38,10 @@ We let each peer provide us with up to 2 unexpected "catchup" rounds.
|
||||
One for their LastCommit round, and another for the official commit round.
|
||||
*/
|
||||
type HeightVoteSet struct {
|
||||
chainID string
|
||||
height int64
|
||||
valSet *types.ValidatorSet
|
||||
chainID string
|
||||
height int64
|
||||
valSet *types.ValidatorSet
|
||||
extensionsEnabled bool
|
||||
|
||||
mtx sync.Mutex
|
||||
round int32 // max tracked round
|
||||
@@ -50,7 +51,17 @@ type HeightVoteSet struct {
|
||||
|
||||
func NewHeightVoteSet(chainID string, height int64, valSet *types.ValidatorSet) *HeightVoteSet {
|
||||
hvs := &HeightVoteSet{
|
||||
chainID: chainID,
|
||||
chainID: chainID,
|
||||
extensionsEnabled: false,
|
||||
}
|
||||
hvs.Reset(height, valSet)
|
||||
return hvs
|
||||
}
|
||||
|
||||
func NewExtendedHeightVoteSet(chainID string, height int64, valSet *types.ValidatorSet) *HeightVoteSet {
|
||||
hvs := &HeightVoteSet{
|
||||
chainID: chainID,
|
||||
extensionsEnabled: true,
|
||||
}
|
||||
hvs.Reset(height, valSet)
|
||||
return hvs
|
||||
@@ -108,7 +119,12 @@ func (hvs *HeightVoteSet) addRound(round int32) {
|
||||
}
|
||||
// log.Debug("addRound(round)", "round", round)
|
||||
prevotes := types.NewVoteSet(hvs.chainID, hvs.height, round, tmproto.PrevoteType, hvs.valSet)
|
||||
precommits := types.NewVoteSet(hvs.chainID, hvs.height, round, tmproto.PrecommitType, hvs.valSet)
|
||||
var precommits *types.VoteSet
|
||||
if hvs.extensionsEnabled {
|
||||
precommits = types.NewExtendedVoteSet(hvs.chainID, hvs.height, round, tmproto.PrecommitType, hvs.valSet)
|
||||
} else {
|
||||
precommits = types.NewVoteSet(hvs.chainID, hvs.height, round, tmproto.PrecommitType, hvs.valSet)
|
||||
}
|
||||
hvs.roundVoteSets[round] = RoundVoteSet{
|
||||
Prevotes: prevotes,
|
||||
Precommits: precommits,
|
||||
|
||||
@@ -27,7 +27,7 @@ func TestPeerCatchupRounds(t *testing.T) {
|
||||
valSet, privVals := factory.ValidatorSet(ctx, t, 10, 1)
|
||||
|
||||
chainID := cfg.ChainID()
|
||||
hvs := NewHeightVoteSet(chainID, 1, valSet)
|
||||
hvs := NewExtendedHeightVoteSet(chainID, 1, valSet)
|
||||
|
||||
vote999_0 := makeVoteHR(ctx, t, 1, 0, 999, privVals, chainID)
|
||||
added, err := hvs.AddVote(vote999_0, "peer1")
|
||||
|
||||
@@ -24,9 +24,9 @@ import (
|
||||
// any number of readers.
|
||||
type Log struct {
|
||||
// These values do not change after construction.
|
||||
windowSize time.Duration
|
||||
maxItems int
|
||||
numItemsGauge gauge
|
||||
windowSize time.Duration
|
||||
maxItems int
|
||||
metrics *Metrics
|
||||
|
||||
// Protects access to the fields below. Lock to modify the values of these
|
||||
// fields, or to read or snapshot the values.
|
||||
@@ -45,14 +45,14 @@ func New(opts LogSettings) (*Log, error) {
|
||||
return nil, errors.New("window size must be positive")
|
||||
}
|
||||
lg := &Log{
|
||||
windowSize: opts.WindowSize,
|
||||
maxItems: opts.MaxItems,
|
||||
numItemsGauge: discard{},
|
||||
ready: make(chan struct{}),
|
||||
source: opts.Source,
|
||||
windowSize: opts.WindowSize,
|
||||
maxItems: opts.MaxItems,
|
||||
metrics: NopMetrics(),
|
||||
ready: make(chan struct{}),
|
||||
source: opts.Source,
|
||||
}
|
||||
if opts.Metrics != nil {
|
||||
lg.numItemsGauge = opts.Metrics.numItemsGauge
|
||||
lg.metrics = opts.Metrics
|
||||
}
|
||||
return lg, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// Code generated by metricsgen. DO NOT EDIT.
|
||||
|
||||
package eventlog
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/metrics/discard"
|
||||
prometheus "github.com/go-kit/kit/metrics/prometheus"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics {
|
||||
labels := []string{}
|
||||
for i := 0; i < len(labelsAndValues); i += 2 {
|
||||
labels = append(labels, labelsAndValues[i])
|
||||
}
|
||||
return &Metrics{
|
||||
numItems: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "num_items",
|
||||
Help: "Number of items currently resident in the event log.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
}
|
||||
}
|
||||
|
||||
func NopMetrics() *Metrics {
|
||||
return &Metrics{
|
||||
numItems: discard.NewGauge(),
|
||||
}
|
||||
}
|
||||
@@ -1,39 +1,14 @@
|
||||
package eventlog
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/metrics/prometheus"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
import "github.com/go-kit/kit/metrics"
|
||||
|
||||
// gauge is the subset of the Prometheus gauge interface used here.
|
||||
type gauge interface {
|
||||
Set(float64)
|
||||
}
|
||||
const MetricsSubsystem = "eventlog"
|
||||
|
||||
//go:generate go run ../../scripts/metricsgen -struct=Metrics
|
||||
|
||||
// Metrics define the metrics exported by the eventlog package.
|
||||
type Metrics struct {
|
||||
numItemsGauge gauge
|
||||
}
|
||||
|
||||
// discard is a no-op implementation of the gauge interface.
|
||||
type discard struct{}
|
||||
|
||||
func (discard) Set(float64) {}
|
||||
|
||||
const eventlogSubsystem = "eventlog"
|
||||
|
||||
// PrometheusMetrics returns a collection of eventlog metrics for Prometheus.
|
||||
func PrometheusMetrics(ns string, fields ...string) *Metrics {
|
||||
var labels []string
|
||||
for i := 0; i < len(fields); i += 2 {
|
||||
labels = append(labels, fields[i])
|
||||
}
|
||||
return &Metrics{
|
||||
numItemsGauge: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: ns,
|
||||
Subsystem: eventlogSubsystem,
|
||||
Name: "num_items",
|
||||
Help: "Number of items currently resident in the event log.",
|
||||
}, labels).With(fields...),
|
||||
}
|
||||
// Number of items currently resident in the event log.
|
||||
numItems metrics.Gauge
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ func (lg *Log) checkPrune(head *logEntry, size int, age time.Duration) error {
|
||||
const windowSlop = 30 * time.Second
|
||||
|
||||
if age < (lg.windowSize+windowSlop) && (lg.maxItems <= 0 || size <= lg.maxItems) {
|
||||
lg.numItemsGauge.Set(float64(lg.numItems))
|
||||
lg.metrics.numItems.Set(float64(lg.numItems))
|
||||
return nil // no pruning is needed
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ func (lg *Log) checkPrune(head *logEntry, size int, age time.Duration) error {
|
||||
lg.mu.Lock()
|
||||
defer lg.mu.Unlock()
|
||||
lg.numItems = newState.size
|
||||
lg.numItemsGauge.Set(float64(newState.size))
|
||||
lg.metrics.numItems.Set(float64(newState.size))
|
||||
lg.oldestCursor = newState.oldest
|
||||
lg.head = newState.head
|
||||
return err
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// Code generated by metricsgen. DO NOT EDIT.
|
||||
|
||||
package evidence
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/metrics/discard"
|
||||
prometheus "github.com/go-kit/kit/metrics/prometheus"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics {
|
||||
labels := []string{}
|
||||
for i := 0; i < len(labelsAndValues); i += 2 {
|
||||
labels = append(labels, labelsAndValues[i])
|
||||
}
|
||||
return &Metrics{
|
||||
NumEvidence: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "num_evidence",
|
||||
Help: "Number of pending evidence in the evidence pool.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
}
|
||||
}
|
||||
|
||||
func NopMetrics() *Metrics {
|
||||
return &Metrics{
|
||||
NumEvidence: discard.NewGauge(),
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,6 @@ package evidence
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/metrics"
|
||||
"github.com/go-kit/kit/metrics/discard"
|
||||
"github.com/go-kit/kit/metrics/prometheus"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -13,35 +10,11 @@ const (
|
||||
MetricsSubsystem = "evidence_pool"
|
||||
)
|
||||
|
||||
//go:generate go run ../../scripts/metricsgen -struct=Metrics
|
||||
|
||||
// Metrics contains metrics exposed by this package.
|
||||
// see MetricsProvider for descriptions.
|
||||
type Metrics struct {
|
||||
// Number of evidence in the evidence pool
|
||||
// Number of pending evidence in the evidence pool.
|
||||
NumEvidence metrics.Gauge
|
||||
}
|
||||
|
||||
// PrometheusMetrics returns Metrics build using Prometheus client library.
|
||||
// Optionally, labels can be provided along with their values ("foo",
|
||||
// "fooValue").
|
||||
func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics {
|
||||
labels := []string{}
|
||||
for i := 0; i < len(labelsAndValues); i += 2 {
|
||||
labels = append(labels, labelsAndValues[i])
|
||||
}
|
||||
return &Metrics{
|
||||
|
||||
NumEvidence: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "num_evidence",
|
||||
Help: "Number of pending evidence in evidence pool.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
}
|
||||
}
|
||||
|
||||
// NopMetrics returns no-op Metrics.
|
||||
func NopMetrics() *Metrics {
|
||||
return &Metrics{
|
||||
NumEvidence: discard.NewGauge(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,8 +249,8 @@ func TestEvidencePoolUpdate(t *testing.T) {
|
||||
evidenceChainID,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
lastCommit := makeCommit(height, val.PrivKey.PubKey().Address())
|
||||
block := types.MakeBlock(height+1, []types.Tx{}, lastCommit, []types.Evidence{ev})
|
||||
lastExtCommit := makeExtCommit(height, val.PrivKey.PubKey().Address())
|
||||
block := types.MakeBlock(height+1, []types.Tx{}, lastExtCommit.ToCommit(), []types.Evidence{ev})
|
||||
|
||||
// update state (partially)
|
||||
state.LastBlockHeight = height + 1
|
||||
@@ -568,8 +568,8 @@ func initializeBlockStore(db dbm.DB, state sm.State, valAddr []byte) (*store.Blo
|
||||
blockStore := store.NewBlockStore(db)
|
||||
|
||||
for i := int64(1); i <= state.LastBlockHeight; i++ {
|
||||
lastCommit := makeCommit(i-1, valAddr)
|
||||
block := sf.MakeBlock(state, i, lastCommit)
|
||||
lastCommit := makeExtCommit(i-1, valAddr)
|
||||
block := sf.MakeBlock(state, i, lastCommit.ToCommit())
|
||||
|
||||
block.Header.Time = defaultEvidenceTime.Add(time.Duration(i) * time.Minute)
|
||||
block.Header.Version = version.Consensus{Block: version.BlockProtocol, App: 1}
|
||||
@@ -579,22 +579,26 @@ func initializeBlockStore(db dbm.DB, state sm.State, valAddr []byte) (*store.Blo
|
||||
return nil, err
|
||||
}
|
||||
|
||||
seenCommit := makeCommit(i, valAddr)
|
||||
blockStore.SaveBlock(block, partSet, seenCommit)
|
||||
seenCommit := makeExtCommit(i, valAddr)
|
||||
blockStore.SaveBlockWithExtendedCommit(block, partSet, seenCommit)
|
||||
}
|
||||
|
||||
return blockStore, nil
|
||||
}
|
||||
|
||||
func makeCommit(height int64, valAddr []byte) *types.Commit {
|
||||
commitSigs := []types.CommitSig{{
|
||||
BlockIDFlag: types.BlockIDFlagCommit,
|
||||
ValidatorAddress: valAddr,
|
||||
Timestamp: defaultEvidenceTime,
|
||||
Signature: []byte("Signature"),
|
||||
}}
|
||||
|
||||
return types.NewCommit(height, 0, types.BlockID{}, commitSigs)
|
||||
func makeExtCommit(height int64, valAddr []byte) *types.ExtendedCommit {
|
||||
return &types.ExtendedCommit{
|
||||
Height: height,
|
||||
ExtendedSignatures: []types.ExtendedCommitSig{{
|
||||
CommitSig: types.CommitSig{
|
||||
BlockIDFlag: types.BlockIDFlagCommit,
|
||||
ValidatorAddress: valAddr,
|
||||
Timestamp: defaultEvidenceTime,
|
||||
Signature: []byte("Signature"),
|
||||
},
|
||||
ExtensionSignature: []byte("Extended Signature"),
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
func defaultTestPool(ctx context.Context, t *testing.T, height int64) (*evidence.Pool, types.MockPV, *eventbus.EventBus) {
|
||||
|
||||
@@ -233,9 +233,10 @@ func TestVerifyLightClientAttack_Equivocation(t *testing.T) {
|
||||
// we are simulating a duplicate vote attack where all the validators in the conflictingVals set
|
||||
// except the last validator vote twice
|
||||
blockID := factory.MakeBlockIDWithHash(conflictingHeader.Hash())
|
||||
voteSet := types.NewVoteSet(evidenceChainID, 10, 1, tmproto.SignedMsgType(2), conflictingVals)
|
||||
commit, err := factory.MakeCommit(ctx, blockID, 10, 1, voteSet, conflictingPrivVals[:4], defaultEvidenceTime)
|
||||
voteSet := types.NewExtendedVoteSet(evidenceChainID, 10, 1, tmproto.SignedMsgType(2), conflictingVals)
|
||||
extCommit, err := factory.MakeExtendedCommit(ctx, blockID, 10, 1, voteSet, conflictingPrivVals[:4], defaultEvidenceTime)
|
||||
require.NoError(t, err)
|
||||
commit := extCommit.ToCommit()
|
||||
|
||||
ev := &types.LightClientAttackEvidence{
|
||||
ConflictingBlock: &types.LightBlock{
|
||||
@@ -252,10 +253,11 @@ func TestVerifyLightClientAttack_Equivocation(t *testing.T) {
|
||||
}
|
||||
|
||||
trustedBlockID := makeBlockID(trustedHeader.Hash(), 1000, []byte("partshash"))
|
||||
trustedVoteSet := types.NewVoteSet(evidenceChainID, 10, 1, tmproto.SignedMsgType(2), conflictingVals)
|
||||
trustedCommit, err := factory.MakeCommit(ctx, trustedBlockID, 10, 1,
|
||||
trustedVoteSet := types.NewExtendedVoteSet(evidenceChainID, 10, 1, tmproto.SignedMsgType(2), conflictingVals)
|
||||
trustedExtCommit, err := factory.MakeExtendedCommit(ctx, trustedBlockID, 10, 1,
|
||||
trustedVoteSet, conflictingPrivVals, defaultEvidenceTime)
|
||||
require.NoError(t, err)
|
||||
trustedCommit := trustedExtCommit.ToCommit()
|
||||
|
||||
trustedSignedHeader := &types.SignedHeader{
|
||||
Header: trustedHeader,
|
||||
@@ -334,9 +336,10 @@ func TestVerifyLightClientAttack_Amnesia(t *testing.T) {
|
||||
// we are simulating an amnesia attack where all the validators in the conflictingVals set
|
||||
// except the last validator vote twice. However this time the commits are of different rounds.
|
||||
blockID := makeBlockID(conflictingHeader.Hash(), 1000, []byte("partshash"))
|
||||
voteSet := types.NewVoteSet(evidenceChainID, height, 0, tmproto.SignedMsgType(2), conflictingVals)
|
||||
commit, err := factory.MakeCommit(ctx, blockID, height, 0, voteSet, conflictingPrivVals, defaultEvidenceTime)
|
||||
voteSet := types.NewExtendedVoteSet(evidenceChainID, height, 0, tmproto.SignedMsgType(2), conflictingVals)
|
||||
extCommit, err := factory.MakeExtendedCommit(ctx, blockID, height, 0, voteSet, conflictingPrivVals, defaultEvidenceTime)
|
||||
require.NoError(t, err)
|
||||
commit := extCommit.ToCommit()
|
||||
|
||||
ev := &types.LightClientAttackEvidence{
|
||||
ConflictingBlock: &types.LightBlock{
|
||||
@@ -353,10 +356,11 @@ func TestVerifyLightClientAttack_Amnesia(t *testing.T) {
|
||||
}
|
||||
|
||||
trustedBlockID := makeBlockID(trustedHeader.Hash(), 1000, []byte("partshash"))
|
||||
trustedVoteSet := types.NewVoteSet(evidenceChainID, height, 1, tmproto.SignedMsgType(2), conflictingVals)
|
||||
trustedCommit, err := factory.MakeCommit(ctx, trustedBlockID, height, 1,
|
||||
trustedVoteSet := types.NewExtendedVoteSet(evidenceChainID, height, 1, tmproto.SignedMsgType(2), conflictingVals)
|
||||
trustedExtCommit, err := factory.MakeExtendedCommit(ctx, trustedBlockID, height, 1,
|
||||
trustedVoteSet, conflictingPrivVals, defaultEvidenceTime)
|
||||
require.NoError(t, err)
|
||||
trustedCommit := trustedExtCommit.ToCommit()
|
||||
|
||||
trustedSignedHeader := &types.SignedHeader{
|
||||
Header: trustedHeader,
|
||||
@@ -549,9 +553,10 @@ func makeLunaticEvidence(
|
||||
})
|
||||
|
||||
blockID := factory.MakeBlockIDWithHash(conflictingHeader.Hash())
|
||||
voteSet := types.NewVoteSet(evidenceChainID, height, 1, tmproto.SignedMsgType(2), conflictingVals)
|
||||
commit, err := factory.MakeCommit(ctx, blockID, height, 1, voteSet, conflictingPrivVals, defaultEvidenceTime)
|
||||
voteSet := types.NewExtendedVoteSet(evidenceChainID, height, 1, tmproto.SignedMsgType(2), conflictingVals)
|
||||
extCommit, err := factory.MakeExtendedCommit(ctx, blockID, height, 1, voteSet, conflictingPrivVals, defaultEvidenceTime)
|
||||
require.NoError(t, err)
|
||||
commit := extCommit.ToCommit()
|
||||
|
||||
ev = &types.LightClientAttackEvidence{
|
||||
ConflictingBlock: &types.LightBlock{
|
||||
@@ -577,9 +582,10 @@ func makeLunaticEvidence(
|
||||
}
|
||||
trustedBlockID := factory.MakeBlockIDWithHash(trustedHeader.Hash())
|
||||
trustedVals, privVals := factory.ValidatorSet(ctx, t, totalVals, defaultVotingPower)
|
||||
trustedVoteSet := types.NewVoteSet(evidenceChainID, height, 1, tmproto.SignedMsgType(2), trustedVals)
|
||||
trustedCommit, err := factory.MakeCommit(ctx, trustedBlockID, height, 1, trustedVoteSet, privVals, defaultEvidenceTime)
|
||||
trustedVoteSet := types.NewExtendedVoteSet(evidenceChainID, height, 1, tmproto.SignedMsgType(2), trustedVals)
|
||||
trustedExtCommit, err := factory.MakeExtendedCommit(ctx, trustedBlockID, height, 1, trustedVoteSet, privVals, defaultEvidenceTime)
|
||||
require.NoError(t, err)
|
||||
trustedCommit := trustedExtCommit.ToCommit()
|
||||
|
||||
trusted = &types.LightBlock{
|
||||
SignedHeader: &types.SignedHeader{
|
||||
|
||||
@@ -125,7 +125,8 @@ func serverRPCConfig(r *config.RPCConfig) *server.Config {
|
||||
// If necessary adjust global WriteTimeout to ensure it's greater than
|
||||
// TimeoutBroadcastTxCommit.
|
||||
// See https://github.com/tendermint/tendermint/issues/3435
|
||||
if cfg.WriteTimeout <= r.TimeoutBroadcastTxCommit {
|
||||
// Note we don't need to adjust anything if the timeout is already unlimited.
|
||||
if cfg.WriteTimeout > 0 && cfg.WriteTimeout <= r.TimeoutBroadcastTxCommit {
|
||||
cfg.WriteTimeout = r.TimeoutBroadcastTxCommit + 1*time.Second
|
||||
}
|
||||
return cfg
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
// Code generated by metricsgen. DO NOT EDIT.
|
||||
|
||||
package mempool
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/metrics/discard"
|
||||
prometheus "github.com/go-kit/kit/metrics/prometheus"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics {
|
||||
labels := []string{}
|
||||
for i := 0; i < len(labelsAndValues); i += 2 {
|
||||
labels = append(labels, labelsAndValues[i])
|
||||
}
|
||||
return &Metrics{
|
||||
Size: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "size",
|
||||
Help: "Number of uncommitted transactions in the mempool.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
TxSizeBytes: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "tx_size_bytes",
|
||||
Help: "Histogram of transaction sizes in bytes.",
|
||||
|
||||
Buckets: stdprometheus.ExponentialBuckets(1, 3, 7),
|
||||
}, labels).With(labelsAndValues...),
|
||||
FailedTxs: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "failed_txs",
|
||||
Help: "Number of failed transactions.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
RejectedTxs: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "rejected_txs",
|
||||
Help: "Number of rejected transactions.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
EvictedTxs: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "evicted_txs",
|
||||
Help: "Number of evicted transactions.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
RecheckTimes: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "recheck_times",
|
||||
Help: "Number of times transactions are rechecked in the mempool.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
}
|
||||
}
|
||||
|
||||
func NopMetrics() *Metrics {
|
||||
return &Metrics{
|
||||
Size: discard.NewGauge(),
|
||||
TxSizeBytes: discard.NewHistogram(),
|
||||
FailedTxs: discard.NewCounter(),
|
||||
RejectedTxs: discard.NewCounter(),
|
||||
EvictedTxs: discard.NewCounter(),
|
||||
RecheckTimes: discard.NewCounter(),
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,6 @@ package mempool
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/metrics"
|
||||
"github.com/go-kit/kit/metrics/discard"
|
||||
"github.com/go-kit/kit/metrics/prometheus"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -13,14 +10,16 @@ const (
|
||||
MetricsSubsystem = "mempool"
|
||||
)
|
||||
|
||||
//go:generate go run ../../scripts/metricsgen -struct=Metrics
|
||||
|
||||
// Metrics contains metrics exposed by this package.
|
||||
// see MetricsProvider for descriptions.
|
||||
type Metrics struct {
|
||||
// Size of the mempool.
|
||||
// Number of uncommitted transactions in the mempool.
|
||||
Size metrics.Gauge
|
||||
|
||||
// Histogram of transaction sizes, in bytes.
|
||||
TxSizeBytes metrics.Histogram
|
||||
// Histogram of transaction sizes in bytes.
|
||||
TxSizeBytes metrics.Histogram `metrics_buckettype:"exp" metrics_bucketsizes:"1,3,7"`
|
||||
|
||||
// Number of failed transactions.
|
||||
FailedTxs metrics.Counter
|
||||
@@ -29,80 +28,16 @@ type Metrics struct {
|
||||
// transactions that passed CheckTx but failed to make it into the mempool
|
||||
// due to resource limits, e.g. mempool is full and no lower priority
|
||||
// transactions exist in the mempool.
|
||||
//metrics:Number of rejected transactions.
|
||||
RejectedTxs metrics.Counter
|
||||
|
||||
// EvictedTxs defines the number of evicted transactions. These are valid
|
||||
// transactions that passed CheckTx and existed in the mempool but were later
|
||||
// evicted to make room for higher priority valid transactions that passed
|
||||
// CheckTx.
|
||||
//metrics:Number of evicted transactions.
|
||||
EvictedTxs metrics.Counter
|
||||
|
||||
// Number of times transactions are rechecked in the mempool.
|
||||
RecheckTimes metrics.Counter
|
||||
}
|
||||
|
||||
// PrometheusMetrics returns Metrics build using Prometheus client library.
|
||||
// Optionally, labels can be provided along with their values ("foo",
|
||||
// "fooValue").
|
||||
func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics {
|
||||
labels := []string{}
|
||||
for i := 0; i < len(labelsAndValues); i += 2 {
|
||||
labels = append(labels, labelsAndValues[i])
|
||||
}
|
||||
return &Metrics{
|
||||
Size: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "size",
|
||||
Help: "Size of the mempool (number of uncommitted transactions).",
|
||||
}, labels).With(labelsAndValues...),
|
||||
|
||||
TxSizeBytes: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "tx_size_bytes",
|
||||
Help: "Transaction sizes in bytes.",
|
||||
Buckets: stdprometheus.ExponentialBuckets(1, 3, 17),
|
||||
}, labels).With(labelsAndValues...),
|
||||
|
||||
FailedTxs: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "failed_txs",
|
||||
Help: "Number of failed transactions.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
|
||||
RejectedTxs: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "rejected_txs",
|
||||
Help: "Number of rejected transactions.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
|
||||
EvictedTxs: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "evicted_txs",
|
||||
Help: "Number of evicted transactions.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
|
||||
RecheckTimes: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "recheck_times",
|
||||
Help: "Number of times transactions are rechecked in the mempool.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
}
|
||||
}
|
||||
|
||||
// NopMetrics returns no-op Metrics.
|
||||
func NopMetrics() *Metrics {
|
||||
return &Metrics{
|
||||
Size: discard.NewGauge(),
|
||||
TxSizeBytes: discard.NewHistogram(),
|
||||
FailedTxs: discard.NewCounter(),
|
||||
RejectedTxs: discard.NewCounter(),
|
||||
EvictedTxs: discard.NewCounter(),
|
||||
RecheckTimes: discard.NewCounter(),
|
||||
}
|
||||
}
|
||||
|
||||
+19
-30
@@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/config"
|
||||
"github.com/tendermint/tendermint/internal/libs/clist"
|
||||
@@ -22,13 +21,6 @@ var (
|
||||
_ p2p.Wrapper = (*protomem.Message)(nil)
|
||||
)
|
||||
|
||||
// PeerManager defines the interface contract required for getting necessary
|
||||
// peer information. This should eventually be replaced with a message-oriented
|
||||
// approach utilizing the p2p stack.
|
||||
type PeerManager interface {
|
||||
GetHeight(types.NodeID) int64
|
||||
}
|
||||
|
||||
// Reactor implements a service that contains mempool of txs that are broadcasted
|
||||
// amongst peers. It maintains a map from peer ID to counter, to prevent gossiping
|
||||
// txs to the peers you received it from.
|
||||
@@ -40,9 +32,8 @@ type Reactor struct {
|
||||
mempool *TxMempool
|
||||
ids *IDs
|
||||
|
||||
getPeerHeight func(types.NodeID) int64
|
||||
peerEvents p2p.PeerEventSubscriber
|
||||
chCreator p2p.ChannelCreator
|
||||
peerEvents p2p.PeerEventSubscriber
|
||||
chCreator p2p.ChannelCreator
|
||||
|
||||
// observePanic is a function for observing panics that were recovered in methods on
|
||||
// Reactor. observePanic is called with the recovered value.
|
||||
@@ -59,18 +50,16 @@ func NewReactor(
|
||||
txmp *TxMempool,
|
||||
chCreator p2p.ChannelCreator,
|
||||
peerEvents p2p.PeerEventSubscriber,
|
||||
getPeerHeight func(types.NodeID) int64,
|
||||
) *Reactor {
|
||||
r := &Reactor{
|
||||
logger: logger,
|
||||
cfg: cfg,
|
||||
mempool: txmp,
|
||||
ids: NewMempoolIDs(),
|
||||
chCreator: chCreator,
|
||||
peerEvents: peerEvents,
|
||||
getPeerHeight: getPeerHeight,
|
||||
peerRoutines: make(map[types.NodeID]context.CancelFunc),
|
||||
observePanic: defaultObservePanic,
|
||||
logger: logger,
|
||||
cfg: cfg,
|
||||
mempool: txmp,
|
||||
ids: NewMempoolIDs(),
|
||||
chCreator: chCreator,
|
||||
peerEvents: peerEvents,
|
||||
peerRoutines: make(map[types.NodeID]context.CancelFunc),
|
||||
observePanic: defaultObservePanic,
|
||||
}
|
||||
|
||||
r.BaseService = *service.NewBaseService(logger, "Mempool", r)
|
||||
@@ -153,6 +142,15 @@ func (r *Reactor) handleMempoolMessage(ctx context.Context, envelope *p2p.Envelo
|
||||
// problem.
|
||||
continue
|
||||
}
|
||||
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
|
||||
// Do not propagate context
|
||||
// cancellation errors, but do
|
||||
// not continue to check
|
||||
// transactions from this
|
||||
// message if we are shutting down.
|
||||
return nil
|
||||
}
|
||||
|
||||
logger.Error("checktx failed for tx",
|
||||
"tx", fmt.Sprintf("%X", types.Tx(tx).Hash()),
|
||||
"err", err)
|
||||
@@ -318,15 +316,6 @@ func (r *Reactor) broadcastTxRoutine(ctx context.Context, peerID types.NodeID, m
|
||||
|
||||
memTx := nextGossipTx.Value.(*WrappedTx)
|
||||
|
||||
if r.getPeerHeight != nil {
|
||||
height := r.getPeerHeight(peerID)
|
||||
if height > 0 && height < memTx.height-1 {
|
||||
// allow for a lag of one block
|
||||
time.Sleep(PeerCatchupSleepIntervalMS * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Transaction batching was disabled due to:
|
||||
// https://github.com/tendermint/tendermint/issues/5796
|
||||
if ok := r.mempool.txStore.TxHasPeer(memTx.hash, peerMempoolID); !ok {
|
||||
|
||||
@@ -85,7 +85,6 @@ func setupReactors(ctx context.Context, t *testing.T, logger log.Logger, numNode
|
||||
mempool,
|
||||
chCreator,
|
||||
func(ctx context.Context) *p2p.PeerUpdates { return rts.peerUpdates[nodeID] },
|
||||
rts.network.Nodes[nodeID].PeerManager.GetHeight,
|
||||
)
|
||||
rts.nodes = append(rts.nodes, nodeID)
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
// Code generated by metricsgen. DO NOT EDIT.
|
||||
|
||||
package p2p
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/metrics/discard"
|
||||
prometheus "github.com/go-kit/kit/metrics/prometheus"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics {
|
||||
labels := []string{}
|
||||
for i := 0; i < len(labelsAndValues); i += 2 {
|
||||
labels = append(labels, labelsAndValues[i])
|
||||
}
|
||||
return &Metrics{
|
||||
Peers: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "peers",
|
||||
Help: "Number of peers.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
PeerReceiveBytesTotal: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "peer_receive_bytes_total",
|
||||
Help: "Number of bytes per channel received from a given peer.",
|
||||
}, append(labels, "peer_id", "chID", "message_type")).With(labelsAndValues...),
|
||||
PeerSendBytesTotal: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "peer_send_bytes_total",
|
||||
Help: "Number of bytes per channel sent to a given peer.",
|
||||
}, append(labels, "peer_id", "chID", "message_type")).With(labelsAndValues...),
|
||||
PeerPendingSendBytes: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "peer_pending_send_bytes",
|
||||
Help: "Number of bytes pending being sent to a given peer.",
|
||||
}, append(labels, "peer_id")).With(labelsAndValues...),
|
||||
RouterPeerQueueRecv: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "router_peer_queue_recv",
|
||||
Help: "The time taken to read off of a peer's queue before sending on the connection.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
RouterPeerQueueSend: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "router_peer_queue_send",
|
||||
Help: "The time taken to send on a peer's queue which will later be read and sent on the connection.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
RouterChannelQueueSend: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "router_channel_queue_send",
|
||||
Help: "The time taken to send on a p2p channel's queue which will later be consued by the corresponding reactor/service.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
PeerQueueDroppedMsgs: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "router_channel_queue_dropped_msgs",
|
||||
Help: "The number of messages dropped from a peer's queue for a specific p2p Channel.",
|
||||
}, append(labels, "ch_id")).With(labelsAndValues...),
|
||||
PeerQueueMsgSize: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "peer_queue_msg_size",
|
||||
Help: "The size of messages sent over a peer's queue for a specific p2p Channel.",
|
||||
}, append(labels, "ch_id")).With(labelsAndValues...),
|
||||
}
|
||||
}
|
||||
|
||||
func NopMetrics() *Metrics {
|
||||
return &Metrics{
|
||||
Peers: discard.NewGauge(),
|
||||
PeerReceiveBytesTotal: discard.NewCounter(),
|
||||
PeerSendBytesTotal: discard.NewCounter(),
|
||||
PeerPendingSendBytes: discard.NewGauge(),
|
||||
RouterPeerQueueRecv: discard.NewHistogram(),
|
||||
RouterPeerQueueSend: discard.NewHistogram(),
|
||||
RouterChannelQueueSend: discard.NewHistogram(),
|
||||
PeerQueueDroppedMsgs: discard.NewCounter(),
|
||||
PeerQueueMsgSize: discard.NewGauge(),
|
||||
}
|
||||
}
|
||||
+25
-106
@@ -7,9 +7,6 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/go-kit/kit/metrics"
|
||||
"github.com/go-kit/kit/metrics/discard"
|
||||
"github.com/go-kit/kit/metrics/prometheus"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -25,140 +22,55 @@ var (
|
||||
valueToLabelRegexp = regexp.MustCompile(`\*?(\w+)\.(.*)`)
|
||||
)
|
||||
|
||||
//go:generate go run ../../scripts/metricsgen -struct=Metrics
|
||||
|
||||
// Metrics contains metrics exposed by this package.
|
||||
type Metrics struct {
|
||||
// Number of peers.
|
||||
Peers metrics.Gauge
|
||||
// Number of bytes received from a given peer.
|
||||
PeerReceiveBytesTotal metrics.Counter
|
||||
// Number of bytes sent to a given peer.
|
||||
PeerSendBytesTotal metrics.Counter
|
||||
// Pending bytes to be sent to a given peer.
|
||||
PeerPendingSendBytes metrics.Gauge
|
||||
// Number of bytes per channel received from a given peer.
|
||||
PeerReceiveBytesTotal metrics.Counter `metrics_labels:"peer_id, chID, message_type"`
|
||||
// Number of bytes per channel sent to a given peer.
|
||||
PeerSendBytesTotal metrics.Counter `metrics_labels:"peer_id, chID, message_type"`
|
||||
// Number of bytes pending being sent to a given peer.
|
||||
PeerPendingSendBytes metrics.Gauge `metrics_labels:"peer_id"`
|
||||
|
||||
// RouterPeerQueueRecv defines the time taken to read off of a peer's queue
|
||||
// before sending on the connection.
|
||||
//metrics:The time taken to read off of a peer's queue before sending on the connection.
|
||||
RouterPeerQueueRecv metrics.Histogram
|
||||
|
||||
// RouterPeerQueueSend defines the time taken to send on a peer's queue which
|
||||
// will later be read and sent on the connection (see RouterPeerQueueRecv).
|
||||
//metrics:The time taken to send on a peer's queue which will later be read and sent on the connection.
|
||||
RouterPeerQueueSend metrics.Histogram
|
||||
|
||||
// RouterChannelQueueSend defines the time taken to send on a p2p channel's
|
||||
// queue which will later be consued by the corresponding reactor/service.
|
||||
//metrics:The time taken to send on a p2p channel's queue which will later be consued by the corresponding reactor/service.
|
||||
RouterChannelQueueSend metrics.Histogram
|
||||
|
||||
// PeerQueueDroppedMsgs defines the number of messages dropped from a peer's
|
||||
// queue for a specific flow (i.e. Channel).
|
||||
PeerQueueDroppedMsgs metrics.Counter
|
||||
//metrics:The number of messages dropped from a peer's queue for a specific p2p Channel.
|
||||
PeerQueueDroppedMsgs metrics.Counter `metrics_labels:"ch_id" metrics_name:"router_channel_queue_dropped_msgs"`
|
||||
|
||||
// PeerQueueMsgSize defines the average size of messages sent over a peer's
|
||||
// queue for a specific flow (i.e. Channel).
|
||||
PeerQueueMsgSize metrics.Gauge
|
||||
//metrics:The size of messages sent over a peer's queue for a specific p2p Channel.
|
||||
PeerQueueMsgSize metrics.Gauge `metrics_labels:"ch_id" metric_name:"router_channel_queue_msg_size"`
|
||||
}
|
||||
|
||||
type metricsLabelCache struct {
|
||||
mtx *sync.RWMutex
|
||||
messageLabelNames map[reflect.Type]string
|
||||
}
|
||||
|
||||
// PrometheusMetrics returns Metrics build using Prometheus client library.
|
||||
// Optionally, labels can be provided along with their values ("foo",
|
||||
// "fooValue").
|
||||
func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics {
|
||||
labels := []string{}
|
||||
for i := 0; i < len(labelsAndValues); i += 2 {
|
||||
labels = append(labels, labelsAndValues[i])
|
||||
}
|
||||
return &Metrics{
|
||||
Peers: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "peers",
|
||||
Help: "Number of peers.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
|
||||
PeerReceiveBytesTotal: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "peer_receive_bytes_total",
|
||||
Help: "Number of bytes received from a given peer.",
|
||||
}, append(labels, "peer_id", "chID", "message_type")).With(labelsAndValues...),
|
||||
|
||||
PeerSendBytesTotal: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "peer_send_bytes_total",
|
||||
Help: "Number of bytes sent to a given peer.",
|
||||
}, append(labels, "peer_id", "chID", "message_type")).With(labelsAndValues...),
|
||||
|
||||
PeerPendingSendBytes: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "peer_pending_send_bytes",
|
||||
Help: "Number of pending bytes to be sent to a given peer.",
|
||||
}, append(labels, "peer_id")).With(labelsAndValues...),
|
||||
|
||||
RouterPeerQueueRecv: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "router_peer_queue_recv",
|
||||
Help: "The time taken to read off of a peer's queue before sending on the connection.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
|
||||
RouterPeerQueueSend: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "router_peer_queue_send",
|
||||
Help: "The time taken to send on a peer's queue which will later be read and sent on the connection (see RouterPeerQueueRecv).",
|
||||
}, labels).With(labelsAndValues...),
|
||||
|
||||
RouterChannelQueueSend: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "router_channel_queue_send",
|
||||
Help: "The time taken to send on a p2p channel's queue which will later be consued by the corresponding reactor/service.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
|
||||
PeerQueueDroppedMsgs: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "router_channel_queue_dropped_msgs",
|
||||
Help: "The number of messages dropped from a peer's queue for a specific p2p Channel.",
|
||||
}, append(labels, "ch_id")).With(labelsAndValues...),
|
||||
|
||||
PeerQueueMsgSize: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "router_channel_queue_msg_size",
|
||||
Help: "The size of messages sent over a peer's queue for a specific p2p Channel.",
|
||||
}, append(labels, "ch_id")).With(labelsAndValues...),
|
||||
|
||||
mtx: &sync.RWMutex{},
|
||||
messageLabelNames: map[reflect.Type]string{},
|
||||
}
|
||||
}
|
||||
|
||||
// NopMetrics returns no-op Metrics.
|
||||
func NopMetrics() *Metrics {
|
||||
return &Metrics{
|
||||
Peers: discard.NewGauge(),
|
||||
PeerReceiveBytesTotal: discard.NewCounter(),
|
||||
PeerSendBytesTotal: discard.NewCounter(),
|
||||
PeerPendingSendBytes: discard.NewGauge(),
|
||||
RouterPeerQueueRecv: discard.NewHistogram(),
|
||||
RouterPeerQueueSend: discard.NewHistogram(),
|
||||
RouterChannelQueueSend: discard.NewHistogram(),
|
||||
PeerQueueDroppedMsgs: discard.NewCounter(),
|
||||
PeerQueueMsgSize: discard.NewGauge(),
|
||||
mtx: &sync.RWMutex{},
|
||||
messageLabelNames: map[reflect.Type]string{},
|
||||
}
|
||||
}
|
||||
|
||||
// ValueToMetricLabel is a method that is used to produce a prometheus label value of the golang
|
||||
// type that is passed in.
|
||||
// This method uses a map on the Metrics struct so that each label name only needs
|
||||
// to be produced once to prevent expensive string operations.
|
||||
func (m *Metrics) ValueToMetricLabel(i interface{}) string {
|
||||
func (m *metricsLabelCache) ValueToMetricLabel(i interface{}) string {
|
||||
t := reflect.TypeOf(i)
|
||||
m.mtx.RLock()
|
||||
|
||||
@@ -176,3 +88,10 @@ func (m *Metrics) ValueToMetricLabel(i interface{}) string {
|
||||
m.messageLabelNames[t] = l
|
||||
return l
|
||||
}
|
||||
|
||||
func newMetricsLabelCache() *metricsLabelCache {
|
||||
return &metricsLabelCache{
|
||||
mtx: &sync.RWMutex{},
|
||||
messageLabelNames: map[reflect.Type]string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,12 +9,12 @@ import (
|
||||
)
|
||||
|
||||
func TestValueToMetricsLabel(t *testing.T) {
|
||||
m := NopMetrics()
|
||||
lc := newMetricsLabelCache()
|
||||
r := &p2p.PexResponse{}
|
||||
str := m.ValueToMetricLabel(r)
|
||||
str := lc.ValueToMetricLabel(r)
|
||||
assert.Equal(t, "p2p_PexResponse", str)
|
||||
|
||||
// subsequent calls to the function should produce the same result
|
||||
str = m.ValueToMetricLabel(r)
|
||||
str = lc.ValueToMetricLabel(r)
|
||||
assert.Equal(t, "p2p_PexResponse", str)
|
||||
}
|
||||
|
||||
@@ -1027,37 +1027,6 @@ func (m *PeerManager) retryDelay(failures uint32, persistent bool) time.Duration
|
||||
return delay
|
||||
}
|
||||
|
||||
// GetHeight returns a peer's height, as reported via SetHeight, or 0 if the
|
||||
// peer or height is unknown.
|
||||
//
|
||||
// FIXME: This is a temporary workaround to share state between the consensus
|
||||
// and mempool reactors, carried over from the legacy P2P stack. Reactors should
|
||||
// not have dependencies on each other, instead tracking this themselves.
|
||||
func (m *PeerManager) GetHeight(peerID types.NodeID) int64 {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
|
||||
peer, _ := m.store.Get(peerID)
|
||||
return peer.Height
|
||||
}
|
||||
|
||||
// SetHeight stores a peer's height, making it available via GetHeight.
|
||||
//
|
||||
// FIXME: This is a temporary workaround to share state between the consensus
|
||||
// and mempool reactors, carried over from the legacy P2P stack. Reactors should
|
||||
// not have dependencies on each other, instead tracking this themselves.
|
||||
func (m *PeerManager) SetHeight(peerID types.NodeID, height int64) error {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
|
||||
peer, ok := m.store.Get(peerID)
|
||||
if !ok {
|
||||
peer = m.newPeerInfo(peerID)
|
||||
}
|
||||
peer.Height = height
|
||||
return m.store.Set(peer)
|
||||
}
|
||||
|
||||
// peerStore stores information about peers. It is not thread-safe, assuming it
|
||||
// is only used by PeerManager which handles concurrency control. This allows
|
||||
// the manager to execute multiple operations atomically via its own mutex.
|
||||
|
||||
@@ -1868,38 +1868,3 @@ func TestPeerManager_Advertise_Self(t *testing.T) {
|
||||
self,
|
||||
}, peerManager.Advertise(dID, 100))
|
||||
}
|
||||
|
||||
func TestPeerManager_SetHeight_GetHeight(t *testing.T) {
|
||||
a := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("a", 40))}
|
||||
b := p2p.NodeAddress{Protocol: "memory", NodeID: types.NodeID(strings.Repeat("b", 40))}
|
||||
|
||||
db := dbm.NewMemDB()
|
||||
peerManager, err := p2p.NewPeerManager(selfID, db, p2p.PeerManagerOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Getting a height should default to 0, for unknown peers and
|
||||
// for known peers without height.
|
||||
added, err := peerManager.Add(a)
|
||||
require.NoError(t, err)
|
||||
require.True(t, added)
|
||||
require.EqualValues(t, 0, peerManager.GetHeight(a.NodeID))
|
||||
require.EqualValues(t, 0, peerManager.GetHeight(b.NodeID))
|
||||
|
||||
// Setting a height should work for a known node.
|
||||
require.NoError(t, peerManager.SetHeight(a.NodeID, 3))
|
||||
require.EqualValues(t, 3, peerManager.GetHeight(a.NodeID))
|
||||
|
||||
// Setting a height should add an unknown node.
|
||||
require.Equal(t, []types.NodeID{a.NodeID}, peerManager.Peers())
|
||||
require.NoError(t, peerManager.SetHeight(b.NodeID, 7))
|
||||
require.EqualValues(t, 7, peerManager.GetHeight(b.NodeID))
|
||||
require.ElementsMatch(t, []types.NodeID{a.NodeID, b.NodeID}, peerManager.Peers())
|
||||
|
||||
// The heights should not be persisted.
|
||||
peerManager, err = p2p.NewPeerManager(selfID, db, p2p.PeerManagerOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
require.ElementsMatch(t, []types.NodeID{a.NodeID, b.NodeID}, peerManager.Peers())
|
||||
require.Zero(t, peerManager.GetHeight(a.NodeID))
|
||||
require.Zero(t, peerManager.GetHeight(b.NodeID))
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ var _ queue = (*pqScheduler)(nil)
|
||||
type pqScheduler struct {
|
||||
logger log.Logger
|
||||
metrics *Metrics
|
||||
lc *metricsLabelCache
|
||||
size uint
|
||||
sizes map[uint]uint // cumulative priority sizes
|
||||
pq *priorityQueue
|
||||
@@ -88,6 +89,7 @@ type pqScheduler struct {
|
||||
func newPQScheduler(
|
||||
logger log.Logger,
|
||||
m *Metrics,
|
||||
lc *metricsLabelCache,
|
||||
chDescs []*ChannelDescriptor,
|
||||
enqueueBuf, dequeueBuf, capacity uint,
|
||||
) *pqScheduler {
|
||||
@@ -117,6 +119,7 @@ func newPQScheduler(
|
||||
return &pqScheduler{
|
||||
logger: logger.With("router", "scheduler"),
|
||||
metrics: m,
|
||||
lc: lc,
|
||||
chDescs: chDescsCopy,
|
||||
capacity: capacity,
|
||||
chPriorities: chPriorities,
|
||||
@@ -251,7 +254,7 @@ func (s *pqScheduler) process(ctx context.Context) {
|
||||
s.metrics.PeerSendBytesTotal.With(
|
||||
"chID", chIDStr,
|
||||
"peer_id", string(pqEnv.envelope.To),
|
||||
"message_type", s.metrics.ValueToMetricLabel(pqEnv.envelope.Message)).Add(float64(pqEnv.size))
|
||||
"message_type", s.lc.ValueToMetricLabel(pqEnv.envelope.Message)).Add(float64(pqEnv.size))
|
||||
s.metrics.PeerPendingSendBytes.With(
|
||||
"peer_id", string(pqEnv.envelope.To)).Add(float64(-pqEnv.size))
|
||||
select {
|
||||
|
||||
@@ -17,7 +17,7 @@ func TestCloseWhileDequeueFull(t *testing.T) {
|
||||
chDescs := []*ChannelDescriptor{
|
||||
{ID: 0x01, Priority: 1},
|
||||
}
|
||||
pqueue := newPQScheduler(log.NewNopLogger(), NopMetrics(), chDescs, uint(enqueueLength), 1, 120)
|
||||
pqueue := newPQScheduler(log.NewNopLogger(), NopMetrics(), newMetricsLabelCache(), chDescs, uint(enqueueLength), 1, 120)
|
||||
|
||||
for i := 0; i < enqueueLength; i++ {
|
||||
pqueue.enqueue() <- Envelope{
|
||||
|
||||
@@ -148,7 +148,9 @@ type Router struct {
|
||||
*service.BaseService
|
||||
logger log.Logger
|
||||
|
||||
metrics *Metrics
|
||||
metrics *Metrics
|
||||
lc *metricsLabelCache
|
||||
|
||||
options RouterOptions
|
||||
privKey crypto.PrivKey
|
||||
peerManager *PeerManager
|
||||
@@ -193,6 +195,7 @@ func NewRouter(
|
||||
router := &Router{
|
||||
logger: logger,
|
||||
metrics: metrics,
|
||||
lc: newMetricsLabelCache(),
|
||||
privKey: privKey,
|
||||
nodeInfoProducer: nodeInfoProducer,
|
||||
connTracker: newConnTracker(
|
||||
@@ -226,7 +229,7 @@ func (r *Router) createQueueFactory(ctx context.Context) (func(int) queue, error
|
||||
size++
|
||||
}
|
||||
|
||||
q := newPQScheduler(r.logger, r.metrics, r.chDescs, uint(size)/2, uint(size)/2, defaultCapacity)
|
||||
q := newPQScheduler(r.logger, r.metrics, r.lc, r.chDescs, uint(size)/2, uint(size)/2, defaultCapacity)
|
||||
q.start(ctx)
|
||||
return q
|
||||
}, nil
|
||||
@@ -839,7 +842,7 @@ func (r *Router) receivePeer(ctx context.Context, peerID types.NodeID, conn Conn
|
||||
r.metrics.PeerReceiveBytesTotal.With(
|
||||
"chID", fmt.Sprint(chID),
|
||||
"peer_id", string(peerID),
|
||||
"message_type", r.metrics.ValueToMetricLabel(msg)).Add(float64(proto.Size(msg)))
|
||||
"message_type", r.lc.ValueToMetricLabel(msg)).Add(float64(proto.Size(msg)))
|
||||
r.metrics.RouterChannelQueueSend.Observe(time.Since(start).Seconds())
|
||||
r.logger.Debug("received message", "peer", peerID, "message", msg)
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ func (app *appConnTest) Info(ctx context.Context, req *types.RequestInfo) (*type
|
||||
var SOCKET = "socket"
|
||||
|
||||
func TestEcho(t *testing.T) {
|
||||
sockPath := fmt.Sprintf("unix:///tmp/echo_%v.sock", tmrand.Str(6))
|
||||
sockPath := fmt.Sprintf("unix://%s/echo_%v.sock", t.TempDir(), tmrand.Str(6))
|
||||
logger := log.NewNopLogger()
|
||||
client, err := abciclient.NewClient(logger, sockPath, SOCKET, true)
|
||||
if err != nil {
|
||||
@@ -98,7 +98,7 @@ func TestEcho(t *testing.T) {
|
||||
|
||||
func BenchmarkEcho(b *testing.B) {
|
||||
b.StopTimer() // Initialize
|
||||
sockPath := fmt.Sprintf("unix:///tmp/echo_%v.sock", tmrand.Str(6))
|
||||
sockPath := fmt.Sprintf("unix://%s/echo_%v.sock", b.TempDir(), tmrand.Str(6))
|
||||
logger := log.NewNopLogger()
|
||||
client, err := abciclient.NewClient(logger, sockPath, SOCKET, true)
|
||||
if err != nil {
|
||||
@@ -146,7 +146,7 @@ func TestInfo(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
sockPath := fmt.Sprintf("unix:///tmp/echo_%v.sock", tmrand.Str(6))
|
||||
sockPath := fmt.Sprintf("unix://%s/echo_%v.sock", t.TempDir(), tmrand.Str(6))
|
||||
logger := log.NewNopLogger()
|
||||
client, err := abciclient.NewClient(logger, sockPath, SOCKET, true)
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
// Code generated by metricsgen. DO NOT EDIT.
|
||||
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/metrics/discard"
|
||||
prometheus "github.com/go-kit/kit/metrics/prometheus"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics {
|
||||
labels := []string{}
|
||||
for i := 0; i < len(labelsAndValues); i += 2 {
|
||||
labels = append(labels, labelsAndValues[i])
|
||||
}
|
||||
return &Metrics{
|
||||
MethodTiming: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "method_timing",
|
||||
Help: "Timing for each ABCI method.",
|
||||
|
||||
Buckets: []float64{.0001, .0004, .002, .009, .02, .1, .65, 2, 6, 25},
|
||||
}, append(labels, "method", "type")).With(labelsAndValues...),
|
||||
}
|
||||
}
|
||||
|
||||
func NopMetrics() *Metrics {
|
||||
return &Metrics{
|
||||
MethodTiming: discard.NewHistogram(),
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,6 @@ package proxy
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/metrics"
|
||||
"github.com/go-kit/kit/metrics/discard"
|
||||
"github.com/go-kit/kit/metrics/prometheus"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -13,35 +10,10 @@ const (
|
||||
MetricsSubsystem = "abci_connection"
|
||||
)
|
||||
|
||||
//go:generate go run ../../scripts/metricsgen -struct=Metrics
|
||||
|
||||
// Metrics contains the prometheus metrics exposed by the proxy package.
|
||||
type Metrics struct {
|
||||
MethodTiming metrics.Histogram
|
||||
}
|
||||
|
||||
// PrometheusMetrics constructs a Metrics instance that collects metrics samples.
|
||||
// The resulting metrics will be prefixed with namespace and labeled with the
|
||||
// defaultLabelsAndValues. defaultLabelsAndValues must be a list of string pairs
|
||||
// where the first of each pair is the label and the second is the value.
|
||||
func PrometheusMetrics(namespace string, defaultLabelsAndValues ...string) *Metrics {
|
||||
defaultLabels := []string{}
|
||||
for i := 0; i < len(defaultLabelsAndValues); i += 2 {
|
||||
defaultLabels = append(defaultLabels, defaultLabelsAndValues[i])
|
||||
}
|
||||
return &Metrics{
|
||||
MethodTiming: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "method_timing",
|
||||
Help: "ABCI Method Timing",
|
||||
Buckets: []float64{.0001, .0004, .002, .009, .02, .1, .65, 2, 6, 25},
|
||||
}, append(defaultLabels, []string{"method", "type"}...)).With(defaultLabelsAndValues...),
|
||||
}
|
||||
}
|
||||
|
||||
// NopMetrics constructs a Metrics instance that discards all samples and is suitable
|
||||
// for testing.
|
||||
func NopMetrics() *Metrics {
|
||||
return &Metrics{
|
||||
MethodTiming: discard.NewHistogram(),
|
||||
}
|
||||
// Timing for each ABCI method.
|
||||
MethodTiming metrics.Histogram `metrics_bucketsizes:".0001,.0004,.002,.009,.02,.1,.65,2,6,25" metrics_labels:"method, type"`
|
||||
}
|
||||
|
||||
@@ -236,7 +236,8 @@ func (env *Environment) StartService(ctx context.Context, conf *config.Config) (
|
||||
// If necessary adjust global WriteTimeout to ensure it's greater than
|
||||
// TimeoutBroadcastTxCommit.
|
||||
// See https://github.com/tendermint/tendermint/issues/3435
|
||||
if cfg.WriteTimeout <= conf.RPC.TimeoutBroadcastTxCommit {
|
||||
// Note we don't need to adjust anything if the timeout is already unlimited.
|
||||
if cfg.WriteTimeout > 0 && cfg.WriteTimeout <= conf.RPC.TimeoutBroadcastTxCommit {
|
||||
cfg.WriteTimeout = conf.RPC.TimeoutBroadcastTxCommit + 1*time.Second
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ func NewRoutesMap(svc RPCService, opts *RouteOptions) RoutesMap {
|
||||
out := RoutesMap{
|
||||
// Event subscription. Note that subscribe, unsubscribe, and
|
||||
// unsubscribe_all are only available via the websocket endpoint.
|
||||
"events": rpc.NewRPCFunc(svc.Events),
|
||||
"events": rpc.NewRPCFunc(svc.Events).Timeout(0),
|
||||
"subscribe": rpc.NewWSRPCFunc(svc.Subscribe),
|
||||
"unsubscribe": rpc.NewWSRPCFunc(svc.Unsubscribe),
|
||||
"unsubscribe_all": rpc.NewWSRPCFunc(svc.UnsubscribeAll),
|
||||
|
||||
+73
-39
@@ -1,13 +1,14 @@
|
||||
package state
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
abciclient "github.com/tendermint/tendermint/abci/client"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/encoding"
|
||||
"github.com/tendermint/tendermint/crypto/merkle"
|
||||
"github.com/tendermint/tendermint/internal/eventbus"
|
||||
@@ -87,9 +88,8 @@ func (blockExec *BlockExecutor) CreateProposalBlock(
|
||||
ctx context.Context,
|
||||
height int64,
|
||||
state State,
|
||||
commit *types.Commit,
|
||||
lastExtCommit *types.ExtendedCommit,
|
||||
proposerAddr []byte,
|
||||
votes []*types.Vote,
|
||||
) (*types.Block, error) {
|
||||
|
||||
maxBytes := state.ConsensusParams.Block.MaxBytes
|
||||
@@ -101,15 +101,14 @@ func (blockExec *BlockExecutor) CreateProposalBlock(
|
||||
maxDataBytes := types.MaxDataBytes(maxBytes, evSize, state.Validators.Size())
|
||||
|
||||
txs := blockExec.mempool.ReapMaxBytesMaxGas(maxDataBytes, maxGas)
|
||||
commit := lastExtCommit.ToCommit()
|
||||
block := state.MakeBlock(height, txs, commit, evidence, proposerAddr)
|
||||
|
||||
localLastCommit := buildLastCommitInfo(block, blockExec.store, state.InitialHeight)
|
||||
rpp, err := blockExec.appClient.PrepareProposal(
|
||||
ctx,
|
||||
&abci.RequestPrepareProposal{
|
||||
MaxTxBytes: maxDataBytes,
|
||||
Txs: block.Txs.ToSliceOfBytes(),
|
||||
LocalLastCommit: extendedCommitInfo(localLastCommit, votes),
|
||||
LocalLastCommit: buildExtendedCommitInfo(lastExtCommit, blockExec.store, state.InitialHeight, state.ConsensusParams.ABCI),
|
||||
ByzantineValidators: block.Evidence.ToABCI(),
|
||||
Height: block.Height,
|
||||
Time: block.Time,
|
||||
@@ -247,6 +246,10 @@ func (blockExec *BlockExecutor) ApplyBlock(
|
||||
}
|
||||
if len(validatorUpdates) > 0 {
|
||||
blockExec.logger.Debug("updates to validators", "updates", types.ValidatorListString(validatorUpdates))
|
||||
blockExec.metrics.ValidatorSetUpdates.Add(1)
|
||||
}
|
||||
if finalizeBlockResponse.ConsensusParamUpdates != nil {
|
||||
blockExec.metrics.ConsensusParamUpdates.Add(1)
|
||||
}
|
||||
|
||||
// Update the state with the block and responses.
|
||||
@@ -318,7 +321,7 @@ func (blockExec *BlockExecutor) VerifyVoteExtension(ctx context.Context, vote *t
|
||||
}
|
||||
|
||||
if !resp.IsOK() {
|
||||
return types.ErrVoteInvalidExtension
|
||||
return errors.New("invalid vote extension")
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -377,14 +380,14 @@ func (blockExec *BlockExecutor) Commit(
|
||||
|
||||
func buildLastCommitInfo(block *types.Block, store Store, initialHeight int64) abci.CommitInfo {
|
||||
if block.Height == initialHeight {
|
||||
// there is no last commmit for the initial height.
|
||||
// there is no last commit for the initial height.
|
||||
// return an empty value.
|
||||
return abci.CommitInfo{}
|
||||
}
|
||||
|
||||
lastValSet, err := store.LoadValidators(block.Height - 1)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
panic(fmt.Errorf("failed to load validator set at height %d: %w", block.Height-1, err))
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -406,7 +409,7 @@ func buildLastCommitInfo(block *types.Block, store Store, initialHeight int64) a
|
||||
commitSig := block.LastCommit.Signatures[i]
|
||||
votes[i] = abci.VoteInfo{
|
||||
Validator: types.TM2PB.Validator(val),
|
||||
SignedLastBlock: !commitSig.Absent(),
|
||||
SignedLastBlock: commitSig.BlockIDFlag != types.BlockIDFlagAbsent,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,44 +419,75 @@ func buildLastCommitInfo(block *types.Block, store Store, initialHeight int64) a
|
||||
}
|
||||
}
|
||||
|
||||
// extendedCommitInfo expects a CommitInfo struct along with all of the
|
||||
// original votes relating to that commit, including their vote extensions. The
|
||||
// order of votes does not matter.
|
||||
func extendedCommitInfo(c abci.CommitInfo, votes []*types.Vote) abci.ExtendedCommitInfo {
|
||||
if len(c.Votes) != len(votes) {
|
||||
panic(fmt.Sprintf("extendedCommitInfo: number of votes from commit differ from the number of votes supplied (%d != %d)", len(c.Votes), len(votes)))
|
||||
// buildExtendedCommitInfo populates an ABCI extended commit from the
|
||||
// corresponding Tendermint extended commit ec, using the stored validator set
|
||||
// from ec. It requires ec to include the original precommit votes along with
|
||||
// the vote extensions from the last commit.
|
||||
//
|
||||
// For heights below the initial height, for which we do not have the required
|
||||
// data, it returns an empty record.
|
||||
//
|
||||
// Assumes that the commit signatures are sorted according to validator index.
|
||||
func buildExtendedCommitInfo(ec *types.ExtendedCommit, store Store, initialHeight int64, ap types.ABCIParams) abci.ExtendedCommitInfo {
|
||||
if ec.Height < initialHeight {
|
||||
// There are no extended commits for heights below the initial height.
|
||||
return abci.ExtendedCommitInfo{}
|
||||
}
|
||||
votesByVal := make(map[string]*types.Vote)
|
||||
for _, vote := range votes {
|
||||
if vote != nil {
|
||||
valAddr := vote.ValidatorAddress.String()
|
||||
if _, ok := votesByVal[valAddr]; ok {
|
||||
panic(fmt.Sprintf("extendedCommitInfo: found duplicate vote for validator with address %s", valAddr))
|
||||
}
|
||||
votesByVal[valAddr] = vote
|
||||
|
||||
valSet, err := store.LoadValidators(ec.Height)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to load validator set at height %d, initial height %d: %w", ec.Height, initialHeight, err))
|
||||
}
|
||||
|
||||
var (
|
||||
ecSize = ec.Size()
|
||||
valSetLen = len(valSet.Validators)
|
||||
)
|
||||
|
||||
// Ensure that the size of the validator set in the extended commit matches
|
||||
// the size of the validator set in the state store.
|
||||
if ecSize != valSetLen {
|
||||
panic(fmt.Errorf(
|
||||
"extended commit size (%d) does not match validator set length (%d) at height %d\n\n%v\n\n%v",
|
||||
ecSize, valSetLen, ec.Height, ec.ExtendedSignatures, valSet.Validators,
|
||||
))
|
||||
}
|
||||
|
||||
votes := make([]abci.ExtendedVoteInfo, ecSize)
|
||||
for i, val := range valSet.Validators {
|
||||
ecs := ec.ExtendedSignatures[i]
|
||||
|
||||
// Absent signatures have empty validator addresses, but otherwise we
|
||||
// expect the validator addresses to be the same.
|
||||
if ecs.BlockIDFlag != types.BlockIDFlagAbsent && !bytes.Equal(ecs.ValidatorAddress, val.Address) {
|
||||
panic(fmt.Errorf("validator address of extended commit signature in position %d (%s) does not match the corresponding validator's at height %d (%s)",
|
||||
i, ecs.ValidatorAddress, ec.Height, val.Address,
|
||||
))
|
||||
}
|
||||
}
|
||||
vs := make([]abci.ExtendedVoteInfo, len(c.Votes))
|
||||
for i := range vs {
|
||||
|
||||
var ext []byte
|
||||
// votes[i] will be nil if c.Votes[i].SignedLastBlock is false
|
||||
if c.Votes[i].SignedLastBlock {
|
||||
valAddr := crypto.Address(c.Votes[i].Validator.Address).String()
|
||||
vote, ok := votesByVal[valAddr]
|
||||
if !ok || vote == nil {
|
||||
panic(fmt.Sprintf("extendedCommitInfo: validator with address %s signed last block, but could not find vote for it", valAddr))
|
||||
// Check if vote extensions were enabled during the commit's height: ec.Height.
|
||||
// ec is the commit from the previous height, so if extensions were enabled
|
||||
// during that height, we ensure they are present and deliver the data to
|
||||
// the proposer. If they were not enabled during this previous height, we
|
||||
// will not deliver extension data.
|
||||
if ap.VoteExtensionsEnabled(ec.Height) && ecs.BlockIDFlag == types.BlockIDFlagCommit {
|
||||
if err := ecs.EnsureExtension(); err != nil {
|
||||
panic(fmt.Errorf("commit at height %d received with missing vote extensions data", ec.Height))
|
||||
}
|
||||
ext = vote.Extension
|
||||
ext = ecs.Extension
|
||||
}
|
||||
vs[i] = abci.ExtendedVoteInfo{
|
||||
Validator: c.Votes[i].Validator,
|
||||
SignedLastBlock: c.Votes[i].SignedLastBlock,
|
||||
|
||||
votes[i] = abci.ExtendedVoteInfo{
|
||||
Validator: types.TM2PB.Validator(val),
|
||||
SignedLastBlock: ecs.BlockIDFlag != types.BlockIDFlagAbsent,
|
||||
VoteExtension: ext,
|
||||
}
|
||||
}
|
||||
|
||||
return abci.ExtendedCommitInfo{
|
||||
Round: c.Round,
|
||||
Votes: vs,
|
||||
Round: ec.Round,
|
||||
Votes: votes,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -79,9 +79,10 @@ func TestApplyBlock(t *testing.T) {
|
||||
assert.EqualValues(t, 1, state.Version.Consensus.App, "App version wasn't updated")
|
||||
}
|
||||
|
||||
// TestFinalizeBlockDecidedLastCommit ensures we correctly send the DecidedLastCommit to the
|
||||
// application. The test ensures that the DecidedLastCommit properly reflects
|
||||
// which validators signed the preceding block.
|
||||
// TestFinalizeBlockDecidedLastCommit ensures we correctly send the
|
||||
// DecidedLastCommit to the application. The test ensures that the
|
||||
// DecidedLastCommit properly reflects which validators signed the preceding
|
||||
// block.
|
||||
func TestFinalizeBlockDecidedLastCommit(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
@@ -96,7 +97,7 @@ func TestFinalizeBlockDecidedLastCommit(t *testing.T) {
|
||||
|
||||
state, stateDB, privVals := makeState(t, 7, 1)
|
||||
stateStore := sm.NewStore(stateDB)
|
||||
absentSig := types.NewCommitSigAbsent()
|
||||
absentSig := types.NewExtendedCommitSigAbsent()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
@@ -134,12 +135,12 @@ func TestFinalizeBlockDecidedLastCommit(t *testing.T) {
|
||||
|
||||
for idx, isAbsent := range tc.absentCommitSigs {
|
||||
if isAbsent {
|
||||
lastCommit.Signatures[idx] = absentSig
|
||||
lastCommit.ExtendedSignatures[idx] = absentSig
|
||||
}
|
||||
}
|
||||
|
||||
// block for height 2
|
||||
block := sf.MakeBlock(state, 2, lastCommit)
|
||||
block := sf.MakeBlock(state, 2, lastCommit.ToCommit())
|
||||
bps, err := block.MakePartSet(testPartSize)
|
||||
require.NoError(t, err)
|
||||
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()}
|
||||
@@ -198,12 +199,15 @@ func TestFinalizeBlockByzantineValidators(t *testing.T) {
|
||||
ConflictingBlock: &types.LightBlock{
|
||||
SignedHeader: &types.SignedHeader{
|
||||
Header: header,
|
||||
Commit: types.NewCommit(10, 0, makeBlockID(header.Hash(), 100, []byte("partshash")), []types.CommitSig{{
|
||||
BlockIDFlag: types.BlockIDFlagNil,
|
||||
ValidatorAddress: crypto.AddressHash([]byte("validator_address")),
|
||||
Timestamp: defaultEvidenceTime,
|
||||
Signature: crypto.CRandBytes(types.MaxSignatureSize),
|
||||
}}),
|
||||
Commit: &types.Commit{
|
||||
Height: 10,
|
||||
BlockID: makeBlockID(header.Hash(), 100, []byte("partshash")),
|
||||
Signatures: []types.CommitSig{{
|
||||
BlockIDFlag: types.BlockIDFlagNil,
|
||||
ValidatorAddress: crypto.AddressHash([]byte("validator_address")),
|
||||
Timestamp: defaultEvidenceTime,
|
||||
Signature: crypto.CRandBytes(types.MaxSignatureSize)}},
|
||||
},
|
||||
},
|
||||
ValidatorSet: state.Validators,
|
||||
},
|
||||
@@ -324,8 +328,10 @@ func TestProcessProposal(t *testing.T) {
|
||||
lastCommitSig = append(lastCommitSig, vote.CommitSig())
|
||||
}
|
||||
|
||||
lastCommit := types.NewCommit(height-1, 0, types.BlockID{}, lastCommitSig)
|
||||
block1 := sf.MakeBlock(state, height, lastCommit)
|
||||
block1 := sf.MakeBlock(state, height, &types.Commit{
|
||||
Height: height - 1,
|
||||
Signatures: lastCommitSig,
|
||||
})
|
||||
block1.Txs = txs
|
||||
|
||||
expectedRpp := &abci.RequestProcessProposal{
|
||||
@@ -653,8 +659,8 @@ func TestEmptyPrepareProposal(t *testing.T) {
|
||||
sm.NopMetrics(),
|
||||
)
|
||||
pa, _ := state.Validators.GetByIndex(0)
|
||||
commit, votes := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
|
||||
_, err = blockExec.CreateProposalBlock(ctx, height, state, commit, pa, votes)
|
||||
commit, _ := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
|
||||
_, err = blockExec.CreateProposalBlock(ctx, height, state, commit, pa)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -708,8 +714,8 @@ func TestPrepareProposalErrorOnNonExistingRemoved(t *testing.T) {
|
||||
sm.NopMetrics(),
|
||||
)
|
||||
pa, _ := state.Validators.GetByIndex(0)
|
||||
commit, votes := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
|
||||
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, votes)
|
||||
commit, _ := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
|
||||
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa)
|
||||
require.ErrorContains(t, err, "new transaction incorrectly marked as removed")
|
||||
require.Nil(t, block)
|
||||
|
||||
@@ -764,8 +770,8 @@ func TestPrepareProposalRemoveTxs(t *testing.T) {
|
||||
sm.NopMetrics(),
|
||||
)
|
||||
pa, _ := state.Validators.GetByIndex(0)
|
||||
commit, votes := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
|
||||
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, votes)
|
||||
commit, _ := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
|
||||
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, block.Data.Txs.ToSliceOfBytes(), len(trs)-2)
|
||||
|
||||
@@ -823,8 +829,8 @@ func TestPrepareProposalAddedTxsIncluded(t *testing.T) {
|
||||
sm.NopMetrics(),
|
||||
)
|
||||
pa, _ := state.Validators.GetByIndex(0)
|
||||
commit, votes := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
|
||||
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, votes)
|
||||
commit, _ := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
|
||||
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, txs[0], block.Data.Txs[0])
|
||||
@@ -879,8 +885,8 @@ func TestPrepareProposalReorderTxs(t *testing.T) {
|
||||
sm.NopMetrics(),
|
||||
)
|
||||
pa, _ := state.Validators.GetByIndex(0)
|
||||
commit, votes := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
|
||||
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, votes)
|
||||
commit, _ := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
|
||||
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa)
|
||||
require.NoError(t, err)
|
||||
for i, tx := range block.Data.Txs {
|
||||
require.Equal(t, types.Tx(trs[i].Tx), tx)
|
||||
@@ -939,9 +945,8 @@ func TestPrepareProposalErrorOnTooManyTxs(t *testing.T) {
|
||||
sm.NopMetrics(),
|
||||
)
|
||||
pa, _ := state.Validators.GetByIndex(0)
|
||||
commit, votes := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
|
||||
|
||||
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, votes)
|
||||
commit, _ := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
|
||||
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa)
|
||||
require.ErrorContains(t, err, "transaction data size exceeds maximum")
|
||||
require.Nil(t, block, "")
|
||||
|
||||
@@ -991,15 +996,124 @@ func TestPrepareProposalErrorOnPrepareProposalError(t *testing.T) {
|
||||
sm.NopMetrics(),
|
||||
)
|
||||
pa, _ := state.Validators.GetByIndex(0)
|
||||
commit, votes := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
|
||||
|
||||
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, votes)
|
||||
commit, _ := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
|
||||
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa)
|
||||
require.Nil(t, block)
|
||||
require.ErrorContains(t, err, "an injected error")
|
||||
|
||||
mp.AssertExpectations(t)
|
||||
}
|
||||
|
||||
// TestCreateProposalBlockPanicOnAbsentVoteExtensions ensures that the CreateProposalBlock
|
||||
// call correctly panics when the vote extension data is missing from the extended commit
|
||||
// data that the method receives.
|
||||
func TestCreateProposalAbsentVoteExtensions(t *testing.T) {
|
||||
for _, testCase := range []struct {
|
||||
name string
|
||||
|
||||
// The height that is about to be proposed
|
||||
height int64
|
||||
|
||||
// The first height during which vote extensions will be required for consensus to proceed.
|
||||
extensionEnableHeight int64
|
||||
expectPanic bool
|
||||
}{
|
||||
{
|
||||
name: "missing extension data on first required height",
|
||||
height: 2,
|
||||
extensionEnableHeight: 1,
|
||||
expectPanic: true,
|
||||
},
|
||||
{
|
||||
name: "missing extension during before required height",
|
||||
height: 2,
|
||||
extensionEnableHeight: 2,
|
||||
expectPanic: false,
|
||||
},
|
||||
{
|
||||
name: "missing extension data and not required",
|
||||
height: 2,
|
||||
extensionEnableHeight: 0,
|
||||
expectPanic: false,
|
||||
},
|
||||
{
|
||||
name: "missing extension data and required in two heights",
|
||||
height: 2,
|
||||
extensionEnableHeight: 3,
|
||||
expectPanic: false,
|
||||
},
|
||||
} {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
logger := log.NewNopLogger()
|
||||
|
||||
eventBus := eventbus.NewDefault(logger)
|
||||
require.NoError(t, eventBus.Start(ctx))
|
||||
|
||||
app := abcimocks.NewApplication(t)
|
||||
if !testCase.expectPanic {
|
||||
app.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{}, nil)
|
||||
}
|
||||
cc := abciclient.NewLocalClient(logger, app)
|
||||
proxyApp := proxy.New(cc, logger, proxy.NopMetrics())
|
||||
err := proxyApp.Start(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
state, stateDB, privVals := makeState(t, 1, int(testCase.height-1))
|
||||
stateStore := sm.NewStore(stateDB)
|
||||
state.ConsensusParams.ABCI.VoteExtensionsEnableHeight = testCase.extensionEnableHeight
|
||||
mp := &mpmocks.Mempool{}
|
||||
mp.On("Lock").Return()
|
||||
mp.On("Unlock").Return()
|
||||
mp.On("FlushAppConn", mock.Anything).Return(nil)
|
||||
mp.On("Update",
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything).Return(nil)
|
||||
mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(types.Txs{})
|
||||
|
||||
blockExec := sm.NewBlockExecutor(
|
||||
stateStore,
|
||||
logger,
|
||||
proxyApp,
|
||||
mp,
|
||||
sm.EmptyEvidencePool{},
|
||||
nil,
|
||||
eventBus,
|
||||
sm.NopMetrics(),
|
||||
)
|
||||
block := sf.MakeBlock(state, testCase.height, new(types.Commit))
|
||||
bps, err := block.MakePartSet(testPartSize)
|
||||
require.NoError(t, err)
|
||||
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()}
|
||||
pa, _ := state.Validators.GetByIndex(0)
|
||||
lastCommit, _ := makeValidCommit(ctx, t, testCase.height-1, blockID, state.Validators, privVals)
|
||||
stripSignatures(lastCommit)
|
||||
if testCase.expectPanic {
|
||||
require.Panics(t, func() {
|
||||
blockExec.CreateProposalBlock(ctx, testCase.height, state, lastCommit, pa) //nolint:errcheck
|
||||
})
|
||||
} else {
|
||||
_, err = blockExec.CreateProposalBlock(ctx, testCase.height, state, lastCommit, pa)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func stripSignatures(ec *types.ExtendedCommit) {
|
||||
for i, commitSig := range ec.ExtendedSignatures {
|
||||
commitSig.Extension = nil
|
||||
commitSig.ExtensionSignature = nil
|
||||
ec.ExtendedSignatures[i] = commitSig
|
||||
}
|
||||
}
|
||||
|
||||
func makeBlockID(hash []byte, partSetSize uint32, partSetHash []byte) types.BlockID {
|
||||
var (
|
||||
h = make([]byte, crypto.HashSize)
|
||||
|
||||
@@ -39,7 +39,7 @@ func makeAndCommitGoodBlock(
|
||||
blockExec *sm.BlockExecutor,
|
||||
privVals map[string]types.PrivValidator,
|
||||
evidence []types.Evidence,
|
||||
) (sm.State, types.BlockID, *types.Commit) {
|
||||
) (sm.State, types.BlockID, *types.ExtendedCommit) {
|
||||
t.Helper()
|
||||
|
||||
// A good block passes
|
||||
@@ -82,19 +82,23 @@ func makeValidCommit(
|
||||
blockID types.BlockID,
|
||||
vals *types.ValidatorSet,
|
||||
privVals map[string]types.PrivValidator,
|
||||
) (*types.Commit, []*types.Vote) {
|
||||
) (*types.ExtendedCommit, []*types.Vote) {
|
||||
t.Helper()
|
||||
sigs := make([]types.CommitSig, vals.Size())
|
||||
sigs := make([]types.ExtendedCommitSig, vals.Size())
|
||||
votes := make([]*types.Vote, vals.Size())
|
||||
for i := 0; i < vals.Size(); i++ {
|
||||
_, val := vals.GetByIndex(int32(i))
|
||||
vote, err := factory.MakeVote(ctx, privVals[val.Address.String()], chainID, int32(i), height, 0, 2, blockID, time.Now())
|
||||
require.NoError(t, err)
|
||||
sigs[i] = vote.CommitSig()
|
||||
sigs[i] = vote.ExtendedCommitSig()
|
||||
votes[i] = vote
|
||||
}
|
||||
|
||||
return types.NewCommit(height, 0, blockID, sigs), votes
|
||||
return &types.ExtendedCommit{
|
||||
Height: height,
|
||||
BlockID: blockID,
|
||||
ExtendedSignatures: sigs,
|
||||
}, votes
|
||||
}
|
||||
|
||||
func makeState(t *testing.T, nVals, height int) (sm.State, dbm.DB, map[string]types.PrivValidator) {
|
||||
|
||||
@@ -65,7 +65,7 @@ func (idx *BlockerIndexer) Index(bh types.EventDataNewBlockHeader) error {
|
||||
}
|
||||
|
||||
// 2. index FinalizeBlock events
|
||||
if err := idx.indexEvents(batch, bh.ResultFinalizeBlock.Events, types.EventTypeFinalizeBlock, height); err != nil {
|
||||
if err := idx.indexEvents(batch, bh.ResultFinalizeBlock.Events, "finalize_block", height); err != nil {
|
||||
return fmt.Errorf("failed to index FinalizeBlock events: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// Code generated by metricsgen. DO NOT EDIT.
|
||||
|
||||
package indexer
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/metrics/discard"
|
||||
prometheus "github.com/go-kit/kit/metrics/prometheus"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics {
|
||||
labels := []string{}
|
||||
for i := 0; i < len(labelsAndValues); i += 2 {
|
||||
labels = append(labels, labelsAndValues[i])
|
||||
}
|
||||
return &Metrics{
|
||||
BlockEventsSeconds: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "block_events_seconds",
|
||||
Help: "Latency for indexing block events.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
TxEventsSeconds: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "tx_events_seconds",
|
||||
Help: "Latency for indexing transaction events.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
BlocksIndexed: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "blocks_indexed",
|
||||
Help: "Number of complete blocks indexed.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
TransactionsIndexed: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "transactions_indexed",
|
||||
Help: "Number of transactions indexed.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
}
|
||||
}
|
||||
|
||||
func NopMetrics() *Metrics {
|
||||
return &Metrics{
|
||||
BlockEventsSeconds: discard.NewHistogram(),
|
||||
TxEventsSeconds: discard.NewHistogram(),
|
||||
BlocksIndexed: discard.NewCounter(),
|
||||
TransactionsIndexed: discard.NewCounter(),
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,10 @@ package indexer
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/metrics"
|
||||
"github.com/go-kit/kit/metrics/discard"
|
||||
|
||||
prometheus "github.com/go-kit/kit/metrics/prometheus"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
//go:generate go run ../../../scripts/metricsgen -struct=Metrics
|
||||
|
||||
// MetricsSubsystem is a the subsystem label for the indexer package.
|
||||
const MetricsSubsystem = "indexer"
|
||||
|
||||
@@ -25,49 +23,3 @@ type Metrics struct {
|
||||
// Number of transactions indexed.
|
||||
TransactionsIndexed metrics.Counter
|
||||
}
|
||||
|
||||
// PrometheusMetrics returns Metrics build using Prometheus client library.
|
||||
// Optionally, labels can be provided along with their values ("foo",
|
||||
// "fooValue").
|
||||
func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics {
|
||||
labels := []string{}
|
||||
for i := 0; i < len(labelsAndValues); i += 2 {
|
||||
labels = append(labels, labelsAndValues[i])
|
||||
}
|
||||
return &Metrics{
|
||||
BlockEventsSeconds: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "block_events_seconds",
|
||||
Help: "Latency for indexing block events.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
TxEventsSeconds: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "tx_events_seconds",
|
||||
Help: "Latency for indexing transaction events.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
BlocksIndexed: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "blocks_indexed",
|
||||
Help: "Number of complete blocks indexed.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
TransactionsIndexed: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "transactions_indexed",
|
||||
Help: "Number of transactions indexed.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
}
|
||||
}
|
||||
|
||||
// NopMetrics returns an indexer metrics stub that discards all samples.
|
||||
func NopMetrics() *Metrics {
|
||||
return &Metrics{
|
||||
BlockEventsSeconds: discard.NewHistogram(),
|
||||
TxEventsSeconds: discard.NewHistogram(),
|
||||
BlocksIndexed: discard.NewCounter(),
|
||||
TransactionsIndexed: discard.NewCounter(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// Code generated by metricsgen. DO NOT EDIT.
|
||||
|
||||
package state
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/metrics/discard"
|
||||
prometheus "github.com/go-kit/kit/metrics/prometheus"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics {
|
||||
labels := []string{}
|
||||
for i := 0; i < len(labelsAndValues); i += 2 {
|
||||
labels = append(labels, labelsAndValues[i])
|
||||
}
|
||||
return &Metrics{
|
||||
BlockProcessingTime: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "block_processing_time",
|
||||
Help: "Time between BeginBlock and EndBlock.",
|
||||
|
||||
Buckets: stdprometheus.LinearBuckets(1, 10, 10),
|
||||
}, labels).With(labelsAndValues...),
|
||||
ConsensusParamUpdates: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "consensus_param_updates",
|
||||
Help: "Number of consensus parameter updates returned by the application since process start.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
ValidatorSetUpdates: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "validator_set_updates",
|
||||
Help: "Number of validator set updates returned by the application since process start.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
}
|
||||
}
|
||||
|
||||
func NopMetrics() *Metrics {
|
||||
return &Metrics{
|
||||
BlockProcessingTime: discard.NewHistogram(),
|
||||
ConsensusParamUpdates: discard.NewCounter(),
|
||||
ValidatorSetUpdates: discard.NewCounter(),
|
||||
}
|
||||
}
|
||||
+11
-28
@@ -2,9 +2,6 @@ package state
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/metrics"
|
||||
"github.com/go-kit/kit/metrics/discard"
|
||||
"github.com/go-kit/kit/metrics/prometheus"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -13,34 +10,20 @@ const (
|
||||
MetricsSubsystem = "state"
|
||||
)
|
||||
|
||||
//go:generate go run ../../scripts/metricsgen -struct=Metrics
|
||||
|
||||
// Metrics contains metrics exposed by this package.
|
||||
type Metrics struct {
|
||||
// Time between BeginBlock and EndBlock.
|
||||
BlockProcessingTime metrics.Histogram
|
||||
}
|
||||
BlockProcessingTime metrics.Histogram `metrics_buckettype:"lin" metrics_bucketsizes:"1,10,10"`
|
||||
|
||||
// PrometheusMetrics returns Metrics build using Prometheus client library.
|
||||
// Optionally, labels can be provided along with their values ("foo",
|
||||
// "fooValue").
|
||||
func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics {
|
||||
labels := []string{}
|
||||
for i := 0; i < len(labelsAndValues); i += 2 {
|
||||
labels = append(labels, labelsAndValues[i])
|
||||
}
|
||||
return &Metrics{
|
||||
BlockProcessingTime: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "block_processing_time",
|
||||
Help: "Time between BeginBlock and EndBlock in ms.",
|
||||
Buckets: stdprometheus.LinearBuckets(1, 10, 10),
|
||||
}, labels).With(labelsAndValues...),
|
||||
}
|
||||
}
|
||||
// ConsensusParamUpdates is the total number of times the application has
|
||||
// udated the consensus params since process start.
|
||||
//metrics:Number of consensus parameter updates returned by the application since process start.
|
||||
ConsensusParamUpdates metrics.Counter
|
||||
|
||||
// NopMetrics returns no-op Metrics.
|
||||
func NopMetrics() *Metrics {
|
||||
return &Metrics{
|
||||
BlockProcessingTime: discard.NewHistogram(),
|
||||
}
|
||||
// ValidatorSetUpdates is the total number of times the application has
|
||||
// udated the validator set since process start.
|
||||
//metrics:Number of validator set updates returned by the application since process start.
|
||||
ValidatorSetUpdates metrics.Counter
|
||||
}
|
||||
|
||||
@@ -107,6 +107,22 @@ func (_m *BlockStore) LoadBlockCommit(height int64) *types.Commit {
|
||||
return r0
|
||||
}
|
||||
|
||||
// LoadBlockExtendedCommit provides a mock function with given fields: height
|
||||
func (_m *BlockStore) LoadBlockExtendedCommit(height int64) *types.ExtendedCommit {
|
||||
ret := _m.Called(height)
|
||||
|
||||
var r0 *types.ExtendedCommit
|
||||
if rf, ok := ret.Get(0).(func(int64) *types.ExtendedCommit); ok {
|
||||
r0 = rf(height)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*types.ExtendedCommit)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// LoadBlockMeta provides a mock function with given fields: height
|
||||
func (_m *BlockStore) LoadBlockMeta(height int64) *types.BlockMeta {
|
||||
ret := _m.Called(height)
|
||||
@@ -197,6 +213,11 @@ func (_m *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, s
|
||||
_m.Called(block, blockParts, seenCommit)
|
||||
}
|
||||
|
||||
// SaveBlockWithExtendedCommit provides a mock function with given fields: block, blockParts, seenCommit
|
||||
func (_m *BlockStore) SaveBlockWithExtendedCommit(block *types.Block, blockParts *types.PartSet, seenCommit *types.ExtendedCommit) {
|
||||
_m.Called(block, blockParts, seenCommit)
|
||||
}
|
||||
|
||||
// Size provides a mock function with given fields:
|
||||
func (_m *BlockStore) Size() int64 {
|
||||
ret := _m.Called()
|
||||
|
||||
@@ -27,6 +27,7 @@ type BlockStore interface {
|
||||
LoadBlock(height int64) *types.Block
|
||||
|
||||
SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit)
|
||||
SaveBlockWithExtendedCommit(block *types.Block, blockParts *types.PartSet, seenCommit *types.ExtendedCommit)
|
||||
|
||||
PruneBlocks(height int64) (uint64, error)
|
||||
|
||||
@@ -36,6 +37,7 @@ type BlockStore interface {
|
||||
|
||||
LoadBlockCommit(height int64) *types.Commit
|
||||
LoadSeenCommit() *types.Commit
|
||||
LoadBlockExtendedCommit(height int64) *types.ExtendedCommit
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
@@ -2,6 +2,7 @@ package state
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
@@ -26,6 +27,9 @@ const (
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
// NB: Before modifying these, cross-check them with those in
|
||||
// internal/store/store.go
|
||||
// TODO(thane): Move these and the ones in internal/store/store.go to their own package.
|
||||
const (
|
||||
// prefixes are unique across all tm db's
|
||||
prefixValidators = int64(5)
|
||||
@@ -56,6 +60,7 @@ func abciResponsesKey(height int64) []byte {
|
||||
|
||||
// stateKey should never change after being set in init()
|
||||
var stateKey []byte
|
||||
var tmpABCIKey []byte
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
@@ -63,6 +68,12 @@ func init() {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// temporary extra key before consensus param protos are regenerated
|
||||
// TODO(wbanfield) remove in next PR
|
||||
tmpABCIKey, err = orderedcode.Append(nil, int64(10000))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------
|
||||
@@ -134,6 +145,12 @@ func (store dbStore) loadState(key []byte) (state State, err error) {
|
||||
if err != nil {
|
||||
return state, err
|
||||
}
|
||||
buf, err = store.db.Get(tmpABCIKey)
|
||||
if err != nil {
|
||||
return state, err
|
||||
}
|
||||
h, _ := binary.Varint(buf)
|
||||
sm.ConsensusParams.ABCI.VoteExtensionsEnableHeight = h
|
||||
|
||||
return *sm, nil
|
||||
}
|
||||
@@ -178,6 +195,11 @@ func (store dbStore) save(state State, key []byte) error {
|
||||
if err := batch.Set(key, stateBz); err != nil {
|
||||
return err
|
||||
}
|
||||
bz := make([]byte, 5)
|
||||
binary.PutVarint(bz, state.ConsensusParams.ABCI.VoteExtensionsEnableHeight)
|
||||
if err := batch.Set(tmpABCIKey, bz); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return batch.WriteSync()
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ func makeBlockAndPartSet(
|
||||
) (*types.Block, *types.PartSet) {
|
||||
t.Helper()
|
||||
|
||||
lastCommit := types.NewCommit(height-1, 0, types.BlockID{}, nil)
|
||||
lastCommit := &types.Commit{Height: height - 1}
|
||||
if height > 1 {
|
||||
vote, err := factory.MakeVote(
|
||||
ctx,
|
||||
@@ -73,8 +73,12 @@ func makeBlockAndPartSet(
|
||||
lastBlockMeta.BlockID,
|
||||
time.Now())
|
||||
require.NoError(t, err)
|
||||
lastCommit = types.NewCommit(vote.Height, vote.Round,
|
||||
lastBlockMeta.BlockID, []types.CommitSig{vote.CommitSig()})
|
||||
lastCommit = &types.Commit{
|
||||
Height: vote.Height,
|
||||
Round: vote.Round,
|
||||
BlockID: lastBlock.LastBlockID,
|
||||
Signatures: []types.CommitSig{vote.CommitSig()},
|
||||
}
|
||||
}
|
||||
|
||||
block := state.MakeBlock(height, []types.Tx{}, lastCommit, nil, state.Validators.GetProposer().Address)
|
||||
|
||||
@@ -65,7 +65,8 @@ func TestValidateBlockHeader(t *testing.T) {
|
||||
eventBus,
|
||||
sm.NopMetrics(),
|
||||
)
|
||||
lastCommit := types.NewCommit(0, 0, types.BlockID{}, nil)
|
||||
lastCommit := &types.Commit{}
|
||||
var lastExtCommit *types.ExtendedCommit
|
||||
|
||||
// some bad values
|
||||
wrongHash := crypto.Checksum([]byte("this hash is wrong"))
|
||||
@@ -100,7 +101,7 @@ func TestValidateBlockHeader(t *testing.T) {
|
||||
{"Proposer invalid", func(block *types.Block) { block.ProposerAddress = []byte("wrong size") }},
|
||||
|
||||
{"first LastCommit contains signatures", func(block *types.Block) {
|
||||
block.LastCommit = types.NewCommit(0, 0, types.BlockID{}, []types.CommitSig{types.NewCommitSigAbsent()})
|
||||
block.LastCommit = &types.Commit{Signatures: []types.CommitSig{types.NewCommitSigAbsent()}}
|
||||
block.LastCommitHash = block.LastCommit.Hash()
|
||||
}},
|
||||
}
|
||||
@@ -121,8 +122,9 @@ func TestValidateBlockHeader(t *testing.T) {
|
||||
/*
|
||||
A good block passes
|
||||
*/
|
||||
state, _, lastCommit = makeAndCommitGoodBlock(ctx, t,
|
||||
state, _, lastExtCommit = makeAndCommitGoodBlock(ctx, t,
|
||||
state, height, lastCommit, state.Validators.GetProposer().Address, blockExec, privVals, nil)
|
||||
lastCommit = lastExtCommit.ToCommit()
|
||||
}
|
||||
|
||||
nextHeight := validationTestsStopHeight
|
||||
@@ -169,8 +171,9 @@ func TestValidateBlockCommit(t *testing.T) {
|
||||
eventBus,
|
||||
sm.NopMetrics(),
|
||||
)
|
||||
lastCommit := types.NewCommit(0, 0, types.BlockID{}, nil)
|
||||
wrongSigsCommit := types.NewCommit(1, 0, types.BlockID{}, nil)
|
||||
lastCommit := &types.Commit{}
|
||||
var lastExtCommit *types.ExtendedCommit
|
||||
wrongSigsCommit := &types.Commit{Height: 1}
|
||||
badPrivVal := types.NewMockPV()
|
||||
|
||||
for height := int64(1); height < validationTestsStopHeight; height++ {
|
||||
@@ -192,12 +195,12 @@ func TestValidateBlockCommit(t *testing.T) {
|
||||
time.Now(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
wrongHeightCommit := types.NewCommit(
|
||||
wrongHeightVote.Height,
|
||||
wrongHeightVote.Round,
|
||||
state.LastBlockID,
|
||||
[]types.CommitSig{wrongHeightVote.CommitSig()},
|
||||
)
|
||||
wrongHeightCommit := &types.Commit{
|
||||
Height: wrongHeightVote.Height,
|
||||
Round: wrongHeightVote.Round,
|
||||
BlockID: state.LastBlockID,
|
||||
Signatures: []types.CommitSig{wrongHeightVote.CommitSig()},
|
||||
}
|
||||
block := statefactory.MakeBlock(state, height, wrongHeightCommit)
|
||||
err = blockExec.ValidateBlock(ctx, state, block)
|
||||
_, isErrInvalidCommitHeight := err.(types.ErrInvalidCommitHeight)
|
||||
@@ -220,7 +223,7 @@ func TestValidateBlockCommit(t *testing.T) {
|
||||
A good block passes
|
||||
*/
|
||||
var blockID types.BlockID
|
||||
state, blockID, lastCommit = makeAndCommitGoodBlock(
|
||||
state, blockID, lastExtCommit = makeAndCommitGoodBlock(
|
||||
ctx,
|
||||
t,
|
||||
state,
|
||||
@@ -231,6 +234,7 @@ func TestValidateBlockCommit(t *testing.T) {
|
||||
privVals,
|
||||
nil,
|
||||
)
|
||||
lastCommit = lastExtCommit.ToCommit()
|
||||
|
||||
/*
|
||||
wrongSigsCommit is fine except for the extra bad precommit
|
||||
@@ -270,8 +274,12 @@ func TestValidateBlockCommit(t *testing.T) {
|
||||
|
||||
goodVote.Signature, badVote.Signature = g.Signature, b.Signature
|
||||
|
||||
wrongSigsCommit = types.NewCommit(goodVote.Height, goodVote.Round,
|
||||
blockID, []types.CommitSig{goodVote.CommitSig(), badVote.CommitSig()})
|
||||
wrongSigsCommit = &types.Commit{
|
||||
Height: goodVote.Height,
|
||||
Round: goodVote.Round,
|
||||
BlockID: blockID,
|
||||
Signatures: []types.CommitSig{goodVote.CommitSig(), badVote.CommitSig()},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,7 +327,8 @@ func TestValidateBlockEvidence(t *testing.T) {
|
||||
eventBus,
|
||||
sm.NopMetrics(),
|
||||
)
|
||||
lastCommit := types.NewCommit(0, 0, types.BlockID{}, nil)
|
||||
lastCommit := &types.Commit{}
|
||||
var lastExtCommit *types.ExtendedCommit
|
||||
|
||||
for height := int64(1); height < validationTestsStopHeight; height++ {
|
||||
proposerAddr := state.Validators.GetProposer().Address
|
||||
@@ -364,7 +373,7 @@ func TestValidateBlockEvidence(t *testing.T) {
|
||||
evidence = append(evidence, newEv)
|
||||
}
|
||||
|
||||
state, _, lastCommit = makeAndCommitGoodBlock(
|
||||
state, _, lastExtCommit = makeAndCommitGoodBlock(
|
||||
ctx,
|
||||
t,
|
||||
state,
|
||||
@@ -375,6 +384,7 @@ func TestValidateBlockEvidence(t *testing.T) {
|
||||
privVals,
|
||||
evidence,
|
||||
)
|
||||
lastCommit = lastExtCommit.ToCommit()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
// Code generated by metricsgen. DO NOT EDIT.
|
||||
|
||||
package statesync
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/metrics/discard"
|
||||
prometheus "github.com/go-kit/kit/metrics/prometheus"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics {
|
||||
labels := []string{}
|
||||
for i := 0; i < len(labelsAndValues); i += 2 {
|
||||
labels = append(labels, labelsAndValues[i])
|
||||
}
|
||||
return &Metrics{
|
||||
TotalSnapshots: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "total_snapshots",
|
||||
Help: "The total number of snapshots discovered.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
ChunkProcessAvgTime: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "chunk_process_avg_time",
|
||||
Help: "The average processing time per chunk.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
SnapshotHeight: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "snapshot_height",
|
||||
Help: "The height of the current snapshot the has been processed.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
SnapshotChunk: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "snapshot_chunk",
|
||||
Help: "The current number of chunks that have been processed.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
SnapshotChunkTotal: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "snapshot_chunk_total",
|
||||
Help: "The total number of chunks in the current snapshot.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
BackFilledBlocks: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "back_filled_blocks",
|
||||
Help: "The current number of blocks that have been back-filled.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
BackFillBlocksTotal: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "back_fill_blocks_total",
|
||||
Help: "The total number of blocks that need to be back-filled.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
}
|
||||
}
|
||||
|
||||
func NopMetrics() *Metrics {
|
||||
return &Metrics{
|
||||
TotalSnapshots: discard.NewCounter(),
|
||||
ChunkProcessAvgTime: discard.NewGauge(),
|
||||
SnapshotHeight: discard.NewGauge(),
|
||||
SnapshotChunk: discard.NewCounter(),
|
||||
SnapshotChunkTotal: discard.NewGauge(),
|
||||
BackFilledBlocks: discard.NewCounter(),
|
||||
BackFillBlocksTotal: discard.NewGauge(),
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,6 @@ package statesync
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/metrics"
|
||||
"github.com/go-kit/kit/metrics/discard"
|
||||
"github.com/go-kit/kit/metrics/prometheus"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -12,80 +9,22 @@ const (
|
||||
MetricsSubsystem = "statesync"
|
||||
)
|
||||
|
||||
//go:generate go run ../../scripts/metricsgen -struct=Metrics
|
||||
|
||||
// Metrics contains metrics exposed by this package.
|
||||
type Metrics struct {
|
||||
TotalSnapshots metrics.Counter
|
||||
// The total number of snapshots discovered.
|
||||
TotalSnapshots metrics.Counter
|
||||
// The average processing time per chunk.
|
||||
ChunkProcessAvgTime metrics.Gauge
|
||||
SnapshotHeight metrics.Gauge
|
||||
SnapshotChunk metrics.Counter
|
||||
SnapshotChunkTotal metrics.Gauge
|
||||
BackFilledBlocks metrics.Counter
|
||||
// The height of the current snapshot the has been processed.
|
||||
SnapshotHeight metrics.Gauge
|
||||
// The current number of chunks that have been processed.
|
||||
SnapshotChunk metrics.Counter
|
||||
// The total number of chunks in the current snapshot.
|
||||
SnapshotChunkTotal metrics.Gauge
|
||||
// The current number of blocks that have been back-filled.
|
||||
BackFilledBlocks metrics.Counter
|
||||
// The total number of blocks that need to be back-filled.
|
||||
BackFillBlocksTotal metrics.Gauge
|
||||
}
|
||||
|
||||
// PrometheusMetrics returns Metrics build using Prometheus client library.
|
||||
// Optionally, labels can be provided along with their values ("foo",
|
||||
// "fooValue").
|
||||
func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics {
|
||||
labels := []string{}
|
||||
for i := 0; i < len(labelsAndValues); i += 2 {
|
||||
labels = append(labels, labelsAndValues[i])
|
||||
}
|
||||
return &Metrics{
|
||||
TotalSnapshots: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "total_snapshots",
|
||||
Help: "The total number of snapshots discovered.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
ChunkProcessAvgTime: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "chunk_process_avg_time",
|
||||
Help: "The average processing time per chunk.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
SnapshotHeight: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "snapshot_height",
|
||||
Help: "The height of the current snapshot the has been processed.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
SnapshotChunk: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "snapshot_chunk",
|
||||
Help: "The current number of chunks that have been processed.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
SnapshotChunkTotal: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "snapshot_chunks_total",
|
||||
Help: "The total number of chunks in the current snapshot.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
BackFilledBlocks: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "backfilled_blocks",
|
||||
Help: "The current number of blocks that have been back-filled.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
BackFillBlocksTotal: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "backfilled_blocks_total",
|
||||
Help: "The total number of blocks that need to be back-filled.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
}
|
||||
}
|
||||
|
||||
// NopMetrics returns no-op Metrics.
|
||||
func NopMetrics() *Metrics {
|
||||
return &Metrics{
|
||||
TotalSnapshots: discard.NewCounter(),
|
||||
ChunkProcessAvgTime: discard.NewGauge(),
|
||||
SnapshotHeight: discard.NewGauge(),
|
||||
SnapshotChunk: discard.NewCounter(),
|
||||
SnapshotChunkTotal: discard.NewGauge(),
|
||||
BackFilledBlocks: discard.NewCounter(),
|
||||
BackFillBlocksTotal: discard.NewGauge(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -855,13 +855,13 @@ func mockLB(ctx context.Context, t *testing.T, height int64, time time.Time, las
|
||||
header.NextValidatorsHash = nextVals.Hash()
|
||||
header.ConsensusHash = types.DefaultConsensusParams().HashConsensusParams()
|
||||
lastBlockID = factory.MakeBlockIDWithHash(header.Hash())
|
||||
voteSet := types.NewVoteSet(factory.DefaultTestChainID, height, 0, tmproto.PrecommitType, currentVals)
|
||||
commit, err := factory.MakeCommit(ctx, lastBlockID, height, 0, voteSet, currentPrivVals, time)
|
||||
voteSet := types.NewExtendedVoteSet(factory.DefaultTestChainID, height, 0, tmproto.PrecommitType, currentVals)
|
||||
extCommit, err := factory.MakeExtendedCommit(ctx, lastBlockID, height, 0, voteSet, currentPrivVals, time)
|
||||
require.NoError(t, err)
|
||||
return nextVals, nextPrivVals, &types.LightBlock{
|
||||
SignedHeader: &types.SignedHeader{
|
||||
Header: header,
|
||||
Commit: commit,
|
||||
Commit: extCommit.ToCommit(),
|
||||
},
|
||||
ValidatorSet: currentVals,
|
||||
}
|
||||
|
||||
+99
-17
@@ -2,6 +2,7 @@ package store
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
@@ -273,11 +274,34 @@ func (bs *BlockStore) LoadBlockCommit(height int64) *types.Commit {
|
||||
}
|
||||
commit, err := types.CommitFromProto(pbc)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("error reading block commit: %w", err))
|
||||
panic(fmt.Errorf("converting commit to proto: %w", err))
|
||||
}
|
||||
return commit
|
||||
}
|
||||
|
||||
// LoadExtendedCommit returns the ExtendedCommit for the given height.
|
||||
// The extended commit is not guaranteed to contain the same +2/3 precommits data
|
||||
// as the commit in the block.
|
||||
func (bs *BlockStore) LoadBlockExtendedCommit(height int64) *types.ExtendedCommit {
|
||||
pbec := new(tmproto.ExtendedCommit)
|
||||
bz, err := bs.db.Get(extCommitKey(height))
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("fetching extended commit: %w", err))
|
||||
}
|
||||
if len(bz) == 0 {
|
||||
return nil
|
||||
}
|
||||
err = proto.Unmarshal(bz, pbec)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("decoding extended commit: %w", err))
|
||||
}
|
||||
extCommit, err := types.ExtendedCommitFromProto(pbec)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("converting extended commit: %w", err))
|
||||
}
|
||||
return extCommit
|
||||
}
|
||||
|
||||
// LoadSeenCommit returns the last locally seen Commit before being
|
||||
// cannonicalized. This is useful when we've seen a commit, but there
|
||||
// has not yet been a new block at `height + 1` that includes this
|
||||
@@ -298,7 +322,7 @@ func (bs *BlockStore) LoadSeenCommit() *types.Commit {
|
||||
|
||||
commit, err := types.CommitFromProto(pbc)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("error from proto commit: %w", err))
|
||||
panic(fmt.Errorf("converting seen commit: %w", err))
|
||||
}
|
||||
return commit
|
||||
}
|
||||
@@ -450,17 +474,69 @@ func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, s
|
||||
if block == nil {
|
||||
panic("BlockStore can only save a non-nil block")
|
||||
}
|
||||
|
||||
batch := bs.db.NewBatch()
|
||||
if err := bs.saveBlockToBatch(batch, block, blockParts, seenCommit); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := batch.WriteSync(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := batch.Close(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// SaveBlockWithExtendedCommit persists the given block, blockParts, and
|
||||
// seenExtendedCommit to the underlying db. seenExtendedCommit is stored under
|
||||
// two keys in the database: as the seenCommit and as the ExtendedCommit data for the
|
||||
// height. This allows the vote extension data to be persisted for all blocks
|
||||
// that are saved.
|
||||
func (bs *BlockStore) SaveBlockWithExtendedCommit(block *types.Block, blockParts *types.PartSet, seenExtendedCommit *types.ExtendedCommit) {
|
||||
if block == nil {
|
||||
panic("BlockStore can only save a non-nil block")
|
||||
}
|
||||
if err := seenExtendedCommit.EnsureExtensions(); err != nil {
|
||||
panic(fmt.Errorf("saving block with extensions: %w", err))
|
||||
}
|
||||
batch := bs.db.NewBatch()
|
||||
if err := bs.saveBlockToBatch(batch, block, blockParts, seenExtendedCommit.ToCommit()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
height := block.Height
|
||||
|
||||
pbec := seenExtendedCommit.ToProto()
|
||||
extCommitBytes := mustEncode(pbec)
|
||||
if err := batch.Set(extCommitKey(height), extCommitBytes); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := batch.WriteSync(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := batch.Close(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (bs *BlockStore) saveBlockToBatch(batch dbm.Batch, block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) error {
|
||||
if block == nil {
|
||||
panic("BlockStore can only save a non-nil block")
|
||||
}
|
||||
|
||||
height := block.Height
|
||||
hash := block.Hash()
|
||||
|
||||
if g, w := height, bs.Height()+1; bs.Base() > 0 && g != w {
|
||||
panic(fmt.Sprintf("BlockStore can only save contiguous blocks. Wanted %v, got %v", w, g))
|
||||
return fmt.Errorf("BlockStore can only save contiguous blocks. Wanted %v, got %v", w, g)
|
||||
}
|
||||
if !blockParts.IsComplete() {
|
||||
panic("BlockStore can only save complete block part sets")
|
||||
return errors.New("BlockStore can only save complete block part sets")
|
||||
}
|
||||
if height != seenCommit.Height {
|
||||
return fmt.Errorf("BlockStore cannot save seen commit of a different height (block: %d, commit: %d)", height, seenCommit.Height)
|
||||
}
|
||||
|
||||
// Save block parts. This must be done before the block meta, since callers
|
||||
@@ -475,38 +551,32 @@ func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, s
|
||||
blockMeta := types.NewBlockMeta(block, blockParts)
|
||||
pbm := blockMeta.ToProto()
|
||||
if pbm == nil {
|
||||
panic("nil blockmeta")
|
||||
return errors.New("nil blockmeta")
|
||||
}
|
||||
|
||||
metaBytes := mustEncode(pbm)
|
||||
if err := batch.Set(blockMetaKey(height), metaBytes); err != nil {
|
||||
panic(err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := batch.Set(blockHashKey(hash), []byte(fmt.Sprintf("%d", height))); err != nil {
|
||||
panic(err)
|
||||
return err
|
||||
}
|
||||
|
||||
pbc := block.LastCommit.ToProto()
|
||||
blockCommitBytes := mustEncode(pbc)
|
||||
if err := batch.Set(blockCommitKey(height-1), blockCommitBytes); err != nil {
|
||||
panic(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Save seen commit (seen +2/3 precommits for block)
|
||||
pbsc := seenCommit.ToProto()
|
||||
seenCommitBytes := mustEncode(pbsc)
|
||||
if err := batch.Set(seenCommitKey(), seenCommitBytes); err != nil {
|
||||
panic(err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := batch.WriteSync(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := batch.Close(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bs *BlockStore) saveBlockPart(height int64, index int, part *types.Part, batch dbm.Batch) {
|
||||
@@ -579,6 +649,9 @@ func (bs *BlockStore) Close() error {
|
||||
//---------------------------------- KEY ENCODING -----------------------------------------
|
||||
|
||||
// key prefixes
|
||||
// NB: Before modifying these, cross-check them with those in
|
||||
// internal/state/store.go
|
||||
// TODO(thane): Move these and the ones in internal/state/store.go to their own package.
|
||||
const (
|
||||
// prefixes are unique across all tm db's
|
||||
prefixBlockMeta = int64(0)
|
||||
@@ -586,6 +659,7 @@ const (
|
||||
prefixBlockCommit = int64(2)
|
||||
prefixSeenCommit = int64(3)
|
||||
prefixBlockHash = int64(4)
|
||||
prefixExtCommit = int64(9) // 5..8 are used by state/store
|
||||
)
|
||||
|
||||
func blockMetaKey(height int64) []byte {
|
||||
@@ -635,6 +709,14 @@ func seenCommitKey() []byte {
|
||||
return key
|
||||
}
|
||||
|
||||
func extCommitKey(height int64) []byte {
|
||||
key, err := orderedcode.Append(nil, prefixExtCommit, height)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func blockHashKey(hash []byte) []byte {
|
||||
key, err := orderedcode.Append(nil, prefixBlockHash, string(hash))
|
||||
if err != nil {
|
||||
|
||||
+156
-88
@@ -2,7 +2,6 @@ package store
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
stdlog "log"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
@@ -27,22 +26,26 @@ import (
|
||||
// test.
|
||||
type cleanupFunc func()
|
||||
|
||||
// make a Commit with a single vote containing just the height and a timestamp
|
||||
func makeTestCommit(height int64, timestamp time.Time) *types.Commit {
|
||||
commitSigs := []types.CommitSig{{
|
||||
BlockIDFlag: types.BlockIDFlagCommit,
|
||||
ValidatorAddress: tmrand.Bytes(crypto.AddressSize),
|
||||
Timestamp: timestamp,
|
||||
Signature: []byte("Signature"),
|
||||
// make an extended commit with a single vote containing just the height and a
|
||||
// timestamp
|
||||
func makeTestExtCommit(height int64, timestamp time.Time) *types.ExtendedCommit {
|
||||
extCommitSigs := []types.ExtendedCommitSig{{
|
||||
CommitSig: types.CommitSig{
|
||||
BlockIDFlag: types.BlockIDFlagCommit,
|
||||
ValidatorAddress: tmrand.Bytes(crypto.AddressSize),
|
||||
Timestamp: timestamp,
|
||||
Signature: []byte("Signature"),
|
||||
},
|
||||
ExtensionSignature: []byte("ExtensionSignature"),
|
||||
}}
|
||||
return types.NewCommit(
|
||||
height,
|
||||
0,
|
||||
types.BlockID{
|
||||
return &types.ExtendedCommit{
|
||||
Height: height,
|
||||
BlockID: types.BlockID{
|
||||
Hash: crypto.CRandBytes(32),
|
||||
PartSetHeader: types.PartSetHeader{Hash: crypto.CRandBytes(32), Total: 2},
|
||||
},
|
||||
commitSigs)
|
||||
ExtendedSignatures: extCommitSigs,
|
||||
}
|
||||
}
|
||||
|
||||
func makeStateAndBlockStore(dir string) (sm.State, *BlockStore, cleanupFunc, error) {
|
||||
@@ -59,47 +62,11 @@ func makeStateAndBlockStore(dir string) (sm.State, *BlockStore, cleanupFunc, err
|
||||
return state, NewBlockStore(blockDB), func() { os.RemoveAll(cfg.RootDir) }, nil
|
||||
}
|
||||
|
||||
func freshBlockStore() (*BlockStore, dbm.DB) {
|
||||
func newInMemoryBlockStore() (*BlockStore, dbm.DB) {
|
||||
db := dbm.NewMemDB()
|
||||
return NewBlockStore(db), db
|
||||
}
|
||||
|
||||
var (
|
||||
state sm.State
|
||||
block *types.Block
|
||||
partSet *types.PartSet
|
||||
part1 *types.Part
|
||||
part2 *types.Part
|
||||
seenCommit1 *types.Commit
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
dir, err := os.MkdirTemp("", "store_test")
|
||||
if err != nil {
|
||||
stdlog.Fatal(err)
|
||||
}
|
||||
var cleanup cleanupFunc
|
||||
|
||||
state, _, cleanup, err = makeStateAndBlockStore(dir)
|
||||
if err != nil {
|
||||
stdlog.Fatal(err)
|
||||
}
|
||||
|
||||
block = factory.MakeBlock(state, 1, new(types.Commit))
|
||||
|
||||
partSet, err = block.MakePartSet(2)
|
||||
if err != nil {
|
||||
stdlog.Fatal(err)
|
||||
}
|
||||
part1 = partSet.GetPart(0)
|
||||
part2 = partSet.GetPart(1)
|
||||
seenCommit1 = makeTestCommit(10, tmtime.Now())
|
||||
code := m.Run()
|
||||
cleanup()
|
||||
os.RemoveAll(dir) // best-effort
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
// TODO: This test should be simplified ...
|
||||
func TestBlockStoreSaveLoadBlock(t *testing.T) {
|
||||
state, bs, cleanup, err := makeStateAndBlockStore(t.TempDir())
|
||||
@@ -120,8 +87,10 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
|
||||
block := factory.MakeBlock(state, bs.Height()+1, new(types.Commit))
|
||||
validPartSet, err := block.MakePartSet(2)
|
||||
require.NoError(t, err)
|
||||
seenCommit := makeTestCommit(10, tmtime.Now())
|
||||
bs.SaveBlock(block, partSet, seenCommit)
|
||||
part2 := validPartSet.GetPart(1)
|
||||
|
||||
seenCommit := makeTestExtCommit(block.Header.Height, tmtime.Now())
|
||||
bs.SaveBlockWithExtendedCommit(block, validPartSet, seenCommit)
|
||||
require.EqualValues(t, 1, bs.Base(), "expecting the new height to be changed")
|
||||
require.EqualValues(t, block.Header.Height, bs.Height(), "expecting the new height to be changed")
|
||||
|
||||
@@ -139,11 +108,11 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
|
||||
}
|
||||
|
||||
// End of setup, test data
|
||||
commitAtH10 := makeTestCommit(10, tmtime.Now())
|
||||
commitAtH10 := makeTestExtCommit(10, tmtime.Now()).ToCommit()
|
||||
tuples := []struct {
|
||||
block *types.Block
|
||||
parts *types.PartSet
|
||||
seenCommit *types.Commit
|
||||
seenCommit *types.ExtendedCommit
|
||||
wantPanic string
|
||||
wantErr bool
|
||||
|
||||
@@ -156,7 +125,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
|
||||
{
|
||||
block: newBlock(header1, commitAtH10),
|
||||
parts: validPartSet,
|
||||
seenCommit: seenCommit1,
|
||||
seenCommit: seenCommit,
|
||||
},
|
||||
|
||||
{
|
||||
@@ -172,22 +141,23 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
|
||||
ChainID: "block_test",
|
||||
Time: tmtime.Now(),
|
||||
ProposerAddress: tmrand.Bytes(crypto.AddressSize)},
|
||||
makeTestCommit(5, tmtime.Now()),
|
||||
makeTestExtCommit(5, tmtime.Now()).ToCommit(),
|
||||
),
|
||||
parts: validPartSet,
|
||||
seenCommit: makeTestCommit(5, tmtime.Now()),
|
||||
seenCommit: makeTestExtCommit(5, tmtime.Now()),
|
||||
},
|
||||
|
||||
{
|
||||
block: newBlock(header1, commitAtH10),
|
||||
parts: incompletePartSet,
|
||||
wantPanic: "only save complete block", // incomplete parts
|
||||
block: newBlock(header1, commitAtH10),
|
||||
parts: incompletePartSet,
|
||||
wantPanic: "only save complete block", // incomplete parts
|
||||
seenCommit: makeTestExtCommit(10, tmtime.Now()),
|
||||
},
|
||||
|
||||
{
|
||||
block: newBlock(header1, commitAtH10),
|
||||
parts: validPartSet,
|
||||
seenCommit: seenCommit1,
|
||||
seenCommit: seenCommit,
|
||||
corruptCommitInDB: true, // Corrupt the DB's commit entry
|
||||
wantPanic: "error reading block commit",
|
||||
},
|
||||
@@ -195,7 +165,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
|
||||
{
|
||||
block: newBlock(header1, commitAtH10),
|
||||
parts: validPartSet,
|
||||
seenCommit: seenCommit1,
|
||||
seenCommit: seenCommit,
|
||||
wantPanic: "unmarshal to tmproto.BlockMeta",
|
||||
corruptBlockInDB: true, // Corrupt the DB's block entry
|
||||
},
|
||||
@@ -203,25 +173,25 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
|
||||
{
|
||||
block: newBlock(header1, commitAtH10),
|
||||
parts: validPartSet,
|
||||
seenCommit: seenCommit1,
|
||||
seenCommit: seenCommit,
|
||||
|
||||
// Expecting no error and we want a nil back
|
||||
eraseSeenCommitInDB: true,
|
||||
},
|
||||
|
||||
{
|
||||
block: newBlock(header1, commitAtH10),
|
||||
block: block,
|
||||
parts: validPartSet,
|
||||
seenCommit: seenCommit1,
|
||||
seenCommit: seenCommit,
|
||||
|
||||
corruptSeenCommitInDB: true,
|
||||
wantPanic: "error reading block seen commit",
|
||||
},
|
||||
|
||||
{
|
||||
block: newBlock(header1, commitAtH10),
|
||||
block: block,
|
||||
parts: validPartSet,
|
||||
seenCommit: seenCommit1,
|
||||
seenCommit: seenCommit,
|
||||
|
||||
// Expecting no error and we want a nil back
|
||||
eraseCommitInDB: true,
|
||||
@@ -238,10 +208,10 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
|
||||
|
||||
for i, tuple := range tuples {
|
||||
tuple := tuple
|
||||
bs, db := freshBlockStore()
|
||||
bs, db := newInMemoryBlockStore()
|
||||
// SaveBlock
|
||||
res, err, panicErr := doFn(func() (interface{}, error) {
|
||||
bs.SaveBlock(tuple.block, tuple.parts, tuple.seenCommit)
|
||||
bs.SaveBlockWithExtendedCommit(tuple.block, tuple.parts, tuple.seenCommit)
|
||||
if tuple.block == nil {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -311,6 +281,90 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestSaveBlockWithExtendedCommitPanicOnAbsentExtension tests that saving a
|
||||
// block with an extended commit panics when the extension data is absent.
|
||||
func TestSaveBlockWithExtendedCommitPanicOnAbsentExtension(t *testing.T) {
|
||||
for _, testCase := range []struct {
|
||||
name string
|
||||
malleateCommit func(*types.ExtendedCommit)
|
||||
shouldPanic bool
|
||||
}{
|
||||
{
|
||||
name: "basic save",
|
||||
malleateCommit: func(_ *types.ExtendedCommit) {},
|
||||
shouldPanic: false,
|
||||
},
|
||||
{
|
||||
name: "save commit with no extensions",
|
||||
malleateCommit: func(c *types.ExtendedCommit) {
|
||||
c.StripExtensions()
|
||||
},
|
||||
shouldPanic: true,
|
||||
},
|
||||
} {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
state, bs, cleanup, err := makeStateAndBlockStore(t.TempDir())
|
||||
require.NoError(t, err)
|
||||
defer cleanup()
|
||||
block := factory.MakeBlock(state, bs.Height()+1, new(types.Commit))
|
||||
seenCommit := makeTestExtCommit(block.Header.Height, tmtime.Now())
|
||||
ps, err := block.MakePartSet(2)
|
||||
require.NoError(t, err)
|
||||
testCase.malleateCommit(seenCommit)
|
||||
if testCase.shouldPanic {
|
||||
require.Panics(t, func() {
|
||||
bs.SaveBlockWithExtendedCommit(block, ps, seenCommit)
|
||||
})
|
||||
} else {
|
||||
bs.SaveBlockWithExtendedCommit(block, ps, seenCommit)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestLoadBlockExtendedCommit tests loading the extended commit for a previously
|
||||
// saved block. The load method should return nil when only a commit was saved and
|
||||
// return the extended commit otherwise.
|
||||
func TestLoadBlockExtendedCommit(t *testing.T) {
|
||||
for _, testCase := range []struct {
|
||||
name string
|
||||
saveExtended bool
|
||||
expectResult bool
|
||||
}{
|
||||
{
|
||||
name: "save commit",
|
||||
saveExtended: false,
|
||||
expectResult: false,
|
||||
},
|
||||
{
|
||||
name: "save extended commit",
|
||||
saveExtended: true,
|
||||
expectResult: true,
|
||||
},
|
||||
} {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
state, bs, cleanup, err := makeStateAndBlockStore(t.TempDir())
|
||||
require.NoError(t, err)
|
||||
defer cleanup()
|
||||
block := factory.MakeBlock(state, bs.Height()+1, new(types.Commit))
|
||||
seenCommit := makeTestExtCommit(block.Header.Height, tmtime.Now())
|
||||
ps, err := block.MakePartSet(2)
|
||||
require.NoError(t, err)
|
||||
if testCase.saveExtended {
|
||||
bs.SaveBlockWithExtendedCommit(block, ps, seenCommit)
|
||||
} else {
|
||||
bs.SaveBlock(block, ps, seenCommit.ToCommit())
|
||||
}
|
||||
res := bs.LoadBlockExtendedCommit(block.Height)
|
||||
if testCase.expectResult {
|
||||
require.Equal(t, seenCommit, res)
|
||||
} else {
|
||||
require.Nil(t, res)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadBaseMeta(t *testing.T) {
|
||||
cfg, err := config.ResetTestRoot(t.TempDir(), "blockchain_reactor_test")
|
||||
require.NoError(t, err)
|
||||
@@ -324,8 +378,8 @@ func TestLoadBaseMeta(t *testing.T) {
|
||||
block := factory.MakeBlock(state, h, new(types.Commit))
|
||||
partSet, err := block.MakePartSet(2)
|
||||
require.NoError(t, err)
|
||||
seenCommit := makeTestCommit(h, tmtime.Now())
|
||||
bs.SaveBlock(block, partSet, seenCommit)
|
||||
seenCommit := makeTestExtCommit(h, tmtime.Now())
|
||||
bs.SaveBlockWithExtendedCommit(block, partSet, seenCommit)
|
||||
}
|
||||
|
||||
pruned, err := bs.PruneBlocks(4)
|
||||
@@ -338,13 +392,19 @@ func TestLoadBaseMeta(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLoadBlockPart(t *testing.T) {
|
||||
bs, db := freshBlockStore()
|
||||
height, index := int64(10), 1
|
||||
cfg, err := config.ResetTestRoot(t.TempDir(), "blockchain_reactor_test")
|
||||
require.NoError(t, err)
|
||||
|
||||
bs, db := newInMemoryBlockStore()
|
||||
const height, index = 10, 1
|
||||
loadPart := func() (interface{}, error) {
|
||||
part := bs.LoadBlockPart(height, index)
|
||||
return part, nil
|
||||
}
|
||||
|
||||
state, err := sm.MakeGenesisStateFromFile(cfg.GenesisFile())
|
||||
require.NoError(t, err)
|
||||
|
||||
// Initially no contents.
|
||||
// 1. Requesting for a non-existent block shouldn't fail
|
||||
res, _, panicErr := doFn(loadPart)
|
||||
@@ -352,13 +412,18 @@ func TestLoadBlockPart(t *testing.T) {
|
||||
require.Nil(t, res, "a non-existent block part should return nil")
|
||||
|
||||
// 2. Next save a corrupted block then try to load it
|
||||
err := db.Set(blockPartKey(height, index), []byte("Tendermint"))
|
||||
err = db.Set(blockPartKey(height, index), []byte("Tendermint"))
|
||||
require.NoError(t, err)
|
||||
res, _, panicErr = doFn(loadPart)
|
||||
require.NotNil(t, panicErr, "expecting a non-nil panic")
|
||||
require.Contains(t, panicErr.Error(), "unmarshal to tmproto.Part failed")
|
||||
|
||||
// 3. A good block serialized and saved to the DB should be retrievable
|
||||
block := factory.MakeBlock(state, height, new(types.Commit))
|
||||
partSet, err := block.MakePartSet(2)
|
||||
require.NoError(t, err)
|
||||
part1 := partSet.GetPart(0)
|
||||
|
||||
pb1, err := part1.ToProto()
|
||||
require.NoError(t, err)
|
||||
err = db.Set(blockPartKey(height, index), mustEncode(pb1))
|
||||
@@ -391,8 +456,8 @@ func TestPruneBlocks(t *testing.T) {
|
||||
block := factory.MakeBlock(state, h, new(types.Commit))
|
||||
partSet, err := block.MakePartSet(2)
|
||||
require.NoError(t, err)
|
||||
seenCommit := makeTestCommit(h, tmtime.Now())
|
||||
bs.SaveBlock(block, partSet, seenCommit)
|
||||
seenCommit := makeTestExtCommit(h, tmtime.Now())
|
||||
bs.SaveBlockWithExtendedCommit(block, partSet, seenCommit)
|
||||
}
|
||||
|
||||
assert.EqualValues(t, 1, bs.Base())
|
||||
@@ -452,7 +517,7 @@ func TestPruneBlocks(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLoadBlockMeta(t *testing.T) {
|
||||
bs, db := freshBlockStore()
|
||||
bs, db := newInMemoryBlockStore()
|
||||
height := int64(10)
|
||||
loadMeta := func() (interface{}, error) {
|
||||
meta := bs.LoadBlockMeta(height)
|
||||
@@ -499,8 +564,8 @@ func TestBlockFetchAtHeight(t *testing.T) {
|
||||
|
||||
partSet, err := block.MakePartSet(2)
|
||||
require.NoError(t, err)
|
||||
seenCommit := makeTestCommit(10, tmtime.Now())
|
||||
bs.SaveBlock(block, partSet, seenCommit)
|
||||
seenCommit := makeTestExtCommit(block.Header.Height, tmtime.Now())
|
||||
bs.SaveBlockWithExtendedCommit(block, partSet, seenCommit)
|
||||
require.Equal(t, bs.Height(), block.Header.Height, "expecting the new height to be changed")
|
||||
|
||||
blockAtHeight := bs.LoadBlock(bs.Height())
|
||||
@@ -521,9 +586,12 @@ func TestBlockFetchAtHeight(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSeenAndCanonicalCommit(t *testing.T) {
|
||||
bs, _ := freshBlockStore()
|
||||
state, store, cleanup, err := makeStateAndBlockStore(t.TempDir())
|
||||
defer cleanup()
|
||||
require.NoError(t, err)
|
||||
|
||||
loadCommit := func() (interface{}, error) {
|
||||
meta := bs.LoadSeenCommit()
|
||||
meta := store.LoadSeenCommit()
|
||||
return meta, nil
|
||||
}
|
||||
|
||||
@@ -536,19 +604,19 @@ func TestSeenAndCanonicalCommit(t *testing.T) {
|
||||
// produce a few blocks and check that the correct seen and cannoncial commits
|
||||
// are persisted.
|
||||
for h := int64(3); h <= 5; h++ {
|
||||
blockCommit := makeTestCommit(h-1, tmtime.Now())
|
||||
blockCommit := makeTestExtCommit(h-1, tmtime.Now()).ToCommit()
|
||||
block := factory.MakeBlock(state, h, blockCommit)
|
||||
partSet, err := block.MakePartSet(2)
|
||||
require.NoError(t, err)
|
||||
seenCommit := makeTestCommit(h, tmtime.Now())
|
||||
bs.SaveBlock(block, partSet, seenCommit)
|
||||
c3 := bs.LoadSeenCommit()
|
||||
seenCommit := makeTestExtCommit(h, tmtime.Now())
|
||||
store.SaveBlockWithExtendedCommit(block, partSet, seenCommit)
|
||||
c3 := store.LoadSeenCommit()
|
||||
require.NotNil(t, c3)
|
||||
require.Equal(t, h, c3.Height)
|
||||
require.Equal(t, seenCommit.Hash(), c3.Hash())
|
||||
c5 := bs.LoadBlockCommit(h)
|
||||
require.Equal(t, seenCommit.ToCommit().Hash(), c3.Hash())
|
||||
c5 := store.LoadBlockCommit(h)
|
||||
require.Nil(t, c5)
|
||||
c6 := bs.LoadBlockCommit(h - 1)
|
||||
c6 := store.LoadBlockCommit(h - 1)
|
||||
require.Equal(t, blockCommit.Hash(), c6.Hash())
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
func MakeCommit(ctx context.Context, blockID types.BlockID, height int64, round int32, voteSet *types.VoteSet, validators []types.PrivValidator, now time.Time) (*types.Commit, error) {
|
||||
func MakeExtendedCommit(ctx context.Context, blockID types.BlockID, height int64, round int32, voteSet *types.VoteSet, validators []types.PrivValidator, now time.Time) (*types.ExtendedCommit, error) {
|
||||
// all sign
|
||||
for i := 0; i < len(validators); i++ {
|
||||
pubKey, err := validators[i].GetPubKey(ctx)
|
||||
@@ -37,5 +37,5 @@ func MakeCommit(ctx context.Context, blockID types.BlockID, height int64, round
|
||||
}
|
||||
}
|
||||
|
||||
return voteSet.MakeCommit(), nil
|
||||
return voteSet.MakeExtendedCommit(), nil
|
||||
}
|
||||
|
||||
@@ -18,5 +18,6 @@ func ConsensusParams() *types.ConsensusParams {
|
||||
VoteDelta: 1 * time.Millisecond,
|
||||
BypassCommitTimeout: true,
|
||||
}
|
||||
c.ABCI.VoteExtensionsEnableHeight = 1
|
||||
return c
|
||||
}
|
||||
|
||||
@@ -72,7 +72,12 @@ func (pkz privKeys) signHeader(t testing.TB, header *types.Header, valSet *types
|
||||
commitSigs[vote.ValidatorIndex] = vote.CommitSig()
|
||||
}
|
||||
|
||||
return types.NewCommit(header.Height, 1, blockID, commitSigs)
|
||||
return &types.Commit{
|
||||
Height: header.Height,
|
||||
Round: 1,
|
||||
BlockID: blockID,
|
||||
Signatures: commitSigs,
|
||||
}
|
||||
}
|
||||
|
||||
func makeVote(t testing.TB, header *types.Header, valset *types.ValidatorSet, key crypto.PrivKey, blockID types.BlockID) *types.Vote {
|
||||
|
||||
+1
-1
@@ -266,7 +266,7 @@ func makeNode(
|
||||
node.evPool = evPool
|
||||
|
||||
mpReactor, mp := createMempoolReactor(logger, cfg, proxyApp, stateStore, nodeMetrics.mempool,
|
||||
peerManager.Subscribe, node.router.OpenChannel, peerManager.GetHeight)
|
||||
peerManager.Subscribe, node.router.OpenChannel)
|
||||
node.rpcEnv.Mempool = mp
|
||||
node.services = append(node.services, mpReactor)
|
||||
|
||||
|
||||
+29
-25
@@ -35,6 +35,7 @@ import (
|
||||
"github.com/tendermint/tendermint/libs/service"
|
||||
tmtime "github.com/tendermint/tendermint/libs/time"
|
||||
"github.com/tendermint/tendermint/privval"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -339,13 +340,13 @@ func TestCreateProposalBlock(t *testing.T) {
|
||||
sm.NopMetrics(),
|
||||
)
|
||||
|
||||
commit := types.NewCommit(height-1, 0, types.BlockID{}, nil)
|
||||
extCommit := &types.ExtendedCommit{Height: height - 1}
|
||||
block, err := blockExec.CreateProposalBlock(
|
||||
ctx,
|
||||
height,
|
||||
state, commit,
|
||||
state,
|
||||
extCommit,
|
||||
proposerAddr,
|
||||
nil,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -419,13 +420,13 @@ func TestMaxTxsProposalBlockSize(t *testing.T) {
|
||||
sm.NopMetrics(),
|
||||
)
|
||||
|
||||
commit := types.NewCommit(height-1, 0, types.BlockID{}, nil)
|
||||
extCommit := &types.ExtendedCommit{Height: height - 1}
|
||||
block, err := blockExec.CreateProposalBlock(
|
||||
ctx,
|
||||
height,
|
||||
state, commit,
|
||||
state,
|
||||
extCommit,
|
||||
proposerAddr,
|
||||
nil,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -525,38 +526,41 @@ func TestMaxProposalBlockSize(t *testing.T) {
|
||||
}
|
||||
state.ChainID = maxChainID
|
||||
|
||||
cs := types.CommitSig{
|
||||
BlockIDFlag: types.BlockIDFlagNil,
|
||||
ValidatorAddress: crypto.AddressHash([]byte("validator_address")),
|
||||
Timestamp: timestamp,
|
||||
Signature: crypto.CRandBytes(types.MaxSignatureSize),
|
||||
}
|
||||
|
||||
commit := &types.Commit{
|
||||
Height: math.MaxInt64,
|
||||
Round: math.MaxInt32,
|
||||
BlockID: blockID,
|
||||
}
|
||||
|
||||
votes := make([]*types.Vote, types.MaxVotesCount)
|
||||
voteSet := types.NewExtendedVoteSet(state.ChainID, math.MaxInt64-1, math.MaxInt32, tmproto.PrecommitType, state.Validators)
|
||||
|
||||
// add maximum amount of signatures to a single commit
|
||||
for i := 0; i < types.MaxVotesCount; i++ {
|
||||
pubKey, err := privVals[i].GetPubKey(ctx)
|
||||
require.NoError(t, err)
|
||||
votes[i] = &types.Vote{
|
||||
ValidatorAddress: pubKey.Address(),
|
||||
valIdx, val := state.Validators.GetByAddress(pubKey.Address())
|
||||
require.NotNil(t, val)
|
||||
|
||||
vote := &types.Vote{
|
||||
Type: tmproto.PrecommitType,
|
||||
Height: math.MaxInt64 - 1,
|
||||
Round: math.MaxInt32,
|
||||
BlockID: blockID,
|
||||
Timestamp: timestamp,
|
||||
ValidatorAddress: val.Address,
|
||||
ValidatorIndex: valIdx,
|
||||
Extension: []byte("extension"),
|
||||
}
|
||||
commit.Signatures = append(commit.Signatures, cs)
|
||||
vpb := vote.ToProto()
|
||||
require.NoError(t, privVals[i].SignVote(ctx, state.ChainID, vpb))
|
||||
vote.Signature = vpb.Signature
|
||||
vote.ExtensionSignature = vpb.ExtensionSignature
|
||||
|
||||
added, err := voteSet.AddVote(vote)
|
||||
require.NoError(t, err)
|
||||
require.True(t, added)
|
||||
}
|
||||
|
||||
block, err := blockExec.CreateProposalBlock(
|
||||
ctx,
|
||||
math.MaxInt64,
|
||||
state,
|
||||
commit,
|
||||
voteSet.MakeExtendedCommit(),
|
||||
proposerAddr,
|
||||
votes,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
partSet, err := block.MakePartSet(types.BlockPartSizeBytes)
|
||||
|
||||
@@ -147,7 +147,6 @@ func createMempoolReactor(
|
||||
memplMetrics *mempool.Metrics,
|
||||
peerEvents p2p.PeerEventSubscriber,
|
||||
chCreator p2p.ChannelCreator,
|
||||
peerHeight func(types.NodeID) int64,
|
||||
) (service.Service, mempool.Mempool) {
|
||||
logger = logger.With("module", "mempool")
|
||||
|
||||
@@ -166,7 +165,6 @@ func createMempoolReactor(
|
||||
mp,
|
||||
chCreator,
|
||||
peerEvents,
|
||||
peerHeight,
|
||||
)
|
||||
|
||||
if cfg.Consensus.WaitForTxs() {
|
||||
|
||||
+4
-4
@@ -375,17 +375,17 @@ func (pv *FilePV) signVote(chainID string, vote *tmproto.Vote) error {
|
||||
|
||||
// Vote extensions are non-deterministic, so it is possible that an
|
||||
// application may have created a different extension. We therefore always
|
||||
// re-sign the vote extensions of precommits. For prevotes, the extension
|
||||
// signature will always be empty.
|
||||
// re-sign the vote extensions of precommits. For prevotes and nil
|
||||
// precommits, the extension signature will always be empty.
|
||||
var extSig []byte
|
||||
if vote.Type == tmproto.PrecommitType {
|
||||
if vote.Type == tmproto.PrecommitType && !types.ProtoBlockIDIsNil(&vote.BlockID) {
|
||||
extSignBytes := types.VoteExtensionSignBytes(chainID, vote)
|
||||
extSig, err = pv.Key.PrivKey.Sign(extSignBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if len(vote.Extension) > 0 {
|
||||
return errors.New("unexpected vote extension - extensions are only allowed in precommits")
|
||||
return errors.New("unexpected vote extension - extensions are only allowed in non-nil precommits")
|
||||
}
|
||||
|
||||
// We might crash before writing to the wal,
|
||||
|
||||
@@ -116,7 +116,8 @@ func (m *NoBlockResponse) GetHeight() int64 {
|
||||
|
||||
// BlockResponse returns block to the requested
|
||||
type BlockResponse struct {
|
||||
Block *types.Block `protobuf:"bytes,1,opt,name=block,proto3" json:"block,omitempty"`
|
||||
Block *types.Block `protobuf:"bytes,1,opt,name=block,proto3" json:"block,omitempty"`
|
||||
ExtCommit *types.ExtendedCommit `protobuf:"bytes,2,opt,name=ext_commit,json=extCommit,proto3" json:"ext_commit,omitempty"`
|
||||
}
|
||||
|
||||
func (m *BlockResponse) Reset() { *m = BlockResponse{} }
|
||||
@@ -159,6 +160,13 @@ func (m *BlockResponse) GetBlock() *types.Block {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *BlockResponse) GetExtCommit() *types.ExtendedCommit {
|
||||
if m != nil {
|
||||
return m.ExtCommit
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// StatusRequest requests the status of a peer.
|
||||
type StatusRequest struct {
|
||||
}
|
||||
@@ -385,30 +393,33 @@ func init() {
|
||||
func init() { proto.RegisterFile("tendermint/blocksync/types.proto", fileDescriptor_19b397c236e0fa07) }
|
||||
|
||||
var fileDescriptor_19b397c236e0fa07 = []byte{
|
||||
// 368 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x93, 0x4d, 0x4f, 0xfa, 0x40,
|
||||
0x10, 0xc6, 0xdb, 0x7f, 0x81, 0x7f, 0x32, 0x50, 0x1a, 0x1b, 0xa3, 0xc4, 0x98, 0x86, 0xd4, 0x97,
|
||||
0xe8, 0xc1, 0x36, 0xc1, 0xa3, 0xc6, 0x03, 0x27, 0x4c, 0x7c, 0x49, 0x4a, 0xbc, 0x78, 0x21, 0x14,
|
||||
0x37, 0x40, 0x94, 0x2e, 0x32, 0xdb, 0x03, 0xdf, 0xc2, 0x2f, 0xe0, 0xf7, 0xf1, 0xc8, 0xd1, 0xa3,
|
||||
0x81, 0x2f, 0x62, 0x98, 0x2d, 0x65, 0x69, 0xb0, 0xb7, 0xdd, 0xe9, 0x33, 0xbf, 0x79, 0xfa, 0x64,
|
||||
0x16, 0xea, 0x82, 0x45, 0x2f, 0x6c, 0x32, 0x1a, 0x46, 0xc2, 0x0f, 0xdf, 0x78, 0xef, 0x15, 0xa7,
|
||||
0x51, 0xcf, 0x17, 0xd3, 0x31, 0x43, 0x6f, 0x3c, 0xe1, 0x82, 0xdb, 0xbb, 0x6b, 0x85, 0x97, 0x2a,
|
||||
0x0e, 0x0e, 0x95, 0x3e, 0x52, 0xcb, 0x6e, 0xd9, 0xe3, 0x9e, 0x42, 0xa5, 0xb9, 0xbc, 0x06, 0xec,
|
||||
0x3d, 0x66, 0x28, 0xec, 0x3d, 0x28, 0x0d, 0xd8, 0xb0, 0x3f, 0x10, 0x35, 0xbd, 0xae, 0x9f, 0x19,
|
||||
0x41, 0x72, 0x73, 0xcf, 0xc1, 0x7a, 0xe0, 0x89, 0x12, 0xc7, 0x3c, 0x42, 0xf6, 0xa7, 0xf4, 0x06,
|
||||
0xcc, 0x4d, 0xe1, 0x05, 0x14, 0x69, 0x24, 0xe9, 0xca, 0x8d, 0x7d, 0x4f, 0xf1, 0x29, 0xfd, 0x4b,
|
||||
0xbd, 0x54, 0xb9, 0x16, 0x98, 0x6d, 0xd1, 0x15, 0x31, 0x26, 0x9e, 0xdc, 0x6b, 0xa8, 0xae, 0x0a,
|
||||
0xf9, 0xa3, 0x6d, 0x1b, 0x0a, 0x61, 0x17, 0x59, 0xed, 0x1f, 0x55, 0xe9, 0xec, 0x7e, 0x1a, 0xf0,
|
||||
0xff, 0x9e, 0x21, 0x76, 0xfb, 0xcc, 0xbe, 0x05, 0x93, 0x66, 0x74, 0x26, 0x12, 0x9d, 0x38, 0x72,
|
||||
0xbd, 0x6d, 0xc9, 0x79, 0x6a, 0x30, 0x2d, 0x2d, 0xa8, 0x84, 0x6a, 0x50, 0x6d, 0xd8, 0x89, 0x78,
|
||||
0x67, 0x45, 0x93, 0xbe, 0x68, 0x6e, 0xb9, 0x71, 0xb2, 0x1d, 0x97, 0xc9, 0xaf, 0xa5, 0x05, 0x56,
|
||||
0x94, 0x89, 0xf4, 0x0e, 0xaa, 0x19, 0xa2, 0x41, 0xc4, 0xa3, 0x5c, 0x83, 0x29, 0xcf, 0x0c, 0xb3,
|
||||
0x34, 0xa4, 0xdc, 0xd2, 0xdf, 0x2d, 0xe4, 0xd1, 0x36, 0x42, 0x5f, 0xd2, 0x50, 0x2d, 0xd8, 0x8f,
|
||||
0x60, 0xa5, 0xb4, 0xc4, 0x5c, 0x91, 0x70, 0xc7, 0xf9, 0xb8, 0xd4, 0x5d, 0x15, 0x37, 0x2a, 0xcd,
|
||||
0x22, 0x18, 0x18, 0x8f, 0x9a, 0x4f, 0x5f, 0x73, 0x47, 0x9f, 0xcd, 0x1d, 0xfd, 0x67, 0xee, 0xe8,
|
||||
0x1f, 0x0b, 0x47, 0x9b, 0x2d, 0x1c, 0xed, 0x7b, 0xe1, 0x68, 0xcf, 0x57, 0xfd, 0xa1, 0x18, 0xc4,
|
||||
0xa1, 0xd7, 0xe3, 0x23, 0x5f, 0x5d, 0xe2, 0xf5, 0x91, 0x76, 0xd8, 0xdf, 0xf6, 0x30, 0xc2, 0x12,
|
||||
0x7d, 0xbb, 0xfc, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xf5, 0x1c, 0xa3, 0x45, 0x37, 0x03, 0x00, 0x00,
|
||||
// 404 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x93, 0xcd, 0x4a, 0xeb, 0x50,
|
||||
0x10, 0xc7, 0x93, 0x9b, 0xb6, 0x97, 0x3b, 0xb7, 0x69, 0xb8, 0xe1, 0xa2, 0x45, 0x24, 0x94, 0xf8,
|
||||
0x81, 0x2e, 0x4c, 0x40, 0x97, 0x0a, 0x42, 0x45, 0xa8, 0xe0, 0x07, 0xa4, 0xb8, 0x71, 0x53, 0x9a,
|
||||
0xf4, 0xd0, 0x06, 0x4d, 0x4e, 0xed, 0x39, 0x81, 0x76, 0xe5, 0x2b, 0xf8, 0x02, 0xbe, 0x8f, 0xcb,
|
||||
0x2e, 0x5d, 0x4a, 0xfb, 0x22, 0xd2, 0x39, 0x69, 0x9a, 0xc6, 0x98, 0xdd, 0x64, 0xce, 0x7f, 0x7e,
|
||||
0xf9, 0xcf, 0x0c, 0x03, 0x0d, 0x4e, 0xc2, 0x1e, 0x19, 0x05, 0x7e, 0xc8, 0x6d, 0xf7, 0x89, 0x7a,
|
||||
0x8f, 0x6c, 0x12, 0x7a, 0x36, 0x9f, 0x0c, 0x09, 0xb3, 0x86, 0x23, 0xca, 0xa9, 0xfe, 0x7f, 0xa5,
|
||||
0xb0, 0x12, 0xc5, 0xd6, 0x76, 0xaa, 0x0e, 0xd5, 0xa2, 0x5a, 0xd4, 0xe4, 0xbc, 0xa6, 0x88, 0xe6,
|
||||
0x3e, 0x54, 0x9b, 0x0b, 0xb1, 0x43, 0x9e, 0x23, 0xc2, 0xb8, 0xbe, 0x01, 0x95, 0x01, 0xf1, 0xfb,
|
||||
0x03, 0x5e, 0x97, 0x1b, 0xf2, 0x81, 0xe2, 0xc4, 0x5f, 0xe6, 0x21, 0x68, 0xb7, 0x34, 0x56, 0xb2,
|
||||
0x21, 0x0d, 0x19, 0xf9, 0x51, 0xfa, 0x02, 0xea, 0xba, 0xf0, 0x08, 0xca, 0x68, 0x08, 0x75, 0x7f,
|
||||
0x8f, 0x37, 0xad, 0x54, 0x17, 0xc2, 0x8b, 0xd0, 0x0b, 0x95, 0x7e, 0x0e, 0x40, 0xc6, 0xbc, 0xe3,
|
||||
0xd1, 0x20, 0xf0, 0x79, 0xfd, 0x17, 0xd6, 0x34, 0xbe, 0xd7, 0x5c, 0x8e, 0x31, 0xd5, 0xbb, 0x40,
|
||||
0x9d, 0xf3, 0x87, 0x8c, 0xb9, 0x08, 0x4d, 0x0d, 0xd4, 0x36, 0xef, 0xf2, 0x88, 0xc5, 0x4d, 0x99,
|
||||
0x67, 0x50, 0x5b, 0x26, 0x8a, 0xbd, 0xeb, 0x3a, 0x94, 0xdc, 0x2e, 0x23, 0xf8, 0x57, 0xc5, 0xc1,
|
||||
0xd8, 0x7c, 0x53, 0xe0, 0xf7, 0x0d, 0x61, 0xac, 0xdb, 0x27, 0xfa, 0x15, 0xa8, 0x68, 0xb2, 0x33,
|
||||
0x12, 0xe8, 0xb8, 0x25, 0xd3, 0xca, 0x5b, 0x8c, 0x95, 0x9e, 0x6c, 0x4b, 0x72, 0xaa, 0x6e, 0x7a,
|
||||
0xd2, 0x6d, 0xf8, 0x17, 0xd2, 0xce, 0x92, 0x26, 0x7c, 0xc5, 0xdd, 0xee, 0xe5, 0xe3, 0x32, 0x0b,
|
||||
0x68, 0x49, 0x8e, 0x16, 0x66, 0x76, 0x72, 0x0d, 0xb5, 0x0c, 0x51, 0x41, 0xe2, 0x4e, 0xa1, 0xc1,
|
||||
0x84, 0xa7, 0xba, 0x59, 0x1a, 0xc3, 0xb9, 0x25, 0xed, 0x96, 0x8a, 0x68, 0x6b, 0x43, 0x5f, 0xd0,
|
||||
0x58, 0x3a, 0xa1, 0xdf, 0x81, 0x96, 0xd0, 0x62, 0x73, 0x65, 0xc4, 0xed, 0x16, 0xe3, 0x12, 0x77,
|
||||
0x35, 0xb6, 0x96, 0x69, 0x96, 0x41, 0x61, 0x51, 0xd0, 0xbc, 0x7f, 0x9f, 0x19, 0xf2, 0x74, 0x66,
|
||||
0xc8, 0x9f, 0x33, 0x43, 0x7e, 0x9d, 0x1b, 0xd2, 0x74, 0x6e, 0x48, 0x1f, 0x73, 0x43, 0x7a, 0x38,
|
||||
0xed, 0xfb, 0x7c, 0x10, 0xb9, 0x96, 0x47, 0x03, 0x3b, 0x7d, 0x05, 0xab, 0x10, 0x8f, 0xc0, 0xce,
|
||||
0xbb, 0x3b, 0xb7, 0x82, 0x6f, 0x27, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xdc, 0x13, 0x4f, 0x42,
|
||||
0x96, 0x03, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func (m *BlockRequest) Marshal() (dAtA []byte, err error) {
|
||||
@@ -487,6 +498,18 @@ func (m *BlockResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if m.ExtCommit != nil {
|
||||
{
|
||||
size, err := m.ExtCommit.MarshalToSizedBuffer(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = encodeVarintTypes(dAtA, i, uint64(size))
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x12
|
||||
}
|
||||
if m.Block != nil {
|
||||
{
|
||||
size, err := m.Block.MarshalToSizedBuffer(dAtA[:i])
|
||||
@@ -740,6 +763,10 @@ func (m *BlockResponse) Size() (n int) {
|
||||
l = m.Block.Size()
|
||||
n += 1 + l + sovTypes(uint64(l))
|
||||
}
|
||||
if m.ExtCommit != nil {
|
||||
l = m.ExtCommit.Size()
|
||||
n += 1 + l + sovTypes(uint64(l))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -1049,6 +1076,42 @@ func (m *BlockResponse) Unmarshal(dAtA []byte) error {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field ExtCommit", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if m.ExtCommit == nil {
|
||||
m.ExtCommit = &types.ExtendedCommit{}
|
||||
}
|
||||
if err := m.ExtCommit.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipTypes(dAtA[iNdEx:])
|
||||
|
||||
@@ -4,6 +4,7 @@ package tendermint.blocksync;
|
||||
option go_package = "github.com/tendermint/tendermint/proto/tendermint/blocksync";
|
||||
|
||||
import "tendermint/types/block.proto";
|
||||
import "tendermint/types/types.proto";
|
||||
|
||||
// BlockRequest requests a block for a specific height
|
||||
message BlockRequest {
|
||||
@@ -19,6 +20,7 @@ message NoBlockResponse {
|
||||
// BlockResponse returns block to the requested
|
||||
message BlockResponse {
|
||||
tendermint.types.Block block = 1;
|
||||
tendermint.types.ExtendedCommit ext_commit = 2;
|
||||
}
|
||||
|
||||
// StatusRequest requests the status of a peer.
|
||||
|
||||
@@ -726,6 +726,162 @@ func (m *CommitSig) GetSignature() []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
type ExtendedCommit struct {
|
||||
Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"`
|
||||
Round int32 `protobuf:"varint,2,opt,name=round,proto3" json:"round,omitempty"`
|
||||
BlockID BlockID `protobuf:"bytes,3,opt,name=block_id,json=blockId,proto3" json:"block_id"`
|
||||
ExtendedSignatures []ExtendedCommitSig `protobuf:"bytes,4,rep,name=extended_signatures,json=extendedSignatures,proto3" json:"extended_signatures"`
|
||||
}
|
||||
|
||||
func (m *ExtendedCommit) Reset() { *m = ExtendedCommit{} }
|
||||
func (m *ExtendedCommit) String() string { return proto.CompactTextString(m) }
|
||||
func (*ExtendedCommit) ProtoMessage() {}
|
||||
func (*ExtendedCommit) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_d3a6e55e2345de56, []int{8}
|
||||
}
|
||||
func (m *ExtendedCommit) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *ExtendedCommit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
if deterministic {
|
||||
return xxx_messageInfo_ExtendedCommit.Marshal(b, m, deterministic)
|
||||
} else {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
}
|
||||
func (m *ExtendedCommit) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ExtendedCommit.Merge(m, src)
|
||||
}
|
||||
func (m *ExtendedCommit) XXX_Size() int {
|
||||
return m.Size()
|
||||
}
|
||||
func (m *ExtendedCommit) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ExtendedCommit.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_ExtendedCommit proto.InternalMessageInfo
|
||||
|
||||
func (m *ExtendedCommit) GetHeight() int64 {
|
||||
if m != nil {
|
||||
return m.Height
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *ExtendedCommit) GetRound() int32 {
|
||||
if m != nil {
|
||||
return m.Round
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *ExtendedCommit) GetBlockID() BlockID {
|
||||
if m != nil {
|
||||
return m.BlockID
|
||||
}
|
||||
return BlockID{}
|
||||
}
|
||||
|
||||
func (m *ExtendedCommit) GetExtendedSignatures() []ExtendedCommitSig {
|
||||
if m != nil {
|
||||
return m.ExtendedSignatures
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExtendedCommitSig retains all the same fields as CommitSig but adds vote
|
||||
// extension-related fields.
|
||||
type ExtendedCommitSig struct {
|
||||
BlockIdFlag BlockIDFlag `protobuf:"varint,1,opt,name=block_id_flag,json=blockIdFlag,proto3,enum=tendermint.types.BlockIDFlag" json:"block_id_flag,omitempty"`
|
||||
ValidatorAddress []byte `protobuf:"bytes,2,opt,name=validator_address,json=validatorAddress,proto3" json:"validator_address,omitempty"`
|
||||
Timestamp time.Time `protobuf:"bytes,3,opt,name=timestamp,proto3,stdtime" json:"timestamp"`
|
||||
Signature []byte `protobuf:"bytes,4,opt,name=signature,proto3" json:"signature,omitempty"`
|
||||
// Vote extension data
|
||||
Extension []byte `protobuf:"bytes,5,opt,name=extension,proto3" json:"extension,omitempty"`
|
||||
// Vote extension signature
|
||||
ExtensionSignature []byte `protobuf:"bytes,6,opt,name=extension_signature,json=extensionSignature,proto3" json:"extension_signature,omitempty"`
|
||||
}
|
||||
|
||||
func (m *ExtendedCommitSig) Reset() { *m = ExtendedCommitSig{} }
|
||||
func (m *ExtendedCommitSig) String() string { return proto.CompactTextString(m) }
|
||||
func (*ExtendedCommitSig) ProtoMessage() {}
|
||||
func (*ExtendedCommitSig) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_d3a6e55e2345de56, []int{9}
|
||||
}
|
||||
func (m *ExtendedCommitSig) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *ExtendedCommitSig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
if deterministic {
|
||||
return xxx_messageInfo_ExtendedCommitSig.Marshal(b, m, deterministic)
|
||||
} else {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
}
|
||||
func (m *ExtendedCommitSig) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ExtendedCommitSig.Merge(m, src)
|
||||
}
|
||||
func (m *ExtendedCommitSig) XXX_Size() int {
|
||||
return m.Size()
|
||||
}
|
||||
func (m *ExtendedCommitSig) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ExtendedCommitSig.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_ExtendedCommitSig proto.InternalMessageInfo
|
||||
|
||||
func (m *ExtendedCommitSig) GetBlockIdFlag() BlockIDFlag {
|
||||
if m != nil {
|
||||
return m.BlockIdFlag
|
||||
}
|
||||
return BlockIDFlagUnknown
|
||||
}
|
||||
|
||||
func (m *ExtendedCommitSig) GetValidatorAddress() []byte {
|
||||
if m != nil {
|
||||
return m.ValidatorAddress
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ExtendedCommitSig) GetTimestamp() time.Time {
|
||||
if m != nil {
|
||||
return m.Timestamp
|
||||
}
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func (m *ExtendedCommitSig) GetSignature() []byte {
|
||||
if m != nil {
|
||||
return m.Signature
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ExtendedCommitSig) GetExtension() []byte {
|
||||
if m != nil {
|
||||
return m.Extension
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ExtendedCommitSig) GetExtensionSignature() []byte {
|
||||
if m != nil {
|
||||
return m.ExtensionSignature
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Proposal struct {
|
||||
Type SignedMsgType `protobuf:"varint,1,opt,name=type,proto3,enum=tendermint.types.SignedMsgType" json:"type,omitempty"`
|
||||
Height int64 `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"`
|
||||
@@ -740,7 +896,7 @@ func (m *Proposal) Reset() { *m = Proposal{} }
|
||||
func (m *Proposal) String() string { return proto.CompactTextString(m) }
|
||||
func (*Proposal) ProtoMessage() {}
|
||||
func (*Proposal) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_d3a6e55e2345de56, []int{8}
|
||||
return fileDescriptor_d3a6e55e2345de56, []int{10}
|
||||
}
|
||||
func (m *Proposal) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -827,7 +983,7 @@ func (m *SignedHeader) Reset() { *m = SignedHeader{} }
|
||||
func (m *SignedHeader) String() string { return proto.CompactTextString(m) }
|
||||
func (*SignedHeader) ProtoMessage() {}
|
||||
func (*SignedHeader) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_d3a6e55e2345de56, []int{9}
|
||||
return fileDescriptor_d3a6e55e2345de56, []int{11}
|
||||
}
|
||||
func (m *SignedHeader) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -879,7 +1035,7 @@ func (m *LightBlock) Reset() { *m = LightBlock{} }
|
||||
func (m *LightBlock) String() string { return proto.CompactTextString(m) }
|
||||
func (*LightBlock) ProtoMessage() {}
|
||||
func (*LightBlock) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_d3a6e55e2345de56, []int{10}
|
||||
return fileDescriptor_d3a6e55e2345de56, []int{12}
|
||||
}
|
||||
func (m *LightBlock) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -933,7 +1089,7 @@ func (m *BlockMeta) Reset() { *m = BlockMeta{} }
|
||||
func (m *BlockMeta) String() string { return proto.CompactTextString(m) }
|
||||
func (*BlockMeta) ProtoMessage() {}
|
||||
func (*BlockMeta) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_d3a6e55e2345de56, []int{11}
|
||||
return fileDescriptor_d3a6e55e2345de56, []int{13}
|
||||
}
|
||||
func (m *BlockMeta) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1002,7 +1158,7 @@ func (m *TxProof) Reset() { *m = TxProof{} }
|
||||
func (m *TxProof) String() string { return proto.CompactTextString(m) }
|
||||
func (*TxProof) ProtoMessage() {}
|
||||
func (*TxProof) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_d3a6e55e2345de56, []int{12}
|
||||
return fileDescriptor_d3a6e55e2345de56, []int{14}
|
||||
}
|
||||
func (m *TxProof) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1063,6 +1219,8 @@ func init() {
|
||||
proto.RegisterType((*Vote)(nil), "tendermint.types.Vote")
|
||||
proto.RegisterType((*Commit)(nil), "tendermint.types.Commit")
|
||||
proto.RegisterType((*CommitSig)(nil), "tendermint.types.CommitSig")
|
||||
proto.RegisterType((*ExtendedCommit)(nil), "tendermint.types.ExtendedCommit")
|
||||
proto.RegisterType((*ExtendedCommitSig)(nil), "tendermint.types.ExtendedCommitSig")
|
||||
proto.RegisterType((*Proposal)(nil), "tendermint.types.Proposal")
|
||||
proto.RegisterType((*SignedHeader)(nil), "tendermint.types.SignedHeader")
|
||||
proto.RegisterType((*LightBlock)(nil), "tendermint.types.LightBlock")
|
||||
@@ -1073,91 +1231,95 @@ func init() {
|
||||
func init() { proto.RegisterFile("tendermint/types/types.proto", fileDescriptor_d3a6e55e2345de56) }
|
||||
|
||||
var fileDescriptor_d3a6e55e2345de56 = []byte{
|
||||
// 1341 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0xcf, 0x73, 0xdb, 0xc4,
|
||||
0x17, 0x8f, 0x62, 0x25, 0xb6, 0x9f, 0xed, 0xc4, 0xd9, 0x6f, 0xda, 0xba, 0x6e, 0xe3, 0x68, 0xfc,
|
||||
0x1d, 0x20, 0x2d, 0x8c, 0x52, 0x52, 0x86, 0x1f, 0x07, 0x0e, 0xb6, 0x93, 0xb6, 0x9e, 0x26, 0x8e,
|
||||
0x91, 0xdd, 0x32, 0x70, 0xd1, 0xc8, 0xd6, 0xd6, 0x16, 0x95, 0x25, 0x8d, 0x76, 0x1d, 0x92, 0xfe,
|
||||
0x05, 0x4c, 0x4e, 0x3d, 0x71, 0xcb, 0x09, 0x0e, 0xdc, 0x39, 0x70, 0x65, 0x38, 0xf5, 0xd8, 0x1b,
|
||||
0x5c, 0x28, 0x4c, 0x3a, 0xc3, 0xdf, 0xc1, 0xec, 0x0f, 0xc9, 0x72, 0x9c, 0x40, 0xa7, 0xd3, 0xe1,
|
||||
0xe2, 0xd1, 0xbe, 0xf7, 0x79, 0x6f, 0xdf, 0x8f, 0xcf, 0xee, 0x5b, 0xc3, 0x75, 0x8a, 0x3d, 0x1b,
|
||||
0x87, 0x23, 0xc7, 0xa3, 0x9b, 0xf4, 0x28, 0xc0, 0x44, 0xfc, 0xea, 0x41, 0xe8, 0x53, 0x1f, 0x15,
|
||||
0x27, 0x5a, 0x9d, 0xcb, 0xcb, 0xab, 0x03, 0x7f, 0xe0, 0x73, 0xe5, 0x26, 0xfb, 0x12, 0xb8, 0xf2,
|
||||
0xfa, 0xc0, 0xf7, 0x07, 0x2e, 0xde, 0xe4, 0xab, 0xde, 0xf8, 0xd1, 0x26, 0x75, 0x46, 0x98, 0x50,
|
||||
0x6b, 0x14, 0x48, 0xc0, 0x5a, 0x62, 0x9b, 0x7e, 0x78, 0x14, 0x50, 0x9f, 0x61, 0xfd, 0x47, 0x52,
|
||||
0x5d, 0x49, 0xa8, 0x0f, 0x70, 0x48, 0x1c, 0xdf, 0x4b, 0xc6, 0x51, 0xd6, 0x66, 0xa2, 0x3c, 0xb0,
|
||||
0x5c, 0xc7, 0xb6, 0xa8, 0x1f, 0x0a, 0x44, 0xf5, 0x13, 0x28, 0xb4, 0xad, 0x90, 0x76, 0x30, 0xbd,
|
||||
0x87, 0x2d, 0x1b, 0x87, 0x68, 0x15, 0x16, 0xa8, 0x4f, 0x2d, 0xb7, 0xa4, 0x68, 0xca, 0x46, 0xc1,
|
||||
0x10, 0x0b, 0x84, 0x40, 0x1d, 0x5a, 0x64, 0x58, 0x9a, 0xd7, 0x94, 0x8d, 0xbc, 0xc1, 0xbf, 0xab,
|
||||
0x43, 0x50, 0x99, 0x29, 0xb3, 0x70, 0x3c, 0x1b, 0x1f, 0x46, 0x16, 0x7c, 0xc1, 0xa4, 0xbd, 0x23,
|
||||
0x8a, 0x89, 0x34, 0x11, 0x0b, 0xf4, 0x01, 0x2c, 0xf0, 0xf8, 0x4b, 0x29, 0x4d, 0xd9, 0xc8, 0x6d,
|
||||
0x95, 0xf4, 0x44, 0xa1, 0x44, 0x7e, 0x7a, 0x9b, 0xe9, 0xeb, 0xea, 0xb3, 0x17, 0xeb, 0x73, 0x86,
|
||||
0x00, 0x57, 0x5d, 0x48, 0xd7, 0x5d, 0xbf, 0xff, 0xb8, 0xb9, 0x1d, 0x07, 0xa2, 0x4c, 0x02, 0x41,
|
||||
0x7b, 0xb0, 0x1c, 0x58, 0x21, 0x35, 0x09, 0xa6, 0xe6, 0x90, 0x67, 0xc1, 0x37, 0xcd, 0x6d, 0xad,
|
||||
0xeb, 0x67, 0xfb, 0xa0, 0x4f, 0x25, 0x2b, 0x77, 0x29, 0x04, 0x49, 0x61, 0xf5, 0x2f, 0x15, 0x16,
|
||||
0x65, 0x31, 0x3e, 0x85, 0xb4, 0x2c, 0x2b, 0xdf, 0x30, 0xb7, 0xb5, 0x96, 0xf4, 0x28, 0x55, 0x7a,
|
||||
0xc3, 0xf7, 0x08, 0xf6, 0xc8, 0x98, 0x48, 0x7f, 0x91, 0x0d, 0x7a, 0x1b, 0x32, 0xfd, 0xa1, 0xe5,
|
||||
0x78, 0xa6, 0x63, 0xf3, 0x88, 0xb2, 0xf5, 0xdc, 0xe9, 0x8b, 0xf5, 0x74, 0x83, 0xc9, 0x9a, 0xdb,
|
||||
0x46, 0x9a, 0x2b, 0x9b, 0x36, 0xba, 0x0c, 0x8b, 0x43, 0xec, 0x0c, 0x86, 0x94, 0x97, 0x25, 0x65,
|
||||
0xc8, 0x15, 0xfa, 0x18, 0x54, 0x46, 0x88, 0x92, 0xca, 0xf7, 0x2e, 0xeb, 0x82, 0x2d, 0x7a, 0xc4,
|
||||
0x16, 0xbd, 0x1b, 0xb1, 0xa5, 0x9e, 0x61, 0x1b, 0x3f, 0xfd, 0x63, 0x5d, 0x31, 0xb8, 0x05, 0x6a,
|
||||
0x40, 0xc1, 0xb5, 0x08, 0x35, 0x7b, 0xac, 0x6c, 0x6c, 0xfb, 0x05, 0xee, 0xe2, 0xea, 0x6c, 0x41,
|
||||
0x64, 0x61, 0x65, 0xe8, 0x39, 0x66, 0x25, 0x44, 0x36, 0xda, 0x80, 0x22, 0x77, 0xd2, 0xf7, 0x47,
|
||||
0x23, 0x87, 0x9a, 0xbc, 0xee, 0x8b, 0xbc, 0xee, 0x4b, 0x4c, 0xde, 0xe0, 0xe2, 0x7b, 0xac, 0x03,
|
||||
0xd7, 0x20, 0x6b, 0x5b, 0xd4, 0x12, 0x90, 0x34, 0x87, 0x64, 0x98, 0x80, 0x2b, 0xdf, 0x81, 0xe5,
|
||||
0x98, 0x75, 0x44, 0x40, 0x32, 0xc2, 0xcb, 0x44, 0xcc, 0x81, 0xb7, 0x60, 0xd5, 0xc3, 0x87, 0xd4,
|
||||
0x3c, 0x8b, 0xce, 0x72, 0x34, 0x62, 0xba, 0x87, 0xd3, 0x16, 0x6f, 0xc1, 0x52, 0x3f, 0x2a, 0xbe,
|
||||
0xc0, 0x02, 0xc7, 0x16, 0x62, 0x29, 0x87, 0x5d, 0x85, 0x8c, 0x15, 0x04, 0x02, 0x90, 0xe3, 0x80,
|
||||
0xb4, 0x15, 0x04, 0x5c, 0x75, 0x13, 0x56, 0x78, 0x8e, 0x21, 0x26, 0x63, 0x97, 0x4a, 0x27, 0x79,
|
||||
0x8e, 0x59, 0x66, 0x0a, 0x43, 0xc8, 0x39, 0xf6, 0xff, 0x50, 0xc0, 0x07, 0x8e, 0x8d, 0xbd, 0x3e,
|
||||
0x16, 0xb8, 0x02, 0xc7, 0xe5, 0x23, 0x21, 0x07, 0xdd, 0x80, 0x62, 0x10, 0xfa, 0x81, 0x4f, 0x70,
|
||||
0x68, 0x5a, 0xb6, 0x1d, 0x62, 0x42, 0x4a, 0x4b, 0xc2, 0x5f, 0x24, 0xaf, 0x09, 0x71, 0xb5, 0x04,
|
||||
0xea, 0xb6, 0x45, 0x2d, 0x54, 0x84, 0x14, 0x3d, 0x24, 0x25, 0x45, 0x4b, 0x6d, 0xe4, 0x0d, 0xf6,
|
||||
0x59, 0xfd, 0x29, 0x05, 0xea, 0x43, 0x9f, 0x62, 0x74, 0x1b, 0x54, 0xd6, 0x26, 0xce, 0xbe, 0xa5,
|
||||
0xf3, 0xf8, 0xdc, 0x71, 0x06, 0x1e, 0xb6, 0xf7, 0xc8, 0xa0, 0x7b, 0x14, 0x60, 0x83, 0x83, 0x13,
|
||||
0x74, 0x9a, 0x9f, 0xa2, 0xd3, 0x2a, 0x2c, 0x84, 0xfe, 0xd8, 0xb3, 0x39, 0xcb, 0x16, 0x0c, 0xb1,
|
||||
0x40, 0x3b, 0x90, 0x89, 0x59, 0xa2, 0xfe, 0x1b, 0x4b, 0x96, 0x19, 0x4b, 0x18, 0x87, 0xa5, 0xc0,
|
||||
0x48, 0xf7, 0x24, 0x59, 0xea, 0x90, 0x8d, 0x2f, 0x2f, 0xc9, 0xb6, 0x57, 0x23, 0xec, 0xc4, 0x0c,
|
||||
0xbd, 0x0b, 0x2b, 0x71, 0xef, 0xe3, 0xe2, 0x09, 0xc6, 0x15, 0x63, 0x85, 0xac, 0xde, 0x14, 0xad,
|
||||
0x4c, 0x71, 0x01, 0xa5, 0x79, 0x5e, 0x13, 0x5a, 0x35, 0xf9, 0x4d, 0x74, 0x1d, 0xb2, 0xc4, 0x19,
|
||||
0x78, 0x16, 0x1d, 0x87, 0x58, 0x32, 0x6f, 0x22, 0x60, 0x5a, 0x7c, 0x48, 0xb1, 0xc7, 0x0f, 0xb9,
|
||||
0x60, 0xda, 0x44, 0x80, 0x36, 0xe1, 0x7f, 0xf1, 0xc2, 0x9c, 0x78, 0x11, 0x2c, 0x43, 0xb1, 0xaa,
|
||||
0x13, 0x69, 0xaa, 0x3f, 0x2b, 0xb0, 0x28, 0x0e, 0x46, 0xa2, 0x0d, 0xca, 0xf9, 0x6d, 0x98, 0xbf,
|
||||
0xa8, 0x0d, 0xa9, 0xd7, 0x6f, 0x43, 0x0d, 0x20, 0x0e, 0x93, 0x94, 0x54, 0x2d, 0xb5, 0x91, 0xdb,
|
||||
0xba, 0x36, 0xeb, 0x48, 0x84, 0xd8, 0x71, 0x06, 0xf2, 0xdc, 0x27, 0x8c, 0xaa, 0xbf, 0x2b, 0x90,
|
||||
0x8d, 0xf5, 0xa8, 0x06, 0x85, 0x28, 0x2e, 0xf3, 0x91, 0x6b, 0x0d, 0x24, 0x15, 0xd7, 0x2e, 0x0c,
|
||||
0xee, 0x8e, 0x6b, 0x0d, 0x8c, 0x9c, 0x8c, 0x87, 0x2d, 0xce, 0x6f, 0xeb, 0xfc, 0x05, 0x6d, 0x9d,
|
||||
0xe2, 0x51, 0xea, 0xf5, 0x78, 0x34, 0xd5, 0x71, 0xf5, 0x4c, 0xc7, 0xab, 0x3f, 0xce, 0x43, 0xa6,
|
||||
0xcd, 0x8f, 0xa2, 0xe5, 0xfe, 0x17, 0x07, 0xec, 0x1a, 0x64, 0x03, 0xdf, 0x35, 0x85, 0x46, 0xe5,
|
||||
0x9a, 0x4c, 0xe0, 0xbb, 0xc6, 0x4c, 0xdb, 0x17, 0xde, 0xd0, 0xe9, 0x5b, 0x7c, 0x03, 0x55, 0x4b,
|
||||
0x9f, 0xad, 0x5a, 0x08, 0x79, 0x51, 0x0a, 0x39, 0x1a, 0x6f, 0xb1, 0x1a, 0xf0, 0x59, 0xab, 0xcc,
|
||||
0x8e, 0x72, 0x11, 0xb6, 0x40, 0x1a, 0x12, 0xc7, 0x2c, 0xc4, 0x24, 0x91, 0xd3, 0xb9, 0x74, 0x11,
|
||||
0x2d, 0x0d, 0x89, 0xab, 0x7e, 0xab, 0x00, 0xec, 0xb2, 0xca, 0xf2, 0x7c, 0xd9, 0x50, 0x23, 0x3c,
|
||||
0x04, 0x73, 0x6a, 0xe7, 0xca, 0x45, 0x4d, 0x93, 0xfb, 0xe7, 0x49, 0x32, 0xee, 0x06, 0x14, 0x26,
|
||||
0x64, 0x24, 0x38, 0x0a, 0xe6, 0x1c, 0x27, 0xf1, 0xac, 0xe9, 0x60, 0x6a, 0xe4, 0x0f, 0x12, 0xab,
|
||||
0xea, 0x2f, 0x0a, 0x64, 0x79, 0x4c, 0x7b, 0x98, 0x5a, 0x53, 0x3d, 0x54, 0x5e, 0xbf, 0x87, 0x6b,
|
||||
0x00, 0xc2, 0x0d, 0x71, 0x9e, 0x60, 0xc9, 0xac, 0x2c, 0x97, 0x74, 0x9c, 0x27, 0x18, 0x7d, 0x18,
|
||||
0x17, 0x3c, 0xf5, 0xcf, 0x05, 0x97, 0x47, 0x3a, 0x2a, 0xfb, 0x15, 0x48, 0x7b, 0xe3, 0x91, 0xc9,
|
||||
0x26, 0x8c, 0x2a, 0xd8, 0xea, 0x8d, 0x47, 0xdd, 0x43, 0x52, 0xfd, 0x0a, 0xd2, 0xdd, 0x43, 0xfe,
|
||||
0xda, 0x62, 0x14, 0x0d, 0x7d, 0x5f, 0x8e, 0x78, 0xf1, 0xb4, 0xca, 0x30, 0x01, 0x9f, 0x68, 0x08,
|
||||
0x54, 0x36, 0xcb, 0xa3, 0xb7, 0x1f, 0xfb, 0x46, 0xfa, 0x2b, 0xbe, 0xe3, 0xe4, 0x0b, 0xee, 0xe6,
|
||||
0xaf, 0x0a, 0xe4, 0x12, 0xf7, 0x03, 0x7a, 0x1f, 0x2e, 0xd5, 0x77, 0xf7, 0x1b, 0xf7, 0xcd, 0xe6,
|
||||
0xb6, 0x79, 0x67, 0xb7, 0x76, 0xd7, 0x7c, 0xd0, 0xba, 0xdf, 0xda, 0xff, 0xbc, 0x55, 0x9c, 0x2b,
|
||||
0x5f, 0x3e, 0x3e, 0xd1, 0x50, 0x02, 0xfb, 0xc0, 0x7b, 0xec, 0xf9, 0x5f, 0xb3, 0xab, 0x78, 0x75,
|
||||
0xda, 0xa4, 0x56, 0xef, 0xec, 0xb4, 0xba, 0x45, 0xa5, 0x7c, 0xe9, 0xf8, 0x44, 0x5b, 0x49, 0x58,
|
||||
0xd4, 0x7a, 0x04, 0x7b, 0x74, 0xd6, 0xa0, 0xb1, 0xbf, 0xb7, 0xd7, 0xec, 0x16, 0xe7, 0x67, 0x0c,
|
||||
0xe4, 0x85, 0x7d, 0x03, 0x56, 0xa6, 0x0d, 0x5a, 0xcd, 0xdd, 0x62, 0xaa, 0x8c, 0x8e, 0x4f, 0xb4,
|
||||
0xa5, 0x04, 0xba, 0xe5, 0xb8, 0xe5, 0xcc, 0x37, 0xdf, 0x55, 0xe6, 0x7e, 0xf8, 0xbe, 0xa2, 0xb0,
|
||||
0xcc, 0x0a, 0x53, 0x77, 0x04, 0x7a, 0x0f, 0xae, 0x74, 0x9a, 0x77, 0x5b, 0x3b, 0xdb, 0xe6, 0x5e,
|
||||
0xe7, 0xae, 0xd9, 0xfd, 0xa2, 0xbd, 0x93, 0xc8, 0x6e, 0xf9, 0xf8, 0x44, 0xcb, 0xc9, 0x94, 0x2e,
|
||||
0x42, 0xb7, 0x8d, 0x9d, 0x87, 0xfb, 0xdd, 0x9d, 0xa2, 0x22, 0xd0, 0xed, 0x10, 0x1f, 0xf8, 0x14,
|
||||
0x73, 0xf4, 0x2d, 0xb8, 0x7a, 0x0e, 0x3a, 0x4e, 0x6c, 0xe5, 0xf8, 0x44, 0x2b, 0xb4, 0x43, 0x2c,
|
||||
0xce, 0x0f, 0xb7, 0xd0, 0xa1, 0x34, 0x6b, 0xb1, 0xdf, 0xde, 0xef, 0xd4, 0x76, 0x8b, 0x5a, 0xb9,
|
||||
0x78, 0x7c, 0xa2, 0xe5, 0xa3, 0xcb, 0x90, 0xe1, 0x27, 0x99, 0xd5, 0x3f, 0x7b, 0x76, 0x5a, 0x51,
|
||||
0x9e, 0x9f, 0x56, 0x94, 0x3f, 0x4f, 0x2b, 0xca, 0xd3, 0x97, 0x95, 0xb9, 0xe7, 0x2f, 0x2b, 0x73,
|
||||
0xbf, 0xbd, 0xac, 0xcc, 0x7d, 0xf9, 0xd1, 0xc0, 0xa1, 0xc3, 0x71, 0x4f, 0xef, 0xfb, 0xa3, 0xcd,
|
||||
0xe4, 0x3f, 0x8c, 0xc9, 0xa7, 0xf8, 0xa7, 0x73, 0xf6, 0xdf, 0x47, 0x6f, 0x91, 0xcb, 0x6f, 0xff,
|
||||
0x1d, 0x00, 0x00, 0xff, 0xff, 0xbb, 0xc0, 0x81, 0x37, 0x3e, 0x0d, 0x00, 0x00,
|
||||
// 1396 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x57, 0x4b, 0x6f, 0x1b, 0xd5,
|
||||
0x17, 0xcf, 0xd8, 0xe3, 0xd7, 0xb1, 0x9d, 0x38, 0xf7, 0x9f, 0xb6, 0xae, 0xdb, 0x38, 0x96, 0xab,
|
||||
0x3f, 0xa4, 0x05, 0x39, 0x25, 0x45, 0x3c, 0x16, 0x2c, 0x6c, 0xc7, 0x6d, 0xad, 0x26, 0x8e, 0x19,
|
||||
0xbb, 0x45, 0x74, 0x33, 0x1a, 0x7b, 0x6e, 0xed, 0xa1, 0xf6, 0xcc, 0x68, 0xe6, 0x3a, 0x38, 0xfd,
|
||||
0x04, 0x28, 0xab, 0xae, 0xd8, 0x65, 0x05, 0x0b, 0xf6, 0x20, 0xb1, 0x45, 0xac, 0xba, 0xec, 0x0e,
|
||||
0x36, 0x14, 0x48, 0x25, 0x3e, 0x07, 0xba, 0x8f, 0x19, 0xcf, 0xc4, 0x31, 0x54, 0x51, 0x05, 0x12,
|
||||
0x9b, 0x68, 0xee, 0x39, 0xbf, 0x73, 0xee, 0x79, 0xfc, 0xee, 0xc9, 0x31, 0x5c, 0x25, 0xd8, 0xd4,
|
||||
0xb1, 0x33, 0x36, 0x4c, 0xb2, 0x45, 0x0e, 0x6d, 0xec, 0xf2, 0xbf, 0x15, 0xdb, 0xb1, 0x88, 0x85,
|
||||
0x72, 0x33, 0x6d, 0x85, 0xc9, 0x0b, 0x6b, 0x03, 0x6b, 0x60, 0x31, 0xe5, 0x16, 0xfd, 0xe2, 0xb8,
|
||||
0xc2, 0xc6, 0xc0, 0xb2, 0x06, 0x23, 0xbc, 0xc5, 0x4e, 0xbd, 0xc9, 0xa3, 0x2d, 0x62, 0x8c, 0xb1,
|
||||
0x4b, 0xb4, 0xb1, 0x2d, 0x00, 0xeb, 0x81, 0x6b, 0xfa, 0xce, 0xa1, 0x4d, 0x2c, 0x8a, 0xb5, 0x1e,
|
||||
0x09, 0x75, 0x31, 0xa0, 0x3e, 0xc0, 0x8e, 0x6b, 0x58, 0x66, 0x30, 0x8e, 0x42, 0x69, 0x2e, 0xca,
|
||||
0x03, 0x6d, 0x64, 0xe8, 0x1a, 0xb1, 0x1c, 0x8e, 0x28, 0x7f, 0x08, 0xd9, 0xb6, 0xe6, 0x90, 0x0e,
|
||||
0x26, 0x77, 0xb1, 0xa6, 0x63, 0x07, 0xad, 0x41, 0x8c, 0x58, 0x44, 0x1b, 0xe5, 0xa5, 0x92, 0xb4,
|
||||
0x99, 0x55, 0xf8, 0x01, 0x21, 0x90, 0x87, 0x9a, 0x3b, 0xcc, 0x47, 0x4a, 0xd2, 0x66, 0x46, 0x61,
|
||||
0xdf, 0xe5, 0x21, 0xc8, 0xd4, 0x94, 0x5a, 0x18, 0xa6, 0x8e, 0xa7, 0x9e, 0x05, 0x3b, 0x50, 0x69,
|
||||
0xef, 0x90, 0x60, 0x57, 0x98, 0xf0, 0x03, 0x7a, 0x17, 0x62, 0x2c, 0xfe, 0x7c, 0xb4, 0x24, 0x6d,
|
||||
0xa6, 0xb7, 0xf3, 0x95, 0x40, 0xa1, 0x78, 0x7e, 0x95, 0x36, 0xd5, 0xd7, 0xe4, 0x67, 0x2f, 0x36,
|
||||
0x96, 0x14, 0x0e, 0x2e, 0x8f, 0x20, 0x51, 0x1b, 0x59, 0xfd, 0xc7, 0xcd, 0x1d, 0x3f, 0x10, 0x69,
|
||||
0x16, 0x08, 0xda, 0x83, 0x15, 0x5b, 0x73, 0x88, 0xea, 0x62, 0xa2, 0x0e, 0x59, 0x16, 0xec, 0xd2,
|
||||
0xf4, 0xf6, 0x46, 0xe5, 0x74, 0x1f, 0x2a, 0xa1, 0x64, 0xc5, 0x2d, 0x59, 0x3b, 0x28, 0x2c, 0xff,
|
||||
0x21, 0x43, 0x5c, 0x14, 0xe3, 0x23, 0x48, 0x88, 0xb2, 0xb2, 0x0b, 0xd3, 0xdb, 0xeb, 0x41, 0x8f,
|
||||
0x42, 0x55, 0xa9, 0x5b, 0xa6, 0x8b, 0x4d, 0x77, 0xe2, 0x0a, 0x7f, 0x9e, 0x0d, 0x7a, 0x03, 0x92,
|
||||
0xfd, 0xa1, 0x66, 0x98, 0xaa, 0xa1, 0xb3, 0x88, 0x52, 0xb5, 0xf4, 0xc9, 0x8b, 0x8d, 0x44, 0x9d,
|
||||
0xca, 0x9a, 0x3b, 0x4a, 0x82, 0x29, 0x9b, 0x3a, 0xba, 0x08, 0xf1, 0x21, 0x36, 0x06, 0x43, 0xc2,
|
||||
0xca, 0x12, 0x55, 0xc4, 0x09, 0x7d, 0x00, 0x32, 0x25, 0x44, 0x5e, 0x66, 0x77, 0x17, 0x2a, 0x9c,
|
||||
0x2d, 0x15, 0x8f, 0x2d, 0x95, 0xae, 0xc7, 0x96, 0x5a, 0x92, 0x5e, 0xfc, 0xf4, 0xd7, 0x0d, 0x49,
|
||||
0x61, 0x16, 0xa8, 0x0e, 0xd9, 0x91, 0xe6, 0x12, 0xb5, 0x47, 0xcb, 0x46, 0xaf, 0x8f, 0x31, 0x17,
|
||||
0x97, 0xe7, 0x0b, 0x22, 0x0a, 0x2b, 0x42, 0x4f, 0x53, 0x2b, 0x2e, 0xd2, 0xd1, 0x26, 0xe4, 0x98,
|
||||
0x93, 0xbe, 0x35, 0x1e, 0x1b, 0x44, 0x65, 0x75, 0x8f, 0xb3, 0xba, 0x2f, 0x53, 0x79, 0x9d, 0x89,
|
||||
0xef, 0xd2, 0x0e, 0x5c, 0x81, 0x94, 0xae, 0x11, 0x8d, 0x43, 0x12, 0x0c, 0x92, 0xa4, 0x02, 0xa6,
|
||||
0x7c, 0x13, 0x56, 0x7c, 0xd6, 0xb9, 0x1c, 0x92, 0xe4, 0x5e, 0x66, 0x62, 0x06, 0xbc, 0x09, 0x6b,
|
||||
0x26, 0x9e, 0x12, 0xf5, 0x34, 0x3a, 0xc5, 0xd0, 0x88, 0xea, 0x1e, 0x84, 0x2d, 0xfe, 0x0f, 0xcb,
|
||||
0x7d, 0xaf, 0xf8, 0x1c, 0x0b, 0x0c, 0x9b, 0xf5, 0xa5, 0x0c, 0x76, 0x19, 0x92, 0x9a, 0x6d, 0x73,
|
||||
0x40, 0x9a, 0x01, 0x12, 0x9a, 0x6d, 0x33, 0xd5, 0x0d, 0x58, 0x65, 0x39, 0x3a, 0xd8, 0x9d, 0x8c,
|
||||
0x88, 0x70, 0x92, 0x61, 0x98, 0x15, 0xaa, 0x50, 0xb8, 0x9c, 0x61, 0xaf, 0x41, 0x16, 0x1f, 0x18,
|
||||
0x3a, 0x36, 0xfb, 0x98, 0xe3, 0xb2, 0x0c, 0x97, 0xf1, 0x84, 0x0c, 0x74, 0x1d, 0x72, 0xb6, 0x63,
|
||||
0xd9, 0x96, 0x8b, 0x1d, 0x55, 0xd3, 0x75, 0x07, 0xbb, 0x6e, 0x7e, 0x99, 0xfb, 0xf3, 0xe4, 0x55,
|
||||
0x2e, 0x2e, 0xe7, 0x41, 0xde, 0xd1, 0x88, 0x86, 0x72, 0x10, 0x25, 0x53, 0x37, 0x2f, 0x95, 0xa2,
|
||||
0x9b, 0x19, 0x85, 0x7e, 0x96, 0xbf, 0x8f, 0x82, 0xfc, 0xc0, 0x22, 0x18, 0xdd, 0x02, 0x99, 0xb6,
|
||||
0x89, 0xb1, 0x6f, 0xf9, 0x2c, 0x3e, 0x77, 0x8c, 0x81, 0x89, 0xf5, 0x3d, 0x77, 0xd0, 0x3d, 0xb4,
|
||||
0xb1, 0xc2, 0xc0, 0x01, 0x3a, 0x45, 0x42, 0x74, 0x5a, 0x83, 0x98, 0x63, 0x4d, 0x4c, 0x9d, 0xb1,
|
||||
0x2c, 0xa6, 0xf0, 0x03, 0x6a, 0x40, 0xd2, 0x67, 0x89, 0xfc, 0x77, 0x2c, 0x59, 0xa1, 0x2c, 0xa1,
|
||||
0x1c, 0x16, 0x02, 0x25, 0xd1, 0x13, 0x64, 0xa9, 0x41, 0xca, 0x1f, 0x5e, 0x82, 0x6d, 0xaf, 0x46,
|
||||
0xd8, 0x99, 0x19, 0x7a, 0x0b, 0x56, 0xfd, 0xde, 0xfb, 0xc5, 0xe3, 0x8c, 0xcb, 0xf9, 0x0a, 0x51,
|
||||
0xbd, 0x10, 0xad, 0x54, 0x3e, 0x80, 0x12, 0x2c, 0xaf, 0x19, 0xad, 0x9a, 0x6c, 0x12, 0x5d, 0x85,
|
||||
0x94, 0x6b, 0x0c, 0x4c, 0x8d, 0x4c, 0x1c, 0x2c, 0x98, 0x37, 0x13, 0x50, 0x2d, 0x9e, 0x12, 0x6c,
|
||||
0xb2, 0x47, 0xce, 0x99, 0x36, 0x13, 0xa0, 0x2d, 0xf8, 0x9f, 0x7f, 0x50, 0x67, 0x5e, 0x38, 0xcb,
|
||||
0x90, 0xaf, 0xea, 0x78, 0x9a, 0xf2, 0x0f, 0x12, 0xc4, 0xf9, 0xc3, 0x08, 0xb4, 0x41, 0x3a, 0xbb,
|
||||
0x0d, 0x91, 0x45, 0x6d, 0x88, 0x9e, 0xbf, 0x0d, 0x55, 0x00, 0x3f, 0x4c, 0x37, 0x2f, 0x97, 0xa2,
|
||||
0x9b, 0xe9, 0xed, 0x2b, 0xf3, 0x8e, 0x78, 0x88, 0x1d, 0x63, 0x20, 0xde, 0x7d, 0xc0, 0xa8, 0xfc,
|
||||
0x8b, 0x04, 0x29, 0x5f, 0x8f, 0xaa, 0x90, 0xf5, 0xe2, 0x52, 0x1f, 0x8d, 0xb4, 0x81, 0xa0, 0xe2,
|
||||
0xfa, 0xc2, 0xe0, 0x6e, 0x8f, 0xb4, 0x81, 0x92, 0x16, 0xf1, 0xd0, 0xc3, 0xd9, 0x6d, 0x8d, 0x2c,
|
||||
0x68, 0x6b, 0x88, 0x47, 0xd1, 0xf3, 0xf1, 0x28, 0xd4, 0x71, 0xf9, 0x54, 0xc7, 0xcb, 0xbf, 0x4b,
|
||||
0xb0, 0xdc, 0x98, 0xb2, 0xf0, 0xf5, 0x7f, 0xb3, 0x55, 0x0f, 0x05, 0xb7, 0x74, 0xac, 0xab, 0x73,
|
||||
0x3d, 0xbb, 0x36, 0xef, 0x31, 0x1c, 0xf3, 0xac, 0x77, 0xc8, 0xf3, 0xd2, 0x99, 0xf5, 0xf0, 0xbb,
|
||||
0x08, 0xac, 0xce, 0xe1, 0xff, 0x7b, 0xbd, 0x0c, 0xbf, 0xde, 0xd8, 0x2b, 0xbe, 0xde, 0xf8, 0xc2,
|
||||
0xd7, 0xfb, 0x6d, 0x04, 0x92, 0x6d, 0x36, 0xa5, 0xb5, 0xd1, 0x3f, 0x31, 0x7b, 0xaf, 0x40, 0xca,
|
||||
0xb6, 0x46, 0x2a, 0xd7, 0xc8, 0x4c, 0x93, 0xb4, 0xad, 0x91, 0x32, 0x47, 0xb3, 0xd8, 0x6b, 0x1a,
|
||||
0xcc, 0xf1, 0xd7, 0xd0, 0x84, 0xc4, 0xe9, 0x07, 0xe5, 0x40, 0x86, 0x97, 0x42, 0x6c, 0x4d, 0x37,
|
||||
0x69, 0x0d, 0xd8, 0x1a, 0x26, 0xcd, 0x6f, 0x79, 0x3c, 0x6c, 0x8e, 0x54, 0x04, 0x8e, 0x5a, 0xf0,
|
||||
0x25, 0x43, 0x2c, 0x6e, 0xf9, 0x45, 0x13, 0x4b, 0x11, 0xb8, 0xf2, 0x97, 0x12, 0xc0, 0x2e, 0xad,
|
||||
0x2c, 0xcb, 0x97, 0xee, 0x3b, 0x2e, 0x0b, 0x41, 0x0d, 0xdd, 0x5c, 0x5c, 0xd4, 0x34, 0x71, 0x7f,
|
||||
0xc6, 0x0d, 0xc6, 0x5d, 0x87, 0xec, 0x8c, 0xdb, 0x2e, 0xf6, 0x82, 0x39, 0xc3, 0x89, 0xbf, 0x86,
|
||||
0x74, 0x30, 0x51, 0x32, 0x07, 0x81, 0x53, 0xf9, 0x47, 0x09, 0x52, 0x2c, 0xa6, 0x3d, 0x4c, 0xb4,
|
||||
0x50, 0x0f, 0xa5, 0xf3, 0xf7, 0x70, 0x1d, 0x80, 0xbb, 0x71, 0x8d, 0x27, 0x58, 0x30, 0x2b, 0xc5,
|
||||
0x24, 0x1d, 0xe3, 0x09, 0x46, 0xef, 0xf9, 0x05, 0x8f, 0xfe, 0x75, 0xc1, 0xc5, 0xc4, 0xf0, 0xca,
|
||||
0x7e, 0x09, 0x12, 0xe6, 0x64, 0xac, 0xd2, 0xe5, 0x43, 0xe6, 0x6c, 0x35, 0x27, 0xe3, 0xee, 0xd4,
|
||||
0x2d, 0x7f, 0x06, 0x89, 0xee, 0x94, 0x2d, 0xe2, 0x94, 0xa2, 0x8e, 0x65, 0x89, 0xed, 0x8f, 0x6f,
|
||||
0xdd, 0x49, 0x2a, 0x60, 0xcb, 0x0e, 0x02, 0x99, 0xae, 0x79, 0xde, 0xcf, 0x02, 0xfa, 0x8d, 0x2a,
|
||||
0xaf, 0xb8, 0xe2, 0x8b, 0xe5, 0xfe, 0xc6, 0x4f, 0x12, 0xa4, 0x03, 0xe3, 0x06, 0xbd, 0x03, 0x17,
|
||||
0x6a, 0xbb, 0xfb, 0xf5, 0x7b, 0x6a, 0x73, 0x47, 0xbd, 0xbd, 0x5b, 0xbd, 0xa3, 0xde, 0x6f, 0xdd,
|
||||
0x6b, 0xed, 0x7f, 0xd2, 0xca, 0x2d, 0x15, 0x2e, 0x1e, 0x1d, 0x97, 0x50, 0x00, 0x7b, 0xdf, 0x7c,
|
||||
0x6c, 0x5a, 0x9f, 0xd3, 0x77, 0xbe, 0x16, 0x36, 0xa9, 0xd6, 0x3a, 0x8d, 0x56, 0x37, 0x27, 0x15,
|
||||
0x2e, 0x1c, 0x1d, 0x97, 0x56, 0x03, 0x16, 0xd5, 0x9e, 0x8b, 0x4d, 0x32, 0x6f, 0x50, 0xdf, 0xdf,
|
||||
0xdb, 0x6b, 0x76, 0x73, 0x91, 0x39, 0x03, 0xf1, 0x0f, 0xe2, 0x3a, 0xac, 0x86, 0x0d, 0x5a, 0xcd,
|
||||
0xdd, 0x5c, 0xb4, 0x80, 0x8e, 0x8e, 0x4b, 0xcb, 0x01, 0x74, 0xcb, 0x18, 0x15, 0x92, 0x5f, 0x7c,
|
||||
0x55, 0x5c, 0xfa, 0xe6, 0xeb, 0xa2, 0x44, 0x33, 0xcb, 0x86, 0x66, 0x04, 0x7a, 0x1b, 0x2e, 0x75,
|
||||
0x9a, 0x77, 0x5a, 0x8d, 0x1d, 0x75, 0xaf, 0x73, 0x47, 0xed, 0x7e, 0xda, 0x6e, 0x04, 0xb2, 0x5b,
|
||||
0x39, 0x3a, 0x2e, 0xa5, 0x45, 0x4a, 0x8b, 0xd0, 0x6d, 0xa5, 0xf1, 0x60, 0xbf, 0xdb, 0xc8, 0x49,
|
||||
0x1c, 0xdd, 0x76, 0xf0, 0x81, 0x45, 0x30, 0x43, 0xdf, 0x84, 0xcb, 0x67, 0xa0, 0xfd, 0xc4, 0x56,
|
||||
0x8f, 0x8e, 0x4b, 0xd9, 0xb6, 0x83, 0xf9, 0xfb, 0x61, 0x16, 0x15, 0xc8, 0xcf, 0x5b, 0xec, 0xb7,
|
||||
0xf7, 0x3b, 0xd5, 0xdd, 0x5c, 0xa9, 0x90, 0x3b, 0x3a, 0x2e, 0x65, 0xbc, 0x61, 0x48, 0xf1, 0xb3,
|
||||
0xcc, 0x6a, 0x1f, 0x3f, 0x3b, 0x29, 0x4a, 0xcf, 0x4f, 0x8a, 0xd2, 0x6f, 0x27, 0x45, 0xe9, 0xe9,
|
||||
0xcb, 0xe2, 0xd2, 0xf3, 0x97, 0xc5, 0xa5, 0x9f, 0x5f, 0x16, 0x97, 0x1e, 0xbe, 0x3f, 0x30, 0xc8,
|
||||
0x70, 0xd2, 0xab, 0xf4, 0xad, 0xf1, 0x56, 0xf0, 0xc7, 0xe7, 0xec, 0x93, 0xff, 0x08, 0x3e, 0xfd,
|
||||
0xc3, 0xb4, 0x17, 0x67, 0xf2, 0x5b, 0x7f, 0x06, 0x00, 0x00, 0xff, 0xff, 0x46, 0xcf, 0x37, 0x28,
|
||||
0x59, 0x0f, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func (m *PartSetHeader) Marshal() (dAtA []byte, err error) {
|
||||
@@ -1634,6 +1796,127 @@ func (m *CommitSig) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func (m *ExtendedCommit) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *ExtendedCommit) MarshalTo(dAtA []byte) (int, error) {
|
||||
size := m.Size()
|
||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *ExtendedCommit) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.ExtendedSignatures) > 0 {
|
||||
for iNdEx := len(m.ExtendedSignatures) - 1; iNdEx >= 0; iNdEx-- {
|
||||
{
|
||||
size, err := m.ExtendedSignatures[iNdEx].MarshalToSizedBuffer(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = encodeVarintTypes(dAtA, i, uint64(size))
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x22
|
||||
}
|
||||
}
|
||||
{
|
||||
size, err := m.BlockID.MarshalToSizedBuffer(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = encodeVarintTypes(dAtA, i, uint64(size))
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x1a
|
||||
if m.Round != 0 {
|
||||
i = encodeVarintTypes(dAtA, i, uint64(m.Round))
|
||||
i--
|
||||
dAtA[i] = 0x10
|
||||
}
|
||||
if m.Height != 0 {
|
||||
i = encodeVarintTypes(dAtA, i, uint64(m.Height))
|
||||
i--
|
||||
dAtA[i] = 0x8
|
||||
}
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func (m *ExtendedCommitSig) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *ExtendedCommitSig) MarshalTo(dAtA []byte) (int, error) {
|
||||
size := m.Size()
|
||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *ExtendedCommitSig) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.ExtensionSignature) > 0 {
|
||||
i -= len(m.ExtensionSignature)
|
||||
copy(dAtA[i:], m.ExtensionSignature)
|
||||
i = encodeVarintTypes(dAtA, i, uint64(len(m.ExtensionSignature)))
|
||||
i--
|
||||
dAtA[i] = 0x32
|
||||
}
|
||||
if len(m.Extension) > 0 {
|
||||
i -= len(m.Extension)
|
||||
copy(dAtA[i:], m.Extension)
|
||||
i = encodeVarintTypes(dAtA, i, uint64(len(m.Extension)))
|
||||
i--
|
||||
dAtA[i] = 0x2a
|
||||
}
|
||||
if len(m.Signature) > 0 {
|
||||
i -= len(m.Signature)
|
||||
copy(dAtA[i:], m.Signature)
|
||||
i = encodeVarintTypes(dAtA, i, uint64(len(m.Signature)))
|
||||
i--
|
||||
dAtA[i] = 0x22
|
||||
}
|
||||
n11, err11 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp):])
|
||||
if err11 != nil {
|
||||
return 0, err11
|
||||
}
|
||||
i -= n11
|
||||
i = encodeVarintTypes(dAtA, i, uint64(n11))
|
||||
i--
|
||||
dAtA[i] = 0x1a
|
||||
if len(m.ValidatorAddress) > 0 {
|
||||
i -= len(m.ValidatorAddress)
|
||||
copy(dAtA[i:], m.ValidatorAddress)
|
||||
i = encodeVarintTypes(dAtA, i, uint64(len(m.ValidatorAddress)))
|
||||
i--
|
||||
dAtA[i] = 0x12
|
||||
}
|
||||
if m.BlockIdFlag != 0 {
|
||||
i = encodeVarintTypes(dAtA, i, uint64(m.BlockIdFlag))
|
||||
i--
|
||||
dAtA[i] = 0x8
|
||||
}
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func (m *Proposal) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
@@ -1661,12 +1944,12 @@ func (m *Proposal) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i--
|
||||
dAtA[i] = 0x3a
|
||||
}
|
||||
n10, err10 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp):])
|
||||
if err10 != nil {
|
||||
return 0, err10
|
||||
n12, err12 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp):])
|
||||
if err12 != nil {
|
||||
return 0, err12
|
||||
}
|
||||
i -= n10
|
||||
i = encodeVarintTypes(dAtA, i, uint64(n10))
|
||||
i -= n12
|
||||
i = encodeVarintTypes(dAtA, i, uint64(n12))
|
||||
i--
|
||||
dAtA[i] = 0x32
|
||||
{
|
||||
@@ -2117,6 +2400,59 @@ func (m *CommitSig) Size() (n int) {
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *ExtendedCommit) Size() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
if m.Height != 0 {
|
||||
n += 1 + sovTypes(uint64(m.Height))
|
||||
}
|
||||
if m.Round != 0 {
|
||||
n += 1 + sovTypes(uint64(m.Round))
|
||||
}
|
||||
l = m.BlockID.Size()
|
||||
n += 1 + l + sovTypes(uint64(l))
|
||||
if len(m.ExtendedSignatures) > 0 {
|
||||
for _, e := range m.ExtendedSignatures {
|
||||
l = e.Size()
|
||||
n += 1 + l + sovTypes(uint64(l))
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *ExtendedCommitSig) Size() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
if m.BlockIdFlag != 0 {
|
||||
n += 1 + sovTypes(uint64(m.BlockIdFlag))
|
||||
}
|
||||
l = len(m.ValidatorAddress)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovTypes(uint64(l))
|
||||
}
|
||||
l = github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp)
|
||||
n += 1 + l + sovTypes(uint64(l))
|
||||
l = len(m.Signature)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovTypes(uint64(l))
|
||||
}
|
||||
l = len(m.Extension)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovTypes(uint64(l))
|
||||
}
|
||||
l = len(m.ExtensionSignature)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovTypes(uint64(l))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *Proposal) Size() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
@@ -3823,6 +4159,399 @@ func (m *CommitSig) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *ExtendedCommit) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: ExtendedCommit: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: ExtendedCommit: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType)
|
||||
}
|
||||
m.Height = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.Height |= int64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Round", wireType)
|
||||
}
|
||||
m.Round = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.Round |= int32(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 3:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field BlockID", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if err := m.BlockID.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 4:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field ExtendedSignatures", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.ExtendedSignatures = append(m.ExtendedSignatures, ExtendedCommitSig{})
|
||||
if err := m.ExtendedSignatures[len(m.ExtendedSignatures)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipTypes(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *ExtendedCommitSig) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: ExtendedCommitSig: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: ExtendedCommitSig: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field BlockIdFlag", wireType)
|
||||
}
|
||||
m.BlockIdFlag = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.BlockIdFlag |= BlockIDFlag(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddress", wireType)
|
||||
}
|
||||
var byteLen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
byteLen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if byteLen < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
postIndex := iNdEx + byteLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.ValidatorAddress = append(m.ValidatorAddress[:0], dAtA[iNdEx:postIndex]...)
|
||||
if m.ValidatorAddress == nil {
|
||||
m.ValidatorAddress = []byte{}
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 3:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.Timestamp, dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 4:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType)
|
||||
}
|
||||
var byteLen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
byteLen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if byteLen < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
postIndex := iNdEx + byteLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...)
|
||||
if m.Signature == nil {
|
||||
m.Signature = []byte{}
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 5:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Extension", wireType)
|
||||
}
|
||||
var byteLen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
byteLen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if byteLen < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
postIndex := iNdEx + byteLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Extension = append(m.Extension[:0], dAtA[iNdEx:postIndex]...)
|
||||
if m.Extension == nil {
|
||||
m.Extension = []byte{}
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 6:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field ExtensionSignature", wireType)
|
||||
}
|
||||
var byteLen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
byteLen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if byteLen < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
postIndex := iNdEx + byteLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.ExtensionSignature = append(m.ExtensionSignature[:0], dAtA[iNdEx:postIndex]...)
|
||||
if m.ExtensionSignature == nil {
|
||||
m.ExtensionSignature = []byte{}
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipTypes(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *Proposal) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
|
||||
@@ -142,6 +142,28 @@ message CommitSig {
|
||||
bytes signature = 4;
|
||||
}
|
||||
|
||||
message ExtendedCommit {
|
||||
int64 height = 1;
|
||||
int32 round = 2;
|
||||
BlockID block_id = 3
|
||||
[(gogoproto.nullable) = false, (gogoproto.customname) = "BlockID"];
|
||||
repeated ExtendedCommitSig extended_signatures = 4 [(gogoproto.nullable) = false];
|
||||
}
|
||||
|
||||
// ExtendedCommitSig retains all the same fields as CommitSig but adds vote
|
||||
// extension-related fields.
|
||||
message ExtendedCommitSig {
|
||||
BlockIDFlag block_id_flag = 1;
|
||||
bytes validator_address = 2;
|
||||
google.protobuf.Timestamp timestamp = 3
|
||||
[(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
|
||||
bytes signature = 4;
|
||||
// Vote extension data
|
||||
bytes extension = 5;
|
||||
// Vote extension signature
|
||||
bytes extension_signature = 6;
|
||||
}
|
||||
|
||||
message Proposal {
|
||||
SignedMsgType type = 1;
|
||||
int64 height = 2;
|
||||
|
||||
@@ -20,16 +20,27 @@ import (
|
||||
|
||||
// Config is a RPC server configuration.
|
||||
type Config struct {
|
||||
// see netutil.LimitListener
|
||||
// The maximum number of connections that will be accepted by the listener.
|
||||
// See https://godoc.org/golang.org/x/net/netutil#LimitListener
|
||||
MaxOpenConnections int
|
||||
// mirrors http.Server#ReadTimeout
|
||||
|
||||
// Used to set the HTTP server's per-request read timeout.
|
||||
// See https://godoc.org/net/http#Server.ReadTimeout
|
||||
ReadTimeout time.Duration
|
||||
// mirrors http.Server#WriteTimeout
|
||||
|
||||
// Used to set the HTTP server's per-request write timeout. Note that this
|
||||
// affects ALL methods on the server, so it should not be set too low. This
|
||||
// should be used as a safety valve, not a resource-control timeout.
|
||||
//
|
||||
// See https://godoc.org/net/http#Server.WriteTimeout
|
||||
WriteTimeout time.Duration
|
||||
// MaxBodyBytes controls the maximum number of bytes the
|
||||
// server will read parsing the request body.
|
||||
|
||||
// Controls the maximum number of bytes the server will read parsing the
|
||||
// request body.
|
||||
MaxBodyBytes int64
|
||||
// mirrors http.Server#MaxHeaderBytes
|
||||
|
||||
// Controls the maximum size of a request header.
|
||||
// See https://godoc.org/net/http#Server.MaxHeaderBytes
|
||||
MaxHeaderBytes int
|
||||
}
|
||||
|
||||
@@ -38,9 +49,9 @@ func DefaultConfig() *Config {
|
||||
return &Config{
|
||||
MaxOpenConnections: 0, // unlimited
|
||||
ReadTimeout: 10 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
MaxBodyBytes: int64(1000000), // 1MB
|
||||
MaxHeaderBytes: 1 << 20, // same as the net/http default
|
||||
WriteTimeout: 0, // no default timeout
|
||||
MaxBodyBytes: 1000000, // 1MB
|
||||
MaxHeaderBytes: 1 << 20, // same as the net/http default
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,11 +9,16 @@ import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
|
||||
)
|
||||
|
||||
// DefaultRPCTimeout is the default context timeout for calls to any RPC method
|
||||
// that does not override it with a more specific timeout.
|
||||
const DefaultRPCTimeout = 60 * time.Second
|
||||
|
||||
// RegisterRPCFuncs adds a route to mux for each non-websocket function in the
|
||||
// funcMap, and also a root JSON-RPC POST handler.
|
||||
func RegisterRPCFuncs(mux *http.ServeMux, funcMap map[string]*RPCFunc, logger log.Logger) {
|
||||
@@ -32,11 +37,12 @@ func RegisterRPCFuncs(mux *http.ServeMux, funcMap map[string]*RPCFunc, logger lo
|
||||
|
||||
// RPCFunc contains the introspected type information for a function.
|
||||
type RPCFunc struct {
|
||||
f reflect.Value // underlying rpc function
|
||||
param reflect.Type // the parameter struct, or nil
|
||||
result reflect.Type // the non-error result type, or nil
|
||||
args []argInfo // names and type information (for URL decoding)
|
||||
ws bool // websocket only
|
||||
f reflect.Value // underlying rpc function
|
||||
param reflect.Type // the parameter struct, or nil
|
||||
result reflect.Type // the non-error result type, or nil
|
||||
args []argInfo // names and type information (for URL decoding)
|
||||
timeout time.Duration // default request timeout, 0 means none
|
||||
ws bool // websocket only
|
||||
}
|
||||
|
||||
// argInfo records the name of a field, along with a bit to tell whether the
|
||||
@@ -52,6 +58,12 @@ type argInfo struct {
|
||||
// with the resulting argument value. It reports an error if parameter parsing
|
||||
// fails, otherwise it returns the result from the wrapped function.
|
||||
func (rf *RPCFunc) Call(ctx context.Context, params json.RawMessage) (interface{}, error) {
|
||||
// If ctx has its own deadline we will respect it; otherwise use rf.timeout.
|
||||
if _, ok := ctx.Deadline(); !ok && rf.timeout > 0 {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(ctx, rf.timeout)
|
||||
defer cancel()
|
||||
}
|
||||
args, err := rf.parseParams(ctx, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -74,6 +86,11 @@ func (rf *RPCFunc) Call(ctx context.Context, params json.RawMessage) (interface{
|
||||
return returns[0].Interface(), nil
|
||||
}
|
||||
|
||||
// Timeout updates rf to include a default timeout for calls to rf. This
|
||||
// timeout is used if one is not already provided on the request context.
|
||||
// Setting d == 0 means there will be no timeout. Returns rf to allow chaining.
|
||||
func (rf *RPCFunc) Timeout(d time.Duration) *RPCFunc { rf.timeout = d; return rf }
|
||||
|
||||
// parseParams parses the parameters of a JSON-RPC request and returns the
|
||||
// corresponding argument values. On success, the first argument value will be
|
||||
// the value of ctx.
|
||||
@@ -129,7 +146,9 @@ func (rf *RPCFunc) adjustParams(data []byte) (json.RawMessage, error) {
|
||||
// func(context.Context, *T) (R, error)
|
||||
//
|
||||
// for an arbitrary struct type T and type R. NewRPCFunc will panic if f does
|
||||
// not have one of these forms.
|
||||
// not have one of these forms. A newly-constructed RPCFunc has a default
|
||||
// timeout of DefaultRPCTimeout; use the Timeout method to adjust this as
|
||||
// needed.
|
||||
func NewRPCFunc(f interface{}) *RPCFunc {
|
||||
rf, err := newRPCFunc(f)
|
||||
if err != nil {
|
||||
@@ -215,10 +234,11 @@ func newRPCFunc(f interface{}) (*RPCFunc, error) {
|
||||
}
|
||||
|
||||
return &RPCFunc{
|
||||
f: fv,
|
||||
param: ptype,
|
||||
result: rtype,
|
||||
args: args,
|
||||
f: fv,
|
||||
param: ptype,
|
||||
result: rtype,
|
||||
args: args,
|
||||
timeout: DefaultRPCTimeout, // until overridden
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
+11
-12
@@ -260,10 +260,9 @@ paths:
|
||||
operationId: events
|
||||
description: |
|
||||
Fetch a batch of events posted by the consensus node and matching a
|
||||
specified query.
|
||||
specified query string.
|
||||
|
||||
The query grammar is defined in
|
||||
https://godoc.org/github.com/tendermint/tendermint/internal/pubsub/query/syntax.
|
||||
The query grammar is defined in [pubsub/query/syntax](https://godoc.org/github.com/tendermint/tendermint/internal/pubsub/query/syntax).
|
||||
An empty query matches all events; otherwise a query comprises one or
|
||||
more terms comparing event metadata to target values. For example, to
|
||||
select new block events:
|
||||
@@ -275,13 +274,13 @@ paths:
|
||||
|
||||
tm.event = 'Tx' AND tx.hash = 'EA7B33F'
|
||||
|
||||
The comparison operators include "=", "<", "<=", ">", ">=", and
|
||||
"CONTAINS". Operands may be strings (in single quotes), numbers, dates,
|
||||
or timestamps. In addition, the "EXISTS" operator allows you to check
|
||||
The comparison operators include `=`, `<`, `<=`, `>`, `>=`, and
|
||||
`CONTAINS`. Operands may be strings (in single quotes), numbers, dates,
|
||||
or timestamps. In addition, the `EXISTS` operator allows you to check
|
||||
for the presence of an attribute regardless of its value.
|
||||
|
||||
Tendermint defines a tm.event attribute for all events. Transactions
|
||||
are also assigned tx.hash and tx.height attributes. Other attributes
|
||||
Tendermint defines a `tm.event` attribute for all events. Transactions
|
||||
are also assigned `tx.hash` and `tx.height` attributes. Other attributes
|
||||
are provided by the application as ABCI Event records. The name of the
|
||||
event in the query is formed by combining the type and attribute key
|
||||
with a period. For example, given:
|
||||
@@ -295,16 +294,16 @@ paths:
|
||||
},
|
||||
}}
|
||||
|
||||
the query may refer to the names "reward.address", "reward.amount", and
|
||||
"reward.balance", as in:
|
||||
the query may refer to the names`"reward.address`,`"reward.amount`, and
|
||||
`reward.balance`, as in:
|
||||
|
||||
reward.address EXISTS AND reward.balance > 45
|
||||
|
||||
The node maintains a log of all events within an operator-defined time
|
||||
window. The /events method returns the most recent items from the log
|
||||
that match the query. Each item returned includes a cursor that marks
|
||||
its location in the log. Cursors can be passed via the "before" and
|
||||
"after" parameters to fetch events earlier in the log.
|
||||
its location in the log. Cursors can be passed via the `before` and
|
||||
`after` parameters to fetch events earlier in the log.
|
||||
parameters:
|
||||
- in: query
|
||||
name: filter
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/creachadair/tomledit"
|
||||
"github.com/creachadair/tomledit/transform"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/tendermint/tendermint/config"
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
// metricsdiff is a tool for generating a diff between two different files containing
|
||||
// prometheus metrics. metricsdiff outputs which metrics have been added, removed,
|
||||
// or have different sets of labels between the two files.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
"github.com/prometheus/common/expfmt"
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintf(os.Stderr, `Usage: %[1]s <path1> <path2>
|
||||
|
||||
Generate the diff between the two files of Prometheus metrics.
|
||||
The input should have the format output by a Prometheus HTTP endpoint.
|
||||
The tool indicates which metrics have been added, removed, or use different
|
||||
label sets from path1 to path2.
|
||||
|
||||
`, filepath.Base(os.Args[0]))
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
}
|
||||
|
||||
// Diff contains the set of metrics that were modified between two files
|
||||
// containing prometheus metrics output.
|
||||
type Diff struct {
|
||||
Adds []string
|
||||
Removes []string
|
||||
|
||||
Changes []LabelDiff
|
||||
}
|
||||
|
||||
// LabelDiff describes the label changes between two versions of the same metric.
|
||||
type LabelDiff struct {
|
||||
Metric string
|
||||
Adds []string
|
||||
Removes []string
|
||||
}
|
||||
|
||||
type parsedMetric struct {
|
||||
name string
|
||||
labels []string
|
||||
}
|
||||
|
||||
type metricsList []parsedMetric
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if flag.NArg() != 2 {
|
||||
log.Fatalf("Usage is '%s <path1> <path2>', got %d arguments",
|
||||
filepath.Base(os.Args[0]), flag.NArg())
|
||||
}
|
||||
fa, err := os.Open(flag.Arg(0))
|
||||
if err != nil {
|
||||
log.Fatalf("Open: %v", err)
|
||||
}
|
||||
defer fa.Close()
|
||||
fb, err := os.Open(flag.Arg(1))
|
||||
if err != nil {
|
||||
log.Fatalf("Open: %v", err)
|
||||
}
|
||||
defer fb.Close()
|
||||
md, err := DiffFromReaders(fa, fb)
|
||||
if err != nil {
|
||||
log.Fatalf("Generating diff: %v", err)
|
||||
}
|
||||
fmt.Print(md)
|
||||
}
|
||||
|
||||
// DiffFromReaders parses the metrics present in the readers a and b and
|
||||
// determines which metrics were added and removed in b.
|
||||
func DiffFromReaders(a, b io.Reader) (Diff, error) {
|
||||
var parser expfmt.TextParser
|
||||
amf, err := parser.TextToMetricFamilies(a)
|
||||
if err != nil {
|
||||
return Diff{}, err
|
||||
}
|
||||
bmf, err := parser.TextToMetricFamilies(b)
|
||||
if err != nil {
|
||||
return Diff{}, err
|
||||
}
|
||||
|
||||
md := Diff{}
|
||||
aList := toList(amf)
|
||||
bList := toList(bmf)
|
||||
|
||||
i, j := 0, 0
|
||||
for i < len(aList) || j < len(bList) {
|
||||
for j < len(bList) && (i >= len(aList) || bList[j].name < aList[i].name) {
|
||||
md.Adds = append(md.Adds, bList[j].name)
|
||||
j++
|
||||
}
|
||||
for i < len(aList) && j < len(bList) && aList[i].name == bList[j].name {
|
||||
adds, removes := listDiff(aList[i].labels, bList[j].labels)
|
||||
if len(adds) > 0 || len(removes) > 0 {
|
||||
md.Changes = append(md.Changes, LabelDiff{
|
||||
Metric: aList[i].name,
|
||||
Adds: adds,
|
||||
Removes: removes,
|
||||
})
|
||||
}
|
||||
i++
|
||||
j++
|
||||
}
|
||||
for i < len(aList) && (j >= len(bList) || aList[i].name < bList[j].name) {
|
||||
md.Removes = append(md.Removes, aList[i].name)
|
||||
i++
|
||||
}
|
||||
}
|
||||
return md, nil
|
||||
}
|
||||
|
||||
func toList(l map[string]*dto.MetricFamily) metricsList {
|
||||
r := make([]parsedMetric, len(l))
|
||||
var idx int
|
||||
for name, family := range l {
|
||||
r[idx] = parsedMetric{
|
||||
name: name,
|
||||
labels: labelsToStringList(family.Metric[0].Label),
|
||||
}
|
||||
idx++
|
||||
}
|
||||
sort.Sort(metricsList(r))
|
||||
return r
|
||||
}
|
||||
|
||||
func labelsToStringList(ls []*dto.LabelPair) []string {
|
||||
r := make([]string, len(ls))
|
||||
for i, l := range ls {
|
||||
r[i] = l.GetName()
|
||||
}
|
||||
return sort.StringSlice(r)
|
||||
}
|
||||
|
||||
func listDiff(a, b []string) ([]string, []string) {
|
||||
adds, removes := []string{}, []string{}
|
||||
i, j := 0, 0
|
||||
for i < len(a) || j < len(b) {
|
||||
for j < len(b) && (i >= len(a) || b[j] < a[i]) {
|
||||
adds = append(adds, b[j])
|
||||
j++
|
||||
}
|
||||
for i < len(a) && j < len(b) && a[i] == b[j] {
|
||||
i++
|
||||
j++
|
||||
}
|
||||
for i < len(a) && (j >= len(b) || a[i] < b[j]) {
|
||||
removes = append(removes, a[i])
|
||||
i++
|
||||
}
|
||||
}
|
||||
return adds, removes
|
||||
}
|
||||
|
||||
func (m metricsList) Len() int { return len(m) }
|
||||
func (m metricsList) Less(i, j int) bool { return m[i].name < m[j].name }
|
||||
func (m metricsList) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
|
||||
|
||||
func (m Diff) String() string {
|
||||
var s strings.Builder
|
||||
if len(m.Adds) > 0 || len(m.Removes) > 0 {
|
||||
fmt.Fprintln(&s, "Metric changes:")
|
||||
}
|
||||
if len(m.Adds) > 0 {
|
||||
for _, add := range m.Adds {
|
||||
fmt.Fprintf(&s, "+++ %s\n", add)
|
||||
}
|
||||
}
|
||||
if len(m.Removes) > 0 {
|
||||
for _, rem := range m.Removes {
|
||||
fmt.Fprintf(&s, "--- %s\n", rem)
|
||||
}
|
||||
}
|
||||
if len(m.Changes) > 0 {
|
||||
fmt.Fprintln(&s, "Label changes:")
|
||||
for _, ld := range m.Changes {
|
||||
fmt.Fprintf(&s, "Metric: %s\n", ld.Metric)
|
||||
for _, add := range ld.Adds {
|
||||
fmt.Fprintf(&s, "+++ %s\n", add)
|
||||
}
|
||||
for _, rem := range ld.Removes {
|
||||
fmt.Fprintf(&s, "--- %s\n", rem)
|
||||
}
|
||||
}
|
||||
}
|
||||
return s.String()
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package main_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
metricsdiff "github.com/tendermint/tendermint/scripts/metricsgen/metricsdiff"
|
||||
)
|
||||
|
||||
func TestDiff(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
aContents string
|
||||
bContents string
|
||||
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "labels",
|
||||
aContents: `
|
||||
metric_one{label_one="content", label_two="content"} 0
|
||||
`,
|
||||
bContents: `
|
||||
metric_one{label_three="content", label_four="content"} 0
|
||||
`,
|
||||
want: `Label changes:
|
||||
Metric: metric_one
|
||||
+++ label_three
|
||||
+++ label_four
|
||||
--- label_one
|
||||
--- label_two
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "metrics",
|
||||
aContents: `
|
||||
metric_one{label_one="content"} 0
|
||||
`,
|
||||
bContents: `
|
||||
metric_two{label_two="content"} 0
|
||||
`,
|
||||
want: `Metric changes:
|
||||
+++ metric_two
|
||||
--- metric_one
|
||||
`,
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
bufA := bytes.NewBuffer([]byte{})
|
||||
bufB := bytes.NewBuffer([]byte{})
|
||||
_, err := io.WriteString(bufA, tc.aContents)
|
||||
require.NoError(t, err)
|
||||
_, err = io.WriteString(bufB, tc.bContents)
|
||||
require.NoError(t, err)
|
||||
md, err := metricsdiff.DiffFromReaders(bufA, bufB)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.want, md.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
// metricsgen is a code generation tool for creating constructors for Tendermint
|
||||
// metrics types.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintf(os.Stderr, `Usage: %[1]s -struct <struct>
|
||||
|
||||
Generate constructors for the metrics type specified by -struct contained in
|
||||
the current directory. The tool creates a new file in the current directory
|
||||
containing the generated code.
|
||||
|
||||
Options:
|
||||
`, filepath.Base(os.Args[0]))
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
}
|
||||
|
||||
const metricsPackageName = "github.com/go-kit/kit/metrics"
|
||||
|
||||
const (
|
||||
metricNameTag = "metrics_name"
|
||||
labelsTag = "metrics_labels"
|
||||
bucketTypeTag = "metrics_buckettype"
|
||||
bucketSizeTag = "metrics_bucketsizes"
|
||||
)
|
||||
|
||||
var (
|
||||
dir = flag.String("dir", ".", "Path to the directory containing the target package")
|
||||
strct = flag.String("struct", "Metrics", "Struct to parse for metrics")
|
||||
)
|
||||
|
||||
var bucketType = map[string]string{
|
||||
"exprange": "stdprometheus.ExponentialBucketsRange",
|
||||
"exp": "stdprometheus.ExponentialBuckets",
|
||||
"lin": "stdprometheus.LinearBuckets",
|
||||
}
|
||||
|
||||
var tmpl = template.Must(template.New("tmpl").Parse(`// Code generated by metricsgen. DO NOT EDIT.
|
||||
|
||||
package {{ .Package }}
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/metrics/discard"
|
||||
prometheus "github.com/go-kit/kit/metrics/prometheus"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func PrometheusMetrics(namespace string, labelsAndValues...string) *Metrics {
|
||||
labels := []string{}
|
||||
for i := 0; i < len(labelsAndValues); i += 2 {
|
||||
labels = append(labels, labelsAndValues[i])
|
||||
}
|
||||
return &Metrics{
|
||||
{{ range $metric := .ParsedMetrics }}
|
||||
{{- $metric.FieldName }}: prometheus.New{{ $metric.TypeName }}From(stdprometheus.{{$metric.TypeName }}Opts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "{{$metric.MetricName }}",
|
||||
Help: "{{ $metric.Description }}",
|
||||
{{ if ne $metric.HistogramOptions.BucketType "" }}
|
||||
Buckets: {{ $metric.HistogramOptions.BucketType }}({{ $metric.HistogramOptions.BucketSizes }}),
|
||||
{{ else if ne $metric.HistogramOptions.BucketSizes "" }}
|
||||
Buckets: []float64{ {{ $metric.HistogramOptions.BucketSizes }} },
|
||||
{{ end }}
|
||||
{{- if eq (len $metric.Labels) 0 }}
|
||||
}, labels).With(labelsAndValues...),
|
||||
{{ else }}
|
||||
}, append(labels, {{$metric.Labels}})).With(labelsAndValues...),
|
||||
{{ end }}
|
||||
{{- end }}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func NopMetrics() *Metrics {
|
||||
return &Metrics{
|
||||
{{- range $metric := .ParsedMetrics }}
|
||||
{{ $metric.FieldName }}: discard.New{{ $metric.TypeName }}(),
|
||||
{{- end }}
|
||||
}
|
||||
}
|
||||
`))
|
||||
|
||||
// ParsedMetricField is the data parsed for a single field of a metric struct.
|
||||
type ParsedMetricField struct {
|
||||
TypeName string
|
||||
FieldName string
|
||||
MetricName string
|
||||
Description string
|
||||
Labels string
|
||||
|
||||
HistogramOptions HistogramOpts
|
||||
}
|
||||
|
||||
type HistogramOpts struct {
|
||||
BucketType string
|
||||
BucketSizes string
|
||||
}
|
||||
|
||||
// TemplateData is all of the data required for rendering a metric file template.
|
||||
type TemplateData struct {
|
||||
Package string
|
||||
ParsedMetrics []ParsedMetricField
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if *strct == "" {
|
||||
log.Fatal("You must specify a non-empty -struct")
|
||||
}
|
||||
td, err := ParseMetricsDir(".", *strct)
|
||||
if err != nil {
|
||||
log.Fatalf("Parsing file: %v", err)
|
||||
}
|
||||
out := filepath.Join(*dir, "metrics.gen.go")
|
||||
f, err := os.Create(out)
|
||||
if err != nil {
|
||||
log.Fatalf("Opening file: %v", err)
|
||||
}
|
||||
err = GenerateMetricsFile(f, td)
|
||||
if err != nil {
|
||||
log.Fatalf("Generating code: %v", err)
|
||||
}
|
||||
}
|
||||
func ignoreTestFiles(f fs.FileInfo) bool {
|
||||
return !strings.Contains(f.Name(), "_test.go")
|
||||
}
|
||||
|
||||
// ParseMetricsDir parses the dir and scans for a struct matching structName,
|
||||
// ignoring all test files. ParseMetricsDir iterates the fields of the metrics
|
||||
// struct and builds a TemplateData using the data obtained from the abstract syntax tree.
|
||||
func ParseMetricsDir(dir string, structName string) (TemplateData, error) {
|
||||
fs := token.NewFileSet()
|
||||
d, err := parser.ParseDir(fs, dir, ignoreTestFiles, parser.ParseComments)
|
||||
if err != nil {
|
||||
return TemplateData{}, err
|
||||
}
|
||||
if len(d) > 1 {
|
||||
return TemplateData{}, fmt.Errorf("multiple packages found in %s", dir)
|
||||
}
|
||||
if len(d) == 0 {
|
||||
return TemplateData{}, fmt.Errorf("no go pacakges found in %s", dir)
|
||||
}
|
||||
|
||||
// Grab the package name.
|
||||
var pkgName string
|
||||
var pkg *ast.Package
|
||||
for pkgName, pkg = range d {
|
||||
}
|
||||
td := TemplateData{
|
||||
Package: pkgName,
|
||||
}
|
||||
// Grab the metrics struct
|
||||
m, mPkgName, err := findMetricsStruct(pkg.Files, structName)
|
||||
if err != nil {
|
||||
return TemplateData{}, err
|
||||
}
|
||||
for _, f := range m.Fields.List {
|
||||
if !isMetric(f.Type, mPkgName) {
|
||||
continue
|
||||
}
|
||||
pmf := parseMetricField(f)
|
||||
td.ParsedMetrics = append(td.ParsedMetrics, pmf)
|
||||
}
|
||||
|
||||
return td, err
|
||||
}
|
||||
|
||||
// GenerateMetricsFile executes the metrics file template, writing the result
|
||||
// into the io.Writer.
|
||||
func GenerateMetricsFile(w io.Writer, td TemplateData) error {
|
||||
b := []byte{}
|
||||
buf := bytes.NewBuffer(b)
|
||||
err := tmpl.Execute(buf, td)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b, err = format.Source(buf.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(w, bytes.NewBuffer(b))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func findMetricsStruct(files map[string]*ast.File, structName string) (*ast.StructType, string, error) {
|
||||
var (
|
||||
st *ast.StructType
|
||||
)
|
||||
for _, file := range files {
|
||||
mPkgName, err := extractMetricsPackageName(file.Imports)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("unable to determine metrics package name: %v", err)
|
||||
}
|
||||
if !ast.FilterFile(file, func(name string) bool {
|
||||
return name == structName
|
||||
}) {
|
||||
continue
|
||||
}
|
||||
ast.Inspect(file, func(n ast.Node) bool {
|
||||
switch f := n.(type) {
|
||||
case *ast.TypeSpec:
|
||||
if f.Name.Name == structName {
|
||||
var ok bool
|
||||
st, ok = f.Type.(*ast.StructType)
|
||||
if !ok {
|
||||
err = fmt.Errorf("found identifier for %q of wrong type", structName)
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if st != nil {
|
||||
return st, mPkgName, nil
|
||||
}
|
||||
}
|
||||
return nil, "", fmt.Errorf("target struct %q not found in dir", structName)
|
||||
}
|
||||
|
||||
func parseMetricField(f *ast.Field) ParsedMetricField {
|
||||
pmf := ParsedMetricField{
|
||||
Description: extractHelpMessage(f.Doc),
|
||||
MetricName: extractFieldName(f.Names[0].String(), f.Tag),
|
||||
FieldName: f.Names[0].String(),
|
||||
TypeName: extractTypeName(f.Type),
|
||||
Labels: extractLabels(f.Tag),
|
||||
}
|
||||
if pmf.TypeName == "Histogram" {
|
||||
pmf.HistogramOptions = extractHistogramOptions(f.Tag)
|
||||
}
|
||||
return pmf
|
||||
}
|
||||
|
||||
func extractTypeName(e ast.Expr) string {
|
||||
return strings.TrimPrefix(path.Ext(types.ExprString(e)), ".")
|
||||
}
|
||||
|
||||
func extractHelpMessage(cg *ast.CommentGroup) string {
|
||||
if cg == nil {
|
||||
return ""
|
||||
}
|
||||
var help []string //nolint: prealloc
|
||||
for _, c := range cg.List {
|
||||
mt := strings.TrimPrefix(c.Text, "//metrics:")
|
||||
if mt != c.Text {
|
||||
return strings.TrimSpace(mt)
|
||||
}
|
||||
help = append(help, strings.TrimSpace(strings.TrimPrefix(c.Text, "//")))
|
||||
}
|
||||
return strings.Join(help, " ")
|
||||
}
|
||||
|
||||
func isMetric(e ast.Expr, mPkgName string) bool {
|
||||
return strings.Contains(types.ExprString(e), fmt.Sprintf("%s.", mPkgName))
|
||||
}
|
||||
|
||||
func extractLabels(bl *ast.BasicLit) string {
|
||||
if bl != nil {
|
||||
t := reflect.StructTag(strings.Trim(bl.Value, "`"))
|
||||
if v := t.Get(labelsTag); v != "" {
|
||||
var res []string
|
||||
for _, s := range strings.Split(v, ",") {
|
||||
res = append(res, strconv.Quote(strings.TrimSpace(s)))
|
||||
}
|
||||
return strings.Join(res, ",")
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func extractFieldName(name string, tag *ast.BasicLit) string {
|
||||
if tag != nil {
|
||||
t := reflect.StructTag(strings.Trim(tag.Value, "`"))
|
||||
if v := t.Get(metricNameTag); v != "" {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return toSnakeCase(name)
|
||||
}
|
||||
|
||||
func extractHistogramOptions(tag *ast.BasicLit) HistogramOpts {
|
||||
h := HistogramOpts{}
|
||||
if tag != nil {
|
||||
t := reflect.StructTag(strings.Trim(tag.Value, "`"))
|
||||
if v := t.Get(bucketTypeTag); v != "" {
|
||||
h.BucketType = bucketType[v]
|
||||
}
|
||||
if v := t.Get(bucketSizeTag); v != "" {
|
||||
h.BucketSizes = v
|
||||
}
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
func extractMetricsPackageName(imports []*ast.ImportSpec) (string, error) {
|
||||
for _, i := range imports {
|
||||
u, err := strconv.Unquote(i.Path.Value)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if u == metricsPackageName {
|
||||
if i.Name != nil {
|
||||
return i.Name.Name, nil
|
||||
}
|
||||
return path.Base(u), nil
|
||||
}
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
var capitalChange = regexp.MustCompile("([a-z0-9])([A-Z])")
|
||||
|
||||
func toSnakeCase(str string) string {
|
||||
snake := capitalChange.ReplaceAllString(str, "${1}_${2}")
|
||||
return strings.ToLower(snake)
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
package main_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
metricsgen "github.com/tendermint/tendermint/scripts/metricsgen"
|
||||
)
|
||||
|
||||
const testDataDir = "./testdata"
|
||||
|
||||
func TestSimpleTemplate(t *testing.T) {
|
||||
m := metricsgen.ParsedMetricField{
|
||||
TypeName: "Histogram",
|
||||
FieldName: "MyMetric",
|
||||
MetricName: "request_count",
|
||||
Description: "how many requests were made since the start of the process",
|
||||
Labels: "first, second, third",
|
||||
}
|
||||
td := metricsgen.TemplateData{
|
||||
Package: "mypack",
|
||||
ParsedMetrics: []metricsgen.ParsedMetricField{m},
|
||||
}
|
||||
b := bytes.NewBuffer([]byte{})
|
||||
err := metricsgen.GenerateMetricsFile(b, td)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to parse template %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromData(t *testing.T) {
|
||||
infos, err := ioutil.ReadDir(testDataDir)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to open file %v", err)
|
||||
}
|
||||
for _, dir := range infos {
|
||||
t.Run(dir.Name(), func(t *testing.T) {
|
||||
if !dir.IsDir() {
|
||||
t.Fatalf("expected file %s to be directory", dir.Name())
|
||||
}
|
||||
dirName := path.Join(testDataDir, dir.Name())
|
||||
pt, err := metricsgen.ParseMetricsDir(dirName, "Metrics")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to parse from dir %q: %v", dir, err)
|
||||
}
|
||||
outFile := path.Join(dirName, "out.go")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to open file %s: %v", outFile, err)
|
||||
}
|
||||
of, err := os.Create(outFile)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to open file %s: %v", outFile, err)
|
||||
}
|
||||
defer os.Remove(outFile)
|
||||
if err := metricsgen.GenerateMetricsFile(of, pt); err != nil {
|
||||
t.Fatalf("unable to generate metrics file %s: %v", outFile, err)
|
||||
}
|
||||
if _, err := parser.ParseFile(token.NewFileSet(), outFile, nil, parser.AllErrors); err != nil {
|
||||
t.Fatalf("unable to parse generated file %s: %v", outFile, err)
|
||||
}
|
||||
bNew, err := ioutil.ReadFile(outFile)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to read generated file %s: %v", outFile, err)
|
||||
}
|
||||
goldenFile := path.Join(dirName, "metrics.gen.go")
|
||||
bOld, err := ioutil.ReadFile(goldenFile)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to read file %s: %v", goldenFile, err)
|
||||
}
|
||||
if !bytes.Equal(bNew, bOld) {
|
||||
t.Fatalf("newly generated code in file %s does not match golden file %s\n"+
|
||||
"if the output of the metricsgen tool is expected to change run the following make target: \n"+
|
||||
"\tmake metrics", outFile, goldenFile)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseMetricsStruct(t *testing.T) {
|
||||
const pkgName = "mypkg"
|
||||
metricsTests := []struct {
|
||||
name string
|
||||
shouldError bool
|
||||
metricsStruct string
|
||||
expected metricsgen.TemplateData
|
||||
}{
|
||||
{
|
||||
name: "basic",
|
||||
metricsStruct: `type Metrics struct {
|
||||
myGauge metrics.Gauge
|
||||
}`,
|
||||
expected: metricsgen.TemplateData{
|
||||
Package: pkgName,
|
||||
ParsedMetrics: []metricsgen.ParsedMetricField{
|
||||
{
|
||||
TypeName: "Gauge",
|
||||
FieldName: "myGauge",
|
||||
MetricName: "my_gauge",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "histogram",
|
||||
metricsStruct: "type Metrics struct {\n" +
|
||||
"myHistogram metrics.Histogram `metrics_buckettype:\"exp\" metrics_bucketsizes:\"1, 100, .8\"`\n" +
|
||||
"}",
|
||||
expected: metricsgen.TemplateData{
|
||||
Package: pkgName,
|
||||
ParsedMetrics: []metricsgen.ParsedMetricField{
|
||||
{
|
||||
TypeName: "Histogram",
|
||||
FieldName: "myHistogram",
|
||||
MetricName: "my_histogram",
|
||||
|
||||
HistogramOptions: metricsgen.HistogramOpts{
|
||||
BucketType: "stdprometheus.ExponentialBuckets",
|
||||
BucketSizes: "1, 100, .8",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "labeled name",
|
||||
metricsStruct: "type Metrics struct {\n" +
|
||||
"myCounter metrics.Counter `metrics_name:\"new_name\"`\n" +
|
||||
"}",
|
||||
expected: metricsgen.TemplateData{
|
||||
Package: pkgName,
|
||||
ParsedMetrics: []metricsgen.ParsedMetricField{
|
||||
{
|
||||
TypeName: "Counter",
|
||||
FieldName: "myCounter",
|
||||
MetricName: "new_name",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "metric labels",
|
||||
metricsStruct: "type Metrics struct {\n" +
|
||||
"myCounter metrics.Counter `metrics_labels:\"label1,label2\"`\n" +
|
||||
"}",
|
||||
expected: metricsgen.TemplateData{
|
||||
Package: pkgName,
|
||||
ParsedMetrics: []metricsgen.ParsedMetricField{
|
||||
{
|
||||
TypeName: "Counter",
|
||||
FieldName: "myCounter",
|
||||
MetricName: "my_counter",
|
||||
Labels: "\"label1\",\"label2\"",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ignore non-metric field",
|
||||
metricsStruct: `type Metrics struct {
|
||||
myCounter metrics.Counter
|
||||
nonMetric string
|
||||
}`,
|
||||
expected: metricsgen.TemplateData{
|
||||
Package: pkgName,
|
||||
ParsedMetrics: []metricsgen.ParsedMetricField{
|
||||
{
|
||||
TypeName: "Counter",
|
||||
FieldName: "myCounter",
|
||||
MetricName: "my_counter",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, testCase := range metricsTests {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
dir, err := os.MkdirTemp(os.TempDir(), "metricsdir")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create directory: %v", err)
|
||||
}
|
||||
defer os.Remove(dir)
|
||||
f, err := os.Create(filepath.Join(dir, "metrics.go"))
|
||||
if err != nil {
|
||||
t.Fatalf("unable to open file: %v", err)
|
||||
}
|
||||
pkgLine := fmt.Sprintf("package %s\n", pkgName)
|
||||
importClause := `
|
||||
import(
|
||||
"github.com/go-kit/kit/metrics"
|
||||
)
|
||||
`
|
||||
|
||||
_, err = io.WriteString(f, pkgLine)
|
||||
require.NoError(t, err)
|
||||
_, err = io.WriteString(f, importClause)
|
||||
require.NoError(t, err)
|
||||
_, err = io.WriteString(f, testCase.metricsStruct)
|
||||
require.NoError(t, err)
|
||||
|
||||
td, err := metricsgen.ParseMetricsDir(dir, "Metrics")
|
||||
if testCase.shouldError {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, testCase.expected, td)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAliasedMetric(t *testing.T) {
|
||||
aliasedData := `
|
||||
package mypkg
|
||||
|
||||
import(
|
||||
mymetrics "github.com/go-kit/kit/metrics"
|
||||
)
|
||||
type Metrics struct {
|
||||
m mymetrics.Gauge
|
||||
}
|
||||
`
|
||||
dir, err := os.MkdirTemp(os.TempDir(), "metricsdir")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create directory: %v", err)
|
||||
}
|
||||
defer os.Remove(dir)
|
||||
f, err := os.Create(filepath.Join(dir, "metrics.go"))
|
||||
if err != nil {
|
||||
t.Fatalf("unable to open file: %v", err)
|
||||
}
|
||||
_, err = io.WriteString(f, aliasedData)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to write to file: %v", err)
|
||||
}
|
||||
td, err := metricsgen.ParseMetricsDir(dir, "Metrics")
|
||||
require.NoError(t, err)
|
||||
|
||||
expected :=
|
||||
metricsgen.TemplateData{
|
||||
Package: "mypkg",
|
||||
ParsedMetrics: []metricsgen.ParsedMetricField{
|
||||
{
|
||||
TypeName: "Gauge",
|
||||
FieldName: "m",
|
||||
MetricName: "m",
|
||||
},
|
||||
},
|
||||
}
|
||||
require.Equal(t, expected, td)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user