Skip to content

Commit

Permalink
Add VerifyHostClientState method
Browse files Browse the repository at this point in the history
  • Loading branch information
h5law committed Jul 20, 2023
1 parent 077a48f commit 80c0319
Show file tree
Hide file tree
Showing 7 changed files with 483 additions and 66 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ protogen_local: go_protoc-go-inject-tag ## Generate go structures for all of the
# IBC
$(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 --go_out=./ibc/client/light_clients/types ./ibc/client/light_clients/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
140 changes: 140 additions & 0 deletions ibc/client/introspect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
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/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 {
hostState, err := c.GetHostClientState(c.GetCurrentHeight())
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 !poktCounter.ProofSpec.ConvertToIcs23ProofSpec().SpecEquals(poktHost.ProofSpec.ConvertToIcs23ProofSpec()) {
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(#705): Use actual revision number
func (h *clientManager) GetCurrentHeight() modules.Height {
return &types.Height{
RevisionNumber: 1,
RevisionHeight: h.GetBus().GetConsensusModule().CurrentHeight(),
}
}
3 changes: 2 additions & 1 deletion ibc/client/light_clients/types/proto/pocket.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ option go_package = "github.com/pokt-network/pocket/ibc/client/light_client/type

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

Expand All @@ -26,7 +27,7 @@ message PocketClientState {
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
bytes proof_specs = 8; // ics23 proof spec used in verifying proofs
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
}
Expand Down
64 changes: 0 additions & 64 deletions ibc/client/queries.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
package client

import (
"time"

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

// GetConsensusState returns the ConsensusState at the given height for the
Expand Down Expand Up @@ -38,61 +32,3 @@ func (c *clientManager) GetClientState(identifier string) (modules.ClientState,

return types.GetClientState(clientStore, identifier)
}

// 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) (*light_client_types.PocketClientState, 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
unbondingPeriod := time.Minute * 15 * time.Duration(unbondingBlocks) // approx minutes per block * blocks
maxDrift := time.Minute * 15 // maximum 15 minutes future
return &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(maxDrift),
LatestHeight: &types.Height{
RevisionNumber: height.GetRevisionNumber(),
RevisionHeight: height.GetRevisionHeight(),
},
}, nil
}
107 changes: 107 additions & 0 deletions ibc/types/proofs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package types

import ics23 "github.com/cosmos/ics23/go"

var SmtSpec = &ProofSpec{
LeafSpec: &LeafOp{
Hash: HashOp_SHA256,
PrehashKey: HashOp_SHA256,
PrehashValue: HashOp_SHA256,
Length: LengthOp_NO_PREFIX,
Prefix: []byte{0},
},
InnerSpec: &InnerSpec{
ChildOrder: []int32{0, 1},
ChildSize: 32,
MinPrefixLength: 1,
MaxPrefixLength: 1,
EmptyChild: make([]byte, 32),
Hash: HashOp_SHA256,
},
MaxDepth: 256,
PrehashKeyBeforeComparison: true,
}

func (p *ProofSpec) ConvertToIcs23ProofSpec() *ics23.ProofSpec {
ics := new(ics23.ProofSpec)
ics.LeafSpec = p.LeafSpec.ConvertToIcs23LeafOp()
ics.InnerSpec = p.InnerSpec.ConvertToIcs23InnerSpec()
ics.MaxDepth = p.MaxDepth
ics.MinDepth = p.MinDepth
ics.PrehashKeyBeforeComparison = p.PrehashKeyBeforeComparison
return ics
}

func (l *LeafOp) ConvertToIcs23LeafOp() *ics23.LeafOp {
ics := new(ics23.LeafOp)
ics.Hash = l.Hash.ConvertToIcs23HashOp()
ics.PrehashKey = l.PrehashKey.ConvertToIcs23HashOp()
ics.PrehashValue = l.PrehashValue.ConvertToIcs23HashOp()
ics.Length = l.Length.ConvertToIcs23LenthOp()
ics.Prefix = l.Prefix
return ics
}

func (i *InnerSpec) ConvertToIcs23InnerSpec() *ics23.InnerSpec {
ics := new(ics23.InnerSpec)
ics.ChildOrder = i.ChildOrder
ics.MinPrefixLength = i.MinPrefixLength
ics.MaxPrefixLength = i.MaxPrefixLength
ics.EmptyChild = i.EmptyChild
ics.Hash = i.Hash.ConvertToIcs23HashOp()
return ics
}

func (h HashOp) ConvertToIcs23HashOp() ics23.HashOp {
switch h {
case HashOp_NO_HASH:
return ics23.HashOp_NO_HASH
case HashOp_SHA256:
return ics23.HashOp_SHA256
case HashOp_SHA512:
return ics23.HashOp_SHA512
case HashOp_KECCAK:
return ics23.HashOp_KECCAK
case HashOp_RIPEMD160:
return ics23.HashOp_RIPEMD160
case HashOp_BITCOIN:
return ics23.HashOp_BITCOIN
case HashOp_SHA512_256:
return ics23.HashOp_SHA512_256
default:
panic("unknown hash op")
}
}

func (l LengthOp) ConvertToIcs23LenthOp() ics23.LengthOp {
switch l {
case LengthOp_NO_PREFIX:
return ics23.LengthOp_NO_PREFIX
case LengthOp_VAR_PROTO:
return ics23.LengthOp_VAR_PROTO
case LengthOp_VAR_RLP:
return ics23.LengthOp_VAR_RLP
case LengthOp_FIXED32_BIG:
return ics23.LengthOp_FIXED32_BIG
case LengthOp_FIXED32_LITTLE:
return ics23.LengthOp_FIXED32_LITTLE
case LengthOp_FIXED64_BIG:
return ics23.LengthOp_FIXED64_BIG
case LengthOp_FIXED64_LITTLE:
return ics23.LengthOp_FIXED64_LITTLE
case LengthOp_REQUIRE_32_BYTES:
return ics23.LengthOp_REQUIRE_32_BYTES
case LengthOp_REQUIRE_64_BYTES:
return ics23.LengthOp_REQUIRE_64_BYTES
default:
panic("unknown length op")
}
}

func (i *InnerOp) ConvertToIcs23InnerOp() *ics23.InnerOp {
ics := new(ics23.InnerOp)
ics.Hash = i.Hash.ConvertToIcs23HashOp()
ics.Prefix = i.Prefix
ics.Suffix = i.Suffix
return ics
}
Loading

0 comments on commit 80c0319

Please sign in to comment.