证据(Evidence)

摘要

x/evidence 是根据 ADR 009 实现的 Cosmos SDK 模块,允许提交和处理各种不当行为的证据,例如双重签名和反事实签名。 与标准的证据处理不同,标准处理通常期望底层的共识引擎(例如 CometBFT)在发现时自动提交证据,而 evidence 模块允许客户端和外部链直接提交更复杂的证据。 所有具体的证据类型必须实现 Evidence 接口合同。提交的证据首先通过 evidence 模块的路由器,在路由器中尝试找到与该特定证据类型对应的注册处理器。每种证据类型必须在 evidence 模块的 keeper 中注册一个处理器,才能成功路由并执行。 每个对应的处理器还必须实现 Handler 接口合同。给定证据类型的处理器可以执行任何任意的状态转换,例如惩罚、监禁和销毁。

概念

Evidence

提交到 x/evidence 模块的任何具体证据类型必须满足以下 Evidence 合约。并非所有具体证据类型都会以相同方式满足该合约,有些数据对于某些类型的证据可能完全不相关。为了定义针对恶意验证人的证据合约,还创建了一个扩展 EvidenceValidatorEvidence

// 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
}

注册与处理

x/evidence 模块必须首先知道所有预期处理的证据类型。这是通过在 Evidence 合约中注册 Route 方法,并将其与称为 Router(定义如下)进行注册来实现的。Router 接受证据,并通过 Route 方法尝试找到与该证据对应的处理器。

type Router interface {
  AddRoute(r string, h Handler) Router
  HasRoute(r string) bool
  GetRoute(path string) Handler
  Seal()
  Sealed() bool
}

Handler(定义如下)负责执行处理证据的全部业务逻辑。通常,这包括验证证据,既通过 ValidateBasic 进行无状态检查,也通过提供给 Handler 的任何 keeper 进行有状态检查。此外,Handler 还可以执行诸如惩罚和监禁验证人的功能。所有由 Handler 处理的证据应被持久化。

// 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) error

状态

目前,x/evidence 模块仅将有效的提交证据存储在状态中。证据状态也存储并导出在 x/evidence 模块的 GenesisState 中。

// GenesisState defines the evidence module's genesis state.
message GenesisState {
  // evidence defines all the evidence at genesis.
  repeated google.protobuf.Any evidence = 1;
}

所有证据通过前缀 KVStore 使用前缀 0x00(KeyPrefixEvidence)进行检索和存储。

消息

MsgSubmitEvidence

证据通过 MsgSubmitEvidence 消息提交:

// 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;
}

注意,MsgSubmitEvidence 消息的证据必须在 x/evidence 模块的 Router 中注册相应的处理器,才能被正确处理和路由。 在证据与相应的处理器注册后,它的处理过程如下:

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
}

首先,不能已经存在相同类型的有效提交证据。其次,证据会被路由到处理器并执行。最后,如果处理证据没有错误,事件将被发出,并且证据将被持久化到状态中。

事件

x/evidence 模块会发出以下事件:

Handlers

MsgSubmitEvidence

类型
属性键
属性值

submit_evidence

evidence_hash

{evidenceHash}

message

module

evidence

message

sender

{senderAddress}

message

action

submit_evidence

参数

evidence 模块不包含任何参数。

BeginBlock

证据处理

CometBFT 区块可以包含证据,指示验证人是否犯下恶意行为。相关信息作为 ABCI Evidence 被转发到应用程序,在 abci.RequestBeginBlock 中,以便对验证人进行相应的惩罚。

双重签名 Cosmos SDK 在 ABCI BeginBlock 中处理两种类型的证据:

  • DuplicateVoteEvidence

  • LightClientAttackEvidence

evidence 模块以相同的方式处理这两种证据类型。首先,Cosmos SDK 将 CometBFT 具体证据类型转换为 SDK Evidence 接口,使用 Equivocation 作为具体类型。

https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/evidence/v1beta1/evidence.proto#L12-L32

要使某个区块中提交的双重签名证据(Equivocation)有效,它必须满足以下条件: Evidence.Timestamp >= block.Timestamp - MaxEvidenceAge

其中:

  • Evidence.Timestamp 是在区块高度 Evidence.Height 处的时间戳

  • block.Timestamp 是当前区块的时间戳

如果有效的双重签名证据包含在区块中,验证人的质押将根据 x/slashing 模块中定义的 SlashFractionDoubleSign 按照违规发生时的质押进行削减,而不是证据被发现时的质押。我们希望“跟随质押”,即贡献于违规的质押应该被削减,即使它此后已经被重新委托或开始解绑。

此外,验证人将被永久监禁并销毁,使得该验证人无法再进入验证人集合。

Equivocation 证据的处理方式如下:

https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/x/evidence/keeper/infraction.go#L26-L140

Note: 惩罚、监禁和销毁的调用是通过 x/slashing 模块委托的,该模块会发出信息性事件,并最终将调用委托给 x/staking 模块。有关惩罚和监禁的文档,请参见状态转换中的相关部分。

客户端

CLI

用户可以使用 CLI 查询和与 evidence 模块进行交互。

Query

query 命令允许用户查询证据状态。

simd query evidence --help

evidence

evidence 命令允许用户列出所有证据或按哈希查询证据。

Usage:

simd query evidence evidence [flags]

按哈希查询证据

示例:

simd query evidence evidence "DF0C23E8634E480F84B9D5674A7CDC9816466DEC28A3358F73260F68D28D7660"

示例输出:

evidence:
  consensus_address: cosmosvalcons1ntk8eualewuprz0gamh8hnvcem2nrcdsgz563h
  height: 11
  power: 100
  time: "2021-10-20T16:08:38.194017624Z"

获取全部证据

示例:

simd query evidence list

示例输出:

evidence:
  consensus_address: cosmosvalcons1ntk8eualewuprz0gamh8hnvcem2nrcdsgz563h
  height: 11
  power: 100
  time: "2021-10-20T16:08:38.194017624Z"
pagination:
  next_key: null
  total: "1"

REST

用户可以使用REST端点查询evidence模块。

Evidence

通过哈希获取证据

/cosmos/evidence/v1beta1/evidence/{hash}

示例:

curl -X GET "http://localhost:1317/cosmos/evidence/v1beta1/evidence/DF0C23E8634E480F84B9D5674A7CDC9816466DEC28A3358F73260F68D28D7660"

示例输出:

{
  "evidence": {
    "consensus_address": "cosmosvalcons1ntk8eualewuprz0gamh8hnvcem2nrcdsgz563h",
    "height": "11",
    "power": "100",
    "time": "2021-10-20T16:08:38.194017624Z"
  }
}

All evidence

获取全部证据

/cosmos/evidence/v1beta1/evidence

示例:

curl -X GET "http://localhost:1317/cosmos/evidence/v1beta1/evidence"

示例输出:

{
  "evidence": [
    {
      "consensus_address": "cosmosvalcons1ntk8eualewuprz0gamh8hnvcem2nrcdsgz563h",
      "height": "11",
      "power": "100",
      "time": "2021-10-20T16:08:38.194017624Z"
    }
  ],
  "pagination": {
    "total": "1"
  }
}

gRPC

用户可以使用gRPC端点查询evidence模块。

Evidence

通过hash获取证据

cosmos.evidence.v1beta1.Query/Evidence

示例:

grpcurl -plaintext -d '{"evidence_hash":"DF0C23E8634E480F84B9D5674A7CDC9816466DEC28A3358F73260F68D28D7660"}' localhost:9090 cosmos.evidence.v1beta1.Query/Evidence

示例输出:

{
  "evidence": {
    "consensus_address": "cosmosvalcons1ntk8eualewuprz0gamh8hnvcem2nrcdsgz563h",
    "height": "11",
    "power": "100",
    "time": "2021-10-20T16:08:38.194017624Z"
  }
}

All evidence

获取全部证据

cosmos.evidence.v1beta1.Query/AllEvidence

示例:

grpcurl -plaintext localhost:9090 cosmos.evidence.v1beta1.Query/AllEvidence

示例输出:

{
  "evidence": [
    {
      "consensus_address": "cosmosvalcons1ntk8eualewuprz0gamh8hnvcem2nrcdsgz563h",
      "height": "11",
      "power": "100",
      "time": "2021-10-20T16:08:38.194017624Z"
    }
  ],
  "pagination": {
    "total": "1"
  }
}

Last updated