Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,8 @@ protogen_local: go_protoc-go-inject-tag ## Generate go structures for all of the
# IBC
make copy_ics23_proto
$(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
74 changes: 74 additions & 0 deletions ibc/client/events.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package client

import (
client_types "github.com/pokt-network/pocket/ibc/client/types"
"github.com/pokt-network/pocket/shared/codec"
core_types "github.com/pokt-network/pocket/shared/core/types"
"github.com/pokt-network/pocket/shared/modules"
)

// emitCreateClientEvent emits a create client event
func (c *clientManager) emitCreateClientEvent(clientId string, clientState modules.ClientState) error {
return c.GetBus().GetEventLogger().EmitEvent(
&core_types.IBCEvent{
Topic: client_types.EventTopicCreateClient,
Attributes: []*core_types.Attribute{
core_types.NewAttribute(client_types.AttributeKeyClientID, []byte(clientId)),
core_types.NewAttribute(client_types.AttributeKeyClientType, []byte(clientState.ClientType())),
core_types.NewAttribute(client_types.AttributeKeyConsensusHeight, []byte(clientState.GetLatestHeight().ToString())),
},
},
)
}

// emitUpdateClientEvent emits an update client event
func (c *clientManager) emitUpdateClientEvent(
clientId, clientType string,
consensusHeight modules.Height,
clientMessage modules.ClientMessage,
) error {
// Marshall the client message
clientMsgBz, err := codec.GetCodec().Marshal(clientMessage)
if err != nil {
return err
}

return c.GetBus().GetEventLogger().EmitEvent(
&core_types.IBCEvent{
Topic: client_types.EventTopicUpdateClient,
Attributes: []*core_types.Attribute{
core_types.NewAttribute(client_types.AttributeKeyClientID, []byte(clientId)),
core_types.NewAttribute(client_types.AttributeKeyClientType, []byte(clientType)),
core_types.NewAttribute(client_types.AttributeKeyConsensusHeight, []byte(consensusHeight.ToString())),
core_types.NewAttribute(client_types.AttributeKeyHeader, clientMsgBz),
},
},
)
}

// emitUpgradeClientEvent emits an upgrade client event
func (c *clientManager) emitUpgradeClientEvent(clientId string, clientState modules.ClientState) error {
return c.GetBus().GetEventLogger().EmitEvent(
&core_types.IBCEvent{
Topic: client_types.EventTopicUpdateClient,
Attributes: []*core_types.Attribute{
core_types.NewAttribute(client_types.AttributeKeyClientID, []byte(clientId)),
core_types.NewAttribute(client_types.AttributeKeyClientType, []byte(clientState.ClientType())),
core_types.NewAttribute(client_types.AttributeKeyConsensusHeight, []byte(clientState.GetLatestHeight().ToString())),
},
},
)
}

// emitSubmitMisbehaviourEvent emits a submit misbehaviour event
func (c *clientManager) emitSubmitMisbehaviourEvent(clientId string, clientState modules.ClientState) error {
return c.GetBus().GetEventLogger().EmitEvent(
&core_types.IBCEvent{
Topic: client_types.EventTopicSubmitMisbehaviour,
Attributes: []*core_types.Attribute{
core_types.NewAttribute(client_types.AttributeKeyClientID, []byte(clientId)),
core_types.NewAttribute(client_types.AttributeKeyClientType, []byte(clientState.ClientType())),
},
},
)
}
152 changes: 152 additions & 0 deletions ibc/client/introspect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package client

import (
"errors"
"time"

light_client_types "github.com/pokt-network/pocket/ibc/client/light_clients/types"
"github.com/pokt-network/pocket/ibc/client/types"
ibc_types "github.com/pokt-network/pocket/ibc/types"
"github.com/pokt-network/pocket/shared/codec"
"github.com/pokt-network/pocket/shared/modules"
util_types "github.com/pokt-network/pocket/utility/types"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/durationpb"
)

// GetHostConsensusState returns the ConsensusState at the given height for the
// host chain, the Pocket network. It then serialises this and packs it into a
// ConsensusState object for use in a WASM client
func (c *clientManager) GetHostConsensusState(height modules.Height) (modules.ConsensusState, error) {
blockStore := c.GetBus().GetPersistenceModule().GetBlockStore()
block, err := blockStore.GetBlock(height.GetRevisionHeight())
if err != nil {
return nil, err
}
pocketConsState := &light_client_types.PocketConsensusState{
Timestamp: block.BlockHeader.Timestamp,
StateHash: block.BlockHeader.StateHash,
StateTreeHashes: block.BlockHeader.StateTreeHashes,
NextValSetHash: block.BlockHeader.NextValSetHash,
}
consBz, err := codec.GetCodec().Marshal(pocketConsState)
if err != nil {
return nil, err
}
return types.NewConsensusState(consBz, uint64(pocketConsState.Timestamp.AsTime().UnixNano())), nil
}

// GetHostClientState returns the ClientState at the given height for the host
// chain, the Pocket network.
//
// This function is used to validate the state of a client running on a
// counterparty chain.
func (c *clientManager) GetHostClientState(height modules.Height) (modules.ClientState, error) {
blockStore := c.GetBus().GetPersistenceModule().GetBlockStore()
block, err := blockStore.GetBlock(height.GetRevisionHeight())
if err != nil {
return nil, err
}
rCtx, err := c.GetBus().GetPersistenceModule().NewReadContext(int64(height.GetRevisionHeight()))
if err != nil {
return nil, err
}
defer rCtx.Release()
unbondingBlocks, err := rCtx.GetIntParam(util_types.ValidatorUnstakingBlocksParamName, int64(height.GetRevisionHeight()))
if err != nil {
return nil, err
}
// TODO_AFTER(#705): use the actual MinimumBlockTime once set
blockTime := time.Minute * 15
unbondingPeriod := blockTime * time.Duration(unbondingBlocks) // approx minutes per block * blocks
pocketClient := &light_client_types.PocketClientState{
NetworkId: block.BlockHeader.NetworkId,
TrustLevel: &light_client_types.Fraction{Numerator: 2, Denominator: 3},
TrustingPeriod: durationpb.New(unbondingPeriod),
UnbondingPeriod: durationpb.New(unbondingPeriod),
MaxClockDrift: durationpb.New(blockTime), // DISCUSS: What is a reasonable MaxClockDrift?
LatestHeight: &types.Height{
RevisionNumber: height.GetRevisionNumber(),
RevisionHeight: height.GetRevisionHeight(),
},
ProofSpec: ibc_types.SmtSpec,
}
clientBz, err := codec.GetCodec().Marshal(pocketClient)
if err != nil {
return nil, err
}
return &types.ClientState{
Data: clientBz,
RecentHeight: pocketClient.LatestHeight,
}, nil
}

// VerifyHostClientState verifies that a ClientState for a light client running
// on a counterparty chain is valid, by checking it against the result of
// GetHostClientState(counterpartyClientState.GetLatestHeight())
func (c *clientManager) VerifyHostClientState(counterparty modules.ClientState) error {
height, err := c.GetCurrentHeight()
if err != nil {
return err
}
hostState, err := c.GetHostClientState(height)
if err != nil {
return err
}
poktHost := new(light_client_types.PocketClientState)
err = codec.GetCodec().Unmarshal(hostState.GetData(), poktHost)
if err != nil {
return err
}
poktCounter := new(light_client_types.PocketClientState)
err = codec.GetCodec().Unmarshal(counterparty.GetData(), poktCounter)
if err != nil {
return errors.New("counterparty client state is not a PocketClientState")
}

if poktCounter.FrozenHeight > 0 {
return errors.New("counterparty client state is frozen")
}
if poktCounter.NetworkId != poktHost.NetworkId {
return errors.New("counterparty client state has different network id")
}
if poktCounter.LatestHeight.RevisionNumber != poktHost.LatestHeight.RevisionNumber {
return errors.New("counterparty client state has different revision number")
}
if poktCounter.GetLatestHeight().GTE(poktHost.GetLatestHeight()) {
return errors.New("counterparty client state has a height greater than or equal to the host client state")
}
if poktCounter.TrustLevel.LT(&light_client_types.Fraction{Numerator: 2, Denominator: 3}) ||
poktCounter.TrustLevel.GT(&light_client_types.Fraction{Numerator: 1, Denominator: 1}) {
return errors.New("counterparty client state trust level is not in the accepted range")
}
if !proto.Equal(poktCounter.ProofSpec, poktHost.ProofSpec) {
return errors.New("counterparty client state has different proof spec")
}
if poktCounter.UnbondingPeriod != poktHost.UnbondingPeriod {
return errors.New("counterparty client state has different unbonding period")
}
if poktCounter.UnbondingPeriod.AsDuration().Nanoseconds() < poktHost.TrustingPeriod.AsDuration().Nanoseconds() {
return errors.New("counterparty client state unbonding period is less than trusting period")
}

// RESEARCH: Look into upgrade paths, their use and if they should just be equal

return nil
}

// GetCurrentHeight returns the current IBC client height of the network
// TODO_AFTER(#882): Use actual revision number
func (h *clientManager) GetCurrentHeight() (modules.Height, error) {
currHeight := h.GetBus().GetConsensusModule().CurrentHeight()
rCtx, err := h.GetBus().GetPersistenceModule().NewReadContext(int64(currHeight))
if err != nil {
return nil, err
}
defer rCtx.Release()
revNum := rCtx.GetRevisionNumber(int64(currHeight))
return &types.Height{
RevisionNumber: revNum,
RevisionHeight: currHeight,
}, nil
}
42 changes: 42 additions & 0 deletions ibc/client/light_clients/types/fraction.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package types

type ord int

const (
lt ord = iota
eq
gt
)

func (f *Fraction) LT(other *Fraction) bool {
return f.compare(other) == lt
}

func (f *Fraction) GT(other *Fraction) bool {
return f.compare(other) == gt
}

func (f *Fraction) EQ(other *Fraction) bool {
return f.compare(other) == eq
}

func (f *Fraction) LTE(other *Fraction) bool {
return f.compare(other) != gt
}

func (f *Fraction) GTE(other *Fraction) bool {
return f.compare(other) != lt
}

func (f *Fraction) compare(other *Fraction) ord {
comDenom := f.Denominator * other.Denominator
aNum := f.Numerator * (comDenom / f.Denominator)
bNum := other.Numerator * (comDenom / other.Denominator)
if aNum < bNum {
return lt
}
if aNum > bNum {
return gt
}
return eq
}
58 changes: 58 additions & 0 deletions ibc/client/light_clients/types/proto/pocket.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
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; // unixnano 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
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
}
34 changes: 34 additions & 0 deletions ibc/client/queries.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package client

import (
"github.com/pokt-network/pocket/ibc/client/types"
"github.com/pokt-network/pocket/ibc/path"
core_types "github.com/pokt-network/pocket/shared/core/types"
"github.com/pokt-network/pocket/shared/modules"
)

// GetConsensusState returns the ConsensusState at the given height for the
// stored client with the given identifier
func (c *clientManager) GetConsensusState(
identifier string, height modules.Height,
) (modules.ConsensusState, error) {
// Retrieve the clientId prefixed client store
prefixed := path.ApplyPrefix(core_types.CommitmentPrefix(path.KeyClientStorePrefix), identifier)
clientStore, err := c.GetBus().GetIBCHost().GetProvableStore(string(prefixed))
if err != nil {
return nil, err
}

return types.GetConsensusState(clientStore, height)
}

// GetClientState returns the ClientState for the stored client with the given identifier
func (c *clientManager) GetClientState(identifier string) (modules.ClientState, error) {
// Retrieve the client store
clientStore, err := c.GetBus().GetIBCHost().GetProvableStore(path.KeyClientStorePrefix)
if err != nil {
return nil, err
}

return types.GetClientState(clientStore, identifier)
}
Loading