chore: add markdownlint to lint commands (#9353)

* add markdownlint config

* update make lint commands

* update markdownlint config

* run make lint-fix

* fix empty link

* resuse docker container

* run lint-fix

* do not echo commands

Co-authored-by: ryanchrypto <12519942+ryanchrypto@users.noreply.github.com>
This commit is contained in:
Ryan Christoffersen
2021-05-27 15:31:04 +00:00
committed by GitHub
co-authored by ryanchrypto
parent b56e1a164b
commit cb66c99eab
147 changed files with 806 additions and 771 deletions
-4
View File
@@ -6,7 +6,6 @@
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.
@@ -23,7 +22,6 @@ ADR creation is an **iterative** process. Instead of trying to solve all decisio
6. Merged ADRs SHOULD NOT be pruned.
### ADR status
Status has two components:
@@ -44,7 +42,6 @@ DRAFT -> PROPOSED -> LAST CALL yyyy-mm-dd -> ACCEPTED | REJECTED -> SUPERSEEDED
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.
@@ -53,7 +50,6 @@ DRAFT -> PROPOSED -> LAST CALL yyyy-mm-dd -> ACCEPTED | REJECTED -> SUPERSEEDED
+ `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.
-1
View File
@@ -32,7 +32,6 @@ 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.
## Creating new ADR
Read about the [PROCESS](./PROCESS.md).
@@ -33,7 +33,7 @@ past transactions and assigned to particular modes), and keep them in a memory-o
chain is running.
The `CapabilityKeeper` will include a persistent `KVStore`, a `MemoryStore`, and an in-memory map.
The persistent `KVStore` tracks which capability is owned by which modules.
The persistent `KVStore` tracks which capability is owned by which modules.
The `MemoryStore` stores a forward mapping that map from module name, capability tuples to capability names and
a reverse mapping that map from module name, capability name to the capability index.
Since we cannot marshal the capability into a `KVStore` and unmarshal without changing the memory location of the capability,
@@ -7,12 +7,10 @@
- 2020-01-14: Updates from review feedback
- 2020-01-30: Updates from implementation
### Glossary
* denom / denomination key -- unique token identifier.
## Context
With permissionless IBC, anyone will be able to send arbitrary denominations to any other account. Currently, all non-zero balances are stored along with the account in an `sdk.Coins` struct, which creates a potential denial-of-service concern, as too many denominations will become expensive to load & store each time the account is modified. See issues [5467](https://github.com/cosmos/cosmos-sdk/issues/5467) and [4982](https://github.com/cosmos/cosmos-sdk/issues/4982) for additional context.
@@ -9,23 +9,20 @@
## Context
Currently, an SDK application's CLI directory stores key material and metadata in a plain text database in the users home directory. Key material is encrypted by a passphrase, protected by bcrypt hashing algorithm. Metadata (e.g. addresses, public keys, key storage details) is available in plain text.
Currently, an SDK application's CLI directory stores key material and metadata in a plain text database in the users home directory. Key material is encrypted by a passphrase, protected by bcrypt hashing algorithm. Metadata (e.g. addresses, public keys, key storage details) is available in plain text.
This is not desirable for a number of reasons. Perhaps the biggest reason is insufficient security protection of key material and metadata. Leaking the plain text allows an attacker to surveil what keys a given computer controls via a number of techniques, like compromised dependencies without any privilege execution. This could be followed by a more targeted attack on a particular user/computer.
All modern desktop computers OS (Ubuntu, Debian, MacOS, Windows) provide a built-in secret store that is designed to allow applications to store information that is isolated from all other applications and requires passphrase entry to access the data.
All modern desktop computers OS (Ubuntu, Debian, MacOS, Windows) provide a built-in secret store that is designed to allow applications to store information that is isolated from all other applications and requires passphrase entry to access the data.
We are seeking solution that provides a common abstraction layer to the many different backends and reasonable fallback for minimal platforms that dont provide a native secret store.
## Decision
We recommend replacing the current Keybase backend based on LevelDB with [Keyring](https://github.com/99designs/keyring) by 99 designs. This application is designed to provide a common abstraction and uniform interface between many secret stores and is used by AWS Vault application by 99-designs application.
This appears to fulfill the requirement of protecting both key material and metadata from rouge software on a users machine.
## Status
Accepted
@@ -55,4 +52,3 @@ Running tests locally on a Mac require numerous repetitive password entries.
- #5097 Add keys migrate command [__MERGED__]
- #5180 Drop on-disk keybase in favor of keyring [_PENDING_REVIEW_]
- cosmos/gaia#164 Drop on-disk keybase in favor of keyring (gaia's changes) [_PENDING_REVIEW_]
@@ -11,7 +11,7 @@ creation of a decentralized Computer Emergency Response Team (dCERT), whose
members would be elected by a governing community and would fulfill the role of
coordinating the community under emergency situations. This thinking
can be further abstracted into the conception of "blockchain specialization
groups".
groups".
The creation of these groups are the beginning of specialization capabilities
within a wider blockchain community which could be used to enable a certain
@@ -19,30 +19,29 @@ level of delegated responsibilities. Examples of specialization which could be
beneficial to a blockchain community include: code auditing, emergency response,
code development etc. This type of community organization paves the way for
individual stakeholders to delegate votes by issue type, if in the future
governance proposals include a field for issue type.
governance proposals include a field for issue type.
## Decision
A specialization group can be broadly broken down into the following functions
(herein containing examples):
- Membership Admittance
- Membership Acceptance
- Membership Revocation
- (probably) Without Penalty
- member steps down (self-Revocation)
- replaced by new member from governance
- (probably) With Penalty
- due to breach of soft-agreement (determined through governance)
- due to breach of hard-agreement (determined by code)
- Execution of Duties
- Special transactions which only execute for members of a specialization
- Membership Admittance
- Membership Acceptance
- Membership Revocation
- (probably) Without Penalty
- member steps down (self-Revocation)
- replaced by new member from governance
- (probably) With Penalty
- due to breach of soft-agreement (determined through governance)
- due to breach of hard-agreement (determined by code)
- Execution of Duties
- Special transactions which only execute for members of a specialization
group (for example, dCERT members voting to turn off transaction routes in
an emergency scenario)
- Compensation
- Group compensation (further distribution decided by the specialization group)
- Individual compensation for all constituents of a group from the
an emergency scenario)
- Compensation
- Group compensation (further distribution decided by the specialization group)
- Individual compensation for all constituents of a group from the
greater community
Membership admittance to a specialization group could take place over a wide
@@ -56,31 +55,31 @@ some of these possiblities in a common interface dubbed the `Electionator`. For
its initial implementation as a part of this ADR we recommend that the general
election abstraction (`Electionator`) is provided as well as a basic
implementation of that abstraction which allows for a continuous election of
members of a specialization group.
members of a specialization group.
``` golang
// The Electionator abstraction covers the concept space for
// The Electionator abstraction covers the concept space for
// a wide variety of election kinds.
type Electionator interface {
// is the election object accepting votes.
Active() bool
Active() bool
// functionality to execute for when a vote is cast in this election, here
// the vote field is anticipated to be marshalled into a vote type used
// by an election.
//
// the vote field is anticipated to be marshalled into a vote type used
// by an election.
//
// NOTE There are no explicit ids here. Just votes which pertain specifically
// to one electionator. Anyone can create and send a vote to the electionator item
// which will presumably attempt to marshal those bytes into a particular struct
// and apply the vote information in some arbitrary way. There can be multiple
// Electionators within the Cosmos-Hub for multiple specialization groups, votes
// would need to be routed to the Electionator upstream of here.
Vote(addr sdk.AccAddress, vote []byte)
Vote(addr sdk.AccAddress, vote []byte)
// here lies all functionality to authenticate and execute changes for
// when a member accepts being elected
AcceptElection(sdk.AccAddress)
AcceptElection(sdk.AccAddress)
// Register a revoker object
RegisterRevoker(Revoker)
@@ -89,25 +88,25 @@ type Electionator interface {
SealRevokers()
// register hooks to call when an election actions occur
RegisterHooks(ElectionatorHooks)
RegisterHooks(ElectionatorHooks)
// query for the current winner(s) of this election based on arbitrary
// election ruleset
QueryElected() []sdk.AccAddress
QueryElected() []sdk.AccAddress
// query metadata for an address in the election this
// query metadata for an address in the election this
// could include for example position that an address
// is being elected for within a group
//
// this metadata may be directly related to
// is being elected for within a group
//
// this metadata may be directly related to
// voting information and/or privileges enabled
// to members within a group.
// to members within a group.
QueryMetadata(sdk.AccAddress) []byte
}
// ElectionatorHooks, once registered with an Electionator,
// trigger execution of relevant interface functions when
// Electionator events occur.
// ElectionatorHooks, once registered with an Electionator,
// trigger execution of relevant interface functions when
// Electionator events occur.
type ElectionatorHooks interface {
AfterVoteCast(addr sdk.AccAddress, vote []byte)
AfterMemberAccepted(addr sdk.AccAddress)
@@ -117,30 +116,30 @@ type ElectionatorHooks interface {
// Revoker defines the function required for a membership revocation rule-set
// used by a specialization group. This could be used to create self revoking,
// and evidence based revoking, etc. Revokers types may be created and
// reused for different election types.
//
// reused for different election types.
//
// When revoking the "cause" bytes may be arbitrarily marshalled into evidence,
// memos, etc.
type Revoker interface {
RevokeName() string // identifier for this revoker type
RevokeName() string // identifier for this revoker type
RevokeMember(addr sdk.AccAddress, cause []byte) error
}
```
Certain level of commonality likely exists between the existing code within
`x/governance` and required functionality of elections. This common
functionality should be abstracted during implementation. Similarly for each
functionality should be abstracted during implementation. Similarly for each
vote implementation client CLI/REST functionality should be abstracted
to be reused for multiple elections.
to be reused for multiple elections.
The specialization group abstraction firstly extends the `Electionator`
but also further defines traits of the group.
but also further defines traits of the group.
``` golang
type SpecializationGroup interface {
Electionator
Electionator
GetName() string
GetDescription() string
GetDescription() string
// general soft contract the group is expected
// to fulfill with the greater community
@@ -151,7 +150,7 @@ type SpecializationGroup interface {
// logic to be executed at endblock, this may for instance
// include payment of a stipend to the group members
// for participation in the security group.
// for participation in the security group.
EndBlocker(ctx sdk.Context)
}
```
@@ -164,16 +163,15 @@ type SpecializationGroup interface {
### Positive
- increases specialization capabilities of a blockchain
- improve abstractions in `x/gov/` such that they can be used with specialization groups
- increases specialization capabilities of a blockchain
- improve abstractions in `x/gov/` such that they can be used with specialization groups
### Negative
- could be used to increase centralization within a community
- could be used to increase centralization within a community
### Neutral
## References
- (dCERT ADR)[./adr-008-dCERT-group.md]
- [dCERT ADR](./adr-008-dCERT-group.md)
+50 -46
View File
@@ -15,13 +15,13 @@ bug-hunters, and developers. During a time of crisis, the dCERT group would
aggregate and relay input from a variety of stakeholders to the developers who
are actively devising a patch to the software, this way sensitive information
does not need to be publicly disclosed while some input from the community can
still be gained.
still be gained.
Additionally, a special privilege is proposed for the dCERT group: the capacity
to "circuit-break" (aka. temporarily disable) a particular message path. Note
that this privilege should be enabled/disabled globally with a governance
parameter such that this privilege could start disabled and later be enabled
through a parameter change proposal, once a dCERT group has been established.
through a parameter change proposal, once a dCERT group has been established.
In the future it is foreseeable that the community may wish to expand the roles
of dCERT with further responsibilities such as the capacity to "pre-approve" a
@@ -32,52 +32,55 @@ vulnerability being patched on the live network.
## Decision
The dCERT group is proposed to include an implementation of a `SpecializationGroup`
as defined in [ADR 007](./adr-007-specialization-groups.md). This will include the
implementation of:
- continuous voting
- slashing due to breach of soft contract
- revoking a member due to breach of soft contract
- emergency disband of the entire dCERT group (ex. for colluding maliciously)
- compensation stipend from the community pool or other means decided by
as defined in [ADR 007](./adr-007-specialization-groups.md). This will include the
implementation of:
- continuous voting
- slashing due to breach of soft contract
- revoking a member due to breach of soft contract
- emergency disband of the entire dCERT group (ex. for colluding maliciously)
- compensation stipend from the community pool or other means decided by
governance
This system necessitates the following new parameters:
- blockly stipend allowance per dCERT member
- maximum number of dCERT members
- required staked slashable tokens for each dCERT member
- quorum for suspending a particular member
- proposal wager for disbanding the dCERT group
- stabilization period for dCERT member transition
- circuit break dCERT privileges enabled
This system necessitates the following new parameters:
These parameters are expected to be implemented through the param keeper such
that governance may change them at any given point.
- blockly stipend allowance per dCERT member
- maximum number of dCERT members
- required staked slashable tokens for each dCERT member
- quorum for suspending a particular member
- proposal wager for disbanding the dCERT group
- stabilization period for dCERT member transition
- circuit break dCERT privileges enabled
These parameters are expected to be implemented through the param keeper such
that governance may change them at any given point.
### Continuous Voting Electionator
An `Electionator` object is to be implemented as continuous voting and with the
following specifications:
- All delegation addresses may submit votes at any point which updates their
preferred representation on the dCERT group.
- Preferred representation may be arbitrarily split between addresses (ex. 50%
to John, 25% to Sally, 25% to Carol)
- In order for a new member to be added to the dCERT group they must
- All delegation addresses may submit votes at any point which updates their
preferred representation on the dCERT group.
- Preferred representation may be arbitrarily split between addresses (ex. 50%
to John, 25% to Sally, 25% to Carol)
- In order for a new member to be added to the dCERT group they must
send a transaction accepting their admission at which point the validity of
their admission is to be confirmed.
- A sequence number is assigned when a member is added to dCERT group.
their admission is to be confirmed.
- A sequence number is assigned when a member is added to dCERT group.
If a member leaves the dCERT group and then enters back, a new sequence number
is assigned.
- Addresses which control the greatest amount of preferred-representation are
eligible to join the dCERT group (up the _maximum number of dCERT members_).
- Addresses which control the greatest amount of preferred-representation are
eligible to join the dCERT group (up the _maximum number of dCERT members_).
If the dCERT group is already full and new member is admitted, the existing
dCERT member with the lowest amount of votes is kicked from the dCERT group.
- In the split situation where the dCERT group is full but a vying candidate
has the same amount of vote as an existing dCERT member, the existing
member should maintain its position.
- In the split situation where somebody must be kicked out but the two
- In the split situation where the dCERT group is full but a vying candidate
has the same amount of vote as an existing dCERT member, the existing
member should maintain its position.
- In the split situation where somebody must be kicked out but the two
addresses with the smallest number of votes have the same number of votes,
the address with the smallest sequence number maintains its position.
- A stabilization period can be optionally included to reduce the
- A stabilization period can be optionally included to reduce the
"flip-flopping" of the dCERT membership tail members. If a stabilization
period is provided which is greater than 0, when members are kicked due to
insufficient support, a queue entry is created which documents which member is
@@ -106,7 +109,7 @@ Membership suspension by the dCERT group takes place through a voting procedure
by the dCERT group members. After this suspension has taken place, a governance
proposal to slash the dCERT member must be submitted, if the proposal is not
approved by the time the rescinding member has completed unbonding their
tokens, then the tokens are no longer staked and unable to be slashed.
tokens, then the tokens are no longer staked and unable to be slashed.
Additionally in the case of an emergency situation of a colluding and malicious
dCERT group, the community needs the capability to disband the entire dCERT
@@ -119,24 +122,25 @@ wager should be required is because as soon as the proposal is made, the
capability of the dCERT group to halt message routes is put on temporarily
suspended, meaning that a malicious actor who created such a proposal could
then potentially exploit a bug during this period of time, with no dCERT group
capable of shutting down the exploitable message routes.
capable of shutting down the exploitable message routes.
### dCERT membership transactions
Active dCERT members
- change of the description of the dCERT group
- circuit break a message route
- vote to suspend a dCERT member.
Active dCERT members
- change of the description of the dCERT group
- circuit break a message route
- vote to suspend a dCERT member.
Here circuit-breaking refers to the capability to disable a groups of messages,
This could for instance mean: "disable all staking-delegation messages", or
"disable all distribution messages". This could be accomplished by verifying
that the message route has not been "circuit-broken" at CheckTx time (in
`baseapp/baseapp.go`).
`baseapp/baseapp.go`).
"unbreaking" a circuit is anticipated only to occur during a hard fork upgrade
meaning that no capability to unbreak a message route on a live chain is
required.
required.
Note also, that if there was a problem with governance voting (for instance a
capability to vote many times) then governance would be broken and should be
@@ -153,15 +157,15 @@ they should all be severely slashed.
### Positive
- Potential to reduces the number of parties to coordinate with during an emergency
- Reduction in possibility of disclosing sensitive information to malicious parties
- Potential to reduces the number of parties to coordinate with during an emergency
- Reduction in possibility of disclosing sensitive information to malicious parties
### Negative
- Centralization risks
- Centralization risks
### Neutral
## References
(Specialization Groups ADR)[./adr-007-specialization-groups.md]
[Specialization Groups ADR](./adr-007-specialization-groups.md)
@@ -17,10 +17,12 @@ For example, let's say a user wants to implement some custom signature verificat
One approach is to use the [ModuleManager](https://godoc.org/github.com/cosmos/cosmos-sdk/types/module) and have each module implement its own antehandler if it requires custom antehandler logic. The ModuleManager can then be passed in an AnteHandler order in the same way it has an order for BeginBlockers and EndBlockers. The ModuleManager returns a single AnteHandler function that will take in a tx and run each module's `AnteHandle` in the specified order. The module manager's AnteHandler is set as the baseapp's AnteHandler.
Pros:
1. Simple to implement
2. Utilizes the existing ModuleManager architecture
Cons:
1. Improves granularity but still cannot get more granular than a per-module basis. e.g. If auth's `AnteHandle` function is in charge of validating memo and signatures, users cannot swap the signature-checking functionality while keeping the rest of auth's `AnteHandle` functionality.
2. Module AnteHandler are run one after the other. There is no way for one AnteHandler to wrap or "decorate" another.
@@ -53,10 +55,12 @@ func (example Decorator) Deliver(ctx Context, store KVStore, tx Tx, next Deliver
```
Pros:
1. Weave Decorators can wrap over the next decorator/handler in the chain. The ability to both pre-process and post-process may be useful in certain settings.
2. Provides a nested modular structure that isn't possible in the solution above, while also allowing for a linear one-after-the-other structure like the solution above.
Cons:
1. It is hard to understand at first glance the state updates that would occur after a Decorator runs given the `ctx`, `store`, and `tx`. A Decorator can have an arbitrary number of nested Decorators being called within its function body, each possibly doing some pre- and post-processing before calling the next decorator on the chain. Thus to understand what a Decorator is doing, one must also understand what every other decorator further along the chain is also doing. This can get quite complicated to understand. A linear, one-after-the-other approach while less powerful, may be much easier to reason about.
### Chained Micro-Functions
@@ -65,17 +69,17 @@ The benefit of Weave's approach is that the Decorators can be very concise, whic
Another approach is to split the AnteHandler functionality into tightly scoped "micro-functions", while preserving the one-after-the-other ordering that would come from the ModuleManager approach.
We can then have a way to chain these micro-functions so that they run one after the other. Modules may define multiple ante micro-functions and then also provide a default per-module AnteHandler that implements a default, suggested order for these micro-functions.
We can then have a way to chain these micro-functions so that they run one after the other. Modules may define multiple ante micro-functions and then also provide a default per-module AnteHandler that implements a default, suggested order for these micro-functions.
Users can order the AnteHandlers easily by simply using the ModuleManager. The ModuleManager will take in a list of AnteHandlers and return a single AnteHandler that runs each AnteHandler in the order of the list provided. If the user is comfortable with the default ordering of each module, this is as simple as providing a list with each module's antehandler (exactly the same as BeginBlocker and EndBlocker).
If however, users wish to change the order or add, modify, or delete ante micro-functions in anyway; they can always define their own ante micro-functions and add them explicitly to the list that gets passed into module manager.
#### Default Workflow:
#### Default Workflow
This is an example of a user's AnteHandler if they choose not to make any custom micro-functions.
##### SDK code:
##### SDK code
```go
// Chains together a list of AnteHandler micro-functions that get run one after the other.
@@ -137,7 +141,7 @@ func (mm ModuleManager) GetAnteHandler() AnteHandler {
}
```
##### User Code:
##### User Code
```go
// Note: Since user is not making any custom modifications, we can just SetAnteHandlerOrder with the default AnteHandlers provided by each module in our preferred order
@@ -166,11 +170,13 @@ moduleManager.SetAnteHandlerOrder([]AnteHandler(ValidateMemo, CustomSigVerify, D
```
Pros:
1. Allows for ante functionality to be as modular as possible.
2. For users that do not need custom ante-functionality, there is little difference between how antehandlers work and how BeginBlock and EndBlock work in ModuleManager.
3. Still easy to understand
Cons:
1. Cannot wrap antehandlers with decorators like you can with Weave.
### Simple Decorators
@@ -33,6 +33,7 @@ However in practice, we likely don't want a linear relation between amount of st
#### Parameterization
This requires parameterizing a logistic function. It is very well understood how to parameterize this. It has four parameters:
1) A minimum slashing factor
2) A maximum slashing factor
3) The inflection point of the S-curve (essentially where do you want to center the S)
@@ -66,7 +67,6 @@ We then will iterate over all the SlashEvents in the queue, adding their `Valida
Once we have the `NewSlashPercent`, we then iterate over all the `SlashEvent`s in the queue once again, and if `NewSlashPercent > SlashedSoFar` for that SlashEvent, we call the `staking.Slash(slashEvent.Address, slashEvent.Power, Math.Min(Math.Max(minSlashPercent, NewSlashPercent - SlashedSoFar), maxSlashPercent)` (we pass in the power of the validator before any slashes occured, so that we slash the right amount of tokens). We then set `SlashEvent.SlashedSoFar` amount to `NewSlashPercent`.
## Status
Proposed
@@ -7,7 +7,7 @@
## Context
Validator consensus key rotation feature has been discussed and requested for a long time, for the sake of safer validator key management policy (e.g. https://github.com/tendermint/tendermint/issues/1136). So, we suggest one of the simplest form of validator consensus key rotation implementation mostly onto Cosmos-SDK.
Validator consensus key rotation feature has been discussed and requested for a long time, for the sake of safer validator key management policy (e.g. https://github.com/tendermint/tendermint/issues/1136). So, we suggest one of the simplest form of validator consensus key rotation implementation mostly onto Cosmos-SDK.
We don't need to make any update on consensus logic in Tendermint because Tendermint does not have any mapping information of consensus key and validator operator key, meaning that from Tendermint point of view, a consensus key rotation of a validator is simply a replacement of a consensus key to another.
@@ -23,7 +23,6 @@ Also, it should be noted that this ADR includes only the simplest form of consen
- start validating with new consensus key.
- validators using HSM and KMS should update the consensus key in HSM to use the new rotated key after the height `h` when `MsgRotateConsPubKey` committed to the blockchain.
### Considerations
- consensus key mapping information management strategy
@@ -33,13 +32,13 @@ Also, it should be noted that this ADR includes only the simplest form of consen
- key rotation costs related to LCD and IBC
- LCD and IBC will have traffic/computation burden when there exists frequent power changes
- In current Tendermint design, consensus key rotations are seen as power changes from LCD or IBC perspective
- Therefore, to minimize unnecessary frequent key rotation behavior, we limited maximum number of rotation in recent unbonding period and also applied exponentially increasing rotation fee
- Therefore, to minimize unnecessary frequent key rotation behavior, we limited maximum number of rotation in recent unbonding period and also applied exponentially increasing rotation fee
- limits
- a validator cannot rotate its consensus key more than `MaxConsPubKeyRotations` time for any unbonding period, to prevent spam.
- parameters can be decided by governance and stored in genesis file.
- key rotation fee
- a validator should pay `KeyRotationFee` to rotate the consensus key which is calculated as below
- `KeyRotationFee` = (max(`VotingPowerPercentage` * 100, 1) * `InitialKeyRotationFee`) * 2^(number of rotations in `ConsPubKeyRotationHistory` in recent unbonding period)
- `KeyRotationFee` = (max(`VotingPowerPercentage` *100, 1)* `InitialKeyRotationFee`) * 2^(number of rotations in `ConsPubKeyRotationHistory` in recent unbonding period)
- evidence module
- evidence module can search corresponding consensus key for any height from slashing keeper so that it can decide which consensus key is supposed to be used for given height.
- abci.ValidatorUpdate
@@ -50,7 +49,6 @@ Also, it should be noted that this ADR includes only the simplest form of consen
- `MaxConsPubKeyRotations` : maximum number of rotation can be executed by a validator in recent unbonding period. default value 10 is suggested(11th key rotation will be rejected)
- `InitialKeyRotationFee` : the initial key rotation fee when no key rotation has happened in recent unbonding period. default value 1atom is suggested(1atom fee for the first key rotation in recent unbonding period)
### Workflow
1. The validator generates a new consensus keypair.
@@ -64,7 +62,7 @@ Also, it should be noted that this ADR includes only the simplest form of consen
```
3. `handleMsgRotateConsPubKey` gets `MsgRotateConsPubKey`, calls `RotateConsPubKey` with emits event
4. `RotateConsPubKey`
4. `RotateConsPubKey`
- checks if `NewPubKey` is not duplicated on `ValidatorsByConsAddr`
- checks if the validator is does not exceed parameter `MaxConsPubKeyRotations` by iterating `ConsPubKeyRotationHistory`
- checks if the signing account has enough balance to pay `KeyRotationFee`
@@ -83,7 +81,7 @@ Also, it should be noted that this ADR includes only the simplest form of consen
}
```
5. `ApplyAndReturnValidatorSetUpdates` checks if there is `ConsPubKeyRotationHistory` with `ConsPubKeyRotationHistory.RotatedHeight == ctx.BlockHeight()` and if so, generates 2 `ValidatorUpdate` , one for a remove validator and one for create new validator
5. `ApplyAndReturnValidatorSetUpdates` checks if there is `ConsPubKeyRotationHistory` with `ConsPubKeyRotationHistory.RotatedHeight == ctx.BlockHeight()` and if so, generates 2 `ValidatorUpdate` , one for a remove validator and one for create new validator
```go
abci.ValidatorUpdate{
@@ -99,6 +97,7 @@ Also, it should be noted that this ADR includes only the simplest form of consen
6. at `previousVotes` Iteration logic of `AllocateTokens`, `previousVote` using `OldConsPubKey` match up with `ConsPubKeyRotationHistory`, and replace validator for token allocation
7. Migrate `ValidatorSigningInfo` and `ValidatorMissedBlockBitArray` from `OldConsPubKey` to `NewConsPubKey`
- Note : All above features shall be implemented in `staking` module.
## Status
@@ -16,7 +16,7 @@ However, we would like to avoid the creation of an entire second voting process
Thus, we propose the following mechanism:
### Params:
### Params
- The current gov param `VotingPeriod` is to be replaced by a `MinVotingPeriod` param. This is the the default voting period that all governance proposal voting periods start with.
- There is a new gov param called `MaxVotingPeriodExtension`.
@@ -182,6 +182,7 @@ In addition to serving as a whitelist, `InterfaceRegistry` can also serve
to communicate the list of concrete types that satisfy an interface to clients.
In .proto files:
* fields which accept interfaces should be annotated with `cosmos_proto.accepts_interface`
using the same full-qualified name passed as `protoName` to `InterfaceRegistry.RegisterInterface`
* interface implementations should be annotated with `cosmos_proto.implements_interface`
@@ -210,7 +211,7 @@ Note that `InterfaceRegistry` usage does not deviate from standard protobuf
usage of `Any`, it just introduces a security and introspection layer for
golang usage.
`InterfaceRegistry` will be a member of `ProtoCodec`
`InterfaceRegistry` will be a member of `ProtoCodec`
described above. In order for modules to register interface types, app modules
can optionally implement the following interface:
@@ -52,7 +52,6 @@ high to justify its usage. However for queries this is not a concern, and
providing generic module-level queries that use `Any` does not preclude apps
from also providing app-level queries that return use the app-level `oneof`s.
A hypothetical example for the `gov` module would look something like:
```proto
@@ -126,7 +125,6 @@ The signature for this method matches the existing
`RegisterServer` method on the GRPC `Server` type where `handler` is the custom
query server implementation described above.
GRPC-like requests are routed by the service name (ex. `cosmos_sdk.x.bank.v1.Query`)
and method name (ex. `QueryBalance`) combined with `/`s to form a full
method name (ex. `/cosmos_sdk.x.bank.v1.Query/QueryBalance`). This gets translated
@@ -140,7 +138,7 @@ there is a quite natural mapping of GRPC-like rpc methods to the existing
This basic specification allows us to reuse protocol buffer `service` definitions
for ABCI custom queries substantially reducing the need for manual decoding and
encoding in query methods.
encoding in query methods.
### GRPC Protocol Support
@@ -178,7 +176,7 @@ service Query {
}
```
grpc-gateway will work direcly against the GRPC proxy described above which will
grpc-gateway will work direcly against the GRPC proxy described above which will
translate requests to ABCI queries under the hood. grpc-gateway can also
generate Swagger definitions automatically.
@@ -211,7 +209,7 @@ we have tweaked the grpc codegen to use an interface rather than concrete type
for the generated client struct. This allows us to also reuse the GRPC infrastructure
for ABCI client queries.
1Context` will receive a new method `QueryConn` that returns a `ClientConn`
1Context`will receive a new method`QueryConn`that returns a`ClientConn`
that routes calls to ABCI queries
Clients (such as CLI methods) will then be able to call query methods like this:
@@ -13,6 +13,7 @@ the need to rewrite whole BaseApp. Also there's one special case for `sdk.ErrorO
might be handled in a "standard" way (middleware) alongside the others.
We propose middleware-solution, which could help developers implement the following cases:
* add external logging (let's say sending reports to external services like [Sentry](https://sentry.io));
* call panic for specific error cases;
@@ -56,7 +57,7 @@ An example:
func exampleErrHandler(recoveryObj interface{}) error {
err, ok := recoveryObj.(error)
if !ok { return nil }
if someSpecificError.Is(err) {
panic(customPanicMsg)
} else {
@@ -87,13 +88,14 @@ func newRecoveryMiddleware(handler RecoveryHandler, next recoveryMiddleware) rec
```
Function receives a `recoveryObj` object and returns:
* (next `recoveryMiddleware`, `nil`) if object wasn't handled (not a target type) by `RecoveryHandler`;
* (`nil`, not nil `error`) if input object was handled and other middlewares in the chain should not be executed;
* (`nil`, `nil`) in case of invalid behavior. Panic recovery might not have been properly handled;
this can be avoided by always using a `default` as a rightmost middleware in the chain (always returns an `error`');
this can be avoided by always using a `default` as a rightmost middleware in the chain (always returns an `error`');
`OutOfGas` middleware example:
```go
func newOutOfGasRecoveryMiddleware(gasWanted uint64, ctx sdk.Context, next recoveryMiddleware) recoveryMiddleware {
handler := func(recoveryObj interface{}) error {
@@ -106,12 +108,13 @@ func newOutOfGasRecoveryMiddleware(gasWanted uint64, ctx sdk.Context, next recov
),
)
}
return newRecoveryMiddleware(handler, next)
}
```
`Default` middleware example:
```go
func newDefaultRecoveryMiddleware() recoveryMiddleware {
handler := func(recoveryObj interface{}) error {
@@ -119,7 +122,7 @@ func newDefaultRecoveryMiddleware() recoveryMiddleware {
sdkerrors.ErrPanic, fmt.Sprintf("recovered: %v\nstack:\n%v", recoveryObj, string(debug.Stack())),
)
}
return newRecoveryMiddleware(handler, nil)
}
```
+6 -1
View File
@@ -57,6 +57,7 @@ third-party modules.
As a starting point, we should adopt all of the [DEFAULT](https://buf.build/docs/lint-checkers#default)
checkers in [Buf's](https://buf.build) including [`PACKAGE_DIRECTORY_MATCH`](https://buf.build/docs/lint-checkers#file_layout),
except:
* [PACKAGE_VERSION_SUFFIX](https://buf.build/docs/lint-checkers#package_version_suffix)
* [SERVICE_SUFFIX](https://buf.build/docs/lint-checkers#service_suffix)
@@ -118,6 +119,7 @@ to prevent such breakage.
With that in mind, different stable versions (i.e. `v1` or `v2`) of a package should more or less be considered
different packages and this should be last resort approach for upgrading protobuf schemas. Scenarios where creating
a `v2` may make sense are:
* we want to create a new module with similar functionality to an existing module and adding `v2` is the most natural
way to do this. In that case, there are really just two different, but similar modules with different APIs.
* we want to add a new revamped API for an existing module and it's just too cumbersome to add it to the existing package,
@@ -127,11 +129,12 @@ so putting it in `v2` is cleaner for users. In this case, care should be made to
#### Guidelines on unstable (alpha and beta) package versions
The following guidelines are recommended for marking packages as alpha or beta:
* marking something as `alpha` or `beta` should be a last resort and just putting something in the
stable package (i.e. `v1` or `v2`) should be preferred
* a package *should* be marked as `alpha` *if and only if* there are active discussions to remove
or significantly alter the package in the near future
* a package *should* be marked as `beta` *if and only if* there is an active discussion to
* a package *should* be marked as `beta` *if and only if* there is an active discussion to
significantly refactor/rework the functionality in the near future but not remove it
* modules *can and should* have types in both stable (i.e. `v1` or `v2`) and unstable (`alpha` or `beta`) packages.
@@ -140,6 +143,7 @@ Whenever code is released into the wild, especially on a blockchain, there is a
cases, for instance with immutable smart contracts, a breaking change may be impossible to fix.
When marking something as `alpha` or `beta`, maintainers should ask the questions:
* what is the cost of asking others to change their code vs the benefit of us maintaining the optionality to change it?
* what is the plan for moving this to `v1` and how will that affect users?
@@ -151,6 +155,7 @@ and so if they actually went and changed the package to `grpc.reflection.v1`, so
they probably don't want to do that... So now the `v1alpha` package is more or less the de-facto `v1`. Let's not do that.
The following are guidelines for working with non-stable packages:
* [Buf's recommended version suffix](https://buf.build/docs/lint-checkers#package_version_suffix)
(ex. `v1alpha1`) _should_ be used for non-stable packages
* non-stable packages should generally be excluded from breaking change detection
@@ -38,7 +38,7 @@ step when sending and signing transactions.
### Decision
The following encoding scheme is to be used by other ADRs,
The following encoding scheme is to be used by other ADRs,
and in particular for `SignDoc` serialization.
## Specification
@@ -71,7 +71,6 @@ malleability.
Among other sources of non-determinism, this ADR eliminates the possibility of
encoding malleability.
### Serialization rules
The serialization is based on the
@@ -275,7 +274,6 @@ for all protobuf documents we need in the context of Cosmos SDK signing.
### Neutral
### Usage in SDK
For the reasons mentioned above ("Negative" section) we prefer to keep workarounds
@@ -18,14 +18,12 @@ This ADR defines an address format for all addressable SDK accounts. That includ
Issue [\#3685](https://github.com/cosmos/cosmos-sdk/issues/3685) identified that public key
address spaces are currently overlapping. We confirmed that it significantly decreases security of Cosmos SDK.
### Problem
An attacker can control an input for an address generation function. This leads to a birthday attack, which significantly decreases the security space.
To overcome this, we need to separate the inputs for different kind of account types:
a security break of one account type shouldn't impact the security of other account types.
### Initial proposals
One initial proposal was extending the address length and
@@ -47,7 +45,6 @@ And explained how this approach should be sufficiently collision resistant:
This led to the first proposal (which we proved to be not good enough):
we concatenate a key type with a public key, hash it and take the first 20 bytes of that hash, summarized as `sha256(keyTypePrefix || keybytes)[:20]`.
### Review and Discussions
In [\#5694](https://github.com/cosmos/cosmos-sdk/issues/5694) we discussed various solutions.
@@ -55,6 +52,7 @@ We agreed that 20 bytes it's not future proof, and extending the address length
This disqualifies the initial proposal.
In the issue we discussed various modifications:
+ Choice of the hash function.
+ Move the prefix out of the hash function: `keyTypePrefix + sha256(keybytes)[:20]` [post-hash-prefix-proposal].
+ Use double hashing: `sha256(keyTypePrefix + sha256(keybytes)[:20])`.
@@ -65,13 +63,11 @@ In the issue we discussed various modifications:
+ Support currently used tools - we don't want to break an ecosystem, or add a long adaptation period. Ref: https://github.com/cosmos/cosmos-sdk/issues/8041
+ Try to keep the address length small - addresses are widely used in state, both as part of a key and object value.
### Scope
This ADR only defines a process for the generation of address bytes. For end-user interactions with addresses (through the API, or CLI, etc.), we still use bech32 to format these addresses as strings. This ADR doesn't change that.
Using Bech32 for string encoding gives us support for checksum error codes and handling of user typos.
## Decision
We define the following account types, for which we define the address function:
@@ -81,7 +77,6 @@ We define the following account types, for which we define the address function:
3. composed accounts with a native address key (ie: bls, group module accounts)
4. module accounts: basically any accounts which cannot sign transactions and which are managed internally by modules
### Legacy Public Key Addresses Don't Change
Currently (Jan 2021), the only officially supported SDK user accounts are `secp256k1` basic accounts and legacy amino multisig.
@@ -120,11 +115,12 @@ and it's more secure than [post-hash-prefix-proposal] (which uses the first 20 b
Moreover the cryptographer motivated the choice of adding `typ` in the hash to protect against a switch table attack.
We use the `address.Hash` function for generating addresses for all accounts represented by a single key:
* simple public keys: `address.Hash(keyType, pubkey)`
+ aggregated keys (eg: BLS): `address.Hash(keyType, aggregatedPubKey)`
+ modules: `address.Hash("module", moduleName)`
### Composed Addresses
For simple composed accounts (like new naive multisig), we generalize the `address.Hash`. The address is constructed by recursively creating addresses for the sub accounts, sorting the addresses and composing them into a single address. It ensures that the ordering of keys doesn't impact the resulting address.
@@ -199,11 +195,13 @@ func Module(moduleName string, key []byte) []byte{
```
**Example** A lending BTC pool address would be:
```
btcPool := address.Module("lending", btc.Addrress()})
```
If we want to create an address for a module account depending on more than one key, we can concatenate them:
```
btcAtomAMM := address.Module("amm", btc.Addrress() + atom.Address()})
```
@@ -221,11 +219,11 @@ func Derive(address []byte, derivationKey []byte) []byte {
Note: `Module` is a special case of the more general _derived_ address, where we set the `"module"` string for the _from address_.
**Example** For a cosmwasm smart-contract address we could use the following construction:
```
smartContractAddr := Derived(Module("cosmwasm", smartContractsNamespace), []{smartContractKey})
```
### Schema Types
A `typ` parameter used in `Hash` function SHOULD be unique for each account type.
@@ -246,8 +244,6 @@ These names are derived directly from .proto files in a standardized way and use
in other places such as the type URL in `Any`s. We can easily obtain the name using
`proto.MessageName(msg)`.
## Consequences
### Backwards Compatibility
@@ -272,19 +268,18 @@ This ADR is compatible with what was committed and directly supported in the SDK
- protobuf message names are used as key type prefixes
## Further Discussions
Some accounts can have a fixed name or may be constructed in other way (eg: modules). We were discussing an idea of an account with a predefined name (eg: `me.regen`), which could be used by institutions.
Without going into details, these kinds of addresses are compatible with the hash based addresses described here as long as they don't have the same length.
More specifically, any special account address must not have a length equal to 20 or 32 bytes.
## Appendix: Consulting session
End of Dec 2020 we had a session with [Alan Szepieniec](https://scholar.google.be/citations?user=4LyZn8oAAAAJ&hl=en) to consult the approach presented above.
Alan general observations:
+ we dont need 2-preimage resistance
+ we need 32bytes address space for collision resistance
+ when an attacker can control an input for object with an address then we have a problem with birthday attack
@@ -292,11 +287,12 @@ Alan general observations:
+ sha2 mining can be use to breaking address pre-image
Hashing algorithm
+ any attack breaking blake3 will break blake2
+ Alan is pretty confident about the current security analysis of the blake hash algorithm. It was a finalist, and the author is well known in security analysis.
Algorithm:
+ Alan recommends to hash the prefix: `address(pub_key) = hash(hash(key_type) + pub_key)[:32]`, main benefits:
+ we are free to user arbitrary long prefix names
+ we still dont risk collisions
@@ -305,24 +301,29 @@ Algorithm:
+ Aaron asked about post hash prefixes (`address(pub_key) = key_type + hash(pub_key)`) and differences. Alan noted that this approach has longer address space and its stronger.
Algorithm for complex / composed keys:
+ merging tree like addresses with same algorithm are fine
Module addresses: Should module addresses have different size to differentiate it?
+ we will need to set a pre-image prefix for module addresse to keept them in 32-byte space: `hash(hash('module') + module_key)`
+ Aaron observation: we already need to deal with variable length (to not break secp256k1 keys).
Discssion about arithmetic hash function for ZKP
+ Posseidon / Rescue
+ Problem: much bigger risk because we dont know much techniques and history of crypto-analysis of arithmetic constructions. Its still a new ground and area of active research.
Post quantum signature size
+ Alan suggestion: Falcon: speed / size ration - very good.
+ Aaron - should we think about it?
Alan: based on early extrapolation this thing will get able to break EC cryptography in 2050 . But thats a lot of uncertainty. But there is magic happening with recurions / linking / simulation and that can speedup the progress.
Other ideas
+ Lets say we use same key and two different address algorithms for 2 different use cases. Is it still safe to use it? Alan: if we want to hide the public key (which is not our use case), then its less secure but there are fixes.
### References
+ [Notes](https://hackmd.io/_NGWI4xZSbKzj1BkCqyZMw)
@@ -115,6 +115,7 @@ message MsgGrantAllowance {
```
In order to use allowances in transactions, we add a new field `granter` to the transaction `Fee` type:
```proto
package cosmos.tx.v1beta1;
@@ -19,6 +19,7 @@ on behalf of that account to other accounts.
## Context
The concrete use cases which motivated this module include:
- the desire to delegate the ability to vote on proposals to other accounts besides the account which one has
delegated stake
- "sub-keys" functionality, as originally proposed in [\#4480](https://github.com/cosmos/cosmos-sdk/issues/4480) which
+2 -1
View File
@@ -182,6 +182,7 @@ This also allows us to change how we perform functional tests. Instead of mockin
Finally, closing a module to client API opens desirable OCAP patterns discussed in ADR-033. Since server implementation and interface is hidden, nobody can hold "keepers"/servers and will be forced to relay on the client interface, which will drive developers for correct encapsulation and software engineering patterns.
### Pros
- communicates return type clearly
- manual handler registration and return type marshaling is no longer needed, just implement the interface and register it
- communication interface is automatically generated, the developer can now focus only on the state transition methods - this would improve the UX of [\#7093](https://github.com/cosmos/cosmos-sdk/issues/7093) approach (1) if we chose to adopt that
@@ -189,8 +190,8 @@ Finally, closing a module to client API opens desirable OCAP patterns discussed
- dramatically reduces and simplifies the code
### Cons
- using `service` definitions outside the context of gRPC could be confusing (but doesnt violate the proto3 spec)
- using `service` definitions outside the context of gRPC could be confusing (but doesnt violate the proto3 spec)
## References
+14 -14
View File
@@ -8,7 +8,7 @@
- Anil Kumar (@anilcse)
- Jack Zampolin (@jackzampolin)
- Adam Bozanich (@boz)
- Adam Bozanich (@boz)
## Status
@@ -16,13 +16,13 @@ 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.
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.
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.
[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.
@@ -39,7 +39,7 @@ __Step-1__: Implement additional functionality in the `types` package: `EmitTyp
```go
// types/events.go
// EmitTypedEvent takes typed event and emits converting it into sdk.Event
// 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)
@@ -82,7 +82,7 @@ func ParseTypedEvent(event abci.Event) (proto.Message, error) {
} 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)
@@ -109,7 +109,7 @@ func ParseTypedEvent(event abci.Event) (proto.Message, error) {
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.
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:
@@ -165,11 +165,10 @@ Please see the below code sample for more detail on this flow looks for clients.
### 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.
`EventHandler`s which define the actions they desire to take.
```go
// EventEmitter is a type that describes event emitter functions
@@ -193,7 +192,7 @@ func main() {
}
// SubmitProposalEventHandler is an example of an event handler that prints proposal details
// when any EventSubmitProposal is emitted.
// when any EventSubmitProposal is emitted.
func SubmitProposalEventHandler(ev proto.Message) (err error) {
switch event := ev.(type) {
// Handle governance proposal events creation events
@@ -206,9 +205,9 @@ func SubmitProposalEventHandler(ev proto.Message) (err error) {
}
}
// 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)
// 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()
@@ -290,7 +289,7 @@ func PublishChainTxEvents(ctx context.Context, client tmclient.EventsClient, bus
if !evt.Result.IsOK() {
continue
}
// range over events, parse them using the basic manager and
// 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)
@@ -315,5 +314,6 @@ func PublishChainTxEvents(ctx context.Context, client tmclient.EventsClient, bus
```
## 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)
@@ -13,6 +13,7 @@ Proposed
This ADR introduces a system for permissioned inter-module communication leveraging the protobuf `Query` and `Msg`
service definitions defined in [ADR 021](./adr-021-protobuf-query-encoding.md) and
[ADR 031](./adr-031-msg-service.md) which provides:
- stable protobuf based module interfaces to potentially later replace the keeper paradigm
- stronger inter-module object capabilities (OCAPs) guarantees
- module accounts and sub-account authorization
@@ -24,6 +25,7 @@ In the current Cosmos SDK documentation on the [Object-Capability Model](../core
> We assume that a thriving ecosystem of Cosmos-SDK modules that are easy to compose into a blockchain application will contain faulty or malicious modules.
There is currently not a thriving ecosystem of Cosmos SDK modules. We hypothesize that this is in part due to:
1. lack of a stable v1.0 Cosmos SDK to build modules off of. Module interfaces are changing, sometimes dramatically, from
point release to point release, often for good reasons, but this does not create a stable foundation to build on.
2. lack of a properly implemented object capability or even object-oriented encapsulation system which makes refactors
@@ -84,6 +86,7 @@ this ADR does not necessitate the creation of new protobuf definitions or servic
based service interfaces already used by clients for inter-module communication.
Using this `QueryClient`/`MsgClient` approach has the following key benefits over exposing keepers to external modules:
1. Protobuf types are checked for breaking changes using [buf](https://buf.build/docs/breaking-overview) and because of
the way protobuf is designed this will give us strong backwards compatibility guarantees while allowing for forward
evolution.
@@ -95,6 +98,7 @@ enabling atomicy of operations ([currently a problem](https://github.com/cosmos/
transaction
This mechanism has the added benefits of:
- reducing boilerplate through code generation, and
- allowing for modules in other languages either via a VM like CosmWasm or sub-processes using gRPC
@@ -116,6 +120,7 @@ For example, module `A` could use its `A.ModuleKey` to create `MsgSend` object f
will assure that the `from` account (`A.ModuleKey` in this case) is the signer.
Here's an example of a hypothetical module `foo` interacting with `x/bank`:
```go
package foo
@@ -247,7 +252,6 @@ In [ADR 031](./adr-031-msg-service.md), the `AppModule.RegisterService(Configura
inter-module communication, we extend the `Configurator` interface to pass in the `ModuleKey` and to allow modules to
specify their dependencies on other modules using `RequireServer()`:
```go
type Configurator interface {
MsgServer() grpc.Server
@@ -334,9 +338,10 @@ other modules. This will be addressed in separate ADRs or updates to this ADR.
### Future Work
Other future improvements may include:
* custom code generation that:
* simplifies interfaces (ex. generates code with `sdk.Context` instead of `context.Context`)
* optimizes inter-module calls - for instance caching resolved methods after first invocation
* simplifies interfaces (ex. generates code with `sdk.Context` instead of `context.Context`)
* optimizes inter-module calls - for instance caching resolved methods after first invocation
* combining `StoreKey`s and `ModuleKey`s into a single interface so that modules have a single OCAPs handle
* code generation which makes inter-module communication more performant
* decoupling `ModuleKey` creation from `AppModuleBasic.Name()` so that app's can override root module account names
@@ -353,8 +358,8 @@ The advantages of the approach described in this ADR are mostly around how it in
specifically:
* protobuf so that:
* code generation of interfaces can be leveraged for a better dev UX
* module interfaces are versioned and checked for breakage using [buf](https://docs.buf.build/breaking-overview)
* code generation of interfaces can be leveraged for a better dev UX
* module interfaces are versioned and checked for breakage using [buf](https://docs.buf.build/breaking-overview)
* sub-module accounts as per ADR 028
* the general `Msg` passing paradigm and the way signers are specified by `GetSigners`
@@ -16,7 +16,7 @@ Account rekeying is a process hat allows an account to replace its authenticatio
Currently, in the Cosmos SDK, the address of an auth `BaseAccount` is based on the hash of the public key. Once an account is created, the public key for the account is set in stone, and cannot be changed. This can be a problem for users, as key rotation is a useful security practice, but is not possible currently. Furthermore, as multisigs are a type of pubkey, once a multisig for an account is set, it can not be updated. This is problematic, as multisigs are often used by organizations or companies, who may need to change their set of multisig signers for internal reasons.
Transferring all the assets of an account to a new account with the updated pubkey is not sufficient, because some "engagements" of an account are not easily transferable. For example, in staking, to transfer bonded Atoms, an account would have to unbond all delegations and wait the three week unbonding period. Even more significantly, for validator operators, ownership over a validator is not transferrable at all, meaning that the operator key for a validator can never be updated, leading to poor operational security for validators.
Transferring all the assets of an account to a new account with the updated pubkey is not sufficient, because some "engagements" of an account are not easily transferable. For example, in staking, to transfer bonded Atoms, an account would have to unbond all delegations and wait the three week unbonding period. Even more significantly, for validator operators, ownership over a validator is not transferrable at all, meaning that the operator key for a validator can never be updated, leading to poor operational security for validators.
## Decision
@@ -43,19 +43,15 @@ The MsgChangePubKey transaction needs to be signed by the existing pubkey in sta
Once, approved, the handler for this message type, which takes in the AccountKeeper, will update the in-state pubkey for the account and replace it with the pubkey from the Msg.
An account that has had its pubkey changed cannot be automatically pruned from state. This is because if pruned, the original pubkey of the account would be needed to recreate the same address, but the owner of the address may not have the original pubkey anymore. Currently, we do not automatically prune any accounts anyways, but we would like to keep this option open the road (this is the purpose of account numbers). To resolve this, we charge an additional gas fee for this operation to compensate for this this externality (this bound gas amount is configured as parameter `PubKeyChangeCost`). The bonus gas is charged inside the handler, using the `ConsumeGas` function. Furthermore, in the future, we can allow accounts that have rekeyed manually prune themselves using a new Msg type such as `MsgDeleteAccount`. Manually pruning accounts can give a gas refund as an incentive for performing the action.
```go
amount := ak.GetParams(ctx).PubKeyChangeCost
ctx.GasMeter().ConsumeGas(amount, "pubkey change fee")
```
Everytime a key for an address is changed, we will store a log of this change in the state of the chain, thus creating a stack of all previous keys for an address and the time intervals for which they were active. This allows dapps and clients to easily query past keys for an account which may be useful for features such as verifying timestamped off-chain signed messages.
## Consequences
### Positive
@@ -70,7 +66,6 @@ Breaks the current assumed relationship between address and pubkeys as H(pubkey)
* This makes wallets that support this feature more complicated. For example, if an address on chain was updated, the corresponding key in the CLI wallet also needs to be updated.
* Cannot automatically prune accounts with 0 balance that have had their pubkey changed.
### Neutral
* While the purpose of this is intended to allow the owner of an account to update to a new pubkey they own, this could technically also be used to transfer ownership of an account to a new owner. For example, this could be use used to sell a staked position without unbonding or an account that has vesting tokens. However, the friction of this is very high as this would essentially have to be done as a very specific OTC trade. Furthermore, additional constraints could be added to prevent accouns with Vesting tokens to use this feature.
@@ -7,33 +7,33 @@
- Alessio Treglia (@alessio)
- Frojdy Dymylja (@fdymylja)
## Changelog
- 2021-05-12: the external library [cosmos-rosetta-gateway](https://github.com/tendermint/cosmos-rosetta-gateway) has been moved within the SDK.
## Context
[Rosetta API](https://www.rosetta-api.org/) is an open-source specification and set of tools developed by Coinbase to
[Rosetta API](https://www.rosetta-api.org/) is an open-source specification and set of tools developed by Coinbase to
standardise blockchain interactions.
Through the use of a standard API for integrating blockchain applications it will
* Be easier for a user to interact with a given blockchain
* Allow exchanges to integrate new blockchains quickly and easily
* Enable application developers to build cross-blockchain applications such as block explorers, wallets and dApps at
* Enable application developers to build cross-blockchain applications such as block explorers, wallets and dApps at
considerably lower cost and effort.
## Decision
It is clear that adding Rosetta API support to the Cosmos SDK will bring value to all the developers and
It is clear that adding Rosetta API support to the Cosmos SDK will bring value to all the developers and
Cosmos SDK based chains in the ecosystem. How it is implemented is key.
The driving principles of the proposed design are:
1. **Extensibility:** it must be as riskless and painless as possible for application developers to set-up network
1. **Extensibility:** it must be as riskless and painless as possible for application developers to set-up network
configurations to expose Rosetta API-compliant services.
2. **Long term support:** This proposal aims to provide support for all the supported Cosmos SDK release series.
3. **Cost-efficiency:** Backporting changes to Rosetta API specifications from `master` to the various stable
3. **Cost-efficiency:** Backporting changes to Rosetta API specifications from `master` to the various stable
branches of Cosmos SDK is a cost that needs to be reduced.
We will achieve these delivering on these principles by the following:
@@ -44,12 +44,11 @@ We will achieve these delivering on these principles by the following:
b. The `Server` functionality as this is independent of the Cosmos SDK version.
c. The `Online/OfflineNetwork`, which is not exported, and implements the rosetta API using the `Client` interface to query the node, build tx and so on.
d. The `errors` package to extend rosetta errors.
2. Due to differences between the Cosmos release series, each series will have its own specific implementation of `Client` interface.
2. Due to differences between the Cosmos release series, each series will have its own specific implementation of `Client` interface.
3. There will be two options for starting an API service in applications:
a. API shares the application process
b. API-specific process.
## Architecture
### The External Repo
@@ -65,6 +64,7 @@ The constructor follows:
`func NewServer(settings Settings) (Server, error)`
`Settings`, which are used to construct a new server, are the following:
```go
// Settings define the rosetta server settings
type Settings struct {
@@ -88,7 +88,6 @@ type Settings struct {
Package types uses a mixture of rosetta types and custom defined type wrappers, that the client must parse and return while executing operations.
##### Interfaces
Every SDK version uses a different format to connect (rpc, gRPC, etc), query and build transactions, we have abstracted this in what is the `Client` interface.
@@ -180,6 +179,7 @@ type Msg interface {
```
Hence developers who want to extend the rosetta set of supported operations just need to extend their module's sdk.Msgs with the `ToOperations` and `FromOperations` methods.
### 3. API service invocation
As stated at the start, application developers will have two methods for invocation of the Rosetta API service:
@@ -191,12 +191,10 @@ As stated at the start, application developers will have two methods for invocat
Rosetta API service could run within the same execution process as the application. This would be enabled via app.toml settings, and if gRPC is not enabled the rosetta instance would be spinned in offline mode (tx building capabilities only).
#### Separate API service
Client application developers can write a new command to launch a Rosetta API server as a separate process too, using the rosetta command contained in the `/server/rosetta` package. Construction of the command depends on cosmos sdk version. Examples can be found inside `simd` for stargate, and `contrib/rosetta/simapp` for other release series.
## Status
Proposed
@@ -5,6 +5,7 @@
- 28/10/2020 - Initial draft
## Authors
- Antoine Herzog (@antoineherzog)
- Zaki Manian (@zmanian)
- Aleksandr Bezobchuk (alexanderbez) [1]
@@ -18,7 +19,8 @@ Draft
Currently, in the SDK, there is no convention to sign arbitrary message like on Ethereum. We propose with this specification, for Cosmos SDK ecosystem, a way to sign and validate off-chain arbitrary messages.
This specification serves the purpose of covering every use case, this means that cosmos-sdk applications developers decide how to serialize and represent `Data` to users.
This specification serves the purpose of covering every use case, this means that cosmos-sdk applications developers decide how to serialize and represent `Data` to users.
## Context
Having the ability to sign messages off-chain has proven to be a fundamental aspect of nearly any blockchain. The notion of signing messages off-chain has many added benefits such as saving on computational costs and reducing transaction throughput and overhead. Within the context of the Cosmos, some of the major applications of signing such data includes, but is not limited to, providing a cryptographic secure and verifiable means of proving validator identity and possibly associating it with some other framework or organization. In addition, having the ability to sign Cosmos messages with a Ledger or similar HSM device.
@@ -36,7 +38,7 @@ Cosmos SDK 0.40 also introduces a concept of “auth_info” this can specify SI
A spec should include an `auth_info` that supports SIGN_MODE_DIRECT and SIGN_MODE_LEGACY_AMINO.
Create the `offchain` proto definitions, we extend the auth module with `offchain` package to offer functionalities to verify and sign offline messages.
An offchain transaction follows these rules:
- the memo must be empty
@@ -51,10 +53,10 @@ The first message added to the `offchain` package is `MsgSignData`.
`MsgSignData` allows developers to sign arbitrary bytes valid offchain only. Where `Signer` is the account address of the signer. `Data` is arbitrary bytes which can represent `text`, `files`, `object`s. It's applications developers decision how `Data` should be deserialized, serialized and the object it can represent in their context.
It's applications developers decision how `Data` should be treated, by treated we mean the serialization and deserialization process and the Object `Data` should represent.
It's applications developers decision how `Data` should be treated, by treated we mean the serialization and deserialization process and the Object `Data` should represent.
Proto definition:
```proto
// MsgSignData defines an arbitrary, general-purpose, off-chain message
message MsgSignData {
@@ -64,7 +66,9 @@ message MsgSignData {
bytes Data = 2 [(gogoproto.jsontag) = "data"];
}
```
Signed MsgSignData json example:
```json
{
"type": "cosmos-sdk/StdTx",
@@ -98,7 +102,7 @@ Signed MsgSignData json example:
## Consequences
There is a specification on how messages, that are not meant to be broadcast to a live chain, should be formed.
There is a specification on how messages, that are not meant to be broadcast to a live chain, should be formed.
### Backwards Compatibility
@@ -125,4 +129,4 @@ Backwards compatibility is maintained as this is a new message spec definition.
1. https://github.com/cosmos/ics/pull/33
2. https://github.com/cosmos/cosmos-sdk/pull/7727#discussion_r515668204
3. https://github.com/cosmos/cosmos-sdk/pull/7727#issuecomment-722478477
4. https://github.com/cosmos/cosmos-sdk/pull/7727#issuecomment-721062923
4. https://github.com/cosmos/cosmos-sdk/pull/7727#issuecomment-721062923
+9 -2
View File
@@ -36,6 +36,7 @@ type Vote struct {
```
And for backwards compatibility, we introduce `MsgVoteWeighted` while keeping `MsgVote`.
```
type MsgVote struct {
ProposalID int64
@@ -51,6 +52,7 @@ type MsgVoteWeighted struct {
```
The `ValidateBasic` of a `MsgVoteWeighted` struct would require that
1. The sum of all the Rates is equal to 1.0
2. No Option is repeated
@@ -69,16 +71,18 @@ tally() {
```
The CLI command for creating a multi-option vote would be as such:
```sh
simd tx gov vote 1 "yes=0.6,no=0.3,abstain=0.05,no_with_veto=0.05" --from mykey
```
To create a single-option vote a user can do either
```
simd tx gov vote 1 "yes=1" --from mykey
```
or
or
```sh
simd tx gov vote 1 yes --from mykey
@@ -86,19 +90,22 @@ simd tx gov vote 1 yes --from mykey
to maintain backwards compatibility.
## Consequences
### Backwards Compatibility
- Previous VoteMsg types will remain the same and so clients will not have to update their procedure unless they want to support the WeightedVoteMsg feature.
- When querying a Vote struct from state, its structure will be different, and so clients wanting to display all voters and their respective votes will have to handle the new format and the fact that a single voter can have split votes.
- The result of querying the tally function should have the same API for clients.
### Positive
- Can make the voting process more accurate for addresses representing multiple stakeholders, often some of the largest addresses.
### Negative
- Is more complex than simple voting, and so may be harder to explain to users. However, this is mostly mitigated because the feature is opt-in.
### Neutral
- Relatively minor change to governance tally function.
+11 -12
View File
@@ -229,6 +229,7 @@ This service uses the same `StoreKVPairWriteListener` for every KVStore, writing
out to the same files, relying on the `StoreKey` field in the `StoreKVPair` protobuf message to later distinguish the source for each pair.
The file naming schema is as such:
* After every `BeginBlock` request a new file is created with the name `block-{N}-begin`, where N is the block number. All
subsequent state changes are written out to this file until the first `DeliverTx` request is received. At the head of these files,
the length-prefixed protobuf encoded `BeginBlock` request is written, and the response is written at the tail.
@@ -398,38 +399,37 @@ func (app *BaseApp) RegisterHooks(s StreamingService) {
We will also modify the `BeginBlock`, `EndBlock`, and `DeliverTx` methods to pass ABCI requests and responses to any streaming service hooks registered
with the `BaseApp`.
```go
func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) {
...
// Call the streaming service hooks with the BeginBlock messages
for _, hook := range app.hooks {
hook.ListenBeginBlock(app.deliverState.ctx, req, res)
}
return res
}
```
```go
func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBlock) {
...
// Call the streaming service hooks with the EndBlock messages
for _, hook := range app.hooks {
hook.ListenEndBlock(app.deliverState.ctx, req, res)
}
return res
}
```
```go
func (app *BaseApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx {
...
gInfo, result, err := app.runTx(runTxModeDeliver, req.Tx)
@@ -450,12 +450,12 @@ func (app *BaseApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx
Data: result.Data,
Events: sdk.MarkEventsToIndex(result.Events, app.indexEvents),
}
// Call the streaming service hooks with the DeliverTx messages
for _, hook := range app.hooks {
hook.ListenDeliverTx(app.deliverState.ctx, req, res)
}
return res
}
```
@@ -544,7 +544,6 @@ func FileStreamingConstructor(opts servertypes.AppOptions, keys []sdk.StoreKey)
As a demonstration, we will implement the state watching features as part of SimApp.
For example, the below is a very rudimentary integration of the state listening features into the SimApp `AppCreator` function:
```go
func NewSimApp(
logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, skipUpgradeHeights map[int64]bool,
@@ -560,7 +559,7 @@ func NewSimApp(
govtypes.StoreKey, paramstypes.StoreKey, ibchost.StoreKey, upgradetypes.StoreKey,
evidencetypes.StoreKey, ibctransfertypes.StoreKey, capabilitytypes.StoreKey,
)
// configure state listening capabilities using AppOptions
listeners := cast.ToStringSlice(appOpts.Get("store.streamers"))
for _, listenerName := range listeners {
@@ -590,7 +589,7 @@ func NewSimApp(
// kick off the background streaming service loop
streamingService.Stream(wg, quitChan) // maybe this should be done from inside BaseApp instead?
}
...
return app
+9 -8
View File
@@ -21,7 +21,7 @@ This ADR updates the proof of stake module to buffer the staking weight updates
The current proof of stake module takes the design decision to apply staking weight changes to the consensus engine immediately. This means that delegations and unbonds get applied immediately to the validator set. This decision was primarily done as it was implementationally simplest, and because we at the time believed that this would lead to better UX for clients.
An alternative design choice is to allow buffering staking updates (delegations, unbonds, validators joining) for a number of blocks. This 'epoch'd proof of stake consensus provides the guarantee that the consensus weights for validators will not change mid-epoch, except in the event of a slash condition.
An alternative design choice is to allow buffering staking updates (delegations, unbonds, validators joining) for a number of blocks. This 'epoch'd proof of stake consensus provides the guarantee that the consensus weights for validators will not change mid-epoch, except in the event of a slash condition.
Additionally, the UX hurdle may not be as significant as was previously thought. This is because it is possible to provide users immediate acknowledgement that their bond was recorded and will be executed.
@@ -31,7 +31,7 @@ Furthermore, it has become clearer over time that immediate execution of staking
* Light client efficiency. This would lessen the overhead for IBC when there is high churn in the validator set. In the Tendermint light client bisection algorithm, the number of headers you need to verify is related to bounding the difference in validator sets between a trusted header and the latest header. If the difference is too great, you verify more header in between the two. By limiting the frequency of validator set changes, we can reduce the worst case size of IBC lite client proofs, which occurs when a validator set has high churn.
* Fairness of deterministic leader election. Currently we have no ways of reasoning of fairness of deterministic leader election in the presence of staking changes without epochs (tendermint/spec#217). Breaking fairness of leader election is profitable for validators, as they earn additional rewards from being the proposer. Adding epochs at least makes it easier for our deterministic leader election to match something we can prove secure. (Albeit, we still havent proven if our current algorithm is fair with > 2 validators in the presence of stake changes)
* Fairness of deterministic leader election. Currently we have no ways of reasoning of fairness of deterministic leader election in the presence of staking changes without epochs (tendermint/spec#217). Breaking fairness of leader election is profitable for validators, as they earn additional rewards from being the proposer. Adding epochs at least makes it easier for our deterministic leader election to match something we can prove secure. (Albeit, we still havent proven if our current algorithm is fair with > 2 validators in the presence of stake changes)
* Staking derivative design. Currently, reward distribution is done lazily using the F1 fee distribution. While saving computational complexity, lazy accounting requires a more stateful staking implementation. Right now, each delegation entry has to track the time of last withdrawal. Handling this can be a challenge for some staking derivatives designs that seek to provide fungibility for all tokens staked to a single validator. Force-withdrawing rewards to users can help solve this, however it is infeasible to force-withdraw rewards to users on a per block basis. With epochs, a chain could more easily alter the design to have rewards be forcefully withdrawn (iterating over delegator accounts only once per-epoch), and can thus remove delegation timing from state. This may be useful for certain staking derivative designs.
@@ -58,7 +58,6 @@ For threshold based cryptography in particular, we need a pipeline for epoch cha
This can be handled by making a parameter for the epoch pipeline length. This parameter should not be alterable except during hard forks, to mitigate implementation complexity of switching the pipeline length.
With pipeline length 1, if I redelegate during epoch N, then my redelegation is applied prior to the beginning of epoch N+1.
With pipeline length 2, if I redelegate during epoch N, then my redelegation is applied prior to the beginning of epoch N+2.
@@ -74,11 +73,12 @@ Until an ABCI mechanism for variable block times is introduced, it is ill-advise
## Decision
__Step-1__: Implement buffering of all staking and slashing messages.
__Step-1__: Implement buffering of all staking and slashing messages.
First we create a pool for storing tokens that are being bonded, but should be applied at the epoch boundary called the `EpochDelegationPool`. Then, we have two separate queues, one for staking, one for slashing. We describe what happens on each message being delivered below:
### Staking messages
- **MsgCreateValidator**: Move user's self-bond to `EpochDelegationPool` immediately. Queue a message for the epoch boundary to handle the self-bond, taking the funds from the `EpochDelegationPool`. If Epoch execution fail, return back funds from `EpochDelegationPool` to user's account.
- **MsgEditValidator**: Validate message and if valid queue the message for execution at the end of the Epoch.
- **MsgDelegate**: Move user's funds to `EpochDelegationPool` immediately. Queue a message for the epoch boundary to handle the delegation, taking the funds from the `EpochDelegationPool`. If Epoch execution fail, return back funds from `EpochDelegationPool` to user's account.
@@ -86,18 +86,19 @@ First we create a pool for storing tokens that are being bonded, but should be a
- **MsgUndelegate**: Validate message and if valid queue the message for execution at the end of the Epoch.
### Slashing messages
- **MsgUnjail**: Validate message and if valid queue the message for execution at the end of the Epoch.
- **Slash Event**: Whenever a slash event is created, it gets queued in the slashing module to apply at the end of the epoch. The queues should be setup such that this slash applies immediately.
### Evidence Messages
- **MsgSubmitEvidence**: This gets executed immediately, and the validator gets jailed immediately. However in slashing, the actual slash event gets queued.
- **MsgSubmitEvidence**: This gets executed immediately, and the validator gets jailed immediately. However in slashing, the actual slash event gets queued.
Then we add methods to the end blockers, to ensure that at the epoch boundary the queues are cleared and delegation updates are applied.
__Step-2__: Implement querying of queued staking txs.
__Step-2__: Implement querying of queued staking txs.
When querying the staking activity of a given address, the status should return not only the amount of tokens staked, but also if there are any queued stake events for that address. This will require more work to be done in the querying logic, to trace the queued upcoming staking events.
When querying the staking activity of a given address, the status should return not only the amount of tokens staked, but also if there are any queued stake events for that address. This will require more work to be done in the querying logic, to trace the queued upcoming staking events.
As an initial implementation, this can be implemented as a linear search over all queued staking events. However, for chains that need long epochs, they should eventually build additional support for nodes that support querying to be able to produce results in constant time. (This is do-able by maintaining an auxilliary hashmap for indexing upcoming staking events by address)
@@ -8,12 +8,10 @@
DRAFT Not Implemented
## Abstract
Sparse Merke Tree ([SMT](https://osf.io/8mcnh/)) is a version of a Merkle Tree with various storage and performance optimizations. This ADR defines a separation of state commitments from data storage and the SDK transition from IAVL to SMT.
## Context
Currently, Cosmos SDK uses IAVL for both state [commitments](https://cryptography.fandom.com/wiki/Commitment_scheme) and data storage.
@@ -30,7 +28,6 @@ In the current design, IAVL is used for both data storage and as a Merkle Tree f
Moreover, the IAVL project lacks support and a maintainer and we already see better and well-established alternatives. Instead of optimizing the IAVL, we are looking into other solutions for both storage and state commitments.
## Decision
We propose to separate the concerns of state commitment (**SC**), needed for consensus, and state storage (**SS**), needed for state machine. Finally we replace IAVL with [LazyLedgers' SMT](https://github.com/lazyledger/smt). LazyLedger SMT is based on Diem (called jellyfish) design [*] - it uses a compute-optimised SMT by replacing subtrees with only default values with a single node (same approach is used by Ethereum2) and implements compact proofs.
@@ -39,7 +36,6 @@ The storage model presented here doesn't deal with data structure nor serializat
### Decouple state commitment from storage
Separation of storage and commitment (by the SMT) will allow the optimization of different components according to their usage and access patterns.
`SS` (SMT) is used to commit to a data and compute merkle proofs. `SC` is used to directly access data. To avoid collisions, both `SS` and `SC` will use a separate storage namespace (they could use the same database underneath). `SC` will store each `(key, value)` pair directly (map key -> value).
@@ -47,16 +43,17 @@ Separation of storage and commitment (by the SMT) will allow the optimization of
SMT is a merkle tree structure: we don't store keys directly. For every `(key, value)` pair, `hash(key)` is stored in a path (we hash a key to evenly distribute keys in the tree) and `hash(key, value)` in a leaf. Since we don't know a structure of a value (in particular if it contains the key) we hash both the key and the value in the `SC` leaf.
For data access we propose 2 additional KV buckets (namespaces for the key-value pairs, sometimes called [column family](https://github.com/facebook/rocksdb/wiki/Terminology)):
1. B1: `key → value`: the principal object storage, used by a state machine, behind the SDK `KVStore` interface: provides direct access by key and allows prefix iteration (KV DB backend must support it).
2. B2: `hash(key, value) → key`: a reverse index to get a key from an SMT path. Recall that SMT will store `(k, v)` as `(hash(k), hash(key, value))`. So, we can get an object value by composing `SMT_path → B2 → B1`.
3. we could use more buckets to optimize the app usage if needed.
Above, we propose to use a KV DB. However, for the state machine, we could use an RDBMS, which we discuss below.
### Requirements
State Storage requirements:
+ range queries
+ quick (key, value) access
+ creating a snapshot
@@ -64,16 +61,15 @@ State Storage requirements:
+ pruning (garbage collection)
State Commitment requirements:
+ fast updates
+ tree path should be short
+ pruning (garbage collection)
### LazyLedger SMT for State Commitment
A Sparse Merkle tree is based on the idea of a complete Merkle tree of an intractable size. The assumption here is that as the size of the tree is intractable, there would only be a few leaf nodes with valid data blocks relative to the tree size, rendering a sparse tree.
### Snapshots for storage sync and state versioning
Below, with simple _snapshot_ we refer to a database snapshot mechanism, not to a _ABCI snapshot sync_. The latter will be referred as _snapshot sync_ (which will directly use DB snapshot as described below).
@@ -93,7 +89,6 @@ Pruning old snapshots is effectively done by a database. Whenever we update a re
To manage the active snapshots we will either us a DB _max number of snapshots_ option (if available), or will remove snapshots in the `EndBlocker`. The latter option can be done efficiently by identifying snapshots with block height.
#### Accessing old state versions
One of the functional requirements is to access old state. This is done through `abci.Query` structure. The version is specified by a block height (so we query for an object by a key `K` at block height `H`). The number of old versions supported for `abci.Query` is configurable. Accessing an old state is done by using available snapshots.
@@ -103,25 +98,20 @@ Moreover, SDK could provide a way to directly access the state. However, a state
We positively [validated](https://github.com/cosmos/cosmos-sdk/discussions/8297) a versioning and snapshot mechanism for querying old state with regards to the database we evaluated.
### State Proofs
For any object stored in State Store (SS), we have corresponding object in `SC`. A proof for object `V` identified by a key `K` is a branch of `SC`, where the path corresponds to the key `hash(K)`, and the leaf is `hash(K, V)`.
### Rollbacks
We need to be able to process transactions and roll-back state updates if a transaction fails. This can be done in the following way: during transaction processing, we keep all state change requests (writes) in a `CacheWrapper` abstraction (as it's done today). Once we finish the block processing, in the `Endblocker`, we commit a root store - at that time, all changes are written to the SMT and to the `SS` and a snapshot is created.
### Committing to an object without saving it
We identified use-cases, where modules will need to save an object commitment without storing an object itself. Sometimes clients are receiving complex objects, and they have no way to prove a correctness of that object without knowing the storage layout. For those use cases it would be easier to commit to the object without storing it directly.
## Consequences
### Backwards Compatibility
This ADR doesn't introduce any SDK level API changes.
@@ -143,14 +133,12 @@ We change the storage layout of the state machine, a storage hard fork and netwo
+ Deprecating IAVL, which is one of the core proposals of Cosmos Whitepaper.
## Alternative designs.
## Alternative designs
Most of the alternative designs were evaluated in [state commitments and storage report](https://paper.dropbox.com/published/State-commitments-and-storage-review--BDvA1MLwRtOx55KRihJ5xxLbBw-KeEB7eOd11pNrZvVtqUgL3h).
Ethereum research published [Verkle Tire](https://notes.ethereum.org/_N1mutVERDKtqGIEYc-Flw#fnref1) - an idea of combining polynomial commitments with merkle tree in order to reduce the tree height. This concept has a very good potential, but we think it's too early to implement it. The current, SMT based design could be easily updated to the Verkle Tire once other research implement all necessary libraries. The main advantage of the design described in this ADR is the separation of state commitments from the data storage and designing a more powerful interface.
## Further Discussions
### Evaluated KV Databases
@@ -165,7 +153,6 @@ Use of RDBMS instead of simple KV store for state. Use of RDBMS will require an
We were discussing use case where modules can use a support database, which is not automatically committed. Module will responsible for having a sound storage model and can optionally use the feature discussed in __Committing to an object without saving it_ section.
## References
+ [IAVL What's Next?](https://github.com/cosmos/cosmos-sdk/issues/7100)
@@ -84,6 +84,7 @@ We introduce a new prefix store in `x/upgrade`'s store. This store will track ea
```
0x2 | {bytes(module_name)} => BigEndian(module_consensus_version)
```
The initial state of the store is set from `app.go`'s `InitChainer` method.
The UpgradeHandler signature needs to be updated to take a `VersionMap`, as well as return an upgraded `VersionMap` and an error:
@@ -118,7 +119,7 @@ Once all the migration handlers are registered inside the configurator (which ha
- Get the old ConsensusVersion of the module from its `VersionMap` argument (let's call it `M`).
- Fetch the new ConsensusVersion of the module from the `ConsensusVersion()` method on `AppModule` (call it `N`).
- If `N>M`, run all registered migrations for the module sequentially `M -> M+1 -> M+2...` until `N`.
- There is a special case where there is no ConsensusVersion for the module, as this means that the module has been newly added during the upgrade. In this case, no migration function is run, and the module's current ConsensusVersion is saved to `x/upgrade`'s store.
- There is a special case where there is no ConsensusVersion for the module, as this means that the module has been newly added during the upgrade. In this case, no migration function is run, and the module's current ConsensusVersion is saved to `x/upgrade`'s store.
If a required migration is missing (e.g. if it has not been registered in the `Configurator`), then the `RunMigrations` function will error.
+18 -16
View File
@@ -15,6 +15,7 @@ This ADR defines the `x/group` module which allows the creation and management o
## Context
The legacy amino multi-signature mechanism of the Cosmos SDK has certain limitations:
- Key rotation is not possible, although this can be solved with [account rekeying](adr-034-account-rekeying.md).
- Thresholds can't be changed.
- UX is cumbersome for non-technical users ([#5661](https://github.com/cosmos/cosmos-sdk/issues/5661)).
@@ -47,7 +48,7 @@ message GroupInfo {
// admin is the account address of the group's admin.
string admin = 2;
// metadata is any arbitrary metadata to attached to the group.
bytes metadata = 3;
@@ -78,10 +79,10 @@ message Member {
// address is the member's account address.
string address = 1;
// weight is the member's voting weight that should be greater than 0.
string weight = 2;
// metadata is any arbitrary metadata to attached to the member.
bytes metadata = 3;
}
@@ -106,13 +107,13 @@ message GroupAccountInfo {
// address is the group account address.
string address = 1;
// group_id is the ID of the Group the GroupAccount belongs to.
uint64 group_id = 2;
// admin is the account address of the group admin.
string admin = 3;
// metadata is any arbitrary metadata of this group account.
bytes metadata = 4;
@@ -132,13 +133,13 @@ For instance, a group admin could be another group account which could "elects"
### Decision Policy
A decision policy is the mechanism by which members of a group can vote on
A decision policy is the mechanism by which members of a group can vote on
proposals.
All decision policies should have a minimum and maximum voting window.
The minimum voting window is the minimum duration that must pass in order
for a proposal to potentially pass, and it may be set to 0. The maximum voting
window is the maximum time that a proposal may be voted on and executed if
window is the maximum time that a proposal may be voted on and executed if
it reached enough support before it is closed.
Both of these values must be less than a chain-wide max voting window parameter.
@@ -171,7 +172,7 @@ message ThresholdDecisionPolicy {
// threshold is the minimum weighted sum of support votes for a proposal to succeed.
string threshold = 1;
// voting_period is the duration from submission of a proposal to the end of voting period
// Within this period, votes and exec messages can be submitted.
google.protobuf.Duration voting_period = 2 [(gogoproto.nullable) = false];
@@ -185,6 +186,7 @@ A proposal consists of a set of `sdk.Msg`s that will be executed if the proposal
passes as well as any metadata associated with the proposal. These `sdk.Msg`s get validated as part of the `Msg/CreateProposal` request validation. They should also have their signer set as the group account.
Internally, a proposal also tracks:
- its current `Status`: submitted, closed or aborted
- its `Result`: unfinalized, accepted or rejected
- its `VoteState` in the form of a `Tally`, which is calculated on new votes and when executing the proposal.
@@ -196,13 +198,13 @@ message Tally {
// yes_count is the weighted sum of yes votes.
string yes_count = 1;
// no_count is the weighted sum of no votes.
string no_count = 2;
// abstain_count is the weighted sum of abstainers.
string abstain_count = 3;
// veto_count is the weighted sum of vetoes.
string veto_count = 4;
}
@@ -265,13 +267,13 @@ Inter-module communication introduced by [ADR-033](adr-033-protobuf-inter-module
- Convergence of `/group` and `x/gov` as both support proposals and voting: https://github.com/cosmos/cosmos-sdk/discussions/9066
- `x/group` possible future improvements:
- Execute proposals on submission (https://github.com/regen-network/regen-ledger/issues/288)
- Withdraw a proposal (https://github.com/regen-network/cosmos-modules/issues/41)
- Make `Tally` more flexible and support non-binary choices
- Execute proposals on submission (https://github.com/regen-network/regen-ledger/issues/288)
- Withdraw a proposal (https://github.com/regen-network/cosmos-modules/issues/41)
- Make `Tally` more flexible and support non-binary choices
## References
- Initial specification:
- https://gist.github.com/aaronc/b60628017352df5983791cad30babe56#group-module
- [#5236](https://github.com/cosmos/cosmos-sdk/pull/5236)
- https://gist.github.com/aaronc/b60628017352df5983791cad30babe56#group-module
- [#5236](https://github.com/cosmos/cosmos-sdk/pull/5236)
- Proposal to add `x/group` into the SDK: [#7633](https://github.com/cosmos/cosmos-sdk/issues/7633)
-9
View File
@@ -11,35 +11,29 @@
> 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
> "If you can't explain it simply, you don't understand it well enough." Provide a simplified and layman-accessible explanation of the ADR.
> A short (~200 word) description of the issue being addressed.
## Context
> This section describes the forces at play, including technological, political, social, and project local. These forces are probably in tension, and should be called out as such. The language in this section is value-neutral. It is simply describing facts. It should clearly explain the problem and motivation that the proposal aims to resolve.
> {context body}
## Decision
> This section describes our response to these forces. It is stated in full sentences, with active voice. "We will ..."
> {decision body}
## Consequences
> This section describes the resulting context, after applying the decision. All consequences should be listed here, not just the "positive" ones. A particular decision may have positive, negative, and neutral consequences, but all of them affect the team and project in the future.
### Backwards Compatibility
> All ADRs that introduce backwards incompatibilities must include a section describing these incompatibilities and their severity. The ADR must explain how the author proposes to deal with these incompatibilities. ADR submissions without a sufficient backwards compatibility treatise may be rejected outright.
### Positive
{positive consequences}
@@ -52,18 +46,15 @@
{neutral consequences}
## Further Discussions
While an ADR is in the DRAFT or PROPOSED stage, this section should contain a summary of issues to be solved in future iterations (usually referencing comments from a pull-request discussion).
Later, this section can optionally list ideas or improvements the author or reviewers found during the analysis of this ADR.
## Test Cases [optional]
Test cases for an implementation are mandatory for ADRs that are affecting consensus changes. Other ADRs can choose to include links to test cases if applicable.
## References
- {reference link}