Skip to content

Commit

Permalink
[IBC] Add ICS-02 Client Interfaces (#932)
Browse files Browse the repository at this point in the history
  • Loading branch information
h5law authored Jul 26, 2023
1 parent fb86e26 commit cbc7921
Show file tree
Hide file tree
Showing 7 changed files with 462 additions and 177 deletions.
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,8 @@ protogen_local: go_protoc-go-inject-tag ## Generate go structures for all of the
make download_ics23_proto; \
fi
$(PROTOC_SHARED) -I=./ibc/types/proto --go_out=./ibc/types ./ibc/types/proto/*.proto
$(PROTOC_SHARED) -I=./ibc/client/types/proto --go_out=./ibc/client/types ./ibc/client/types/proto/*.proto
$(PROTOC_SHARED) -I=./ibc/client/types/proto -I=./ibc/client/light_clients/types/proto -I=./shared/core/types/proto -I=./ibc/types/proto --go_out=./ibc/client/light_clients/types ./ibc/client/light_clients/types/proto/*.proto

# echo "View generated proto files by running: make protogen_show"

Expand Down
59 changes: 59 additions & 0 deletions ibc/client/light_clients/types/proto/pocket.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
syntax = "proto3";

package core;

option go_package = "github.com/pokt-network/pocket/ibc/client/light_client/types";

import "google/protobuf/timestamp.proto";
import "google/protobuf/duration.proto";
import "proofs.proto";
import "wasm.proto";
import "block.proto";

// PocketConsensusState defines the ibc client consensus state for Pocket
message PocketConsensusState {
google.protobuf.Timestamp timestamp = 1; // unix nano timestamp of the block
string state_hash = 2; // hex encoded root state tree hash
map<string, string> state_tree_hashes = 3; // map of state tree hashes; map[TreeName]hex(TreeRootHash)
string next_val_set_hash = 4; // hex encoded sha3_256 hash of the next validator set
}

// PocketClientState defines the ibc client state for Pocket
message PocketClientState {
string network_id = 1; // network identifier string
Fraction trust_level = 2; // fraction of the validator set that is required to sign off on new blocks
google.protobuf.Duration trusting_period = 3; // the duration of the period since the LastestTimestamp where the state can be upgraded
google.protobuf.Duration unbonding_period = 4; // the duration of the staking unbonding period
google.protobuf.Duration max_clock_drift = 5; // the max duration a new header's time can be in the future
Height latest_height = 6; // the latest height the client was updated to
uint64 frozen_height = 7; // the height at which the client was frozen due to a misbehaviour
ProofSpec proof_spec = 8; // ics23 proof spec used in verifying proofs
// RESEARCH: Figure out exactly what this is for in tendermint, why it is needed and if we need it also
// repeated string upgrade_path = 9; // the upgrade path for the new client state
}

// Fraction defines a positive rational number
message Fraction {
uint64 numerator = 1;
uint64 denominator = 2;
}

// PocketHeader defines the ibc client header for the Pocket network
message PocketHeader {
BlockHeader block_header = 1; // pocket consensus block header
ValidatorSet validator_set = 2; // new validator set for the updating client
// the consensus state at trusted_height must be within the unbonding_period to correctly verify the new header
Height trusted_height = 3; // height of the ConsensusState stored used to verify the new header
// trusted_validators must hash to the ConsensusState.NextValSetHash as this is the last trusted validator set
// hashed using SHA3Hash(validatorSetBytes) in shared/crypto/sha3.go
ValidatorSet trusted_validators = 4; // already stored validator set used to verify the update
}

// PocketMisbehaviour defines the ibc client misbehaviour for the Pocket network
//
// The two conflicting headers are submitted as evidence to verify the Pocket
// network has misbehaved.
message PocketMisbehaviour {
PocketHeader header_1 = 1; // the first header
PocketHeader header_2 = 2; // the second header
}
35 changes: 35 additions & 0 deletions ibc/client/types/proto/wasm.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
syntax = "proto3";

package core;

option go_package = "github.com/pokt-network/pocket/ibc/client/types";

// ClientState for a Wasm light client
message ClientState {
bytes data = 1; // opaque data passed to the wasm client
bytes wasm_checksum = 2; // checksum of the wasm client code
Height recent_height = 3; // latest height of the client
}

// ConsensusState for a Wasm light client
message ConsensusState {
bytes data = 1; // opaque data passed to the wasm client
uint64 timestamp = 2; // unix nano timestamp of the block
}

// Header for a Wasm light client
message Header {
bytes data = 1; // opaque data passed to the wasm client
Height height = 2; // height of the header
}

// Misbehaviour for a Wasm light client
message Misbehaviour {
bytes data = 1; // opaque data passed to the wasm client
}

// Height represents the height of a client
message Height {
uint64 revision_number = 1;
uint64 revision_height = 2;
}
1 change: 1 addition & 0 deletions shared/modules/bus_module.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,5 @@ type Bus interface {
GetIBCHost() IBCHostSubmodule
GetBulkStoreCacher() BulkStoreCacher
GetEventLogger() EventLogger
GetClientManager() ClientManager
}
212 changes: 212 additions & 0 deletions shared/modules/ibc_client_module.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
package modules

//go:generate mockgen -destination=./mocks/ibc_client_module_mock.go github.com/pokt-network/pocket/shared/modules ClientManager

import (
"google.golang.org/protobuf/proto"
)

type ClientStatus string

const (
ClientManagerModuleName = "client_manager"

// Client Status types
ActiveStatus ClientStatus = "active"
ExpiredStatus ClientStatus = "expired"
FrozenStatus ClientStatus = "frozen"
UnauthorizedStatus ClientStatus = "unauthorized"
UnknownStatus ClientStatus = "unknown"
)

type ClientManagerOption func(ClientManager)

type clientManagerFactory = FactoryWithOptions[ClientManager, ClientManagerOption]

// ClientManager is the interface that defines the methods needed to interact with an
// IBC light client it manages the different lifecycle methods for the different clients
// https://github.com/cosmos/ibc/tree/main/spec/core/ics-002-client-semantics
type ClientManager interface {
Submodule
clientManagerFactory

// === Client Lifecycle Management ===

// CreateClient creates a new client with the given client state and initial consensus state
// and initialises its unique identifier in the IBC store
CreateClient(ClientState, ConsensusState) (string, error)

// UpdateClient updates an existing client with the given ClientMessage, given that
// the ClientMessage can be verified using the existing ClientState and ConsensusState
UpdateClient(identifier string, clientMessage ClientMessage) error

// UpgradeClient upgrades an existing client with the given identifier using the
// ClientState and ConsenusState provided. It can only do so if the new client
// was committed to by the old client at the specified upgrade height
UpgradeClient(
identifier string,
clientState ClientState, consensusState ConsensusState,
proofUpgradeClient, proofUpgradeConsState []byte,
) error

// === Client Queries ===

// GetConsensusState returns the ConsensusState at the given height for the given client
GetConsensusState(identifier string, height Height) (ConsensusState, error)

// GetClientState returns the ClientState for the given client
GetClientState(identifier string) (ClientState, error)

// GetHostConsensusState returns the ConsensusState at the given height for the host chain
GetHostConsensusState(height Height) (ConsensusState, error)

// GetHostClientState returns the ClientState at the provided height for the host chain
GetHostClientState(height Height) (ClientState, error)

// GetCurrentHeight returns the current IBC client height of the network
GetCurrentHeight() (Height, error)

// VerifyHostClientState verifies the client state for a client running on a
// counterparty chain is valid, checking against the current host client state
VerifyHostClientState(ClientState) error
}

// ClientState is an interface that defines the methods required by a clients
// implementation of their own client state object
//
// ClientState is an opaque data structure defined by a client type. It may keep
// arbitrary internal state to track verified roots and past misbehaviours.
type ClientState interface {
proto.Message

GetData() []byte
GetWasmChecksum() []byte
ClientType() string
GetLatestHeight() Height
Validate() error

// Status returns the status of the client. Only Active clients are allowed
// to process packets.
Status(clientStore ProvableStore) ClientStatus

// GetTimestampAtHeight must return the timestamp for the consensus state
// associated with the provided height.
GetTimestampAtHeight(clientStore ProvableStore, height Height) (uint64, error)

// Initialise is called upon client creation, it allows the client to perform
// validation on the initial consensus state and set the client state,
// consensus state and any client-specific metadata necessary for correct
// light client operation in the provided client store.
Initialise(clientStore ProvableStore, consensusState ConsensusState) error

// VerifyMembership is a generic proof verification method which verifies a
// proof of the existence of a value at a given CommitmentPath at the
// specified height. The path is expected to be the full CommitmentPath
VerifyMembership(
clientStore ProvableStore,
height Height,
delayTimePeriod, delayBlockPeriod uint64,
proof, path, value []byte,
) error

// VerifyNonMembership is a generic proof verification method which verifies
// the absence of a given CommitmentPath at a specified height. The path is
// expected to be the full CommitmentPath
VerifyNonMembership(
clientStore ProvableStore,
height Height,
delayTimePeriod, delayBlockPeriod uint64,
proof, path []byte,
) error

// VerifyClientMessage verifies a ClientMessage. A ClientMessage could be a
// Header, Misbehaviour, or batch update. It must handle each type of
// ClientMessage appropriately. Calls to CheckForMisbehaviour, UpdateState,
// and UpdateStateOnMisbehaviour will assume that the content of the
// ClientMessage has been verified and can be trusted. An error should be
// returned if the ClientMessage fails to verify.
VerifyClientMessage(clientStore ProvableStore, clientMsg ClientMessage) error

// Checks for evidence of a misbehaviour in Header or Misbehaviour type.
// It assumes the ClientMessage has already been verified.
CheckForMisbehaviour(clientStore ProvableStore, clientMsg ClientMessage) bool

// UpdateStateOnMisbehaviour should perform appropriate state changes on a
// client state given that misbehaviour has been detected and verified
UpdateStateOnMisbehaviour(clientStore ProvableStore, clientMsg ClientMessage) error

// UpdateState updates and stores as necessary any associated information
// for an IBC client, such as the ClientState and corresponding ConsensusState.
// Upon successful update, a consensus height is returned.
// It assumes the ClientMessage has already been verified.
UpdateState(clientStore ProvableStore, clientMsg ClientMessage) (Height, error)

// Upgrade functions
// NOTE: proof heights are not included as upgrade to a new revision is expected to pass only on the last
// height committed by the current revision. Clients are responsible for ensuring that the planned last
// height of the current revision is somehow encoded in the proof verification process.
// This is to ensure that no premature upgrades occur, since upgrade plans committed to by the counterparty
// may be cancelled or modified before the last planned height.
// If the upgrade is verified, the upgraded client and consensus states must be set in the client store.
VerifyUpgradeAndUpdateState(
clientStore ProvableStore,
newClient ClientState,
newConsState ConsensusState,
proofUpgradeClient,
proofUpgradeConsState []byte,
) error
}

// ConsensusState is an interface that defines the methods required by a clients
// implementation of their own consensus state object
//
// ConsensusState is an opaque data structure defined by a client type, used by the
// validity predicate to verify new commits & state roots. Likely the structure will
// contain the last commit produced by the consensus process, including signatures
// and validator set metadata.
type ConsensusState interface {
proto.Message

GetData() []byte
ClientType() string
GetTimestamp() uint64
ValidateBasic() error
}

// ClientMessage is an interface that defines the methods required by a clients
// implementation of their own client message object
//
// A ClientMessage is an opaque data structure defined by a client type which
// provides information to update the client. ClientMessages can be submitted
// to an associated client to add new ConsensusState(s) and/or update the
// ClientState. They likely contain a height, a proof, a commitment root, and
// possibly updates to the validity predicate.
type ClientMessage interface {
proto.Message

GetData() []byte
ClientType() string
ValidateBasic() error
}

// Height is an interface that defines the methods required by a clients
// implementation of their own height object
//
// Heights usually have two components: revision number and revision height.
type Height interface {
IsZero() bool
LT(Height) bool
LTE(Height) bool
EQ(Height) bool
GT(Height) bool
GTE(Height) bool
Increment() Height
Decrement() Height
GetRevisionNumber() uint64
GetRevisionHeight() uint64
ToString() string // must define a determinstic `String()` method not the generated protobuf method
}

func (s ClientStatus) String() string {
return string(s)
}
Loading

0 comments on commit cbc7921

Please sign in to comment.