Merge branch 'master' into hulatown/adr-043-nft

This commit is contained in:
vincent 2021-05-17 18:10:52 +08:00 committed by GitHub
commit be60788e23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
117 changed files with 1920 additions and 2245 deletions

40
.github/labeler.yml vendored
View File

@ -1,41 +1,41 @@
"Scope: x/auth":
"C:x/auth":
- x/auth/**/*
"Scope: x/authz":
"C:x/authz":
- x/authz/**/*
"Scope: x/bank":
"C:x/bank":
- x/bank/**/*
"Scope: x/capability":
"C:x/capability":
- x/capability/**/*
"Scope: x/crisis":
"C:x/crisis":
- x/crisis/**/*
"Scope: x/distribution":
"C:x/distribution":
- x/distribution/**/*
"Scope: x/evidence":
"C:x/evidence":
- x/evidence/**/*
"Scope: x/feegrant":
"C:x/feegrant":
- x/feegrant/**/*
"Scope: x/genutil":
"C:x/genutil":
- x/genutil/**/*
"Scope: x/gov":
"C:x/gov":
- x/gov/**/*
"Scope: x/mint":
"C:x/mint":
- x/mint/**/*
"Scope: x/params":
"C:x/params":
- x/params/**/*
"Scope: Simulations":
"C:Simulations":
- x/simulation/**/*
- x/*/simulation/**/*
"Scope: x/slashing":
"C:x/slashing":
- x/slashing/**/*
"Scope: x/staking":
"C:x/staking":
- x/staking/**/*
"Scope: x/upgrade":
"C:x/upgrade":
- x/upgrade/**/*
"Scope: Cosmovisor":
"C:Cosmovisor":
- cosmovisor/**/*
"Scope: Rosetta":
"C:Rosetta":
- contrib/rosetta/**/*
"Scope: Keys":
"C:Keys":
- client/keys/**/*
"Type: Build":
- Makefile
@ -47,7 +47,7 @@
- buf.yaml
- .mergify.yml
- .golangci.yml
"Scope: CLI":
"C:CLI":
- client/**/*
- x/*/client/**/*
"Type: Docs":

View File

@ -7,7 +7,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v3
- uses: actions/stale@v3.0.18
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-pr-message: "This pull request has been automatically marked as stale because it has not had

View File

@ -38,9 +38,11 @@ Ref: https://keepachangelog.com/en/1.0.0/
* [\#9205](https://github.com/cosmos/cosmos-sdk/pull/9205) Improve readability in `abci` handleQueryP2P
* [\#9235](https://github.com/cosmos/cosmos-sdk/pull/9235) CreateMembershipProof/CreateNonMembershipProof now returns an error
if input key is empty, or input data contains empty key.
* [\#9314](https://github.com/cosmos/cosmos-sdk/pull/9314) Update Rosetta SDK to upstream's latest release.
### Features
* [\#8077](https://github.com/cosmos/cosmos-sdk/pull/8077) Added support for grpc-web, enabling browsers to communicate with a chain's gRPC server
* [\#8965](https://github.com/cosmos/cosmos-sdk/pull/8965) cosmos reflection now provides more information on the application such as: deliverable msgs, sdk.Config info etc (still in alpha stage).
* [\#8559](https://github.com/cosmos/cosmos-sdk/pull/8559) Added Protobuf compatible secp256r1 ECDSA signatures.
* [\#8786](https://github.com/cosmos/cosmos-sdk/pull/8786) Enabled secp256r1 in x/auth.
@ -67,6 +69,7 @@ if input key is empty, or input data contains empty key.
* via `ServiceMsg` TypeURLs (e.g. `message.action='/cosmos.bank.v1beta1.Msg/Send'`) does not work anymore,
* via legacy `msg.Type()` (e.g. `message.action='send'`) is being deprecated, new `Msg`s won't emit these events.
* Please use concrete `Msg` TypeURLs instead (e.g. `message.action='/cosmos.bank.v1beta1.MsgSend'`).
* [\#9291](https://github.com/cosmos/cosmos-sdk/pull/9291) Migration scripts prior to v0.38 have been removed from the CLI `migrate` command. The oldest supported migration is v0.39->v0.42.
### API Breaking Changes
@ -136,7 +139,6 @@ if input key is empty, or input data contains empty key.
* (keyring) [#\8635](https://github.com/cosmos/cosmos-sdk/issues/8635) Remove hardcoded default passphrase value on `NewMnemonic`
* (x/bank) [\#8434](https://github.com/cosmos/cosmos-sdk/pull/8434) Fix legacy REST API `GET /bank/total` and `GET /bank/total/{denom}` in swagger
* (x/slashing) [\#8427](https://github.com/cosmos/cosmos-sdk/pull/8427) Fix query signing infos command
* (server) [\#8399](https://github.com/cosmos/cosmos-sdk/pull/8399) fix gRPC-web flag default value
* (x/bank) [\#9229](https://github.com/cosmos/cosmos-sdk/pull/9229) Now zero coin balances cannot be added to balances & supply stores. If any denom becomes zero corresponding key gets deleted from store.
### Deprecated

View File

@ -4,19 +4,16 @@
Only the following release series are currently supported and receive bug fixes:
The `0.37.x` release series will continue receiving bug fixes until the Cosmos Hub
migrates to a newer release of the Cosmos-SDK.
* **0.39 «Launchpad»** will be supported until 6 months after **0.42.0** is published. A fairly strict **bugfix-only** rule applies to pull requests that are requested to be included into a stable point-release.
* **0.42 «Stargate»** is the latest stable release.
* **0.37** will continue receiving bug fixes until the Cosmos Hub migrates to a newer release series of the Cosmos-SDK.
* **0.39 «Launchpad»** will be supported until 6 months after **0.40.0** is published. A fairly strict **bugfix-only** rule applies to pull requests that are requested to be included into a stable point-release.
The **0.39 «Launchpad»** release series is maintained in compliance with the **Stable Release Policy** as described in this document.
The **0.42 «Stargate»** release series is maintained in compliance with the **Stable Release Policy** as described in this document.
## Stable Release Policy
This policy presently applies *only* to the following release series:
* **0.39 «Launchpad»**
* **0.42 «Stargate»**
### Point Releases
@ -26,7 +23,7 @@ and must follow the [Point Release Procedure](CONTRIBUTING.md).
### Rationale
Unlike in-development `master` branch snapshots, **Cosmos-SDK** releases are subject to much wider adoption,
and by a significantly different demographic of users. During development, changes in the `master` branch
and by a significantly different demographic of users. During development, changes in the `master` branch
affect SDK users, application developers, early adopters, and other advanced users that elect to use
unstable experimental software at their own risk.
@ -48,6 +45,10 @@ priority is to minimise the risk caused by changes that are not strictly require
be correlated with minimising the size of such changes. As such, the same bug may need to be fixed in different
ways in stable releases and `master` branch.
### Migrations
To smoothen the update to the latest stable release, the SDK includes a set of CLI commands for managing migrations between SDK versions, under the `migrate` subcommand. Only migration scripts between stable releases are included. For the current release, **0.39 «Launchpad»** and later migrations are supported.
### What qualifies as a Stable Release Update (SRU)
* **High-impact bugs**
@ -81,17 +82,17 @@ As rule of thumb, the following changes will **NOT** be automatically accepted i
* **State machine changes**.
* **Client application's code-breaking changes**, i.e. changes that prevent client applications to *build without modifications* to the client application's source code.
In some circumstances, PRs that don't meet the aforementioned criteria might be raised and asked to be granted a *Stable Release Exception*.
## Stable Release Exception - Procedure
1. Check that the bug is either fixed or not reproducible in `master`. It is, in general, not appropriate to release bug fixes for stable releases without first testing them in `master`. Please apply the label [0.39 «Launchpad»](https://github.com/cosmos/cosmos-sdk/labels/0.39%20LTS%20%28Launchpad%29) to the issue.
1. Check that the bug is either fixed or not reproducible in `master`. It is, in general, not appropriate to release bug fixes for stable releases without first testing them in `master`. Please apply the label [0.42 «Stargate»](https://github.com/cosmos/cosmos-sdk/labels/0.42%20LTS%20%28Stargate%29) to the issue.
2. Add a comment to the issue and ensure it contains the following information (see the bug template below):
* **[Impact]** An explanation of the bug on users and justification for backporting the fix to the stable release.
* A **[Test Case]** section containing detailed instructions on how to reproduce the bug.
* A **[Regression Potential]** section with a clear assessment on how regressions are most likely to manifest as a result of the pull request that aims to fix the bug in the target stable release.
3. **Stable Release Managers** will review and discuss the PR. Once *consensus* surrounding the rationale has been reached and the technical review has successfully concluded, the pull request will be merged in the respective point-release target branch (e.g. `release/launchpad/0.39.X` being `X` the Launchpad's upcoming point-release) and the PR included in the point-release's respective milestone (e.g. `0.39.5`).
3. **Stable Release Managers** will review and discuss the PR. Once *consensus* surrounding the rationale has been reached and the technical review has successfully concluded, the pull request will be merged in the respective point-release target branch (e.g. `release/v0.42.x`) and the PR included in the point-release's respective milestone (e.g. `0.42.5`).
### Stable Release Exception - Bug template
@ -102,7 +103,7 @@ Brief xplanation of the effects of the bug on users and a justification for back
#### Test Case
Detailed instructions on how to reproduce the bug on Launchpad's most recently published point-release.
Detailed instructions on how to reproduce the bug on Stargate's most recently published point-release.
#### Regression Potential
@ -122,10 +123,8 @@ Their responsibilites include:
* Approving/rejecting proposed changes to a stable release series.
* Executing the release process of stable point-releases in compliance with the [Point Release Procedure](CONTRIBUTING.md).
The Stable Release Managers are appointed by the Interchain Foundation.
The Stable Release Managers are appointed by the Interchain Foundation. Currently residing Stable Release Managers:
*Stable Release Managers* for the **0.39 «Launchpad»** release series follow:
* @alessio - Alessio Treglia
* @clevinson - Cory Levinson-
* @ethanfrey - Ethan Frey
* @clevinson - Cory Levinson
* @amaurym - Amaruy Martiny
* @robert-zaremba - Robert Zaremba

View File

@ -36,8 +36,11 @@ func runConfigCmd(cmd *cobra.Command, args []string) error {
switch len(args) {
case 0:
// print all client config fields to sdt out
s, _ := json.MarshalIndent(conf, "", "\t")
// print all client config fields to stdout
s, err := json.MarshalIndent(conf, "", "\t")
if err != nil {
return err
}
cmd.Println(string(s))
case 1:

File diff suppressed because one or more lines are too long

View File

@ -1545,7 +1545,7 @@ paths:
parameters:
- in: body
name: delegation
description: The password of the account to remove from the KMS
description: Delegate an amount of liquid coins to a validator
schema:
type: object
properties:
@ -1760,7 +1760,7 @@ paths:
parameters:
- in: body
name: delegation
description: The password of the account to remove from the KMS
description: Unbond an amount of bonded shares from a validator
schema:
type: object
properties:
@ -16945,6 +16945,11 @@ paths:
bond_denom:
type: string
description: bond_denom defines the bondable coin denomination.
power_reduction:
type: string
title: >-
power_reduction is the amount of staking tokens required
for 1 unit of consensus-engine power
description: >-
QueryParamsResponse is response type for the Query/Params RPC
method.
@ -31115,6 +31120,11 @@ definitions:
bond_denom:
type: string
description: bond_denom defines the bondable coin denomination.
power_reduction:
type: string
title: >-
power_reduction is the amount of staking tokens required for 1 unit of
consensus-engine power
description: Params defines the parameters for the staking module.
cosmos.staking.v1beta1.Pool:
type: object
@ -32355,6 +32365,11 @@ definitions:
bond_denom:
type: string
description: bond_denom defines the bondable coin denomination.
power_reduction:
type: string
title: >-
power_reduction is the amount of staking tokens required for 1
unit of consensus-engine power
description: QueryParamsResponse is response type for the Query/Params RPC method.
cosmos.staking.v1beta1.QueryPoolResponse:
type: object
@ -35881,7 +35896,14 @@ definitions:
transaction.
memo:
type: string
title: memo is any arbitrary memo to be added to the transaction
description: >-
memo is any arbitrary note/comment to be added to the transaction.
WARNING: in clients, any publicly exposed text should not be
called memo,
but should be called `note` instead (see
https://github.com/cosmos/cosmos-sdk/issues/9122).
timeout_height:
type: string
format: uint64
@ -36451,7 +36473,14 @@ definitions:
transaction.
memo:
type: string
title: memo is any arbitrary memo to be added to the transaction
description: >-
memo is any arbitrary note/comment to be added to the transaction.
WARNING: in clients, any publicly exposed text should not be called
memo,
but should be called `note` instead (see
https://github.com/cosmos/cosmos-sdk/issues/9122).
timeout_height:
type: string
format: uint64

View File

@ -556,7 +556,7 @@ paths:
parameters:
- in: body
name: delegation
description: The password of the account to remove from the KMS
description: Delegate an amount of liquid coins to a validator
schema:
type: object
properties:
@ -646,7 +646,7 @@ paths:
parameters:
- in: body
name: delegation
description: The password of the account to remove from the KMS
description: Unbond an amount of bonded shares from a validator
schema:
type: object
properties:

View File

@ -102,7 +102,6 @@ output
- armor encrypted private key (saved to file)
*/
func RunAddCmd(ctx client.Context, cmd *cobra.Command, args []string, inBuf *bufio.Reader) error {
// func RunAddCmd(cmd *cobra.Command, args []string, kb keyring.Keyring, inBuf *bufio.Reader) error {
var err error
name := args[0]

View File

@ -32,7 +32,7 @@ services:
- 8080
test_rosetta:
image: tendermintdev/rosetta-cli:v0.6.6
image: tendermintdev/rosetta-cli:v0.6.7
volumes:
- ./configuration:/rosetta/config:z
command: ["./config/run_tests.sh"]

View File

@ -2,7 +2,7 @@ FROM golang:1.15-alpine as build
RUN apk add git gcc libc-dev --no-cache
ARG ROSETTA_VERSION="v0.5.23"
ARG ROSETTA_VERSION="v0.6.7"
# build rosetta CLI
WORKDIR /rosetta

View File

@ -24,7 +24,7 @@ func (suite *SKSuite) TestPubKey() {
suite.True(suite.sk.PublicKey.Equal(&pk.PublicKey))
}
func (suite *SKSuite) Bytes() {
func (suite *SKSuite) TestBytes() {
bz := suite.sk.Bytes()
suite.Len(bz, 32)
var sk *PrivKey

View File

@ -26,7 +26,9 @@ type PubKey struct {
address tmcrypto.Address
}
// Address creates an ADR-28 address for ECDSA keys. protoName is a concrete proto structure id.
// Address gets the address associated with a pubkey. If no address exists, it returns a newly created ADR-28 address
// for ECDSA keys.
// protoName is a concrete proto structure id.
func (pk *PubKey) Address(protoName string) tmcrypto.Address {
if pk.address == nil {
pk.address = address.Hash(protoName, pk.Bytes())

View File

@ -48,9 +48,11 @@ func (suite *PKSuite) TestString() {
}
func (suite *PKSuite) TestBytes() {
require := suite.Require()
bz := suite.sk.Bytes()
fieldSize := (suite.sk.Curve.Params().BitSize + 7) / 8
suite.Len(bz, fieldSize)
var pk *PubKey
require.Nil(pk.Bytes())
suite.Nil(pk.Bytes())
}
func (suite *PKSuite) TestMarshal() {

View File

@ -33,6 +33,9 @@ func (m *PrivKey) Sign(msg []byte) ([]byte, error) {
// Bytes serialize the private key.
func (m *PrivKey) Bytes() []byte {
if m == nil {
return nil
}
return m.Secret.Bytes()
}

View File

@ -3,7 +3,7 @@ package secp256r1
import (
"testing"
proto "github.com/gogo/protobuf/proto"
"github.com/gogo/protobuf/proto"
"github.com/stretchr/testify/suite"
"github.com/tendermint/tendermint/crypto"
@ -41,7 +41,7 @@ func (suite *SKSuite) TestPubKey() {
suite.True(suite.sk.(*PrivKey).Secret.PublicKey.Equal(&pk.(*PubKey).Key.PublicKey))
}
func (suite *SKSuite) Bytes() {
func (suite *SKSuite) TestBytes() {
bz := suite.sk.Bytes()
suite.Len(bz, fieldSize)
var sk *PrivKey

View File

@ -15,6 +15,9 @@ func (m *PubKey) String() string {
// Bytes implements SDK PubKey interface.
func (m *PubKey) Bytes() []byte {
if m == nil {
return nil
}
return m.Key.Bytes()
}

View File

@ -44,6 +44,13 @@ func (suite *PKSuite) TestType() {
suite.Require().Equal(name, suite.pk.Type())
}
func (suite *PKSuite) TestBytes() {
bz := suite.pk.Bytes()
suite.Len(bz, fieldSize+1)
var pk *PubKey
suite.Nil(pk.Bytes())
}
func (suite *PKSuite) TestEquals() {
require := suite.Require()

View File

@ -73,3 +73,4 @@ Read about the [PROCESS](./PROCESS.md).
- [ADR 037: Governance Split Votes](./adr-037-gov-split-vote.md)
- [ADR 038: State Listening](./adr-038-state-listening.md)
- [ADR 039: Epoched Staking](./adr-039-epoched-staking.md)
- [ADR 040: Storage and SMT State Commitments](./adr-040-storage-and-smt-state-commitments.md)

View File

@ -7,6 +7,10 @@
- Alessio Treglia (@alessio)
- Frojdy Dymylja (@fdymylja)
## Changelog
- 2021-05-12: the external library [cosmos-rosetta-gateway](https://github.com/tendermint/cosmos-rosetta-gateway) has been moved within the SDK.
## Context
[Rosetta API](https://www.rosetta-api.org/) is an open-source specification and set of tools developed by Coinbase to
@ -34,7 +38,7 @@ The driving principles of the proposed design are:
We will achieve these delivering on these principles by the following:
1. There will be an external repo called [cosmos-rosetta-gateway](https://github.com/tendermint/cosmos-rosetta-gateway)
1. There will be a package `rosetta/lib`
for the implementation of the core Rosetta API features, particularly:
a. The types and interfaces (`Client`, `OfflineClient`...), this separates design from implementation detail.
b. The `Server` functionality as this is independent of the Cosmos SDK version.
@ -207,4 +211,3 @@ Proposed
## References
- https://www.rosetta-api.org/
- https://github.com/tendermint/cosmos-rosetta-gateway

View File

@ -0,0 +1,177 @@
# ADR 040: Storage and SMT State Commitments
## Changelog
- 2020-01-15: Draft
## Status
DRAFT Not Implemented
## Abstract
Sparse Merke Tree ([SMT](https://osf.io/8mcnh/)) is a version of a Merkle Tree with various storage and performance optimizations. This ADR defines a separation of state commitments from data storage and the SDK transition from IAVL to SMT.
## Context
Currently, Cosmos SDK uses IAVL for both state [commitments](https://cryptography.fandom.com/wiki/Commitment_scheme) and data storage.
IAVL has effectively become an orphaned project within the Cosmos ecosystem and it's proven to be an inefficient state commitment data structure.
In the current design, IAVL is used for both data storage and as a Merkle Tree for state commitments. IAVL is meant to be a standalone Merkelized key/value database, however it's using a KV DB engine to store all tree nodes. So, each node is stored in a separate record in the KV DB. This causes many inefficiencies and problems:
+ Each object query requires a tree traversal from the root. Subsequent queries for the same object are cached on the SDK level.
+ Each edge traversal requires a DB query.
+ Creating snapshots is [expensive](https://github.com/cosmos/cosmos-sdk/issues/7215#issuecomment-684804950). It takes about 30 seconds to export less than 100 MB of state (as of March 2020).
+ Updates in IAVL may trigger tree reorganization and possible O(log(n)) hashes re-computation, which can become a CPU bottleneck.
+ The node structure is pretty expensive - it contains a standard tree node elements (key, value, left and right element) and additional metadata such as height, version (which is not required by the SDK). The entire node is hashed, and that hash is used as the key in the underlying database, [ref](https://github.com/cosmos/iavl/blob/master/docs/node/node.md
).
Moreover, the IAVL project lacks support and a maintainer and we already see better and well-established alternatives. Instead of optimizing the IAVL, we are looking into other solutions for both storage and state commitments.
## Decision
We propose to separate the concerns of state commitment (**SC**), needed for consensus, and state storage (**SS**), needed for state machine. Finally we replace IAVL with [LazyLedgers' SMT](https://github.com/lazyledger/smt). LazyLedger SMT is based on Diem (called jellyfish) design [*] - it uses a compute-optimised SMT by replacing subtrees with only default values with a single node (same approach is used by Ethereum2) and implements compact proofs.
The storage model presented here doesn't deal with data structure nor serialization. It's a Key-Value database, where both key and value are binaries. The storage user is responsible for data serialization.
### Decouple state commitment from storage
Separation of storage and commitment (by the SMT) will allow the optimization of different components according to their usage and access patterns.
`SS` (SMT) is used to commit to a data and compute merkle proofs. `SC` is used to directly access data. To avoid collisions, both `SS` and `SC` will use a separate storage namespace (they could use the same database underneath). `SC` will store each `(key, value)` pair directly (map key -> value).
SMT is a merkle tree structure: we don't store keys directly. For every `(key, value)` pair, `hash(key)` is stored in a path (we hash a key to evenly distribute keys in the tree) and `hash(key, value)` in a leaf. Since we don't know a structure of a value (in particular if it contains the key) we hash both the key and the value in the `SC` leaf.
For data access we propose 2 additional KV buckets (namespaces for the key-value pairs, sometimes called [column family](https://github.com/facebook/rocksdb/wiki/Terminology)):
1. B1: `key → value`: the principal object storage, used by a state machine, behind the SDK `KVStore` interface: provides direct access by key and allows prefix iteration (KV DB backend must support it).
2. B2: `hash(key, value) → key`: a reverse index to get a key from an SMT path. Recall that SMT will store `(k, v)` as `(hash(k), hash(key, value))`. So, we can get an object value by composing `SMT_path → B2 → B1`.
3. we could use more buckets to optimize the app usage if needed.
Above, we propose to use a KV DB. However, for the state machine, we could use an RDBMS, which we discuss below.
### Requirements
State Storage requirements:
+ range queries
+ quick (key, value) access
+ creating a snapshot
+ historical versioning
+ pruning (garbage collection)
State Commitment requirements:
+ fast updates
+ tree path should be short
+ pruning (garbage collection)
### LazyLedger SMT for State Commitment
A Sparse Merkle tree is based on the idea of a complete Merkle tree of an intractable size. The assumption here is that as the size of the tree is intractable, there would only be a few leaf nodes with valid data blocks relative to the tree size, rendering a sparse tree.
### Snapshots for storage sync and state versioning
Below, with simple _snapshot_ we refer to a database snapshot mechanism, not to a _ABCI snapshot sync_. The latter will be referred as _snapshot sync_ (which will directly use DB snapshot as described below).
Database snapshot is a view of DB state at a certain time or transaction. It's not a full copy of a database (it would be too big), usually a snapshot mechanism is based on a _copy on write_ and it allows to efficiently deliver DB state at a certain stage.
Some DB engines support snapshotting. Hence, we propose to reuse that functionality for the state sync and versioning (described below). It will the supported DB engines to ones which efficiently implement snapshots. In a final section we will discuss evaluated DBs.
One of the Stargate core features is a _snapshot sync_ delivered in the `/snapshot` package. It provides a way to trustlessly sync a blockchain without repeating all transactions from the genesis. This feature is implemented in SDK and requires storage support. Currently IAVL is the only supported backend. It works by streaming to a client a snapshot of a `SS` at a certain version together with a header chain.
A new `SS` snapshot will be created in every `EndBlocker` and identified by a block height. The `rootmulti.Store` keeps track of the available snapshots to offer `SS` at a certain version. The `rootmulti.Store` implements the `CommitMultiStore` interface, which encapsulates a `Committer` interface. `Committer` has a `Commit`, `SetPruning`, `GetPruning` functions which will be used for creating and removing snapshots. The `rootStore.Commit` function creates a new snapshot and increments the version on each call, and checks if it needs to remove old versions. We will need to update the SMT interface to implement the `Committer` interface.
NOTE: `Commit` must be called exactly once per block. Otherwise we risk going out of sync for the version number and block height.
NOTE: For the SDK storage, we may consider splitting that interface into `Committer` and `PruningCommitter` - only the multiroot should implement `PruningCommitter` (cache and prefix store don't need pruning).
Number of historical versions for `abci.Query` and state sync snapshots is part of a node configuration, not a chain configuration (configuration implied by the blockchain consensus). A configuration should allow to specify number of past blocks and number of past blocks modulo some number (eg: 100 past blocks and one snapshot every 100 blocks for past 2000 blocks). Archival nodes can keep all past versions.
Pruning old snapshots is effectively done by a database. Whenever we update a record in `SC`, SMT won't update nodes - instead it creates new nodes on the update path, without removing the old one. Since we are snapshoting each block, we need to update that mechanism to immediately remove orphaned nodes from the storage. This is a safe operation - snapshots will keep track of the records which should be available for past versions.
To manage the active snapshots we will either us a DB _max number of snapshots_ option (if available), or will remove snapshots in the `EndBlocker`. The latter option can be done efficiently by identifying snapshots with block height.
#### Accessing old state versions
One of the functional requirements is to access old state. This is done through `abci.Query` structure. The version is specified by a block height (so we query for an object by a key `K` at block height `H`). The number of old versions supported for `abci.Query` is configurable. Accessing an old state is done by using available snapshots.
`abci.Query` doesn't need old state of `SC`. So, for efficiency, we should keep `SC` and `SS` in different databases (however using the same DB engine).
Moreover, SDK could provide a way to directly access the state. However, a state machine shouldn't do that - since the number of snapshots is configurable, it would lead to nondeterministic execution.
We positively [validated](https://github.com/cosmos/cosmos-sdk/discussions/8297) a versioning and snapshot mechanism for querying old state with regards to the database we evaluated.
### State Proofs
For any object stored in State Store (SS), we have corresponding object in `SC`. A proof for object `V` identified by a key `K` is a branch of `SC`, where the path corresponds to the key `hash(K)`, and the leaf is `hash(K, V)`.
### Rollbacks
We need to be able to process transactions and roll-back state updates if a transaction fails. This can be done in the following way: during transaction processing, we keep all state change requests (writes) in a `CacheWrapper` abstraction (as it's done today). Once we finish the block processing, in the `Endblocker`, we commit a root store - at that time, all changes are written to the SMT and to the `SS` and a snapshot is created.
### Committing to an object without saving it
We identified use-cases, where modules will need to save an object commitment without storing an object itself. Sometimes clients are receiving complex objects, and they have no way to prove a correctness of that object without knowing the storage layout. For those use cases it would be easier to commit to the object without storing it directly.
## Consequences
### Backwards Compatibility
This ADR doesn't introduce any SDK level API changes.
We change the storage layout of the state machine, a storage hard fork and network upgrade is required to incorporate these changes. SMT provides a merkle proof functionality, however it is not compatible with ICS23. Updating the proofs for ICS23 compatibility is required.
### Positive
+ Decoupling state from state commitment introduce better engineering opportunities for further optimizations and better storage patterns.
+ Performance improvements.
+ Joining SMT based camp which has wider and proven adoption than IAVL. Example projects which decided on SMT: Ethereum2, Diem (Libra), Trillan, Tezos, LazyLedger.
### Negative
+ Storage migration
+ LL SMT doesn't support pruning - we will need to add and test that functionality.
### Neutral
+ Deprecating IAVL, which is one of the core proposals of Cosmos Whitepaper.
## Alternative designs.
Most of the alternative designs were evaluated in [state commitments and storage report](https://paper.dropbox.com/published/State-commitments-and-storage-review--BDvA1MLwRtOx55KRihJ5xxLbBw-KeEB7eOd11pNrZvVtqUgL3h).
Ethereum research published [Verkle Tire](https://notes.ethereum.org/_N1mutVERDKtqGIEYc-Flw#fnref1) - an idea of combining polynomial commitments with merkle tree in order to reduce the tree height. This concept has a very good potential, but we think it's too early to implement it. The current, SMT based design could be easily updated to the Verkle Tire once other research implement all necessary libraries. The main advantage of the design described in this ADR is the separation of state commitments from the data storage and designing a more powerful interface.
## Further Discussions
### Evaluated KV Databases
We verified existing databases KV databases for evaluating snapshot support. The following databases provide efficient snapshot mechanism: Badger, RocksDB, [Pebble](https://github.com/cockroachdb/pebble). Databases which don't provide such support or are not production ready: boltdb, leveldb, goleveldb, membdb, lmdb.
### RDBMS
Use of RDBMS instead of simple KV store for state. Use of RDBMS will require an SDK API breaking change (`KVStore` interface), will allow better data extraction and indexing solutions. Instead of saving an object as a single blob of bytes, we could save it as record in a table in the state storage layer, and as a `hash(key, protobuf(object))` in the SMT as outlined above. To verify that an object registered in RDBMS is same as the one committed to SMT, one will need to load it from RDBMS, marshal using protobuf, hash and do SMT search.
### Off Chain Store
We were discussing use case where modules can use a support database, which is not automatically committed. Module will responsible for having a sound storage model and can optionally use the feature discussed in __Committing to an object without saving it_ section.
## References
+ [IAVL What's Next?](https://github.com/cosmos/cosmos-sdk/issues/7100)
+ [IAVL overview](https://docs.google.com/document/d/16Z_hW2rSAmoyMENO-RlAhQjAG3mSNKsQueMnKpmcBv0/edit#heading=h.yd2th7x3o1iv) of it's state v0.15
+ [State commitments and storage report](https://paper.dropbox.com/published/State-commitments-and-storage-review--BDvA1MLwRtOx55KRihJ5xxLbBw-KeEB7eOd11pNrZvVtqUgL3h)
+ [LazyLedger SMT](https://github.com/lazyledger/smt)
+ Facebook Diem (Libra) SMT [design](https://developers.diem.com/papers/jellyfish-merkle-tree/2021-01-14.pdf)
+ [Trillian Revocation Transparency](https://github.com/google/trillian/blob/master/docs/papers/RevocationTransparency.pdf), [Trillian Verifiable Data Structures](https://github.com/google/trillian/blob/master/docs/papers/VerifiableDataStructures.pdf).
+ Design and implementation [discussion](https://github.com/cosmos/cosmos-sdk/discussions/8297).

View File

@ -139,22 +139,24 @@ Modules must implement [interfaces](../building-modules/module-manager.md#applic
### `Msg` Services
Each module defines two [Protobuf services](https://developers.google.com/protocol-buffers/docs/proto#services): one `Msg` service to handle messages, and one gRPC `Query` service to handle queries. If we consider the module as a state-machine, then a `Msg` is a state transition. A `Msg` service is a Protobuf service defining all possible `Msg`s a module exposes. Note that `Msg`s are bundled in [`transactions`](../core/transactions.md), and each transaction contains one or multiple `messages`.
Each module defines two [Protobuf services](https://developers.google.com/protocol-buffers/docs/proto#services): one `Msg` service to handle messages, and one gRPC `Query` service to handle queries. If we consider the module as a state-machine, then a `Msg` service is a set of state transition RPC methods.
Each Protobuf `Msg` service method is 1:1 related to a Protobuf request type, which must implement `sdk.Msg` interface.
Note that `sdk.Msg`s are bundled in [transactions](../core/transactions.md), and each transaction contains one or multiple messages.
When a valid block of transactions is received by the full-node, Tendermint relays each one to the application via [`DeliverTx`](https://tendermint.com/docs/app-dev/abci-spec.html#delivertx). Then, the application handles the transaction:
1. Upon receiving the transaction, the application first unmarshalls it from `[]bytes`.
2. Then, it verifies a few things about the transaction like [fee payment and signatures](#gas-fees.md#antehandler) before extracting the `Msg`(s) contained in the transaction.
3. `Msg`s are encoded as Protobuf [`Any`s](#register-codec) via the `sdk.ServiceMsg` struct. By analyzing each `Any`'s `type_url`, baseapp's `msgServiceRouter` routes the `Msg` to the corresponding module's `Msg` service.
3. `sdk.Msg`s are encoded using Protobuf [`Any`s](#register-codec). By analyzing each `Any`'s `type_url`, baseapp's `msgServiceRouter` routes the `sdk.Msg` to the corresponding module's `Msg` service.
4. If the message is successfully processed, the state is updated.
For a more detailed look at a transaction lifecycle, click [here](./tx-lifecycle.md).
For a more details look at a transaction [lifecycle](./tx-lifecycle.md).
Module developers create custom `Msg`s when they build their own module. The general practice is to define all `Msg`s in a Protobuf service called `service Msg {}`, and define each `Msg` as a Protobuf service method, using the `rpc` keyword. These definitions usually reside in a `tx.proto` file. For example, the `x/bank` module defines two `Msg`s to allows users to transfer tokens:
Module developers create custom `Msg` services when they build their own module. The general practice is to define the `Msg` Protobuf service in a `tx.proto` file. For example, the `x/bank` module defines a service with two methods to transfer tokens:
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/proto/cosmos/bank/v1beta1/tx.proto#L10-L17
These two `Msg`s are processed by the `Msg` service of the `x/bank` module, which ultimately calls the `keeper` of the `x/auth` module in order to update the state.
Service methods use `keeper` in order to update the module state.
Each module should also implement the `RegisterServices` method as part of the [`AppModule` interface](#application-module-interface). This method should call the `RegisterMsgServer` function provided by the generated Protobuf code.

View File

@ -63,7 +63,7 @@ ctx.BlockGasMeter().ConsumeGas(
## AnteHandler
The `AnteHandler` is run for every transaction during `CheckTx` and `DeliverTx`, before the `Msg` service of each `Msg` in the transaction. `AnteHandler`s have the following signature:
The `AnteHandler` is run for every transaction during `CheckTx` and `DeliverTx`, before a Protobuf `Msg` service method for each `sdk.Msg` in the transaction. `AnteHandler`s have the following signature:
```go
// AnteHandler authenticates transactions, before their internal messages are handled.

View File

@ -83,7 +83,7 @@ When `Tx` is received by the application from the underlying consensus engine (e
### ValidateBasic
[`Msg`s](../core/transactions.md#messages) are extracted from `Tx` and `ValidateBasic`, a method of the `Msg` interface implemented by the module developer, is run for each one. It should include basic **stateless** sanity checks. For example, if the message is to send coins from one address to another, `ValidateBasic` likely checks for nonempty addresses and a nonnegative coin amount, but does not require knowledge of state such as account balance of an address.
[`sdk.Msg`s](../core/transactions.md#messages) are extracted from `Tx`, and `ValidateBasic`, a method of the `sdk.Msg` interface implemented by the module developer, is run for each one. `ValidateBasic` should include basic **stateless** sanity checks. For example, if the message is to send coins from one address to another, `ValidateBasic` likely checks for nonempty addresses and a nonnegative coin amount, but does not require knowledge of state such as the account balance of an address.
### AnteHandler
@ -199,12 +199,11 @@ Instead of using their `checkState`, full-nodes use `deliverState`:
- **`MsgServiceRouter`:** While `CheckTx` would have exited, `DeliverTx` continues to run
[`runMsgs`](../core/baseapp.md#runtx-and-runmsgs) to fully execute each `Msg` within the transaction.
Since the transaction may have messages from different modules, `BaseApp` needs to know which module
to find the appropriate handler. This is achieved using `BaseApp`'s `MsgServiceRouter` so that it can be processed by the module's [`Msg` service](../building-modules/msg-services.md).
For legacy `Msg` routing, the `Route` function is called via the [module manager](../building-modules/module-manager.md) to retrieve the route name and find the legacy [`Handler`](../building-modules/msg-services.md#handler-type) within the module.
to find the appropriate handler. This is achieved using `BaseApp`'s `MsgServiceRouter` so that it can be processed by the module's Protobuf [`Msg` service](../building-modules/msg-services.md).
For `LegacyMsg` routing, the `Route` function is called via the [module manager](../building-modules/module-manager.md) to retrieve the route name and find the legacy [`Handler`](../building-modules/msg-services.md#handler-type) within the module.
- **`Msg` service:** The `Msg` service, a step up from `AnteHandler`, is responsible for executing each
message in the `Tx` and causes state transitions to persist in `deliverTxState`. It is defined
within a module `Msg` protobuf service and writes to the appropriate stores within the module.
- **`Msg` service:** a Protobuf `Msg` service, a step up from `AnteHandler`, is responsible for executing each
message in the `Tx` and causes state transitions to persist in `deliverTxState`.
- **Gas:** While a `Tx` is being delivered, a `GasMeter` is used to keep track of how much
gas is being used; if execution completes, `GasUsed` is set and returned in the

View File

@ -1,5 +1,5 @@
<!--
order: 1
order: 1
-->
# Introduction to SDK Modules
@ -13,18 +13,18 @@ Modules define most of the logic of SDK applications. Developers compose modules
## Role of Modules in an SDK Application
The Cosmos SDK can be thought of as the Ruby-on-Rails of blockchain development. It comes with a core that provides the basic functionalities every blockchain application needs, like a [boilerplate implementation of the ABCI](../core/baseapp.md) to communicate with the underlying consensus engine, a [`multistore`](../core/store.md#multistore) to persist state, a [server](../core/node.md) to form a full-node and [interfaces](./module-interfaces.md) to handle queries.
The Cosmos SDK can be thought of as the Ruby-on-Rails of blockchain development. It comes with a core that provides the basic functionalities every blockchain application needs, like a [boilerplate implementation of the ABCI](../core/baseapp.md) to communicate with the underlying consensus engine, a [`multistore`](../core/store.md#multistore) to persist state, a [server](../core/node.md) to form a full-node and [interfaces](./module-interfaces.md) to handle queries.
On top of this core, the Cosmos SDK enables developers to build modules that implement the business logic of their application. In other words, SDK modules implement the bulk of the logic of applications, while the core does the wiring and enables modules to be composed together. The end goal is to build a robust ecosystem of open-source SDK modules, making it increasingly easier to build complex blockchain applications.
On top of this core, the Cosmos SDK enables developers to build modules that implement the business logic of their application. In other words, SDK modules implement the bulk of the logic of applications, while the core does the wiring and enables modules to be composed together. The end goal is to build a robust ecosystem of open-source SDK modules, making it increasingly easier to build complex blockchain applications.
SDK modules can be seen as little state-machines within the state-machine. They generally define a subset of the state using one or more `KVStore`s in the [main multistore](../core/store.md), as well as a subset of [message types](./messages-and-queries.md#messages). These messages are routed by one of the main components of SDK core, [`BaseApp`](../core/baseapp.md), to the [`Msg` service](./msg-services.md) of the module that defines them.
SDK modules can be seen as little state-machines within the state-machine. They generally define a subset of the state using one or more `KVStore`s in the [main multistore](../core/store.md), as well as a subset of [message types](./messages-and-queries.md#messages). These messages are routed by one of the main components of SDK core, [`BaseApp`](../core/baseapp.md), to a module Protobuf [`Msg` service](./msg-services.md) that defines them.
```
+
|
| Transaction relayed from the full-node's consensus engine
| Transaction relayed from the full-node's consensus engine
| to the node's application via DeliverTx
|
|
|
|
+---------------------v--------------------------+
@ -64,28 +64,28 @@ SDK modules can be seen as little state-machines within the state-machine. They
v
```
As a result of this architecture, building an SDK application usually revolves around writing modules to implement the specialized logic of the application, and composing them with existing modules to complete the application. Developers will generally work on modules that implement logic needed for their specific use case that do not exist yet, and will use existing modules for more generic functionalities like staking, accounts or token management.
As a result of this architecture, building an SDK application usually revolves around writing modules to implement the specialized logic of the application, and composing them with existing modules to complete the application. Developers will generally work on modules that implement logic needed for their specific use case that do not exist yet, and will use existing modules for more generic functionalities like staking, accounts or token management.
## How to Approach Building Modules as a Developer
While there are no definitive guidelines for writing modules, here are some important design principles developers should keep in mind when building them:
- **Composability**: SDK applications are almost always composed of multiple modules. This means developers need to carefully consider the integration of their module not only with the core of the Cosmos SDK, but also with other modules. The former is achieved by following standard design patterns outlined [here](#main-components-of-sdk-modules), while the latter is achieved by properly exposing the store(s) of the module via the [`keeper`](./keeper.md).
- **Specialization**: A direct consequence of the **composability** feature is that modules should be **specialized**. Developers should carefully establish the scope of their module and not batch multiple functionalities into the same module. This separation of concerns enables modules to be re-used in other projects and improves the upgradability of the application. **Specialization** also plays an important role in the [object-capabilities model](../core/ocap.md) of the Cosmos SDK.
- **Capabilities**: Most modules need to read and/or write to the store(s) of other modules. However, in an open-source environment, it is possible for some modules to be malicious. That is why module developers need to carefully think not only about how their module interacts with other modules, but also about how to give access to the module's store(s). The Cosmos SDK takes a capabilities-oriented approach to inter-module security. This means that each store defined by a module is accessed by a `key`, which is held by the module's [`keeper`](./keeper.md). This `keeper` defines how to access the store(s) and under what conditions. Access to the module's store(s) is done by passing a reference to the module's `keeper`.
- **Composability**: SDK applications are almost always composed of multiple modules. This means developers need to carefully consider the integration of their module not only with the core of the Cosmos SDK, but also with other modules. The former is achieved by following standard design patterns outlined [here](#main-components-of-sdk-modules), while the latter is achieved by properly exposing the store(s) of the module via the [`keeper`](./keeper.md).
- **Specialization**: A direct consequence of the **composability** feature is that modules should be **specialized**. Developers should carefully establish the scope of their module and not batch multiple functionalities into the same module. This separation of concerns enables modules to be re-used in other projects and improves the upgradability of the application. **Specialization** also plays an important role in the [object-capabilities model](../core/ocap.md) of the Cosmos SDK.
- **Capabilities**: Most modules need to read and/or write to the store(s) of other modules. However, in an open-source environment, it is possible for some modules to be malicious. That is why module developers need to carefully think not only about how their module interacts with other modules, but also about how to give access to the module's store(s). The Cosmos SDK takes a capabilities-oriented approach to inter-module security. This means that each store defined by a module is accessed by a `key`, which is held by the module's [`keeper`](./keeper.md). This `keeper` defines how to access the store(s) and under what conditions. Access to the module's store(s) is done by passing a reference to the module's `keeper`.
## Main Components of SDK Modules
Modules are by convention defined in the `./x/` subfolder (e.g. the `bank` module will be defined in the `./x/bank` folder). They generally share the same core components:
- A [`keeper`](./keeper.md), used to access the module's store(s) and update the state.
- A [`Msg` service](./messages-and-queries.md#messages) used to process messages when they are routed to the module by [`BaseApp`](../core/baseapp.md#message-routing) and trigger state-transitions.
- A [`Msg` service](./messages-and-queries.md#messages), used to process messages when they are routed to the module by [`BaseApp`](../core/baseapp.md#message-routing) and trigger state-transitions.
- A [query service](./query-services.md), used to process user queries when they are routed to the module by [`BaseApp`](../core/baseapp.md#query-routing).
- Interfaces, for end users to query the subset of the state defined by the module and create `message`s of the custom types defined in the module.
In addition to these components, modules implement the `AppModule` interface in order to be managed by the [`module manager`](./module-manager.md).
In addition to these components, modules implement the `AppModule` interface in order to be managed by the [`module manager`](./module-manager.md).
Please refer to the [structure document](./structure.md) to learn about the recommended structure of a module's directory.
Please refer to the [structure document](./structure.md) to learn about the recommended structure of a module's directory.
## Next {hide}

View File

@ -12,38 +12,41 @@ order: 3
## Messages
`Msg`s are objects whose end-goal is to trigger state-transitions. They are wrapped in [transactions](../core/transactions.md), which may contain one or more of them.
`Msg`s are objects whose end-goal is to trigger state-transitions. They are wrapped in [transactions](../core/transactions.md), which may contain one or more of them.
When a transaction is relayed from the underlying consensus engine to the SDK application, it is first decoded by [`BaseApp`](../core/baseapp.md). Then, each message contained in the transaction is extracted and routed to the appropriate module via `BaseApp`'s `MsgServiceRouter` so that it can be processed by the module's [`Msg` service](./msg-services.md). For a more detailed explanation of the lifecycle of a transaction, click [here](../basics/tx-lifecycle.md).
### `Msg` Services
Starting from v0.40, defining Protobuf `Msg` services is the recommended way to handle messages. A `Msg` protobuf service should be created per module, typically in `tx.proto` (see more info about [conventions and naming](../core/encoding.md#faq)). It must have an RPC service method defined for each message in the module.
Starting from v0.40, defining Protobuf `Msg` services is the recommended way to handle messages. A Protobuf `Msg` service should be created for each module, typically in `tx.proto` (see more info about [conventions and naming](../core/encoding.md#faq)). It must have an RPC service method defined for each message in the module.
See an example of a `Msg` service definition from `x/bank` module:
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc1/proto/cosmos/bank/v1beta1/tx.proto#L10-L17
For backwards compatibility with [legacy Amino `Msg`s](#legacy-amino-msgs), existing `Msg` types should be used as the request parameter for `service` definitions. Newer `Msg` types which only support `service` definitions should use the more canonical `Msg...Request` names.
Each `Msg` service method must have exactly one argument, which must implement the `sdk.Msg` interface, and a Protobuf response. The naming convention is to call the RPC argument `Msg<service-rpc-name>` and the RPC response `Msg<service-rpc-name>Response`. For example:
```
rpc Send(MsgSend) returns (MsgSendResponse);
```
`Msg` request types need to implement the `MsgRequest` interface which is a simplified version of the `Msg` interface described [below](#legacy-amino-msgs) with only `ValidateBasic()` and `GetSigners()` methods.
`sdk.Msg` interface is a simplified version of the Amino `LegacyMsg` interface described [below](#legacy-amino-msgs) with only `ValidateBasic()` and `GetSigners()` methods. For backwards compatibility with [Amino `LegacyMsg`s](#legacy-amino-msgs), existing `LegacyMsg` types should be used as the request parameter for `service` RPC definitions. Newer `sdk.Msg` types, which only support `service` definitions, should use canonical `Msg...` name.
Defining such `Msg` services allow to specify return types as part of `Msg` response using the canonical `Msg...Response` names.
In addition, this generates client and server code.
The generated `MsgServer` interface defines the server API for the `Msg` service and its implementation is described as part of the [`Msg` services](./msg-services.md) documentation.
Cosmos SDK uses Protobuf definitions to generate client and server code:
* `MsgServer` interface defines the server API for the `Msg` service and its implementation is described as part of the [`Msg` services](./msg-services.md) documentation.
* Structures are generated for all RPC request and response types.
A `RegisterMsgServer` method is also generated and should be used to register the module's `MsgServer` implementation in `RegisterServices` method from the [`AppModule` interface](./module-manager.md#appmodule).
In order for clients (CLI and grpc-gateway) to have these URLs registered, the SDK provides the function `RegisterMsgServiceDesc(registry codectypes.InterfaceRegistry, sd *grpc.ServiceDesc)` that should be called inside module's [`RegisterInterfaces`](module-manager.md#appmodulebasic) method, using the proto-generated `&_Msg_serviceDesc` as `*grpc.ServiceDesc` argument.
### Legacy Amino `Msg`s
### Legacy Amino `LegacyMsg`s
This way of defining messages is deprecated and using [`Msg` services](#msg-services) is preferred.
The following way of defining messages is deprecated and using [`Msg` services](#msg-services) is preferred.
Legacy `Msg`s can be defined as protobuf messages. The messages definition usually includes a list of parameters needed to process the message that will be provided by end-users when they want to create a new transaction containing said message.
Amino `LegacyMsg`s can be defined as protobuf messages. The messages definition usually includes a list of parameters needed to process the message that will be provided by end-users when they want to create a new transaction containing said message.
The `Msg` is typically accompanied by a standard constructor function, that is called from one of the [module's interface](./module-interfaces.md). `message`s also need to implement the `Msg` interface:
A `LegacyMsg` is typically accompanied by a standard constructor function, that is called from one of the [module's interface](./module-interfaces.md). `message`s also need to implement the `sdk.Msg` interface:
+++ https://github.com/cosmos/cosmos-sdk/blob/4a1b2fba43b1052ca162b3a1e0b6db6db9c26656/types/tx_msg.go#L10-L33
@ -52,8 +55,8 @@ It extends `proto.Message` and contains the following methods:
- `Route() string`: Name of the route for this message. Typically all `message`s in a module have the same route, which is most often the module's name.
- `Type() string`: Type of the message, used primarly in [events](../core/events.md). This should return a message-specific `string`, typically the denomination of the message itself.
- `ValidateBasic() error`: This method is called by `BaseApp` very early in the processing of the `message` (in both [`CheckTx`](../core/baseapp.md#checktx) and [`DeliverTx`](../core/baseapp.md#delivertx)), in order to discard obviously invalid messages. `ValidateBasic` should only include *stateless* checks, i.e. checks that do not require access to the state. This usually consists in checking that the message's parameters are correctly formatted and valid (i.e. that the `amount` is strictly positive for a transfer).
- `GetSignBytes() []byte`: Return the canonical byte representation of the message. Used to generate a signature.
- `GetSigners() []AccAddress`: Return the list of signers. The SDK will make sure that each `message` contained in a transaction is signed by all the signers listed in the list returned by this method.
- `GetSignBytes() []byte`: Return the canonical byte representation of the message. Used to generate a signature.
- `GetSigners() []AccAddress`: Return the list of signers. The SDK will make sure that each `message` contained in a transaction is signed by all the signers listed in the list returned by this method.
See an example implementation of a `message` from the `gov` module:
@ -61,11 +64,11 @@ See an example implementation of a `message` from the `gov` module:
## Queries
A `query` is a request for information made by end-users of applications through an interface and processed by a full-node. A `query` is received by a full-node through its consensus engine and relayed to the application via the ABCI. It is then routed to the appropriate module via `BaseApp`'s `queryrouter` so that it can be processed by the module's query service (./query-services.md). For a deeper look at the lifecycle of a `query`, click [here](../interfaces/query-lifecycle.md).
A `query` is a request for information made by end-users of applications through an interface and processed by a full-node. A `query` is received by a full-node through its consensus engine and relayed to the application via the ABCI. It is then routed to the appropriate module via `BaseApp`'s `queryrouter` so that it can be processed by the module's query service (./query-services.md). For a deeper look at the lifecycle of a `query`, click [here](../interfaces/query-lifecycle.md).
### gRPC Queries
Starting from v0.40, the prefered way to define queries is by using [Protobuf services](https://developers.google.com/protocol-buffers/docs/proto#services). A `Query` service should be created per module in `query.proto`. This service lists endpoints starting with `rpc`.
Starting from v0.40, the prefered way to define queries is by using [Protobuf services](https://developers.google.com/protocol-buffers/docs/proto#services). A `Query` service should be created per module in `query.proto`. This service lists endpoints starting with `rpc`.
Here's an example of such a `Query` service definition:
@ -87,14 +90,14 @@ where:
- `queryCategory` is the category of the `query`, typically `custom` for module queries. It is used to differentiate between different kinds of queries within `BaseApp`'s [`Query` method](../core/baseapp.md#query).
- `queryRoute` is used by `BaseApp`'s [`queryRouter`](../core/baseapp.md#query-routing) to map the `query` to its module. Usually, `queryRoute` should be the name of the module.
- `queryType` is used by the module's [`querier`](./query-services.md#legacy-queriers) to map the `query` to the appropriate `querier function` within the module.
- `args` are the actual arguments needed to process the `query`. They are filled out by the end-user. Note that for bigger queries, you might prefer passing arguments in the `Data` field of the request `req` instead of the `path`.
- `queryType` is used by the module's [`querier`](./query-services.md#legacy-queriers) to map the `query` to the appropriate `querier function` within the module.
- `args` are the actual arguments needed to process the `query`. They are filled out by the end-user. Note that for bigger queries, you might prefer passing arguments in the `Data` field of the request `req` instead of the `path`.
The `path` for each `query` must be defined by the module developer in the module's [command-line interface file](./module-interfaces.md#query-commands).Overall, there are 3 mains components module developers need to implement in order to make the subset of the state defined by their module queryable:
- A [`querier`](./query-services.md#legacy-queriers), to process the `query` once it has been [routed to the module](../core/baseapp.md#query-routing).
- [Query commands](./module-interfaces.md#query-commands) in the module's CLI file, where the `path` for each `query` is specified.
- `query` return types. Typically defined in a file `types/querier.go`, they specify the result type of each of the module's `queries`. These custom types must implement the `String()` method of [`fmt.Stringer`](https://golang.org/pkg/fmt/#Stringer).
- A [`querier`](./query-services.md#legacy-queriers), to process the `query` once it has been [routed to the module](../core/baseapp.md#query-routing).
- [Query commands](./module-interfaces.md#query-commands) in the module's CLI file, where the `path` for each `query` is specified.
- `query` return types. Typically defined in a file `types/querier.go`, they specify the result type of each of the module's `queries`. These custom types must implement the `String()` method of [`fmt.Stringer`](https://golang.org/pkg/fmt/#Stringer).
### Store Queries

View File

@ -3,7 +3,7 @@ order: 4
-->
# `Msg` Services
A `Msg` Service processes [messages](./messages-and-queries.md#messages). `Msg` Services are specific to the module in which they are defined, and only process messages defined within the said module. They are called from `BaseApp` during [`DeliverTx`](../core/baseapp.md#delivertx). {synopsis}
A Protobuf `Msg` service processes [messages](./messages-and-queries.md#messages). Protobuf `Msg` services are specific to the module in which they are defined, and only process messages defined within the said module. They are called from `BaseApp` during [`DeliverTx`](../core/baseapp.md#delivertx). {synopsis}
## Pre-requisite Readings
@ -12,11 +12,11 @@ A `Msg` Service processes [messages](./messages-and-queries.md#messages). `Msg`
## Implementation of a module `Msg` service
All `Msg` processing is done by a [`Msg`](messages-and-queries.md#msg-services) protobuf service. Each module should define a `Msg` service, which will be responsible for request and response serialization.
Each module should define a Protobuf `Msg` service, which will be responsible for processing requests (implementing `sdk.Msg`) and returning responses.
As further described in [ADR 031](../architecture/adr-031-msg-service.md), this approach has the advantage of clearly specifying return types and generating server and client code.
Based on the definition of the `Msg` service, Protobuf generates a `MsgServer` interface. It is the role of the module developer to implement this interface, by implementing the state transition logic that should happen upon receival of each `Msg`. As an example, here is the generated `MsgServer` interface for `x/bank`, which exposes two `Msg`s:
Protobuf generates a `MsgServer` interface based on a definition of `Msg` service. It is the role of the module developer to implement this interface, by implementing the state transition logic that should happen upon receival of each `sdk.Msg`. As an example, here is the generated `MsgServer` interface for `x/bank`, which exposes two `sdk.Msg`s:
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/x/bank/types/tx.pb.go#L285-L291
@ -28,10 +28,10 @@ When possible, the existing module's [`Keeper`](keeper.md) should implement `Msg
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc1/x/bank/keeper/msg_server.go#L27-L28
`Msg` processing usually follows these 2 steps:
`sdk.Msg` processing usually follows these 2 steps:
- First, they perform *stateful* checks to make sure the `message` is valid. At this stage, the `message`'s `ValidateBasic()` method has already been called, meaning *stateless* checks on the message (like making sure parameters are correctly formatted) have already been performed. Checks performed in the `msgServer` method can be more expensive and require access to the state. For example, a `msgServer` method for a `transfer` message might check that the sending account has enough funds to actually perform the transfer. To access the state, the `msgServer` method needs to call the [`keeper`'s](./keeper.md) getter functions.
- Then, if the checks are successful, the `msgServer` method calls the [`keeper`'s](./keeper.md) setter functions to actually perform the state transition.
- First, they perform *stateful* checks to make sure the `message` is valid. At this stage, the `message`'s `ValidateBasic()` method has already been called, meaning *stateless* checks on the message (like making sure parameters are correctly formatted) have already been performed. Checks performed in the `msgServer` method can be more expensive and require access to the state. For example, a `msgServer` method for a `transfer` message might check that the sending account has enough funds to actually perform the transfer. To access the state, the `msgServer` method needs to call the [`keeper`'s](./keeper.md) getter functions.
- Then, if the checks are successful, the `msgServer` method calls the [`keeper`'s](./keeper.md) setter functions to actually perform the state transition.
Before returning, `msgServer` methods generally emit one or more [events](../core/events.md) via the `EventManager` held in the `ctx`:
@ -44,7 +44,7 @@ ctx.EventManager().EmitEvent(
)
```
These events are relayed back to the underlying consensus engine and can be used by service providers to implement services around the application. Click [here](../core/events.md) to learn more about events.
These events are relayed back to the underlying consensus engine and can be used by service providers to implement services around the application. Click [here](../core/events.md) to learn more about events.
The invoked `msgServer` method returns a `proto.Message` response and an `error`. These return values are then wrapped into an `*sdk.Result` or an `error` using `sdk.WrapServiceResult(ctx sdk.Context, res proto.Message, err error)`:
@ -54,11 +54,11 @@ This method takes care of marshaling the `res` parameter to protobuf and attachi
+++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/proto/cosmos/base/abci/v1beta1/abci.proto#L81-L95
This diagram shows a typical structure of a `Msg` Service, and how the message propagates through the module.
This diagram shows a typical structure of a Protobuf `Msg` service, and how the message propagates through the module.
![Transaction flow](../uml/svg/transaction_flow.svg)
## Legacy Amino `Msg`s
## Amino `LegacyMsg`s
### `handler` type
@ -70,7 +70,7 @@ Here is the typical structure of a `handler` function:
Let us break it down:
- The [`Msg`](./messages-and-queries.md#messages) is the actual object being processed.
- The [`LegacyMsg`](./messages-and-queries.md#messages) is the actual object being processed.
- The [`Context`](../core/context.md) contains all the necessary information needed to process the `msg`, as well as a branch of the latest state. If the `msg` is successfully processed, the branched version of the state contained in the `ctx` will be written to the main state (branch).
- The `*Result` returned to `BaseApp` contains (among other things) information on the execution of the `handler` and [events](../core/events.md).
@ -82,19 +82,20 @@ the manager's `Route()` method simply constructs a Route that calls a `NewHandle
### Implementation
`NewHandler` function dispatches a `Msg` to appropriate handler function, usually by using a switch statement:
`NewHandler` function dispatches a `LegacyMsg` to appropriate handler function, usually by using a switch statement:
+++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/x/bank/handler.go#L13-L29
First, `NewHandler` function sets a new `EventManager` to the context to isolate events per `msg`.
Then, a simple switch calls the appropriate `handler` based on the `Msg` type.
Then, a simple switch calls the appropriate `handler` based on the `LegacyMsg` type.
In this regard, `handler`s functions need to be implemented for each module `Msg`. This will also involve manual handler registration of `Msg` types.
In this regard, `handler`s functions need to be implemented for each module `LegacyMsg`. This will also involve manual handler registration of `LegacyMsg` types.
`handler`s functions should return a `*Result` and an `error`.
## Telemetry
New [telemetry metrics](../core/telemetry.md) can be created from `msgServer` methods when handling messages.
New [telemetry metrics](../core/telemetry.md) can be created from `msgServer` methods when handling messages.
This is an example from the `x/auth/vesting` module:

View File

@ -4,7 +4,7 @@ order: 5
# Query Services
A query service processes [`queries`](./messages-and-queries.md#queries). Query services are specific to the module in which they are defined, and only process `queries` defined within said module. They are called from `BaseApp`'s [`Query` method](../core/baseapp.md#query). {synopsis}
A Protobuf Query service processes [`queries`](./messages-and-queries.md#queries). Query services are specific to the module in which they are defined, and only process `queries` defined within said module. They are called from `BaseApp`'s [`Query` method](../core/baseapp.md#query). {synopsis}
## Pre-requisite Readings
@ -66,11 +66,11 @@ func NewQuerier(keeper Keeper) sdk.Querier {
}
```
This simple switch returns a `querier` function specific to the type of the received `query`. At this point of the [query lifecycle](../interfaces/query-lifecycle.md), the first element of the `path` (`path[0]`) contains the type of the query. The following elements are either empty or contain arguments needed to process the query.
This simple switch returns a `querier` function specific to the type of the received `query`. At this point of the [query lifecycle](../interfaces/query-lifecycle.md), the first element of the `path` (`path[0]`) contains the type of the query. The following elements are either empty or contain arguments needed to process the query.
The `querier` functions themselves are pretty straighforward. They generally fetch a value or values from the state using the [`keeper`](./keeper.md). Then, they marshall the value(s) using the [`codec`](../core/encoding.md) and return the `[]byte` obtained as result.
The `querier` functions themselves are pretty straighforward. They generally fetch a value or values from the state using the [`keeper`](./keeper.md). Then, they marshall the value(s) using the [`codec`](../core/encoding.md) and return the `[]byte` obtained as result.
For a deeper look at `querier`s, see this [example implementation of a `querier` function](https://github.com/cosmos/cosmos-sdk/blob/7f59723d889b69ca19966167f0b3a7fec7a39e53/x/gov/keeper/querier.go) from the bank module.
For a deeper look at `querier`s, see this [example implementation of a `querier` function](https://github.com/cosmos/cosmos-sdk/blob/7f59723d889b69ca19966167f0b3a7fec7a39e53/x/gov/keeper/querier.go) from the bank module.
## Next {hide}

View File

@ -25,8 +25,8 @@ proto
- `{module_name}.proto`: The module's common message type definitions.
- `event.proto`: The module's message type definitions related to events.
- `genesis.proto`: The module's message type definitions related to genesis state.
- `query.proto`: The module's service and message type definitions related to the query service.
- `tx.proto`: The module's service and message type definitions related to the message service.
- `query.proto`: The module's Query service and related message type definitions.
- `tx.proto`: The module's Msg service and related message type definitions.
```shell
x/{module_name}

View File

@ -60,9 +60,9 @@ First, the important parameters that are initialized during the bootstrapping of
The `CommitMultiStore` is a multi-store, meaning a store of stores. Each module of the application
uses one or multiple `KVStores` in the multi-store to persist their subset of the state.
- Database: The `db` is used by the `CommitMultiStore` to handle data persistence.
- [`Msg` Service Router](#msg-service-router): The `msgServiceRouter` facilitates the routing of service `Msg`s to the appropriate
module for it to be processed. Here a service `Msg` refers to the transaction components that need to be
processed by the application in order to update the state, and not to ABCI messages which implement
- [`Msg` Service Router](#msg-service-router): The `msgServiceRouter` facilitates the routing of `sdk.Msg` requests to the appropriate
module `Msg` service for processing. Here a `sdk.Msg` refers to the transaction component that needs to be
processed by a service in order to update the application state, and not to ABCI message which implements
the interface between the application and the underlying consensus engine.
- [gRPC Query Router](#grpc-query-router): The `grpcQueryRouter` facilitates the routing of gRPC queries to the
appropriate module for it to be processed. These queries are not ABCI messages themselves, but they
@ -191,7 +191,7 @@ When messages and queries are received by the application, they must be routed t
### `Msg` Service Router
[`Msg`s](#../building-modules/messages-and-queries.md#messages) need to be routed after they are extracted from transactions, which are sent from the underlying Tendermint engine via the [`CheckTx`](#checktx) and [`DeliverTx`](#delivertx) ABCI messages. To do so, `BaseApp` holds a `msgServiceRouter` which maps fully-qualified service methods (`string`, defined in each module's `Msg` Protobuf service) to the appropriate module's `Msg` server implementation.
[`sdk.Msg`s](#../building-modules/messages-and-queries.md#messages) need to be routed after they are extracted from transactions, which are sent from the underlying Tendermint engine via the [`CheckTx`](#checktx) and [`DeliverTx`](#delivertx) ABCI messages. To do so, `BaseApp` holds a `msgServiceRouter` which maps fully-qualified service methods (`string`, defined in each module's Protobuf `Msg` service) to the appropriate module's `MsgServer` implementation.
The [default `msgServiceRouter` included in `BaseApp`](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/baseapp/msg_service_router.go) is stateless. However, some applications may want to make use of more stateful routing mechanisms such as allowing governance to disable certain routes or point them to new modules for upgrade purposes. For this reason, the `sdk.Context` is also passed into each [route handler inside `msgServiceRouter`](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/baseapp/msg_service_router.go#L31-L32). For a stateless router that doesn't want to make use of this, you can just ignore the `ctx`.
@ -199,7 +199,7 @@ The application's `msgServiceRouter` is initialized with all the routes using th
### gRPC Query Router
Similar to `Msg`s, [`queries`](../building-modules/messages-and-queries.md#queries) need to be routed to the appropriate module's [`Query` service](../building-modules/query-services.md). To do so, `BaseApp` holds a `grpcQueryRouter`, which maps modules' fully-qualified service methods (`string`, defined in their Protobuf `Query` gRPC) to their `Query` server implementation. The `grpcQueryRouter` is called during the initial stages of query processing, which can be either by directly sending a gRPC query to the gRPC endpoint, or via the [`Query` ABCI message](#query) on the Tendermint RPC endpoint.
Similar to `sdk.Msg`s, [`queries`](../building-modules/messages-and-queries.md#queries) need to be routed to the appropriate module's [`Query` service](../building-modules/query-services.md). To do so, `BaseApp` holds a `grpcQueryRouter`, which maps modules' fully-qualified service methods (`string`, defined in their Protobuf `Query` gRPC) to their `QueryServer` implementation. The `grpcQueryRouter` is called during the initial stages of query processing, which can be either by directly sending a gRPC query to the gRPC endpoint, or via the [`Query` ABCI message](#query) on the Tendermint RPC endpoint.
Just like the `msgServiceRouter`, the `grpcQueryRouter` is initialized with all the query routes using the application's [module manager](../building-modules/module-manager.md) (via the `RegisterServices` method), which itself is initialized with all the application's modules in the application's [constructor](../basics/app-anatomy.md#app-constructor).
@ -227,18 +227,18 @@ Unconfirmed transactions are relayed to peers only if they pass `CheckTx`.
make them lightweight. In the Cosmos SDK, after [decoding transactions](./encoding.md), `CheckTx()` is implemented
to do the following checks:
1. Extract the `Msg`s from the transaction.
2. Perform _stateless_ checks by calling `ValidateBasic()` on each of the `Msg`s. This is done
1. Extract the `sdk.Msg`s from the transaction.
2. Perform _stateless_ checks by calling `ValidateBasic()` on each of the `sdk.Msg`s. This is done
first, as _stateless_ checks are less computationally expensive than _stateful_ checks. If
`ValidateBasic()` fail, `CheckTx` returns before running _stateful_ checks, which saves resources.
3. Perform non-module related _stateful_ checks on the [account](../basics/accounts.md). This step is mainly about checking
that the `Msg` signatures are valid, that enough fees are provided and that the sending account
that the `sdk.Msg` signatures are valid, that enough fees are provided and that the sending account
has enough funds to pay for said fees. Note that no precise [`gas`](../basics/gas-fees.md) counting occurs here,
as `Msg`s are not processed. Usually, the [`AnteHandler`](../basics/gas-fees.md#antehandler) will check that the `gas` provided
as `sdk.Msg`s are not processed. Usually, the [`AnteHandler`](../basics/gas-fees.md#antehandler) will check that the `gas` provided
with the transaction is superior to a minimum reference gas amount based on the raw transaction size,
in order to avoid spam with transactions that provide 0 gas.
4. Ensure that each `Msg`'s fully-qualified service method matches on of the routes inside the `msgServiceRouter`, but do **not** actually
process `Msg`s. `Msg`s only need to be processed when the canonical state need to be updated,
4. Ensure that each `sdk.Msg`'s fully-qualified service method matches on of the routes inside the `msgServiceRouter`, but do **not** actually
process `sdk.Msg`s. `sdk.Msg`s only need to be processed when the canonical state need to be updated,
which happens during `DeliverTx`.
Steps 2. and 3. are performed by the [`AnteHandler`](../basics/gas-fees.md#antehandler) in the [`RunTx()`](#runtx-antehandler-and-runmsgs)
@ -284,7 +284,7 @@ Before the first transaction of a given block is processed, a [volatile state](#
`DeliverTx` performs the **exact same steps as `CheckTx`**, with a little caveat at step 3 and the addition of a fifth step:
1. The `AnteHandler` does **not** check that the transaction's `gas-prices` is sufficient. That is because the `min-gas-prices` value `gas-prices` is checked against is local to the node, and therefore what is enough for one full-node might not be for another. This means that the proposer can potentially include transactions for free, although they are not incentivised to do so, as they earn a bonus on the total fee of the block they propose.
2. For each `Msg` in the transaction, route to the appropriate module's [`Msg` service](../building-modules/msg-services.md). Additional _stateful_ checks are performed, and the branched multistore held in `deliverState`'s `context` is updated by the module's `keeper`. If the `Msg` service returns successfully, the branched multistore held in `context` is written to `deliverState` `CacheMultiStore`.
2. For each `sdk.Msg` in the transaction, route to the appropriate module's Protobuf [`Msg` service](../building-modules/msg-services.md). Additional _stateful_ checks are performed, and the branched multistore held in `deliverState`'s `context` is updated by the module's `keeper`. If the `Msg` service returns successfully, the branched multistore held in `context` is written to `deliverState` `CacheMultiStore`.
During the additional fifth step outlined in (2), each read/write to the store increases the value of `GasConsumed`. You can find the default cost of each operation:
@ -311,7 +311,7 @@ At any point, if `GasConsumed > GasWanted`, the function returns with `Code != 0
The first thing `RunTx` does upon being called is to retrieve the `context`'s `CacheMultiStore` by calling the `getContextForTx()` function with the appropriate mode (either `runTxModeCheck` or `runTxModeDeliver`). This `CacheMultiStore` is a branch of the main store, with cache functionality (for query requests), instantiated during `BeginBlock` for `DeliverTx` and during the `Commit` of the previous block for `CheckTx`. After that, two `defer func()` are called for [`gas`](../basics/gas-fees.md) management. They are executed when `runTx` returns and make sure `gas` is actually consumed, and will throw errors, if any.
After that, `RunTx()` calls `ValidateBasic()` on each `Msg`in the `Tx`, which runs preliminary _stateless_ validity checks. If any `Msg` fails to pass `ValidateBasic()`, `RunTx()` returns with an error.
After that, `RunTx()` calls `ValidateBasic()` on each `sdk.Msg`in the `Tx`, which runs preliminary _stateless_ validity checks. If any `sdk.Msg` fails to pass `ValidateBasic()`, `RunTx()` returns with an error.
Then, the [`anteHandler`](#antehandler) of the application is run (if it exists). In preparation of this step, both the `checkState`/`deliverState`'s `context` and `context`'s `CacheMultiStore` are branched using the `cacheTxContext()` function.
@ -319,7 +319,7 @@ Then, the [`anteHandler`](#antehandler) of the application is run (if it exists)
This allows `RunTx` not to commit the changes made to the state during the execution of `anteHandler` if it ends up failing. It also prevents the module implementing the `anteHandler` from writing to state, which is an important part of the [object-capabilities](./ocap.md) of the Cosmos SDK.
Finally, the [`RunMsgs()`](#runmsgs) function is called to process the `Msg`s in the `Tx`. In preparation of this step, just like with the `anteHandler`, both the `checkState`/`deliverState`'s `context` and `context`'s `CacheMultiStore` are branched using the `cacheTxContext()` function.
Finally, the [`RunMsgs()`](#runmsgs) function is called to process the `sdk.Msg`s in the `Tx`. In preparation of this step, just like with the `anteHandler`, both the `checkState`/`deliverState`'s `context` and `context`'s `CacheMultiStore` are branched using the `cacheTxContext()` function.
### AnteHandler
@ -339,9 +339,9 @@ Click [here](../basics/gas-fees.md#antehandler) for more on the `anteHandler`.
### RunMsgs
`RunMsgs` is called from `RunTx` with `runTxModeCheck` as parameter to check the existence of a route for each message the transaction, and with `runTxModeDeliver` to actually process the `Msg`s.
`RunMsgs` is called from `RunTx` with `runTxModeCheck` as parameter to check the existence of a route for each message the transaction, and with `runTxModeDeliver` to actually process the `sdk.Msg`s.
First, it retrieves the `Msg`'s fully-qualified service method name, by checking the `type_url` of the Protobuf `Any` representing the service `Msg`. Then, using the application's [`msgServiceRouter`](#msg-service-router), it checks for the existence of this fully-qualified service method. At this point, if `mode == runTxModeCheck`, `RunMsgs` returns. If instead `mode == runTxModeDeliver`, the [`Msg` server](../building-modules/msg-services.md) implementation for the message is executed, before `RunMsgs` returns.
First, it retrieves the `sdk.Msg`'s fully-qualified type name, by checking the `type_url` of the Protobuf `Any` representing the `sdk.Msg`. Then, using the application's [`msgServiceRouter`](#msg-service-router), it checks for the existence of `Msg` service method related to that `type_url`. At this point, if `mode == runTxModeCheck`, `RunMsgs` returns. Otherwise, if `mode == runTxModeDeliver`, the [`Msg` service](../building-modules/msg-services.md) RPC is executed, before `RunMsgs` returns.
## Other ABCI Messages

View File

@ -28,7 +28,7 @@ To parse the attribute values as strings, make sure to add `'` (single quotes) a
:::
Events, the `type` and `attributes` are defined on a **per-module basis** in the module's
`/types/events.go` file, and triggered from the module's [`Msg` service](../building-modules/msg-services.md)
`/types/events.go` file, and triggered from the module's Protobuf [`Msg` service](../building-modules/msg-services.md)
by using the [`EventManager`](#eventmanager). In addition, each module documents its Events under
`spec/xx_events.md`.

View File

@ -65,7 +65,7 @@ All routes are configured under the following fields in `~/.simapp/config/app.to
If, for various reasons, you cannot use gRPC (for example, you are building a web application, and browsers don't support HTTP2 on which gRPC is built), then the SDK offers REST routes via gRPC-gateway.
[gRPC-gateway](https://grpc-ecosystem.github.io/grpc-gateway/) is a tool to expose gRPC endpoints as REST endpoints. For each RPC endpoint defined in a Protobuf service, the SDK offers a REST equivalent. For instance, querying a balance could be done via the `/cosmos.bank.v1beta1.Query/AllBalances` gRPC endpoint, or alternatively via the gRPC-gateway `"/cosmos/bank/v1beta1/balances/{address}"` REST endpoint: both will return the same result. For each RPC method defined in a Protobuf service, the corresponding REST endpoint is defined as an option:
[gRPC-gateway](https://grpc-ecosystem.github.io/grpc-gateway/) is a tool to expose gRPC endpoints as REST endpoints. For each RPC endpoint defined in a Protobuf service, the SDK offers a REST equivalent. For instance, querying a balance could be done via the `/cosmos.bank.v1beta1.QueryAllBalances` gRPC endpoint, or alternatively via the gRPC-gateway `"/cosmos/bank/v1beta1/balances/{address}"` REST endpoint: both will return the same result. For each RPC method defined in a Protobuf service, the corresponding REST endpoint is defined as an option:
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.41.0/proto/cosmos/bank/v1beta1/query.proto#L19-L22
@ -92,7 +92,7 @@ Independently from the Cosmos SDK, Tendermint also exposes a RPC server. This RP
Some Tendermint RPC endpoints are directly related to the Cosmos SDK:
- `/abci_query`: this endpoint will query the application for state. As the `path` parameter, you can send the following strings:
- any Protobuf fully-qualified service method, such as `/cosmos.bank.v1beta1.Query/AllBalances`. The `data` field should then include the method's request parameter(s) encoded as bytes using Protobuf.
- any Protobuf fully-qualified service method, such as `/cosmos.bank.v1beta1.QueryAllBalances`. The `data` field should then include the method's request parameter(s) encoded as bytes using Protobuf.
- `/app/simulate`: this will simulate a transaction, and return some information such as gas used.
- `/app/version`: this will return the application's version.
- `/store/{path}`: this will query the store directly.

View File

@ -12,9 +12,9 @@ order: 2
## Transactions
Transactions are comprised of metadata held in [contexts](./context.md) and [`Msg`s](../building-modules/messages-and-queries.md) that trigger state changes within a module through the module's [`Msg` service](../building-modules/msg-services.md).
Transactions are comprised of metadata held in [contexts](./context.md) and [`sdk.Msg`s](../building-modules/messages-and-queries.md) that trigger state changes within a module through the module's Protobuf [`Msg` service](../building-modules/msg-services.md).
When users want to interact with an application and make state changes (e.g. sending coins), they create transactions. Each of a transaction's `Msg`s must be signed using the private key associated with the appropriate account(s), before the transaction is broadcasted to the network. A transaction must then be included in a block, validated, and approved by the network through the consensus process. To read more about the lifecycle of a transaction, click [here](../basics/tx-lifecycle.md).
When users want to interact with an application and make state changes (e.g. sending coins), they create transactions. Each of a transaction's `sdk.Msg` must be signed using the private key associated with the appropriate account(s), before the transaction is broadcasted to the network. A transaction must then be included in a block, validated, and approved by the network through the consensus process. To read more about the lifecycle of a transaction, click [here](../basics/tx-lifecycle.md).
## Type Definition
@ -24,8 +24,8 @@ Transaction objects are SDK types that implement the `Tx` interface
It contains the following methods:
- **GetMsgs:** unwraps the transaction and returns a list of its `Msg`s - one transaction may have one or multiple [`Msg`s](../building-modules/messages-and-queries.md#messages), which are defined by module developers.
- **ValidateBasic:** includes lightweight, [_stateless_](../basics/tx-lifecycle.md#types-of-checks) checks used by ABCI messages [`CheckTx`](./baseapp.md#checktx) and [`DeliverTx`](./baseapp.md#delivertx) to make sure transactions are not invalid. For example, the [`auth`](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth) module's `StdTx` `ValidateBasic` function checks that its transactions are signed by the correct number of signers and that the fees do not exceed what the user's maximum. Note that this function is to be distinct from the `ValidateBasic` functions for `Msg`s, which perform basic validity checks on messages only. For example, when [`runTx`](./baseapp.md#runtx) is checking a transaction created from the [`auth`](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth/spec) module, it first runs `ValidateBasic` on each message, then runs the `auth` module AnteHandler which calls `ValidateBasic` for the transaction itself.
- **GetMsgs:** unwraps the transaction and returns a list of contained `sdk.Msg`s - one transaction may have one or multiple messages, which are defined by module developers.
- **ValidateBasic:** includes lightweight, [_stateless_](../basics/tx-lifecycle.md#types-of-checks) checks used by ABCI messages [`CheckTx`](./baseapp.md#checktx) and [`DeliverTx`](./baseapp.md#delivertx) to make sure transactions are not invalid. For example, the [`auth`](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth) module's `StdTx` `ValidateBasic` function checks that its transactions are signed by the correct number of signers and that the fees do not exceed what the user's maximum. Note that this function is to be distinct from the `ValidateBasic` functions for `sdk.Msg`s, which perform basic validity checks on messages only. For example, when [`runTx`](./baseapp.md#runtx) is checking a transaction created from the [`auth`](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth/spec) module, it first runs `ValidateBasic` on each message, then runs the `auth` module AnteHandler which calls `ValidateBasic` for the transaction itself.
As a developer, you should rarely manipulate `Tx` directly, as `Tx` is really an intermediate type used for transaction generation. Instead, developers should prefer the `TxBuilder` interface, which you can learn more about [below](#transaction-generation).
@ -74,14 +74,15 @@ The next paragraphs will describe each of these components, in this order.
### Messages
::: tip
Module `Msg`s are not to be confused with [ABCI Messages](https://tendermint.com/docs/spec/abci/abci.html#messages) which define interactions between the Tendermint and application layers.
Module `sdk.Msg`s are not to be confused with [ABCI Messages](https://tendermint.com/docs/spec/abci/abci.html#messages) which define interactions between the Tendermint and application layers.
:::
**Messages** (or `Msg`s) are module-specific objects that trigger state transitions within the scope of the module they belong to. Module developers define the messages for their module by adding methods to the Protobuf [`Msg` service](../building-modules/msg-services.md), and also implement the corresponding `MsgServer`.
**Messages** (or `sdk.Msg`s) are module-specific objects that trigger state transitions within the scope of the module they belong to. Module developers define the messages for their module by adding methods to the Protobuf [`Msg` service](../building-modules/msg-services.md), and also implement the corresponding `MsgServer`.
`Msg`s in a module are defined as methods in the [`Msg` service] inside each module's `tx.proto` file. Since `Msg`s are module-specific, each module needs a to process all of its message types and trigger state changes within the module's scope. This design puts more responsibility on module developers, allowing application developers to reuse common functionalities without having to implement state transition logic repetitively. To achieve this, Protobuf generates a `MsgServer` interface for each module, and the module developer needs to implement this interface. The methods on the `MsgServer` interface corresponds on how to handle each of the different `Msg`.
Each `sdk.Msg`s is related to exactly one Protobuf [`Msg` service](../building-modules/msg-services.md) RPC, defined inside each module's `tx.proto` file. An SKD app router automatically maps every `sdk.Msg` to a corresponding RPC. Protobuf generates a `MsgServer` interface for each module `Msg` service, and the module developer needs to implement this interface.
This design puts more responsibility on module developers, allowing application developers to reuse common functionalities without having to implement state transition logic repetitively.
To learn more about `Msg` services and how to implement `MsgServer`, click [here](../building-modules/msg-services.md).
To learn more about Protobuf `Msg` services and how to implement `MsgServer`, click [here](../building-modules/msg-services.md).
While messages contain the information for state transition logic, a transaction's other metadata and relevant information are stored in the `TxBuilder` and `Context`.

View File

@ -4,13 +4,13 @@ order: 3
# Blockchain Architecture
## State machine
## State machine
At its core, a blockchain is a [replicated deterministic state machine](https://en.wikipedia.org/wiki/State_machine_replication).
At its core, a blockchain is a [replicated deterministic state machine](https://en.wikipedia.org/wiki/State_machine_replication).
A state machine is a computer science concept whereby a machine can have multiple states, but only one at any given time. There is a `state`, which describes the current state of the system, and `transactions`, that trigger state transitions.
A state machine is a computer science concept whereby a machine can have multiple states, but only one at any given time. There is a `state`, which describes the current state of the system, and `transactions`, that trigger state transitions.
Given a state S and a transaction T, the state machine will return a new state S'.
Given a state S and a transaction T, the state machine will return a new state S'.
```
+--------+ +--------+
@ -30,9 +30,9 @@ In practice, the transactions are bundled in blocks to make the process more eff
+--------+ +--------+
```
In a blockchain context, the state machine is deterministic. This means that if a node is started at a given state and replays the same sequence of transactions, it will always end up with the same final state.
In a blockchain context, the state machine is deterministic. This means that if a node is started at a given state and replays the same sequence of transactions, it will always end up with the same final state.
The Cosmos SDK gives developers maximum flexibility to define the state of their application, transaction types and state transition functions. The process of building state-machines with the SDK will be described more in depth in the following sections. But first, let us see how the state-machine is replicated using **Tendermint**.
The Cosmos SDK gives developers maximum flexibility to define the state of their application, transaction types and state transition functions. The process of building state-machines with the SDK will be described more in depth in the following sections. But first, let us see how the state-machine is replicated using **Tendermint**.
## Tendermint
@ -56,13 +56,13 @@ Blockchain node | | Consensus | |
```
[Tendermint](https://docs.tendermint.com/v0.34/introduction/what-is-tendermint.html) is an application-agnostic engine that is responsible for handling the *networking* and *consensus* layers of a blockchain. In practice, this means that Tendermint is responsible for propagating and ordering transaction bytes. Tendermint Core relies on an eponymous Byzantine-Fault-Tolerant (BFT) algorithm to reach consensus on the order of transactions.
[Tendermint](https://docs.tendermint.com/v0.34/introduction/what-is-tendermint.html) is an application-agnostic engine that is responsible for handling the *networking* and *consensus* layers of a blockchain. In practice, this means that Tendermint is responsible for propagating and ordering transaction bytes. Tendermint Core relies on an eponymous Byzantine-Fault-Tolerant (BFT) algorithm to reach consensus on the order of transactions.
The Tendermint [consensus algorithm](https://docs.tendermint.com/v0.34/introduction/what-is-tendermint.html#consensus-overview) works with a set of special nodes called *Validators*. Validators are responsible for adding blocks of transactions to the blockchain. At any given block, there is a validator set V. A validator in V is chosen by the algorithm to be the proposer of the next block. This block is considered valid if more than two thirds of V signed a *[prevote](https://docs.tendermint.com/v0.34/spec/consensus/consensus.html#prevote-step-height-h-round-r)* and a *[precommit](https://docs.tendermint.com/v0.34/spec/consensus/consensus.html#precommit-step-height-h-round-r)* on it, and if all the transactions that it contains are valid. The validator set can be changed by rules written in the state-machine.
The Tendermint [consensus algorithm](https://docs.tendermint.com/v0.34/introduction/what-is-tendermint.html#consensus-overview) works with a set of special nodes called *Validators*. Validators are responsible for adding blocks of transactions to the blockchain. At any given block, there is a validator set V. A validator in V is chosen by the algorithm to be the proposer of the next block. This block is considered valid if more than two thirds of V signed a *[prevote](https://docs.tendermint.com/v0.34/spec/consensus/consensus.html#prevote-step-height-h-round-r)* and a *[precommit](https://docs.tendermint.com/v0.34/spec/consensus/consensus.html#precommit-step-height-h-round-r)* on it, and if all the transactions that it contains are valid. The validator set can be changed by rules written in the state-machine.
## ABCI
Tendermint passes transactions to the application through an interface called the [ABCI](https://docs.tendermint.com/v0.34/spec/abci/), which the application must implement.
Tendermint passes transactions to the application through an interface called the [ABCI](https://docs.tendermint.com/v0.34/spec/abci/), which the application must implement.
```
+---------------------+
@ -82,13 +82,13 @@ Tendermint passes transactions to the application through an interface called th
+---------------------+
```
Note that **Tendermint only handles transaction bytes**. It has no knowledge of what these bytes mean. All Tendermint does is order these transaction bytes deterministically. Tendermint passes the bytes to the application via the ABCI, and expects a return code to inform it if the messages contained in the transactions were successfully processed or not.
Note that **Tendermint only handles transaction bytes**. It has no knowledge of what these bytes mean. All Tendermint does is order these transaction bytes deterministically. Tendermint passes the bytes to the application via the ABCI, and expects a return code to inform it if the messages contained in the transactions were successfully processed or not.
Here are the most important messages of the ABCI:
- `CheckTx`: When a transaction is received by Tendermint Core, it is passed to the application to check if a few basic requirements are met. `CheckTx` is used to protect the mempool of full-nodes against spam transactions. A special handler called the [`AnteHandler`](../basics/gas-fees.md#antehandler) is used to execute a series of validation steps such as checking for sufficient fees and validating the signatures. If the checks are valid, the transaction is added to the [mempool](https://docs.tendermint.com/v0.34/tendermint-core/mempool.html#mempool) and relayed to peer nodes. Note that transactions are not processed (i.e. no modification of the state occurs) with `CheckTx` since they have not been included in a block yet.
- `DeliverTx`: When a [valid block](https://docs.tendermint.com/v0.34/spec/blockchain/blockchain.html#validation) is received by Tendermint Core, each transaction in the block is passed to the application via `DeliverTx` in order to be processed. It is during this stage that the state transitions occur. The `AnteHandler` executes again along with the actual [`Msg` service methods](../building-modules/msg-services.md) for each message in the transaction.
- `BeginBlock`/`EndBlock`: These messages are executed at the beginning and the end of each block, whether the block contains transaction or not. It is useful to trigger automatic execution of logic. Proceed with caution though, as computationally expensive loops could slow down your blockchain, or even freeze it if the loop is infinite.
- `CheckTx`: When a transaction is received by Tendermint Core, it is passed to the application to check if a few basic requirements are met. `CheckTx` is used to protect the mempool of full-nodes against spam transactions. A special handler called the [`AnteHandler`](../basics/gas-fees.md#antehandler) is used to execute a series of validation steps such as checking for sufficient fees and validating the signatures. If the checks are valid, the transaction is added to the [mempool](https://docs.tendermint.com/v0.34/tendermint-core/mempool.html#mempool) and relayed to peer nodes. Note that transactions are not processed (i.e. no modification of the state occurs) with `CheckTx` since they have not been included in a block yet.
- `DeliverTx`: When a [valid block](https://docs.tendermint.com/v0.34/spec/blockchain/blockchain.html#validation) is received by Tendermint Core, each transaction in the block is passed to the application via `DeliverTx` in order to be processed. It is during this stage that the state transitions occur. The `AnteHandler` executes again along with the actual [`Msg` service](../building-modules/msg-services.md) RPC for each message in the transaction.
- `BeginBlock`/`EndBlock`: These messages are executed at the beginning and the end of each block, whether the block contains transaction or not. It is useful to trigger automatic execution of logic. Proceed with caution though, as computationally expensive loops could slow down your blockchain, or even freeze it if the loop is infinite.
Find a more detailed view of the ABCI methods from the [Tendermint docs](https://docs.tendermint.com/v0.34/spec/abci/abci.html#overview).

View File

@ -77,7 +77,7 @@ Cosmos SDK v0.40 uses Protobuf services to define state transitions (`Msg`s) and
#### `Msg` Service
For migrating `Msg`s, the handler pattern (inside the `handler.go` file) is deprecated. You may still keep it if you wish to support `Msg`s defined in older versions of the SDK. However, it is strongly recommended to add a `Msg` service to your Protobuf files, and each old `Msg` should be converted into a service method. Taking [x/bank's](../../x/bank/spec/README.md) `MsgSend` as an example, we have a corresponding `cosmos.bank.v1beta1.Msg/Send` service method:
The handler pattern (inside the `handler.go` file) for handling `Msg`s is deprecated. You may still keep it if you wish to support `Msg`s defined in pre-Stargate versions of the SDK. However, it is strongly recommended to add a `Msg` service to your Protobuf files, and each old `Msg` handler should be converted into a service method. Taking [x/bank's](../../x/bank/spec/README.md) `MsgSend` as an example, we have a corresponding `/cosmos.bank.v1beta1.MsgSend` RPC:
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/proto/cosmos/bank/v1beta1/tx.proto#L10-L31
@ -89,7 +89,7 @@ For more information, please check our [`Msg` service guide](../building-modules
#### `Query` Service
For migrating state queries, the querier pattern (inside the `querier.go` file) is deprecated. You may still keep this file to support legacy queries, but it is strongly recommended to use a Protobuf `Query` service to handle state queries.
The querier pattern (inside the `querier.go` file) for handling state queries is deprecated. You may still keep this file to support legacy queries, but it is strongly recommended to use a Protobuf `Query` service to handle state queries.
Each query endpoint is now defined as a separate service method in the `Query` service. Still taking `x/bank` as an example, here are the queries to fetch an account's balances:
@ -117,7 +117,7 @@ If you wish to expose your `Query` endpoints as REST endpoints (as proposed in t
If you still use Amino (which is deprecated since Stargate), you must register related types using the `RegisterLegacyAminoCodec(cdc *codec.LegacyAmino)` method (previously it was called `RegisterCodec(cdc *codec.Codec)`).
Moreover, a new `RegisterInterfaces` method has been added to the `AppModule` interface that all modules must implement. This method must register the interfaces that Protobuf messages implement, as well as the service `Msg`s used in the module. An example from x/bank is given below:
Moreover, a new `RegisterInterfaces` method has been added to the `AppModule` interface that all modules must implement. This method must register the interfaces that Protobuf messages implement, as well as the service request `Msg`s used in the module. An example from x/bank is given below:
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/x/bank/types/codec.go#L21-L34
@ -224,7 +224,7 @@ We described in the [modules migration section](#updating-modules) `Query` and `
- the [Tx Service](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/proto/cosmos/tx/v1beta1/service.proto), to perform operations on transactions,
- the [Tendermint service](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/proto/cosmos/base/tendermint/v1beta1/query.proto), to have a more idiomatic interface to the [Tendermint RPC](https://docs.tendermint.com/master/rpc/).
These services are optional, if you wish to use them, or if you wish to add more module-agnostic Protobuf services into your app, then you need to add them inside `app.go`:
These services are optional. To use them, or if you wish to add more module-agnostic Protobuf services into your app, you need to register them inside `app.go`:
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/simapp/app.go#L577-L585

View File

@ -2,8 +2,6 @@
Package rosetta implements the rosetta API for the current cosmos sdk release series.
The client satisfies [cosmos-rosetta-gateway](https://github.com/tendermint/cosmos-rosetta-gateway) `Client` interface implementation.
## Extension
There are two ways in which you can customize and extend the implementation with your custom settings.
@ -24,7 +22,7 @@ import (
"context"
"github.com/coinbase/rosetta-sdk-go/types"
"github.com/cosmos/cosmos-sdk/server/rosetta"
"github.com/cosmos/cosmos-sdk/server/rosetta/lib"
)
// CustomClient embeds the standard cosmos client
@ -48,7 +46,7 @@ Example:
```go
package custom_errors
import crgerrs "github.com/tendermint/cosmos-rosetta-gateway/errors"
import crgerrs "github.com/cosmos/cosmos-sdk/server/rosetta/lib/errors"
var customErrRetriable = true
var CustomError = crgerrs.RegisterError(100, "custom message", customErrRetriable, "description")

7
go.mod
View File

@ -4,11 +4,11 @@ module github.com/cosmos/cosmos-sdk
require (
github.com/99designs/keyring v1.1.6
github.com/armon/go-metrics v0.3.7
github.com/armon/go-metrics v0.3.8
github.com/bgentry/speakeasy v0.1.0
github.com/btcsuite/btcd v0.21.0-beta
github.com/btcsuite/btcutil v1.0.2
github.com/coinbase/rosetta-sdk-go v0.5.9
github.com/coinbase/rosetta-sdk-go v0.6.10
github.com/confio/ics23/go v0.6.6
github.com/cosmos/go-bip39 v1.0.0
github.com/cosmos/iavl v0.16.0
@ -19,7 +19,6 @@ require (
github.com/gogo/protobuf v1.3.3
github.com/golang/mock v1.4.4
github.com/golang/protobuf v1.5.2
github.com/golang/snappy v0.0.2 // indirect
github.com/gorilla/handlers v1.5.1
github.com/gorilla/mux v1.8.0
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
@ -46,12 +45,12 @@ require (
github.com/spf13/viper v1.7.1
github.com/stretchr/testify v1.7.0
github.com/tendermint/btcd v0.1.1
github.com/tendermint/cosmos-rosetta-gateway v0.3.0-rc2.0.20210304154332-87d6ca4410df
github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15
github.com/tendermint/go-amino v0.16.0
github.com/tendermint/tendermint v0.34.10
github.com/tendermint/tm-db v0.6.4
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect
google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f
google.golang.org/grpc v1.37.0
google.golang.org/protobuf v1.26.0

53
go.sum
View File

@ -46,6 +46,7 @@ github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrd
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/Workiva/go-datastructures v1.0.52 h1:PLSK6pwn8mYdaoaCZEMsXBpBotr4HHn9abU0yMQt0NI=
github.com/Workiva/go-datastructures v1.0.52/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA=
github.com/Zilliqa/gozilliqa-sdk v1.2.1-0.20201201074141-dd0ecada1be6/go.mod h1:eSYp2T6f0apnuW8TzhV3f6Aff2SE8Dwio++U4ha4yEM=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@ -61,8 +62,8 @@ github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-metrics v0.3.7 h1:c/oCtWzYpboy6+6f6LjXRlyW7NwA2SWf+a9KMlHq/bM=
github.com/armon/go-metrics v0.3.7/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
github.com/armon/go-metrics v0.3.8 h1:oOxq3KPj0WhCuy50EhzwiyMyG2ovRQZpZLXQuOh2a/M=
github.com/armon/go-metrics v0.3.8/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
@ -78,11 +79,13 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ=
github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0=
github.com/btcsuite/btcd v0.0.0-20190315201642-aa6e0f35703c/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btcd v0.21.0-beta h1:At9hIZdJW0s9E/fAz28nrz6AmcNlSVucCH796ZteX1M=
github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts=
github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts=
@ -108,9 +111,8 @@ github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/coinbase/rosetta-sdk-go v0.5.8/go.mod h1:xd4wYUhV3LkY78SPH8BUhc88rXfn2jYgN9BfiSjbcvM=
github.com/coinbase/rosetta-sdk-go v0.5.9 h1:CuGQE3HFmYwdEACJnuOtVI9cofqPsGvq6FdFIzaOPKI=
github.com/coinbase/rosetta-sdk-go v0.5.9/go.mod h1:xd4wYUhV3LkY78SPH8BUhc88rXfn2jYgN9BfiSjbcvM=
github.com/coinbase/rosetta-sdk-go v0.6.10 h1:rgHD/nHjxLh0lMEdfGDqpTtlvtSBwULqrrZ2qPdNaCM=
github.com/coinbase/rosetta-sdk-go v0.6.10/go.mod h1:J/JFMsfcePrjJZkwQFLh+hJErkAmdm9Iyy3D5Y0LfXo=
github.com/confio/ics23/go v0.0.0-20200817220745-f173e6211efb/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg=
github.com/confio/ics23/go v0.6.3/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg=
github.com/confio/ics23/go v0.6.6 h1:pkOy18YxxJ/r0XFDCnrl4Bjv6h4LkBSpLS6F38mrKL8=
@ -180,7 +182,7 @@ github.com/enigmampc/btcutil v1.0.3-0.20200723161021-e2fb6adb2a25 h1:2vLKys4RBU4
github.com/enigmampc/btcutil v1.0.3-0.20200723161021-e2fb6adb2a25/go.mod h1:hTr8+TLQmkUkgcuh3mcr5fjrT9c64ZzsBCdCEC6UppY=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ethereum/go-ethereum v1.9.23/go.mod h1:JIfVb6esrqALTExdz9hRYvrP0xBDf6wCncIu1hNwHpM=
github.com/ethereum/go-ethereum v1.9.25/go.mod h1:vMkFiYLHI4tgPw4k2j4MHKoovchFE8plZ0M9VMk4/oM=
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ=
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
@ -189,7 +191,7 @@ github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQD
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
@ -266,9 +268,8 @@ github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.2-0.20200707131729-196ae77b8a26/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw=
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.3-0.20201103224600-674baa8c7fc3 h1:ur2rms48b3Ep1dxh7aUV2FZEQ8jEVO2F6ILKx8ofkAg=
github.com/golang/snappy v0.0.3-0.20201103224600-674baa8c7fc3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@ -292,6 +293,7 @@ github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OI
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
@ -376,6 +378,7 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt
github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jhump/protoreflect v1.8.2 h1:k2xE7wcUomeqwY0LDCYA16y4WWfyTcMx5mKhk0d4ua0=
@ -430,15 +433,12 @@ github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaW
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
@ -482,6 +482,7 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/neilotoole/errgroup v0.1.5/go.mod h1:Q2nLGf+594h0CLBs/Mbg6qOr7GtqDK7C2S41udRnToE=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso=
@ -611,6 +612,7 @@ github.com/sasha-s/go-deadlock v0.2.0/go.mod h1:StQn567HiB1fF2yJ44N9au7wOhrPS3iZ
github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa h1:0U2s5loxrTy6/VgfVoLuVLFJcURKLH49ie0zSch7gh4=
github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY=
github.com/shirou/gopsutil v2.20.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
@ -663,6 +665,7 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@ -674,8 +677,6 @@ github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzH
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8=
github.com/tendermint/btcd v0.1.1 h1:0VcxPfflS2zZ3RiOAHkBiFUcPvbtRj5O7zHmcJWHV7s=
github.com/tendermint/btcd v0.1.1/go.mod h1:DC6/m53jtQzr/NFmMNEu0rxf18/ktVoVtMrnDD5pN+U=
github.com/tendermint/cosmos-rosetta-gateway v0.3.0-rc2.0.20210304154332-87d6ca4410df h1:hoMLrOS4WyyMM+Y+iWdGu94o0zzp6Q43y7v89Q1/OIw=
github.com/tendermint/cosmos-rosetta-gateway v0.3.0-rc2.0.20210304154332-87d6ca4410df/go.mod h1:gBPw8WV2Erm4UGHlBRiM3zaEBst4bsuihmMCNQdgP/s=
github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 h1:hqAk8riJvK4RMWx1aInLzndwxKalgi5rTqgfXxOxbEI=
github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15/go.mod h1:z4YtwM70uOnk8h0pjJYlj3zdYwi9l03By6iAIF5j/Pk=
github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E=
@ -689,14 +690,15 @@ github.com/tendermint/tm-db v0.6.2/go.mod h1:GYtQ67SUvATOcoY8/+x6ylk8Qo02BQyLrAs
github.com/tendermint/tm-db v0.6.3/go.mod h1:lfA1dL9/Y/Y8wwyPp2NMLyn5P5Ptr/gvDFNWtrCWSf8=
github.com/tendermint/tm-db v0.6.4 h1:3N2jlnYQkXNQclQwd/eKV/NzlqPlfK21cpRRIx80XXQ=
github.com/tendermint/tm-db v0.6.4/go.mod h1:dptYhIpJ2M5kUuenLr+Yyf3zQOv1SgBZcl8/BmWlMBw=
github.com/tidwall/gjson v1.6.1/go.mod h1:BaHyNc5bjzYkPqgLq7mdVzeiRtULKULXLgZFKsxEHI0=
github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
github.com/tidwall/gjson v1.6.7/go.mod h1:zeFuBCIqD4sN/gmqBzZ4j7Jd6UcA2Fc56x7QFsv+8fI=
github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/sjson v1.1.2/go.mod h1:SEzaDwxiPzKzNfUEO4HbYF/m4UCSJDsGgNqsS1LvdoY=
github.com/tidwall/sjson v1.1.4/go.mod h1:wXpKXu8CtDjKAZ+3DrKY5ROCorDFahq8l0tey/Lx1fg=
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/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
github.com/tyler-smith/go-bip39 v1.0.2/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
@ -705,11 +707,12 @@ github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/vmihailenco/msgpack/v5 v5.0.0-beta.9/go.mod h1:HVxBVPUK/+fZMonk4bi1islLa8V3cfnBug0+4dykPzo=
github.com/vmihailenco/msgpack/v5 v5.1.4/go.mod h1:C5gboKD0TJPqWDTVTtrQNfRbiBwHZGo8UTqP/9/XvLI=
github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/ybbus/jsonrpc v2.1.2+incompatible/go.mod h1:XJrh1eMSzdIYFbM08flv0wp5G35eRniyeGut1z+LSiE=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@ -738,10 +741,12 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@ -833,7 +838,6 @@ golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -864,13 +868,13 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 h1:46ULzRKLh1CwgRq2dC5SlBzEqqNCi8rreOZnNrbqcIY=
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -992,9 +996,8 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -16,7 +16,7 @@ message GenesisState {
repeated SigningInfo signing_infos = 2
[(gogoproto.moretags) = "yaml:\"signing_infos\"", (gogoproto.nullable) = false];
// signing_infos represents a map between validator addresses and their
// missed_blocks represents a map between validator addresses and their
// missed blocks.
repeated ValidatorMissedBlocks missed_blocks = 3
[(gogoproto.moretags) = "yaml:\"missed_blocks\"", (gogoproto.nullable) = false];

View File

@ -5,10 +5,9 @@ import (
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/server/rosetta"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/server/rosetta"
)
// RosettaCommand builds the rosetta root command given

View File

@ -5,7 +5,8 @@ import (
"encoding/hex"
"github.com/coinbase/rosetta-sdk-go/types"
crgerrs "github.com/tendermint/cosmos-rosetta-gateway/errors"
crgerrs "github.com/cosmos/cosmos-sdk/server/rosetta/lib/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
)

View File

@ -18,8 +18,8 @@ import (
"github.com/tendermint/tendermint/rpc/client/http"
"google.golang.org/grpc"
crgerrs "github.com/tendermint/cosmos-rosetta-gateway/errors"
crgtypes "github.com/tendermint/cosmos-rosetta-gateway/types"
crgerrs "github.com/cosmos/cosmos-sdk/server/rosetta/lib/errors"
crgtypes "github.com/cosmos/cosmos-sdk/server/rosetta/lib/types"
sdk "github.com/cosmos/cosmos-sdk/types"
grpctypes "github.com/cosmos/cosmos-sdk/types/grpc"

View File

@ -7,7 +7,8 @@ import (
"github.com/coinbase/rosetta-sdk-go/types"
"github.com/spf13/pflag"
crg "github.com/tendermint/cosmos-rosetta-gateway/server"
crg "github.com/cosmos/cosmos-sdk/server/rosetta/lib/server"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"

View File

@ -11,17 +11,19 @@ import (
"github.com/tendermint/tendermint/crypto"
"github.com/btcsuite/btcd/btcec"
crgtypes "github.com/tendermint/cosmos-rosetta-gateway/types"
tmcoretypes "github.com/tendermint/tendermint/rpc/core/types"
crgtypes "github.com/cosmos/cosmos-sdk/server/rosetta/lib/types"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
rosettatypes "github.com/coinbase/rosetta-sdk-go/types"
crgerrs "github.com/tendermint/cosmos-rosetta-gateway/errors"
abci "github.com/tendermint/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"
crgerrs "github.com/cosmos/cosmos-sdk/server/rosetta/lib/errors"
sdkclient "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
@ -251,7 +253,7 @@ func (c converter) Ops(status string, msg sdk.Msg) ([]*rosettatypes.Operation, e
for i, signer := range msg.GetSigners() {
op := &rosettatypes.Operation{
Type: opName,
Status: status,
Status: &status,
Account: &rosettatypes.AccountIdentifier{Address: signer.String()},
Metadata: meta,
}
@ -395,7 +397,7 @@ func sdkEventToBalanceOperations(status string, event abci.Event) (operations []
op := &rosettatypes.Operation{
Type: event.Type,
Status: status,
Status: &status,
Account: &rosettatypes.AccountIdentifier{Address: accountIdentifier},
Amount: &rosettatypes.Amount{
Value: value,
@ -520,7 +522,7 @@ func (c converter) SyncStatus(status *tmcoretypes.ResultStatus) *rosettatypes.Sy
}
return &rosettatypes.SyncStatus{
CurrentIndex: status.SyncInfo.LatestBlockHeight,
CurrentIndex: &status.SyncInfo.LatestBlockHeight,
TargetIndex: nil, // sync info does not allow us to get target height
Stage: &stage,
}

View File

@ -5,8 +5,6 @@ import (
"encoding/json"
"testing"
"github.com/cosmos/cosmos-sdk/server/rosetta"
abci "github.com/tendermint/tendermint/abci/types"
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
@ -17,7 +15,9 @@ import (
rosettatypes "github.com/coinbase/rosetta-sdk-go/types"
"github.com/stretchr/testify/suite"
crgerrs "github.com/tendermint/cosmos-rosetta-gateway/errors"
"github.com/cosmos/cosmos-sdk/server/rosetta"
crgerrs "github.com/cosmos/cosmos-sdk/server/rosetta/lib/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"

View File

@ -0,0 +1,146 @@
package errors
// errors.go contains all the errors returned by the adapter implementation
// plus some extra utilities to parse those errors
import (
"fmt"
grpccodes "google.golang.org/grpc/codes"
grpcstatus "google.golang.org/grpc/status"
"github.com/coinbase/rosetta-sdk-go/types"
)
// ListErrors lists all the registered errors
func ListErrors() []*types.Error {
return registry.list()
}
// SealAndListErrors seals the registry and lists its errors
func SealAndListErrors() []*types.Error {
registry.seal()
return registry.list()
}
// Error defines an error that can be converted to a Rosetta API error.
type Error struct {
rosErr *types.Error
}
func (e *Error) Error() string {
if e.rosErr == nil {
return ErrUnknown.Error()
}
return fmt.Sprintf("rosetta: (%d) %s", e.rosErr.Code, e.rosErr.Message)
}
// Is implements errors.Is for *Error, two errors are considered equal
// if their error codes are identical
func (e *Error) Is(err error) bool {
// assert it can be casted
rosErr, ok := err.(*Error)
if rosErr == nil || !ok {
return false
}
// check that both *Error's are correctly initialized to avoid dereference panics
if rosErr.rosErr == nil || e.rosErr == nil {
return false
}
// messages are equal if their error codes match
return rosErr.rosErr.Code == e.rosErr.Code
}
// WrapError wraps the rosetta error with additional context
func WrapError(err *Error, msg string) *Error {
return &Error{rosErr: &types.Error{
Code: err.rosErr.Code,
Message: err.rosErr.Message,
Description: err.rosErr.Description,
Retriable: err.rosErr.Retriable,
Details: map[string]interface{}{
"info": msg,
},
}}
}
// ToRosetta attempts to converting an error into a rosetta
// error, if the error cannot be converted it will be parsed as unknown
func ToRosetta(err error) *types.Error {
// if it's null or not known
rosErr, ok := err.(*Error)
if rosErr == nil || !ok {
return ToRosetta(WrapError(ErrUnknown, ErrUnknown.Error()))
}
return rosErr.rosErr
}
// FromGRPCToRosettaError converts a gRPC error to rosetta error
func FromGRPCToRosettaError(err error) *Error {
status, ok := grpcstatus.FromError(err)
if !ok {
return WrapError(ErrUnknown, err.Error())
}
switch status.Code() {
case grpccodes.NotFound:
return WrapError(ErrNotFound, status.Message())
case grpccodes.FailedPrecondition:
return WrapError(ErrBadArgument, status.Message())
case grpccodes.InvalidArgument:
return WrapError(ErrBadArgument, status.Message())
case grpccodes.Internal:
return WrapError(ErrInternal, status.Message())
default:
return WrapError(ErrUnknown, status.Message())
}
}
func RegisterError(code int32, message string, retryable bool, description string) *Error {
e := &Error{rosErr: &types.Error{
Code: code,
Message: message,
Description: &description,
Retriable: retryable,
Details: nil,
}}
registry.add(e)
return e
}
// Default error list
var (
// ErrUnknown defines an unknown error, if this is returned it means
// the library is ignoring an error
ErrUnknown = RegisterError(0, "unknown", false, "unknown error")
// ErrOffline is returned when there is an attempt to query an endpoint in offline mode
ErrOffline = RegisterError(1, "cannot query endpoint in offline mode", false, "returned when querying an online endpoint in offline mode")
// ErrNetworkNotSupported is returned when there is an attempt to query a network which is not supported
ErrNetworkNotSupported = RegisterError(2, "network is not supported", false, "returned when querying a non supported network")
// ErrCodec is returned when there's an error while marshalling or unmarshalling data
ErrCodec = RegisterError(3, "encode/decode error", true, "returned when there are errors encoding or decoding information to and from the node")
// ErrInvalidOperation is returned when the operation supplied to rosetta is not a valid one
ErrInvalidOperation = RegisterError(4, "invalid operation", false, "returned when the operation is not valid")
// ErrInvalidTransaction is returned when the provided hex bytes of a TX are not valid
ErrInvalidTransaction = RegisterError(5, "invalid transaction", false, "returned when the transaction is invalid")
// ErrInvalidAddress is returned when the byte of the address are bad
ErrInvalidAddress = RegisterError(7, "invalid address", false, "returned when the address is malformed")
// ErrInvalidPubkey is returned when the public key is invalid
ErrInvalidPubkey = RegisterError(8, "invalid pubkey", false, "returned when the public key is invalid")
// ErrInterpreting is returned when there are errors interpreting the data from the node, most likely related to breaking changes, version incompatibilities
ErrInterpreting = RegisterError(9, "error interpreting data from node", false, "returned when there are issues interpreting requests or response from node")
ErrInvalidMemo = RegisterError(11, "invalid memo", false, "returned when the memo is invalid")
// ErrBadArgument is returned when the request is malformed
ErrBadArgument = RegisterError(400, "bad argument", false, "request is malformed")
// ErrNotFound is returned when the required object was not found
// retry is set to true because something that is not found now
// might be found later, example: a TX
ErrNotFound = RegisterError(404, "not found", true, "returned when the node does not find what the client is asking for")
// ErrInternal is returned when the node is experiencing internal errors
ErrInternal = RegisterError(500, "internal error", false, "returned when the node experiences internal errors")
// ErrBadGateway is returned when there are problems interacting with the nodes
ErrBadGateway = RegisterError(502, "bad gateway", true, "return when the node is unreachable")
// ErrNotImplemented is returned when a method is not implemented yet
ErrNotImplemented = RegisterError(14, "not implemented", false, "returned when querying an endpoint which is not implemented")
// ErrUnsupportedCurve is returned when the curve specified is not supported
ErrUnsupportedCurve = RegisterError(15, "unsupported curve, expected secp256k1", false, "returned when using an unsupported crypto curve")
)

View File

@ -0,0 +1,68 @@
package errors
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestRegisterError(t *testing.T) {
var error *Error
// this is the number of errors registered by default in errors.go
registeredErrorsCount := 16
assert.Equal(t, len(registry.list()), registeredErrorsCount)
assert.ElementsMatch(t, registry.list(), ListErrors())
// add a new Error
error = RegisterError(69, "nice!", false, "nice!")
assert.NotNil(t, error)
// now we have a new error
registeredErrorsCount++
assert.Equal(t, len(ListErrors()), registeredErrorsCount)
// re-register an error should not change anything
error = RegisterError(69, "nice!", false, "nice!")
assert.Equal(t, len(ListErrors()), registeredErrorsCount)
// test sealing
assert.Equal(t, registry.sealed, false)
errors := SealAndListErrors()
assert.Equal(t, registry.sealed, true)
assert.Equal(t, len(errors), registeredErrorsCount)
// add a new error on a sealed registry
error = RegisterError(1024, "bytes", false, "bytes")
assert.NotNil(t, error)
}
func TestError_Error(t *testing.T) {
var error *Error
// nil cases
assert.False(t, ErrOffline.Is(error))
error = &Error{}
assert.False(t, ErrOffline.Is(error))
// wrong type
assert.False(t, ErrOffline.Is(&MyError{}))
// test with wrapping an error
error = WrapError(ErrOffline, "offline")
assert.True(t, ErrOffline.Is(error))
// test equality
assert.False(t, ErrOffline.Is(ErrBadGateway))
assert.True(t, ErrBadGateway.Is(ErrBadGateway))
}
func TestToRosetta(t *testing.T) {
var error *Error
// nil case
assert.NotNil(t, ToRosetta(error))
// wrong type
assert.NotNil(t, ToRosetta(&MyError{}))
}
type MyError struct {
}
func (e *MyError) Error() string {
return ""
}
func (e *MyError) Is(err error) bool {
return true
}

View File

@ -0,0 +1,48 @@
package errors
import (
"fmt"
"os"
"sync"
"github.com/coinbase/rosetta-sdk-go/types"
)
type errorRegistry struct {
mu *sync.RWMutex
sealed bool
errors map[int32]*types.Error
}
func (r *errorRegistry) add(err *Error) {
r.mu.Lock()
defer r.mu.Unlock()
if r.sealed {
_, _ = fmt.Fprintln(os.Stderr, "[ROSETTA] WARNING: attempts to register errors after seal will be ignored")
}
if _, ok := r.errors[err.rosErr.Code]; ok {
_, _ = fmt.Fprintln(os.Stderr, "[ROSETTA] WARNING: attempts to register an already registered error will be ignored, code: ", err.rosErr.Code)
}
r.errors[err.rosErr.Code] = err.rosErr
}
func (r errorRegistry) list() []*types.Error {
r.mu.RLock()
defer r.mu.RUnlock()
rosErrs := make([]*types.Error, 0, len(registry.errors))
for _, v := range r.errors {
rosErrs = append(rosErrs, v)
}
return rosErrs
}
func (r *errorRegistry) seal() {
r.mu.Lock()
defer r.mu.Unlock()
r.sealed = true
}
var registry = errorRegistry{
mu: new(sync.RWMutex),
errors: make(map[int32]*types.Error),
}

View File

@ -0,0 +1,138 @@
package service
import (
"context"
"crypto/sha256"
"encoding/hex"
"strings"
"github.com/coinbase/rosetta-sdk-go/types"
"github.com/cosmos/cosmos-sdk/server/rosetta/lib/errors"
)
// ConstructionCombine Combine creates a network-specific transaction from an unsigned transaction
// and an array of provided signatures. The signed transaction returned from this method will be
// sent to the /construction/submit endpoint by the caller.
func (on OnlineNetwork) ConstructionCombine(ctx context.Context, request *types.ConstructionCombineRequest) (*types.ConstructionCombineResponse, *types.Error) {
txBytes, err := hex.DecodeString(request.UnsignedTransaction)
if err != nil {
return nil, errors.ToRosetta(err)
}
signedTx, err := on.client.SignedTx(ctx, txBytes, request.Signatures)
if err != nil {
return nil, errors.ToRosetta(err)
}
return &types.ConstructionCombineResponse{
SignedTransaction: hex.EncodeToString(signedTx),
}, nil
}
// ConstructionDerive Derive returns the AccountIdentifier associated with a public key.
func (on OnlineNetwork) ConstructionDerive(_ context.Context, request *types.ConstructionDeriveRequest) (*types.ConstructionDeriveResponse, *types.Error) {
account, err := on.client.AccountIdentifierFromPublicKey(request.PublicKey)
if err != nil {
return nil, errors.ToRosetta(err)
}
return &types.ConstructionDeriveResponse{
AccountIdentifier: account,
Metadata: nil,
}, nil
}
// ConstructionHash TransactionHash returns the network-specific transaction hash for a signed
// transaction.
func (on OnlineNetwork) ConstructionHash(ctx context.Context, request *types.ConstructionHashRequest) (*types.TransactionIdentifierResponse, *types.Error) {
bz, err := hex.DecodeString(request.SignedTransaction)
if err != nil {
return nil, errors.ToRosetta(errors.WrapError(errors.ErrInvalidTransaction, "error decoding tx"))
}
hash := sha256.Sum256(bz)
bzHash := hash[:]
hashString := hex.EncodeToString(bzHash)
return &types.TransactionIdentifierResponse{
TransactionIdentifier: &types.TransactionIdentifier{
Hash: strings.ToUpper(hashString),
},
}, nil
}
// ConstructionMetadata Get any information required to construct a transaction for a specific
// network (i.e. ChainID, Gas, Memo, ...).
func (on OnlineNetwork) ConstructionMetadata(ctx context.Context, request *types.ConstructionMetadataRequest) (*types.ConstructionMetadataResponse, *types.Error) {
metadata, err := on.client.ConstructionMetadataFromOptions(ctx, request.Options)
if err != nil {
return nil, errors.ToRosetta(err)
}
return &types.ConstructionMetadataResponse{
Metadata: metadata,
}, nil
}
// ConstructionParse Parse is called on both unsigned and signed transactions to understand the
// intent of the formulated transaction. This is run as a sanity check before signing (after
// /construction/payloads) and before broadcast (after /construction/combine).
func (on OnlineNetwork) ConstructionParse(ctx context.Context, request *types.ConstructionParseRequest) (*types.ConstructionParseResponse, *types.Error) {
txBytes, err := hex.DecodeString(request.Transaction)
if err != nil {
err := errors.WrapError(errors.ErrInvalidTransaction, err.Error())
return nil, errors.ToRosetta(err)
}
ops, signers, err := on.client.TxOperationsAndSignersAccountIdentifiers(request.Signed, txBytes)
if err != nil {
return nil, errors.ToRosetta(err)
}
return &types.ConstructionParseResponse{
Operations: ops,
AccountIdentifierSigners: signers,
Metadata: nil,
}, nil
}
// ConstructionPayloads Payloads is called with an array of operations and the response from
// /construction/metadata. It returns an unsigned transaction blob and a collection of payloads that
// must be signed by particular AccountIdentifiers using a certain SignatureType.
func (on OnlineNetwork) ConstructionPayloads(ctx context.Context, request *types.ConstructionPayloadsRequest) (*types.ConstructionPayloadsResponse, *types.Error) {
payload, err := on.client.ConstructionPayload(ctx, request)
if err != nil {
return nil, errors.ToRosetta(err)
}
return payload, nil
}
// ConstructionPreprocess Preprocess is called prior to /construction/payloads to construct a
// request for any metadata that is needed for transaction construction given (i.e. account nonce).
func (on OnlineNetwork) ConstructionPreprocess(ctx context.Context, request *types.ConstructionPreprocessRequest) (*types.ConstructionPreprocessResponse, *types.Error) {
options, err := on.client.PreprocessOperationsToOptions(ctx, request)
if err != nil {
return nil, errors.ToRosetta(err)
}
return options, nil
}
// ConstructionSubmit Submit a pre-signed transaction to the node. This call does not block on the
// transaction being included in a block. Rather, it returns immediately with an indication of
// whether or not the transaction was included in the mempool.
func (on OnlineNetwork) ConstructionSubmit(ctx context.Context, request *types.ConstructionSubmitRequest) (*types.TransactionIdentifierResponse, *types.Error) {
txBytes, err := hex.DecodeString(request.SignedTransaction)
if err != nil {
return nil, errors.ToRosetta(err)
}
res, meta, err := on.client.PostTx(txBytes)
if err != nil {
return nil, errors.ToRosetta(err)
}
return &types.TransactionIdentifierResponse{
TransactionIdentifier: res,
Metadata: meta,
}, nil
}

View File

@ -0,0 +1,159 @@
package service
import (
"context"
"github.com/coinbase/rosetta-sdk-go/types"
"github.com/cosmos/cosmos-sdk/server/rosetta/lib/errors"
crgtypes "github.com/cosmos/cosmos-sdk/server/rosetta/lib/types"
)
// AccountBalance retrieves the account balance of an address
// rosetta requires us to fetch the block information too
func (on OnlineNetwork) AccountBalance(ctx context.Context, request *types.AccountBalanceRequest) (*types.AccountBalanceResponse, *types.Error) {
var (
height int64
block crgtypes.BlockResponse
err error
)
switch {
case request.BlockIdentifier == nil:
block, err = on.client.BlockByHeight(ctx, nil)
if err != nil {
return nil, errors.ToRosetta(err)
}
case request.BlockIdentifier.Hash != nil:
block, err = on.client.BlockByHash(ctx, *request.BlockIdentifier.Hash)
if err != nil {
return nil, errors.ToRosetta(err)
}
height = block.Block.Index
case request.BlockIdentifier.Index != nil:
height = *request.BlockIdentifier.Index
block, err = on.client.BlockByHeight(ctx, &height)
if err != nil {
return nil, errors.ToRosetta(err)
}
}
accountCoins, err := on.client.Balances(ctx, request.AccountIdentifier.Address, &height)
if err != nil {
return nil, errors.ToRosetta(err)
}
return &types.AccountBalanceResponse{
BlockIdentifier: block.Block,
Balances: accountCoins,
Metadata: nil,
}, nil
}
// Block gets the transactions in the given block
func (on OnlineNetwork) Block(ctx context.Context, request *types.BlockRequest) (*types.BlockResponse, *types.Error) {
var (
blockResponse crgtypes.BlockTransactionsResponse
err error
)
// block identifier is assumed not to be nil as rosetta will do this check for us
// check if we have to query via hash or block number
switch {
case request.BlockIdentifier.Hash != nil:
blockResponse, err = on.client.BlockTransactionsByHash(ctx, *request.BlockIdentifier.Hash)
if err != nil {
return nil, errors.ToRosetta(err)
}
case request.BlockIdentifier.Index != nil:
blockResponse, err = on.client.BlockTransactionsByHeight(ctx, request.BlockIdentifier.Index)
if err != nil {
return nil, errors.ToRosetta(err)
}
default:
err := errors.WrapError(errors.ErrBadArgument, "at least one of hash or index needs to be specified")
return nil, errors.ToRosetta(err)
}
return &types.BlockResponse{
Block: &types.Block{
BlockIdentifier: blockResponse.Block,
ParentBlockIdentifier: blockResponse.ParentBlock,
Timestamp: blockResponse.MillisecondTimestamp,
Transactions: blockResponse.Transactions,
Metadata: nil,
},
OtherTransactions: nil,
}, nil
}
// BlockTransaction gets the given transaction in the specified block, we do not need to check the block itself too
// due to the fact that tendermint achieves instant finality
func (on OnlineNetwork) BlockTransaction(ctx context.Context, request *types.BlockTransactionRequest) (*types.BlockTransactionResponse, *types.Error) {
tx, err := on.client.GetTx(ctx, request.TransactionIdentifier.Hash)
if err != nil {
return nil, errors.ToRosetta(err)
}
return &types.BlockTransactionResponse{
Transaction: tx,
}, nil
}
// Mempool fetches the transactions contained in the mempool
func (on OnlineNetwork) Mempool(ctx context.Context, _ *types.NetworkRequest) (*types.MempoolResponse, *types.Error) {
txs, err := on.client.Mempool(ctx)
if err != nil {
return nil, errors.ToRosetta(err)
}
return &types.MempoolResponse{
TransactionIdentifiers: txs,
}, nil
}
// MempoolTransaction fetches a single transaction in the mempool
// NOTE: it is not implemented yet
func (on OnlineNetwork) MempoolTransaction(ctx context.Context, request *types.MempoolTransactionRequest) (*types.MempoolTransactionResponse, *types.Error) {
tx, err := on.client.GetUnconfirmedTx(ctx, request.TransactionIdentifier.Hash)
if err != nil {
return nil, errors.ToRosetta(err)
}
return &types.MempoolTransactionResponse{
Transaction: tx,
}, nil
}
func (on OnlineNetwork) NetworkList(_ context.Context, _ *types.MetadataRequest) (*types.NetworkListResponse, *types.Error) {
return &types.NetworkListResponse{NetworkIdentifiers: []*types.NetworkIdentifier{on.network}}, nil
}
func (on OnlineNetwork) NetworkOptions(_ context.Context, _ *types.NetworkRequest) (*types.NetworkOptionsResponse, *types.Error) {
return on.networkOptions, nil
}
func (on OnlineNetwork) NetworkStatus(ctx context.Context, _ *types.NetworkRequest) (*types.NetworkStatusResponse, *types.Error) {
block, err := on.client.BlockByHeight(ctx, nil)
if err != nil {
return nil, errors.ToRosetta(err)
}
peers, err := on.client.Peers(ctx)
if err != nil {
return nil, errors.ToRosetta(err)
}
syncStatus, err := on.client.Status(ctx)
if err != nil {
return nil, errors.ToRosetta(err)
}
return &types.NetworkStatusResponse{
CurrentBlockIdentifier: block.Block,
CurrentBlockTimestamp: block.MillisecondTimestamp,
GenesisBlockIdentifier: on.genesisBlockIdentifier,
OldestBlockIdentifier: nil,
SyncStatus: syncStatus,
Peers: peers,
}, nil
}

View File

@ -0,0 +1,63 @@
package service
import (
"context"
"github.com/coinbase/rosetta-sdk-go/types"
crgerrs "github.com/cosmos/cosmos-sdk/server/rosetta/lib/errors"
crgtypes "github.com/cosmos/cosmos-sdk/server/rosetta/lib/types"
)
// NewOffline instantiates the instance of an offline network
// whilst the offline network does not support the DataAPI,
// it supports a subset of the construction API.
func NewOffline(network *types.NetworkIdentifier, client crgtypes.Client) (crgtypes.API, error) {
return OfflineNetwork{
OnlineNetwork{
client: client,
network: network,
networkOptions: networkOptionsFromClient(client),
},
}, nil
}
// OfflineNetwork implements an offline data API
// which is basically a data API that constantly
// returns errors, because it cannot be used if offline
type OfflineNetwork struct {
OnlineNetwork
}
// Implement DataAPI in offline mode, which means no method is available
func (o OfflineNetwork) AccountBalance(_ context.Context, _ *types.AccountBalanceRequest) (*types.AccountBalanceResponse, *types.Error) {
return nil, crgerrs.ToRosetta(crgerrs.ErrOffline)
}
func (o OfflineNetwork) Block(_ context.Context, _ *types.BlockRequest) (*types.BlockResponse, *types.Error) {
return nil, crgerrs.ToRosetta(crgerrs.ErrOffline)
}
func (o OfflineNetwork) BlockTransaction(_ context.Context, _ *types.BlockTransactionRequest) (*types.BlockTransactionResponse, *types.Error) {
return nil, crgerrs.ToRosetta(crgerrs.ErrOffline)
}
func (o OfflineNetwork) Mempool(_ context.Context, _ *types.NetworkRequest) (*types.MempoolResponse, *types.Error) {
return nil, crgerrs.ToRosetta(crgerrs.ErrOffline)
}
func (o OfflineNetwork) MempoolTransaction(_ context.Context, _ *types.MempoolTransactionRequest) (*types.MempoolTransactionResponse, *types.Error) {
return nil, crgerrs.ToRosetta(crgerrs.ErrOffline)
}
func (o OfflineNetwork) NetworkStatus(_ context.Context, _ *types.NetworkRequest) (*types.NetworkStatusResponse, *types.Error) {
return nil, crgerrs.ToRosetta(crgerrs.ErrOffline)
}
func (o OfflineNetwork) ConstructionSubmit(_ context.Context, _ *types.ConstructionSubmitRequest) (*types.TransactionIdentifierResponse, *types.Error) {
return nil, crgerrs.ToRosetta(crgerrs.ErrOffline)
}
func (o OfflineNetwork) ConstructionMetadata(_ context.Context, _ *types.ConstructionMetadataRequest) (*types.ConstructionMetadataResponse, *types.Error) {
return nil, crgerrs.ToRosetta(crgerrs.ErrOffline)
}

View File

@ -0,0 +1,66 @@
package service
import (
"context"
"time"
"github.com/coinbase/rosetta-sdk-go/types"
crgerrs "github.com/cosmos/cosmos-sdk/server/rosetta/lib/errors"
crgtypes "github.com/cosmos/cosmos-sdk/server/rosetta/lib/types"
)
// genesisBlockFetchTimeout defines a timeout to fetch the genesis block
const genesisBlockFetchTimeout = 15 * time.Second
// NewOnlineNetwork builds a single network adapter.
// It will get the Genesis block on the beginning to avoid calling it everytime.
func NewOnlineNetwork(network *types.NetworkIdentifier, client crgtypes.Client) (crgtypes.API, error) {
ctx, cancel := context.WithTimeout(context.Background(), genesisBlockFetchTimeout)
defer cancel()
var genesisHeight int64 = 1
block, err := client.BlockByHeight(ctx, &genesisHeight)
if err != nil {
return OnlineNetwork{}, err
}
return OnlineNetwork{
client: client,
network: network,
networkOptions: networkOptionsFromClient(client),
genesisBlockIdentifier: block.Block,
}, nil
}
// OnlineNetwork groups together all the components required for the full rosetta implementation
type OnlineNetwork struct {
client crgtypes.Client // used to query cosmos app + tendermint
network *types.NetworkIdentifier // identifies the network, it's static
networkOptions *types.NetworkOptionsResponse // identifies the network options, it's static
genesisBlockIdentifier *types.BlockIdentifier // identifies genesis block, it's static
}
// AccountsCoins - relevant only for UTXO based chain
// see https://www.rosetta-api.org/docs/AccountApi.html#accountcoins
func (o OnlineNetwork) AccountCoins(_ context.Context, _ *types.AccountCoinsRequest) (*types.AccountCoinsResponse, *types.Error) {
return nil, crgerrs.ToRosetta(crgerrs.ErrOffline)
}
// networkOptionsFromClient builds network options given the client
func networkOptionsFromClient(client crgtypes.Client) *types.NetworkOptionsResponse {
return &types.NetworkOptionsResponse{
Version: &types.Version{
RosettaVersion: crgtypes.SpecVersion,
NodeVersion: client.Version(),
},
Allow: &types.Allow{
OperationStatuses: client.OperationStatuses(),
OperationTypes: client.SupportedOperations(),
Errors: crgerrs.SealAndListErrors(),
HistoricalBalanceLookup: true,
},
}
}

View File

@ -0,0 +1,116 @@
package server
import (
"fmt"
"net/http"
"time"
assert "github.com/coinbase/rosetta-sdk-go/asserter"
"github.com/coinbase/rosetta-sdk-go/server"
"github.com/coinbase/rosetta-sdk-go/types"
"github.com/cosmos/cosmos-sdk/server/rosetta/lib/internal/service"
crgtypes "github.com/cosmos/cosmos-sdk/server/rosetta/lib/types"
)
const DefaultRetries = 5
const DefaultRetryWait = 5 * time.Second
// Settings define the rosetta server settings
type Settings struct {
// Network contains the information regarding the network
Network *types.NetworkIdentifier
// Client is the online API handler
Client crgtypes.Client
// Listen is the address the handler will listen at
Listen string
// Offline defines if the rosetta service should be exposed in offline mode
Offline bool
// Retries is the number of readiness checks that will be attempted when instantiating the handler
// valid only for online API
Retries int
// RetryWait is the time that will be waited between retries
RetryWait time.Duration
}
type Server struct {
h http.Handler
addr string
}
func (h Server) Start() error {
return http.ListenAndServe(h.addr, h.h)
}
func NewServer(settings Settings) (Server, error) {
asserter, err := assert.NewServer(
settings.Client.SupportedOperations(),
true,
[]*types.NetworkIdentifier{settings.Network},
nil,
false,
)
if err != nil {
return Server{}, fmt.Errorf("cannot build asserter: %w", err)
}
var (
adapter crgtypes.API
)
switch settings.Offline {
case true:
adapter, err = newOfflineAdapter(settings)
case false:
adapter, err = newOnlineAdapter(settings)
}
if err != nil {
return Server{}, err
}
h := server.NewRouter(
server.NewAccountAPIController(adapter, asserter),
server.NewBlockAPIController(adapter, asserter),
server.NewNetworkAPIController(adapter, asserter),
server.NewMempoolAPIController(adapter, asserter),
server.NewConstructionAPIController(adapter, asserter),
)
return Server{
h: h,
addr: settings.Listen,
}, nil
}
func newOfflineAdapter(settings Settings) (crgtypes.API, error) {
if settings.Client == nil {
return nil, fmt.Errorf("client is nil")
}
return service.NewOffline(settings.Network, settings.Client)
}
func newOnlineAdapter(settings Settings) (crgtypes.API, error) {
if settings.Client == nil {
return nil, fmt.Errorf("client is nil")
}
if settings.Retries <= 0 {
settings.Retries = DefaultRetries
}
if settings.RetryWait == 0 {
settings.RetryWait = DefaultRetryWait
}
var err error
err = settings.Client.Bootstrap()
if err != nil {
return nil, err
}
for i := 0; i < settings.Retries; i++ {
err = settings.Client.Ready()
if err != nil {
time.Sleep(settings.RetryWait)
continue
}
return service.NewOnlineNetwork(settings.Network, settings.Client)
}
return nil, fmt.Errorf("maximum number of retries exceeded, last error: %w", err)
}

View File

@ -0,0 +1,164 @@
package types
import (
"context"
"github.com/coinbase/rosetta-sdk-go/server"
"github.com/coinbase/rosetta-sdk-go/types"
)
// SpecVersion defines the specification of rosetta
const SpecVersion = ""
// NetworkInformationProvider defines the interface used to provide information regarding
// the network and the version of the cosmos sdk used
type NetworkInformationProvider interface {
// SupportedOperations lists the operations supported by the implementation
SupportedOperations() []string
// OperationStatuses returns the list of statuses supported by the implementation
OperationStatuses() []*types.OperationStatus
// Version returns the version of the node
Version() string
}
// Client defines the API the client implementation should provide.
type Client interface {
// Bootstrap Needed if the client needs to perform some action before connecting.
Bootstrap() error
// Ready checks if the servicer constraints for queries are satisfied
// for example the node might still not be ready, it's useful in process
// when the rosetta instance might come up before the node itself
// the servicer must return nil if the node is ready
Ready() error
// Data API
// Balances fetches the balance of the given address
// if height is not nil, then the balance will be displayed
// at the provided height, otherwise last block balance will be returned
Balances(ctx context.Context, addr string, height *int64) ([]*types.Amount, error)
// BlockByHash gets a block and its transaction at the provided height
BlockByHash(ctx context.Context, hash string) (BlockResponse, error)
// BlockByHeight gets a block given its height, if height is nil then last block is returned
BlockByHeight(ctx context.Context, height *int64) (BlockResponse, error)
// BlockTransactionsByHash gets the block, parent block and transactions
// given the block hash.
BlockTransactionsByHash(ctx context.Context, hash string) (BlockTransactionsResponse, error)
// BlockTransactionsByHeight gets the block, parent block and transactions
// given the block hash.
BlockTransactionsByHeight(ctx context.Context, height *int64) (BlockTransactionsResponse, error)
// GetTx gets a transaction given its hash
GetTx(ctx context.Context, hash string) (*types.Transaction, error)
// GetUnconfirmedTx gets an unconfirmed Tx given its hash
// NOTE(fdymylja): NOT IMPLEMENTED YET!
GetUnconfirmedTx(ctx context.Context, hash string) (*types.Transaction, error)
// Mempool returns the list of the current non confirmed transactions
Mempool(ctx context.Context) ([]*types.TransactionIdentifier, error)
// Peers gets the peers currently connected to the node
Peers(ctx context.Context) ([]*types.Peer, error)
// Status returns the node status, such as sync data, version etc
Status(ctx context.Context) (*types.SyncStatus, error)
// Construction API
// PostTx posts txBytes to the node and returns the transaction identifier plus metadata related
// to the transaction itself.
PostTx(txBytes []byte) (res *types.TransactionIdentifier, meta map[string]interface{}, err error)
// ConstructionMetadataFromOptions builds metadata map from an option map
ConstructionMetadataFromOptions(ctx context.Context, options map[string]interface{}) (meta map[string]interface{}, err error)
OfflineClient
}
// OfflineClient defines the functionalities supported without having access to the node
type OfflineClient interface {
NetworkInformationProvider
// SignedTx returns the signed transaction given the tx bytes (msgs) plus the signatures
SignedTx(ctx context.Context, txBytes []byte, sigs []*types.Signature) (signedTxBytes []byte, err error)
// TxOperationsAndSignersAccountIdentifiers returns the operations related to a transaction and the account
// identifiers if the transaction is signed
TxOperationsAndSignersAccountIdentifiers(signed bool, hexBytes []byte) (ops []*types.Operation, signers []*types.AccountIdentifier, err error)
// ConstructionPayload returns the construction payload given the request
ConstructionPayload(ctx context.Context, req *types.ConstructionPayloadsRequest) (resp *types.ConstructionPayloadsResponse, err error)
// PreprocessOperationsToOptions returns the options given the preprocess operations
PreprocessOperationsToOptions(ctx context.Context, req *types.ConstructionPreprocessRequest) (resp *types.ConstructionPreprocessResponse, err error)
// AccountIdentifierFromPublicKey returns the account identifier given the public key
AccountIdentifierFromPublicKey(pubKey *types.PublicKey) (*types.AccountIdentifier, error)
}
type BlockTransactionsResponse struct {
BlockResponse
Transactions []*types.Transaction
}
type BlockResponse struct {
Block *types.BlockIdentifier
ParentBlock *types.BlockIdentifier
MillisecondTimestamp int64
TxCount int64
}
// API defines the exposed APIs
// if the service is online
type API interface {
DataAPI
ConstructionAPI
}
// DataAPI defines the full data API implementation
type DataAPI interface {
server.NetworkAPIServicer
server.AccountAPIServicer
server.BlockAPIServicer
server.MempoolAPIServicer
}
var _ server.ConstructionAPIServicer = ConstructionAPI(nil)
// ConstructionAPI defines the full construction API with
// the online and offline endpoints
type ConstructionAPI interface {
ConstructionOnlineAPI
ConstructionOfflineAPI
}
// ConstructionOnlineAPI defines the construction methods
// allowed in an online implementation
type ConstructionOnlineAPI interface {
ConstructionMetadata(
context.Context,
*types.ConstructionMetadataRequest,
) (*types.ConstructionMetadataResponse, *types.Error)
ConstructionSubmit(
context.Context,
*types.ConstructionSubmitRequest,
) (*types.TransactionIdentifierResponse, *types.Error)
}
// ConstructionOfflineAPI defines the construction methods
// allowed
type ConstructionOfflineAPI interface {
ConstructionCombine(
context.Context,
*types.ConstructionCombineRequest,
) (*types.ConstructionCombineResponse, *types.Error)
ConstructionDerive(
context.Context,
*types.ConstructionDeriveRequest,
) (*types.ConstructionDeriveResponse, *types.Error)
ConstructionHash(
context.Context,
*types.ConstructionHashRequest,
) (*types.TransactionIdentifierResponse, *types.Error)
ConstructionParse(
context.Context,
*types.ConstructionParseRequest,
) (*types.ConstructionParseResponse, *types.Error)
ConstructionPayloads(
context.Context,
*types.ConstructionPayloadsRequest,
) (*types.ConstructionPayloadsResponse, *types.Error)
ConstructionPreprocess(
context.Context,
*types.ConstructionPreprocessRequest,
) (*types.ConstructionPreprocessResponse, *types.Error)
}

View File

@ -4,7 +4,7 @@ import (
"encoding/json"
"time"
crgerrs "github.com/tendermint/cosmos-rosetta-gateway/errors"
crgerrs "github.com/cosmos/cosmos-sdk/server/rosetta/lib/errors"
)
// timeToMilliseconds converts time to milliseconds timestamp

View File

@ -9,14 +9,11 @@ import (
"runtime/pprof"
"time"
"github.com/cosmos/cosmos-sdk/server/rosetta"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/spf13/cobra"
"google.golang.org/grpc"
crgserver "github.com/tendermint/cosmos-rosetta-gateway/server"
"github.com/tendermint/tendermint/abci/server"
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
tmos "github.com/tendermint/tendermint/libs/os"
@ -26,6 +23,9 @@ import (
"github.com/tendermint/tendermint/proxy"
"github.com/tendermint/tendermint/rpc/client/local"
"github.com/cosmos/cosmos-sdk/server/rosetta"
crgserver "github.com/cosmos/cosmos-sdk/server/rosetta/lib/server"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/server/api"

View File

@ -78,6 +78,9 @@ func NewRootCmd() (*cobra.Command, params.EncodingConfig) {
}
func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) {
cfg := sdk.GetConfig()
cfg.Seal()
rootCmd.AddCommand(
genutilcli.InitCmd(simapp.ModuleBasics, simapp.DefaultNodeHome),
genutilcli.CollectGenTxsCmd(banktypes.GenesisBalancesIterator{}, simapp.DefaultNodeHome),

View File

@ -64,11 +64,13 @@ type Metrics struct {
prometheusEnabled bool
}
// GatherResponse is the response type of registered metrics
type GatherResponse struct {
Metrics []byte
ContentType string
}
// New creates a new instance of Metrics
func New(cfg Config) (*Metrics, error) {
if !cfg.Enabled {
return nil, nil

View File

@ -13,6 +13,7 @@ const (
MetricLabelNameModule = "module"
)
// NewLabel creates a new instance of Label with name and value
func NewLabel(name, value string) metrics.Label {
return metrics.Label{Name: name, Value: value}
}

View File

@ -11,6 +11,7 @@ import (
const flagLong = "long"
// NewVersionCommand returns a CLI command to interactively print the application binary version information.
func NewVersionCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "version",

View File

@ -31,7 +31,7 @@ type AccountKeeperI interface {
// Remove an account from the store.
RemoveAccount(sdk.Context, types.AccountI)
// Iterate over all accounts, calling the provided function. Stop iteraiton when it returns false.
// Iterate over all accounts, calling the provided function. Stop iteration when it returns true.
IterateAccounts(sdk.Context, func(types.AccountI) bool)
// Fetch the public key of an account at a specified address

View File

@ -1,3 +1,6 @@
// Package v034 is used for legacy migration scripts. Actual migration scripts
// for v034 have been removed, but the v039->v042 migration script still
// references types from this file, so we're keeping it for now.
// DONTCOVER
package v034

View File

@ -1,13 +0,0 @@
// DONTCOVER
package v036
import (
v034auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v034"
)
// Migrate accepts exported genesis state from v0.34 and migrates it to v0.36
// genesis state. This migration removes the CollectedFees coins from the old
// FeeCollectorKeeper.
func Migrate(oldGenState v034auth.GenesisState) GenesisState {
return NewGenesisState(oldGenState.Params)
}

View File

@ -1,26 +0,0 @@
package v036
import (
"testing"
"github.com/cosmos/cosmos-sdk/types"
v034auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v034"
"github.com/stretchr/testify/require"
)
func TestMigrate(t *testing.T) {
var genesisState GenesisState
require.NotPanics(t, func() {
genesisState = Migrate(v034auth.GenesisState{
CollectedFees: types.Coins{
{
Amount: types.NewInt(10),
Denom: "stake",
},
},
Params: v034auth.Params{}, // forwarded structure: filling and checking will be testing a no-op
})
})
require.Equal(t, genesisState, GenesisState{Params: v034auth.Params{}})
}

View File

@ -1,18 +0,0 @@
// DONTCOVER
package v036
import v034auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v034"
const (
ModuleName = "auth"
)
type (
GenesisState struct {
Params v034auth.Params `json:"params"`
}
)
func NewGenesisState(params v034auth.Params) GenesisState {
return GenesisState{params}
}

View File

@ -1,52 +0,0 @@
package v038
import (
v036auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v036"
v036genaccounts "github.com/cosmos/cosmos-sdk/x/genaccounts/legacy/v036"
)
// Migrate accepts exported genesis state from v0.34 and migrates it to v0.38
// genesis state.
func Migrate(authGenState v036auth.GenesisState, genAccountsGenState v036genaccounts.GenesisState) GenesisState {
accounts := make(GenesisAccounts, len(genAccountsGenState))
for i, acc := range genAccountsGenState {
var genAccount GenesisAccount
baseAccount := NewBaseAccount(acc.Address, acc.Coins.Sort(), nil, acc.AccountNumber, acc.Sequence)
switch {
case !acc.OriginalVesting.IsZero():
baseVestingAccount := NewBaseVestingAccount(
baseAccount, acc.OriginalVesting.Sort(), acc.DelegatedFree.Sort(),
acc.DelegatedVesting.Sort(), acc.EndTime,
)
if acc.StartTime != 0 && acc.EndTime != 0 {
// continuous vesting account type
genAccount = NewContinuousVestingAccountRaw(baseVestingAccount, acc.StartTime)
} else if acc.EndTime != 0 {
// delayed vesting account type
genAccount = NewDelayedVestingAccountRaw(baseVestingAccount)
}
case acc.ModuleName != "":
// module account type
genAccount = NewModuleAccount(baseAccount, acc.ModuleName, acc.ModulePermissions...)
default:
// standard account type
genAccount = baseAccount
}
accounts[i] = genAccount
}
accounts = SanitizeGenesisAccounts(accounts)
if err := ValidateGenAccounts(accounts); err != nil {
panic(err)
}
return NewGenesisState(authGenState.Params, accounts)
}

View File

@ -1,159 +0,0 @@
package v038
import (
"testing"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
v034auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v034"
v036auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v036"
v036genaccounts "github.com/cosmos/cosmos-sdk/x/genaccounts/legacy/v036"
"github.com/stretchr/testify/require"
)
func accAddressFromBech32(t *testing.T, addrStr string) sdk.AccAddress {
addr, err := sdk.AccAddressFromBech32(addrStr)
require.NoError(t, err)
return addr
}
func TestMigrate(t *testing.T) {
var genesisState GenesisState
params := v034auth.Params{
MaxMemoCharacters: 10,
TxSigLimit: 10,
TxSizeCostPerByte: 10,
SigVerifyCostED25519: 10,
SigVerifyCostSecp256k1: 10,
}
acc1 := v036genaccounts.GenesisAccount{
Address: accAddressFromBech32(t, "cosmos1f9xjhxm0plzrh9cskf4qee4pc2xwp0n0556gh0"),
Coins: sdk.NewCoins(sdk.NewInt64Coin("stake", 400000)),
Sequence: 1,
AccountNumber: 1,
}
acc2 := v036genaccounts.GenesisAccount{
Address: accAddressFromBech32(t, "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh"),
Coins: sdk.NewCoins(sdk.NewInt64Coin("stake", 400000000)),
Sequence: 4,
AccountNumber: 2,
ModuleName: "bonded_tokens_pool",
ModulePermissions: []string{"burner", "staking"},
}
acc3 := v036genaccounts.GenesisAccount{
Address: accAddressFromBech32(t, "cosmos17n9sztlhx32tfy0tg0zc2ttmkeeth50yyuv9he"),
Coins: sdk.NewCoins(sdk.NewInt64Coin("stake", 10000205)),
OriginalVesting: sdk.NewCoins(sdk.NewInt64Coin("stake", 10000205)),
StartTime: time.Now().Unix(),
EndTime: time.Now().Add(48 * time.Hour).Unix(),
Sequence: 5,
AccountNumber: 3,
}
acc4 := v036genaccounts.GenesisAccount{
Address: accAddressFromBech32(t, "cosmos1fmk5elg4r62mlexd36tqjcwyafs7mek0js5m4d"),
Coins: sdk.NewCoins(sdk.NewInt64Coin("stake", 10000205)),
OriginalVesting: sdk.NewCoins(sdk.NewInt64Coin("stake", 10000205)),
EndTime: time.Now().Add(48 * time.Hour).Unix(),
Sequence: 15,
AccountNumber: 4,
}
require.NotPanics(t, func() {
genesisState = Migrate(
v036auth.GenesisState{
Params: params,
},
v036genaccounts.GenesisState{acc1, acc2, acc3, acc4},
)
})
expectedAcc1 := NewBaseAccount(acc1.Address, acc1.Coins, nil, acc1.AccountNumber, acc1.Sequence)
expectedAcc2 := NewModuleAccount(
NewBaseAccount(acc2.Address, acc2.Coins, nil, acc2.AccountNumber, acc2.Sequence),
acc2.ModuleName, acc2.ModulePermissions...,
)
expectedAcc3 := NewContinuousVestingAccountRaw(
NewBaseVestingAccount(
NewBaseAccount(acc3.Address, acc3.Coins, nil, acc3.AccountNumber, acc3.Sequence),
acc3.OriginalVesting, acc3.DelegatedFree, acc3.DelegatedVesting, acc3.EndTime,
),
acc3.StartTime,
)
expectedAcc4 := NewDelayedVestingAccountRaw(
NewBaseVestingAccount(
NewBaseAccount(acc4.Address, acc4.Coins, nil, acc4.AccountNumber, acc4.Sequence),
acc4.OriginalVesting, acc4.DelegatedFree, acc4.DelegatedVesting, acc4.EndTime,
),
)
require.Equal(
t, genesisState, GenesisState{
Params: params,
Accounts: GenesisAccounts{expectedAcc1, expectedAcc2, expectedAcc3, expectedAcc4},
},
)
}
func TestMigrateInvalid(t *testing.T) {
testCases := []struct {
name string
acc v036genaccounts.GenesisAccount
}{
{
"module account with invalid name",
v036genaccounts.GenesisAccount{
Address: accAddressFromBech32(t, "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh"),
Coins: sdk.NewCoins(sdk.NewInt64Coin("stake", 400000000)),
Sequence: 4,
AccountNumber: 2,
ModuleName: " ",
ModulePermissions: []string{"burner", "staking"},
},
},
{
"module account with invalid permissions",
v036genaccounts.GenesisAccount{
Address: accAddressFromBech32(t, "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh"),
Coins: sdk.NewCoins(sdk.NewInt64Coin("stake", 400000000)),
Sequence: 4,
AccountNumber: 2,
ModuleName: "bonded_tokens_pool",
ModulePermissions: []string{""},
},
},
{
"module account with invalid address",
v036genaccounts.GenesisAccount{
Address: accAddressFromBech32(t, "cosmos17n9sztlhx32tfy0tg0zc2ttmkeeth50yyuv9he"),
Coins: sdk.NewCoins(sdk.NewInt64Coin("stake", 400000000)),
Sequence: 4,
AccountNumber: 2,
ModuleName: "bonded_tokens_pool",
ModulePermissions: []string{"burner", "staking"},
},
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
require.Panics(t, func() {
Migrate(
v036auth.GenesisState{
Params: v034auth.Params{
MaxMemoCharacters: 10,
TxSigLimit: 10,
TxSizeCostPerByte: 10,
SigVerifyCostED25519: 10,
SigVerifyCostSecp256k1: 10,
},
},
v036genaccounts.GenesisState{tc.acc},
)
})
})
}
}

View File

@ -1,3 +1,6 @@
// Package v038 is used for legacy migration scripts. Actual migration scripts
// for v038 have been removed, but the v039->v042 migration script still
// references types from this file, so we're keeping it for now.
package v038
// DONTCOVER

View File

@ -1,60 +0,0 @@
package v039
import (
"fmt"
v038auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v038"
)
// Migrate accepts exported genesis state from v0.38 and migrates it to v0.39
// genesis state.
func Migrate(oldAuthGenState v038auth.GenesisState) GenesisState {
accounts := make(v038auth.GenesisAccounts, len(oldAuthGenState.Accounts))
for i, acc := range oldAuthGenState.Accounts {
switch t := acc.(type) {
case *v038auth.BaseAccount:
accounts[i] = NewBaseAccount(t.Address, t.Coins, t.PubKey, t.AccountNumber, t.Sequence)
case *v038auth.BaseVestingAccount:
accounts[i] = NewBaseVestingAccount(
NewBaseAccount(t.Address, t.Coins, t.PubKey, t.AccountNumber, t.Sequence),
t.OriginalVesting, t.DelegatedFree, t.DelegatedVesting, t.EndTime,
)
case *v038auth.ContinuousVestingAccount:
accounts[i] = NewContinuousVestingAccountRaw(
NewBaseVestingAccount(
NewBaseAccount(t.Address, t.Coins, t.PubKey, t.AccountNumber, t.Sequence),
t.OriginalVesting, t.DelegatedFree, t.DelegatedVesting, t.EndTime,
),
t.StartTime,
)
case *v038auth.DelayedVestingAccount:
accounts[i] = NewDelayedVestingAccountRaw(
NewBaseVestingAccount(
NewBaseAccount(t.Address, t.Coins, t.PubKey, t.AccountNumber, t.Sequence),
t.OriginalVesting, t.DelegatedFree, t.DelegatedVesting, t.EndTime,
),
)
case *v038auth.ModuleAccount:
accounts[i] = NewModuleAccount(
NewBaseAccount(t.Address, t.Coins, t.PubKey, t.AccountNumber, t.Sequence),
t.Name, t.Permissions...,
)
default:
panic(fmt.Sprintf("unexpected account type: %T", acc))
}
}
accounts = v038auth.SanitizeGenesisAccounts(accounts)
if err := v038auth.ValidateGenAccounts(accounts); err != nil {
panic(err)
}
return NewGenesisState(oldAuthGenState.Params, accounts)
}

View File

@ -1,104 +0,0 @@
package v039_test
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
sdk "github.com/cosmos/cosmos-sdk/types"
v038auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v038"
v039auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v039"
)
func TestMigrate(t *testing.T) {
aminoCdc := codec.NewLegacyAmino()
v039auth.RegisterLegacyAminoCodec(aminoCdc)
pub1 := ed25519.GenPrivKeyFromSecret([]byte("acc1")).PubKey()
pub2 := secp256k1.GenPrivKeyFromSecret([]byte("acc2")).PubKey()
acc1 := v038auth.BaseAccount{
Address: sdk.AccAddress(pub1.Address()),
Coins: sdk.NewCoins(sdk.NewInt64Coin("stake", 400000)),
Sequence: 1,
AccountNumber: 1,
PubKey: pub1,
}
acc2 := v038auth.BaseAccount{
Address: sdk.AccAddress(pub2.Address()),
Coins: sdk.NewCoins(sdk.NewInt64Coin("stake", 400000)),
Sequence: 2,
AccountNumber: 2,
PubKey: pub2,
}
migrated := v039auth.Migrate(
v038auth.GenesisState{
Accounts: v038auth.GenesisAccounts{&acc1, &acc2},
},
)
expectedAcc1 := v039auth.NewBaseAccount(acc1.Address, acc1.Coins, acc1.PubKey, acc1.AccountNumber, acc1.Sequence)
expectedAcc2 := v039auth.NewBaseAccount(acc2.Address, acc2.Coins, acc2.PubKey, acc2.AccountNumber, acc2.Sequence)
require.Equal(
t, migrated, v039auth.GenesisState{
Accounts: v038auth.GenesisAccounts{expectedAcc1, expectedAcc2},
},
)
json, err := aminoCdc.MarshalJSONIndent(migrated, "", " ")
require.NoError(t, err)
expectedJSON := `{
"params": {
"max_memo_characters": "0",
"tx_sig_limit": "0",
"tx_size_cost_per_byte": "0",
"sig_verify_cost_ed25519": "0",
"sig_verify_cost_secp256k1": "0"
},
"accounts": [
{
"type": "cosmos-sdk/Account",
"value": {
"address": "cosmos1j7skdhh9raxdmfhmcy2gxz8hgn0jnhfmujjsfe",
"coins": [
{
"denom": "stake",
"amount": "400000"
}
],
"public_key": {
"type": "tendermint/PubKeyEd25519",
"value": "eB0AcLMLKFRNFfh4XAAMstexfAIUQQCDnfjLZ2KJg+A="
},
"account_number": "1",
"sequence": "1"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"address": "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h",
"coins": [
{
"denom": "stake",
"amount": "400000"
}
],
"public_key": {
"type": "tendermint/PubKeySecp256k1",
"value": "AruDygh5HprMOpHOEato85dLgAsybMJVyxBGUa3KuWCr"
},
"account_number": "2",
"sequence": "2"
}
}
]
}`
require.Equal(t, expectedJSON, string(json))
}

View File

@ -4,41 +4,37 @@ order: 3
# AnteHandlers
## Handlers
The `x/auth` module presently has no transaction handlers of its own, but does expose the special `AnteHandler`, used for performing basic validity checks on a transaction, such that it could be thrown out of the mempool.
The `AnteHandler` can be seen as a set of decorators that check transactions within the current context, per [ADR 010](https://github.com/cosmos/cosmos-sdk/blob/v0.43.0-alpha1/docs/architecture/adr-010-modular-antehandler.md).
The auth module presently has no transaction handlers of its own, but does expose
the special `AnteHandler`, used for performing basic validity checks on a transaction,
such that it could be thrown out of the mempool. Note that the ante handler is called on
`CheckTx`, but _also_ on `DeliverTx`, as Tendermint proposers presently have the ability
to include in their proposed block transactions which fail `CheckTx`.
Note that the `AnteHandler` is called on both `CheckTx` and `DeliverTx`, as Tendermint proposers presently have the ability to include in their proposed block transactions which fail `CheckTx`.
### Ante Handler
## Decorators
```go
anteHandler(ak AccountKeeper, fck FeeCollectionKeeper, tx sdk.Tx)
if !tx.(StdTx)
fail with "not a StdTx"
The auth module provides `AnteDecorator`s that are recursively chained together into a single `AnteHandler` in the following order:
if isCheckTx and tx.Fee < config.SubjectiveMinimumFee
fail with "insufficient fee for mempool inclusion"
- `SetUpContextDecorator`: Sets the `GasMeter` in the `Context` and wraps the next `AnteHandler` with a defer clause to recover from any downstream `OutOfGas` panics in the `AnteHandler` chain to return an error with information on gas provided and gas used.
if tx.ValidateBasic() != nil
fail with "tx failed ValidateBasic"
- `RejectExtensionOptionsDecorator`: Rejects all extension options which can optionally be included in protobuf transactions.
if tx.Fee > 0
account = GetAccount(tx.GetSigners()[0])
coins := acount.GetCoins()
if coins < tx.Fee
fail with "insufficient fee to pay for transaction"
account.SetCoins(coins - tx.Fee)
fck.AddCollectedFees(tx.Fee)
- `MempoolFeeDecorator`: Checks if the `tx` fee is above local mempool `minFee` parameter during `CheckTx`.
for index, signature in tx.GetSignatures()
account = GetAccount(tx.GetSigners()[index])
bytesToSign := StdSignBytes(chainID, acc.GetAccountNumber(),
acc.GetSequence(), tx.Fee, tx.Msgs, tx.Memo)
if !signature.Verify(bytesToSign)
fail with "invalid signature"
- `ValidateBasicDecorator`: Calls `tx.ValidateBasic` and returns any non-nil error.
return
```
- `TxTimeoutHeightDecorator`: Check for a `tx` height timeout.
- `ValidateMemoDecorator`: Validates `tx` memo with application parameters and returns any non-nil error.
- `ConsumeGasTxSizeDecorator`: Consumes gas proportional to the `tx` size based on application parameters.
- `DeductFeeDecorator`: Deducts the `FeeAmount` from first signer of the `tx`. If the `x/feegrant` module is enabled and a fee granter is set, it will deduct fees from the fee granter account.
- `SetPubKeyDecorator`: Sets the pubkey from a `tx`'s signers that does not already have its corresponding pubkey saved in the state machine and in the current context.
- `ValidateSigCountDecorator`: Validates the number of signatures in `tx` based on app-parameters.
- `SigGasConsumeDecorator`: Consumes parameter-defined amount of gas for each signature. This requires pubkeys to be set in context for all signers as part of `SetPubKeyDecorator`.
- `SigVerificationDecorator`: Verifies all signatures are valid. This requires pubkeys to be set in context for all signers as part of `SetPubKeyDecorator`.
- `IncrementSequenceDecorator`: Increments the account sequence for each signer to prevent replay attacks.

View File

@ -29,7 +29,7 @@ type AccountKeeperI interface {
// Remove an account from the store.
RemoveAccount(sdk.Context, types.AccountI)
// Iterate over all accounts, calling the provided function. Stop iteraiton when it returns false.
// Iterate over all accounts, calling the provided function. Stop iteration when it returns true.
IterateAccounts(sdk.Context, func(types.AccountI) bool)
// Fetch the public key of an account at a specified address

View File

@ -15,8 +15,8 @@ type Authorization interface {
// which will process and accept or reject a request.
MsgTypeURL() string
// Accept determines whether this grant permits the provided sdk.ServiceMsg to be performed, and if
// so provides an upgraded authorization instance.
// Accept determines whether this grant permits the provided sdk.Msg to be performed,
// and if so provides an upgraded authorization instance.
Accept(ctx sdk.Context, msg sdk.Msg) (AcceptResponse, error)
// ValidateBasic does a simple validation check that

View File

@ -13,4 +13,5 @@ type AccountKeeper interface {
// BankKeeper defines the expected interface needed to retrieve account balances.
type BankKeeper interface {
SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
IsSendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error
}

View File

@ -5,10 +5,11 @@ import (
"fmt"
"time"
"github.com/stretchr/testify/require"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/authz"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/stretchr/testify/require"
)
func (suite *TestSuite) TestGRPCQueryAuthorization() {

View File

@ -32,19 +32,19 @@ func TestDecodeStore(t *testing.T) {
tests := []struct {
name string
expectErr bool
expectedLog string
}{
{"Grant", fmt.Sprintf("%v\n%v", grant, grant)},
{"other", ""},
{"Grant", false, fmt.Sprintf("%v\n%v", grant, grant)},
{"other", true, ""},
}
for i, tt := range tests {
i, tt := i, tt
t.Run(tt.name, func(t *testing.T) {
switch i {
case len(tests) - 1:
if tt.expectErr {
require.Panics(t, func() { dec(kvPairs.Pairs[i], kvPairs.Pairs[i]) }, tt.name)
default:
} else {
require.Equal(t, tt.expectedLog, dec(kvPairs.Pairs[i], kvPairs.Pairs[i]), tt.name)
}
})

View File

@ -1,35 +1,59 @@
package simulation
import (
"encoding/json"
"fmt"
"math/rand"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
"github.com/cosmos/cosmos-sdk/x/authz"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
)
// GenAuthorizationGrant returns an empty slice of authorization grants.
func GenAuthorizationGrant(_ *rand.Rand, _ []simtypes.Account) []authz.GrantAuthorization {
return []authz.GrantAuthorization{}
// genGrant returns a slice of authorization grants.
func genGrant(r *rand.Rand, accounts []simtypes.Account) []authz.GrantAuthorization {
authorizations := make([]authz.GrantAuthorization, len(accounts)-1)
for i := 0; i < len(accounts)-1; i++ {
granter := accounts[i]
grantee := accounts[i+1]
authorizations[i] = authz.GrantAuthorization{
Granter: granter.Address.String(),
Grantee: grantee.Address.String(),
Authorization: generateRandomGrant(r),
}
}
return authorizations
}
func generateRandomGrant(r *rand.Rand) *codectypes.Any {
authorizations := make([]*codectypes.Any, 2)
authorizations[0] = newAnyAuthorization(banktypes.NewSendAuthorization(sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1000)))))
authorizations[1] = newAnyAuthorization(authz.NewGenericAuthorization(sdk.MsgTypeURL(&govtypes.MsgSubmitProposal{})))
return authorizations[r.Intn(len(authorizations))]
}
func newAnyAuthorization(a authz.Authorization) *codectypes.Any {
any, err := codectypes.NewAnyWithValue(a)
if err != nil {
panic(err)
}
return any
}
// RandomizedGenState generates a random GenesisState for authz.
func RandomizedGenState(simState *module.SimulationState) {
var grants []authz.GrantAuthorization
simState.AppParams.GetOrGenerate(
simState.Cdc, "authz", &grants, simState.Rand,
func(r *rand.Rand) { grants = GenAuthorizationGrant(r, simState.Accounts) },
func(r *rand.Rand) { grants = genGrant(r, simState.Accounts) },
)
authzGrantsGenesis := authz.NewGenesisState(grants)
bz, err := json.MarshalIndent(&authzGrantsGenesis, "", " ")
if err != nil {
panic(err)
}
fmt.Printf("Selected randomly generated %s parameters:\n%s\n", authz.ModuleName, bz)
simState.GenState[authz.ModuleName] = simState.Cdc.MustMarshalJSON(authzGrantsGenesis)
}

View File

@ -7,8 +7,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/simapp"
"github.com/cosmos/cosmos-sdk/types/module"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
"github.com/cosmos/cosmos-sdk/x/authz"
@ -16,15 +15,14 @@ import (
)
func TestRandomizedGenState(t *testing.T) {
interfaceRegistry := codectypes.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(interfaceRegistry)
app := simapp.Setup(false)
s := rand.NewSource(1)
r := rand.New(s)
simState := module.SimulationState{
AppParams: make(simtypes.AppParams),
Cdc: cdc,
Cdc: app.AppCodec(),
Rand: r,
NumBonded: 3,
Accounts: simtypes.RandomAccounts(r, 3),
@ -36,5 +34,5 @@ func TestRandomizedGenState(t *testing.T) {
var authzGenesis authz.GenesisState
simState.Cdc.MustUnmarshalJSON(simState.GenState[authz.ModuleName], &authzGenesis)
require.Len(t, authzGenesis.Authorization, 0)
require.Len(t, authzGenesis.Authorization, len(simState.Accounts)-1)
}

View File

@ -1,8 +1,10 @@
package simulation
import (
"fmt"
"math/rand"
"strings"
"github.com/gogo/protobuf/proto"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
@ -12,23 +14,33 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
"github.com/cosmos/cosmos-sdk/x/authz"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/authz/keeper"
banktype "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/cosmos/cosmos-sdk/x/simulation"
)
// authz message types
var (
TypeMsgGrantAuthorization = sdk.MsgTypeURL(&authz.MsgGrant{})
TypeMsgRevokeAuthorization = sdk.MsgTypeURL(&authz.MsgRevoke{})
TypeMsgExecDelegated = sdk.MsgTypeURL(&authz.MsgExec{})
TypeMsgGrant = sdk.MsgTypeURL(&authz.MsgGrant{})
TypeMsgRevoke = sdk.MsgTypeURL(&authz.MsgRevoke{})
TypeMsgExec = sdk.MsgTypeURL(&authz.MsgExec{})
)
// Simulation operation weights constants
const (
OpWeightMsgGrantAuthorization = "op_weight_msg_grant"
OpWeightRevokeAuthorization = "op_weight_msg_revoke"
OpWeightExecAuthorized = "op_weight_msg_execute"
OpWeightMsgGrant = "op_weight_msg_grant"
OpWeightRevoke = "op_weight_msg_revoke"
OpWeightExec = "op_weight_msg_execute"
)
// authz operations weights
const (
WeightGrant = 100
WeightRevoke = 90
WeightExec = 90
)
// WeightedOperations returns all the operations from the module with their respective weights
@ -36,91 +48,91 @@ func WeightedOperations(
appParams simtypes.AppParams, cdc codec.JSONCodec, ak authz.AccountKeeper, bk authz.BankKeeper, k keeper.Keeper, appCdc cdctypes.AnyUnpacker, protoCdc *codec.ProtoCodec) simulation.WeightedOperations {
var (
weightMsgGrantAuthorization int
weightRevokeAuthorization int
weightExecAuthorized int
weightMsgGrant int
weightRevoke int
weightExec int
)
appParams.GetOrGenerate(cdc, OpWeightMsgGrantAuthorization, &weightMsgGrantAuthorization, nil,
appParams.GetOrGenerate(cdc, OpWeightMsgGrant, &weightMsgGrant, nil,
func(_ *rand.Rand) {
weightMsgGrantAuthorization = simappparams.DefaultWeightMsgDelegate
weightMsgGrant = WeightGrant
},
)
appParams.GetOrGenerate(cdc, OpWeightRevokeAuthorization, &weightRevokeAuthorization, nil,
appParams.GetOrGenerate(cdc, OpWeightRevoke, &weightRevoke, nil,
func(_ *rand.Rand) {
weightRevokeAuthorization = simappparams.DefaultWeightMsgUndelegate
weightRevoke = WeightRevoke
},
)
appParams.GetOrGenerate(cdc, OpWeightExecAuthorized, &weightExecAuthorized, nil,
appParams.GetOrGenerate(cdc, OpWeightExec, &weightExec, nil,
func(_ *rand.Rand) {
weightExecAuthorized = simappparams.DefaultWeightMsgSend
weightExec = WeightExec
},
)
return simulation.WeightedOperations{
simulation.NewWeightedOperation(
weightMsgGrantAuthorization,
SimulateMsgGrantAuthorization(ak, bk, k, protoCdc),
weightMsgGrant,
SimulateMsgGrant(ak, bk, k, protoCdc),
),
simulation.NewWeightedOperation(
weightRevokeAuthorization,
SimulateMsgRevokeAuthorization(ak, bk, k, protoCdc),
weightRevoke,
SimulateMsgRevoke(ak, bk, k, protoCdc),
),
simulation.NewWeightedOperation(
weightExecAuthorized,
SimulateMsgExecuteAuthorized(ak, bk, k, appCdc, protoCdc),
weightExec,
SimulateMsgExec(ak, bk, k, appCdc, protoCdc),
),
}
}
// SimulateMsgGrantAuthorization generates a MsgGrantAuthorization with random values.
// nolint: funlen
func SimulateMsgGrantAuthorization(ak authz.AccountKeeper, bk authz.BankKeeper, _ keeper.Keeper,
// SimulateMsgGrant generates a MsgGrant with random values.
func SimulateMsgGrant(ak authz.AccountKeeper, bk authz.BankKeeper, _ keeper.Keeper,
protoCdc *codec.ProtoCodec) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
granter := accs[0]
grantee := accs[1]
granter, _ := simtypes.RandomAcc(r, accs)
grantee, _ := simtypes.RandomAcc(r, accs)
account := ak.GetAccount(ctx, granter.Address)
if granter.Address.Equals(grantee.Address) {
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgGrant, "granter and grantee are same"), nil, nil
}
spendableCoins := bk.SpendableCoins(ctx, account.GetAddress())
granterAcc := ak.GetAccount(ctx, granter.Address)
spendableCoins := bk.SpendableCoins(ctx, granter.Address)
fees, err := simtypes.RandomFees(r, ctx, spendableCoins)
if err != nil {
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgGrantAuthorization, err.Error()), nil, err
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgGrant, err.Error()), nil, err
}
blockTime := ctx.BlockTime()
spendLimit := spendableCoins.Sub(fees)
if spendLimit == nil {
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgGrantAuthorization, "spend limit is nil"), nil, nil
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgGrant, "spend limit is nil"), nil, nil
}
msg, err := authz.NewMsgGrant(granter.Address, grantee.Address,
banktype.NewSendAuthorization(spendLimit), blockTime.AddDate(1, 0, 0))
expiration := ctx.BlockTime().AddDate(1, 0, 0)
msg, err := authz.NewMsgGrant(granter.Address, grantee.Address, generateRandomAuthorization(r, spendLimit), expiration)
if err != nil {
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgGrantAuthorization, err.Error()), nil, err
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgGrant, err.Error()), nil, err
}
txGen := simappparams.MakeTestEncodingConfig().TxConfig
txCfg := simappparams.MakeTestEncodingConfig().TxConfig
tx, err := helpers.GenTx(
txGen,
txCfg,
[]sdk.Msg{msg},
fees,
helpers.DefaultGenTxGas,
chainID,
[]uint64{account.GetAccountNumber()},
[]uint64{account.GetSequence()},
[]uint64{granterAcc.GetAccountNumber()},
[]uint64{granterAcc.GetSequence()},
granter.PrivKey,
)
if err != nil {
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgGrantAuthorization, "unable to generate mock tx"), nil, err
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgGrant, "unable to generate mock tx"), nil, err
}
_, _, err = app.Deliver(txGen.TxEncoder(), tx)
_, _, err = app.Deliver(txCfg.TxEncoder(), tx)
if err != nil {
return simtypes.NoOpMsg(authz.ModuleName, sdk.MsgTypeURL(msg), "unable to deliver tx"), nil, err
}
@ -128,18 +140,25 @@ func SimulateMsgGrantAuthorization(ak authz.AccountKeeper, bk authz.BankKeeper,
}
}
// SimulateMsgRevokeAuthorization generates a MsgRevokeAuthorization with random values.
// nolint: funlen
func SimulateMsgRevokeAuthorization(ak authz.AccountKeeper, bk authz.BankKeeper, k keeper.Keeper, protoCdc *codec.ProtoCodec) simtypes.Operation {
func generateRandomAuthorization(r *rand.Rand, spendLimit sdk.Coins) authz.Authorization {
authorizations := make([]authz.Authorization, 2)
authorizations[0] = banktype.NewSendAuthorization(spendLimit)
authorizations[1] = authz.NewGenericAuthorization(sdk.MsgTypeURL(&banktype.MsgSend{}))
return authorizations[r.Intn(len(authorizations))]
}
// SimulateMsgRevoke generates a MsgRevoke with random values.
func SimulateMsgRevoke(ak authz.AccountKeeper, bk authz.BankKeeper, k keeper.Keeper, protoCdc *codec.ProtoCodec) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
var granterAddr, granteeAddr sdk.AccAddress
var grant authz.Grant
hasGrant := false
var targetGrant authz.Grant
var granterAddr sdk.AccAddress
var granteeAddr sdk.AccAddress
k.IterateGrants(ctx, func(granter, grantee sdk.AccAddress, grant authz.Grant) bool {
targetGrant = grant
k.IterateGrants(ctx, func(granter, grantee sdk.AccAddress, g authz.Grant) bool {
grant = g
granterAddr = granter
granteeAddr = grantee
hasGrant = true
@ -147,51 +166,52 @@ func SimulateMsgRevokeAuthorization(ak authz.AccountKeeper, bk authz.BankKeeper,
})
if !hasGrant {
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevokeAuthorization, "no grants"), nil, nil
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "no grants"), nil, nil
}
granter, ok := simtypes.FindAccount(accs, granterAddr)
granterAcc, ok := simtypes.FindAccount(accs, granterAddr)
if !ok {
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevokeAuthorization, "Account not found"), nil, nil
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "account not found"), nil, sdkerrors.Wrapf(sdkerrors.ErrNotFound, "account not found")
}
account := ak.GetAccount(ctx, granter.Address)
spendableCoins := bk.SpendableCoins(ctx, account.GetAddress())
spendableCoins := bk.SpendableCoins(ctx, granterAddr)
fees, err := simtypes.RandomFees(r, ctx, spendableCoins)
if err != nil {
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevokeAuthorization, "fee error"), nil, err
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "fee error"), nil, err
}
auth := targetGrant.GetAuthorization()
msg := authz.NewMsgRevoke(granterAddr, granteeAddr, auth.MsgTypeURL())
txGen := simappparams.MakeTestEncodingConfig().TxConfig
a := grant.GetAuthorization()
msg := authz.NewMsgRevoke(granterAddr, granteeAddr, a.MsgTypeURL())
txCfg := simappparams.MakeTestEncodingConfig().TxConfig
account := ak.GetAccount(ctx, granterAddr)
tx, err := helpers.GenTx(
txGen,
txCfg,
[]sdk.Msg{&msg},
fees,
helpers.DefaultGenTxGas,
chainID,
[]uint64{account.GetAccountNumber()},
[]uint64{account.GetSequence()},
granter.PrivKey,
granterAcc.PrivKey,
)
if err != nil {
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevokeAuthorization, err.Error()), nil, err
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, err.Error()), nil, err
}
_, _, err = app.Deliver(txGen.TxEncoder(), tx)
return simtypes.NewOperationMsg(&msg, true, "", protoCdc), nil, err
_, _, err = app.Deliver(txCfg.TxEncoder(), tx)
if err != nil {
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "unable to deliver tx"), nil, err
}
return simtypes.NewOperationMsg(&msg, true, "", protoCdc), nil, nil
}
}
// SimulateMsgExecuteAuthorized generates a MsgExecuteAuthorized with random values.
// nolint: funlen
func SimulateMsgExecuteAuthorized(ak authz.AccountKeeper, bk authz.BankKeeper, k keeper.Keeper, cdc cdctypes.AnyUnpacker, protoCdc *codec.ProtoCodec) simtypes.Operation {
// SimulateMsgExec generates a MsgExec with random values.
func SimulateMsgExec(ak authz.AccountKeeper, bk authz.BankKeeper, k keeper.Keeper, cdc cdctypes.AnyUnpacker, protoCdc *codec.ProtoCodec) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
hasGrant := false
var targetGrant authz.Grant
var granterAddr sdk.AccAddress
@ -205,67 +225,73 @@ func SimulateMsgExecuteAuthorized(ak authz.AccountKeeper, bk authz.BankKeeper, k
})
if !hasGrant {
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExecDelegated, "Not found"), nil, nil
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "no grant found"), nil, nil
}
grantee, _ := simtypes.FindAccount(accs, granteeAddr)
granterAccount := ak.GetAccount(ctx, granterAddr)
granteeAccount := ak.GetAccount(ctx, granteeAddr)
grantee, ok := simtypes.FindAccount(accs, granteeAddr)
if !ok {
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "Account not found"), nil, sdkerrors.Wrapf(sdkerrors.ErrNotFound, "grantee account not found")
}
granterspendableCoins := bk.SpendableCoins(ctx, granterAccount.GetAddress())
if granterspendableCoins.Empty() {
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExecDelegated, "no coins"), nil, nil
if _, ok := simtypes.FindAccount(accs, granterAddr); !ok {
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "Account not found"), nil, sdkerrors.Wrapf(sdkerrors.ErrNotFound, "granter account not found")
}
if targetGrant.Expiration.Before(ctx.BlockHeader().Time) {
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExecDelegated, "grant expired"), nil, nil
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "grant expired"), nil, nil
}
granteespendableCoins := bk.SpendableCoins(ctx, granteeAccount.GetAddress())
fees, err := simtypes.RandomFees(r, ctx, granteespendableCoins)
coins := sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(int64(simtypes.RandIntBetween(r, 100, 1000000)))))
// Check send_enabled status of each sent coin denom
if err := bk.IsSendEnabledCoins(ctx, coins...); err != nil {
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, err.Error()), nil, nil
}
if targetGrant.Authorization.TypeUrl == fmt.Sprintf("/%s", proto.MessageName(&banktype.SendAuthorization{})) {
sendAuthorization := targetGrant.GetAuthorization().(*banktype.SendAuthorization)
if sendAuthorization.SpendLimit.IsAllLT(coins) {
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "over spend limit"), nil, nil
}
}
granterspendableCoins := bk.SpendableCoins(ctx, granterAddr)
if granterspendableCoins.IsAllLTE(coins) {
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "insufficient funds"), nil, nil
}
granteeSpendableCoins := bk.SpendableCoins(ctx, granteeAddr)
fees, err := simtypes.RandomFees(r, ctx, granteeSpendableCoins)
if err != nil {
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExecDelegated, "fee error"), nil, err
}
sendCoins := sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(10)))
execMsg := banktype.NewMsgSend(
granterAddr,
granteeAddr,
sendCoins,
)
msg := authz.NewMsgExec(grantee.Address, []sdk.Msg{execMsg})
sendGrant := targetGrant.Authorization.GetCachedValue().(*banktype.SendAuthorization)
_, err = sendGrant.Accept(ctx, execMsg)
if err != nil {
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExecDelegated, err.Error()), nil, nil
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "fee error"), nil, err
}
txGen := simappparams.MakeTestEncodingConfig().TxConfig
msg := authz.NewMsgExec(granteeAddr, []sdk.Msg{banktype.NewMsgSend(granterAddr, granteeAddr, coins)})
txCfg := simappparams.MakeTestEncodingConfig().TxConfig
granteeAcc := ak.GetAccount(ctx, granteeAddr)
tx, err := helpers.GenTx(
txGen,
txCfg,
[]sdk.Msg{&msg},
fees,
helpers.DefaultGenTxGas,
chainID,
[]uint64{granteeAccount.GetAccountNumber()},
[]uint64{granteeAccount.GetSequence()},
[]uint64{granteeAcc.GetAccountNumber()},
[]uint64{granteeAcc.GetSequence()},
grantee.PrivKey,
)
if err != nil {
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, err.Error()), nil, err
}
_, _, err = app.Deliver(txCfg.TxEncoder(), tx)
if err != nil {
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExecDelegated, err.Error()), nil, err
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, err.Error()), nil, err
}
_, _, err = app.Deliver(txGen.TxEncoder(), tx)
err = msg.UnpackInterfaces(cdc)
if err != nil {
if strings.Contains(err.Error(), "insufficient fee") {
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExecDelegated, "insufficient fee"), nil, nil
}
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExecDelegated, err.Error()), nil, err
}
msg.UnpackInterfaces(cdc)
if err != nil {
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExecDelegated, "unmarshal error"), nil, err
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "unmarshal error"), nil, err
}
return simtypes.NewOperationMsg(&msg, true, "success", protoCdc), nil, nil
}

View File

@ -12,7 +12,6 @@ import (
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/simapp"
simappparams "github.com/cosmos/cosmos-sdk/simapp/params"
sdk "github.com/cosmos/cosmos-sdk/types"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
"github.com/cosmos/cosmos-sdk/x/authz"
@ -54,9 +53,9 @@ func (suite *SimTestSuite) TestWeightedOperations() {
opMsgRoute string
opMsgName string
}{
{simappparams.DefaultWeightMsgDelegate, authz.ModuleName, simulation.TypeMsgGrantAuthorization},
{simappparams.DefaultWeightMsgUndelegate, authz.ModuleName, simulation.TypeMsgRevokeAuthorization},
{simappparams.DefaultWeightMsgSend, authz.ModuleName, simulation.TypeMsgExecDelegated},
{simulation.WeightGrant, authz.ModuleName, simulation.TypeMsgGrant},
{simulation.WeightRevoke, authz.ModuleName, simulation.TypeMsgRevoke},
{simulation.WeightExec, authz.ModuleName, simulation.TypeMsgExec},
}
for i, w := range weightesOps {
@ -74,7 +73,7 @@ func (suite *SimTestSuite) getTestingAccounts(r *rand.Rand, n int) []simtypes.Ac
accounts := simtypes.RandomAccounts(r, n)
initAmt := suite.app.StakingKeeper.TokensFromConsensusPower(suite.ctx, 200000)
initCoins := sdk.NewCoins(sdk.NewCoin("foo", initAmt))
initCoins := sdk.NewCoins(sdk.NewCoin("stake", initAmt))
// add coins to the accounts
for _, account := range accounts {
@ -86,8 +85,7 @@ func (suite *SimTestSuite) getTestingAccounts(r *rand.Rand, n int) []simtypes.Ac
return accounts
}
func (suite *SimTestSuite) TestSimulateGrantAuthorization() {
// setup 3 accounts
func (suite *SimTestSuite) TestSimulateGrant() {
s := rand.NewSource(1)
r := rand.New(s)
accounts := suite.getTestingAccounts(r, 2)
@ -106,7 +104,7 @@ func (suite *SimTestSuite) TestSimulateGrantAuthorization() {
grantee := accounts[1]
// execute operation
op := simulation.SimulateMsgGrantAuthorization(suite.app.AccountKeeper, suite.app.BankKeeper, suite.app.AuthzKeeper, suite.protoCdc)
op := simulation.SimulateMsgGrant(suite.app.AccountKeeper, suite.app.BankKeeper, suite.app.AuthzKeeper, suite.protoCdc)
operationMsg, futureOperations, err := op(r, suite.app.BaseApp, ctx, accounts, "")
suite.Require().NoError(err)
@ -119,9 +117,9 @@ func (suite *SimTestSuite) TestSimulateGrantAuthorization() {
}
func (suite *SimTestSuite) TestSimulateRevokeAuthorization() {
func (suite *SimTestSuite) TestSimulateRevoke() {
// setup 3 accounts
s := rand.NewSource(1)
s := rand.NewSource(2)
r := rand.New(s)
accounts := suite.getTestingAccounts(r, 3)
@ -133,7 +131,7 @@ func (suite *SimTestSuite) TestSimulateRevokeAuthorization() {
}})
initAmt := suite.app.StakingKeeper.TokensFromConsensusPower(suite.ctx, 200000)
initCoins := sdk.NewCoins(sdk.NewCoin("foo", initAmt))
initCoins := sdk.NewCoins(sdk.NewCoin("stake", initAmt))
granter := accounts[0]
grantee := accounts[1]
@ -143,7 +141,7 @@ func (suite *SimTestSuite) TestSimulateRevokeAuthorization() {
suite.Require().NoError(err)
// execute operation
op := simulation.SimulateMsgRevokeAuthorization(suite.app.AccountKeeper, suite.app.BankKeeper, suite.app.AuthzKeeper, suite.protoCdc)
op := simulation.SimulateMsgRevoke(suite.app.AccountKeeper, suite.app.BankKeeper, suite.app.AuthzKeeper, suite.protoCdc)
operationMsg, futureOperations, err := op(r, suite.app.BaseApp, suite.ctx, accounts, "")
suite.Require().NoError(err)
@ -158,7 +156,7 @@ func (suite *SimTestSuite) TestSimulateRevokeAuthorization() {
}
func (suite *SimTestSuite) TestSimulateExecAuthorization() {
func (suite *SimTestSuite) TestSimulateExec() {
// setup 3 accounts
s := rand.NewSource(1)
r := rand.New(s)
@ -168,7 +166,7 @@ func (suite *SimTestSuite) TestSimulateExecAuthorization() {
suite.app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: suite.app.LastBlockHeight() + 1, AppHash: suite.app.LastCommitID().Hash}})
initAmt := suite.app.StakingKeeper.TokensFromConsensusPower(suite.ctx, 200000)
initCoins := sdk.NewCoins(sdk.NewCoin("foo", initAmt))
initCoins := sdk.NewCoins(sdk.NewCoin("stake", initAmt))
granter := accounts[0]
grantee := accounts[1]
@ -178,7 +176,7 @@ func (suite *SimTestSuite) TestSimulateExecAuthorization() {
suite.Require().NoError(err)
// execute operation
op := simulation.SimulateMsgExecuteAuthorized(suite.app.AccountKeeper, suite.app.BankKeeper, suite.app.AuthzKeeper, suite.app.AppCodec(), suite.protoCdc)
op := simulation.SimulateMsgExec(suite.app.AccountKeeper, suite.app.BankKeeper, suite.app.AuthzKeeper, suite.app.AppCodec(), suite.protoCdc)
operationMsg, futureOperations, err := op(r, suite.app.BaseApp, suite.ctx, accounts, "")
suite.Require().NoError(err)

View File

@ -1,3 +1,6 @@
// Package v036 is used for legacy migration scripts. Actual migration scripts
// for v036 have been removed, but the v039->v042 migration script still
// references types from this file, so we're keeping it for now.
// DONTCOVER
package v036
@ -12,9 +15,3 @@ type (
Supply sdk.Coins `json:"supply" yaml:"supply"`
}
)
func EmptyGenesisState() GenesisState {
return GenesisState{
Supply: sdk.NewCoins(), // leave this empty as it's filled on initialization
}
}

View File

@ -1,3 +1,6 @@
// Package v038 is used for legacy migration scripts. Actual migration scripts
// for v038 have been removed, but the v039->v042 migration script still
// references types from this file, so we're keeping it for now.
package v038
// DONTCOVER

View File

@ -12,6 +12,7 @@ import (
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
"github.com/cosmos/cosmos-sdk/x/bank/keeper"
"github.com/cosmos/cosmos-sdk/x/bank/types"
distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
"github.com/cosmos/cosmos-sdk/x/simulation"
)
@ -58,7 +59,7 @@ func SimulateMsgSend(ak types.AccountKeeper, bk keeper.Keeper) simtypes.Operatio
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
accs []simtypes.Account, chainID string,
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
simAccount, toSimAcc, coins, skip := randomSendFields(r, ctx, accs, bk, ak)
from, to, coins, skip := randomSendFields(r, ctx, accs, bk, ak)
// Check send_enabled status of each coin denom
if err := bk.IsSendEnabledCoins(ctx, coins...); err != nil {
@ -69,9 +70,39 @@ func SimulateMsgSend(ak types.AccountKeeper, bk keeper.Keeper) simtypes.Operatio
return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgSend, "skip all transfers"), nil, nil
}
msg := types.NewMsgSend(simAccount.Address, toSimAcc.Address, coins)
msg := types.NewMsgSend(from.Address, to.Address, coins)
err := sendMsgSend(r, app, bk, ak, msg, ctx, chainID, []cryptotypes.PrivKey{simAccount.PrivKey})
err := sendMsgSend(r, app, bk, ak, msg, ctx, chainID, []cryptotypes.PrivKey{from.PrivKey})
if err != nil {
return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "invalid transfers"), nil, err
}
return simtypes.NewOperationMsg(msg, true, "", nil), nil, nil
}
}
// SimulateMsgSendToModuleAccount tests and runs a single msg send where both
// accounts already exist.
func SimulateMsgSendToModuleAccount(ak types.AccountKeeper, bk keeper.Keeper, moduleAccCount int) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
accs []simtypes.Account, chainID string,
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
from := accs[0]
to := getModuleAccounts(ak, ctx, moduleAccCount)[0]
spendable := bk.SpendableCoins(ctx, from.Address)
coins := simtypes.RandSubsetCoins(r, spendable)
// Check send_enabled status of each coin denom
if err := bk.IsSendEnabledCoins(ctx, coins...); err != nil {
return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgSend, err.Error()), nil, nil
}
msg := types.NewMsgSend(from.Address, to.Address, coins)
err := sendMsgSend(r, app, bk, ak, msg, ctx, chainID, []cryptotypes.PrivKey{from.PrivKey})
if err != nil {
return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "invalid transfers"), nil, err
}
@ -150,11 +181,11 @@ func SimulateMsgMultiSend(ak types.AccountKeeper, bk keeper.Keeper) simtypes.Ope
var totalSentCoins sdk.Coins
for i := range inputs {
// generate random input fields, ignore to address
simAccount, _, coins, skip := randomSendFields(r, ctx, accs, bk, ak)
from, _, coins, skip := randomSendFields(r, ctx, accs, bk, ak)
// make sure account is fresh and not used in previous input
for usedAddrs[simAccount.Address.String()] {
simAccount, _, coins, skip = randomSendFields(r, ctx, accs, bk, ak)
for usedAddrs[from.Address.String()] {
from, _, coins, skip = randomSendFields(r, ctx, accs, bk, ak)
}
if skip {
@ -162,13 +193,13 @@ func SimulateMsgMultiSend(ak types.AccountKeeper, bk keeper.Keeper) simtypes.Ope
}
// set input address in used address map
usedAddrs[simAccount.Address.String()] = true
usedAddrs[from.Address.String()] = true
// set signer privkey
privs[i] = simAccount.PrivKey
privs[i] = from.PrivKey
// set next input and accumulate total sent coins
inputs[i] = types.NewInput(simAccount.Address, coins)
inputs[i] = types.NewInput(from.Address, coins)
totalSentCoins = totalSentCoins.Add(coins...)
}
@ -195,8 +226,8 @@ func SimulateMsgMultiSend(ak types.AccountKeeper, bk keeper.Keeper) simtypes.Ope
}
// remove any output that has no coins
i := 0
for i < len(outputs) {
for i := 0; i < len(outputs); {
if outputs[i].Coins.Empty() {
outputs[i] = outputs[len(outputs)-1]
outputs = outputs[:len(outputs)-1]
@ -210,7 +241,73 @@ func SimulateMsgMultiSend(ak types.AccountKeeper, bk keeper.Keeper) simtypes.Ope
Inputs: inputs,
Outputs: outputs,
}
err := sendMsgMultiSend(r, app, bk, ak, msg, ctx, chainID, privs)
if err != nil {
return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "invalid transfers"), nil, err
}
return simtypes.NewOperationMsg(msg, true, "", nil), nil, nil
}
}
// SimulateMsgMultiSendToModuleAccount sends coins to Module Accounts
func SimulateMsgMultiSendToModuleAccount(ak types.AccountKeeper, bk keeper.Keeper, moduleAccCount int) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
accs []simtypes.Account, chainID string,
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
inputs := make([]types.Input, 2)
outputs := make([]types.Output, moduleAccCount)
// collect signer privKeys
privs := make([]cryptotypes.PrivKey, len(inputs))
var totalSentCoins sdk.Coins
for i := range inputs {
sender := accs[i]
privs[i] = sender.PrivKey
spendable := bk.SpendableCoins(ctx, sender.Address)
coins := simtypes.RandSubsetCoins(r, spendable)
inputs[i] = types.NewInput(sender.Address, coins)
totalSentCoins = totalSentCoins.Add(coins...)
}
if err := bk.IsSendEnabledCoins(ctx, totalSentCoins...); err != nil {
return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgMultiSend, err.Error()), nil, nil
}
moduleAccounts := getModuleAccounts(ak, ctx, moduleAccCount)
for i := range outputs {
var outCoins sdk.Coins
// split total sent coins into random subsets for output
if i == len(outputs)-1 {
outCoins = totalSentCoins
} else {
// take random subset of remaining coins for output
// and update remaining coins
outCoins = simtypes.RandSubsetCoins(r, totalSentCoins)
totalSentCoins = totalSentCoins.Sub(outCoins)
}
outputs[i] = types.NewOutput(moduleAccounts[i].Address, outCoins)
}
// remove any output that has no coins
for i := 0; i < len(outputs); {
if outputs[i].Coins.Empty() {
outputs[i] = outputs[len(outputs)-1]
outputs = outputs[:len(outputs)-1]
} else {
// continue onto next coin
i++
}
}
msg := &types.MsgMultiSend{
Inputs: inputs,
Outputs: outputs,
}
err := sendMsgMultiSend(r, app, bk, ak, msg, ctx, chainID, privs)
if err != nil {
return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "invalid transfers"), nil, err
@ -291,25 +388,44 @@ func randomSendFields(
r *rand.Rand, ctx sdk.Context, accs []simtypes.Account, bk keeper.Keeper, ak types.AccountKeeper,
) (simtypes.Account, simtypes.Account, sdk.Coins, bool) {
simAccount, _ := simtypes.RandomAcc(r, accs)
toSimAcc, _ := simtypes.RandomAcc(r, accs)
from, _ := simtypes.RandomAcc(r, accs)
to, _ := simtypes.RandomAcc(r, accs)
// disallow sending money to yourself
for simAccount.PubKey.Equals(toSimAcc.PubKey) {
toSimAcc, _ = simtypes.RandomAcc(r, accs)
for from.PubKey.Equals(to.PubKey) {
to, _ = simtypes.RandomAcc(r, accs)
}
acc := ak.GetAccount(ctx, simAccount.Address)
acc := ak.GetAccount(ctx, from.Address)
if acc == nil {
return simAccount, toSimAcc, nil, true
return from, to, nil, true
}
spendable := bk.SpendableCoins(ctx, acc.GetAddress())
sendCoins := simtypes.RandSubsetCoins(r, spendable)
if sendCoins.Empty() {
return simAccount, toSimAcc, nil, true
return from, to, nil, true
}
return simAccount, toSimAcc, sendCoins, false
return from, to, sendCoins, false
}
func getModuleAccounts(ak types.AccountKeeper, ctx sdk.Context, moduleAccCount int) []simtypes.Account {
moduleAccounts := make([]simtypes.Account, moduleAccCount)
for i := 0; i < moduleAccCount; i++ {
addr := ak.GetModuleAddress(distributiontypes.ModuleName)
acc := ak.GetAccount(ctx, addr)
mAcc := simtypes.Account{
Address: acc.GetAddress(),
PrivKey: nil,
ConsKey: nil,
PubKey: acc.GetPubKey(),
}
moduleAccounts[i] = mAcc
}
return moduleAccounts
}

View File

@ -104,18 +104,80 @@ func (suite *SimTestSuite) TestSimulateMsgMultiSend() {
// execute operation
op := simulation.SimulateMsgMultiSend(suite.app.AccountKeeper, suite.app.BankKeeper)
operationMsg, futureOperations, err := op(r, suite.app.BaseApp, suite.ctx, accounts, "")
suite.Require().NoError(err)
require := suite.Require()
require.NoError(err)
var msg types.MsgMultiSend
types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg)
suite.Require().True(operationMsg.OK)
suite.Require().Len(msg.Inputs, 3)
suite.Require().Equal("cosmos1p8wcgrjr4pjju90xg6u9cgq55dxwq8j7u4x9a0", msg.Inputs[1].Address)
suite.Require().Equal("185121068stake", msg.Inputs[1].Coins.String())
suite.Require().Len(msg.Outputs, 2)
suite.Require().Equal("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", msg.Outputs[1].Address)
suite.Require().Equal("260469617stake", msg.Outputs[1].Coins.String())
require.True(operationMsg.OK)
require.Len(msg.Inputs, 3)
require.Equal("cosmos1p8wcgrjr4pjju90xg6u9cgq55dxwq8j7u4x9a0", msg.Inputs[1].Address)
require.Equal("185121068stake", msg.Inputs[1].Coins.String())
require.Len(msg.Outputs, 2)
require.Equal("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", msg.Outputs[1].Address)
require.Equal("260469617stake", msg.Outputs[1].Coins.String())
require.Equal(types.TypeMsgMultiSend, msg.Type())
require.Equal(types.ModuleName, msg.Route())
require.Len(futureOperations, 0)
}
func (suite *SimTestSuite) TestSimulateModuleAccountMsgSend() {
const (
accCount = 1
moduleAccCount = 1
)
s := rand.NewSource(1)
r := rand.New(s)
accounts := suite.getTestingAccounts(r, accCount)
// begin a new block
suite.app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: suite.app.LastBlockHeight() + 1, AppHash: suite.app.LastCommitID().Hash}})
// execute operation
op := simulation.SimulateMsgSendToModuleAccount(suite.app.AccountKeeper, suite.app.BankKeeper, moduleAccCount)
s = rand.NewSource(1)
r = rand.New(s)
operationMsg, futureOperations, err := op(r, suite.app.BaseApp, suite.ctx, accounts, "")
suite.Require().Error(err)
var msg types.MsgSend
types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg)
suite.Require().False(operationMsg.OK)
suite.Require().Equal(operationMsg.Comment, "invalid transfers")
suite.Require().Equal(types.TypeMsgSend, msg.Type())
suite.Require().Equal(types.ModuleName, msg.Route())
suite.Require().Len(futureOperations, 0)
}
func (suite *SimTestSuite) TestSimulateMsgMultiSendToModuleAccount() {
const (
accCount = 2
mAccCount = 2
)
s := rand.NewSource(1)
r := rand.New(s)
accounts := suite.getTestingAccounts(r, accCount)
// begin a new block
suite.app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: suite.app.LastBlockHeight() + 1, AppHash: suite.app.LastCommitID().Hash}})
// execute operation
op := simulation.SimulateMsgMultiSendToModuleAccount(suite.app.AccountKeeper, suite.app.BankKeeper, mAccCount)
operationMsg, futureOperations, err := op(r, suite.app.BaseApp, suite.ctx, accounts, "")
suite.Require().Error(err)
var msg types.MsgMultiSend
types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg)
suite.Require().False(operationMsg.OK) // sending tokens to a module account should fail
suite.Require().Equal(operationMsg.Comment, "invalid transfers")
suite.Require().Equal(types.TypeMsgMultiSend, msg.Type())
suite.Require().Equal(types.ModuleName, msg.Route())
suite.Require().Len(futureOperations, 0)

View File

@ -1,3 +1,6 @@
// Package v034 is used for legacy migration scripts. Actual migration scripts
// for v034 have been removed, but the v039->v042 migration script still
// references types from this file, so we're keeping it for now.
// DONTCOVER
package v034

View File

@ -1,30 +0,0 @@
package v036
import (
v034distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v034"
)
// Migrate accepts exported genesis state from v0.34 and migrates it to v0.36
// genesis state. All entries are identical except for validator slashing events
// which now include the period.
func Migrate(oldGenState v034distr.GenesisState) GenesisState {
// migrate slash events which now have the period included
slashEvents := make([]ValidatorSlashEventRecord, len(oldGenState.ValidatorSlashEvents))
for i, se := range oldGenState.ValidatorSlashEvents {
slashEvents[i] = ValidatorSlashEventRecord{
ValidatorAddress: se.ValidatorAddress,
Height: se.Height,
Period: se.Event.ValidatorPeriod,
Event: se.Event,
}
}
return NewGenesisState(
oldGenState.FeePool, oldGenState.CommunityTax, oldGenState.BaseProposerReward,
oldGenState.BonusProposerReward, oldGenState.WithdrawAddrEnabled,
oldGenState.DelegatorWithdrawInfos, oldGenState.PreviousProposer,
oldGenState.OutstandingRewards, oldGenState.ValidatorAccumulatedCommissions,
oldGenState.ValidatorHistoricalRewards, oldGenState.ValidatorCurrentRewards,
oldGenState.DelegatorStartingInfos, slashEvents,
)
}

View File

@ -1,64 +0,0 @@
package v036
import (
"testing"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
"github.com/cosmos/cosmos-sdk/types"
v034distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v034"
"github.com/stretchr/testify/require"
)
var (
priv = secp256k1.GenPrivKey()
addr = types.AccAddress(priv.PubKey().Address())
valAddr, _ = types.ValAddressFromBech32(addr.String())
event = v034distr.ValidatorSlashEvent{
ValidatorPeriod: 1,
Fraction: types.Dec{},
}
)
func TestMigrate(t *testing.T) {
var genesisState GenesisState
require.NotPanics(t, func() {
genesisState = Migrate(v034distr.GenesisState{
ValidatorSlashEvents: []v034distr.ValidatorSlashEventRecord{
{
ValidatorAddress: valAddr,
Height: 1,
Event: event,
},
},
})
})
require.Equal(t, genesisState.ValidatorSlashEvents[0], ValidatorSlashEventRecord{
ValidatorAddress: valAddr,
Height: 1,
Period: event.ValidatorPeriod,
Event: event,
})
}
func TestMigrateEmptyRecord(t *testing.T) {
var genesisState GenesisState
require.NotPanics(t, func() {
genesisState = Migrate(v034distr.GenesisState{
ValidatorSlashEvents: []v034distr.ValidatorSlashEventRecord{{}},
})
})
require.Equal(t, genesisState.ValidatorSlashEvents[0], ValidatorSlashEventRecord{
ValidatorAddress: valAddr,
Height: 0,
Period: 0,
Event: v034distr.ValidatorSlashEvent{
ValidatorPeriod: 0,
Fraction: types.Dec{},
},
})
}

View File

@ -1,3 +1,6 @@
// Package v036 is used for legacy migration scripts. Actual migration scripts
// for v036 have been removed, but the v039->v042 migration script still
// references types from this file, so we're keeping it for now.
// DONTCOVER
package v036

View File

@ -1,26 +0,0 @@
package v038
// DONTCOVER
import (
v036distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v036"
)
// Migrate accepts exported genesis state from v0.36 or v0.37 and migrates it to
// v0.38 genesis state. All entries are identical except for parameters.
func Migrate(oldGenState v036distr.GenesisState) GenesisState {
params := Params{
CommunityTax: oldGenState.CommunityTax,
BaseProposerReward: oldGenState.BaseProposerReward,
BonusProposerReward: oldGenState.BonusProposerReward,
WithdrawAddrEnabled: oldGenState.WithdrawAddrEnabled,
}
return NewGenesisState(
params, oldGenState.FeePool,
oldGenState.DelegatorWithdrawInfos, oldGenState.PreviousProposer,
oldGenState.OutstandingRewards, oldGenState.ValidatorAccumulatedCommissions,
oldGenState.ValidatorHistoricalRewards, oldGenState.ValidatorCurrentRewards,
oldGenState.DelegatorStartingInfos, oldGenState.ValidatorSlashEvents,
)
}

View File

@ -1,3 +1,6 @@
// Package v038 is used for legacy migration scripts. Actual migration scripts
// for v038 have been removed, but the v039->v042 migration script still
// references types from this file, so we're keeping it for now.
package v038
import (

View File

@ -1,3 +1,6 @@
// Package v038 is used for legacy migration scripts. Actual migration scripts
// for v038 have been removed, but the v039->v042 migration script still
// references types from this file, so we're keeping it for now.
package v038
import (

View File

@ -1,10 +1,11 @@
package feegrant
import (
"github.com/gogo/protobuf/proto"
"github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/gogo/protobuf/proto"
)
// TODO: Revisit this once we have propoer gas fee framework.

View File

@ -4,11 +4,12 @@ import (
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/feegrant"
"github.com/stretchr/testify/require"
)
func TestMsgGrantAllowance(t *testing.T) {

View File

@ -1,15 +0,0 @@
/*
Package genaccounts is now deprecated.
IMPORTANT: This module has been replaced by ADR 011: Generalize Module Accounts.
The ADR can be found here: https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-011-generalize-genesis-accounts.md.
Genesis accounts that existed in the genesis application state under `app_state.accounts`
now exists under the x/auth module's genesis state under the `app_state.auth.accounts` key.
Migration can be performed via x/auth/legacy/v038/migrate.go. In addition, because genesis
accounts are now generalized via an interface, it is now up to the application to
define the concrete types and the respective client logic to add them to a genesis
state/file. For an example implementation of the `add-genesis-account` command please
refer to https://github.com/cosmos/gaia/pull/122.
*/
package genaccounts

View File

@ -1,27 +0,0 @@
// DONTCOVER
package v034
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
const (
ModuleName = "accounts"
)
type (
GenesisAccount struct {
Address sdk.AccAddress `json:"address"`
Coins sdk.Coins `json:"coins"`
Sequence uint64 `json:"sequence_number"`
AccountNumber uint64 `json:"account_number"`
OriginalVesting sdk.Coins `json:"original_vesting"`
DelegatedFree sdk.Coins `json:"delegated_free"`
DelegatedVesting sdk.Coins `json:"delegated_vesting"`
StartTime int64 `json:"start_time"`
EndTime int64 `json:"end_time"`
}
GenesisState []GenesisAccount
)

View File

@ -1,179 +0,0 @@
// DONTCOVER
// nolint
package v036
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
v034distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v034"
v034accounts "github.com/cosmos/cosmos-sdk/x/genaccounts/legacy/v034"
v034gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v034"
v034staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v034"
"github.com/tendermint/tendermint/crypto"
)
const (
notBondedPoolName = "not_bonded_tokens_pool"
bondedPoolName = "bonded_tokens_pool"
feeCollectorName = "fee_collector"
mintModuleName = "mint"
basic = "basic"
minter = "minter"
burner = "burner"
staking = "staking"
)
// Migrate accepts exported genesis state from v0.34 and migrates it to v0.36
// genesis state. It deletes the governance base accounts and creates the new module accounts.
// The remaining accounts are updated to the new GenesisAccount type from 0.36
func Migrate(
oldGenState v034accounts.GenesisState, fees sdk.Coins, communityPool sdk.DecCoins,
deposits []v034gov.DepositWithMetadata, vals v034staking.Validators, ubds []v034staking.UnbondingDelegation,
valOutRewards []v034distr.ValidatorOutstandingRewardsRecord, bondDenom, distrModuleName, govModuleName string,
) GenesisState {
depositedCoinsAccAddr := sdk.AccAddress(crypto.AddressHash([]byte("govDepositedCoins")))
burnedDepositCoinsAccAddr := sdk.AccAddress(crypto.AddressHash([]byte("govBurnedDepositCoins")))
bondedAmt := sdk.ZeroInt()
notBondedAmt := sdk.ZeroInt()
// remove the two previous governance base accounts for deposits and burned
// coins from rejected proposals add six new module accounts:
// distribution, gov, mint, fee collector, bonded and not bonded pool
var (
newGenState GenesisState
govCoins sdk.Coins
extraAccounts = 6
)
for _, acc := range oldGenState {
switch {
case acc.Address.Equals(depositedCoinsAccAddr):
// remove gov deposits base account
govCoins = acc.Coins
extraAccounts -= 1
case acc.Address.Equals(burnedDepositCoinsAccAddr):
// remove gov burned deposits base account
extraAccounts -= 1
default:
newGenState = append(
newGenState,
NewGenesisAccount(
acc.Address, acc.Coins, acc.Sequence,
acc.OriginalVesting, acc.DelegatedFree, acc.DelegatedVesting,
acc.StartTime, acc.EndTime, "", []string{},
),
)
}
}
var expDeposits sdk.Coins
for _, deposit := range deposits {
expDeposits = expDeposits.Add(deposit.Deposit.Amount...)
}
if !expDeposits.IsEqual(govCoins) {
panic(
fmt.Sprintf(
"pre migration deposit base account coins ≠ stored deposits coins (%s ≠ %s)",
expDeposits.String(), govCoins.String(),
),
)
}
// get staking module accounts coins
for _, validator := range vals {
switch validator.Status {
case v034staking.Bonded:
bondedAmt = bondedAmt.Add(validator.Tokens)
case v034staking.Unbonding, v034staking.Unbonded:
notBondedAmt = notBondedAmt.Add(validator.Tokens)
default:
panic("invalid validator status")
}
}
for _, ubd := range ubds {
for _, entry := range ubd.Entries {
notBondedAmt = notBondedAmt.Add(entry.Balance)
}
}
bondedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, bondedAmt))
notBondedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, notBondedAmt))
// get distr module account coins
var distrDecCoins sdk.DecCoins
for _, reward := range valOutRewards {
distrDecCoins = distrDecCoins.Add(reward.OutstandingRewards...)
}
distrCoins, _ := distrDecCoins.Add(communityPool...).TruncateDecimal()
// get module account addresses
feeCollectorAddr := sdk.AccAddress(crypto.AddressHash([]byte(feeCollectorName)))
govAddr := sdk.AccAddress(crypto.AddressHash([]byte(govModuleName)))
bondedAddr := sdk.AccAddress(crypto.AddressHash([]byte(bondedPoolName)))
notBondedAddr := sdk.AccAddress(crypto.AddressHash([]byte(notBondedPoolName)))
distrAddr := sdk.AccAddress(crypto.AddressHash([]byte(distrModuleName)))
mintAddr := sdk.AccAddress(crypto.AddressHash([]byte(mintModuleName)))
// create module genesis accounts
feeCollectorModuleAcc := NewGenesisAccount(
feeCollectorAddr, fees, 0,
sdk.Coins{}, sdk.Coins{}, sdk.Coins{},
0, 0, feeCollectorName, []string{basic},
)
govModuleAcc := NewGenesisAccount(
govAddr, govCoins, 0,
sdk.Coins{}, sdk.Coins{}, sdk.Coins{},
0, 0, govModuleName, []string{burner},
)
distrModuleAcc := NewGenesisAccount(
distrAddr, distrCoins, 0,
sdk.Coins{}, sdk.Coins{}, sdk.Coins{},
0, 0, distrModuleName, []string{basic},
)
bondedModuleAcc := NewGenesisAccount(
bondedAddr, bondedCoins, 0,
sdk.Coins{}, sdk.Coins{}, sdk.Coins{},
0, 0, bondedPoolName, []string{burner, staking},
)
notBondedModuleAcc := NewGenesisAccount(
notBondedAddr, notBondedCoins, 0,
sdk.Coins{}, sdk.Coins{}, sdk.Coins{},
0, 0, notBondedPoolName, []string{burner, staking},
)
mintModuleAcc := NewGenesisAccount(
mintAddr, sdk.Coins{}, 0,
sdk.Coins{}, sdk.Coins{}, sdk.Coins{},
0, 0, mintModuleName, []string{minter},
)
newGenState = append(
newGenState,
[]GenesisAccount{
feeCollectorModuleAcc, govModuleAcc, distrModuleAcc,
bondedModuleAcc, notBondedModuleAcc, mintModuleAcc,
}...,
)
// verify the total number of accounts is correct
if len(newGenState) != len(oldGenState)+extraAccounts {
panic(
fmt.Sprintf(
"invalid total number of genesis accounts; got: %d, expected: %d",
len(newGenState), len(oldGenState)+extraAccounts),
)
}
return newGenState
}

View File

@ -1,126 +0,0 @@
package v036
import (
"testing"
"github.com/tendermint/tendermint/crypto"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
"github.com/cosmos/cosmos-sdk/types"
v034distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v034"
v034accounts "github.com/cosmos/cosmos-sdk/x/genaccounts/legacy/v034"
v034gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v034"
v034staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v034"
"github.com/stretchr/testify/require"
)
var (
priv = secp256k1.GenPrivKey()
addr = types.AccAddress(priv.PubKey().Address())
depositedCoinsAccAddr = types.AccAddress(crypto.AddressHash([]byte("govDepositedCoins")))
burnedDepositCoinsAccAddr = types.AccAddress(crypto.AddressHash([]byte("govBurnedDepositCoins")))
coins = types.Coins{types.NewInt64Coin(types.DefaultBondDenom, 10)}
halfCoins = types.Coins{types.NewInt64Coin(types.DefaultBondDenom, 5)}
accountDeposited = v034accounts.GenesisAccount{
Address: depositedCoinsAccAddr,
Coins: coins,
Sequence: 1,
AccountNumber: 1,
OriginalVesting: coins,
DelegatedFree: coins,
DelegatedVesting: coins,
StartTime: 0,
EndTime: 0,
}
accountBurned = v034accounts.GenesisAccount{
Address: burnedDepositCoinsAccAddr,
Coins: coins,
Sequence: 2,
AccountNumber: 2,
OriginalVesting: coins,
DelegatedFree: coins,
DelegatedVesting: coins,
StartTime: 0,
EndTime: 0,
}
deposit = v034gov.DepositWithMetadata{
ProposalID: 1,
Deposit: v034gov.Deposit{
ProposalID: 1,
Depositor: addr,
Amount: coins,
},
}
)
func TestMigrateEmptyRecord(t *testing.T) {
type args struct {
accounts v034accounts.GenesisState
deposits []v034gov.DepositWithMetadata
}
tests := []struct {
name string
args args
}{
{"No Accounts", args{v034accounts.GenesisState{}, []v034gov.DepositWithMetadata{}}},
{"Deposited account", args{v034accounts.GenesisState{accountDeposited}, []v034gov.DepositWithMetadata{deposit}}},
{"Burned account", args{v034accounts.GenesisState{accountBurned}, []v034gov.DepositWithMetadata{}}},
{"Burned and deposited accounts", args{v034accounts.GenesisState{accountDeposited, accountBurned}, []v034gov.DepositWithMetadata{deposit}}},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
require.NotPanics(t, func() {
Migrate(
tt.args.accounts,
types.Coins{},
types.DecCoins{},
tt.args.deposits,
v034staking.Validators{},
[]v034staking.UnbondingDelegation{},
[]v034distr.ValidatorOutstandingRewardsRecord{},
types.DefaultBondDenom,
v034distr.ModuleName,
v034gov.ModuleName,
)
})
})
}
}
func TestMigrateWrongDeposit(t *testing.T) {
require.Panics(t, func() {
Migrate(
v034accounts.GenesisState{
accountDeposited,
accountBurned,
},
types.Coins{},
types.DecCoins{},
[]v034gov.DepositWithMetadata{
{
ProposalID: 1,
Deposit: v034gov.Deposit{
ProposalID: 1,
Depositor: addr,
Amount: halfCoins,
},
},
},
v034staking.Validators{},
[]v034staking.UnbondingDelegation{},
[]v034distr.ValidatorOutstandingRewardsRecord{},
types.DefaultBondDenom,
v034distr.ModuleName,
v034gov.ModuleName,
)
})
}

View File

@ -1,52 +0,0 @@
// DONTCOVER
package v036
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
const (
ModuleName = "accounts"
)
type (
GenesisAccount struct {
Address sdk.AccAddress `json:"address" yaml:"address"`
Coins sdk.Coins `json:"coins" yaml:"coins"`
Sequence uint64 `json:"sequence_number" yaml:"sequence_number"`
AccountNumber uint64 `json:"account_number" yaml:"account_number"`
OriginalVesting sdk.Coins `json:"original_vesting" yaml:"original_vesting"`
DelegatedFree sdk.Coins `json:"delegated_free" yaml:"delegated_free"`
DelegatedVesting sdk.Coins `json:"delegated_vesting" yaml:"delegated_vesting"`
StartTime int64 `json:"start_time" yaml:"start_time"`
EndTime int64 `json:"end_time" yaml:"end_time"`
ModuleName string `json:"module_name" yaml:"module_name"`
ModulePermissions []string `json:"module_permissions" yaml:"module_permissions"`
}
GenesisState []GenesisAccount
)
// NewGenesisAccount creates a new GenesisAccount object
func NewGenesisAccount(
address sdk.AccAddress, coins sdk.Coins, sequence uint64,
vestingAmount, delFree, delVesting sdk.Coins, vestingStartTime, vestingEndTime int64,
module string, permissions []string,
) GenesisAccount {
return GenesisAccount{
Address: address,
Coins: coins,
Sequence: sequence,
AccountNumber: 0, // ignored set by the account keeper during InitGenesis
OriginalVesting: vestingAmount,
DelegatedFree: delFree,
DelegatedVesting: delVesting,
StartTime: vestingStartTime,
EndTime: vestingEndTime,
ModuleName: module,
ModulePermissions: permissions,
}
}

View File

@ -14,9 +14,6 @@ import (
"github.com/cosmos/cosmos-sdk/client/flags"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/version"
v036 "github.com/cosmos/cosmos-sdk/x/genutil/legacy/v036"
v038 "github.com/cosmos/cosmos-sdk/x/genutil/legacy/v038"
v039 "github.com/cosmos/cosmos-sdk/x/genutil/legacy/v039"
v040 "github.com/cosmos/cosmos-sdk/x/genutil/legacy/v040"
v043 "github.com/cosmos/cosmos-sdk/x/genutil/legacy/v043"
"github.com/cosmos/cosmos-sdk/x/genutil/types"
@ -28,9 +25,6 @@ const flagGenesisTime = "genesis-time"
//
// Ref: https://github.com/cosmos/cosmos-sdk/issues/5041
var migrationMap = types.MigrationMap{
"v0.36": v036.Migrate,
"v0.38": v038.Migrate, // NOTE: v0.37 and v0.38 are genesis compatible.
"v0.39": v039.Migrate,
"v0.42": v040.Migrate, // NOTE: v0.40, v0.41 and v0.42 are genesis compatible.
"v0.43": v043.Migrate,
}

View File

@ -27,12 +27,6 @@ func (s *IntegrationTestSuite) TestMigrateGenesis() {
expErrMsg string
check func(jsonOut string)
}{
{
"migrate 0.34 to 0.36",
`{"chain_id":"test","app_state":{}}`,
"v0.36",
false, "", func(_ string) {},
},
{
"migrate 0.37 to 0.42",
v037Exported,

Some files were not shown because too many files have changed in this diff Show More