-
Notifications
You must be signed in to change notification settings - Fork 33
[IBC] Implement the ICS-02 Client interfaces #933
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
672e202
513005c
0ce694a
ac50e06
06cb90e
064f69c
61fcc38
1519ad0
d87cf1c
b5958f8
b218b27
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 | ||
h5law marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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())), | ||
| }, | ||
| }, | ||
| ) | ||
| } | ||
| 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 | ||
h5law marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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. | ||
|
Comment on lines
+42
to
+43
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment is confusing me. Is the purpose of this to verify the state of pocket on another chain, or the state of another chain on pocket?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So this function gets the current network state and is used in the verification of a pocket client running on another network. I will try to clarify the comment |
||
| 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 | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| blockTime := time.Minute * 15 | ||
| unbondingPeriod := blockTime * time.Duration(unbondingBlocks) // approx minutes per block * blocks | ||
h5law marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| pocketClient := &light_client_types.PocketClientState{ | ||
| NetworkId: block.BlockHeader.NetworkId, | ||
| TrustLevel: &light_client_types.Fraction{Numerator: 2, Denominator: 3}, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you sure we don't need a
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean we could just do our check of the trust level to be |
||
| TrustingPeriod: durationpb.New(unbondingPeriod), | ||
| UnbondingPeriod: durationpb.New(unbondingPeriod), | ||
| MaxClockDrift: durationpb.New(blockTime), // DISCUSS: What is a reasonable MaxClockDrift? | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Anything < one block. I'd just do like 0.5 * blockTime, move
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have actually gone with tendermints approach here and left it at 10s if a header is 7.5 minutes in the future we probably should be treating the client as going bad. |
||
| 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 | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TOL(Thinking Out Loud): Can we replace
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I dont mind either way, but we have the WDYT swap or stay? |
||
| // 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 { | ||
h5law marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return err | ||
| } | ||
| poktCounter := new(light_client_types.PocketClientState) | ||
h5law marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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") | ||
h5law marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| 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}) || | ||
h5law marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| poktCounter.TrustLevel.GT(&light_client_types.Fraction{Numerator: 1, Denominator: 1}) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When/how can
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It cant in reality but this just does the check that 2/3 <= x <= 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 | ||
h5law marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| return nil | ||
| } | ||
|
|
||
| // GetCurrentHeight returns the current IBC client height of the network | ||
| // TODO_AFTER(#882): Use actual revision number | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cc @0xBigBoss - just FYI |
||
| 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 | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| package types | ||
h5law marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| 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 { | ||
h5law marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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 | ||
| } | ||
| 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, | ||
h5law marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ) (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) | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.