Merge branch 'master' into sunny/change-pubkey-adr
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
# ADR Creation Process
|
||||
|
||||
1. Copy the `adr-template.md` file. Use the following filename pattern: `adr-next_number-title.md`
|
||||
2. Create a draft Pull Request if you want to get an early feedback.
|
||||
3. Make sure the context and a solution is clear and well documented.
|
||||
4. Add an entry to a list in the [README](./README.md) file.
|
||||
5. Create a Pull Request to propose a new ADR.
|
||||
|
||||
|
||||
## ADR life cycle
|
||||
|
||||
ADR creation is an **iterative** process. Instead of trying to solve all decisions in a single ADR pull request, we MUST firstly understand the problem and collect feedback through a GitHub Issue.
|
||||
|
||||
1. Every proposal SHOULD start with a new GitHub Issue or be a result of existing Issues. The Issue should contain just a brief proposal summary.
|
||||
|
||||
2. Once the motivation is validated, a GitHub Pull Request (PR) is created with a new document based on the `adr-template.md`.
|
||||
|
||||
3. An ADR doesn't have to arrive to `master` with an _accepted_ status in a single PR. If the motivation is clear and the solution is sound, we SHOULD be able to merge it and keep a _proposed_ status. It's preferable to have an iterative approach rather than long, not merged Pull Requests.
|
||||
|
||||
4. If a _proposed_ ADR is merged, then it should clearly document outstanding issues either in ADR document notes or in a GitHub Issue.
|
||||
|
||||
5. The PR SHOULD always be merged. In the case of a faulty ADR, we still prefer to merge it with a _rejected_ status. The only time the ADR SHOULD NOT be merged is if the author abandons it.
|
||||
|
||||
6. Merged ADRs SHOULD NOT be pruned.
|
||||
|
||||
|
||||
### ADR status
|
||||
|
||||
Status has two components:
|
||||
|
||||
```
|
||||
{CONSENSUS STATUS} {IMPLEMENTATION STATUS}
|
||||
```
|
||||
|
||||
IMPLEMENTATION STATUS is either `Implemented` or `Not Implemented`.
|
||||
|
||||
#### Consensus Status
|
||||
|
||||
```
|
||||
DRAFT -> PROPOSED -> LAST CALL yyyy-mm-dd -> ACCEPTED | REJECTED -> SUPERSEEDED by ADR-xxx
|
||||
\ |
|
||||
\ |
|
||||
v v
|
||||
ABANDONED
|
||||
```
|
||||
|
||||
|
||||
+ `DRAFT`: [optional] an ADR which is work in progress, not being ready for a general review. This is to present an early work and get an early feedback in a Draft Pull Request form.
|
||||
+ `PROPOSED`: an ADR covering a full solution architecture and still in the review - project stakeholders haven't reached an agreed yet.
|
||||
+ `LAST CALL <date for the last call>`: [optional] clear notify that we are close to accept updates. Changing a status to `LAST CALL` means that social consensus (of Cosmos SDK maintainers) has been reached and we still want to give it a time to let the community react or analyze.
|
||||
+ `ACCEPTED`: ADR which will represent a currently implemented or to be implemented architecture design.
|
||||
+ `REJECTED`: ADR can go from PROPOSED or ACCEPTED to rejected if the consensus among project stakeholders will decide so.
|
||||
+ `SUPERSEEDED by ADR-xxx`: ADR which has been superseded by a new ADR.
|
||||
+ `ABANDONED`: the ADR is no longer pursued by the original authors.
|
||||
|
||||
|
||||
## Language used in ADR
|
||||
|
||||
+ The context/background should be written in the present tense.
|
||||
+ Avoid using a first, personal form.
|
||||
@@ -8,8 +8,15 @@ parent:
|
||||
|
||||
This is a location to record all high-level architecture decisions in the Cosmos-SDK.
|
||||
|
||||
An Architectural Decision (**AD**) is a software design choice that addresses a functional or non-functional requirement that is architecturally significant.
|
||||
An Architecturally Significant Requirement (**ASR**) is a requirement that has a measurable effect on a software system’s architecture and quality.
|
||||
An Architectural Decision Record (**ADR**) captures a single AD, such as often done when writing personal notes or meeting minutes; the collection of ADRs created and maintained in a project constitute its decision log. All these are within the topic of Architectural Knowledge Management (AKM).
|
||||
|
||||
You can read more about the ADR concept in this [blog post](https://product.reverb.com/documenting-architecture-decisions-the-reverb-way-a3563bb24bd0#.78xhdix6t).
|
||||
|
||||
## Rationale
|
||||
|
||||
ADRs are intended to be the primary mechanism for proposing new feature designs and new processes, for collecting community input on an issue, and for documenting the design decisions.
|
||||
An ADR should provide:
|
||||
|
||||
- Context on the relevant goals and the current state
|
||||
@@ -25,19 +32,29 @@ it stands today.
|
||||
|
||||
If recorded decisions turned out to be lacking, convene a discussion, record the new decisions here, and then modify the code to match.
|
||||
|
||||
Note the context/background should be written in the present tense.
|
||||
|
||||
Please add a entry below in your Pull Request for an ADR.
|
||||
## Creating new ADR
|
||||
|
||||
Read about the [PROCESS](./PROCESS.md).
|
||||
|
||||
## ADR Table of Contents
|
||||
|
||||
### Accepted
|
||||
|
||||
- [ADR 001: Coin Source Tracing](./adr-001-coin-source-tracing.md)
|
||||
- [ADR 002: SDK Documentation Structure](./adr-002-docs-structure.md)
|
||||
- [ADR 003: Dynamic Capability Store](./adr-003-dynamic-capability-store.md)
|
||||
- [ADR 004: Split Denomination Keys](./adr-004-split-denomination-keys.md)
|
||||
- [ADR 006: Secret Store Replacement](./adr-006-secret-store-replacement.md)
|
||||
- [ADR 009: Evidence Module](./adr-009-evidence-module.md)
|
||||
- [ADR 010: Modular AnteHandler](./adr-010-modular-antehandler.md)
|
||||
- [ADR 019: Protocol Buffer State Encoding](./adr-019-protobuf-state-encoding.md)
|
||||
- [ADR 020: Protocol Buffer Transaction Encoding](./adr-020-protobuf-transaction-encoding.md)
|
||||
- [ADR 026: IBC Client Recovery Mechanisms](./adr-026-ibc-client-recovery-mechanisms.md)
|
||||
- [ADR 029: Fee Grant Module](./adr-029-fee-grant-module.md)
|
||||
|
||||
### Proposed
|
||||
|
||||
- [ADR 003: Dynamic Capability Store](./adr-003-dynamic-capability-store.md)
|
||||
- [ADR 004: Split Denomination Keys](./adr-004-split-denomination-keys.md)
|
||||
- [ADR 011: Generalize Genesis Accounts](./adr-011-generalize-genesis-accounts.md)
|
||||
- [ADR 012: State Accessors](./adr-012-state-accessors.md)
|
||||
- [ADR 013: Metrics](./adr-013-metrics.md)
|
||||
@@ -45,12 +62,12 @@ Please add a entry below in your Pull Request for an ADR.
|
||||
- [ADR 016: Validator Consensus Key Rotation](./adr-016-validator-consensus-key-rotation.md)
|
||||
- [ADR 017: Historical Header Module](./adr-017-historical-header-module.md)
|
||||
- [ADR 018: Extendable Voting Periods](./adr-018-extendable-voting-period.md)
|
||||
- [ADR 019: Protocol Buffer State Encoding](./adr-019-protobuf-state-encoding.md)
|
||||
- [ADR 020: Protocol Buffer Transaction Encoding](./adr-020-protobuf-transaction-encoding.md)
|
||||
- [ADR 021: Protocol Buffer Query Encoding](./adr-021-protobuf-query-encoding.md)
|
||||
- [ADR 022: Custom baseapp panic handling](./adr-022-custom-panic-handling.md)
|
||||
- [ADR 023: Protocol Buffer Naming and Versioning](./adr-023-protobuf-naming.md)
|
||||
- [ADR 024: Coin Metadata](./adr-024-coin-metadata.md)
|
||||
- [ADR 025: IBC Passive Channels](./adr-025-ibc-passive-channels.md)
|
||||
- [ADR 026: IBC Client Recovery Mechanisms](./adr-026-ibc-client-recovery-mechanisms.md)
|
||||
- [ADR 027: Deterministic Protobuf Serialization](./adr-027-deterministic-protobuf-serialization.md)
|
||||
- [ADR 028: Public Key Addresses](./adr-028-public-key-addresses.md)
|
||||
- [ADR 031: Protobuf Msg Services](./adr-031-msg-service.md)
|
||||
- [ADR 032: Typed Events](./adr-032-typed-events.md)
|
||||
|
||||
@@ -9,8 +9,9 @@
|
||||
- 2020 May 14: Describe public key encoding
|
||||
- 2020 June 08: Store `TxBody` and `AuthInfo` as bytes in `SignDoc`; Document `TxRaw` as broadcast and storage type.
|
||||
- 2020 August 07: Use ADR 027 for serializing `SignDoc`.
|
||||
- 2020 August 19: Move sequence field from `SignDoc` to `SignerInfo`.
|
||||
- 2020 August 19: Move sequence field from `SignDoc` to `SignerInfo`, as discussed in [#6966](https://github.com/cosmos/cosmos-sdk/issues/6966).
|
||||
- 2020 September 25: Remove `PublicKey` type in favor of `secp256k1.PubKey`, `ed25519.PubKey` and `multisig.LegacyAminoPubKey`.
|
||||
- 2020 October 15: Add `GetAccount` and `GetAccountWithHeight` methods to the `AccountRetriever` interface.
|
||||
|
||||
## Status
|
||||
|
||||
@@ -315,6 +316,8 @@ and messages.
|
||||
|
||||
```go
|
||||
type AccountRetriever interface {
|
||||
GetAccount(clientCtx Context, addr sdk.AccAddress) (client.Account, error)
|
||||
GetAccountWithHeight(clientCtx Context, addr sdk.AccAddress) (client.Account, int64, error)
|
||||
EnsureExists(clientCtx client.Context, addr sdk.AccAddress) error
|
||||
GetAccountNumberSequence(clientCtx client.Context, addr sdk.AccAddress) (uint64, uint64, error)
|
||||
}
|
||||
|
||||
@@ -109,12 +109,12 @@ func (q Querier) QueryBalance(ctx context.Context, params *types.QueryBalancePar
|
||||
### Custom Query Registration and Routing
|
||||
|
||||
Query server implementations as above would be registered with `AppModule`s using
|
||||
a new method `RegisterQueryServer(grpc.Server)` which could be implemented simply
|
||||
a new method `RegisterQueryService(grpc.Server)` which could be implemented simply
|
||||
as below:
|
||||
|
||||
```go
|
||||
// x/bank/module.go
|
||||
func (am AppModule) RegisterQueryServer(server grpc.Server) {
|
||||
func (am AppModule) RegisterQueryService(server grpc.Server) {
|
||||
types.RegisterQueryServer(server, keeper.Querier{am.keeper})
|
||||
}
|
||||
```
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
# ADR 028: Public Key Addresses
|
||||
|
||||
## Changelog
|
||||
|
||||
- 2020/08/18: Initial version
|
||||
|
||||
## Status
|
||||
|
||||
Proposed
|
||||
|
||||
## Abstract
|
||||
|
||||
This ADR defines a canonical 20-byte address format for new public key algorithms, multisig public keys, and module
|
||||
accounts using string prefixes.
|
||||
|
||||
## Context
|
||||
|
||||
Issue [\#3685](https://github.com/cosmos/cosmos-sdk/issues/3685) identified that public key
|
||||
address spaces are currently overlapping. One initial proposal was extending the address length and
|
||||
adding prefixes for different types of addresses.
|
||||
|
||||
@ethanfrey explained an alternate approach originally used in https://github.com/iov-one/weave:
|
||||
|
||||
> I spent quite a bit of time thinking about this issue while building weave... The other cosmos Sdk.
|
||||
|
||||
> Basically I define a condition to be a type and format as human readable string with some binary data appended. This condition is hashed into an Address (again at 20 bytes). The use of this prefix makes it impossible to find a preimage for a given address with a different condition (eg ed25519 vs secp256k1).
|
||||
|
||||
> This is explained in depth here https://weave.readthedocs.io/en/latest/design/permissions.html
|
||||
|
||||
> And the code is here, look mainly at the top where we process conditions. https://github.com/iov-one/weave/blob/master/conditions.go
|
||||
|
||||
And explained how this approach should be sufficiently collision resistant:
|
||||
> Yeah, AFAIK, 20 bytes should be collision resistance when the preimages are unique and not malleable. A space of 2^160 would expect some collision to be likely around 2^80 elements (birthday paradox). And if you want to find a collision for some existing element in the database, it is still 2^160. 2^80 only is if all these elements are written to state.
|
||||
|
||||
> The good example you brought up was eg. a public key bytes being a valid public key on two algorithms supported by the codec. Meaning if either was broken, you would break accounts even if they were secured with the safer variant. This is only as the issue when no differentiating type info is present in the preimage (before hashing into an address).
|
||||
|
||||
> I would like to hear an argument if the 20 bytes space is an actual issue for security, as I would be happy to increase my address sizes in weave. I just figured cosmos and ethereum and bitcoin all use 20 bytes, it should be good enough. And the arguments above which made me feel it was secure. But I have not done a deeper analysis.
|
||||
|
||||
In discussions in [\#5694](https://github.com/cosmos/cosmos-sdk/issues/5694), we agreed to go with an
|
||||
approach similar to this where essentially we take the first 20 bytes of the `sha256` hash of
|
||||
the key type concatenated with the key bytes, summarized as `Sha256(KeyTypePrefix || Keybytes)[:20]`.
|
||||
|
||||
## Decision
|
||||
|
||||
### Legacy Public Key Addresses Don't Change
|
||||
|
||||
`secp256k1` and multisig public keys are currently in use in existing Cosmos SDK zones. They use the following
|
||||
address formats:
|
||||
|
||||
- secp256k1: `ripemd160(sha256(pk_bytes))[:20]`
|
||||
- legacy amino multisig: `sha256(aminoCdc.Marshal(pk))[:20]`
|
||||
|
||||
We don't want to change existing addresses. So the addresses for these two key types will remain the same.
|
||||
|
||||
The current multisig public keys use amino serialization to generate the address. We will retain
|
||||
those public keys and their address formatting, and call them "legacy amino" multisig public keys
|
||||
in protobuf. We will also create multisig public keys without amino addresses to be described below.
|
||||
|
||||
|
||||
### Canonical Address Format
|
||||
|
||||
We have three types of accounts we would like to create addresses for in the future:
|
||||
- regular public key addresses for new signature algorithms (ex. `sr25519`).
|
||||
- public key addresses for multisig public keys that don't use amino encoding
|
||||
- module accounts: basically any accounts which cannot sign transactions and
|
||||
which are managed internally by modules
|
||||
|
||||
To address all of these use cases we propose the following basic `AddressHash` function,
|
||||
based on the discussions in [\#5694](https://github.com/cosmos/cosmos-sdk/issues/5694):
|
||||
|
||||
```go
|
||||
func AddressHash(prefix string, contents []byte) []byte {
|
||||
preImage := []byte(prefix)
|
||||
if len(contents) != 0 {
|
||||
preImage = append(preImage, 0)
|
||||
preImage = append(preImage, contents...)
|
||||
}
|
||||
return sha256.Sum256(preImage)[:20]
|
||||
}
|
||||
```
|
||||
|
||||
`AddressHash` always take a string `prefix` as a starting point which should represent the
|
||||
type of public key (ex. `sr25519`) or module account being used (ex. `staking` or `group`).
|
||||
For public keys, the `contents` parameter is used to specify the binary contents of the public
|
||||
key. For module accounts, `contents` can be left empty (for modules which don't manage "sub-accounts"),
|
||||
or can be some module-specific content to specify different pools (ex. `bonded` or `not-bonded` for `staking`)
|
||||
or managed accounts (ex. different accounts managed by the `group` module).
|
||||
|
||||
In the `preImage`, the byte value `0` is used as the separator between `prefix` and `contents`. This is a logical
|
||||
choice given that `0` is an invalid value for a string character and is commonly used as a null terminator.
|
||||
|
||||
### Canonical Public Key Address Prefixes
|
||||
|
||||
All public key types will have a unique protobuf message type such as:
|
||||
|
||||
```proto
|
||||
package cosmos.crypto.sr25519;
|
||||
|
||||
message PubKey {
|
||||
bytes key = 1;
|
||||
}
|
||||
```
|
||||
|
||||
All protobuf messages have unique fully qualified names, in this example `cosmos.crypto.sr25519.PubKey`.
|
||||
These names are derived directly from .proto files in a standardized way and used
|
||||
in other places such as the type URL in `Any`s. Since there is an easy and obvious
|
||||
way to get this name for every protobuf type, we can use this message name as the
|
||||
key type `prefix` when creating addresses. For all basic public keys, `contents`
|
||||
should just be the raw unencoded public key bytes.
|
||||
|
||||
Thus the canonical address for new public key types would be `AddressHash(proto.MessageName(pk), pk.Bytes)`.
|
||||
|
||||
### Multisig Addresses
|
||||
|
||||
For new multisig public keys, we define a custom address format not based on any encoding scheme
|
||||
(amino or protobuf). This avoids issues with non-determinism in the encoding scheme. It also
|
||||
ensures that multisig public keys which differ simply in the ordering of keys have the same
|
||||
address by sorting child public keys first.
|
||||
|
||||
First we define a proto message for multisig public keys:
|
||||
```proto
|
||||
package cosmos.crypto.multisig;
|
||||
|
||||
message PubKey {
|
||||
uint32 threshold = 1;
|
||||
repeated google.protobuf.Any public_keys = 2;
|
||||
}
|
||||
```
|
||||
|
||||
We define the following `Address()` function for this public key:
|
||||
|
||||
```
|
||||
func (multisig PubKey) Address() {
|
||||
// first gather all the addresses of each nested public key
|
||||
var addresses [][]byte
|
||||
for key := range multisig.Keys {
|
||||
addresses = append(joinedAddresses, key.Address())
|
||||
}
|
||||
|
||||
// then sort them in ascending order
|
||||
addresses = Sort(addresses)
|
||||
|
||||
// then concatenate them together
|
||||
var joinedAddresses []byte
|
||||
for addr := range addresses {
|
||||
joinedAddresses := append(joinedAddresses, addr...)
|
||||
}
|
||||
|
||||
// form the string prefix from the message name (cosmos.crypto.multisig.PubKey) and the threshold joined together
|
||||
prefix := fmt.Sprintf("%s/%d", proto.MessageName(multisig), multisig.Threshold)
|
||||
|
||||
// use the standard AddressHash function
|
||||
return AddressHash(prefix, joinedAddresses)
|
||||
}
|
||||
```
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
- a simple algorithm for generating addresses for new public keys and module accounts
|
||||
|
||||
### Negative
|
||||
- addresses do not communicate key type, a prefixed approach would have done this
|
||||
|
||||
### Neutral
|
||||
- protobuf message names are used as key type prefixes
|
||||
|
||||
## References
|
||||
@@ -0,0 +1,162 @@
|
||||
# ADR 029: Fee Grant Module
|
||||
|
||||
## Changelog
|
||||
|
||||
- 2020/08/18: Initial Draft
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
In order to make blockchain transactions, the signing account must possess a sufficient balance of the right denomination
|
||||
in order to pay fees. There are classes of transactions where needing to maintain a wallet with sufficient fees is a
|
||||
barrier to adoption.
|
||||
|
||||
For instance, when proper permissions are setup, someone may temporarily delegate the ability to vote on proposals to
|
||||
a "burner" account that is stored on a mobile phone with only minimal security.
|
||||
|
||||
Other use cases include workers tracking items in a supply chain or farmers submitting field data for analytics
|
||||
or compliance purposes.
|
||||
|
||||
For all of these use cases, UX would be significantly enhanced by obviating the need for these accounts to always
|
||||
maintain the appropriate fee balance. This is especially true if we wanted to achieve enterprise adoption for something
|
||||
like supply chain tracking.
|
||||
|
||||
While one solution would be to have a service that fills up these accounts automatically with the appropriate fees, a better UX
|
||||
would be provided by allowing these accounts to pull from a common fee pool account with proper spending limits.
|
||||
A single pool would reduce the churn of making lots of small "fill up" transactions and also more effectively leverages
|
||||
the resources of the organization setting up the pool.
|
||||
|
||||
## Decision
|
||||
|
||||
As a solution we propose a module, `x/feegrant` which allows one account, the "granter" to grant another account, the "grantee"
|
||||
an allowance to spend the granter's account balance for fees within certain well-defined limits.
|
||||
|
||||
Fee allowances are defined by the extensible `FeeAllowanceI` interface:
|
||||
|
||||
```go
|
||||
type FeeAllowanceI {
|
||||
// Accept can use fee payment requested as well as timestamp/height of the current block
|
||||
// to determine whether or not to process this. This is checked in
|
||||
// Keeper.UseGrantedFees and the return values should match how it is handled there.
|
||||
//
|
||||
// If it returns an error, the fee payment is rejected, otherwise it is accepted.
|
||||
// The FeeAllowance implementation is expected to update it's internal state
|
||||
// and will be saved again after an acceptance.
|
||||
//
|
||||
// If remove is true (regardless of the error), the FeeAllowance will be deleted from storage
|
||||
// (eg. when it is used up). (See call to RevokeFeeAllowance in Keeper.UseGrantedFees)
|
||||
Accept(fee sdk.Coins, blockTime time.Time, blockHeight int64) (remove bool, err error)
|
||||
}
|
||||
```
|
||||
|
||||
Two basic fee allowance types, `BasicFeeAllowance` and `PeriodicFeeAllowance` are defined to support known use cases:
|
||||
|
||||
```proto
|
||||
// BasicFeeAllowance implements FeeAllowance with a one-time grant of tokens
|
||||
// that optionally expires. The delegatee can use up to SpendLimit to cover fees.
|
||||
message BasicFeeAllowance {
|
||||
// spend_limit specifies the maximum amount of tokens that can be spent
|
||||
// by this allowance and will be updated as tokens are spent. If it is
|
||||
// empty, there is no spend limit and any amount of coins can be spent.
|
||||
repeated cosmos_sdk.v1.Coin spend_limit = 1;
|
||||
|
||||
// expires_at specifies an optional time when this allowance expires
|
||||
ExpiresAt expiration = 2;
|
||||
}
|
||||
|
||||
// PeriodicFeeAllowance extends FeeAllowance to allow for both a maximum cap,
|
||||
// as well as a limit per time period.
|
||||
message PeriodicFeeAllowance {
|
||||
BasicFeeAllowance basic = 1;
|
||||
|
||||
// period specifies the time duration in which period_spend_limit coins can
|
||||
// be spent before that allowance is reset
|
||||
Duration period = 2;
|
||||
|
||||
// period_spend_limit specifies the maximum number of coins that can be spent
|
||||
// in the period
|
||||
repeated cosmos_sdk.v1.Coin period_spend_limit = 3;
|
||||
|
||||
// period_can_spend is the number of coins left to be spent before the period_reset time
|
||||
repeated cosmos_sdk.v1.Coin period_can_spend = 4;
|
||||
|
||||
// period_reset is the time at which this period resets and a new one begins,
|
||||
// it is calculated from the start time of the first transaction after the
|
||||
// last period ended
|
||||
ExpiresAt period_reset = 5;
|
||||
}
|
||||
|
||||
// ExpiresAt is a point in time where something expires.
|
||||
// It may be *either* block time or block height
|
||||
message ExpiresAt {
|
||||
oneof sum {
|
||||
google.protobuf.Timestamp time = 1;
|
||||
uint64 height = 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Duration is a repeating unit of either clock time or number of blocks.
|
||||
message Duration {
|
||||
oneof sum {
|
||||
google.protobuf.Duration duration = 1;
|
||||
uint64 blocks = 2;
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Allowances can be granted and revoked using `MsgGrantFeeAllowance` and `MsgRevokeFeeAllowance`:
|
||||
|
||||
```proto
|
||||
message MsgGrantFeeAllowance {
|
||||
string granter = 1;
|
||||
string grantee = 2;
|
||||
google.protobuf.Any allowance = 3;
|
||||
}
|
||||
|
||||
// MsgRevokeFeeAllowance removes any existing FeeAllowance from Granter to Grantee.
|
||||
message MsgRevokeFeeAllowance {
|
||||
string granter = 1;
|
||||
string grantee = 2;
|
||||
}
|
||||
```
|
||||
|
||||
In order to use allowances in transactions, we add a new field `granter` to the transaction `Fee` type:
|
||||
```proto
|
||||
package cosmos.tx.v1beta1;
|
||||
|
||||
message Fee {
|
||||
repeated cosmos.base.v1beta1.Coin amount = 1;
|
||||
uint64 gas_limit = 2;
|
||||
string payer = 3;
|
||||
string granter = 4;
|
||||
}
|
||||
```
|
||||
|
||||
`granter` must either be left empty or must correspond to an account which has granted
|
||||
a fee allowance to fee payer (either the first signer or the value of the `payer` field).
|
||||
|
||||
A new `AnteDecorator` named `DeductGrantedFeeDecorator` will be created in order to process transactions with `fee_payer`
|
||||
set and correctly deduct fees based on fee allowances.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- improved UX for use cases where it is cumbersome to maintain an account balance just for fees
|
||||
|
||||
### Negative
|
||||
|
||||
### Neutral
|
||||
|
||||
- a new field must be added to the transaction `Fee` message and a new `AnteDecorator` must be
|
||||
created to use it
|
||||
|
||||
## References
|
||||
|
||||
- Blog article describing initial work: https://medium.com/regen-network/hacking-the-cosmos-cosmwasm-and-key-management-a08b9f561d1b
|
||||
- Initial public specification: https://gist.github.com/aaronc/b60628017352df5983791cad30babe56
|
||||
- Original subkeys proposal from B-harvest which influenced this design: https://github.com/cosmos/cosmos-sdk/issues/4480
|
||||
@@ -0,0 +1,238 @@
|
||||
# ADR 031: Protobuf Msg Services
|
||||
|
||||
## Changelog
|
||||
|
||||
- 2020-10-05: Initial Draft
|
||||
|
||||
## Status
|
||||
|
||||
Proposed
|
||||
|
||||
## Abstract
|
||||
|
||||
We want to leverage protobuf `service` definitions for defining `Msg`s which will give us significant developer UX
|
||||
improvements in terms of the code that is generated and the fact that return types will now be well defined.
|
||||
|
||||
## Context
|
||||
|
||||
Currently `Msg` handlers in the Cosmos SDK do have return values that are placed in the `data` field of the response.
|
||||
These return values, however, are not specified anywhere except in the golang handler code.
|
||||
|
||||
In early conversations [it was proposed](https://docs.google.com/document/d/1eEgYgvgZqLE45vETjhwIw4VOqK-5hwQtZtjVbiXnIGc/edit)
|
||||
that `Msg` return types be captured using a protobuf extension field, ex:
|
||||
|
||||
```protobuf
|
||||
package cosmos.gov;
|
||||
|
||||
message MsgSubmitProposal
|
||||
option (cosmos_proto.msg_return) = “uint64”;
|
||||
string delegator_address = 1;
|
||||
string validator_address = 2;
|
||||
repeated sdk.Coin amount = 3;
|
||||
}
|
||||
```
|
||||
|
||||
This was never adopted, however.
|
||||
|
||||
Having a well-specified return value for `Msg`s would improve client UX. For instance,
|
||||
in `x/gov`, `MsgSubmitProposal` returns the proposal ID as a big-endian `uint64`.
|
||||
This isn’t really documented anywhere and clients would need to know the internals
|
||||
of the SDK to parse that value and return it to users.
|
||||
|
||||
Also, there may be cases where we want to use these return values programatically.
|
||||
For instance, https://github.com/cosmos/cosmos-sdk/issues/7093 proposes a method for
|
||||
doing inter-module Ocaps using the `Msg` router. A well-defined return type would
|
||||
improve the developer UX for this approach.
|
||||
|
||||
In addition, handler registration of `Msg` types tends to add a bit of
|
||||
boilerplate on top of keepers and is usually done through manual type switches.
|
||||
This isn't necessarily bad, but it does add overhead to creating modules.
|
||||
|
||||
## Decision
|
||||
|
||||
We decide to use protobuf `service` definitions for defining `Msg`s as well as
|
||||
the code generated by them as a replacement for `Msg` handlers.
|
||||
|
||||
Below we define how this will look for the `SubmitProposal` message from `x/gov` module.
|
||||
We start with a `Msg` `service` definition:
|
||||
|
||||
```proto
|
||||
package cosmos.gov;
|
||||
|
||||
service Msg {
|
||||
rpc SubmitProposal(MsgSubmitProposal) returns (MsgSubmitProposalResponse);
|
||||
}
|
||||
|
||||
// Note that for backwards compatibility this uses MsgSubmitProposal as the request
|
||||
// type instead of the more canonical MsgSubmitProposalRequest
|
||||
message MsgSubmitProposal {
|
||||
google.protobuf.Any content = 1;
|
||||
string proposer = 2;
|
||||
}
|
||||
|
||||
message MsgSubmitProposalResponse {
|
||||
uint64 proposal_id;
|
||||
}
|
||||
```
|
||||
|
||||
While this is most commonly used for gRPC, overloading protobuf `service` definitions like this does not violate
|
||||
the intent of the [protobuf spec](https://developers.google.com/protocol-buffers/docs/proto3#services) which says:
|
||||
> If you don’t want to use gRPC, it’s also possible to use protocol buffers with your own RPC implementation.
|
||||
With this approach, we would get an auto-generated `MsgServer` interface:
|
||||
|
||||
In addition to clearly specifying return types, this has the benefit of generating client and server code. On the server
|
||||
side, this is almost like an automatically generated keeper method and could maybe be used intead of keepers eventually
|
||||
(see [\#7093](https://github.com/cosmos/cosmos-sdk/issues/7093)):
|
||||
|
||||
```go
|
||||
package gov
|
||||
|
||||
type MsgServer interface {
|
||||
SubmitProposal(context.Context, *MsgSubmitProposal) (*MsgSubmitProposalResponse, error)
|
||||
}
|
||||
```
|
||||
|
||||
On the client side, developers could take advantage of this by creating RPC implementations that encapsulate transaction
|
||||
logic. Protobuf libraries that use asynchronous callbacks, like [protobuf.js](https://github.com/protobufjs/protobuf.js#using-services)
|
||||
could use this to register callbacks for specific messages even for transactions that include multiple `Msg`s.
|
||||
|
||||
For backwards compatibility, 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.
|
||||
|
||||
### Encoding
|
||||
|
||||
Currently, we are encoding `Msg`s as `Any` in `Tx`s which involves packing the
|
||||
binary-encoded `Msg` with its type URL.
|
||||
|
||||
The type URL for `MsgSubmitProposal` based on the proto3 spec is `/cosmos.gov.MsgSubmitProposal`.
|
||||
|
||||
The fully-qualified name for the `SubmitProposal` service method above (also
|
||||
based on the proto3 and gRPC specs) is `/cosmos.gov.Msg/SubmitProposal` which varies
|
||||
by a single `/` character. The generated `.pb.go` files for protobuf `service`s
|
||||
include names of this form and any compliant protobuf/gRPC code generator will
|
||||
generate the same name.
|
||||
|
||||
In order to encode service methods in transactions, we encode them as `Any`s in
|
||||
the same `TxBody.messages` field as other `Msg`s. We simply set `Any.type_url`
|
||||
to the full-qualified method name (ex. `/cosmos.gov.Msg/SubmitProposal`) and
|
||||
set `Any.value` to the protobuf encoding of the request message
|
||||
(`MsgSubmitProposal` in this case).
|
||||
|
||||
### Decoding
|
||||
|
||||
When decoding, `TxBody.UnpackInterfaces` will need a special case
|
||||
to detect if `Any` type URLs match the service method format (ex. `/cosmos.gov.Msg/SubmitProposal`)
|
||||
by checking for two `/` characters. Messages that are method names plus request parameters
|
||||
instead of a normal `Any` messages will get unpacked into the `ServiceMsg` struct:
|
||||
|
||||
```go
|
||||
type ServiceMsg struct {
|
||||
// MethodName is the fully-qualified service name
|
||||
MethodName string
|
||||
// Request is the request payload
|
||||
Request MsgRequest
|
||||
}
|
||||
```
|
||||
|
||||
### Routing
|
||||
|
||||
In the future, `service` definitions may become the primary method for defining
|
||||
`Msg`s. As a starting point, we need to integrate with the SDK's existing routing
|
||||
and `Msg` interface.
|
||||
|
||||
To do this, `ServiceMsg` implements the `sdk.Msg` interface and its handler does the
|
||||
actual method routing, allowing this feature to be added incrementally on top of
|
||||
existing functionality.
|
||||
|
||||
### `MsgRequest` interface
|
||||
|
||||
All request messages will need to implement the `MsgRequest` interface which is a
|
||||
simplified version of `Msg`, without `Route()`, `Type()` and `GetSignBytes()` which
|
||||
are no longer needed:
|
||||
|
||||
```go
|
||||
type MsgRequest interface {
|
||||
proto.Message
|
||||
ValidateBasic() error
|
||||
GetSigners() []AccAddress
|
||||
}
|
||||
```
|
||||
|
||||
`ServiceMsg` will forward its `ValidateBasic` and `GetSigners` methods to the `MsgRequest`
|
||||
methods.
|
||||
|
||||
### Module Configuration
|
||||
|
||||
In [ADR 021](./adr-021-protobuf-query-encoding.md), we introduced a method `RegisterQueryService`
|
||||
to `AppModule` which allows for modules to register gRPC queriers.
|
||||
|
||||
To register `Msg` services, we attempt a more extensible approach by converting `RegisterQueryService`
|
||||
to a more generic `RegisterServices` method:
|
||||
|
||||
```go
|
||||
type AppModule interface {
|
||||
RegisterServices(Configurator)
|
||||
...
|
||||
}
|
||||
|
||||
type Configurator interface {
|
||||
QueryServer() grpc.Server
|
||||
MsgServer() grpc.Server
|
||||
}
|
||||
|
||||
// example module:
|
||||
func (am AppModule) RegisterServices(cfg Configurator) {
|
||||
types.RegisterQueryServer(cfg.QueryServer(), keeper)
|
||||
types.RegisterMsgServer(cfg.MsgServer(), keeper)
|
||||
}
|
||||
```
|
||||
|
||||
The `RegisterServices` method and the `Configurator` interface are intended to
|
||||
evolve to satisfy the use cases discussed in [\#7093](https://github.com/cosmos/cosmos-sdk/issues/7093)
|
||||
and [\#7122](https://github.com/cosmos/cosmos-sdk/issues/7421).
|
||||
|
||||
When `Msg` services are registered, the framework _should_ verify that all `Msg...Request` types
|
||||
implement the `MsgRequest` interface described above and throw an error during initialization rather
|
||||
than later when transactions are processed.
|
||||
|
||||
### `Msg` Service Implementation
|
||||
|
||||
Just like query services, `Msg` service methods can retrieve the `sdk.Context`
|
||||
from the `context.Context` parameter method using the `sdk.UnwrapSDKContext`
|
||||
method:
|
||||
|
||||
```go
|
||||
package gov
|
||||
|
||||
func (k Keeper) SubmitProposal(goCtx context.Context, params *types.MsgSubmitProposal) (*MsgSubmitProposalResponse, error) {
|
||||
ctx := sdk.UnwrapSDKContext(goCtx)
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
The `sdk.Context` should have an `EventManager` already attached by the `ServiceMsg`
|
||||
router.
|
||||
|
||||
Separate handler definition is no longer needed with this approach.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Pros
|
||||
- communicates return type clearly
|
||||
- manual handler registration and return type marshaling is no longer needed, just implement the interface and register it
|
||||
- some keeper code could be automatically generate, this would improve the UX of [\#7093](https://github.com/cosmos/cosmos-sdk/issues/7093) approach (1) if we chose to adopt that
|
||||
- generated client code could be useful for clients
|
||||
|
||||
### Cons
|
||||
- supporting both this and the current concrete `Msg` type approach simultaneously could be confusing
|
||||
(we could choose to deprecate the current approach)
|
||||
- using `service` definitions outside the context of gRPC could be confusing (but doesn’t violate the proto3 spec)
|
||||
|
||||
## References
|
||||
|
||||
- [Initial Github Issue \#7122](https://github.com/cosmos/cosmos-sdk/issues/7122)
|
||||
- [proto 3 Language Guide: Defining Services](https://developers.google.com/protocol-buffers/docs/proto3#services)
|
||||
- [Initial pre-`Any` `Msg` designs](https://docs.google.com/document/d/1eEgYgvgZqLE45vETjhwIw4VOqK-5hwQtZtjVbiXnIGc)
|
||||
- [ADR 020](./adr-020-protobuf-transaction-encoding.md)
|
||||
- [ADR 021](./adr-021-protobuf-query-encoding.md)
|
||||
@@ -0,0 +1,319 @@
|
||||
# ADR 032: Typed Events
|
||||
|
||||
## Changelog
|
||||
|
||||
- 28-Sept-2020: Initial Draft
|
||||
|
||||
## Authors
|
||||
|
||||
- Anil Kumar (@anilcse)
|
||||
- Jack Zampolin (@jackzampolin)
|
||||
- Adam Bozanich (@boz)
|
||||
|
||||
## Status
|
||||
|
||||
Proposed
|
||||
|
||||
## Abstract
|
||||
|
||||
Currently in the SDK, events are defined in the handlers for each message as well as `BeginBlock` and `EndBlock`. Each module doesn't have types defined for each event, they are implemented as `map[string]string`. Above all else this makes these events difficult to consume as it requires a great deal of raw string matching and parsing. This proposal focuses on updating the events to use **typed events** defined in each module such that emiting and subscribing to events will be much easier. This workflow comes from the experience of the Akash Network team.
|
||||
|
||||
## Context
|
||||
|
||||
Currently in the SDK, events are defined in the handlers for each message, meaning each module doesn't have a cannonical set of types for each event. Above all else this makes these events difficult to consume as it requires a great deal of raw string matching and parsing. This proposal focuses on updating the events to use **typed events** defined in each module such that emiting and subscribing to events will be much easier. This workflow comes from the experience of the Akash Network team.
|
||||
|
||||
[Our platform](http://github.com/ovrclk/akash) requires a number of programatic on chain interactions both on the provider (datacenter - to bid on new orders and listen for leases created) and user (application developer - to send the app manifest to the provider) side. In addition the Akash team is now maintaining the IBC [`relayer`](https://github.com/ovrclk/relayer), another very event driven process. In working on these core pieces of infrastructure, and integrating lessons learned from Kubernetes developement, our team has developed a standard method for defining and consuming typed events in SDK modules. We have found that it is extremely useful in building this type of event driven application.
|
||||
|
||||
As the SDK gets used more extensively for apps like `peggy`, other peg zones, IBC, DeFi, etc... there will be an exploding demand for event driven applications to support new features desired by users. We propose upstreaming our findings into the SDK to enable all SDK applications to quickly and easily build event driven apps to aid their core application. Wallets, exchanges, explorers, and defi protocols all stand to benefit from this work.
|
||||
|
||||
If this proposal is accepted, users will be able to build event driven SDK apps in go by just writing `EventHandler`s for their specific event types and passing them to `EventEmitters` that are defined in the SDK.
|
||||
|
||||
The end of this proposal contains a detailed example of how to consume events after this refactor.
|
||||
|
||||
This proposal is specifically about how to consume these events as a client of the blockchain, not for intermodule communication.
|
||||
|
||||
## Decision
|
||||
|
||||
__Step-1__: Implement additional functionality in the `types` package: `EmitTypedEvent` and `ParseTypedEvent` functions
|
||||
|
||||
```go
|
||||
// types/events.go
|
||||
|
||||
// EmitTypedEvent takes typed event and emits converting it into sdk.Event
|
||||
func (em *EventManager) EmitTypedEvent(event proto.Message) error {
|
||||
evtType := proto.MessageName(event)
|
||||
evtJSON, err := codec.ProtoMarshalJSON(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var attrMap map[string]json.RawMessage
|
||||
err = json.Unmarshal(evtJSON, &attrMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var attrs []abci.EventAttribute
|
||||
for k, v := range attrMap {
|
||||
attrs = append(attrs, abci.EventAttribute{
|
||||
Key: []byte(k),
|
||||
Value: v,
|
||||
})
|
||||
}
|
||||
|
||||
em.EmitEvent(Event{
|
||||
Type: evtType,
|
||||
Attributes: attrs,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParseTypedEvent converts abci.Event back to typed event
|
||||
func ParseTypedEvent(event abci.Event) (proto.Message, error) {
|
||||
concreteGoType := proto.MessageType(event.Type)
|
||||
if concreteGoType == nil {
|
||||
return nil, fmt.Errorf("failed to retrieve the message of type %q", event.Type)
|
||||
}
|
||||
|
||||
var value reflect.Value
|
||||
if concreteGoType.Kind() == reflect.Ptr {
|
||||
value = reflect.New(concreteGoType.Elem())
|
||||
} else {
|
||||
value = reflect.Zero(concreteGoType)
|
||||
}
|
||||
|
||||
protoMsg, ok := value.Interface().(proto.Message)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%q does not implement proto.Message", event.Type)
|
||||
}
|
||||
|
||||
attrMap := make(map[string]json.RawMessage)
|
||||
for _, attr := range event.Attributes {
|
||||
attrMap[string(attr.Key)] = attr.Value
|
||||
}
|
||||
|
||||
attrBytes, err := json.Marshal(attrMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = jsonpb.Unmarshal(strings.NewReader(string(attrBytes)), protoMsg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return protoMsg, nil
|
||||
}
|
||||
```
|
||||
|
||||
Here, the `EmitTypedEvent` is a method on `EventManager` which takes typed event as input and apply json serialization on it. Then it maps the JSON key/value pairs to `event.Attributes` and emits it in form of `sdk.Event`. `Event.Type` will be the type URL of the proto message.
|
||||
|
||||
When we subscribe to emitted events on the tendermint websocket, they are emitted in the form of an `abci.Event`. `ParseTypedEvent` parses the event back to it's original proto message.
|
||||
|
||||
__Step-2__: Add proto definitions for typed events for msgs in each module:
|
||||
|
||||
For example, let's take `MsgSubmitProposal` of `gov` module and implement this event's type.
|
||||
|
||||
```protobuf
|
||||
// proto/cosmos/gov/v1beta1/gov.proto
|
||||
// Add typed event definition
|
||||
|
||||
package cosmos.gov.v1beta1;
|
||||
|
||||
message EventSubmitProposal {
|
||||
string from_address = 1;
|
||||
uint64 proposal_id = 2;
|
||||
TextProposal proposal = 3;
|
||||
}
|
||||
```
|
||||
|
||||
__Step-3__: Refactor event emission to use the typed event created and emit using `sdk.EmitTypedEvent`:
|
||||
|
||||
```go
|
||||
// x/gov/handler.go
|
||||
func handleMsgSubmitProposal(ctx sdk.Context, keeper keeper.Keeper, msg types.MsgSubmitProposalI) (*sdk.Result, error) {
|
||||
...
|
||||
types.Context.EventManager().EmitTypedEvent(
|
||||
&EventSubmitProposal{
|
||||
FromAddress: fromAddress,
|
||||
ProposalId: id,
|
||||
Proposal: proposal,
|
||||
},
|
||||
)
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
#### How to subscribe to these typed events in `Client`
|
||||
|
||||
> NOTE: Full code example below
|
||||
|
||||
Users will be able to subscribe using `client.Context.Client.Subscribe` and consume events which are emitted using `EventHandler`s.
|
||||
|
||||
Akash Network has built a simple [`pubsub`](https://github.com/ovrclk/akash/blob/90d258caeb933b611d575355b8df281208a214f8/pubsub/bus.go#L20). This can be used to subscribe to `abci.Events` and [publish](https://github.com/ovrclk/akash/blob/90d258caeb933b611d575355b8df281208a214f8/events/publish.go#L21) them as typed events.
|
||||
|
||||
Please see the below code sample for more detail on this flow looks for clients.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
* Improves consistency of implementation for the events currently in the sdk
|
||||
* Provides a much more ergonomic way to handle events and facilitates writing event driven applications
|
||||
* This implementation will support a middleware ecosystem of `EventHandler`s
|
||||
|
||||
### Negative
|
||||
|
||||
|
||||
## Detailed code example of publishing events
|
||||
|
||||
This ADR also proposes adding affordances to emit and consume these events. This way developers will only need to write
|
||||
`EventHandler`s which define the actions they desire to take.
|
||||
|
||||
```go
|
||||
// EventEmitter is a type that describes event emitter functions
|
||||
// This should be defined in `types/events.go`
|
||||
type EventEmitter func(context.Context, client.Context, ...EventHandler) error
|
||||
|
||||
// EventHandler is a type of function that handles events coming out of the event bus
|
||||
// This should be defined in `types/events.go`
|
||||
type EventHandler func(proto.Message) error
|
||||
|
||||
// Sample use of the functions below
|
||||
func main() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
if err := TxEmitter(ctx, client.Context{}.WithNodeURI("tcp://localhost:26657"), SubmitProposalEventHandler); err != nil {
|
||||
cancel()
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SubmitProposalEventHandler is an example of an event handler that prints proposal details
|
||||
// when any EventSubmitProposal is emitted.
|
||||
func SubmitProposalEventHandler(ev proto.Message) (err error) {
|
||||
switch event := ev.(type) {
|
||||
// Handle governance proposal events creation events
|
||||
case govtypes.EventSubmitProposal:
|
||||
// Users define business logic here e.g.
|
||||
fmt.Println(ev.FromAddress, ev.ProposalId, ev.Proposal)
|
||||
return nil
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// TxEmitter is an example of an event emitter that emits just transaction events. This can and
|
||||
// should be implemented somewhere in the SDK. The SDK can include an EventEmitters for tm.event='Tx'
|
||||
// and/or tm.event='NewBlock' (the new block events may contain typed events)
|
||||
func TxEmitter(ctx context.Context, cliCtx client.Context, ehs ...EventHandler) (err error) {
|
||||
// Instantiate and start tendermint RPC client
|
||||
client, err := cliCtx.GetNode()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = client.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Start the pubsub bus
|
||||
bus := pubsub.NewBus()
|
||||
defer bus.Close()
|
||||
|
||||
// Initialize a new error group
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
|
||||
// Publish chain events to the pubsub bus
|
||||
eg.Go(func() error {
|
||||
return PublishChainTxEvents(ctx, client, bus, simapp.ModuleBasics)
|
||||
})
|
||||
|
||||
// Subscribe to the bus events
|
||||
subscriber, err := bus.Subscribe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Handle all the events coming out of the bus
|
||||
eg.Go(func() error {
|
||||
var err error
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case <-subscriber.Done():
|
||||
return nil
|
||||
case ev := <-subscriber.Events():
|
||||
for _, eh := range ehs {
|
||||
if err = eh(ev); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return group.Wait()
|
||||
}
|
||||
|
||||
// PublishChainTxEvents events using tmclient. Waits on context shutdown signals to exit.
|
||||
func PublishChainTxEvents(ctx context.Context, client tmclient.EventsClient, bus pubsub.Bus, mb module.BasicManager) (err error) {
|
||||
// Subscribe to transaction events
|
||||
txch, err := client.Subscribe(ctx, "txevents", "tm.event='Tx'", 100)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Unsubscribe from transaction events on function exit
|
||||
defer func() {
|
||||
err = client.UnsubscribeAll(ctx, "txevents")
|
||||
}()
|
||||
|
||||
// Use errgroup to manage concurrency
|
||||
g, ctx := errgroup.WithContext(ctx)
|
||||
|
||||
// Publish transaction events in a goroutine
|
||||
g.Go(func() error {
|
||||
var err error
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
break
|
||||
case ed := <-ch:
|
||||
switch evt := ed.Data.(type) {
|
||||
case tmtypes.EventDataTx:
|
||||
if !evt.Result.IsOK() {
|
||||
continue
|
||||
}
|
||||
// range over events, parse them using the basic manager and
|
||||
// send them to the pubsub bus
|
||||
for _, abciEv := range events {
|
||||
typedEvent, err := sdk.ParseTypedEvent(abciEv)
|
||||
if err != nil {
|
||||
return er
|
||||
}
|
||||
if err := bus.Publish(typedEvent); err != nil {
|
||||
bus.Close()
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
})
|
||||
|
||||
// Exit on error or context cancelation
|
||||
return g.Wait()
|
||||
}
|
||||
```
|
||||
|
||||
## References
|
||||
- [Publish Custom Events via a bus](https://github.com/ovrclk/akash/blob/90d258caeb933b611d575355b8df281208a214f8/events/publish.go#L19-L58)
|
||||
- [Consuming the events in `Client`](https://github.com/ovrclk/deploy/blob/bf6c633ab6c68f3026df59efd9982d6ca1bf0561/cmd/event-handlers.go#L57)
|
||||
@@ -6,8 +6,10 @@
|
||||
|
||||
## Status
|
||||
|
||||
> A decision may be "proposed" if the project stakeholders haven't agreed with it yet, or "accepted" once it is agreed. If a later ADR changes or reverses a decision, it may be marked as "deprecated" or "superseded" with a reference to its replacement.
|
||||
> {Deprecated|Proposed|Accepted} {Implemented|Not Implemented}
|
||||
{DRAFT | PROPOSED} Not Implemented
|
||||
|
||||
> Please have a look at the [PROCESS](./PROCESS.md#adr-status) page.
|
||||
> Use DRAFT if the ADR is in a draft stage (draft PR) or PROPOSED if it's in review.
|
||||
|
||||
|
||||
## Abstract
|
||||
|
||||
@@ -6,11 +6,11 @@ parent:
|
||||
|
||||
# Basics
|
||||
|
||||
This repository contains reference documentation on the basic concepts of the Cosmos SDK.
|
||||
This repository contains reference documentation on the basic concepts of the Cosmos SDK.
|
||||
|
||||
1. [Anatomy of an SDK Application](./app-anatomy.md)
|
||||
2. [Lifecycle of a transaction](./tx-lifecycle.md)
|
||||
3. [Accounts](./accounts.md)
|
||||
4. [Gas and Fees](./gas-fees.md)
|
||||
|
||||
After reading the basics, head on to the [Core Reference](../core/README.md) for more advanced material.
|
||||
After reading the basics, head on to the [Core Reference](../core/README.md) for more advanced material.
|
||||
|
||||
+36
-36
@@ -2,7 +2,7 @@
|
||||
order: 3
|
||||
-->
|
||||
|
||||
# Accounts
|
||||
# Accounts
|
||||
|
||||
This document describes the in-built accounts system of the Cosmos SDK. {synopsis}
|
||||
|
||||
@@ -12,9 +12,9 @@ This document describes the in-built accounts system of the Cosmos SDK. {synopsi
|
||||
|
||||
## Account Definition
|
||||
|
||||
In the Cosmos SDK, an *account* designates a pair of *public key* `PubKey` and *private key* `PrivKey`. The `PubKey` can be derived to generate various `Addresses`, which are used to identify users (among other parties) in the application. `Addresses` are also associated with [`message`s](../building-modules/messages-and-queries.md#messages) to identify the sender of the `message`. The `PrivKey` is used to generate [digital signatures](#signatures) to prove that an `Address` associated with the `PrivKey` approved of a given `message`.
|
||||
In the Cosmos SDK, an _account_ designates a pair of _public key_ `PubKey` and _private key_ `PrivKey`. The `PubKey` can be derived to generate various `Addresses`, which are used to identify users (among other parties) in the application. `Addresses` are also associated with [`message`s](../building-modules/messages-and-queries.md#messages) to identify the sender of the `message`. The `PrivKey` is used to generate [digital signatures](#signatures) to prove that an `Address` associated with the `PrivKey` approved of a given `message`.
|
||||
|
||||
To derive `PubKey`s and `PrivKey`s, the Cosmos SDK uses a standard called [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki). This standard defines how to build an HD wallet, where a wallet is a set of accounts. At the core of every account, there is a seed, which takes the form of a 12 or 24-words mnemonic. From this mnemonic, it is possible to derive any number of `PrivKey`s using one-way cryptographic function. Then, a `PubKey` can be derived from the `PrivKey`. Naturally, the mnemonic is the most sensitive information, as private keys can always be re-generated if the mnemonic is preserved.
|
||||
To derive `PubKey`s and `PrivKey`s, the Cosmos SDK uses a standard called [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki). This standard defines how to build an HD wallet, where a wallet is a set of accounts. At the core of every account, there is a seed, which takes the form of a 12 or 24-words mnemonic. From this mnemonic, it is possible to derive any number of `PrivKey`s using one-way cryptographic function. Then, a `PubKey` can be derived from the `PrivKey`. Naturally, the mnemonic is the most sensitive information, as private keys can always be re-generated if the mnemonic is preserved.
|
||||
|
||||
```
|
||||
Account 0 Account 1 Account 2
|
||||
@@ -56,65 +56,65 @@ To derive `PubKey`s and `PrivKey`s, the Cosmos SDK uses a standard called [BIP32
|
||||
+-------------------+
|
||||
```
|
||||
|
||||
In the Cosmos SDK, accounts are stored and managed via an object called a [`Keybase`](#keybase).
|
||||
In the Cosmos SDK, accounts are stored and managed via an object called a [`Keyring`](#keyring).
|
||||
|
||||
## Keybase
|
||||
## Keyring
|
||||
|
||||
A `Keybase` is an object that stores and manages accounts. In the Cosmos SDK, a `Keybase` implementation follows the `Keybase` interface:
|
||||
A `Keyring` is an object that stores and manages accounts. In the Cosmos SDK, a `Keyring` implementation follows the `Keyring` interface:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/crypto/keys/types.go#L13-L86
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/crypto/keyring/keyring.go#L50-L88
|
||||
|
||||
The default implementation of `Keybase` of the Cosmos SDK is `dbKeybase`.
|
||||
The default implementation of `Keyring` comes from the third-party [`99designs/keyring`](https://github.com/99designs/keyring) library.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/crypto/keys/keybase.go
|
||||
A few notes on the `Keyring` methods:
|
||||
|
||||
A few notes on the `Keybase` methods as implemented in `dbKeybase`:
|
||||
- `Sign(uid string, msg []byte) ([]byte, tmcrypto.PubKey, error)` strictly deals with the signature of the `message` bytes. Some preliminary work should be done beforehand to prepare and encode the `message` into a canonical `[]byte` form, and this is done in the `GetSignBytes` method. See an example of `message` preparation from the `x/bank` module. Note that signature verification is not implemented in the SDK by default. It is deferred to the [`anteHandler`](#antehandler).
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/x/bank/types/msgs.go#L51-L54
|
||||
- `NewAccount(uid, mnemonic, bip39Passwd, hdPath string, algo SignatureAlgo) (Info, error)` creates a new account based on the [`bip44 path`](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) and persists it on disk (note that the `PrivKey` is [encrypted with a passphrase before being persisted](https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/crypto/keys/mintkey/mintkey.go), it is **never stored unencrypted**). In the context of this method, the `account` and `address` parameters refer to the segment of the BIP44 derivation path (e.g. `0`, `1`, `2`, ...) used to derive the `PrivKey` and `PubKey` from the mnemonic (note that given the same mnemonic and `account`, the same `PrivKey` will be generated, and given the same `account` and `address`, the same `PubKey` and `Address` will be generated). Finally, note that the `NewAccount` method derives keys and addresses using the algorithm specified in the last argument `algo`. Currently, the SDK supports two public key algorithms:
|
||||
- `secp256k1`, as implemented in the [SDK's `crypto/keys/secp256k1` package](https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/crypto/keys/secp256k1/secp256k1.go),
|
||||
- `ed25519`, as implemented in the [SDK's `crypto/keys/ed25519` package](https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/crypto/keys/ed25519/ed25519.go).
|
||||
- `ExportPrivKeyArmor(uid, encryptPassphrase string) (armor string, err error)` exports a private key in ASCII-armored encrypted format, using the given passphrase. You can then either import it again into the keyring using the `ImportPrivKey(uid, armor, passphrase string)` function, or decrypt it into a raw private key using the `UnarmorDecryptPrivKey(armorStr string, passphrase string)` function.
|
||||
|
||||
- `Sign(name, passphrase string, msg []byte) ([]byte, crypto.PubKey, error)` strictly deals with the signature of the `message` bytes. Some preliminary work should be done beforehand to prepare and encode the `message` into a canonical `[]byte` form. See an example of `message` preparation from the `auth` module. Note that signature verification is not implemented in the SDK by default. It is deferred to the [`anteHandler`](#antehandler).
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/auth/types/txbuilder.go#L176-L209
|
||||
- `CreateMnemonic(name string, language Language, passwd string, algo SigningAlgo) (info Info, seed string, err error)` creates a new mnemonic and prints it in the logs, but it **does not persist it on disk**.
|
||||
- `CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd string, account uint32, index uint32) (Info, error)` creates a new account based on the [`bip44 path`](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) and persists it on disk (note that the `PrivKey` is [encrypted with a passphrase before being persisted](https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/crypto/keys/mintkey/mintkey.go), it is **never stored unencrypted**). In the context of this method, the `account` and `address` parameters refer to the segment of the BIP44 derivation path (e.g. `0`, `1`, `2`, ...) used to derive the `PrivKey` and `PubKey` from the mnemonic (note that given the same mnemonic and `account`, the same `PrivKey` will be generated, and given the same `account` and `address`, the same `PubKey` and `Address` will be generated). Finally, note that the `CreateAccount` method derives keys and addresses using `secp256k1` as implemented in the [Tendermint library](https://github.com/tendermint/tendermint/tree/bc572217c07b90ad9cee851f193aaa8e9557cbc7/crypto/secp256k1). As a result, it only works for creating account keys and addresses, not consensus keys. See [`Addresses`](#addresses) for more.
|
||||
|
||||
The current implementation of `dbKeybase` is basic and does not offer on-demand locking. If an instance of `dbKeybase` is created, the underlying `db` is locked meaning no other process can access it besides the one in which it was instantiated. This is the reason why the default SDK client uses another implementation of the `Keybase` interface called `lazyKeybase`:
|
||||
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/crypto/keys/lazy_keybase.go
|
||||
|
||||
`lazyKeybase` is simple wrapper around `dbKeybase` which locks the database only when operations are to be performed and unlocks it immediately after. With the `lazyKeybase`, it is possible for the [command-line interface](../interfaces/cli.md) to create a new account while the [rest server](../interfaces/rest.md) is running. It is also possible to pipe multiple CLI commands.
|
||||
Also see the [`Addresses`](#addresses) section for more information.
|
||||
|
||||
## Addresses and PubKeys
|
||||
|
||||
`Addresses` and `PubKey`s are both public information that identify actors in the application. There are 3 main types of `Addresses`/`PubKeys` available by default in the Cosmos SDK:
|
||||
|
||||
- Addresses and Keys for **accounts**, which identify users (e.g. the sender of a `message`). They are derived using the **`secp256k1`** curve.
|
||||
- Addresses and Keys for **validator operators**, which identify the operators of validators. They are derived using the **`secp256k1`** curve.
|
||||
- Addresses and Keys for **accounts**, which identify users (e.g. the sender of a `message`). They are derived using the **`secp256k1`** curve.
|
||||
- Addresses and Keys for **validator operators**, which identify the operators of validators. They are derived using the **`secp256k1`** curve.
|
||||
- Addresses and Keys for **consensus nodes**, which identify the validator nodes participating in consensus. They are derived using the **`ed25519`** curve.
|
||||
|
||||
| | Address bech32 Prefix | Pubkey bech32 Prefix | Curve | Address byte length | Pubkey byte length |
|
||||
|--------------------|-----------------------|----------------------|-------------|---------------------|--------------------|
|
||||
| ------------------ | --------------------- | -------------------- | ----------- | ------------------- | ------------------ |
|
||||
| Accounts | cosmos | cosmospub | `secp256k1` | `20` | `33` |
|
||||
| Validator Operator | cosmosvaloper | cosmosvaloperpub | `secp256k1` | `20` | `33` |
|
||||
| Consensus Nodes | cosmosvalcons | cosmosvalconspub | `ed25519` | `20` | `32` |
|
||||
| Consensus Nodes | cosmosvalcons | cosmosvalconspub | `ed25519` | `20` | `32` |
|
||||
|
||||
### PubKeys
|
||||
|
||||
`PubKey`s used in the Cosmos SDK follow the `Pubkey` interface defined in tendermint's `crypto` package:
|
||||
`PubKey`s used in the Cosmos SDK are Protobuf messages and extend the `Pubkey` interface defined in tendermint's `crypto` package:
|
||||
|
||||
+++ https://github.com/tendermint/tendermint/blob/bc572217c07b90ad9cee851f193aaa8e9557cbc7/crypto/crypto.go#L22-L27
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/crypto/types/types.go#L8-L13
|
||||
|
||||
For `secp256k1` keys, the actual implementation can be found [here](https://github.com/tendermint/tendermint/blob/bc572217c07b90ad9cee851f193aaa8e9557cbc7/crypto/secp256k1/secp256k1.go#L140). For `ed25519` keys, it can be found [here](https://github.com/tendermint/tendermint/blob/bc572217c07b90ad9cee851f193aaa8e9557cbc7/crypto/ed25519/ed25519.go#L135).
|
||||
+++ https://github.com/tendermint/tendermint/blob/01c32c62e8840d812359c9e87e9c575aa67acb09/crypto/crypto.go#L22-L28
|
||||
|
||||
Note that in the Cosmos SDK, `Pubkeys` are not manipulated in their raw form. Instead, they are double encoded using [`Amino`](../core/encoding.md#amino) and [`bech32`](https://en.bitcoin.it/wiki/Bech32). In the SDK is done by first calling the `Bytes()` method on the raw `Pubkey` (which applies amino encoding), and then the `ConvertAndEncode` method of `bech32`.
|
||||
- For `secp256k1` keys, the actual implementation can be found [here](https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/crypto/keys/secp256k1/secp256k1.go).
|
||||
- For `ed25519` keys, it can be found [here](https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/crypto/keys/ed25519/ed25519.go).
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/address.go#L579-L729
|
||||
In both case, the actual key (as raw bytes) is the compressed form of the pubkey. The first byte is a `0x02` byte if the `y`-coordinate is the lexicographically largest of the two associated with the `x`-coordinate. Otherwise the first byte is a `0x03`. This prefix is followed with the `x`-coordinate.
|
||||
|
||||
Note that in the Cosmos SDK, `Pubkeys` are not manipulated in their raw bytes form. Instead, they are encoded to string using [`Amino`](../core/encoding.md#amino) and [`bech32`](https://en.bitcoin.it/wiki/Bech32). In the SDK, it is done by first calling the `Bytes()` method on the raw `Pubkey` (which applies amino encoding), and then the `ConvertAndEncode` method of `bech32`.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/types/address.go#L579-L729
|
||||
|
||||
### Addresses
|
||||
|
||||
The Cosmos SDK comes by default with 3 types of addresses:
|
||||
|
||||
- `AccAddress` for accounts.
|
||||
- `ValAddress` for validator operators.
|
||||
- `ConsAddress` for validator nodes.
|
||||
- `ValAddress` for validator operators.
|
||||
- `ConsAddress` for validator nodes.
|
||||
|
||||
Each of these address types are an alias for an hex-encoded `[]byte` array of length 20. Here is the standard way to obtain an address `aa` from a `Pubkey pub`:
|
||||
|
||||
@@ -124,12 +124,12 @@ aa := sdk.AccAddress(pub.Address().Bytes())
|
||||
|
||||
These addresses implement the `Address` interface:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/address.go#L71-L80
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/types/address.go#L73-L82
|
||||
|
||||
Of note, the `Marhsal()` and `Bytes()` method both return the same raw `[]byte` form of the address, the former being needed for Protobuff compatibility. Also, the `String()` method is used to return the `bech32` encoded form of the address, which should be the only address format with which end-user interract. Next is an example:
|
||||
Of note, the `Marshal()` and `Bytes()` method both return the same raw `[]byte` form of the address, the former being needed for Protobuf compatibility. Also, the `String()` method is used to return the `bech32` encoded form of the address, which should be the only address format with which end-user interract. Here is an example:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/address.go#L229-L243
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/types/address.go#L232-L246
|
||||
|
||||
## Next {hide}
|
||||
|
||||
Learn about [gas and fees](./gas-fees.md) {hide}
|
||||
Learn about [gas and fees](./gas-fees.md) {hide}
|
||||
|
||||
+106
-66
@@ -2,7 +2,7 @@
|
||||
order: 1
|
||||
-->
|
||||
|
||||
# Anatomy of an SDK Application
|
||||
# Anatomy of an SDK Application
|
||||
|
||||
This document describes the core parts of a Cosmos SDK application. Throughout the document, a placeholder application named `app` will be used. {synopsis}
|
||||
|
||||
@@ -44,53 +44,54 @@ In general, the core of the state-machine is defined in a file called `app.go`.
|
||||
|
||||
The first thing defined in `app.go` is the `type` of the application. It is generally comprised of the following parts:
|
||||
|
||||
- **A reference to [`baseapp`](../core/baseapp.md).** The custom application defined in `app.go` is an extension of `baseapp`. When a transaction is relayed by Tendermint to the application, `app` uses `baseapp`'s methods to route them to the appropriate module. `baseapp` implements most of the core logic for the application, including all the [ABCI methods](https://tendermint.com/docs/spec/abci/abci.html#overview) and the [routing logic](../core/baseapp.md#routing).
|
||||
- **A list of store keys**. The [store](../core/store.md), which contains the entire state, is implemented as a [`multistore`](../core/store.md#multistore) (i.e. a store of stores) in the Cosmos SDK. Each module uses one or multiple stores in the multistore to persist their part of the state. These stores can be accessed with specific keys that are declared in the `app` type. These keys, along with the `keepers`, are at the heart of the [object-capabilities model](../core/ocap.md) of the Cosmos SDK.
|
||||
- **A list of module's `keeper`s.** Each module defines an abstraction called [`keeper`](../building-modules/keeper.md), which handles reads and writes for this module's store(s). The `keeper`'s methods of one module can be called from other modules (if authorized), which is why they are declared in the application's type and exported as interfaces to other modules so that the latter can only access the authorized functions.
|
||||
- **A reference to a [`codec`](../core/encoding.md).** The application's `codec` is used to serialize and deserialize data structures in order to store them, as stores can only persist `[]bytes`. The `codec` must be deterministic. The default codec is [amino](../core/encoding.md).
|
||||
- **A reference to a [module manager](../building-modules/module-manager.md#manager)** and a [basic module manager](../building-modules/module-manager.md#basicmanager). The module manager is an object that contains a list of the application's module. It facilitates operations related to these modules, like registering [`routes`](../core/baseapp.md#routing), [query routes](../core/baseapp.md#query-routing) or setting the order of execution between modules for various functions like [`InitChainer`](#initchainer), [`BeginBlocker` and `EndBlocker`](#beginblocker-and-endblocker).
|
||||
- **A reference to [`baseapp`](../core/baseapp.md).** The custom application defined in `app.go` is an extension of `baseapp`. When a transaction is relayed by Tendermint to the application, `app` uses `baseapp`'s methods to route them to the appropriate module. `baseapp` implements most of the core logic for the application, including all the [ABCI methods](https://tendermint.com/docs/spec/abci/abci.html#overview) and the [routing logic](../core/baseapp.md#routing).
|
||||
- **A list of store keys**. The [store](../core/store.md), which contains the entire state, is implemented as a [`multistore`](../core/store.md#multistore) (i.e. a store of stores) in the Cosmos SDK. Each module uses one or multiple stores in the multistore to persist their part of the state. These stores can be accessed with specific keys that are declared in the `app` type. These keys, along with the `keepers`, are at the heart of the [object-capabilities model](../core/ocap.md) of the Cosmos SDK.
|
||||
- **A list of module's `keeper`s.** Each module defines an abstraction called [`keeper`](../building-modules/keeper.md), which handles reads and writes for this module's store(s). The `keeper`'s methods of one module can be called from other modules (if authorized), which is why they are declared in the application's type and exported as interfaces to other modules so that the latter can only access the authorized functions.
|
||||
- **A reference to an [`appCodec`](../core/encoding.md).** The application's `appCodec` is used to serialize and deserialize data structures in order to store them, as stores can only persist `[]bytes`. The default codec is [Protocol Buffers](../core/encoding.md).
|
||||
- **A reference to a [`legacyAmino`](../core/encoding.md) codec.** Some parts of the SDK have not been migrated to use the `appCodec` above, and are still hardcoded to use Amino. Other parts explicity use Amino for backwards compatibility. For these reasons, the application still holds a reference to the legacy Amino codec. Please note that the Amino codec will be removed from the SDK in the upcoming releases.
|
||||
- **A reference to a [module manager](../building-modules/module-manager.md#manager)** and a [basic module manager](../building-modules/module-manager.md#basicmanager). The module manager is an object that contains a list of the application's module. It facilitates operations related to these modules, like registering [`routes`](../core/baseapp.md#routing), [gRPC query services](../core/baseapp.md#grpc-query-services) and [legacy Tendermint query routes](../core/baseapp.md#legacy-query-routing) or setting the order of execution between modules for various functions like [`InitChainer`](#initchainer), [`BeginBlocker` and `EndBlocker`](#beginblocker-and-endblocker).
|
||||
|
||||
See an example of application type definition from [`gaia`](https://github.com/cosmos/gaia)
|
||||
See an example of application type definition from `simapp`, the SDK's own app used for demo and testing purposes:
|
||||
|
||||
+++ https://github.com/cosmos/gaia/blob/5bc422e6868d04747e50b467e8eeb31ae2fe98a3/app/app.go#L87-L115
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/simapp/app.go#L140-L179
|
||||
|
||||
### Constructor Function
|
||||
|
||||
This function constructs a new application of the type defined in the section above. It must fulfill the `AppCreator` signature in order to be used in the [`start` command](../core/node.md#start-command) of the application's daemon command.
|
||||
This function constructs a new application of the type defined in the section above. It must fulfill the `AppCreator` signature in order to be used in the [`start` command](../core/node.md#start-command) of the application's daemon command.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/server/constructors.go#L20
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/server/types/app.go#L42-L44
|
||||
|
||||
Here are the main actions performed by this function:
|
||||
|
||||
- Instantiate a new [`codec`](../core/encoding.md) and initialize the `codec` of each of the application's module using the [basic manager](../building-modules/module-manager.md#basicmanager)
|
||||
- Instantiate a new application with a reference to a `baseapp` instance, a codec and all the appropriate store keys.
|
||||
- Instantiate all the [`keeper`s](#keeper) defined in the application's `type` using the `NewKeeper` function of each of the application's modules. Note that `keepers` must be instantiated in the correct order, as the `NewKeeper` of one module might require a reference to another module's `keeper`.
|
||||
- Instantiate the application's [module manager](../building-modules/module-manager.md#manager) with the [`AppModule`](#application-module-interface) object of each of the application's modules.
|
||||
- With the module manager, initialize the application's [`routes`](../core/baseapp.md#routing) and [query routes](../core/baseapp.md#query-routing). When a transaction is relayed to the application by Tendermint via the ABCI, it is routed to the appropriate module's [`handler`](#handler) using the routes defined here. Likewise, when a query is received by the application, it is routed to the appropriate module's [`querier`](#querier) using the query routes defined here.
|
||||
- With the module manager, register the [application's modules' invariants](../building-modules/invariants.md). Invariants are variables (e.g. total supply of a token) that are evaluated at the end of each block. The process of checking invariants is done via a special module called the [`InvariantsRegistry`](../building-modules/invariants.md#invariant-registry). The value of the invariant should be equal to a predicted value defined in the module. Should the value be different than the predicted one, special logic defined in the invariant registry will be triggered (usually the chain is halted). This is useful to make sure no critical bug goes unnoticed and produces long-lasting effects that would be hard to fix.
|
||||
- With the module manager, set the order of execution between the `InitGenesis`, `BegingBlocker` and `EndBlocker` functions of each of the [application's modules](#application-module-interface). Note that not all modules implement these functions.
|
||||
- Instantiate all the [`keeper`s](#keeper) defined in the application's `type` using the `NewKeeper` function of each of the application's modules. Note that `keepers` must be instantiated in the correct order, as the `NewKeeper` of one module might require a reference to another module's `keeper`.
|
||||
- Instantiate the application's [module manager](../building-modules/module-manager.md#manager) with the [`AppModule`](#application-module-interface) object of each of the application's modules.
|
||||
- With the module manager, initialize the application's [`routes`](../core/baseapp.md#routing), [`gRPC query services`](../core/baseapp.md#grpc-query-services) and [`legacy query routes`](../core/baseapp.md#query-routing). When a transaction is relayed to the application by Tendermint via the ABCI, it is routed to the appropriate module's [`handler`](#handler) using the `routes` defined here. Likewise, when a gRPC request is received by the application, it is routed to the appropriate module's [`gRPC query service`](#grpc-query-services) using the gRPC routes defined here. The SDK still supports legacy Tendermint queries, and these queries are routes using the `legacy query routes`.
|
||||
- With the module manager, register the [application's modules' invariants](../building-modules/invariants.md). Invariants are variables (e.g. total supply of a token) that are evaluated at the end of each block. The process of checking invariants is done via a special module called the [`InvariantsRegistry`](../building-modules/invariants.md#invariant-registry). The value of the invariant should be equal to a predicted value defined in the module. Should the value be different than the predicted one, special logic defined in the invariant registry will be triggered (usually the chain is halted). This is useful to make sure no critical bug goes unnoticed and produces long-lasting effects that would be hard to fix.
|
||||
- With the module manager, set the order of execution between the `InitGenesis`, `BegingBlocker` and `EndBlocker` functions of each of the [application's modules](#application-module-interface). Note that not all modules implement these functions.
|
||||
- Set the remainer of application's parameters:
|
||||
+ [`InitChainer`](#initchainer): used to initialize the application when it is first started.
|
||||
+ [`BeginBlocker`, `EndBlocker`](#beginblocker-and-endlbocker): called at the beginning and the end of every block).
|
||||
+ [`anteHandler`](../core/baseapp.md#antehandler): used to handle fees and signature verification.
|
||||
- Mount the stores.
|
||||
- Return the application.
|
||||
- [`InitChainer`](#initchainer): used to initialize the application when it is first started.
|
||||
- [`BeginBlocker`, `EndBlocker`](#beginblocker-and-endlbocker): called at the beginning and the end of every block).
|
||||
- [`anteHandler`](../core/baseapp.md#antehandler): used to handle fees and signature verification.
|
||||
- Mount the stores.
|
||||
- Return the application.
|
||||
|
||||
Note that this function only creates an instance of the app, while the actual state is either carried over from the `~/.appd/data` folder if the node is restarted, or generated from the genesis file if the node is started for the first time.
|
||||
|
||||
See an example of application constructor from [`gaia`](https://github.com/cosmos/gaia):
|
||||
See an example of application constructor from `simapp`:
|
||||
|
||||
+++ https://github.com/cosmos/gaia/blob/f41a660cdd5bea173139965ade55bd25d1ee3429/app/app.go#L110-L222
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/simapp/app.go#L190-L427
|
||||
|
||||
### InitChainer
|
||||
|
||||
The `InitChainer` is a function that initializes the state of the application from a genesis file (i.e. token balances of genesis accounts). It is called when the application receives the `InitChain` message from the Tendermint engine, which happens when the node is started at `appBlockHeight == 0` (i.e. on genesis). The application must set the `InitChainer` in its [constructor](#constructor-function) via the [`SetInitChainer`](https://godoc.org/github.com/cosmos/cosmos-sdk/baseapp#BaseApp.SetInitChainer) method.
|
||||
The `InitChainer` is a function that initializes the state of the application from a genesis file (i.e. token balances of genesis accounts). It is called when the application receives the `InitChain` message from the Tendermint engine, which happens when the node is started at `appBlockHeight == 0` (i.e. on genesis). The application must set the `InitChainer` in its [constructor](#constructor-function) via the [`SetInitChainer`](https://godoc.org/github.com/cosmos/cosmos-sdk/baseapp#BaseApp.SetInitChainer) method.
|
||||
|
||||
In general, the `InitChainer` is mostly composed of the [`InitGenesis`](../building-modules/genesis.md#initgenesis) function of each of the application's modules. This is done by calling the `InitGenesis` function of the module manager, which in turn will call the `InitGenesis` function of each of the modules it contains. Note that the order in which the modules' `InitGenesis` functions must be called has to be set in the module manager using the [module manager's](../building-modules/module-manager.md) `SetOrderInitGenesis` method. This is done in the [application's constructor](#application-constructor), and the `SetOrderInitGenesis` has to be called before the `SetInitChainer`.
|
||||
|
||||
See an example of an `InitChainer` from [`gaia`](https://github.com/cosmos/gaia):
|
||||
|
||||
+++ https://github.com/cosmos/gaia/blob/f41a660cdd5bea173139965ade55bd25d1ee3429/app/app.go#L235-L239
|
||||
See an example of an `InitChainer` from `simapp`:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/simapp/app.go#L450-L455
|
||||
|
||||
### BeginBlocker and EndBlocker
|
||||
|
||||
@@ -98,48 +99,58 @@ The SDK offers developers the possibility to implement automatic execution of co
|
||||
|
||||
In general, the `BeginBlocker` and `EndBlocker` functions are mostly composed of the [`BeginBlock` and `EndBlock`](../building-modules/beginblock-endblock.md) functions of each of the application's modules. This is done by calling the `BeginBlock` and `EndBlock` functions of the module manager, which in turn will call the `BeginBLock` and `EndBlock` functions of each of the modules it contains. Note that the order in which the modules' `BegingBlock` and `EndBlock` functions must be called has to be set in the module manager using the `SetOrderBeginBlock` and `SetOrderEndBlock` methods respectively. This is done via the [module manager](../building-modules/module-manager.md) in the [application's constructor](#application-constructor), and the `SetOrderBeginBlock` and `SetOrderEndBlock` methods have to be called before the `SetBeginBlocker` and `SetEndBlocker` functions.
|
||||
|
||||
As a sidenote, it is important to remember that application-specific blockchains are deterministic. Developers must be careful not to introduce non-determinism in `BeginBlocker` or `EndBlocker`, and must also be careful not to make them too computationally expensive, as [gas](./gas-fees.md) does not constrain the cost of `BeginBlocker` and `EndBlocker` execution.
|
||||
As a sidenote, it is important to remember that application-specific blockchains are deterministic. Developers must be careful not to introduce non-determinism in `BeginBlocker` or `EndBlocker`, and must also be careful not to make them too computationally expensive, as [gas](./gas-fees.md) does not constrain the cost of `BeginBlocker` and `EndBlocker` execution.
|
||||
|
||||
See an example of `BeginBlocker` and `EndBlocker` functions from [`gaia`](https://github.com/cosmos/gaia)
|
||||
See an example of `BeginBlocker` and `EndBlocker` functions from `simapp`
|
||||
|
||||
+++ https://github.com/cosmos/gaia/blob/f41a660cdd5bea173139965ade55bd25d1ee3429/app/app.go#L224-L232
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/simapp/app.go#L440-L448
|
||||
|
||||
### Register Codec
|
||||
|
||||
The `MakeCodec` function is the last important function of the `app.go` file. The goal of this function is to instantiate a [codec `cdc`](../core/encoding.md) (e.g. amino) initialize the codec of the SDK and each of the application's modules using the `RegisterLegacyAminoCodec` function.
|
||||
The `EncodingConfig` structure is the last important part of the `app.go` file. The goal of this structure is to define the codecs that will be used throughout the app.
|
||||
|
||||
To register the application's modules, the `MakeCodec` function calls `RegisterLegacyAminoCodec` on `ModuleBasics`. `ModuleBasics` is a [basic manager](../building-modules/module-manager.md#basicmanager) which lists all of the application's modules. It is instanciated in the `init()` function, and only serves to easily register non-dependant elements of application's modules (such as codec). To learn more about the basic module manager, click [here](../building-modules/module-manager.md#basicmanager).
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/simapp/params/encoding.go#L9-L16
|
||||
|
||||
See an example of a `MakeCodec` from [`gaia`](https://github.com/cosmos/gaia):
|
||||
|
||||
+++ https://github.com/cosmos/gaia/blob/f41a660cdd5bea173139965ade55bd25d1ee3429/app/app.go#L64-L70
|
||||
Here are descriptions of what each of the four fields means:
|
||||
|
||||
- `InterfaceRegistry`: The `InterfaceRegistry` is used by the Protobuf codec to handle interfaces, which are encoded and decoded (we also say "unpacked") using [`google.protobuf.Any`](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto). `Any` could be thought as a struct which contains a `type_url` (the concrete type of the interface) and a `value` (its encoded bytes). `InterfaceRegistry` provides a mechanism for registering interfaces and implementations that can be safely unpacked from `Any`. Each of the application's modules implements the `RegisterInterfaces` method, which can be used to register the module's own interfaces and implementations.
|
||||
- To go more into details, the SDK uses an implementation of the Protobuf specification called [`gogoprotobuf`](https://github.com/gogo/protobuf). By default, the [gogo protobuf implementation of `Any`](https://godoc.org/github.com/gogo/protobuf/types) uses [global type registration](https://github.com/gogo/protobuf/blob/master/proto/properties.go#L540) to decode values packed in `Any` into concrete Go types. This introduces a vulnerability where any malicious module in the dependency tree could registry a type with the global protobuf registry and cause it to be loaded and unmarshaled by a transaction that referenced it in the `type_url` field. For more information, please refer to [ADR-019](../architecture/adr-019-protobuf-state-encoding.md).
|
||||
- `Marshaler`: The `Marshaler` is the default codec used throughout the SDK. It is composed of a `BinaryMarshaler` used to encode and decode state, and a `JSONMarshaler` used to output data to the users (for example in the [CLI](#cli)). By default, the SDK uses Protobuf as `Marshaler`.
|
||||
- `TxConfig`: `TxConfig` defines an interface a client can utilize to generate an application-defined concrete transaction type. Currently, the SDK handles two transaction types: `SIGN_MODE_DIRECT` (which uses Protobuf binary as over-the-wire encoding) and `SIGN_MODE_LEGACY_AMINO_JSON` (which depends on Amino). Read more about transactions [here](../core/transactions.md).
|
||||
- `Amino`: Some legacy parts of the SDK still use Amino for backwards-compatibility. Each module exposes a `RegisterLegacyAmino` method to register the module's specific types within Amino. This `Amino` codec should not be used by app developers anymore, and will be removed in future releases.
|
||||
|
||||
The SDK exposes a `MakeCodecs` function used to create a `EncodingConfig`. It uses Protobuf as default `Marshaler`, and passes it down to the app's `appCodec` field. It also instantiates a legacy `Amino` codec inside the app's `legacyAmino` field.
|
||||
|
||||
See an example of a `MakeCodecs` from `simapp`:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/simapp/app.go#L429-L435
|
||||
|
||||
## Modules
|
||||
|
||||
[Modules](../building-modules/intro.md) are the heart and soul of SDK applications. They can be considered as state-machines within the state-machine. When a transaction is relayed from the underlying Tendermint engine via the ABCI to the application, it is routed by [`baseapp`](../core/baseapp.md) to the appropriate module in order to be processed. This paradigm enables developers to easily build complex state-machines, as most of the modules they need often already exist. For developers, most of the work involved in building an SDK application revolves around building custom modules required by their application that do not exist yet, and integrating them with modules that do already exist into one coherent application. In the application directory, the standard practice is to store modules in the `x/` folder (not to be confused with the SDK's `x/` folder, which contains already-built modules).
|
||||
[Modules](../building-modules/intro.md) are the heart and soul of SDK applications. They can be considered as state-machines within the state-machine. When a transaction is relayed from the underlying Tendermint engine via the ABCI to the application, it is routed by [`baseapp`](../core/baseapp.md) to the appropriate module in order to be processed. This paradigm enables developers to easily build complex state-machines, as most of the modules they need often already exist. For developers, most of the work involved in building an SDK application revolves around building custom modules required by their application that do not exist yet, and integrating them with modules that do already exist into one coherent application. In the application directory, the standard practice is to store modules in the `x/` folder (not to be confused with the SDK's `x/` folder, which contains already-built modules).
|
||||
|
||||
### Application Module Interface
|
||||
|
||||
Modules must implement [interfaces](../building-modules/module-manager.md#application-module-interfaces) defined in the Cosmos SDK, [`AppModuleBasic`](../building-modules/module-manager.md#appmodulebasic) and [`AppModule`](../building-modules/module-manager.md#appmodule). The former implements basic non-dependant elements of the module, such as the `codec`, while the latter handles the bulk of the module methods (including methods that require references to other modules' `keeper`s). Both the `AppModule` and `AppModuleBasic` types are defined in a file called `./module.go`.
|
||||
Modules must implement [interfaces](../building-modules/module-manager.md#application-module-interfaces) defined in the Cosmos SDK, [`AppModuleBasic`](../building-modules/module-manager.md#appmodulebasic) and [`AppModule`](../building-modules/module-manager.md#appmodule). The former implements basic non-dependant elements of the module, such as the `codec`, while the latter handles the bulk of the module methods (including methods that require references to other modules' `keeper`s). Both the `AppModule` and `AppModuleBasic` types are defined in a file called `./module.go`.
|
||||
|
||||
`AppModule` exposes a collection of useful methods on the module that facilitates the composition of modules into a coherent application. These methods are are called from the `module manager`(../building-modules/module-manager.md#manager), which manages the application's collection of modules.
|
||||
`AppModule` exposes a collection of useful methods on the module that facilitates the composition of modules into a coherent application. These methods are are called from the `module manager`(../building-modules/module-manager.md#manager), which manages the application's collection of modules.
|
||||
|
||||
### Message Types
|
||||
|
||||
[`Message`s](../building-modules/messages-and-queries.md#messages) are objects defined by each module that implement the [`message`](../building-modules/messages-and-queries.md#messages) interface. Each [`transaction`](../core/transactions.md) contains one or multiple `messages`.
|
||||
[`Message`s](../building-modules/messages-and-queries.md#messages) are objects defined by each module that implement the [`message`](../building-modules/messages-and-queries.md#messages) interface. Each [`transaction`](../core/transactions.md) 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 message(s) contained in the transaction.
|
||||
3. With the `Type()` method of the `message`, `baseapp` is able to route it to the appropriate module's [`handler`](#handler) in order for it to be processed.
|
||||
4. If the message is successfully processed, the state is updated.
|
||||
2. Then, it verifies a few things about the transaction like [fee payment and signatures](#gas-fees.md#antehandler) before extracting the message(s) contained in the transaction.
|
||||
3. With the `Type()` method of the `message`, `baseapp` is able to route it to the appropriate module's [`handler`](#handler) in order for it to be processed.
|
||||
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).
|
||||
|
||||
Module developers create custom message types when they build their own module. The general practice is to prefix the type declaration of the message with `Msg`. For example, the message type `MsgSend` allows users to transfer tokens:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/bank/internal/types/msgs.go#L10-L15
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/proto/cosmos/bank/v1beta1/tx.proto#L10-L19
|
||||
|
||||
It is processed by the `handler` of the `bank` module, which ultimately calls the `keeper` of the `auth` module in order to update the state.
|
||||
|
||||
@@ -150,25 +161,36 @@ The [`handler`](../building-modules/handler.md) refers to the part of the module
|
||||
The `handler` of a module is generally defined in a file called `handler.go` and consists of:
|
||||
|
||||
- A **switch function** `NewHandler` to route the message to the appropriate `handler` function. This function returns a `handler` function, and is registered in the [`AppModule`](#application-module-interface) to be used in the application's module manager to initialize the [application's router](../core/baseapp.md#routing). Next is an example of such a switch from the [nameservice tutorial](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice)
|
||||
+++ https://github.com/cosmos/sdk-tutorials/blob/master/nameservice/x/nameservice/handler.go#L12-L26
|
||||
- **One handler function for each message type defined by the module**. Developers write the message processing logic in these functions. This generally involves doing stateful checks to ensure the message is valid and calling [`keeper`](#keeper)'s methods to update the state.
|
||||
+++ https://github.com/cosmos/sdk-tutorials/blob/master/nameservice/x/nameservice/handler.go#L12-L26
|
||||
- **One handler function for each message type defined by the module**. Developers write the message processing logic in these functions. This generally involves doing stateful checks to ensure the message is valid and calling [`keeper`](#keeper)'s methods to update the state.
|
||||
|
||||
Handler functions return a result of type `sdk.Result`, which informs the application on whether the message was successfully processed:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/result.go#L15-L40
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/types/result.go#L15-L40
|
||||
|
||||
### Querier
|
||||
### gRPC Query Services
|
||||
|
||||
[`Queriers`](../building-modules/querier.md) are very similar to `handlers`, except they serve user queries to the state as opposed to processing transactions. A [query](../building-modules/messages-and-queries.md#queries) is initiated from an [interface](#interfaces) by an end-user who provides a `queryRoute` and some `data`. The query is then routed to the correct application's `querier` by `baseapp`'s `handleQueryCustom` method using `queryRoute`:
|
||||
gRPC query services are introduced in the v0.40 Stargate release. They allow users to query the state using [gRPC](https://grpc.io). They are enabled by default, and can be configued under the `grpc.enable` and `grpc.address` fields inside `app.toml`.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/baseapp/abci.go#L395-L453
|
||||
gRPC query services are defined in the module's Protobuf definition, specifically inside `query.proto`. The `query.proto` definition file exposes a single `Query` [Protobuf service](https://developers.google.com/protocol-buffers/docs/proto#services). Each gRPC query endpoint corresponds to a service method, starting with the `rpc` keyword, inside the `Query` service.
|
||||
|
||||
The `Querier` of a module is defined in a file called `querier.go`, and consists of:
|
||||
Protobuf generates a `QueryServer` interface for each module, containing all the service methods. A module's [`keeper`](#keeper) then needs to implement this `QueryServer` interface, by providing the concrete implementation of each service method. This concrete implementation is the handler of the corresponding gRPC query endpoint.
|
||||
|
||||
Finally, each module should also implement the `RegisterQueryService` method as part of the [`AppModule` interface](#application-module-interface). This method should call the `RegisterQueryServer` function provided by the generated Protobuf code.
|
||||
|
||||
### Legacy Querier
|
||||
|
||||
Legacy queriers were queriers used before the introduction of Protobuf and gRPC in the SDK. They are present for existing modules, but will be deprecated in a future release of the SDK. If you are developing new modules, gRPC query services should be preferred, and you only need to implement the `LegacyQuerierHandler` interface if you wish to use legacy queriers.
|
||||
|
||||
[`Legacy queriers`](../building-modules/query-services.md#legacy-queriers) are very similar to `handlers`, except they serve user queries to the state as opposed to processing transactions. A [query](../building-modules/messages-and-queries.md#queries) is initiated from an [interface](#application-interface) by an end-user who provides a `queryRoute` and some `data`. The query is then routed to the correct application's `querier` by `baseapp`'s `handleQueryCustom` method using `queryRoute`:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/baseapp/abci.go#L388-L418
|
||||
|
||||
The `Querier` of a module is defined in a file called `keeper/querier.go`, and consists of:
|
||||
|
||||
- A **switch function** `NewQuerier` to route the query to the appropriate `querier` function. This function returns a `querier` function, and is is registered in the [`AppModule`](#application-module-interface) to be used in the application's module manager to initialize the [application's query router](../core/baseapp.md#query-routing). See an example of such a switch from the [nameservice tutorial](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice):
|
||||
+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/internal/keeper/querier.go#L19-L32
|
||||
- **One querier function for each data type defined by the module that needs to be queryable**. Developers write the query processing logic in these functions. This generally involves calling [`keeper`](#keeper)'s methods to query the state and marshalling it to JSON.
|
||||
|
||||
+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/internal/keeper/querier.go#L19-L32
|
||||
- **One querier function for each data type defined by the module that needs to be queryable**. Developers write the query processing logic in these functions. This generally involves calling [`keeper`](#keeper)'s methods to query the state and marshalling it to JSON.
|
||||
|
||||
### Keeper
|
||||
|
||||
@@ -184,36 +206,54 @@ The `keeper` type definition generally consists of:
|
||||
|
||||
Along with the type definition, the next important component of the `keeper.go` file is the `keeper`'s constructor function, `NewKeeper`. This function instantiates a new `keeper` of the type defined above, with a `codec`, store `keys` and potentially references to other modules' `keeper`s as parameters. The `NewKeeper` function is called from the [application's constructor](#constructor-function). The rest of the file defines the `keeper`'s methods, primarily getters and setters.
|
||||
|
||||
### Command-Line and REST Interfaces
|
||||
### Command-Line, gRPC Services and REST Interfaces
|
||||
|
||||
Each module defines command-line commands and REST routes to be exposed to end-user via the [application's interfaces](#application-interfaces). This enables end-users to create messages of the types defined in the module, or to query the subset of the state managed by the module.
|
||||
Each module defines command-line commands, gRPC services and REST routes to be exposed to end-user via the [application's interfaces](#application-interfaces). This enables end-users to create messages of the types defined in the module, or to query the subset of the state managed by the module.
|
||||
|
||||
#### CLI
|
||||
|
||||
Generally, the [commands related to a module](../building-modules/module-interfaces.md#cli) are defined in a folder called `client/cli` in the module's folder. The CLI divides commands in two category, transactions and queries, defined in `client/cli/tx.go` and `client/cli/query.go` respectively. Both commands are built on top of the [Cobra Library](https://github.com/spf13/cobra):
|
||||
|
||||
- Transactions commands let users generate new transactions so that they can be included in a block and eventually update the state. One command should be created for each [message type](#message-types) defined in the module. The command calls the constructor of the message with the parameters provided by the end-user, and wraps it into a transaction. The SDK handles signing and the addition of other transaction metadata.
|
||||
- Queries let users query the subset of the state defined by the module. Query commands forward queries to the [application's query router](../core/baseapp.md#query-routing), which routes them to the appropriate [querier](#querier) the `queryRoute` parameter supplied.
|
||||
- Transactions commands let users generate new transactions so that they can be included in a block and eventually update the state. One command should be created for each [message type](#message-types) defined in the module. The command calls the constructor of the message with the parameters provided by the end-user, and wraps it into a transaction. The SDK handles signing and the addition of other transaction metadata.
|
||||
- Queries let users query the subset of the state defined by the module. Query commands forward queries to the [application's query router](../core/baseapp.md#query-routing), which routes them to the appropriate [querier](#querier) the `queryRoute` parameter supplied.
|
||||
|
||||
#### REST
|
||||
#### gRPC
|
||||
|
||||
The [module's REST interface](../building-modules/module-interfaces.md#rest) lets users generate transactions and query the state through REST calls to the application's [light client daemon](../core/node.md#lcd) (LCD). REST routes are defined in a file `client/rest/rest.go`, which is composed of:
|
||||
[gRPC](https://grpc.io) is a modern open source high performance RPC framework that has support in multiple languages. It is the recommended way for external clients (such as wallets, browsers and other backend services) to interact with a node.
|
||||
|
||||
Each module can expose gRPC endpoints, called [service methods](https://grpc.io/docs/what-is-grpc/core-concepts/#service-definition) and are defined in the [module's Protobuf `query.proto` file](#grpc-query-services). A service method is defined by its name, input arguments and output response. The module then needs to:
|
||||
|
||||
- define a `RegisterGRPCRoutes` method on `AppModuleBasic` to wire the client gRPC requests to the correct handler inside the module.
|
||||
- for each service method, define a corresponding handler. The handler implements the core logic necessary to serve the gRPC request, and is located in the `keeper/grpc_query.go` file.
|
||||
|
||||
#### gRPC-gateway REST Endpoints
|
||||
|
||||
Some external clients may not wish to use gRPC. The SDK provides in this case a gRPC gateway service, which exposes each gRPC service as a correspoding REST endpoint. Please refer to the [grpc-gateway](https://grpc-ecosystem.github.io/grpc-gateway/) documentation to learn more.
|
||||
|
||||
The REST endpoints are defined in the Protobuf files, along with the gRPC services, using Protobuf annotations. Modules that want to expose REST queries should add `google.api.http` annotations to their `rpc` methods. By default, all REST endpoints defined in the SDK have an URL starting with the `/cosmos/` prefix.
|
||||
|
||||
The SDK also provides a development endpoint to generate [Swagger](https://swagger.io/) definition files for these REST endpoints. This endpoint can be enabled inside the `app.toml` config file, under the `api.swagger` key.
|
||||
|
||||
#### Legacy API REST Endpoints
|
||||
|
||||
The [module's Legacy REST interface](../building-modules/module-interfaces.md#legacy-rest) lets users generate transactions and query the state through REST calls to the application's Legacy API Service. REST routes are defined in a file `client/rest/rest.go`, which is composed of:
|
||||
|
||||
- A `RegisterRoutes` function, which registers each route defined in the file. This function is called from the [main application's interface](#application-interfaces) for each module used within the application. The router used in the SDK is [Gorilla's mux](https://github.com/gorilla/mux).
|
||||
- Custom request type definitions for each query or transaction creation function that needs to be exposed. These custom request types build on the base `request` type of the Cosmos SDK:
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/rest/rest.go#L47-L60
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/types/rest/rest.go#L62-L76
|
||||
- One handler function for each request that can be routed to the given module. These functions implement the core logic necessary to serve the request.
|
||||
|
||||
These Legacy API endpoints are present in the SDK for backward compatibility purposes and will be removed in the next release.
|
||||
|
||||
## Application Interface
|
||||
|
||||
[Interfaces](../interfaces/interfaces-intro.md) let end-users interact with full-node clients. This means querying data from the full-node or creating and sending new transactions to be relayed by the full-node and eventually included in a block.
|
||||
|
||||
The main interface is the [Command-Line Interface](../interfaces/cli.md). The CLI of an SDK application is built by aggregating [CLI commands](#cli) defined in each of the modules used by the application. The CLI of an application generally has the `-cli` suffix (e.g. `appcli`), and defined in a file called `cmd/appcli/main.go`. The file contains:
|
||||
The main interface is the [Command-Line Interface](../interfaces/cli.md). The CLI of an SDK application is built by aggregating [CLI commands](#cli) defined in each of the modules used by the application. The CLI of an application is the same as the daemon (e.g. `appd`), and defined in a file called `appd/main.go`. The file contains:
|
||||
|
||||
- **A `main()` function**, which is executed to build the `appcli` interface client. This function prepares each command and adds them to the `rootCmd` before building them. At the root of `appCli`, the function adds generic commands like `status`, `keys` and `config`, query commands, tx commands and `rest-server`.
|
||||
- **Query commands** are added by calling the `queryCmd` function, also defined in `appcli/main.go`. This function returns a Cobra command that contains the query commands defined in each of the application's modules (passed as an array of `sdk.ModuleClients` from the `main()` function), as well as some other lower level query commands such as block or validator queries. Query command are called by using the command `appcli query [query]` of the CLI.
|
||||
- **Transaction commands** are added by calling the `txCmd` function. Similar to `queryCmd`, the function returns a Cobra command that contains the tx commands defined in each of the application's modules, as well as lower level tx commands like transaction signing or broadcasting. Tx commands are called by using the command `appcli tx [tx]` of the CLI.
|
||||
- **A `registerRoutes` function**, which is called from the `main()` function when initializing the [application's light-client daemon (LCD)](../core/node.md#lcd) (i.e. `rest-server`). `registerRoutes` calls the `RegisterRoutes` function of each of the application's module, thereby registering the routes of the module to the lcd's router. The LCD can be started by running the following command `appcli rest-server`.
|
||||
- **A `main()` function**, which is executed to build the `appd` interface client. This function prepares each command and adds them to the `rootCmd` before building them. At the root of `appd`, the function adds generic commands like `status`, `keys` and `config`, query commands, tx commands and `rest-server`.
|
||||
- **Query commands** are added by calling the `queryCmd` function. This function returns a Cobra command that contains the query commands defined in each of the application's modules (passed as an array of `sdk.ModuleClients` from the `main()` function), as well as some other lower level query commands such as block or validator queries. Query command are called by using the command `appd query [query]` of the CLI.
|
||||
- **Transaction commands** are added by calling the `txCmd` function. Similar to `queryCmd`, the function returns a Cobra command that contains the tx commands defined in each of the application's modules, as well as lower level tx commands like transaction signing or broadcasting. Tx commands are called by using the command `appd tx [tx]` of the CLI.
|
||||
|
||||
See an example of an application's main command-line file from the [nameservice tutorial](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice)
|
||||
|
||||
@@ -221,11 +261,11 @@ See an example of an application's main command-line file from the [nameservice
|
||||
|
||||
## Dependencies and Makefile
|
||||
|
||||
This section is optional, as developers are free to choose their dependency manager and project building method. That said, the current most used framework for versioning control is [`go.mod`](https://github.com/golang/go/wiki/Modules). It ensures each of the libraries used throughout the application are imported with the correct version. See an example from the [nameservice tutorial](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice):
|
||||
This section is optional, as developers are free to choose their dependency manager and project building method. That said, the current most used framework for versioning control is [`go.mod`](https://github.com/golang/go/wiki/Modules). It ensures each of the libraries used throughout the application are imported with the correct version. See an example from the [nameservice tutorial](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice):
|
||||
|
||||
+++ https://github.com/cosmos/sdk-tutorials/blob/c6754a1e313eb1ed973c5c91dcc606f2fd288811/go.mod#L1-L18
|
||||
|
||||
For building the application, a [Makefile](https://en.wikipedia.org/wiki/Makefile) is generally used. The Makefile primarily ensures that the `go.mod` is run before building the two entrypoints to the application, [`appd`](#node-client) and [`appcli`](#application-interface). See an example of Makefile from the [nameservice tutorial]()
|
||||
For building the application, a [Makefile](https://en.wikipedia.org/wiki/Makefile) is generally used. The Makefile primarily ensures that the `go.mod` is run before building the two entrypoints to the application, [`appd`](#node-client) and [`appd`](#application-interface). See an example of Makefile from the [nameservice tutorial]()
|
||||
|
||||
+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/Makefile
|
||||
|
||||
|
||||
+54
-54
@@ -28,10 +28,11 @@ There are several required and optional flags for transaction creation. The `--f
|
||||
|
||||
Additionally, there are several [flags](../interfaces/cli.md) users can use to indicate how much they are willing to pay in [fees](./gas-fees.md):
|
||||
|
||||
* `--gas` refers to how much [gas](./gas-fees.md), which represents computational resources, `Tx` consumes. Gas is dependent on the transaction and is not precisely calculated until execution, but can be estimated by providing `auto` as the value for `--gas`.
|
||||
* `--gas-adjustment` (optional) can be used to scale `gas` up in order to avoid underestimating. For example, users can specify their gas adjustment as 1.5 to use 1.5 times the estimated gas.
|
||||
* `--gas-prices` specifies how much the user is willing pay per unit of gas, which can be one or multiple denominations of tokens. For example, `--gas-prices=0.025uatom, 0.025upho` means the user is willing to pay 0.025uatom AND 0.025upho per unit of gas.
|
||||
* `--fees` specifies how much in fees the user is willing to pay in total.
|
||||
- `--gas` refers to how much [gas](./gas-fees.md), which represents computational resources, `Tx` consumes. Gas is dependent on the transaction and is not precisely calculated until execution, but can be estimated by providing `auto` as the value for `--gas`.
|
||||
- `--gas-adjustment` (optional) can be used to scale `gas` up in order to avoid underestimating. For example, users can specify their gas adjustment as 1.5 to use 1.5 times the estimated gas.
|
||||
- `--gas-prices` specifies how much the user is willing pay per unit of gas, which can be one or multiple denominations of tokens. For example, `--gas-prices=0.025uatom, 0.025upho` means the user is willing to pay 0.025uatom AND 0.025upho per unit of gas.
|
||||
- `--fees` specifies how much in fees the user is willing to pay in total.
|
||||
- `--timeout-height` specifies a block timeout height to prevent the tx from being committed past a certain height.
|
||||
|
||||
The ultimate value of the fees paid is equal to the gas multiplied by the gas prices. In other words, `fees = ceil(gas * gasPrices)`. Thus, since fees can be calculated using gas prices and vice versa, the users specify only one of the two.
|
||||
|
||||
@@ -42,7 +43,7 @@ Later, validators decide whether or not to include the transaction in their bloc
|
||||
Users of application `app` can enter the following command into their CLI to generate a transaction to send 1000uatom from a `senderAddress` to a `recipientAddress`. It specifies how much gas they are willing to pay: an automatic estimate scaled up by 1.5 times, with a gas price of 0.025uatom per unit gas.
|
||||
|
||||
```bash
|
||||
appcli tx send <recipientAddress> 1000uatom --from <senderAddress> --gas auto --gas-adjustment 1.5 --gas-prices 0.025uatom
|
||||
appd tx send <recipientAddress> 1000uatom --from <senderAddress> --gas auto --gas-adjustment 1.5 --gas-prices 0.025uatom
|
||||
```
|
||||
|
||||
#### Other Transaction Creation Methods
|
||||
@@ -60,11 +61,11 @@ Each full-node (running Tendermint) that receives a `Tx` sends an [ABCI message]
|
||||
The full-nodes perform stateless, then stateful checks on `Tx` during `CheckTx`, with the goal to
|
||||
identify and reject an invalid transaction as early on as possible to avoid wasted computation.
|
||||
|
||||
***Stateless*** checks do not require nodes to access state - light clients or offline nodes can do
|
||||
**_Stateless_** checks do not require nodes to access state - light clients or offline nodes can do
|
||||
them - and are thus less computationally expensive. Stateless checks include making sure addresses
|
||||
are not empty, enforcing nonnegative numbers, and other logic specified in the definitions.
|
||||
|
||||
***Stateful*** checks validate transactions and messages based on a committed state. Examples
|
||||
**_Stateful_** checks validate transactions and messages based on a committed state. Examples
|
||||
include checking that the relevant values exist and are able to be transacted with, the address
|
||||
has sufficient funds, and the sender is authorized or has the correct ownership to transact.
|
||||
At any given moment, full-nodes typically have [multiple versions](../core/baseapp.md#volatile-states)
|
||||
@@ -140,39 +141,39 @@ locally, this process yields a single, unambiguous result, since the messages' s
|
||||
explicitly ordered in the block proposal.
|
||||
|
||||
```
|
||||
-----------------------
|
||||
-----------------------
|
||||
|Receive Block Proposal|
|
||||
-----------------------
|
||||
|
|
||||
-----------------------
|
||||
|
|
||||
v
|
||||
-----------------------
|
||||
| BeginBlock |
|
||||
-----------------------
|
||||
|
|
||||
v
|
||||
-----------------------
|
||||
| DeliverTx(tx0) |
|
||||
| DeliverTx(tx1) |
|
||||
| DeliverTx(tx2) |
|
||||
| DeliverTx(tx3) |
|
||||
| . |
|
||||
-----------------------
|
||||
| BeginBlock |
|
||||
-----------------------
|
||||
|
|
||||
v
|
||||
-----------------------
|
||||
| DeliverTx(tx0) |
|
||||
| DeliverTx(tx1) |
|
||||
| DeliverTx(tx2) |
|
||||
| DeliverTx(tx3) |
|
||||
| . |
|
||||
| . |
|
||||
| . |
|
||||
-----------------------
|
||||
|
|
||||
v
|
||||
-----------------------
|
||||
| EndBlock |
|
||||
|
|
||||
v
|
||||
-----------------------
|
||||
|
|
||||
v
|
||||
| EndBlock |
|
||||
-----------------------
|
||||
| Consensus |
|
||||
|
|
||||
v
|
||||
-----------------------
|
||||
|
|
||||
v
|
||||
| Consensus |
|
||||
-----------------------
|
||||
| Commit |
|
||||
|
|
||||
v
|
||||
-----------------------
|
||||
| Commit |
|
||||
-----------------------
|
||||
```
|
||||
|
||||
@@ -184,30 +185,29 @@ to during consensus. Under the hood, `DeliverTx` is almost identical to `CheckTx
|
||||
[`runTx`](../core/baseapp.md#runtx) function in deliver mode instead of check mode.
|
||||
Instead of using their `checkState`, full-nodes use `deliverState`:
|
||||
|
||||
* **Decoding:** Since `DeliverTx` is an ABCI call, `Tx` is received in the encoded `[]byte` form.
|
||||
Nodes first unmarshal the transaction, then call `runTx` in `runTxModeDeliver`, which is very
|
||||
similar to `CheckTx` but also executes and writes state changes.
|
||||
- **Decoding:** Since `DeliverTx` is an ABCI call, `Tx` is received in the encoded `[]byte` form.
|
||||
Nodes first unmarshal the transaction, using the [`TxConfig`](./app-anatomy#register-codec) defined in the app, then call `runTx` in `runTxModeDeliver`, which is very similar to `CheckTx` but also executes and writes state changes.
|
||||
|
||||
* **Checks:** Full-nodes call `validateBasicMsgs` and the `AnteHandler` again. This second check
|
||||
happens because they may not have seen the same transactions during the addition to Mempool stage\
|
||||
and a malicious proposer may have included invalid ones. One difference here is that the
|
||||
`AnteHandler` will not compare `gas-prices` to the node's `min-gas-prices` since that value is local
|
||||
to each node - differing values across nodes would yield nondeterministic results.
|
||||
- **Checks:** Full-nodes call `validateBasicMsgs` and the `AnteHandler` again. This second check
|
||||
happens because they may not have seen the same transactions during the addition to Mempool stage\
|
||||
and a malicious proposer may have included invalid ones. One difference here is that the
|
||||
`AnteHandler` will not compare `gas-prices` to the node's `min-gas-prices` since that value is local
|
||||
to each node - differing values across nodes would yield nondeterministic results.
|
||||
|
||||
* **Route and Handler:** 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. Thus, the `route` function is called via the [module manager](../building-modules/module-manager.md) to
|
||||
retrieve the route name and find the [`Handler`](../building-modules/handler.md) within the module.
|
||||
- **Route and Handler:** 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. Thus, the `route` function is called via the [module manager](../building-modules/module-manager.md) to
|
||||
retrieve the route name and find the [`Handler`](../building-modules/handler.md) within the module.
|
||||
|
||||
* **Handler:** The `handler`, 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 `Msg`'s module and writes to the appropriate stores within the module.
|
||||
- **Handler:** The `handler`, 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 `Msg`'s module and writes to the appropriate stores within the module.
|
||||
|
||||
* **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
|
||||
`abci.ResponseDeliverTx`. If execution halts because `BlockGasMeter` or `GasMeter` has run out or something else goes
|
||||
wrong, a deferred function at the end appropriately errors or panics.
|
||||
- **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
|
||||
`abci.ResponseDeliverTx`. If execution halts because `BlockGasMeter` or `GasMeter` has run out or something else goes
|
||||
wrong, a deferred function at the end appropriately errors or panics.
|
||||
|
||||
If there are any failed state changes resulting from a `Tx` being invalid or `GasMeter` running out,
|
||||
the transaction processing terminates and any state changes are reverted. Invalid transactions in a
|
||||
@@ -219,14 +219,14 @@ The final step is for nodes to commit the block and state changes. Validator nod
|
||||
perform the previous step of executing state transitions in order to validate the transactions,
|
||||
then sign the block to confirm it. Full nodes that are not validators do not
|
||||
participate in consensus - i.e. they cannot vote - but listen for votes to understand whether or
|
||||
not they should commit the state changes.
|
||||
not they should commit the state changes.
|
||||
|
||||
When they receive enough validator votes (2/3+ *precommits* weighted by voting power), full nodes commit to a new block to be added to the blockchain and
|
||||
When they receive enough validator votes (2/3+ _precommits_ weighted by voting power), full nodes commit to a new block to be added to the blockchain and
|
||||
finalize the state transitions in the application layer. A new state root is generated to serve as
|
||||
a merkle proof for the state transitions. Applications use the [`Commit`](../core/baseapp.md#commit)
|
||||
ABCI method inherited from [Baseapp](../core/baseapp.md); it syncs all the state transitions by
|
||||
writing the `deliverState` into the application's internal state. As soon as the state changes are
|
||||
committed, `checkState` start afresh from the most recently committed state and `deliverState`
|
||||
committed, `checkState` start afresh from the most recently committed state and `deliverState`
|
||||
resets to `nil` in order to be consistent and reflect the changes.
|
||||
|
||||
Note that not all blocks have the same number of transactions and it is possible for consensus to
|
||||
|
||||
@@ -12,7 +12,7 @@ This repository contains documentation on concepts developers need to know in or
|
||||
2. [`AppModule` Interface and Module Manager](./module-manager.md)
|
||||
3. [Messages and Queries](./messages-and-queries.md)
|
||||
4. [`Handler`s - Processing Messages](./handler.md)
|
||||
5. [`Querier`s - Processing Queries](./querier.md)
|
||||
5. [Query Services - Processing Queries](./query-services.md)
|
||||
6. [BeginBlocker and EndBlocker](./beginblock-endblock.md)
|
||||
7. [`Keeper`s](./keeper.md)
|
||||
8. [Invariants](./invariants.md)
|
||||
|
||||
@@ -28,11 +28,11 @@ It is possible for developers to defined the order of execution between the `Beg
|
||||
|
||||
See an example implementation of `BeginBlocker` from the `distr` module:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/distribution/abci.go#L10-L32
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/f33749263f4ecc796115ad6e789cb0f7cddf9148/x/distribution/abci.go#L14-L38
|
||||
|
||||
and an example implementation of `EndBlocker` from the `staking` module:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/staking/handler.go#L44-L96
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/f33749263f4ecc796115ad6e789cb0f7cddf9148/x/staking/abci.go#L22-L27
|
||||
|
||||
## Next {hide}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ execution.
|
||||
|
||||
Example:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.38.1/x/distribution/keeper/querier.go#L62-L80
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/b2d48a9e815fe534a7faeec6ca2adb0874252b81/x/bank/keeper/keeper.go#L85-L122
|
||||
|
||||
Regardless if an error is wrapped or not, the SDK's `errors` package provides an API to determine if
|
||||
an error is of a particular kind via `Is`.
|
||||
|
||||
@@ -13,25 +13,25 @@ Modules generally handle a subset of the state and, as such, they need to define
|
||||
|
||||
## Type Definition
|
||||
|
||||
The subset of the genesis state defined from a given module is generally defined in a `./internal/types/genesis.go` file, along with the `DefaultGenesis` and `ValidateGenesis` methods. The struct defining the module's subset of the genesis state is usually called `GenesisState` and contains all the module-related values that need to be initialized during the genesis process.
|
||||
The subset of the genesis state defined from a given module is generally defined in a `genesis.proto` file ([more info](../core/encoding.md#gogoproto) on how to define protobuf messages). The struct defining the module's subset of the genesis state is usually called `GenesisState` and contains all the module-related values that need to be initialized during the genesis process.
|
||||
|
||||
See an example of `GenesisState` type definition from the nameservice tutorial
|
||||
See an example of `GenesisState` protobuf message definition from the `auth` module:
|
||||
|
||||
+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/genesis.go#L10-L12
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/a9547b54ffac9729fe1393651126ddfc0d236cff/proto/cosmos/auth/v1beta1/genesis.proto
|
||||
|
||||
Next we present the main genesis-related methods that need to be implemented by module developers in order for their module to be used in Cosmos SDK applications.
|
||||
|
||||
### `DefaultGenesis`
|
||||
|
||||
The `DefaultGenesis()` method is a simple method that calls the constructor function for `GenesisState` with the default value for each parameter. See an example from the `nameservice` module:
|
||||
The `DefaultGenesis()` method is a simple method that calls the constructor function for `GenesisState` with the default value for each parameter. See an example from the `auth` module:
|
||||
|
||||
+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/genesis.go#L33-L37
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/64b6bb5270e1a3b688c2d98a8f481ae04bb713ca/x/auth/module.go#L48-L52
|
||||
|
||||
### `ValidateGenesis`
|
||||
|
||||
The `ValidateGenesis(genesisState GenesisState)` method is called to verify that the provided `genesisState` is correct. It should perform validity checks on each of the parameter listed in `GenesisState`. See an example from the `nameservice` module:
|
||||
The `ValidateGenesis(genesisState GenesisState)` method is called to verify that the provided `genesisState` is correct. It should perform validity checks on each of the parameter listed in `GenesisState`. See an example from the `auth` module:
|
||||
|
||||
+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/genesis.go#L18-L31
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/64b6bb5270e1a3b688c2d98a8f481ae04bb713ca/x/auth/types/genesis.go#L57-L70
|
||||
|
||||
## Other Genesis Methods
|
||||
|
||||
@@ -43,18 +43,18 @@ The `InitGenesis` method is executed during [`InitChain`](../core/baseapp.md#ini
|
||||
|
||||
The [module manager](./module-manager.md#manager) of the application is responsible for calling the `InitGenesis` method of each of the application's modules, in order. This order is set by the application developer via the manager's `SetOrderGenesisMethod`, which is called in the [application's constructor function](../basics/app-anatomy.md#constructor-function)
|
||||
|
||||
See an example of `InitGenesis` from the nameservice tutorial
|
||||
See an example of `InitGenesis` from the `auth` module
|
||||
|
||||
+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/genesis.go#L39-L44
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/64b6bb5270e1a3b688c2d98a8f481ae04bb713ca/x/auth/genesis.go#L13-L28
|
||||
|
||||
### `ExportGenesis`
|
||||
|
||||
The `ExportGenesis` method is executed whenever an export of the state is made. It takes the latest known version of the subset of the state managed by the module and creates a new `GenesisState` out of it. This is mainly used when the chain needs to be upgraded via a hard fork.
|
||||
|
||||
See an example of `ExportGenesis` from the nameservice tutorial.
|
||||
See an example of `ExportGenesis` from the `auth` module.
|
||||
|
||||
+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/genesis.go#L46-L57
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/64b6bb5270e1a3b688c2d98a8f481ae04bb713ca/x/auth/genesis.go#L31-L42
|
||||
|
||||
## Next {hide}
|
||||
|
||||
Learn about [modules interfaces](#module-interfaces.md) {hide}
|
||||
Learn about [modules interfaces](module-interfaces.md) {hide}
|
||||
@@ -21,8 +21,8 @@ Let us break it down:
|
||||
|
||||
- The [`Msg`](./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 cache-wrapped copy of the latest state. If the `msg` is succesfully processed, the modified version of the temporary state contained in the `ctx` will be written to the main state.
|
||||
- The [`Result`] returned to `baseapp`, which contains (among other things) information on the execution of the `handler`, [`gas`](../basics/gas-fees.md) consumption and [`events`](../core/events.md).
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/result.go#L15-L40
|
||||
- The [`*Result`] returned to `baseapp`, which contains (among other things) information on the execution of the `handler` and [`events`](../core/events.md).
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/proto/cosmos/base/abci/v1beta1/abci.proto#L81-L95
|
||||
|
||||
## Implementation of a module `handler`s
|
||||
|
||||
@@ -37,10 +37,10 @@ func NewHandler(keeper Keeper) sdk.Handler {
|
||||
return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
|
||||
ctx = ctx.WithEventManager(sdk.NewEventManager())
|
||||
switch msg := msg.(type) {
|
||||
case MsgType1:
|
||||
case *MsgType1:
|
||||
return handleMsgType1(ctx, keeper, msg)
|
||||
|
||||
case MsgType2:
|
||||
case *MsgType2:
|
||||
return handleMsgType2(ctx, keeper, msg)
|
||||
|
||||
default:
|
||||
@@ -69,16 +69,28 @@ 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`.
|
||||
|
||||
Finally, the `handler` function returns a `sdk.Result` which contains the aforementioned `events` and an optional `Data` field.
|
||||
Finally, the `handler` function returns a `*sdk.Result` which contains the aforementioned `events` and an optional `Data` field.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/result.go#L15-L40
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/proto/cosmos/base/abci/v1beta1/abci.proto#L81-L95
|
||||
|
||||
Next is an example of how to return a `Result` from the `gov` module:
|
||||
Next is an example of how to return a `*Result` from the `gov` module:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/gov/handler.go#L59-L62
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/x/gov/handler.go#L67-L70
|
||||
|
||||
For a deeper look at `handler`s, see this [example implementation of a `handler` function](https://github.com/cosmos/sdk-application-tutorial/blob/c6754a1e313eb1ed973c5c91dcc606f2fd288811/x/nameservice/handler.go) from the nameservice tutorial.
|
||||
For a deeper look at `handler`s, see this [example implementation of a `handler` function](https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/x/gov/handler.go) from the `gov` module.
|
||||
|
||||
The `handler` can then be registered from [`AppModule.Route()`](./module-manager.md#appmodule) as shown in the example below:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/228728cce2af8d494c8b4e996d011492139b04ab/x/gov/module.go#L143-L146
|
||||
|
||||
## Telemetry
|
||||
|
||||
New [telemetry metrics](../core/telemetry.md) can be created from the `handler` when handling messages for instance.
|
||||
|
||||
This is an example from the `auth` module:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/x/auth/vesting/handler.go#L68-L80
|
||||
|
||||
## Next {hide}
|
||||
|
||||
Learn about [queriers](./querier.md) {hide}
|
||||
Learn about [query services](./query-services.md) {hide}
|
||||
|
||||
@@ -81,7 +81,7 @@ Modules are by convention defined in the `.x/` subfolder (e.g. the `bank` module
|
||||
- Custom [`message` types](./messages-and-queries.md#messages) to trigger state-transitions.
|
||||
- A [`handler`](./handler.md) used to process messages when they are routed to the module by [`baseapp`](../core/baseapp.md#message-routing).
|
||||
- A [`keeper`](./keeper.md), used to access the module's store(s) and update the state.
|
||||
- A [`querier`](./querier.md), used to process user queries when they are routed to the module by [`baseapp`](../core/baseapp.md#query-routing).
|
||||
- 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).
|
||||
@@ -91,4 +91,3 @@ Please refer to the [structure document](./structure.md) to learn about the reco
|
||||
## Next {hide}
|
||||
|
||||
Read more on the [`AppModule` interface and the `module manager`](./module-manager.md) {hide}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ An `Invariant` is a function that checks for a particular invariant within a mod
|
||||
|
||||
where the `string` return value is the invariant message, which can be used when printing logs, and the `bool` return value is the actual result of the invariant check.
|
||||
|
||||
In practice, each module implements `Invariant`s in a `./internal/keeper/invariants.go` file within the module's folder. The standard is to implement one `Invariant` function per logical grouping of invariants with the following model:
|
||||
In practice, each module implements `Invariant`s in a `./keeper/invariants.go` file within the module's folder. The standard is to implement one `Invariant` function per logical grouping of invariants with the following model:
|
||||
|
||||
```go
|
||||
// Example for an Invariant that checks balance-related invariants
|
||||
@@ -74,7 +74,7 @@ At its core, the `InvariantRegistry` is defined in the SDK as an interface:
|
||||
|
||||
Typically, this interface is implemented in the `keeper` of a specific module. The most used implementation of an `InvariantRegistry` can be found in the `crisis` module:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/crisis/internal/keeper/keeper.go#L45-L49
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/master/x/crisis/keeper/keeper.go#L50-L54
|
||||
|
||||
The `InvariantRegistry` is therefore typically instantiated by instantiating the `keeper` of the `crisis` module in the [application's constructor function](../basics/app-anatomy.md#constructor-function).
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ The core idea behind the object-capabilities approach is to only reveal what is
|
||||
|
||||
## Type Definition
|
||||
|
||||
`keeper`s are generally implemented in a `internal/keeper/keeper.go` file located in the module's folder. By convention, the type `keeper` of a module is simply named `Keeper` and usually follows the following structure:
|
||||
`keeper`s are generally implemented in a `/keeper/keeper.go` file located in the module's folder. By convention, the type `keeper` of a module is simply named `Keeper` and usually follows the following structure:
|
||||
|
||||
```go
|
||||
type Keeper struct {
|
||||
@@ -32,15 +32,15 @@ type Keeper struct {
|
||||
}
|
||||
```
|
||||
|
||||
For example, here is the [type definition of the `keeper` from the nameservice tutorial:
|
||||
For example, here is the type definition of the `keeper` from the `staking` module:
|
||||
|
||||
+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/internal/keeper/keeper.go#L10-L17
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/3bafd8255a502e5a9cee07391cf8261538245dfd/x/staking/keeper/keeper.go#L23-L33
|
||||
|
||||
Let us go through the different parameters:
|
||||
|
||||
- An expected `keeper` is a `keeper` external to a module that is required by the internal `keeper` of said module. External `keeper`s are listed in the internal `keeper`'s type definition as interfaces. These interfaces are themselves defined in a `internal/types/expected_keepers.go` file within the module's folder. In this context, interfaces are used to reduce the number of dependencies, as well as to facilitate the maintenance of the module itself.
|
||||
- An expected `keeper` is a `keeper` external to a module that is required by the internal `keeper` of said module. External `keeper`s are listed in the internal `keeper`'s type definition as interfaces. These interfaces are themselves defined in a `types/expected_keepers.go` file within the module's folder. In this context, interfaces are used to reduce the number of dependencies, as well as to facilitate the maintenance of the module itself.
|
||||
- `storeKey`s grant access to the store(s) of the [multistore](../core/store.md) managed by the module. They should always remain unexposed to external modules.
|
||||
- A [codec `cdc`](../core/encoding.md), used to marshall and unmarshall struct to/from `[]byte`.
|
||||
- A [codec `cdc`](../core/encoding.md), used to marshall and unmarshall struct to/from `[]byte`, that can be any of `codec.BinaryMarshaler`,`codec.JSONMarshaler` or `codec.Marshaler` based on your requirements. It can be either a proto or amino codec as long as they implement these interfaces.
|
||||
|
||||
Of course, it is possible to define different types of internal `keeper`s for the same module (e.g. a read-only `keeper`). Each type of `keeper` comes with its own constructor function, which is called from the [application's constructor function](../basics/app-anatomy.md). This is where `keeper`s are instantiated, and where developers make sure to pass correct instances of modules' `keeper`s to other modules that require it.
|
||||
|
||||
@@ -56,7 +56,7 @@ func (k Keeper) Get(ctx sdk.Context, key string) returnType
|
||||
|
||||
and go through the following steps:
|
||||
|
||||
1. Retrieve the appropriate store from the `ctx` using the `storeKey`. This is done through the `KVStore(storeKey sdk.StoreKey` method of the `ctx`.
|
||||
1. Retrieve the appropriate store from the `ctx` using the `storeKey`. This is done through the `KVStore(storeKey sdk.StoreKey)` method of the `ctx`. Then it's prefered to use the `prefix.Store` to access only the desired limited subset of the store for convenience and safety.
|
||||
2. If it exists, get the `[]byte` value stored at location `[]byte(key)` using the `Get(key []byte)` method of the store.
|
||||
3. Unmarshall the retrieved value from `[]byte` to `returnType` using the codec `cdc`. Return the value.
|
||||
|
||||
@@ -68,11 +68,17 @@ func (k Keeper) Set(ctx sdk.Context, key string, value valueType)
|
||||
|
||||
and go through the following steps:
|
||||
|
||||
1. Retrieve the appropriate store from the `ctx` using the `storeKey`. This is done through the `KVStore(storeKey sdk.StoreKey` method of the `ctx`.
|
||||
2. Marhsall `value` to `[]byte` using the codec `cdc`.
|
||||
1. Retrieve the appropriate store from the `ctx` using the `storeKey`. This is done through the `KVStore(storeKey sdk.StoreKey)` method of the `ctx`. Then it's prefered to use the `prefix.Store` to access only the desired limited subset of the store for convenience and safety.
|
||||
2. Marshal `value` to `[]byte` using the codec `cdc`.
|
||||
3. Set the encoded value in the store at location `key` using the `Set(key []byte, value []byte)` method of the store.
|
||||
|
||||
For more, see an example of `keeper`'s [methods implementation from the nameservice tutorial](https://github.com/cosmos/sdk-application-tutorial/blob/c6754a1e313eb1ed973c5c91dcc606f2fd288811/x/nameservice/internal/keeper/keeper.go).
|
||||
For more, see an example of `keeper`'s [methods implementation from the `staking` module](https://github.com/cosmos/cosmos-sdk/blob/3bafd8255a502e5a9cee07391cf8261538245dfd/x/staking/keeper/keeper.go).
|
||||
|
||||
The [module `KVStore`](../core/store.md#kvstore-and-commitkvstore-interfaces) also provides an `Iterator()` method which returns an `Iterator` object to iterate over a domain of keys.
|
||||
|
||||
This is an example from the `auth` module to iterate accounts:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/bf8809ef9840b4f5369887a38d8345e2380a567f/x/auth/keeper/account.go#L70-L83
|
||||
|
||||
## Next {hide}
|
||||
|
||||
|
||||
@@ -16,39 +16,47 @@ order: 3
|
||||
|
||||
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 `router` so that it can be processed by the module's [`handler`](./handler.md). For a more detailed explanation of the lifecycle of a transaction, click [here](../basics/tx-lifecycle.md).
|
||||
|
||||
Defining `message`s is the responsibility of module developers. Typically, they are defined in a `./internal/types/msgs.go` file inside the module's folder. The `message`'s type 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`.
|
||||
Defining `message`s is the responsibility of module developers. Typically, they are defined as protobuf messages in a `proto/` directory (see more info about [conventions and naming](../core/encoding.md#faq)). The `message`'s 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`.
|
||||
|
||||
```go
|
||||
// Example of a message type definition
|
||||
Here's an example of a protobuf message definition:
|
||||
|
||||
type MsgSubmitProposal struct {
|
||||
Content Content `json:"content" yaml:"content"`
|
||||
InitialDeposit sdk.Coins `json:"initial_deposit" yaml:"initial_deposit"`
|
||||
Proposer sdk.AccAddress `json:"proposer" yaml:"proposer"`
|
||||
}
|
||||
```
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/proto/cosmos/gov/v1beta1/tx.proto#L15-L27
|
||||
|
||||
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:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/tx_msg.go#L7-L29
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/4a1b2fba43b1052ca162b3a1e0b6db6db9c26656/types/tx_msg.go#L10-L33
|
||||
|
||||
It contains the following methods:
|
||||
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).
|
||||
- `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.
|
||||
|
||||
See an example implementation of a `message` from the `nameservice` module:
|
||||
See an example implementation of a `message` from the `gov` module:
|
||||
|
||||
+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/internal/types/msgs.go#L10-L51
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/master/x/gov/types/msgs.go#L94-L136
|
||||
|
||||
## 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 [`querier`](./querier.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).
|
||||
|
||||
Contrary to `message`s, there is usually no specific `query` object defined by module developers. Instead, the SDK takes the simpler approach of using a simple `path` to define each `query`. The `path` contains the `query` type and all the arguments needed in order to process it. For most module queries, the `path` should look like the following:
|
||||
### 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`.
|
||||
|
||||
Here's an example of such a `Query` service definition:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/proto/cosmos/auth/v1beta1/query.proto#L12-L23
|
||||
|
||||
As `proto.Message`s, generated `Response` types implement by default `String()` method of [`fmt.Stringer`](https://golang.org/pkg/fmt/#Stringer).
|
||||
|
||||
A `RegisterQueryServer` method is also generated and should be used to register the module's query server in `RegisterQueryService` method from the [`AppModule` interface](./module-manager.md#appmodule).
|
||||
|
||||
### Legacy Queries
|
||||
|
||||
Before the introduction of Protobuf and gRPC in the SDK, there was usually no specific `query` object defined by module developers, contrary to `message`s. Instead, the SDK took the simpler approach of using a simple `path` to define each `query`. The `path` contains the `query` type and all the arguments needed in order to process it. For most module queries, the `path` should look like the following:
|
||||
|
||||
```
|
||||
queryCategory/queryRoute/queryType/arg1/arg2/...
|
||||
@@ -58,18 +66,24 @@ 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`](./querier.md) to map the `query` to the appropriate `querier function` within 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`.
|
||||
|
||||
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`](./querier.md), to process the `query` once it has been [routed to the module](../core/baseapp.md#query-routing).
|
||||
- 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 `internal/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).
|
||||
- `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).
|
||||
|
||||
See an example of `query` return types from the `nameservice` module:
|
||||
### Store Queries
|
||||
|
||||
+++ https://github.com/cosmos/sdk-tutorials/blob/c6754a1e313eb1ed973c5c91dcc606f2fd288811/x/nameservice/internal/types/querier.go#L5-L21
|
||||
Store queries query directly for store keys. They use `clientCtx.QueryABCI(req abci.RequestQuery)` to return the full `abci.ResponseQuery` with inclusion Merkle proofs.
|
||||
|
||||
See following examples:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/080fcf1df25ccdf97f3029b6b6f83caaf5a235e4/x/ibc/core/client/query.go#L36-L46
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/080fcf1df25ccdf97f3029b6b6f83caaf5a235e4/baseapp/abci.go#L722-L749
|
||||
|
||||
## Next {hide}
|
||||
|
||||
|
||||
@@ -16,49 +16,47 @@ One of the main interfaces for an application is the [command-line interface](..
|
||||
|
||||
### Transaction Commands
|
||||
|
||||
[Transactions](../core/transactions.md) are created by users to wrap messages that trigger state changes when they get included in a valid block. Transaction commands typically have their own `tx.go` file in the module `./x/moduleName/client/cli` folder. The commands are specified in getter functions prefixed with `GetCmd` and include the name of the command.
|
||||
[Transactions](../core/transactions.md) are created by users to wrap messages that trigger state changes when they get included in a valid block. Transaction commands typically have their own `tx.go` file in the module `./x/moduleName/client/cli` folder. The commands are specified in getter functions and include the name of the command.
|
||||
|
||||
Here is an example from the `nameservice` module:
|
||||
Here is an example from the `auth` module:
|
||||
|
||||
+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/client/cli/tx.go#L33-L58
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/64b6bb5270e1a3b688c2d98a8f481ae04bb713ca/x/auth/client/cli/tx_sign.go#L160-L194
|
||||
|
||||
This getter function creates the command for the Buy Name transaction. It does the following:
|
||||
This getter function creates the command for the `Sign` transaction. It does the following:
|
||||
|
||||
- **Construct the command:** Read the [Cobra Documentation](https://github.com/spf13/cobra) for details on how to create commands.
|
||||
- **Construct the command:** Read the [Cobra Documentation](https://godoc.org/github.com/spf13/cobra) for details on how to create commands.
|
||||
- **Use:** Specifies the format of a command-line entry users should type in order to invoke this command. In this case, the user uses `buy-name` as the name of the transaction command and provides the `name` the user wishes to buy and the `amount` the user is willing to pay.
|
||||
- **Args:** The number of arguments the user provides, in this case exactly two: `name` and `amount`.
|
||||
- **Short and Long:** A description for the function is provided here. A `Short` description is expected, and `Long` can be used to provide a more detailed description when a user uses the `--help` flag to ask for more information.
|
||||
- **RunE:** Defines a function that can return an error, called when the command is executed. Using `Run` would do the same thing, but would not allow for errors to be returned.
|
||||
- **`RunE` Function Body:** The function should be specified as a `RunE` to allow for errors to be returned. This function encapsulates all of the logic to create a new transaction that is ready to be relayed to nodes.
|
||||
- The function should first initialize a [`TxBuilder`](../core/transactions.md#txbuilder) with the application `codec`'s `TxEncoder`, as well as a new [`Context`](../interfaces/query-lifecycle.md#context) with the `codec` and `AccountDecoder`. These contexts contain all the information provided by the user and will be used to transfer this user-specific information between processes. To learn more about how contexts are used in a transaction, click [here](../core/transactions.md#transaction-generation).
|
||||
- If applicable, the command's arguments are parsed. Here, the `amount` given by the user is parsed into a denomination of `coins`.
|
||||
- If applicable, the `Context` is used to retrieve any parameters such as the transaction originator's address to be used in the transaction. Here, the `from` address is retrieved by calling `cliCtx.getFromAddress()`.
|
||||
- The function should first get the `clientCtx` with `client.GetClientContextFromCmd(cmd)` and `client.ReadTxCommandFlags(clientCtx, cmd.Flags())`. This context contains all the information provided by the user and will be used to transfer this user-specific information between processes. To learn more about how contexts are used in a transaction, click [here](../core/transactions.md#transaction-generation).
|
||||
- If applicable, the command's arguments are parsed.
|
||||
- If applicable, the `Context` is used to retrieve any parameters such as the transaction originator's address to be used in the transaction. Here, the `from` address is retrieved by calling `clientCtx.GetFromAddress()`.
|
||||
- A [message](./messages-and-queries.md) is created using all parameters parsed from the command arguments and `Context`. The constructor function of the specific message type is called directly. It is good practice to call `ValidateBasic()` on the newly created message to run a sanity check and check for invalid arguments.
|
||||
- Depending on what the user wants, the transaction is either generated offline or signed and broadcasted to the preconfigured node using `GenerateOrBroadcastMsgs()`.
|
||||
- **Flags.** Add any [flags](#flags) to the command. No flags were specified here, but all transaction commands have flags to provide additional information from the user (e.g. amount of fees they are willing to pay). These _persistent_ [transaction flags](../interfaces/cli.md#flags) can be added to a higher-level command so that they apply to all transaction commands.
|
||||
- **Flags.** Add any [flags](#flags) to the command. All transaction commands have flags to provide additional information from the user (e.g. amount of fees they are willing to pay). These _persistent_ [transaction flags](../interfaces/cli.md#flags) can be added to a higher-level command so that they apply to all transaction commands.
|
||||
|
||||
Finally, the module needs to have a `GetTxCmd()`, which aggregates all of the transaction commands of the module. Often, each command getter function has its own file in the module's `cli` folder, and a separate `tx.go` file contains `GetTxCmd()`. Application developers wishing to include the module's transactions will call this function to add them as subcommands in their CLI. Here is the `auth` `GetTxCmd()` function, which adds the `Sign` and `MultiSign` commands.
|
||||
Finally, the module needs to have a `GetTxCmd()`, which aggregates all of the transaction commands of the module. Often, each command getter function has its own file in the module's `cli` folder, and a separate `tx.go` file contains `GetTxCmd()`. Application developers wishing to include the module's transactions will call this function to add them as subcommands in their CLI. Here is the `auth` `GetTxCmd()` function, which adds the `Sign`, `MultiSign`, `ValidateSignatures` and `SignBatch` commands.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/67f6b021180c7ef0bcf25b6597a629aca27766b8/x/auth/client/cli/tx.go#L11-L25
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/351192aa0b52a42b66ff06e81cfa7a9e26667a7f/x/auth/client/cli/tx.go#L10-L26
|
||||
|
||||
An application using this module likely adds `auth` module commands to its root `TxCmd` command by calling `txCmd.AddCommand(authModuleClient.GetTxCmd())`.
|
||||
|
||||
### Query Commands
|
||||
|
||||
[Queries](./messages-and-queries.md#queries) allow users to gather information about the application or network state; they are routed by the application and processed by the module in which they are defined. Query commands typically have their own `query.go` file in the module `x/moduleName/client/cli` folder. Like transaction commands, they are specified in getter functions and have the prefix `GetCmdQuery`. Here is an example of a query command from the `nameservice` module:
|
||||
[Queries](./messages-and-queries.md#queries) allow users to gather information about the application or network state; they are routed by the application and processed by the module in which they are defined. Query commands typically have their own `query.go` file in the module `x/moduleName/client/cli` folder. Like transaction commands, they are specified in getter functions. Here is an example of a query command from the `auth` module:
|
||||
|
||||
+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/client/cli/query.go#L52-L73
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/x/auth/client/cli/query.go#L76-L108
|
||||
|
||||
This query returns the address that owns a particular name. The getter function does the following:
|
||||
This query returns the account at a given address. The getter function does the following:
|
||||
|
||||
- **`codec` and `queryRoute`.** In addition to taking in the application `codec`, query command getters also take a `queryRoute` used to construct a path [Baseapp](../core/baseapp.md#query-routing) uses to route the query in the application.
|
||||
- **Construct the command.** Read the [Cobra Documentation](https://github.com/spf13/cobra) and the [transaction command](#transaction-commands) example above for more information. The user must type `whois` and provide the `name` they are querying for as the only argument.
|
||||
- **Construct the command.** Read the [Cobra Documentation](https://godoc.org/github.com/spf13/cobra) and the [transaction command](#transaction-commands) example above for more information. The user must type `account` and provide the `address` they are querying for as the only argument.
|
||||
- **`RunE`.** The function should be specified as a `RunE` to allow for errors to be returned. This function encapsulates all of the logic to create a new query that is ready to be relayed to nodes.
|
||||
- The function should first initialize a new [`Context`](../interfaces/query-lifecycle.md#context) with the application `codec`.
|
||||
- The function should first initialize a new client [`Context`](../interfaces/query-lifecycle.md#context) as described in the [previous section](#transaction-commands)
|
||||
- If applicable, the `Context` is used to retrieve any parameters (e.g. the query originator's address to be used in the query) and marshal them with the query parameter type, in preparation to be relayed to a node. There are no `Context` parameters in this case because the query does not involve any information about the user.
|
||||
- The `queryRoute` is used to construct a `path` [`baseapp`](../core/baseapp.md) will use to route the query to the appropriate [querier](./querier.md). The expected format for a query `path` is "queryCategory/queryRoute/queryType/arg1/arg2/...", where `queryCategory` can be `p2p`, `store`, `app`, or `custom`, `queryRoute` is the name of the module, and `queryType` is the name of the query type defined within the module. [`baseapp`](../core/baseapp.md) can handle each type of `queryCategory` by routing it to a module querier or retrieving results directly from stores and functions for querying peer nodes. Module queries are `custom` type queries (some SDK modules have exceptions, such as `auth` and `gov` module queries).
|
||||
- The `Context` `QueryWithData()` function is used to relay the query to a node and retrieve the response. It requires the `path`. It returns the result and height of the query upon success or an error if the query fails.
|
||||
- The `codec` is used to nmarshal the response and the `Context` is used to print the output back to the user.
|
||||
- A new `queryClient` should be initialized using `NewQueryClient(clientCtx)`, this method being generated from `query.proto`. Then it can be used to call the appropriate [query](./messages-and-queries.md#grpc-queries).
|
||||
- The `clientCtx.PrintOutput` method is used to print the output back to the user.
|
||||
- **Flags.** Add any [flags](#flags) to the command.
|
||||
|
||||
Finally, the module also needs a `GetQueryCmd`, which aggregates all of the query commands of the module. Application developers wishing to include the module's queries will call this function to add them as subcommands in their CLI. Its structure is identical to the `GetTxCmd` command shown above.
|
||||
@@ -71,9 +69,9 @@ The flags for a module are typically found in a `flags.go` file in the `./x/modu
|
||||
|
||||
For full details on flags, visit the [Cobra Documentation](https://github.com/spf13/cobra).
|
||||
|
||||
For example, the SDK `./client/flags` package includes a `PostCommands()` function that adds necessary flags to transaction commands, such as the `from` flag to indicate which address the transaction originates from.
|
||||
For example, the SDK `./client/flags` package includes a `AddTxFlagsToCmd(cmd *cobra.Command)` function that adds necessary flags to a transaction command, such as the `from` flag to indicate which address the transaction originates from.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/client/flags/flags.go#L85-L116
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/cfb5fc03e5092395403d10156c0ee96e6ff1ddbe/client/flags/flags.go#L85-L112
|
||||
|
||||
Here is an example of how to add a flag using the `from` flag from this function.
|
||||
|
||||
@@ -89,11 +87,54 @@ A flag can be marked as _required_ so that an error is automatically thrown if t
|
||||
cmd.MarkFlagRequired(FlagFrom)
|
||||
```
|
||||
|
||||
Since `PostCommands()` includes all of the basic flags required for a transaction command, module developers may choose not to add any of their own (specifying arguments instead may often be more appropriate). For a full list of what flags are included in the `PostCommands()` function, including which are required inputs from users, see the CLI documentation [here](../interfaces/cli.md#transaction-flags).
|
||||
Since `AddTxFlagsToCmd(cmd *cobra.Command)` includes all of the basic flags required for a transaction command, module developers may choose not to add any of their own (specifying arguments instead may often be more appropriate).
|
||||
|
||||
## REST
|
||||
Similarly, there is a `AddQueryFlagsToCmd(cmd *cobra.Command)` to add common flags to a module query command.
|
||||
|
||||
Applications typically support web services that use HTTP requests (e.g. a web wallet like [Lunie.io](https://lunie.io). Thus, application developers will also use REST Routes to route HTTP requests to the application's modules; these routes will be used by service providers. The module developer's responsibility is to define the REST client by defining [routes](#register-routes) for all possible [requests](#request-types) and [handlers](#request-handlers) for each of them. It's up to the module developer how to organize the REST interface files; there is typically a `rest.go` file found in the module's `./x/moduleName/client/rest` folder.
|
||||
## gRPC
|
||||
|
||||
[gRPC](https://grpc.io/) is the prefered way for external clients like wallets and exchanges to interact with a node.
|
||||
|
||||
In addition to providing an ABCI query pathway, modules [custom queries](./messages-and-queries.md#grpc-queries) can provide a GRPC proxy server that routes requests in the GRPC protocol to ABCI query requests under the hood.
|
||||
|
||||
In order to do that, module should implement `RegisterGRPCRoutes(clientCtx client.Context, mux *runtime.ServeMux)` on `AppModuleBasic` to wire the client gRPC requests to the correct handler inside the module.
|
||||
|
||||
Here's an example from the `auth` module:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/64b6bb5270e1a3b688c2d98a8f481ae04bb713ca/x/auth/module.go#L69-L72
|
||||
|
||||
## gRPC-gateway REST
|
||||
|
||||
Applications typically support web services that use HTTP requests (e.g. a web wallet like [Lunie.io](https://lunie.io). Thus, application developers can also use REST Routes to route HTTP requests to the application's modules; these routes will be used by service providers.
|
||||
|
||||
[grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway) translates REST calls into gRPC calls, which might be useful for clients that do not use gRPC.
|
||||
|
||||
Modules that want to expose REST queries should add `google.api.http` annotations to their `rpc` methods, such as in the example below from the `auth` module:
|
||||
|
||||
```proto
|
||||
// Query defines the gRPC querier service.
|
||||
service Query{
|
||||
// Account returns account details based on address.
|
||||
rpc Account (QueryAccountRequest) returns (QueryAccountResponse) {
|
||||
option (google.api.http).get = "/cosmos/auth/v1beta1/accounts/{address}";
|
||||
}
|
||||
|
||||
// Params queries all parameters.
|
||||
rpc Params (QueryParamsRequest) returns (QueryParamsResponse) {
|
||||
option (google.api.http).get = "/cosmos/auth/v1beta1/params";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
gRPC gateway is started in-process along with the application and Tendermint. It can be enabled or disabled by setting gRPC Configuration `enable` in `app.toml`.
|
||||
|
||||
The SDK provides a command for generating [Swagger](https://swagger.io/) documentation (`protoc-gen-swagger`). Setting `swagger` in `app.toml` defines if swagger documentation should be automatically registered.
|
||||
|
||||
## Legacy REST
|
||||
|
||||
Legacy REST endpoints will be deprecated. But developers may choose to keep using legacy REST endpoints for backward compatibility, although the recommended way is to use [gRPC](#grpc) and [gRPC-gateway](#grpc-gateway-rest).
|
||||
|
||||
With this implementation, module developers need to define the REST client by defining [routes](#register-routes) for all possible [requests](#request-types) and [handlers](#request-handlers) for each of them. It's up to the module developer how to organize the REST interface files; there is typically a `rest.go` file found in the module's `./x/moduleName/client/rest` folder.
|
||||
|
||||
To support HTTP requests, the module developer needs to define possible request types, how to handle them, and provide a way to register them with a provided router.
|
||||
|
||||
@@ -101,11 +142,11 @@ To support HTTP requests, the module developer needs to define possible request
|
||||
|
||||
Request types, which define structured interactions from users, must be defined for all _transaction_ requests. Users using this method to interact with an application will send HTTP Requests with the required fields in order to trigger state changes in the application. Conventionally, each request is named with the suffix `Req`, e.g. `SendReq` for a Send transaction. Each struct should include a base request [`baseReq`](../interfaces/rest.md#basereq), the name of the transaction, and all the arguments the user must provide for the transaction.
|
||||
|
||||
Here is an example of a request to buy a name from the `nameservice` module:
|
||||
Here is an example of a request to send coins from the `bank` module:
|
||||
|
||||
+++ https://github.com/cosmos/sdk-tutorials/blob/master/nameservice/x/nameservice/client/rest/tx.go#L14-L19
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7f59723d889b69ca19966167f0b3a7fec7a39e53/x/bank/client/rest/tx.go#L15-L19
|
||||
|
||||
The `BaseReq` includes basic information that every request needs to have, similar to required flags in a CLI. All of these values, including `GasPrices` and `AccountNumber`, will be provided in the request body. The user will also need to specify the arguments `Name` and `Amount` fields in the body and `Buyer` will be provided by the user's address.
|
||||
The `BaseReq` includes basic information that every request needs to have, similar to required flags in a CLI. All of these values, including `GasPrices` and `AccountNumber`, will be provided in the request body. The user will also need to specify the argument `Amount` fields in the body.
|
||||
|
||||
#### BaseReq
|
||||
|
||||
@@ -116,6 +157,7 @@ The `BaseReq` includes basic information that every request needs to have, simil
|
||||
- `ChainID` specifies the unique identifier of the blockchain the transaction pertains to.
|
||||
- `AccountNumber` is an identifier for the account.
|
||||
- `Sequence`is the value of a counter measuring how many transactions have been sent from the account. It is used to prevent replay attacks.
|
||||
- `TimeoutHeight` allows a transaction to be rejected if it's committed at a height greater than the timeout.
|
||||
- `Gas` refers to how much [gas](../basics/gas-fees.md), which represents computational resources, Tx consumes. Gas is dependent on the transaction and is not precisely calculated until execution, but can be estimated by providing auto as the value for `Gas`.
|
||||
- `GasAdjustment` can be used to scale gas up in order to avoid underestimating. For example, users can specify their gas adjustment as 1.5 to use 1.5 times the estimated gas.
|
||||
- `GasPrices` specifies how much the user is willing pay per unit of gas, which can be one or multiple denominations of tokens. For example, --gas-prices=0.025uatom, 0.025upho means the user is willing to pay 0.025uatom AND 0.025upho per unit of gas.
|
||||
@@ -124,17 +166,17 @@ The `BaseReq` includes basic information that every request needs to have, simil
|
||||
|
||||
### Request Handlers
|
||||
|
||||
Request handlers must be defined for both transaction and query requests. Handlers' arguments include a reference to the application's `codec` and the [`Context`](../interfaces/query-lifecycle.md#context) created in the user interaction.
|
||||
Request handlers must be defined for both transaction and query requests. Handlers' arguments include a reference to the [client `Context`](../interfaces/query-lifecycle.md#context).
|
||||
|
||||
Here is an example of a request handler for the nameservice module `buyNameReq` request (the same one shown above):
|
||||
Here is an example of a request handler for the `bank` module `SendReq` request (the same one shown above):
|
||||
|
||||
+++ https://github.com/cosmos/sdk-tutorials/blob/master/nameservice/x/nameservice/client/rest/tx.go#L21-L57
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7f59723d889b69ca19966167f0b3a7fec7a39e53/x/bank/client/rest/tx.go#L21-L51
|
||||
|
||||
The request handler can be broken down as follows:
|
||||
|
||||
- **Parse Request:** The request handler first attempts to parse the request, and then run `Sanitize` and `ValidateBasic` on the underlying `BaseReq` to check the validity of the request. Next, it attempts to parse the arguments `Buyer` and `Amount` to the types `AccountAddress` and `Coins` respectively.
|
||||
- **Message:** Then, a [message](./messages-and-queries.md) of the type `MsgBuyName` (defined by the module developer to trigger the state changes for this transaction) is created from the values and another sanity check, `ValidateBasic` is run on it.
|
||||
- **Generate Transaction:** Finally, the HTTP `ResponseWriter`, application [`codec`](../core/encoding.md), [`Context`](../interfaces/query-lifecycle.md#context), request [`BaseReq`](../interfaces/rest.md#basereq), and message is passed to `WriteGenerateStdTxResponse` to further process the request.
|
||||
- **Parse Request:** First, it tries to parse the argument `address` into a `AccountAddress`. Then, the request handler attempts to parse the request, and then run `Sanitize` and `ValidateBasic` on the underlying `BaseReq` to check the validity of the request. Finally, it attempts to parse `BaseReq.From` to the type `AccountAddress`.
|
||||
- **Message:** Then, a [message](./messages-and-queries.md#messages) of the type `MsgSend` (defined by the module developer to trigger the state changes for this transaction) is created from the values.
|
||||
- **Generate Transaction:** Finally, the HTTP `ResponseWriter`, client `Context`, request [`BaseReq`](../interfaces/rest.md#basereq), and message is passed to `WriteGeneratedTxResponse` to further process the request.
|
||||
|
||||
To read more about how a transaction is generated, visit the transactions documentation [here](../core/transactions.md#transaction-generation).
|
||||
|
||||
|
||||
@@ -29,15 +29,17 @@ are only used for genesis can take advantage of the `Module` patterns without ha
|
||||
|
||||
The `AppModuleBasic` interface defines the independent methods modules need to implement.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/module/module.go#L46-L59
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/325be6ff215db457c6fc7668109640cd7fdac461/types/module/module.go#L49-L63
|
||||
|
||||
Let us go through the methods:
|
||||
|
||||
- `Name()`: Returns the name of the module as a `string`.
|
||||
- `RegisterLegacyAminoCodec(*codec.LegacyAmino)`: Registers the `amino` codec for the module, which is used to marhsal and unmarshal structs to/from `[]byte` in order to persist them in the moduel's `KVStore`.
|
||||
- `DefaultGenesis()`: Returns a default [`GenesisState`](./genesis.md#genesisstate) for the module, marshalled to `json.RawMessage`. The default `GenesisState` need to be defined by the module developer and is primarily used for testing.
|
||||
- `ValidateGenesis(json.RawMessage)`: Used to validate the `GenesisState` defined by a module, given in its `json.RawMessage` form. It will usually unmarshall the `json` before running a custom [`ValidateGenesis`](./genesis.md#validategenesis) function defined by the module developer.
|
||||
- `RegisterLegacyAminoCodec(*codec.LegacyAmino)`: Registers the `amino` codec for the module, which is used to marshal and unmarshal structs to/from `[]byte` in order to persist them in the module's `KVStore`.
|
||||
- `RegisterInterfaces(codectypes.InterfaceRegistry)`: Registers a module's interface types and their concrete implementations as `proto.Message`.
|
||||
- `DefaultGenesis(codec.JSONMarshaler)`: Returns a default [`GenesisState`](./genesis.md#genesisstate) for the module, marshalled to `json.RawMessage`. The default `GenesisState` need to be defined by the module developer and is primarily used for testing.
|
||||
- `ValidateGenesis(codec.JSONMarshaler, client.TxEncodingConfig, json.RawMessage)`: Used to validate the `GenesisState` defined by a module, given in its `json.RawMessage` form. It will usually unmarshall the `json` before running a custom [`ValidateGenesis`](./genesis.md#validategenesis) function defined by the module developer.
|
||||
- `RegisterRESTRoutes(client.Context, *mux.Router)`: Registers the REST routes for the module. These routes will be used to map REST request to the module in order to process them. See [../interfaces/rest.md] for more.
|
||||
- `RegisterGRPCRoutes(client.Context, *runtime.ServeMux)`: Registers gRPC routes for the module.
|
||||
- `GetTxCmd()`: Returns the root [`Tx` command](./module-interfaces.md#tx) for the module. The subcommands of this root command are used by end-users to generate new transactions containing [`message`s](./messages-and-queries.md#queries) defined in the module.
|
||||
- `GetQueryCmd()`: Return the root [`query` command](./module-interfaces.md#query) for the module. The subcommands of this root command are used by end-users to generate new queries to the subset of the state defined by the module.
|
||||
|
||||
@@ -47,20 +49,20 @@ All the `AppModuleBasic` of an application are managed by the [`BasicManager`](#
|
||||
|
||||
The `AppModuleGenesis` interface is a simple embedding of the `AppModuleBasic` interface with two added methods.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/module/module.go#L126-L131
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/325be6ff215db457c6fc7668109640cd7fdac461/types/module/module.go#L152-L158
|
||||
|
||||
Let us go through the two added methods:
|
||||
|
||||
- `InitGenesis(sdk.Context, json.RawMessage)`: Initializes the subset of the state managed by the module. It is called at genesis (i.e. when the chain is first started).
|
||||
- `ExportGenesis(sdk.Context)`: Exports the latest subset of the state managed by the module to be used in a new genesis file. `ExportGenesis` is called for each module when a new chain is started from the state of an existing chain.
|
||||
- `InitGenesis(sdk.Context, codec.JSONMarshaler, json.RawMessage)`: Initializes the subset of the state managed by the module. It is called at genesis (i.e. when the chain is first started).
|
||||
- `ExportGenesis(sdk.Context, codec.JSONMarshaler)`: Exports the latest subset of the state managed by the module to be used in a new genesis file. `ExportGenesis` is called for each module when a new chain is started from the state of an existing chain.
|
||||
|
||||
It does not have its own manager, and exists separately from [`AppModule`](#appmodule) only for modules that exist only to implement genesis functionalities, so that they can be managed without having to implement all of `AppModule`'s methods. If the module is not only used during genesis, `InitGenesis(sdk.Context, json.RawMessage)` and `ExportGenesis(sdk.Context)` will generally be defined as methods of the concrete type implementing hte `AppModule` interface.
|
||||
It does not have its own manager, and exists separately from [`AppModule`](#appmodule) only for modules that exist only to implement genesis functionalities, so that they can be managed without having to implement all of `AppModule`'s methods. If the module is not only used during genesis, `InitGenesis(sdk.Context, codec.JSONMarshaler, json.RawMessage)` and `ExportGenesis(sdk.Context, codec.JSONMarshaler)` will generally be defined as methods of the concrete type implementing hte `AppModule` interface.
|
||||
|
||||
### `AppModule`
|
||||
|
||||
The `AppModule` interface defines the inter-dependent methods modules need to implement.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/module/module.go#L133-L149
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/228728cce2af8d494c8b4e996d011492139b04ab/types/module/module.go#L160-L182
|
||||
|
||||
`AppModule`s are managed by the [module manager](#manager). This interface embeds the `AppModuleGenesis` interface so that the manager can access all the independent and genesis inter-dependent methods of the module. This means that a concrete type implementing the `AppModule` interface must either implement all the methods of `AppModuleGenesis` (and by extension `AppModuleBasic`), or include a concrete type that does as parameter.
|
||||
|
||||
@@ -68,8 +70,9 @@ Let us go through the methods of `AppModule`:
|
||||
|
||||
- `RegisterInvariants(sdk.InvariantRegistry)`: Registers the [`invariants`](./invariants.md) of the module. If the invariants deviates from its predicted value, the [`InvariantRegistry`](./invariants.md#registry) triggers appropriate logic (most often the chain will be halted).
|
||||
- `Route()`: Returns the route for [`message`s](./messages-and-queries.md#messages) to be routed to the module by [`baseapp`](../core/baseapp.md#message-routing).
|
||||
- `QuerierRoute()`: Returns the name of the module's query route, for [`queries`](./messages-and-queries.md#queries) to be routes to the module by [`baseapp`](../core/baseapp.md#query-routing).
|
||||
- `NewQuerierHandler()`: Returns a [`querier`](./querier.md) given the query `path`, in order to process the `query`.
|
||||
- `QuerierRoute()` (deprecated): Returns the name of the module's query route, for [`queries`](./messages-and-queries.md#queries) to be routes to the module by [`baseapp`](../core/baseapp.md#query-routing).
|
||||
- `LegacyQuerierHandler(*codec.LegacyAmino)` (deprecated): Returns a [`querier`](./query-services.md#legacy-queriers) given the query `path`, in order to process the `query`.
|
||||
- `RegisterQueryService(grpc.Server)`: Allows a module to register a gRPC query service.
|
||||
- `BeginBlock(sdk.Context, abci.RequestBeginBlock)`: This method gives module developers the option to implement logic that is automatically triggered at the beginning of each block. Implement empty if no logic needs to be triggered at the beginning of each block for this module.
|
||||
- `EndBlock(sdk.Context, abci.RequestEndBlock)`: This method gives module developers the option to implement logic that is automatically triggered at the beginning of each block. This is also where the module can inform the underlying consensus engine of validator set changes (e.g. the `staking` module). Implement empty if no logic needs to be triggered at the beginning of each block for this module.
|
||||
|
||||
@@ -103,15 +106,17 @@ Module managers are used to manage collections of `AppModuleBasic` and `AppModul
|
||||
|
||||
The `BasicManager` is a structure that lists all the `AppModuleBasic` of an application:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/module/module.go#L61-L63
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/325be6ff215db457c6fc7668109640cd7fdac461/types/module/module.go#L65-L66
|
||||
|
||||
It implements the following methods:
|
||||
|
||||
- `NewBasicManager(modules ...AppModuleBasic)`: Constructor function. It takes a list of the application's `AppModuleBasic` and builds a new `BasicManager`. This function is generally called in the `init()` function of [`app.go`](../basics/app-anatomy.md#core-application-file) to quickly initialize the independent elements of the application's modules (click [here](https://github.com/cosmos/gaia/blob/master/app/app.go#L59-L74) to see an example).
|
||||
- `RegisterLegacyAminoCodec(cdc *codec.LegacyAmino)`: Registers the [`codec`s](../core/encoding.md) of each of the application's `AppModuleBasic`. This function is usually called early on in the [application's construction](../basics/app-anatomy.md#constructor).
|
||||
- `DefaultGenesis()`: Provides default genesis information for modules in the application by calling the [`DefaultGenesis()`](./genesis.md#defaultgenesis) function of each module. It is used to construct a default genesis file for the application.
|
||||
- `ValidateGenesis(genesis map[string]json.RawMessage)`: Validates the genesis information modules by calling the [`ValidateGenesis()`](./genesis.md#validategenesis) function of each module.
|
||||
- `RegisterLegacyAminoCodec(cdc *codec.LegacyAmino)`: Registers the [`codec.LegacyAmino`s](../core/encoding.md#amino) of each of the application's `AppModuleBasic`. This function is usually called early on in the [application's construction](../basics/app-anatomy.md#constructor).
|
||||
- `RegisterInterfaces(registry codectypes.InterfaceRegistry)`: Registers interface types and implementations of each of the application's `AppModuleBasic`.
|
||||
- `DefaultGenesis(cdc codec.JSONMarshaler)`: Provides default genesis information for modules in the application by calling the [`DefaultGenesis(cdc codec.JSONMarshaler)`](./genesis.md#defaultgenesis) function of each module. It is used to construct a default genesis file for the application.
|
||||
- `ValidateGenesis(cdc codec.JSONMarshaler, txEncCfg client.TxEncodingConfig, genesis map[string]json.RawMessage)`: Validates the genesis information modules by calling the [`ValidateGenesis(codec.JSONMarshaler, client.TxEncodingConfig, json.RawMessage)`](./genesis.md#validategenesis) function of each module.
|
||||
- `RegisterRESTRoutes(ctx client.Context, rtr *mux.Router)`: Registers REST routes for modules by calling the [`RegisterRESTRoutes`](./module-interfaces.md#register-routes) function of each module. This function is usually called function from the `main.go` function of the [application's command-line interface](../interfaces/cli.md).
|
||||
- `RegisterGRPCRoutes(clientCtx client.Context, rtr *runtime.ServeMux)`: Registers gRPC routes for modules.
|
||||
- `AddTxCommands(rootTxCmd *cobra.Command)`: Adds modules' transaction commands to the application's [`rootTxCommand`](../interfaces/cli.md#transaction-commands). This function is usually called function from the `main.go` function of the [application's command-line interface](../interfaces/cli.md).
|
||||
- `AddQueryCommands(rootQueryCmd *cobra.Command)`: Adds modules' query commands to the application's [`rootQueryCommand`](../interfaces/cli.md#query-commands). This function is usually called function from the `main.go` function of the [application's command-line interface](../interfaces/cli.md).
|
||||
|
||||
@@ -119,7 +124,7 @@ It implements the following methods:
|
||||
|
||||
The `Manager` is a structure that holds all the `AppModule` of an application, and defines the order of execution between several key components of these modules:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/module/module.go#L190-L198
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/325be6ff215db457c6fc7668109640cd7fdac461/types/module/module.go#L223-L231
|
||||
|
||||
The module manager is used throughout the application whenever an action on a collection of modules is required. It implements the following methods:
|
||||
|
||||
@@ -129,12 +134,17 @@ The module manager is used throughout the application whenever an action on a co
|
||||
- `SetOrderBeginBlockers(moduleNames ...string)`: Sets the order in which the `BeginBlock()` function of each module will be called at the beginning of each block. This function is generally called from the application's main [constructor function](../basics/app-anatomy.md#constructor-function).
|
||||
- `SetOrderEndBlockers(moduleNames ...string)`: Sets the order in which the `EndBlock()` function of each module will be called at the beginning of each block. This function is generally called from the application's main [constructor function](../basics/app-anatomy.md#constructor-function).
|
||||
- `RegisterInvariants(ir sdk.InvariantRegistry)`: Registers the [invariants](./invariants.md) of each module.
|
||||
- `RegisterRoutes(router sdk.Router, queryRouter sdk.QueryRouter)`: Registers module routes to the application's `router`, in order to route [`message`s](./messages-and-queries.md#messages) to the appropriate [`handler`](./handler.md), and module query routes to the application's `queryRouter`, in order to route [`queries`](./messages-and-queries.md#queries) to the appropriate [`querier`](./querier.md).
|
||||
- `InitGenesis(ctx sdk.Context, genesisData map[string]json.RawMessage)`: Calls the [`InitGenesis`](./genesis.md#initgenesis) function of each module when the application is first started, in the order defined in `OrderInitGenesis`. Returns an `abci.ResponseInitChain` to the underlying consensus engine, which can contain validator updates.
|
||||
- `ExportGenesis(ctx sdk.Context)`: Calls the [`ExportGenesis`](./genesis.md#exportgenesis) function of each module, in the order defined in `OrderExportGenesis`. The export constructs a genesis file from a previously existing state, and is mainly used when a hard-fork upgrade of the chain is required.
|
||||
- `RegisterRoutes(router sdk.Router, queryRouter sdk.QueryRouter, legacyQuerierCdc *codec.LegacyAmino)`: Registers module routes to the application's `router`, in order to route [`message`s](./messages-and-queries.md#messages) to the appropriate [`handler`](./handler.md), and module query routes to the application's `queryRouter`, in order to route [`queries`](./messages-and-queries.md#queries) to the appropriate [`querier`](./query-services.md#legacy-queriers).
|
||||
- `RegisterQueryServices(grpcRouter grpc.Server)`: Registers all module gRPC query services.
|
||||
- `InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, genesisData map[string]json.RawMessage)`: Calls the [`InitGenesis`](./genesis.md#initgenesis) function of each module when the application is first started, in the order defined in `OrderInitGenesis`. Returns an `abci.ResponseInitChain` to the underlying consensus engine, which can contain validator updates.
|
||||
- `ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler)`: Calls the [`ExportGenesis`](./genesis.md#exportgenesis) function of each module, in the order defined in `OrderExportGenesis`. The export constructs a genesis file from a previously existing state, and is mainly used when a hard-fork upgrade of the chain is required.
|
||||
- `BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock)`: At the beginning of each block, this function is called from [`baseapp`](../core/baseapp.md#beginblock) and, in turn, calls the [`BeginBlock`](./beginblock-endblock.md) function of each module, in the order defined in `OrderBeginBlockers`. It creates a child [context](../core/context.md) with an event manager to aggregate [events](../core/events.md) emitted from all modules. The function returns an `abci.ResponseBeginBlock` which contains the aforementioned events.
|
||||
- `EndBlock(ctx sdk.Context, req abci.RequestEndBlock)`: At the end of each block, this function is called from [`baseapp`](../core/baseapp.md#endblock) and, in turn, calls the [`EndBlock`](./beginblock-endblock.md) function of each module, in the order defined in `OrderEndBlockers`. It creates a child [context](../core/context.md) with an event manager to aggregate [events](../core/events.md) emitted from all modules. The function returns an `abci.ResponseEndBlock` which contains the aforementioned events, as well as validator set updates (if any).
|
||||
|
||||
Here's an example of a concrete integration within an application:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/2323f1ac0e9a69a0da6b43693061036134193464/simapp/app.go#L315-L362
|
||||
|
||||
## Next {hide}
|
||||
|
||||
Learn more about [`message`s and `queries`](./messages-and-queries.md) {hide}
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
<!--
|
||||
order: 5
|
||||
-->
|
||||
|
||||
# Queriers
|
||||
|
||||
A `Querier` designates a function that processes [`queries`](./messages-and-queries.md#queries). `querier`s 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
|
||||
|
||||
- [Module Manager](./module-manager.md) {prereq}
|
||||
- [Messages and Queries](./messages-and-queries.md) {prereq}
|
||||
|
||||
## `Querier` type
|
||||
|
||||
The `querier` type defined in the Cosmos SDK specifies the typical structure of a `querier` function:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/queryable.go#L6
|
||||
|
||||
Let us break it down:
|
||||
|
||||
- The `path` is an array of `string`s that contains the type of the query, and that can also contain `query` arguments. See [`queries`](./messages-and-queries.md#queries) for more information.
|
||||
- The `req` itself is primarily used to retrieve arguments if they are too large to fit in the `path`. This is done using the `Data` field of `req`.
|
||||
- The [`Context`](../core/context.md) contains all the necessary information needed to process the `query`, as well as a cache-wrapped copy of the latest state. It is primarily used by the [`keeper`](./keeper.md) to access the state.
|
||||
- The result `res` returned to `baseapp`, marhsalled using the application's [`codec`](../core/encoding.md).
|
||||
|
||||
## Implementation of a module `querier`s
|
||||
|
||||
Module `querier`s are typically implemented in a `./internal/keeper/querier.go` file inside the module's folder. The [module manager](./module-manager.md) is used to add the module's `querier`s to the [application's `queryRouter`](../core/baseapp.md#query-routing) via the `NewQuerier()` method. Typically, the manager's `NewQuerier()` method simply calls a `NewQuerier()` method defined in `keeper/querier.go`, which looks like the following:
|
||||
|
||||
```go
|
||||
func NewQuerier(keeper Keeper) sdk.Querier {
|
||||
return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) {
|
||||
switch path[0] {
|
||||
case QueryType1:
|
||||
return queryType1(ctx, path[1:], req, keeper)
|
||||
|
||||
case QueryType2:
|
||||
return queryType2(ctx, path[1:], req, keeper)
|
||||
|
||||
default:
|
||||
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint: %s", types.ModuleName, path[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
For a deeper look at `querier`s, see this [example implementation of a `querier` function](https://github.com/cosmos/sdk-application-tutorial/blob/c6754a1e313eb1ed973c5c91dcc606f2fd288811/x/nameservice/internal/keeper/querier.go) from the nameservice tutorial.
|
||||
|
||||
## Next {hide}
|
||||
|
||||
Learn about [`BeginBlocker` and `EndBlocker`](./beginblock-endblock.md) {hide}
|
||||
@@ -0,0 +1,77 @@
|
||||
<!--
|
||||
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}
|
||||
|
||||
## Pre-requisite Readings
|
||||
|
||||
- [Module Manager](./module-manager.md) {prereq}
|
||||
- [Messages and Queries](./messages-and-queries.md) {prereq}
|
||||
|
||||
## `Querier` type
|
||||
|
||||
The `querier` type defined in the Cosmos SDK will be deprecated in favor of [gRPC Services](#grpc-service). It specifies the typical structure of a `querier` function:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/9a183ffbcc0163c8deb71c7fd5f8089a83e58f05/types/queryable.go#L9
|
||||
|
||||
Let us break it down:
|
||||
|
||||
- The `path` is an array of `string`s that contains the type of the query, and that can also contain `query` arguments. See [`queries`](./messages-and-queries.md#queries) for more information.
|
||||
- The `req` itself is primarily used to retrieve arguments if they are too large to fit in the `path`. This is done using the `Data` field of `req`.
|
||||
- The [`Context`](../core/context.md) contains all the necessary information needed to process the `query`, as well as a cache-wrapped copy of the latest state. It is primarily used by the [`keeper`](./keeper.md) to access the state.
|
||||
- The result `res` returned to `baseapp`, marshalled using the application's [`codec`](../core/encoding.md).
|
||||
|
||||
## Implementation of a module query service
|
||||
|
||||
### gRPC Service
|
||||
|
||||
When defining a Protobuf `Query` service, a `QueryServer` interface is generated for each module with all the service methods:
|
||||
|
||||
```go
|
||||
type QueryServer interface {
|
||||
QueryBalance(context.Context, *QueryBalanceParams) (*types.Coin, error)
|
||||
QueryAllBalances(context.Context, *QueryAllBalancesParams) (*QueryAllBalancesResponse, error)
|
||||
}
|
||||
```
|
||||
|
||||
These custom queries methods should be implemented by a module's keeper, typically in `./keeper/grpc_query.go`. The first parameter of these methods is a generic `context.Context`, whereas querier methods generally need an instance of `sdk.Context` to read
|
||||
from the store. Therefore, the SDK provides a function `sdk.UnwrapSDKContext` to retrieve the `sdk.Context` from the provided
|
||||
`context.Context`.
|
||||
|
||||
Here's an example implementation for the bank module:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/x/bank/keeper/grpc_query.go
|
||||
|
||||
### Legacy Queriers
|
||||
|
||||
Module legacy `querier`s are typically implemented in a `./keeper/querier.go` file inside the module's folder. The [module manager](./module-manager.md) is used to add the module's `querier`s to the [application's `queryRouter`](../core/baseapp.md#query-routing) via the `NewQuerier()` method. Typically, the manager's `NewQuerier()` method simply calls a `NewQuerier()` method defined in `keeper/querier.go`, which looks like the following:
|
||||
|
||||
```go
|
||||
func NewQuerier(keeper Keeper) sdk.Querier {
|
||||
return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) {
|
||||
switch path[0] {
|
||||
case QueryType1:
|
||||
return queryType1(ctx, path[1:], req, keeper)
|
||||
|
||||
case QueryType2:
|
||||
return queryType2(ctx, path[1:], req, keeper)
|
||||
|
||||
default:
|
||||
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint: %s", types.ModuleName, path[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
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}
|
||||
|
||||
Learn about [`BeginBlocker` and `EndBlocker`](./beginblock-endblock.md) {hide}
|
||||
@@ -27,6 +27,7 @@ x/{module}
|
||||
│ ├── keeper.go
|
||||
│ ├── ...
|
||||
│ └── querier.go
|
||||
│ └── grpc_query.go
|
||||
├── types
|
||||
│ ├── codec.go
|
||||
│ ├── errors.go
|
||||
@@ -36,10 +37,12 @@ x/{module}
|
||||
│ ├── keys.go
|
||||
│ ├── msgs.go
|
||||
│ ├── params.go
|
||||
│ ├── types.pb.go
|
||||
│ ├── types.proto
|
||||
│ ├── ...
|
||||
│ └── querier.go
|
||||
│ └── {module_name}.pb.go
|
||||
│ └── query.pb.go
|
||||
│ └── genesis.pb.go
|
||||
├── simulation
|
||||
│ ├── decoder.go
|
||||
│ ├── genesis.go
|
||||
|
||||
@@ -200,7 +200,7 @@ The application's `router` is initilalized with all the routes using the applica
|
||||
|
||||
### Query Routing
|
||||
|
||||
Similar to `message`s, [`queries`](../building-modules/messages-and-queries.md#queries) need to be routed to the appropriate module's [querier](../building-modules/querier.md). To do so, `baseapp` holds a `query router`, which maps module names to module `querier`s. The `queryRouter` is called during the initial stages of `query` processing, which is done via the [`Query` ABCI message](#query).
|
||||
Similar to `message`s, [`queries`](../building-modules/messages-and-queries.md#queries) need to be routed to the appropriate module's [querier](../building-modules/query-services.md). To do so, `baseapp` holds a `query router`, which maps module names to module `querier`s. The `queryRouter` is called during the initial stages of `query` processing, which is done via the [`Query` ABCI message](#query).
|
||||
|
||||
Just like the `router`, the `query router` is initilalized with all the query routes using the application's [module manager](../building-modules/module-manager.md), which itself is initialized with all the application's modules in the application's [constructor](../basics/app-anatomy.md#app-constructor).
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ Modules are encouraged to utilize Protobuf encoding for their respective types.
|
||||
Protobuf types can be defined to encode:
|
||||
- state
|
||||
- [`Msg`s](../building-modules/messages-and-queries.md#messages)
|
||||
- [queries](../building-modules/querier.md)
|
||||
- [Query services](../building-modules/query-services.md)
|
||||
- [genesis](../building-modules/genesis.md)
|
||||
|
||||
**Naming and conventions**
|
||||
|
||||
@@ -39,7 +39,7 @@ A transaction is created by an end-user through one of the possible [interfaces]
|
||||
|
||||
Application developers create entrypoints to the application by creating a [command-line interface](../interfaces/cli.md) and/or [REST interface](../interfaces/rest.md), typically found in the application's `./cmd` folder. These interfaces allow users to interact with the application through command-line or through HTTP requests.
|
||||
|
||||
For the [command-line interface](../building-modules/module-interfaces.md#cli), module developers create subcommands to add as children to the application top-level transaction command `TxCmd`. For [HTTP requests](../building-modules/module-interfaces.md#rest), module developers specify acceptable request types, register REST routes, and create HTTP Request Handlers.
|
||||
For the [command-line interface](../building-modules/module-interfaces.md#cli), module developers create subcommands to add as children to the application top-level transaction command `TxCmd`. For [HTTP requests](../building-modules/module-interfaces.md#legacy-rest), module developers specify acceptable request types, register REST routes, and create HTTP Request Handlers.
|
||||
|
||||
When users interact with the application's interfaces, they invoke the underlying modules' handlers or command functions, directly creating messages.
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ params := types.NewQueryDelegatorParams(delegatorAddr)
|
||||
|
||||
#### Query Route Creation
|
||||
|
||||
Important to note is that there will never be a "query" object created for `Query`; the SDK actually takes a simpler approach. Instead of an object, all the full-node needs to process a query is its `route` which specifies exactly which module to route the query to and the name of this query type. The `route` will be passed to the application `baseapp`, then module, then [querier](../building-modules/querier.md), and each will understand the `route` and pass it to the appropriate next step. [`baseapp`](../core/baseapp.md#query-routing) will understand this query to be a `custom` query in the module `staking`, and the `staking` module querier supports the type `QueryDelegatorDelegations`. Thus, the route will be `"custom/staking/delegatorDelegations"`.
|
||||
Important to note is that there will never be a "query" object created for `Query`; the SDK actually takes a simpler approach. Instead of an object, all the full-node needs to process a query is its `route` which specifies exactly which module to route the query to and the name of this query type. The `route` will be passed to the application `baseapp`, then module, then [querier](../building-modules/query-services.md#legacy-queriers), and each will understand the `route` and pass it to the appropriate next step. [`baseapp`](../core/baseapp.md#query-routing) will understand this query to be a `custom` query in the module `staking`, and the `staking` module querier supports the type `QueryDelegatorDelegations`. Thus, the route will be `"custom/staking/delegatorDelegations"`.
|
||||
|
||||
Here is what the code looks like:
|
||||
|
||||
@@ -133,9 +133,9 @@ Read more about ABCI Clients and Tendermint RPC in the Tendermint documentation
|
||||
|
||||
## Application Query Handling
|
||||
|
||||
When a query is received by the full-node after it has been relayed from the underlying consensus engine, it is now being handled within an environment that understands application-specific types and has a copy of the state. [`baseapp`](../core/baseapp.md) implements the ABCI [`Query()`](../core/baseapp.md#query) function and handles four different types of queries: `app`, `store`, `p2p`, and `custom`. The `queryRoute` is parsed such that the first string must be one of the four options, then the rest of the path is parsed within the subroutines handling each type of query. The first three types (`app`, `store`, `p2p`) are purely application-level and thus directly handled by `baseapp` or the stores, but the `custom` query type requires `baseapp` to route the query to a module's [querier](../building-modules/querier.md).
|
||||
When a query is received by the full-node after it has been relayed from the underlying consensus engine, it is now being handled within an environment that understands application-specific types and has a copy of the state. [`baseapp`](../core/baseapp.md) implements the ABCI [`Query()`](../core/baseapp.md#query) function and handles four different types of queries: `app`, `store`, `p2p`, and `custom`. The `queryRoute` is parsed such that the first string must be one of the four options, then the rest of the path is parsed within the subroutines handling each type of query. The first three types (`app`, `store`, `p2p`) are purely application-level and thus directly handled by `baseapp` or the stores, but the `custom` query type requires `baseapp` to route the query to a module's [query service](../building-modules/query-services.md).
|
||||
|
||||
Since `Query` is a custom query type from the `staking` module, `baseapp` first parses the path, then uses the `QueryRouter` to retrieve the corresponding querier, and routes the query to the module. The querier is responsible for recognizing this query, retrieving the appropriate values from the application's stores, and returning a response. Read more about queriers [here](../building-modules/querier.md).
|
||||
Since `Query` is a custom query type from the `staking` module, `baseapp` first parses the path, then uses the `QueryRouter` to retrieve the corresponding querier, and routes the query to the module. The querier is responsible for recognizing this query, retrieving the appropriate values from the application's stores, and returning a response. Read more about query services [here](../building-modules/query-services.md).
|
||||
|
||||
Once a result is received from the querier, `baseapp` begins the process of returning a response to the user.
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ order: 4
|
||||
|
||||
# REST Interface
|
||||
|
||||
This document describes how to create a REST interface for an SDK **application**. A separate document for creating a [**module**](../building-modules/intro.md) REST interface can be found [here](#../module-interfaces.md#rest). {synopsis}
|
||||
This document describes how to create a REST interface for an SDK **application**. A separate document for creating a [**module**](../building-modules/intro.md) REST interface can be found [here](#../module-interfaces.md#legacy-rest). {synopsis}
|
||||
|
||||
## Pre-requisite Readings
|
||||
|
||||
@@ -41,7 +41,7 @@ In order to enable the REST Server in an SDK application, the `rest.ServeCommand
|
||||
|
||||
## Registering Routes
|
||||
|
||||
To include routes for each module in an application, the CLI must have some kind of function to register routes in its REST Server. This function is called `RegisterRoutes()`, and is utilized by the `ServeCommand` and must include routes for each of the application's modules. Since each module used by an SDK application implements a [`RegisterRESTRoutes`](../building-modules/module-interfaces.md#rest) function, application developers simply use the [Module Manager](../building-modules/module-manager.md) to call this function for each module (this is done in the [application's constructor](../basics/app-anatomy.md#constructor-function)).
|
||||
To include routes for each module in an application, the CLI must have some kind of function to register routes in its REST Server. This function is called `RegisterRoutes()`, and is utilized by the `ServeCommand` and must include routes for each of the application's modules. Since each module used by an SDK application implements a [`RegisterRESTRoutes`](../building-modules/module-interfaces.md#legacy-rest) function, application developers simply use the [Module Manager](../building-modules/module-manager.md) to call this function for each module (this is done in the [application's constructor](../basics/app-anatomy.md#constructor-function)).
|
||||
|
||||
At the bare minimum, a `RegisterRoutes()` function should use the SDK client package `RegisterRoutes()` function to be able to route RPC calls, and instruct the application Module Manager to call `RegisterRESTRoutes()` for all of its modules. This is done in the `main.go` file of the CLI (typically located in `./cmd/appcli/main.go`).
|
||||
|
||||
|
||||
+18
-23
@@ -19,33 +19,28 @@ element as a part of a larger description.
|
||||
|
||||
## Common Layout
|
||||
|
||||
The specifications should be contained in a single `README.md` file inside the
|
||||
`spec/` folder of a given module.
|
||||
The following generalized file structure should be used to breakdown
|
||||
specifications for modules. With the exception of README.md, `XX` at the
|
||||
beginning of the file name should be replaced with a number to indicate
|
||||
document flow (ex. read `01_state.md` before `02_state_transitions.md`). The
|
||||
following list is nonbinding and all files are optional.
|
||||
|
||||
The following generalized document structure should be used to breakdown
|
||||
specifications for modules. Each bullet item corresponds to a new section in
|
||||
the document, and should begin with a secondary heading (`## {HEADING}` in
|
||||
Markdown). The `XX` at the beginning of the section name should be replaced
|
||||
with a number to indicate document flow (ex. read `01. Concepts` before
|
||||
`02. State Transitions`). The following list is nonbinding and all sections are
|
||||
optional.
|
||||
|
||||
- `XX. Abstract` - overview of the module
|
||||
- `XX. Concepts` - describe specialized concepts and definitions used throughout the spec
|
||||
- `XX. State` - specify and describe structures expected to marshalled into the store, and their keys
|
||||
- `XX. State Transitions` - standard state transition operations triggered by hooks, messages, etc.
|
||||
- `XX. Messages` - specify message structure(s) and expected state machine behaviour(s)
|
||||
- `XX. BeginBlock` - specify any begin-block operations
|
||||
- `XX. EndBlock` - specify any end-block operations
|
||||
- `XX. Hooks` - describe available hooks to be called by/from this module
|
||||
- `XX. Events` - list and describe event tags used
|
||||
- `XX. Params` - list all module parameters, their types (in JSON) and examples
|
||||
- `XX. Future Improvements` - describe future improvements of this module
|
||||
- `XX. Appendix` - supplementary details referenced elsewhere within the spec
|
||||
- `README.md` - overview of the module
|
||||
- `XX_concepts.md` - describe specialized concepts and definitions used throughout the spec
|
||||
- `XX_state.md` - specify and describe structures expected to marshalled into the store, and their keys
|
||||
- `XX_state_transitions.md` - standard state transition operations triggered by hooks, messages, etc.
|
||||
- `XX_messages.md` - specify message structure(s) and expected state machine behaviour(s)
|
||||
- `XX_begin_block.md` - specify any begin-block operations
|
||||
- `XX_end_block.md` - specify any end-block operations
|
||||
- `XX_hooks.md` - describe available hooks to be called by/from this module
|
||||
- `XX_events.md` - list and describe event tags used
|
||||
- `XX_params.md` - list all module parameters, their types (in JSON) and examples
|
||||
- `XX_future_improvements.md` - describe future improvements of this module
|
||||
- `XX_appendix.md` - supplementary details referenced elsewhere within the spec
|
||||
|
||||
### Notation for key-value mapping
|
||||
|
||||
Within the `State` section, the following notation `->` should be used to describe key to
|
||||
Within `state.md` the following notation `->` should be used to describe key to
|
||||
value mapping:
|
||||
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user