Update SPEC-SPEC & documentation for x/{bank,evidence} (#7404)
* Update documentation for x/bank * Update ModuleAccounts * Put contets on top * Update comments * Do x/evidence * Add ValidatorEvidence * Update SPEC-SPEC * Update anchor
This commit is contained in:
parent
be59020f29
commit
e17dd4fd50
|
@ -19,28 +19,33 @@ element as a part of a larger description.
|
|||
|
||||
## Common Layout
|
||||
|
||||
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 specifications should be contained in a single `README.md` file inside the
|
||||
`spec/` folder of a given module.
|
||||
|
||||
- `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
|
||||
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
|
||||
|
||||
### Notation for key-value mapping
|
||||
|
||||
Within `state.md` the following notation `->` should be used to describe key to
|
||||
Within the `State` section, the following notation `->` should be used to describe key to
|
||||
value mapping:
|
||||
|
||||
```
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
<!--
|
||||
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)`
|
|
@ -1,135 +0,0 @@
|
|||
<!--
|
||||
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
|
||||
|
||||
```go
|
||||
type Input struct {
|
||||
Address AccAddress
|
||||
Coins Coins
|
||||
}
|
||||
```
|
||||
|
||||
### Output
|
||||
|
||||
An output of a multiparty transfer.
|
||||
|
||||
```go
|
||||
type Output struct {
|
||||
Address AccAddress
|
||||
Coins Coins
|
||||
}
|
||||
```
|
||||
|
||||
## BaseKeeper
|
||||
|
||||
The base keeper provides full-permission access: the ability to arbitrary modify any account's balance and mint or burn coins.
|
||||
|
||||
```go
|
||||
type BaseKeeper interface {
|
||||
SetCoins(addr AccAddress, amt Coins)
|
||||
SubtractCoins(addr AccAddress, amt Coins)
|
||||
AddCoins(addr AccAddress, amt Coins)
|
||||
InputOutputCoins(inputs []Input, outputs []Output)
|
||||
}
|
||||
```
|
||||
|
||||
`setCoins` fetches an account by address, sets the coins on the account, and saves the account.
|
||||
|
||||
```
|
||||
setCoins(addr AccAddress, amt Coins)
|
||||
account = accountKeeper.getAccount(addr)
|
||||
if account == nil
|
||||
fail with "no account found"
|
||||
account.Coins = amt
|
||||
accountKeeper.setAccount(account)
|
||||
```
|
||||
|
||||
`subtractCoins` fetches the coins of an account, subtracts the provided amount, and saves the account. This decreases the total supply.
|
||||
|
||||
```
|
||||
subtractCoins(addr AccAddress, amt Coins)
|
||||
oldCoins = getCoins(addr)
|
||||
newCoins = oldCoins - amt
|
||||
if newCoins < 0
|
||||
fail with "cannot end up with negative coins"
|
||||
setCoins(addr, newCoins)
|
||||
```
|
||||
|
||||
`addCoins` fetches the coins of an account, adds the provided amount, and saves the account. This increases the total supply.
|
||||
|
||||
```
|
||||
addCoins(addr AccAddress, amt Coins)
|
||||
oldCoins = getCoins(addr)
|
||||
newCoins = oldCoins + amt
|
||||
setCoins(addr, newCoins)
|
||||
```
|
||||
|
||||
`inputOutputCoins` transfers coins from any number of input accounts to any number of output accounts.
|
||||
|
||||
```
|
||||
inputOutputCoins(inputs []Input, outputs []Output)
|
||||
for input in inputs
|
||||
subtractCoins(input.Address, input.Coins)
|
||||
for output in outputs
|
||||
addCoins(output.Address, output.Coins)
|
||||
```
|
||||
|
||||
## 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
|
||||
type SendKeeper interface {
|
||||
SendCoins(from AccAddress, to AccAddress, amt Coins)
|
||||
}
|
||||
```
|
||||
|
||||
`sendCoins` transfers coins from one account to another.
|
||||
|
||||
```
|
||||
sendCoins(from AccAddress, to AccAddress, amt Coins)
|
||||
subtractCoins(from, amt)
|
||||
addCoins(to, amt)
|
||||
```
|
||||
|
||||
## ViewKeeper
|
||||
|
||||
The view keeper provides read-only access to account balances but no balance alteration functionality. All balance lookups are `O(1)`.
|
||||
|
||||
```go
|
||||
type ViewKeeper interface {
|
||||
GetCoins(addr AccAddress) Coins
|
||||
HasCoins(addr AccAddress, amt Coins) bool
|
||||
}
|
||||
```
|
||||
|
||||
`getCoins` returns the coins associated with an account.
|
||||
|
||||
```
|
||||
getCoins(addr AccAddress)
|
||||
account = accountKeeper.getAccount(addr)
|
||||
if account == nil
|
||||
return Coins{}
|
||||
return account.Coins
|
||||
```
|
||||
|
||||
`hasCoins` returns whether or not an account has at least the provided amount of coins.
|
||||
|
||||
```
|
||||
hasCoins(addr AccAddress, amt Coins)
|
||||
account = accountKeeper.getAccount(addr)
|
||||
coins = getCoins(addr)
|
||||
return coins >= amt
|
||||
```
|
|
@ -1,30 +0,0 @@
|
|||
<!--
|
||||
order: 3
|
||||
-->
|
||||
|
||||
# Messages
|
||||
|
||||
## MsgSend
|
||||
|
||||
```go
|
||||
type MsgSend struct {
|
||||
Inputs []Input
|
||||
Outputs []Output
|
||||
}
|
||||
```
|
||||
|
||||
`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)
|
||||
```
|
|
@ -1,29 +0,0 @@
|
|||
<!--
|
||||
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} |
|
|
@ -1,25 +0,0 @@
|
|||
<!--
|
||||
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.
|
|
@ -1,13 +1,26 @@
|
|||
<!--
|
||||
order: 0
|
||||
title: Bank Overview
|
||||
parent:
|
||||
title: "bank"
|
||||
-->
|
||||
|
||||
# `x/bank`
|
||||
|
||||
## Abstract
|
||||
## 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
|
||||
|
||||
This document specifies the bank module of the Cosmos SDK.
|
||||
|
||||
|
@ -22,7 +35,9 @@ supply of all assets used in the application.
|
|||
|
||||
This module will be used in the Cosmos Hub.
|
||||
|
||||
## Supply
|
||||
## 02. Concepts
|
||||
|
||||
### Supply
|
||||
|
||||
The `supply` module:
|
||||
|
||||
|
@ -30,55 +45,57 @@ The `supply` module:
|
|||
- 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.Account` which can be used by
|
||||
modules to allocate tokens and in special cases mint or burn tokens. At a base
|
||||
The supply module introduces a new type of `auth.AccountI` interface, called `ModuleAccountI`, 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.Account`s and other module accounts. This design replaces previous
|
||||
`auth.AccountI` interfaces 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 `ModuleAccount` interface is defined as follows:
|
||||
The `ModuleAccountI` interface is defined as follows:
|
||||
|
||||
```go
|
||||
type ModuleAccount interface {
|
||||
auth.Account // same methods as the Account interface
|
||||
// ModuleAccountI defines an account interface for modules that hold tokens in
|
||||
// an escrow.
|
||||
type ModuleAccountI interface {
|
||||
AccountI // 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
|
||||
}
|
||||
```
|
||||
|
||||
> **WARNING!**
|
||||
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).
|
||||
> 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 `ModuleAccount`s in order to be able
|
||||
and the bank `Keeper` that are related to `ModuleAccountI`s in order to be able
|
||||
to:
|
||||
|
||||
- Get and set `ModuleAccount`s by providing the `Name`.
|
||||
- Send coins from and to other `ModuleAccount`s or standard `Account`s
|
||||
- Get and set `ModuleAccountI`s by providing the `Name`.
|
||||
- Send coins from and to other `ModuleAccountI`s or standard `Account`s
|
||||
(`BaseAccount` or `VestingAccount`) by passing only the `Name`.
|
||||
- `Mint` or `Burn` coins for a `ModuleAccount` (restricted to its permissions).
|
||||
- `Mint` or `Burn` coins for a `ModuleAccountI` (restricted to its permissions).
|
||||
|
||||
### Permissions
|
||||
#### Permissions
|
||||
|
||||
Each `ModuleAccount` has a different set of permissions that provide different
|
||||
Each `ModuleAccountI` 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
|
||||
`ModuleAccount` calls the allowed functions, the `Keeper` can lookup the
|
||||
`ModuleAccountI` calls the allowed functions, the `Keeper` can lookup the
|
||||
permissions to that specific account and perform or not the action.
|
||||
|
||||
The available permissions are:
|
||||
|
@ -87,16 +104,217 @@ 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.
|
||||
|
||||
## Contents
|
||||
## 03. State
|
||||
|
||||
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)**
|
||||
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,62 +0,0 @@
|
|||
<!--
|
||||
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.
|
||||
|
||||
```go
|
||||
type Evidence interface {
|
||||
Route() string
|
||||
Type() string
|
||||
String() string
|
||||
Hash() HexBytes
|
||||
ValidateBasic() error
|
||||
|
||||
// The consensus address of the malicious validator at time of infraction
|
||||
GetConsensusAddress() ConsAddress
|
||||
|
||||
// Height at which the infraction occurred
|
||||
GetHeight() int64
|
||||
|
||||
// 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
|
||||
type Handler func(Context, Evidence) error
|
||||
```
|
|
@ -1,16 +0,0 @@
|
|||
<!--
|
||||
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`.
|
||||
|
||||
```go
|
||||
type GenesisState struct {
|
||||
Evidence []Evidence `json:"evidence" yaml:"evidence"`
|
||||
}
|
||||
```
|
||||
|
||||
All `Evidence` is retrieved and stored via a prefix `KVStore` using prefix `0x00` (`KeyPrefixEvidence`).
|
|
@ -1,46 +0,0 @@
|
|||
<!--
|
||||
order: 3
|
||||
-->
|
||||
|
||||
# Messages
|
||||
|
||||
## MsgSubmitEvidence
|
||||
|
||||
Evidence is submitted through a `MsgSubmitEvidence` message:
|
||||
|
||||
```go
|
||||
type MsgSubmitEvidence struct {
|
||||
Evidence Evidence
|
||||
Submitter AccAddress
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
|
@ -1,18 +0,0 @@
|
|||
<!--
|
||||
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 |
|
|
@ -1,11 +0,0 @@
|
|||
<!--
|
||||
order: 5
|
||||
-->
|
||||
|
||||
# Parameters
|
||||
|
||||
The evidence module contains the following parameters:
|
||||
|
||||
| Key | Type | Example |
|
||||
| -------------- | ---------------- | -------------- |
|
||||
| MaxEvidenceAge | string (time ns) | "120000000000" |
|
|
@ -1,100 +0,0 @@
|
|||
<!--
|
||||
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 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)
|
|
@ -1,23 +1,18 @@
|
|||
<!--
|
||||
order: 0
|
||||
title: Evidence Overview
|
||||
parent:
|
||||
title: "evidence"
|
||||
-->
|
||||
|
||||
# `evidence`
|
||||
# `x/evidence`
|
||||
|
||||
## Table of Contents
|
||||
|
||||
<!-- TOC -->
|
||||
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)**
|
||||
|
||||
## Abstract
|
||||
- **[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)**
|
||||
|
||||
## 01. 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
|
||||
|
@ -37,3 +32,253 @@ 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)
|
||||
|
|
Loading…
Reference in New Issue