Evidence
Abstract
x/evidence is an implementation of a Cosmos SDK module, per ADR 009,
that allows for the submission and handling of arbitrary evidence of misbehavior such
as equivocation and counterfactual signing.
The evidence module differs from standard evidence handling which typically expects the underlying consensus engine, e.g. CometBFT, to automatically submit evidence when it is discovered by allowing clients and foreign chains to submit more complex evidence directly.
All concrete evidence types must implement the Evidence interface contract. SubmittedEvidence is first routed through the evidence module's Router in which it attempts
to find a corresponding registered Handler for that specific Evidence type.
Each Evidence type must have a Handler registered with the evidence module's
keeper in order for it to be successfully routed and executed.
Each corresponding handler must also fulfill the Handler interface contract. TheHandler for a given Evidence type can perform any arbitrary state transitions
such as slashing, jailing, and tombstoning.
Concepts
Evidence
Any concrete type of evidence submitted to the x/evidence module must fulfill theEvidence 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.
// Evidence defines the contract which concrete evidence types of misbehavior
// must implement.
type Evidence interface {
proto.Message
Route() string
String() string
Hash() []byte
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 acceptsEvidence and attempts to find the corresponding Handler for the Evidence
via the Route method.
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. All Evidence handled
by the Handler should be persisted.
// 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.Context, Evidence) errorState
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.
// 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).
Messages
MsgSubmitEvidence
Evidence is submitted through a MsgSubmitEvidence message:
// 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 correspondingHandler 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:
func SubmitEvidence(ctx Context, evidence Evidence) error {
if _, err := GetEvidence(ctx, evidence.Hash()); err == nil {
return errorsmod.Wrap(types.ErrEvidenceExists, strings.ToUpper(hex.EncodeToString(evidence.Hash())))
}
if !router.HasRoute(evidence.Route()) {
return errorsmod.Wrap(types.ErrNoEvidenceHandlerExists, evidence.Route())
}
handler := router.GetRoute(evidence.Route())
if err := handler(ctx, evidence); err != nil {
return errorsmod.Wrap(types.ErrInvalidEvidence, err.Error())
}
ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeSubmitEvidence,
sdk.NewAttribute(types.AttributeKeyEvidenceHash, strings.ToUpper(hex.EncodeToString(evidence.Hash()))),
),
)
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, an event is emitted and it is persisted to state.
Events
The x/evidence module emits the following events:
Handlers
MsgSubmitEvidence
submit_evidence
evidence_hash
{evidenceHash}
message
module
evidence
message
sender
{senderAddress}
message
action
submit_evidence
Parameters
The evidence module does not contain any parameters.
BeginBlock
Evidence Handling
CometBFT blocks can includeEvidence that indicates if a validator committed malicious behavior. The relevant information is forwarded to the application as ABCI Evidence in abci.RequestBeginBlock so that the validator can be punished accordingly.
Equivocation
The Cosmos SDK handles two types of evidence inside the ABCI BeginBlock:
DuplicateVoteEvidence,LightClientAttackEvidence.
The evidence module handles these two evidence types the same way. First, the Cosmos SDK converts the CometBFT concrete evidence type to an SDK Evidence interface using Equivocation as the concrete type.
https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/evidence/v1beta1/evidence.proto#L12-L32For some Equivocation submitted in block to be valid, it must satisfy:
Evidence.Timestamp >= block.Timestamp - MaxEvidenceAge
Where:
Evidence.Timestampis the timestamp in the block at heightEvidence.Heightblock.Timestampis the current block timestamp.
If valid Equivocation evidence is included in a block, the validator's stake is
reduced (slashed) by SlashFractionDoubleSign as 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 that 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 to make it impossible for that validator to ever re-enter the validator set.
The Equivocation evidence is handled as follows:
https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/x/evidence/keeper/infraction.go#L26-L140Note: The slashing, jailing, and tombstoning calls are delegated through the x/slashing module
that emits informative events and finally delegates calls to the x/staking module. See documentation
on slashing and jailing in State Transitions.
Client
CLI
A user can query and interact with the evidence module using the CLI.
Query
The query commands allows users to query evidence state.
simd query evidence --helpevidence
The evidence command allows users to list all evidence or evidence by hash.
Usage:
simd query evidence evidence [flags]To query evidence by hash
Example:
simd query evidence evidence "DF0C23E8634E480F84B9D5674A7CDC9816466DEC28A3358F73260F68D28D7660"Example Output:
evidence:
consensus_address: cosmosvalcons1ntk8eualewuprz0gamh8hnvcem2nrcdsgz563h
height: 11
power: 100
time: "2021-10-20T16:08:38.194017624Z"To get all evidence
Example:
simd query evidence listExample Output:
evidence:
consensus_address: cosmosvalcons1ntk8eualewuprz0gamh8hnvcem2nrcdsgz563h
height: 11
power: 100
time: "2021-10-20T16:08:38.194017624Z"
pagination:
next_key: null
total: "1"REST
A user can query the evidence module using REST endpoints.
Evidence
Get evidence by hash
/cosmos/evidence/v1beta1/evidence/{hash}Example:
curl -X GET "http://localhost:1317/cosmos/evidence/v1beta1/evidence/DF0C23E8634E480F84B9D5674A7CDC9816466DEC28A3358F73260F68D28D7660"Example Output:
{
"evidence": {
"consensus_address": "cosmosvalcons1ntk8eualewuprz0gamh8hnvcem2nrcdsgz563h",
"height": "11",
"power": "100",
"time": "2021-10-20T16:08:38.194017624Z"
}
}All evidence
Get all evidence
/cosmos/evidence/v1beta1/evidenceExample:
curl -X GET "http://localhost:1317/cosmos/evidence/v1beta1/evidence"Example Output:
{
"evidence": [
{
"consensus_address": "cosmosvalcons1ntk8eualewuprz0gamh8hnvcem2nrcdsgz563h",
"height": "11",
"power": "100",
"time": "2021-10-20T16:08:38.194017624Z"
}
],
"pagination": {
"total": "1"
}
}gRPC
A user can query the evidence module using gRPC endpoints.
Evidence
Get evidence by hash
cosmos.evidence.v1beta1.Query/EvidenceExample:
grpcurl -plaintext -d '{"evidence_hash":"DF0C23E8634E480F84B9D5674A7CDC9816466DEC28A3358F73260F68D28D7660"}' localhost:9090 cosmos.evidence.v1beta1.Query/EvidenceExample Output:
{
"evidence": {
"consensus_address": "cosmosvalcons1ntk8eualewuprz0gamh8hnvcem2nrcdsgz563h",
"height": "11",
"power": "100",
"time": "2021-10-20T16:08:38.194017624Z"
}
}All evidence
Get all evidence
cosmos.evidence.v1beta1.Query/AllEvidenceExample:
grpcurl -plaintext localhost:9090 cosmos.evidence.v1beta1.Query/AllEvidenceExample Output:
{
"evidence": [
{
"consensus_address": "cosmosvalcons1ntk8eualewuprz0gamh8hnvcem2nrcdsgz563h",
"height": "11",
"power": "100",
"time": "2021-10-20T16:08:38.194017624Z"
}
],
"pagination": {
"total": "1"
}
}Last updated