docs: Revert SPEC-SPEC and update x/{auth,bank,evidence,slashing} (#7407)

* Revert some changes from #7404

* Update x/slashing

* Address review comments

* Small tweak

Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
Amaury Martiny 2020-10-16 14:42:48 +02:00 committed by GitHub
parent 7818867163
commit 69e2b7df16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 799 additions and 744 deletions

View File

@ -19,33 +19,28 @@ element as a part of a larger description.
## Common Layout
The specifications should be contained in a single `README.md` file inside the
`spec/` folder of a given module.
The following generalized file structure should be used to breakdown
specifications for modules. With the exception of README.md, `XX` at the
beginning of the file name should be replaced with a number to indicate
document flow (ex. read `01_state.md` before `02_state_transitions.md`). The
following list is nonbinding and all files are optional.
The following generalized document structure should be used to breakdown
specifications for modules. Each bullet item corresponds to a new section in
the document, and should begin with a secondary heading (`## {HEADING}` in
Markdown). The `XX` at the beginning of the section name should be replaced
with a number to indicate document flow (ex. read `01. Concepts` before
`02. State Transitions`). The following list is nonbinding and all sections are
optional.
- `XX. Abstract` - overview of the module
- `XX. Concepts` - describe specialized concepts and definitions used throughout the spec
- `XX. State` - specify and describe structures expected to marshalled into the store, and their keys
- `XX. State Transitions` - standard state transition operations triggered by hooks, messages, etc.
- `XX. Messages` - specify message structure(s) and expected state machine behaviour(s)
- `XX. BeginBlock` - specify any begin-block operations
- `XX. EndBlock` - specify any end-block operations
- `XX. Hooks` - describe available hooks to be called by/from this module
- `XX. Events` - list and describe event tags used
- `XX. Params` - list all module parameters, their types (in JSON) and examples
- `XX. Future Improvements` - describe future improvements of this module
- `XX. Appendix` - supplementary details referenced elsewhere within the spec
- `README.md` - overview of the module
- `XX_concepts.md` - describe specialized concepts and definitions used throughout the spec
- `XX_state.md` - specify and describe structures expected to marshalled into the store, and their keys
- `XX_state_transitions.md` - standard state transition operations triggered by hooks, messages, etc.
- `XX_messages.md` - specify message structure(s) and expected state machine behaviour(s)
- `XX_begin_block.md` - specify any begin-block operations
- `XX_end_block.md` - specify any end-block operations
- `XX_hooks.md` - describe available hooks to be called by/from this module
- `XX_events.md` - list and describe event tags used
- `XX_params.md` - list all module parameters, their types (in JSON) and examples
- `XX_future_improvements.md` - describe future improvements of this module
- `XX_appendix.md` - supplementary details referenced elsewhere within the spec
### Notation for key-value mapping
Within the `State` section, the following notation `->` should be used to describe key to
Within `state.md` the following notation `->` should be used to describe key to
value mapping:
```

View File

@ -5,7 +5,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth/types"
)
// NewAccountWithAddress implements sdk.AccountKeeper.
// NewAccountWithAddress implements AccountKeeperI.
func (ak AccountKeeper) NewAccountWithAddress(ctx sdk.Context, addr sdk.AccAddress) types.AccountI {
acc := ak.proto()
err := acc.SetAddress(addr)
@ -25,7 +25,7 @@ func (ak AccountKeeper) NewAccount(ctx sdk.Context, acc types.AccountI) types.Ac
return acc
}
// GetAccount implements sdk.AccountKeeper.
// GetAccount implements AccountKeeperI.
func (ak AccountKeeper) GetAccount(ctx sdk.Context, addr sdk.AccAddress) types.AccountI {
store := ctx.KVStore(ak.key)
bz := store.Get(types.AddressStoreKey(addr))
@ -46,7 +46,7 @@ func (ak AccountKeeper) GetAllAccounts(ctx sdk.Context) (accounts []types.Accoun
return accounts
}
// SetAccount implements sdk.AccountKeeper.
// SetAccount implements AccountKeeperI.
func (ak AccountKeeper) SetAccount(ctx sdk.Context, acc types.AccountI) {
addr := acc.GetAddress()
store := ctx.KVStore(ak.key)

View File

@ -14,6 +14,36 @@ import (
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
)
// AccountKeeperI is the interface contract that x/auth's keeper implements.
type AccountKeeperI interface {
// Return a new account with the next account number and the specified address. Does not save the new account to the store.
NewAccountWithAddress(sdk.Context, sdk.AccAddress) types.AccountI
// Return a new account with the next account number. Does not save the new account to the store.
NewAccount(sdk.Context, types.AccountI) types.AccountI
// Retrieve an account from the store.
GetAccount(sdk.Context, sdk.AccAddress) types.AccountI
// Set an account in the store.
SetAccount(sdk.Context, types.AccountI)
// Remove an account from the store.
RemoveAccount(sdk.Context, types.AccountI)
// Iterate over all accounts, calling the provided function. Stop iteraiton when it returns false.
IterateAccounts(sdk.Context, func(types.AccountI) bool)
// Fetch the public key of an account at a specified address
GetPubKey(sdk.Context, sdk.AccAddress) (crypto.PubKey, error)
// Fetch the sequence of an account at a specified address.
GetSequence(sdk.Context, sdk.AccAddress) (uint64, error)
// Fetch the next account number, and increment the internal counter.
GetNextAccountNumber(sdk.Context) uint64
}
// AccountKeeper encodes/decodes accounts using the go-amino (binary)
// encoding/decoding library.
type AccountKeeper struct {
@ -26,7 +56,9 @@ type AccountKeeper struct {
proto func() types.AccountI
}
// NewAccountKeeper returns a new sdk.AccountKeeper that uses go-amino to
var _ AccountKeeperI = &AccountKeeper{}
// NewAccountKeeper returns a new AccountKeeperI that uses go-amino to
// (binary) encode and decode concrete sdk.Accounts.
func NewAccountKeeper(
cdc codec.BinaryMarshaler, key sdk.StoreKey, paramstore paramtypes.Subspace, proto func() types.AccountI,

View File

@ -15,30 +15,39 @@ Accounts are exposed externally as an interface, and stored internally as
either a base account or vesting account. Module clients wishing to add more
account types may do so.
- `0x01 | Address -> amino(account)`
- `0x01 | Address -> ProtocolBuffer(account)`
### Account Interface
The account interface exposes methods to read and write standard account information.
Note that all of these methods operate on an account struct confirming to the interface
- in order to write the account to the store, the account keeper will need to be used.
Note that all of these methods operate on an account struct confirming to the
interface - in order to write the account to the store, the account keeper will
need to be used.
```go
type Account interface {
GetAddress() AccAddress
SetAddress(AccAddress)
// AccountI is an interface used to store coins at a given address within state.
// It presumes a notion of sequence numbers for replay protection,
// a notion of account numbers for replay protection for previously pruned accounts,
// and a pubkey for authentication purposes.
//
// Many complex conditions can be used in the concrete struct which implements AccountI.
type AccountI interface {
proto.Message
GetPubKey() PubKey
SetPubKey(PubKey)
GetAddress() sdk.AccAddress
SetAddress(sdk.AccAddress) error // errors if already set.
GetAccountNumber() uint64
SetAccountNumber(uint64)
GetPubKey() crypto.PubKey // can return nil.
SetPubKey(crypto.PubKey) error
GetSequence() uint64
SetSequence(uint64)
GetAccountNumber() uint64
SetAccountNumber(uint64) error
GetCoins() Coins
SetCoins(Coins)
GetSequence() uint64
SetSequence(uint64) error
// Ensure that account implements stringer
String() string
}
```
@ -47,13 +56,15 @@ type Account interface {
A base account is the simplest and most common account type, which just stores all requisite
fields directly in a struct.
```go
type BaseAccount struct {
Address AccAddress
Coins Coins
PubKey PubKey
AccountNumber uint64
Sequence uint64
```protobuf
// BaseAccount defines a base account type. It contains all the necessary fields
// for basic account functionality. Any custom account type should extend this
// type for additional functionality (e.g. vesting).
message BaseAccount {
string address = 1;
google.protobuf.Any pub_key = 2;
uint64 account_number = 3;
uint64 sequence = 4;
}
```

View File

@ -2,16 +2,14 @@
order: 3
-->
# Messages
TODO make this file conform to typical messages spec
# AnthHandlers
## Handlers
The auth module presently has no transaction handlers of its own, but does expose
the special `AnteHandler`, used for performing basic validity checks on a transaction,
such that it could be thrown out of the mempool. Note that the ante handler is called on
`CheckTx`, but *also* on `DeliverTx`, as Tendermint proposers presently have the ability
`CheckTx`, but _also_ on `DeliverTx`, as Tendermint proposers presently have the ability
to include in their proposed block transactions which fail `CheckTx`.
### Ante Handler

View File

@ -1,70 +0,0 @@
<!--
order: 4
-->
# Types
Besides accounts (specified in [State](02_state.md)), the types exposed by the auth module
are `StdFee`, the combination of an amount and gas limit, `StdSignature`, the combination
of an optional public key and a cryptographic signature as a byte array, `StdTx`,
a struct which implements the `sdk.Tx` interface using `StdFee` and `StdSignature`, and
`StdSignDoc`, a replay-prevention structure for `StdTx` which transaction senders must sign over.
## StdFee
A `StdFee` is simply the combination of a fee amount, in any number of denominations,
and a gas limit (where dividing the amount by the gas limit gives a "gas price").
```go
type StdFee struct {
Amount Coins
Gas uint64
}
```
## StdSignature
A `StdSignature` is the combination of an optional public key and a cryptographic signature
as a byte array. The SDK is agnostic to particular key or signature formats and supports any
supported by the `PubKey` interface.
```go
type StdSignature struct {
PubKey PubKey
Signature []byte
}
```
## StdTx
A `StdTx` is a struct which implements the `sdk.Tx` interface, and is likely to be generic
enough to serve the purposes of many Cosmos SDK blockchains.
```go
type StdTx struct {
Msgs []sdk.Msg
Fee StdFee
Signatures []StdSignature
Memo string
}
```
## StdSignDoc
A `StdSignDoc` is a replay-prevention structure to be signed over, which ensures that
any submitted transaction (which is simply a signature over a particular bytestring)
will only be executable once on a particular blockchain.
`json.RawMessage` is preferred over using the SDK types for future compatibility.
```go
type StdSignMsg struct {
ChainID string
AccountNumber uint64
Sequence uint64
TimeoutHeight uint64
Fee StdFee
Msgs []sdk.Msg
Memo string
}
```

View File

@ -12,32 +12,33 @@ Presently only one fully-permissioned account keeper is exposed, which has the a
all fields of all accounts, and to iterate over all stored accounts.
```go
type AccountKeeper interface {
// Return a new account with the next account number and the specified address. Does not save the new account to the store.
NewAccountWithAddress(AccAddress) Account
// AccountKeeperI is the interface contract that x/auth's keeper implements.
type AccountKeeperI interface {
// Return a new account with the next account number and the specified address. Does not save the new account to the store.
NewAccountWithAddress(sdk.Context, sdk.AccAddress) types.AccountI
// Return a new account with the next account number. Does not save the new account to the store.
NewAccount(Account) Account
// Return a new account with the next account number. Does not save the new account to the store.
NewAccount(sdk.Context, types.AccountI) types.AccountI
// Retrieve an account from the store
GetAccount(AccAddress) Account
// Retrieve an account from the store.
GetAccount(sdk.Context, sdk.AccAddress) types.AccountI
// Set an account in the store
SetAccount(Account)
// Set an account in the store.
SetAccount(sdk.Context, types.AccountI)
// Remove an account from the store
RemoveAccount(Account)
// Remove an account from the store.
RemoveAccount(sdk.Context, types.AccountI)
// Iterate over all accounts, calling the provided function. Stop iteraiton when it returns false.
IterateAccounts(func(Account) (bool))
// Iterate over all accounts, calling the provided function. Stop iteraiton when it returns false.
IterateAccounts(sdk.Context, func(types.AccountI) bool)
// Fetch the public key of an account at a specified address
GetPubKey(AccAddress) PubKey
// Fetch the public key of an account at a specified address
GetPubKey(sdk.Context, sdk.AccAddress) (crypto.PubKey, error)
// Fetch the sequence of an account at a specified address
GetSequence(AccAddress) uint64
// Fetch the sequence of an account at a specified address.
GetSequence(sdk.Context, sdk.AccAddress) (uint64, error)
// Fetch the next account number, and increment the internal counter
GetNextAccountNumber() uint64
// Fetch the next account number, and increment the internal counter.
GetNextAccountNumber(sdk.Context) uint64
}
```

View File

@ -7,7 +7,7 @@ order: 7
The auth module contains the following parameters:
| Key | Type | Example |
|------------------------|-----------------|---------|
| ---------------------- | --------------- | ------- |
| MaxMemoCharacters | string (uint64) | "256" |
| TxSigLimit | string (uint64) | "7" |
| TxSizeCostPerByte | string (uint64) | "10" |

View File

@ -21,24 +21,19 @@ This module will be used in the Cosmos Hub.
## Contents
1. **[Concepts](01_concepts.md)**
- [Gas & Fees](01_concepts.md#gas-&-fees)
- [Gas & Fees](01_concepts.md#gas-&-fees)
2. **[State](02_state.md)**
- [Accounts](02_state.md#accounts)
3. **[Messages](03_messages.md)**
- [Handlers](03_messages.md#handlers)
4. **[Types](03_types.md)**
- [StdFee](03_types.md#stdfee)
- [StdSignature](03_types.md#stdsignature)
- [StdTx](03_types.md#stdtx)
- [StdSignDoc](03_types.md#stdsigndoc)
5. **[Keepers](04_keepers.md)**
- [Account Keeper](04_keepers.md#account-keeper)
6. **[Vesting](05_vesting.md)**
- [Intro and Requirements](05_vesting.md#intro-and-requirements)
- [Vesting Account Types](05_vesting.md#vesting-account-types)
- [Vesting Account Specification](05_vesting.md#vesting-account-specification)
- [Keepers & Handlers](05_vesting.md#keepers-&-handlers)
- [Genesis Initialization](05_vesting.md#genesis-initialization)
- [Examples](05_vesting.md#examples)
- [Glossary](05_vesting.md#glossary)
7. **[Parameters](07_params.md)**
- [Accounts](02_state.md#accounts)
3. **[AnteHandlers](03_antehandlers.md)**
- [Handlers](03_antehandlers.md#handlers)
4. **[Keepers](04_keepers.md)**
- [Account Keeper](04_keepers.md#account-keeper)
5. **[Vesting](05_vesting.md)**
- [Intro and Requirements](05_vesting.md#intro-and-requirements)
- [Vesting Account Types](05_vesting.md#vesting-account-types)
- [Vesting Account Specification](05_vesting.md#vesting-account-specification)
- [Keepers & Handlers](05_vesting.md#keepers-&-handlers)
- [Genesis Initialization](05_vesting.md#genesis-initialization)
- [Examples](05_vesting.md#examples)
- [Glossary](05_vesting.md#glossary)
6. **[Parameters](07_params.md)**

11
x/bank/spec/01_state.md Normal file
View File

@ -0,0 +1,11 @@
<!--
order: 1
-->
# State
The `x/bank` module keeps state of two primary objects, account balances and the
total supply of all balances.
- Balances: `[]byte("balances") | []byte(address) / []byte(balance.Denom) -> ProtocolBuffer(balance)`
- Supply: `0x0 -> ProtocolBuffer(Supply)`

123
x/bank/spec/02_keepers.md Normal file
View File

@ -0,0 +1,123 @@
<!--
order: 2
-->
# Keepers
The bank module provides three different exported keeper interfaces which can be passed to other modules which need to read or update account balances. Modules should use the least-permissive interface which provides the functionality they require.
Note that you should always review the `bank` module code to ensure that permissions are limited in the way that you expect.
## Common Types
### Input
An input of a multiparty transfer
```protobuf
// Input models transaction input.
message Input {
string address = 1;
repeated cosmos.base.v1beta1.Coin coins = 2;
}
```
### Output
An output of a multiparty transfer.
```protobuf
// Output models transaction outputs.
message Output {
string address = 1;
repeated cosmos.base.v1beta1.Coin coins = 2;
}
```
## BaseKeeper
The base keeper provides full-permission access: the ability to arbitrary modify any account's balance and mint or burn coins.
```go
// Keeper defines a module interface that facilitates the transfer of coins
// between accounts.
type Keeper interface {
SendKeeper
InitGenesis(sdk.Context, types.GenesisState)
ExportGenesis(sdk.Context) *types.GenesisState
GetSupply(ctx sdk.Context) exported.SupplyI
SetSupply(ctx sdk.Context, supply exported.SupplyI)
GetDenomMetaData(ctx sdk.Context, denom string) types.Metadata
SetDenomMetaData(ctx sdk.Context, denomMetaData types.Metadata)
IterateAllDenomMetaData(ctx sdk.Context, cb func(types.Metadata) bool)
SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
SendCoinsFromModuleToModule(ctx sdk.Context, senderModule, recipientModule string, amt sdk.Coins) error
SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
DelegateCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
UndelegateCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error
BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error
DelegateCoins(ctx sdk.Context, delegatorAddr, moduleAccAddr sdk.AccAddress, amt sdk.Coins) error
UndelegateCoins(ctx sdk.Context, moduleAccAddr, delegatorAddr sdk.AccAddress, amt sdk.Coins) error
MarshalSupply(supplyI exported.SupplyI) ([]byte, error)
UnmarshalSupply(bz []byte) (exported.SupplyI, error)
types.QueryServer
}
```
## SendKeeper
The send keeper provides access to account balances and the ability to transfer coins between accounts, but not to alter the total supply (mint or burn coins).
```go
// SendKeeper defines a module interface that facilitates the transfer of coins
// between accounts without the possibility of creating coins.
type SendKeeper interface {
ViewKeeper
InputOutputCoins(ctx sdk.Context, inputs []types.Input, outputs []types.Output) error
SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error
SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error
AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error
SetBalance(ctx sdk.Context, addr sdk.AccAddress, balance sdk.Coin) error
SetBalances(ctx sdk.Context, addr sdk.AccAddress, balances sdk.Coins) error
GetParams(ctx sdk.Context) types.Params
SetParams(ctx sdk.Context, params types.Params)
SendEnabledCoin(ctx sdk.Context, coin sdk.Coin) bool
SendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error
BlockedAddr(addr sdk.AccAddress) bool
}
```
## ViewKeeper
The view keeper provides read-only access to account balances but no balance alteration functionality. All balance lookups are `O(1)`.
```go
// ViewKeeper defines a module interface that facilitates read only access to
// account balances.
type ViewKeeper interface {
ValidateBalance(ctx sdk.Context, addr sdk.AccAddress) error
HasBalance(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coin) bool
GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
GetAccountsBalances(ctx sdk.Context) []types.Balance
GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin
LockedCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
IterateAccountBalances(ctx sdk.Context, addr sdk.AccAddress, cb func(coin sdk.Coin) (stop bool))
IterateAllBalances(ctx sdk.Context, cb func(address sdk.AccAddress, coin sdk.Coin) (stop bool))
}
```

View File

@ -0,0 +1,32 @@
<!--
order: 3
-->
# Messages
## MsgSend
```go
// MsgSend represents a message to send coins from one account to another.
message MsgSend {
string from_address = 1;
string to_address = 2;
repeated cosmos.base.v1beta1.Coin amount = 3;
}
```
`handleMsgSend` just runs `inputOutputCoins`.
```
handleMsgSend(msg MsgSend)
inputSum = 0
for input in inputs
inputSum += input.Amount
outputSum = 0
for output in outputs
outputSum += output.Amount
if inputSum != outputSum:
fail with "input/output amount mismatch"
return inputOutputCoins(msg.Inputs, msg.Outputs)
```

29
x/bank/spec/04_events.md Normal file
View File

@ -0,0 +1,29 @@
<!--
order: 4
-->
# Events
The bank module emits the following events:
## Handlers
### MsgSend
| Type | Attribute Key | Attribute Value |
| -------- | ------------- | ------------------ |
| transfer | recipient | {recipientAddress} |
| transfer | amount | {amount} |
| message | module | bank |
| message | action | send |
| message | sender | {senderAddress} |
### MsgMultiSend
| Type | Attribute Key | Attribute Value |
| -------- | ------------- | ------------------ |
| transfer | recipient | {recipientAddress} |
| transfer | amount | {amount} |
| message | module | bank |
| message | action | multisend |
| message | sender | {senderAddress} |

24
x/bank/spec/05_params.md Normal file
View File

@ -0,0 +1,24 @@
<!--
order: 5
-->
# Parameters
The bank module contains the following parameters:
| Key | Type | Example |
| ------------------ | ------------- | ---------------------------------- |
| SendEnabled | []SendEnabled | [{denom: "stake", enabled: true }] |
| DefaultSendEnabled | bool | true |
## SendEnabled
The send enabled parameter is an array of SendEnabled entries mapping coin
denominations to their send_enabled status. Entries in this list take
precedence over the `DefaultSendEnabled` setting.
## DefaultSendEnabled
The default send enabled value controls send transfer capability for all
coin denominations unless specifically included in the array of `SendEnabled`
parameters.

View File

@ -1,26 +1,13 @@
<!--
order: 0
title: Bank Overview
parent:
title: "bank"
-->
# `x/bank`
## Table of Contents
<!-- TOC -->
- **[01. Abstract](#01-abstract)**
- **[02. Concepts](#02-concepts)**
- [Supply](#supply)
- [Module Accounts](#module-accounts)
- **[03. State](#03-state)**
- **[04. Keepers](#04-keepers)**
- [Common Types](#common-types)
- [BaseKeeper](#basekeeper)
- [SendKeeper](#sendkeeper)
- [ViewKeeper](#viewkeeper)
- **[05. Messages](#05-messages)**
- [MsgSend](#msgsend)
- **[06. Events](#06-events)**
- [Handlers](#handlers)
- **[07. Parameters](#07-parameters)**
## 01. Abstract
## Abstract
This document specifies the bank module of the Cosmos SDK.
@ -35,46 +22,42 @@ supply of all assets used in the application.
This module will be used in the Cosmos Hub.
## 02. Concepts
## Supply
### Supply
The `supply` module:
The `supply` functionality:
- passively tracks the total supply of coins within a chain,
- provides a pattern for modules to hold/interact with `Coins`, and
- introduces the invariant check to verify a chain's total supply.
#### Total Supply
### Total Supply
The total `Supply` of the network is equal to the sum of all coins from the
account. The total supply is updated every time a `Coin` is minted (eg: as part
of the inflation mechanism) or burned (eg: due to slashing or if a governance
proposal is vetoed).
### Module Accounts
## Module Accounts
The supply module introduces a new type of `auth.AccountI` interface, called `ModuleAccountI`, which can be used by
The supply functionality introduces a new type of `auth.Account` which can be used by
modules to allocate tokens and in special cases mint or burn tokens. At a base
level these module accounts are capable of sending/receiving tokens to and from
`auth.AccountI` interfaces and other module accounts. This design replaces previous
`auth.Account`s and other module accounts. This design replaces previous
alternative designs where, to hold tokens, modules would burn the incoming
tokens from the sender account, and then track those tokens internally. Later,
in order to send tokens, the module would need to effectively mint tokens
within a destination account. The new design removes duplicate logic between
modules to perform this accounting.
The `ModuleAccountI` interface is defined as follows:
The `ModuleAccount` interface is defined as follows:
```go
// ModuleAccountI defines an account interface for modules that hold tokens in
// an escrow.
type ModuleAccountI interface {
AccountI // same methods as the Account interface
type ModuleAccount interface {
auth.Account // same methods as the Account interface
GetName() string // name of the module; used to obtain the address
GetPermissions() []string // permissions of module account
HasPermission(string) bool
GetName() string // name of the module; used to obtain the address
GetPermissions() []string // permissions of module account
HasPermission(string) bool
}
```
@ -82,20 +65,20 @@ type ModuleAccountI interface {
> Any module or message handler that allows either direct or indirect sending of funds must explicitly guarantee those funds cannot be sent to module accounts (unless allowed).
The supply `Keeper` also introduces new wrapper functions for the auth `Keeper`
and the bank `Keeper` that are related to `ModuleAccountI`s in order to be able
and the bank `Keeper` that are related to `ModuleAccount`s in order to be able
to:
- Get and set `ModuleAccountI`s by providing the `Name`.
- Send coins from and to other `ModuleAccountI`s or standard `Account`s
- Get and set `ModuleAccount`s by providing the `Name`.
- Send coins from and to other `ModuleAccount`s or standard `Account`s
(`BaseAccount` or `VestingAccount`) by passing only the `Name`.
- `Mint` or `Burn` coins for a `ModuleAccountI` (restricted to its permissions).
- `Mint` or `Burn` coins for a `ModuleAccount` (restricted to its permissions).
#### Permissions
### Permissions
Each `ModuleAccountI` has a different set of permissions that provide different
Each `ModuleAccount` has a different set of permissions that provide different
object capabilities to perform certain actions. Permissions need to be
registered upon the creation of the supply `Keeper` so that every time a
`ModuleAccountI` calls the allowed functions, the `Keeper` can lookup the
`ModuleAccount` calls the allowed functions, the `Keeper` can lookup the
permissions to that specific account and perform or not the action.
The available permissions are:
@ -104,217 +87,16 @@ The available permissions are:
- `Burner`: allows for a module to burn a specific amount of coins.
- `Staking`: allows for a module to delegate and undelegate a specific amount of coins.
## 03. State
## Contents
The `x/bank` module keeps state of two primary objects, account balances and the
total supply of all balances.
- Balances: `[]byte("balances") | []byte(address) / []byte(balance.Denom) -> ProtocolBuffer(balance)`
- Supply: `0x0 -> ProtocolBuffer(Supply)`
## 04. Keepers
The bank module provides three different exported keeper interfaces which can be passed to other modules which need to read or update account balances. Modules should use the least-permissive interface which provides the functionality they require.
Note that you should always review the `bank` module code to ensure that permissions are limited in the way that you expect.
### Common Types
#### Input
An input of a multiparty transfer
```protobuf
// Input models transaction input.
message Input {
string address = 1;
repeated cosmos.base.v1beta1.Coin coins = 2;
}
```
#### Output
An output of a multiparty transfer.
```protobuf
// Output models transaction outputs.
message Output {
string address = 1;
repeated cosmos.base.v1beta1.Coin coins = 2;
}
```
### BaseKeeper
The base keeper provides full-permission access: the ability to arbitrary modify any account's balance and mint or burn coins. The `BaseKeeper` struct implements the following `Keeper` interface.
```go
// Keeper defines a module interface that facilitates the transfer of coins
// between accounts.
type Keeper interface {
SendKeeper
InitGenesis(sdk.Context, types.GenesisState)
ExportGenesis(sdk.Context) *types.GenesisState
GetSupply(ctx sdk.Context) exported.SupplyI
SetSupply(ctx sdk.Context, supply exported.SupplyI)
GetDenomMetaData(ctx sdk.Context, denom string) types.Metadata
SetDenomMetaData(ctx sdk.Context, denomMetaData types.Metadata)
IterateAllDenomMetaData(ctx sdk.Context, cb func(types.Metadata) bool)
SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
SendCoinsFromModuleToModule(ctx sdk.Context, senderModule, recipientModule string, amt sdk.Coins) error
SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
DelegateCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
UndelegateCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error
BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error
DelegateCoins(ctx sdk.Context, delegatorAddr, moduleAccAddr sdk.AccAddress, amt sdk.Coins) error
UndelegateCoins(ctx sdk.Context, moduleAccAddr, delegatorAddr sdk.AccAddress, amt sdk.Coins) error
MarshalSupply(supplyI exported.SupplyI) ([]byte, error)
UnmarshalSupply(bz []byte) (exported.SupplyI, error)
types.QueryServer
}
```
### SendKeeper
The send keeper provides access to account balances and the ability to transfer coins between accounts, but not to alter the total supply (mint or burn coins).
```go
// SendKeeper defines a module interface that facilitates the transfer of coins
// between accounts without the possibility of creating coins.
type SendKeeper interface {
ViewKeeper
InputOutputCoins(ctx sdk.Context, inputs []types.Input, outputs []types.Output) error
SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error
SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error
AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error
SetBalance(ctx sdk.Context, addr sdk.AccAddress, balance sdk.Coin) error
SetBalances(ctx sdk.Context, addr sdk.AccAddress, balances sdk.Coins) error
GetParams(ctx sdk.Context) types.Params
SetParams(ctx sdk.Context, params types.Params)
SendEnabledCoin(ctx sdk.Context, coin sdk.Coin) bool
SendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error
BlockedAddr(addr sdk.AccAddress) bool
}
```
### ViewKeeper
The view keeper provides read-only access to account balances but no balance alteration functionality. All balance lookups are `O(1)`.
```go
// ViewKeeper defines a module interface that facilitates read only access to
// account balances.
type ViewKeeper interface {
ValidateBalance(ctx sdk.Context, addr sdk.AccAddress) error
HasBalance(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coin) bool
GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
GetAccountsBalances(ctx sdk.Context) []types.Balance
GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin
LockedCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
IterateAccountBalances(ctx sdk.Context, addr sdk.AccAddress, cb func(coin sdk.Coin) (stop bool))
IterateAllBalances(ctx sdk.Context, cb func(address sdk.AccAddress, coin sdk.Coin) (stop bool))
}
```
## 05. Messages
### MsgSend
```protobuf
// MsgSend represents a message to send coins from one account to another.
message MsgSend {
string from_address = 1;
string to_address = 2;
repeated cosmos.base.v1beta1.Coin amount = 3;
}
```
`handleMsgSend` just runs `inputOutputCoins`.
```
handleMsgSend(msg MsgSend)
inputSum = 0
for input in inputs
inputSum += input.Amount
outputSum = 0
for output in outputs
outputSum += output.Amount
if inputSum != outputSum:
fail with "input/output amount mismatch"
return inputOutputCoins(msg.Inputs, msg.Outputs)
```
## 06. Events
The bank module emits the following events:
### Handlers
#### MsgSend
| Type | Attribute Key | Attribute Value |
| -------- | ------------- | ------------------ |
| transfer | recipient | {recipientAddress} |
| transfer | amount | {amount} |
| message | module | bank |
| message | action | send |
| message | sender | {senderAddress} |
#### MsgMultiSend
| Type | Attribute Key | Attribute Value |
| -------- | ------------- | ------------------ |
| transfer | recipient | {recipientAddress} |
| transfer | amount | {amount} |
| message | module | bank |
| message | action | multisend |
| message | sender | {senderAddress} |
## 07. Parameters
The bank module contains the following parameters:
| Key | Type | Example |
| ------------------ | ------------- | ---------------------------------- |
| SendEnabled | []SendEnabled | [{denom: "stake", enabled: true }] |
| DefaultSendEnabled | bool | true |
The corresponding Protobuf message is:
```protobuf
// Params defines the parameters for the bank module.
message Params {
option = false;
repeated SendEnabled send_enabled = 1;
bool default_send_enabled = 2;
}
```
### SendEnabled
The send enabled parameter is an array of SendEnabled entries mapping coin
denominations to their send_enabled status. Entries in this list take
precedence over the `DefaultSendEnabled` setting.
### DefaultSendEnabled
The default send enabled value controls send transfer capability for all
coin denominations unless specifically included in the array of `SendEnabled`
parameters.
1. **[State](01_state.md)**
2. **[Keepers](02_keepers.md)**
- [Common Types](02_keepers.md#common-types)
- [BaseKeeper](02_keepers.md#basekeeper)
- [SendKeeper](02_keepers.md#sendkeeper)
- [ViewKeeper](02_keepers.md#viewkeeper)
3. **[Messages](03_messages.md)**
- [MsgSend](03_messages.md#msgsend)
4. **[Events](04_events.md)**
- [Handlers](04_events.md#handlers)
5. **[Parameters](05_params.md)**

View File

@ -0,0 +1,75 @@
<!--
order: 1
-->
# Concepts
## Evidence
Any concrete type of evidence submitted to the `x/evidence` module must fulfill the
`Evidence` contract outlined below. Not all concrete types of evidence will fulfill
this contract in the same way and some data may be entirely irrelevant to certain
types of evidence. An additional `ValidatorEvidence`, which extends `Evidence`,
has also been created to define a contract for evidence against malicious validators.
```go
// Evidence defines the contract which concrete evidence types of misbehavior
// must implement.
type Evidence interface {
Route() string
Type() string
String() string
Hash() tmbytes.HexBytes
ValidateBasic() error
// Height at which the infraction occurred
GetHeight() int64
}
// ValidatorEvidence extends Evidence interface to define contract
// for evidence against malicious validators
type ValidatorEvidence interface {
Evidence
// The consensus address of the malicious validator at time of infraction
GetConsensusAddress() sdk.ConsAddress
// The total power of the malicious validator at time of infraction
GetValidatorPower() int64
// The total validator set power at time of infraction
GetTotalPower() int64
}
```
## Registration & Handling
The `x/evidence` module must first know about all types of evidence it is expected
to handle. This is accomplished by registering the `Route` method in the `Evidence`
contract with what is known as a `Router` (defined below). The `Router` accepts
`Evidence` and attempts to find the corresponding `Handler` for the `Evidence`
via the `Route` method.
```go
type Router interface {
AddRoute(r string, h Handler) Router
HasRoute(r string) bool
GetRoute(path string) Handler
Seal()
Sealed() bool
}
```
The `Handler` (defined below) is responsible for executing the entirety of the
business logic for handling `Evidence`. This typically includes validating the
evidence, both stateless checks via `ValidateBasic` and stateful checks via any
keepers provided to the `Handler`. In addition, the `Handler` may also perform
capabilities such as slashing and jailing a validator.
```go
// Handler defines an agnostic Evidence handler. The handler is responsible
// for executing all corresponding business logic necessary for verifying the
// evidence as valid. In addition, the Handler may execute any necessary
// slashing and potential jailing.
type Handler func(Context, Evidence) error
```

View File

@ -0,0 +1,19 @@
<!--
order: 2
-->
# State
Currently the `x/evidence` module only stores valid submitted `Evidence` in state.
The evidence state is also stored and exported in the `x/evidence` module's `GenesisState`.
```protobuf
// GenesisState defines the evidence module's genesis state.
message GenesisState {
// evidence defines all the evidence at genesis.
repeated google.protobuf.Any evidence = 1;
}
```
All `Evidence` is retrieved and stored via a prefix `KVStore` using prefix `0x00` (`KeyPrefixEvidence`).

View File

@ -0,0 +1,48 @@
<!--
order: 3
-->
# Messages
## MsgSubmitEvidence
Evidence is submitted through a `MsgSubmitEvidence` message:
```protobuf
// MsgSubmitEvidence represents a message that supports submitting arbitrary
// Evidence of misbehavior such as equivocation or counterfactual signing.
message MsgSubmitEvidence {
string submitter = 1;
google.protobuf.Any evidence = 2;
}
```
Note, the `Evidence` of a `MsgSubmitEvidence` message must have a corresponding
`Handler` registered with the `x/evidence` module's `Router` in order to be processed
and routed correctly.
Given the `Evidence` is registered with a corresponding `Handler`, it is processed
as follows:
```go
func SubmitEvidence(ctx Context, evidence Evidence) error {
if _, ok := GetEvidence(ctx, evidence.Hash()); ok {
return ErrEvidenceExists(codespace, evidence.Hash().String())
}
if !router.HasRoute(evidence.Route()) {
return ErrNoEvidenceHandlerExists(codespace, evidence.Route())
}
handler := router.GetRoute(evidence.Route())
if err := handler(ctx, evidence); err != nil {
return ErrInvalidEvidence(codespace, err.Error())
}
SetEvidence(ctx, evidence)
return nil
}
```
First, there must not already exist valid submitted `Evidence` of the exact same
type. Secondly, the `Evidence` is routed to the `Handler` and executed. Finally,
if there is no error in handling the `Evidence`, it is persisted to state.

View File

@ -0,0 +1,18 @@
<!--
order: 4
-->
# Events
The `x/evidence` module emits the following events:
## Handlers
### MsgSubmitEvidence
| Type | Attribute Key | Attribute Value |
| --------------- | ------------- | --------------- |
| submit_evidence | evidence_hash | {evidenceHash} |
| message | module | evidence |
| message | sender | {senderAddress} |
| message | action | submit_evidence |

View File

@ -0,0 +1,7 @@
<!--
order: 5
-->
# Parameters
The evidence module does not contain any parameters.

View File

@ -0,0 +1,155 @@
<!--
order: 6
-->
# BeginBlock
## Evidence Handling
Tendermint blocks can include
[Evidence](https://github.com/tendermint/tendermint/blob/master/docs/spec/blockchain/blockchain.md#evidence),
which indicates that a validator committed malicious behavior. The relevant information is
forwarded to the application as ABCI Evidence in `abci.RequestBeginBlock` so that
the validator an be accordingly punished.
### Equivocation
Currently, the SDK handles two types of evidence inside ABCI's `BeginBlock`:
- `DuplicateVoteEvidence`,
- `LightClientAttackEvidence`.
These two evidence types are handled the same way by the evidence module. First, the SDK converts the Tendermint concrete evidence type to a SDK `Evidence` interface using `Equivocation` as the concrete type.
```proto
// Equivocation implements the Evidence interface.
message Equivocation {
int64 height = 1;
google.protobuf.Timestamp time = 2;
int64 power = 3;
string consensus_address = 4;
}
```
For some `Equivocation` submitted in `block` to be valid, it must satisfy:
`Evidence.Timestamp >= block.Timestamp - MaxEvidenceAge`
Where `Evidence.Timestamp` is the timestamp in the block at height `Evidence.Height` and
`block.Timestamp` is the current block timestamp.
If valid `Equivocation` evidence is included in a block, the validator's stake is
reduced (slashed) by `SlashFractionDoubleSign`, which is defined by the `x/slashing` module,
of what their stake was when the infraction occurred (rather than when the evidence was discovered).
We want to "follow the stake", i.e. the stake which contributed to the infraction
should be slashed, even if it has since been redelegated or started unbonding.
In addition, the validator is permanently jailed and tombstoned making it impossible for that
validator to ever re-enter the validator set.
The `Equivocation` evidence is handled as follows:
```go
func (k Keeper) HandleEquivocationEvidence(ctx sdk.Context, evidence *types.Equivocation) {
logger := k.Logger(ctx)
consAddr := evidence.GetConsensusAddress()
if _, err := k.slashingKeeper.GetPubkey(ctx, consAddr.Bytes()); err != nil {
// Ignore evidence that cannot be handled.
//
// NOTE: We used to panic with:
// `panic(fmt.Sprintf("Validator consensus-address %v not found", consAddr))`,
// but this couples the expectations of the app to both Tendermint and
// the simulator. Both are expected to provide the full range of
// allowable but none of the disallowed evidence types. Instead of
// getting this coordination right, it is easier to relax the
// constraints and ignore evidence that cannot be handled.
return
}
// calculate the age of the evidence
infractionHeight := evidence.GetHeight()
infractionTime := evidence.GetTime()
ageDuration := ctx.BlockHeader().Time.Sub(infractionTime)
ageBlocks := ctx.BlockHeader().Height - infractionHeight
// Reject evidence if the double-sign is too old. Evidence is considered stale
// if the difference in time and number of blocks is greater than the allowed
// parameters defined.
cp := ctx.ConsensusParams()
if cp != nil && cp.Evidence != nil {
if ageDuration > cp.Evidence.MaxAgeDuration && ageBlocks > cp.Evidence.MaxAgeNumBlocks {
logger.Info(
"ignored equivocation; evidence too old",
"validator", consAddr,
"infraction_height", infractionHeight,
"max_age_num_blocks", cp.Evidence.MaxAgeNumBlocks,
"infraction_time", infractionTime,
"max_age_duration", cp.Evidence.MaxAgeDuration,
)
return
}
}
validator := k.stakingKeeper.ValidatorByConsAddr(ctx, consAddr)
if validator == nil || validator.IsUnbonded() {
// Defensive: Simulation doesn't take unbonding periods into account, and
// Tendermint might break this assumption at some point.
return
}
if ok := k.slashingKeeper.HasValidatorSigningInfo(ctx, consAddr); !ok {
panic(fmt.Sprintf("expected signing info for validator %s but not found", consAddr))
}
// ignore if the validator is already tombstoned
if k.slashingKeeper.IsTombstoned(ctx, consAddr) {
logger.Info(
"ignored equivocation; validator already tombstoned",
"validator", consAddr,
"infraction_height", infractionHeight,
"infraction_time", infractionTime,
)
return
}
logger.Info(
"confirmed equivocation",
"validator", consAddr,
"infraction_height", infractionHeight,
"infraction_time", infractionTime,
)
// We need to retrieve the stake distribution which signed the block, so we
// subtract ValidatorUpdateDelay from the evidence height.
// Note, that this *can* result in a negative "distributionHeight", up to
// -ValidatorUpdateDelay, i.e. at the end of the
// pre-genesis block (none) = at the beginning of the genesis block.
// That's fine since this is just used to filter unbonding delegations & redelegations.
distributionHeight := infractionHeight - sdk.ValidatorUpdateDelay
// Slash validator. The `power` is the int64 power of the validator as provided
// to/by Tendermint. This value is validator.Tokens as sent to Tendermint via
// ABCI, and now received as evidence. The fraction is passed in to separately
// to slash unbonding and rebonding delegations.
k.slashingKeeper.Slash(
ctx,
consAddr,
k.slashingKeeper.SlashFractionDoubleSign(ctx),
evidence.GetValidatorPower(), distributionHeight,
)
// Jail the validator if not already jailed. This will begin unbonding the
// validator if not already unbonding (tombstoned).
if !validator.IsJailed() {
k.slashingKeeper.Jail(ctx, consAddr)
}
k.slashingKeeper.JailUntil(ctx, consAddr, types.DoubleSignJailEndTime)
k.slashingKeeper.Tombstone(ctx, consAddr)
}
```
Note, the slashing, jailing, and tombstoning calls are delegated through the `x/slashing` module
which emit informative events and finally delegate calls to the `x/staking` module. Documentation
on slashing and jailing can be found in the [x/staking spec](/.././cosmos-sdk/x/staking/spec/02_state_transitions.md)

View File

@ -1,18 +1,24 @@
<!--
order: 0
title: Evidence Overview
parent:
title: "evidence"
-->
# `x/evidence`
## Table of Contents
<!-- TOC -->
- **[01. Abstract](#01-abstract)**
- **[02. Concepts](#02-concepts)**
- **[03. State](#03-state)**
- **[04. Messages](#04-messages)**
- **[05. Events](#05-events)**
- **[06. Parameters](#06-parameters)**
- **[07. BeginBlock](#07-beginblock)**
1. **[Concepts](01_concepts.md)**
2. **[State](02_state.md)**
3. **[Messages](03_messages.md)**
4. **[Events](04_events.md)**
5. **[Params](05_params.md)**
6. **[BeginBlock](06_begin_block.md)**
## 01. Abstract
## Abstract
`x/evidence` is an implementation of a Cosmos SDK module, per [ADR 009](./../../../docs/architecture/adr-009-evidence-module.md),
that allows for the submission and handling of arbitrary evidence of misbehavior such
@ -32,253 +38,3 @@ keeper in order for it to be successfully routed and executed.
Each corresponding handler must also fulfill the `Handler` interface contract. The
`Handler` for a given `Evidence` type can perform any arbitrary state transitions
such as slashing, jailing, and tombstoning.
## 02. Concepts
### Evidence
Any concrete type of evidence submitted to the `x/evidence` module must fulfill the
`Evidence` contract outlined below. Not all concrete types of evidence will fulfill
this contract in the same way and some data may be entirely irrelevant to certain
types of evidence. An additional `ValidatorEvidence`, which extends `Evidence`, has also
been created to define a contract for evidence against malicious validators.
```go
// Evidence defines the contract which concrete evidence types of misbehavior
// must implement.
type Evidence interface {
Route() string
Type() string
String() string
Hash() tmbytes.HexBytes
ValidateBasic() error
// Height at which the infraction occurred
GetHeight() int64
}
// ValidatorEvidence extends Evidence interface to define contract
// for evidence against malicious validators
type ValidatorEvidence interface {
Evidence
// The consensus address of the malicious validator at time of infraction
GetConsensusAddress() sdk.ConsAddress
// The total power of the malicious validator at time of infraction
GetValidatorPower() int64
// The total validator set power at time of infraction
GetTotalPower() int64
}
```
### Registration & Handling
The `x/evidence` module must first know about all types of evidence it is expected
to handle. This is accomplished by registering the `Route` method in the `Evidence`
contract with what is known as a `Router` (defined below). The `Router` accepts
`Evidence` and attempts to find the corresponding `Handler` for the `Evidence`
via the `Route` method.
```go
// Router defines a contract for which any Evidence handling module must
// implement in order to route Evidence to registered Handlers.
type Router interface {
AddRoute(r string, h Handler) Router
HasRoute(r string) bool
GetRoute(path string) Handler
Seal()
Sealed() bool
}
```
The `Handler` (defined below) is responsible for executing the entirety of the
business logic for handling `Evidence`. This typically includes validating the
evidence, both stateless checks via `ValidateBasic` and stateful checks via any
keepers provided to the `Handler`. In addition, the `Handler` may also perform
capabilities such as slashing and jailing a validator.
```go
// Handler defines an agnostic Evidence handler. The handler is responsible
// for executing all corresponding business logic necessary for verifying the
// evidence as valid. In addition, the Handler may execute any necessary
// slashing and potential jailing.
type Handler func(Context, Evidence) error
```
## 03. State
Currently the `x/evidence` module only stores valid submitted `Evidence` in state.
The evidence state is also stored and exported in the `x/evidence` module's `GenesisState`.
```protobuf
// GenesisState defines the evidence module's genesis state.
message GenesisState {
// evidence defines all the evidence at genesis.
repeated google.protobuf.Any evidence = 1;
}
```
All `Evidence` is retrieved and stored via a prefix `KVStore` using prefix `0x00` (`KeyPrefixEvidence`).
## 04. Messages
### MsgSubmitEvidence
Evidence is submitted through a `MsgSubmitEvidence` message:
```protobuf
// MsgSubmitEvidence represents a message that supports submitting arbitrary
// Evidence of misbehavior such as equivocation or counterfactual signing.
message MsgSubmitEvidence {
string submitter = 1;
google.protobuf.Any evidence = 2;
}
```
Note, the `Evidence` of a `MsgSubmitEvidence` message must have a corresponding
`Handler` registered with the `x/evidence` module's `Router` in order to be processed
and routed correctly.
Given the `Evidence` is registered with a corresponding `Handler`, it is processed
as follows:
```go
func SubmitEvidence(ctx Context, evidence Evidence) error {
if _, ok := GetEvidence(ctx, evidence.Hash()); ok {
return ErrEvidenceExists(codespace, evidence.Hash().String())
}
if !router.HasRoute(evidence.Route()) {
return ErrNoEvidenceHandlerExists(codespace, evidence.Route())
}
handler := router.GetRoute(evidence.Route())
if err := handler(ctx, evidence); err != nil {
return ErrInvalidEvidence(codespace, err.Error())
}
SetEvidence(ctx, evidence)
return nil
}
```
First, there must not already exist valid submitted `Evidence` of the exact same
type. Secondly, the `Evidence` is routed to the `Handler` and executed. Finally,
if there is no error in handling the `Evidence`, it is persisted to state.
## 05. Events
The `x/evidence` module emits the following events:
### Handlers
#### MsgSubmitEvidence
| Type | Attribute Key | Attribute Value |
| --------------- | ------------- | --------------- |
| submit_evidence | evidence_hash | {evidenceHash} |
| message | module | evidence |
| message | sender | {senderAddress} |
| message | action | submit_evidence |
## 06. Parameters
The evidence module does not have any parameters.
## 07. BeginBlock
### Evidence Handling
Tendermint blocks can include
[Evidence](https://github.com/tendermint/tendermint/blob/master/docs/spec/blockchain/blockchain.md#evidence),
which indicates that a validator committed malicious behavior. The relevant information is
forwarded to the application as ABCI Evidence in `abci.RequestBeginBlock` so that
the validator an be accordingly punished.
#### Equivocation
Currently, the evidence module only handles evidence of type `Equivocation` which is derived from
Tendermint's `ABCIEvidenceTypeDuplicateVote` during `BeginBlock`.
For some `Equivocation` submitted in `block` to be valid, it must satisfy:
`Evidence.Timestamp >= block.Timestamp - MaxEvidenceAge`
Where `Evidence.Timestamp` is the timestamp in the block at height `Evidence.Height` and
`block.Timestamp` is the current block timestamp.
If valid `Equivocation` evidence is included in a block, the validator's stake is
reduced (slashed) by `SlashFractionDoubleSign`, which is defined by the `x/slashing` module,
of what their stake was when the infraction occurred (rather than when the evidence was discovered).
We want to "follow the stake", i.e. the stake which contributed to the infraction
should be slashed, even if it has since been redelegated or started unbonding.
In addition, the validator is permanently jailed and tombstoned making it impossible for that
validator to ever re-enter the validator set.
The `Equivocation` evidence is handled as follows:
```go
func (k Keeper) HandleDoubleSign(ctx Context, evidence Equivocation) {
consAddr := evidence.GetConsensusAddress()
infractionHeight := evidence.GetHeight()
// calculate the age of the evidence
blockTime := ctx.BlockHeader().Time
age := blockTime.Sub(evidence.GetTime())
// reject evidence we cannot handle
if _, err := k.slashingKeeper.GetPubkey(ctx, consAddr.Bytes()); err != nil {
return
}
// reject evidence if it is too old
if age > k.MaxEvidenceAge(ctx) {
return
}
// reject evidence if the validator is already unbonded
validator := k.stakingKeeper.ValidatorByConsAddr(ctx, consAddr)
if validator == nil || validator.IsUnbonded() {
return
}
// verify the validator has signing info in order to be slashed and tombstoned
if ok := k.slashingKeeper.HasValidatorSigningInfo(ctx, consAddr); !ok {
panic(...)
}
// reject evidence if the validator is already tombstoned
if k.slashingKeeper.IsTombstoned(ctx, consAddr) {
return
}
// We need to retrieve the stake distribution which signed the block, so we
// subtract ValidatorUpdateDelay from the evidence height.
// Note, that this *can* result in a negative "distributionHeight", up to
// -ValidatorUpdateDelay, i.e. at the end of the
// pre-genesis block (none) = at the beginning of the genesis block.
// That's fine since this is just used to filter unbonding delegations & redelegations.
distributionHeight := infractionHeight - sdk.ValidatorUpdateDelay
// Slash validator. The `power` is the int64 power of the validator as provided
// to/by Tendermint. This value is validator.Tokens as sent to Tendermint via
// ABCI, and now received as evidence. The fraction is passed in to separately
// to slash unbonding and rebonding delegations.
k.slashingKeeper.Slash(ctx, consAddr, evidence.GetValidatorPower(), distributionHeight)
// Jail the validator if not already jailed. This will begin unbonding the
// validator if not already unbonding (tombstoned).
if !validator.IsJailed() {
k.slashingKeeper.Jail(ctx, consAddr)
}
k.slashingKeeper.JailUntil(ctx, consAddr, types.DoubleSignJailEndTime)
k.slashingKeeper.Tombstone(ctx, consAddr)
}
```
Note, the slashing, jailing, and tombstoning calls are delegated through the `x/slashing` module
which emit informative events and finally delegate calls to the `x/staking` module. Documentation
on slashing and jailing can be found in the [x/staking spec](/.././cosmos-sdk/x/staking/spec/02_state_transitions.md)

View File

@ -8,8 +8,8 @@ order: 1
At any given time, there are any number of validators registered in the state
machine. Each block, the top `MaxValidators` (defined by `x/staking`) validators
who are not jailed become *bonded*, meaning that they may propose and vote on
blocks. Validators who are *bonded* are *at stake*, meaning that part or all of
who are not jailed become _bonded_, meaning that they may propose and vote on
blocks. Validators who are _bonded_ are _at stake_, meaning that part or all of
their stake and their delegators' stake is at risk if they commit a protocol fault.
For each of these validators we keep a `ValidatorSigningInfo` record that contains
@ -20,26 +20,26 @@ attributes.
In order to mitigate the impact of initially likely categories of non-malicious
protocol faults, the Cosmos Hub implements for each validator
a *tombstone* cap, which only allows a validator to be slashed once for a double
a _tombstone_ cap, which only allows a validator to be slashed once for a double
sign fault. For example, if you misconfigure your HSM and double-sign a bunch of
old blocks, you'll only be punished for the first double-sign (and then immediately tombstombed). This will still be quite expensive and desirable to avoid, but tombstone caps
somewhat blunt the economic impact of unintentional misconfiguration.
Liveness faults do not have caps, as they can't stack upon each other. Liveness bugs are "detected" as soon as the infraction occurs, and the validators are immediately put in jail, so it is not possible for them to commit multiple liveness faults without unjailing in between.
Liveness faults do not have caps, as they can't stack upon each other. Liveness bugs are "detected" as soon as the infraction occurs, and the validators are immediately put in jail, so it is not possible for them to commit multiple liveness faults without unjailing in between.
## Infraction Timelines
To illustrate how the `x/slashing` module handles submitted evidence through
Tendermint consensus, consider the following examples:
__Definitions__:
**Definitions**:
*[* : timeline start
*]* : timeline end
*C<sub>n</sub>* : infraction `n` committed
*D<sub>n</sub>* : infraction `n` discovered
*V<sub>b</sub>* : validator bonded
*V<sub>u</sub>* : validator unbonded
_[_ : timeline start
_]_ : timeline end
_C<sub>n</sub>_ : infraction `n` committed
_D<sub>n</sub>_ : infraction `n` discovered
_V<sub>b</sub>_ : validator bonded
_V<sub>u</sub>_ : validator unbonded
### Single Double Sign Infraction

View File

@ -40,27 +40,35 @@ bonded validator. The `SignedBlocksWindow` parameter defines the size
The information stored for tracking validator liveness is as follows:
```go
type ValidatorSigningInfo struct {
Address sdk.ConsAddress
StartHeight int64
IndexOffset int64
JailedUntil time.Time
Tombstoned bool
MissedBlocksCounter int64
```protobuf
// ValidatorSigningInfo defines a validator's signing info for monitoring their
// liveness activity.
message ValidatorSigningInfo {
string address = 1;
// height at which validator was first a candidate OR was unjailed
int64 start_height = 2;
// index offset into signed block bit array
int64 index_offset = 3;
// timestamp validator cannot be unjailed until
google.protobuf.Timestamp jailed_until = 4;
// whether or not a validator has been tombstoned (killed out of validator
// set)
bool tombstoned = 5;
// missed blocks counter (to avoid scanning the array every time)
int64 missed_blocks_counter = 6;
}
```
Where:
- __Address__: The validator's consensus address.
- __StartHeight__: The height that the candidate became an active validator
- **Address**: The validator's consensus address.
- **StartHeight**: The height that the candidate became an active validator
(with non-zero voting power).
- __IndexOffset__: Index which is incremented each time the validator was a bonded
- **IndexOffset**: Index which is incremented each time the validator was a bonded
in a block and may have signed a precommit or not. This in conjunction with the
`SignedBlocksWindow` param determines the index in the `MissedBlocksBitArray`.
- __JailedUntil__: Time for which the validator is jailed until due to liveness downtime.
- __Tombstoned__: Desribes if the validator is tombstoned or not. It is set once the
- **JailedUntil**: Time for which the validator is jailed until due to liveness downtime.
- **Tombstoned**: Desribes if the validator is tombstoned or not. It is set once the
validator commits an equivocation or for any other configured misbehiavor.
- __MissedBlocksCounter__: A counter kept to avoid unnecessary array reads. Note
- **MissedBlocksCounter**: A counter kept to avoid unnecessary array reads. Note
that `Sum(MissedBlocksBitArray)` equals `MissedBlocksCounter` always.

View File

@ -9,15 +9,21 @@ In this section we describe the processing of messages for the `slashing` module
## Unjail
If a validator was automatically unbonded due to downtime and wishes to come back online &
possibly rejoin the bonded set, it must send `TxUnjail`:
possibly rejoin the bonded set, it must send `MsgUnjail`:
```protobuf
// MsgUnjail is an sdk.Msg used for unjailing a jailed validator, thus returning
// them into the bonded validator set, so they can begin receiving provisions
// and rewards again.
message MsgUnjail {
string validator_addr = 1;
}
```
And below is its corresponding handler:
```
type TxUnjail struct {
ValidatorAddr sdk.AccAddress
}
handleMsgUnjail(tx TxUnjail)
handleMsgUnjail(tx MsgUnjail)
validator = getValidator(tx.ValidatorAddr)
if validator == nil
fail with "No validator found"

View File

@ -23,7 +23,7 @@ greater than `minHeight` and the validator's `MissedBlocksCounter` is greater th
for `DowntimeJailDuration`, and have the following values reset:
`MissedBlocksBitArray`, `MissedBlocksCounter`, and `IndexOffset`.
__Note__: Liveness slashes do **NOT** lead to a tombstombing.
**Note**: Liveness slashes do **NOT** lead to a tombstombing.
```go
height := block.Height

View File

@ -25,6 +25,6 @@ onValidatorBonded(address sdk.ValAddress)
}
setValidatorSigningInfo(signingInfo)
}
return
```

View File

@ -15,18 +15,18 @@ and evidence of the infraction reaching the state machine (this is one of the
primary reasons for the existence of the unbonding period).
> Note: The tombstone concept, only applies to faults that have a delay between
the infraction occurring and evidence reaching the state machine. For example,
evidence of a validator double signing may take a while to reach the state machine
due to unpredictable evidence gossip layer delays and the ability of validators to
selectively reveal double-signatures (e.g. to infrequently-online light clients).
Liveness slashing, on the other hand, is detected immediately as soon as the
infraction occurs, and therefore no slashing period is needed. A validator is
immediately put into jail period, and they cannot commit another liveness fault
until they unjail. In the future, there may be other types of byzantine faults
that have delays (for example, submitting evidence of an invalid proposal as a transaction).
When implemented, it will have to be decided whether these future types of
byzantine faults will result in a tombstoning (and if not, the slash amounts
will not be capped by a slashing period).
> the infraction occurring and evidence reaching the state machine. For example,
> evidence of a validator double signing may take a while to reach the state machine
> due to unpredictable evidence gossip layer delays and the ability of validators to
> selectively reveal double-signatures (e.g. to infrequently-online light clients).
> Liveness slashing, on the other hand, is detected immediately as soon as the
> infraction occurs, and therefore no slashing period is needed. A validator is
> immediately put into jail period, and they cannot commit another liveness fault
> until they unjail. In the future, there may be other types of byzantine faults
> that have delays (for example, submitting evidence of an invalid proposal as a transaction).
> When implemented, it will have to be decided whether these future types of
> byzantine faults will result in a tombstoning (and if not, the slash amounts
> will not be capped by a slashing period).
In the current system design, once a validator is put in the jail for a consensus
fault, after the `JailPeriod` they are allowed to send a transaction to `unjail`
@ -72,10 +72,10 @@ As the number of slashing periods increase, it creates more complexity as we hav
to keep track of the highest infraction amount for every single slashing period.
> Note: Currently, according to the `slashing` module spec, a new slashing period
is created every time a validator is unbonded then rebonded. This should probably
be changed to jailed/unjailed. See issue [#3205](https://github.com/cosmos/cosmos-sdk/issues/3205)
for further details. For the remainder of this, I will assume that we only start
a new slashing period when a validator gets unjailed.
> is created every time a validator is unbonded then rebonded. This should probably
> be changed to jailed/unjailed. See issue [#3205](https://github.com/cosmos/cosmos-sdk/issues/3205)
> for further details. For the remainder of this, I will assume that we only start
> a new slashing period when a validator gets unjailed.
The maximum number of slashing periods is the `len(UnbondingPeriod) / len(JailPeriod)`.
The current defaults in Gaia for the `UnbondingPeriod` and `JailPeriod` are 3 weeks
@ -85,7 +85,7 @@ we only have to track 1 slashing period (i.e not have to track slashing periods)
Currently, in the jail period implementation, once a validator unjails, all of
their delegators who are delegated to them (haven't unbonded / redelegated away),
stay with them. Given that consensus safety faults are so egregious
stay with them. Given that consensus safety faults are so egregious
(way more so than liveness faults), it is probably prudent to have delegators not
"auto-rebond" to the validator. Thus, we propose setting the "jail time" for a
validator who commits a consensus safety fault, to `infinite` (i.e. a tombstone state).
@ -93,7 +93,7 @@ This essentially kicks the validator out of the validator set and does not allow
them to re-enter the validator set. All of their delegators (including the operator themselves)
have to either unbond or redelegate away. The validator operator can create a new
validator if they would like, with a new operator key and consensus key, but they
have to "re-earn" their delegations back. To put the validator in the tombstone
have to "re-earn" their delegations back. To put the validator in the tombstone
state, we set `DoubleSignJailEndTime` to `time.Unix(253402300800)`, the maximum
time supported by Amino.
@ -106,7 +106,7 @@ of the hooks defined in the `slashing` module consumed by the `staking` module
Another optimization that can be made is that if we assume that all ABCI faults
for Tendermint consensus are slashed at the same level, we don't have to keep
track of "max slash". Once an ABCI fault happens, we don't have to worry about
track of "max slash". Once an ABCI fault happens, we don't have to worry about
comparing potential future ones to find the max.
Currently the only Tendermint ABCI fault is:
@ -121,5 +121,5 @@ Given that these faults are both attributable byzantine faults, we will likely
want to slash them equally, and thus we can enact the above change.
> Note: This change may make sense for current Tendermint consensus, but maybe
not for a different consensus algorithm or future versions of Tendermint that
may want to punish at different levels (for example, partial slashing).
> not for a different consensus algorithm or future versions of Tendermint that
> may want to punish at different levels (for example, partial slashing).

View File

@ -5,7 +5,7 @@ parent:
title: "slashing"
-->
# `slashing`
# `x/slashing`
## Abstract
@ -25,21 +25,21 @@ This module will be used by the Cosmos Hub, the first hub in the Cosmos ecosyste
## Contents
1. **[Concepts](01_concepts.md)**
- [States](01_concepts.md#states)
- [Tombstone Caps](01_concepts.md#tombstone-caps)
- [ASCII timelines](01_concepts.md#ascii-timelines)
- [States](01_concepts.md#states)
- [Tombstone Caps](01_concepts.md#tombstone-caps)
- [ASCII timelines](01_concepts.md#ascii-timelines)
2. **[State](02_state.md)**
- [Signing Info](02_state.md#signing-info)
- [Signing Info](02_state.md#signing-info)
3. **[Messages](03_messages.md)**
- [Unjail](03_messages.md#unjail)
- [Unjail](03_messages.md#unjail)
4. **[Begin-Block](04_begin_block.md)**
- [Evidence handling](04_begin_block.md#evidence-handling)
- [Uptime tracking](04_begin_block.md#uptime-tracking)
- [Evidence handling](04_begin_block.md#evidence-handling)
- [Uptime tracking](04_begin_block.md#uptime-tracking)
5. **[05_hooks.md](05_hooks.md)**
- [Hooks](05_hooks.md#hooks)
- [Hooks](05_hooks.md#hooks)
6. **[Events](06_events.md)**
- [BeginBlocker](06_events.md#beginblocker)
- [Handlers](06_events.md#handlers)
- [BeginBlocker](06_events.md#beginblocker)
- [Handlers](06_events.md#handlers)
7. **[Staking Tombstone](07_tombstone.md)**
- [Abstract](07_tombstone.md#abstract)
- [Abstract](07_tombstone.md#abstract)
8. **[Parameters](08_params.md)**