-
Notifications
You must be signed in to change notification settings - Fork 635
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
feat: add light client module for Celestia DA light client #6053
Changes from all commits
50be2ee
e9483b3
72732f9
840fa84
47640c1
f79727f
c37bd64
62f901a
5948289
f4b8a0c
1e2702d
62a5dc7
c0bec40
cfbe313
4c05af6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ import ( | |
sdk "github.com/cosmos/cosmos-sdk/types" | ||
|
||
clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" | ||
commitmenttypes "github.com/cosmos/ibc-go/v8/modules/core/23-commitment/types" | ||
ibcerrors "github.com/cosmos/ibc-go/v8/modules/core/errors" | ||
"github.com/cosmos/ibc-go/v8/modules/core/exported" | ||
ibctm "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" | ||
|
@@ -20,39 +21,21 @@ func (*ClientState) ClientType() string { | |
return ModuleName | ||
} | ||
|
||
// GetLatestHeight implements exported.ClientState. | ||
func (cs *ClientState) GetLatestHeight() exported.Height { | ||
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 removed these functions from 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. Will we need to backport this feature? 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. Yes, we will have to backport these changes to the release branch of the DA light client that is compatible with ibc-go v8.3.x (and that version doesn't include the 02-client routing refactor). 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 might be less overhead to take whats on the target branch now and separate that to a different branch for v8.3.x before merging this PR. It's no harm at least to replicate the feat/celestia-da-client before this PR is merged in case that might be helpful. |
||
return cs.BaseClient.LatestHeight | ||
} | ||
|
||
// GetTimestampAtHeight implements exported.ClientState. | ||
func (cs *ClientState) GetTimestampAtHeight(ctx sdk.Context, clientStore storetypes.KVStore, cdc codec.BinaryCodec, height exported.Height) (uint64, error) { | ||
return cs.BaseClient.GetTimestampAtHeight(ctx, clientStore, cdc, height) | ||
} | ||
|
||
// Status implements exported.ClientState. | ||
func (cs *ClientState) Status(ctx sdk.Context, clientStore storetypes.KVStore, cdc codec.BinaryCodec) exported.Status { | ||
return cs.BaseClient.Status(ctx, clientStore, cdc) | ||
} | ||
|
||
// Initialize implements exported.ClientState. | ||
func (cs *ClientState) Initialize(ctx sdk.Context, cdc codec.BinaryCodec, clientStore storetypes.KVStore, consensusState exported.ConsensusState) error { | ||
return cs.BaseClient.Initialize(ctx, cdc, clientStore, consensusState) | ||
} | ||
|
||
// Validate implements exported.ClientState. | ||
func (cs *ClientState) Validate() error { | ||
return cs.BaseClient.Validate() | ||
} | ||
|
||
// VerifyMembership implements exported.ClientState. | ||
// VerifyMembership is a generic proof verification method which verifies an NMT proof | ||
// that a set of shares exist in a set of rows and a Merkle proof that those rows exist | ||
// in a Merkle tree with a given data root. | ||
// TODO: Revise and look into delay periods for this. | ||
// TODO: Validate key path and value against the shareProof extracted from proof bytes. | ||
func (cs *ClientState) VerifyMembership(ctx sdk.Context, clientStore storetypes.KVStore, cdc codec.BinaryCodec, height exported.Height, delayTimePeriod uint64, delayBlockPeriod uint64, proof []byte, path exported.Path, value []byte) error { | ||
if cs.BaseClient.LatestHeight.LT(height) { | ||
return errorsmod.Wrapf( | ||
ibcerrors.ErrInvalidHeight, | ||
"client state height < proof height (%d < %d), please ensure the client has been updated", cs.GetLatestHeight(), height, | ||
"client state height < proof height (%d < %d), please ensure the client has been updated", cs.BaseClient.LatestHeight, height, | ||
) | ||
} | ||
|
||
|
@@ -62,7 +45,7 @@ func (cs *ClientState) VerifyMembership(ctx sdk.Context, clientStore storetypes. | |
|
||
var shareProofProto ShareProof | ||
if err := cdc.Unmarshal(proof, &shareProofProto); err != nil { | ||
return err | ||
return errorsmod.Wrapf(commitmenttypes.ErrInvalidProof, "could not unmarshal share proof: %v", err) | ||
} | ||
|
||
shareProof, err := shareProofFromProto(&shareProofProto) | ||
|
@@ -78,11 +61,6 @@ func (cs *ClientState) VerifyMembership(ctx sdk.Context, clientStore storetypes. | |
return shareProof.Validate(consensusState.GetRoot().GetHash()) | ||
} | ||
|
||
// VerifyNonMembership implements exported.ClientState. | ||
func (*ClientState) VerifyNonMembership(ctx sdk.Context, clientStore storetypes.KVStore, cdc codec.BinaryCodec, height exported.Height, delayTimePeriod uint64, delayBlockPeriod uint64, proof []byte, path exported.Path) error { | ||
panic("unimplemented") | ||
} | ||
|
||
// verifyDelayPeriodPassed will ensure that at least delayTimePeriod amount of time and delayBlockPeriod number of blocks have passed | ||
// since consensus state was submitted before allowing verification to continue. | ||
func verifyDelayPeriodPassed(ctx sdk.Context, store storetypes.KVStore, proofHeight exported.Height, delayTimePeriod, delayBlockPeriod uint64) error { | ||
|
@@ -101,7 +79,6 @@ func verifyDelayPeriodPassed(ctx sdk.Context, store storetypes.KVStore, proofHei | |
return errorsmod.Wrapf(ibctm.ErrDelayPeriodNotPassed, "cannot verify packet until time: %d, current time: %d", | ||
validTime, currentTimestamp) | ||
} | ||
|
||
} | ||
|
||
if delayBlockPeriod != 0 { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,240 @@ | ||
package celestia | ||
|
||
import ( | ||
errorsmod "cosmossdk.io/errors" | ||
|
||
"github.com/cosmos/cosmos-sdk/codec" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
|
||
clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" | ||
"github.com/cosmos/ibc-go/v8/modules/core/exported" | ||
ibctm "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" | ||
) | ||
|
||
var _ exported.LightClientModule = (*LightClientModule)(nil) | ||
|
||
// LightClientModule implements the core IBC api.LightClientModule interface. | ||
type LightClientModule struct { | ||
cdc codec.BinaryCodec | ||
storeProvider exported.ClientStoreProvider | ||
} | ||
|
||
// NewLightClientModule creates and returns a new 07-celestia LightClientModule. | ||
func NewLightClientModule(cdc codec.BinaryCodec) LightClientModule { | ||
return LightClientModule{ | ||
cdc: cdc, | ||
} | ||
} | ||
|
||
// RegisterStoreProvider is called by core IBC when a LightClientModule is added to the router. | ||
// It allows the LightClientModule to set a ClientStoreProvider which supplies isolated prefix client stores | ||
// to IBC light client instances. | ||
func (l *LightClientModule) RegisterStoreProvider(storeProvider exported.ClientStoreProvider) { | ||
l.storeProvider = storeProvider | ||
} | ||
|
||
// Initialize unmarshals the provided client and consensus states and performs basic validation. It calls into the | ||
// clientState.Initialize method. | ||
// | ||
// CONTRACT: clientID is validated in 02-client router, thus clientID is assumed here to have the format 07-celestia-{n}. | ||
func (l LightClientModule) Initialize(ctx sdk.Context, clientID string, clientStateBz, consensusStateBz []byte) error { | ||
var clientState ClientState | ||
if err := l.cdc.Unmarshal(clientStateBz, &clientState); err != nil { | ||
return errorsmod.Wrap(clienttypes.ErrInvalidClient, err.Error()) | ||
} | ||
|
||
if err := clientState.Validate(); err != nil { | ||
return err | ||
} | ||
|
||
var consensusState ibctm.ConsensusState | ||
if err := l.cdc.Unmarshal(consensusStateBz, &consensusState); err != nil { | ||
return errorsmod.Wrap(clienttypes.ErrInvalidConsensus, err.Error()) | ||
} | ||
|
||
clientStore := l.storeProvider.ClientStore(ctx, clientID) | ||
|
||
return clientState.BaseClient.Initialize(ctx, l.cdc, clientStore, &consensusState) | ||
} | ||
|
||
// VerifyClientMessage obtains the client state associated with the client identifier and calls into the clientState.VerifyClientMessage method. | ||
// | ||
// CONTRACT: clientID is validated in 02-client router, thus clientID is assumed here to have the format 07-celestia-{n}. | ||
func (l LightClientModule) VerifyClientMessage(ctx sdk.Context, clientID string, clientMsg exported.ClientMessage) error { | ||
clientStore := l.storeProvider.ClientStore(ctx, clientID) | ||
clientState, found := getClientState(clientStore, l.cdc) | ||
if !found { | ||
return errorsmod.Wrap(clienttypes.ErrClientNotFound, clientID) | ||
} | ||
|
||
return clientState.BaseClient.VerifyClientMessage(ctx, l.cdc, clientStore, clientMsg) | ||
} | ||
|
||
// CheckForMisbehaviour obtains the client state associated with the client identifier and calls into the clientState.CheckForMisbehaviour method. | ||
// | ||
// CONTRACT: clientID is validated in 02-client router, thus clientID is assumed here to have the format 07-celestia-{n}. | ||
func (l LightClientModule) CheckForMisbehaviour(ctx sdk.Context, clientID string, clientMsg exported.ClientMessage) bool { | ||
clientStore := l.storeProvider.ClientStore(ctx, clientID) | ||
clientState, found := getClientState(clientStore, l.cdc) | ||
if !found { | ||
panic(errorsmod.Wrap(clienttypes.ErrClientNotFound, clientID)) | ||
} | ||
|
||
return clientState.BaseClient.CheckForMisbehaviour(ctx, l.cdc, clientStore, clientMsg) | ||
} | ||
|
||
// UpdateStateOnMisbehaviour obtains the client state associated with the client identifier and calls into the clientState.UpdateStateOnMisbehaviour method. | ||
// | ||
// CONTRACT: clientID is validated in 02-client router, thus clientID is assumed here to have the format 07-celestia-{n}. | ||
func (l LightClientModule) UpdateStateOnMisbehaviour(ctx sdk.Context, clientID string, clientMsg exported.ClientMessage) { | ||
clientStore := l.storeProvider.ClientStore(ctx, clientID) | ||
clientState, found := getClientState(clientStore, l.cdc) | ||
if !found { | ||
panic(errorsmod.Wrap(clienttypes.ErrClientNotFound, clientID)) | ||
} | ||
|
||
clientState.BaseClient.UpdateStateOnMisbehaviour(ctx, l.cdc, clientStore, clientMsg) | ||
} | ||
|
||
// UpdateState obtains the client state associated with the client identifier and calls into the clientState.UpdateState method. | ||
// | ||
// CONTRACT: clientID is validated in 02-client router, thus clientID is assumed here to have the format 07-celestia-{n}. | ||
func (l LightClientModule) UpdateState(ctx sdk.Context, clientID string, clientMsg exported.ClientMessage) []exported.Height { | ||
clientStore := l.storeProvider.ClientStore(ctx, clientID) | ||
clientState, found := getClientState(clientStore, l.cdc) | ||
if !found { | ||
panic(errorsmod.Wrap(clienttypes.ErrClientNotFound, clientID)) | ||
} | ||
|
||
// execute custom 07-celestia update state logic | ||
return clientState.UpdateState(ctx, l.cdc, clientStore, clientMsg) | ||
crodriguezvega marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
// VerifyMembership obtains the client state associated with the client identifier and calls into the clientState.VerifyMembership method. | ||
// | ||
// CONTRACT: clientID is validated in 02-client router, thus clientID is assumed here to have the format 07-celestia-{n}. | ||
func (l LightClientModule) VerifyMembership( | ||
ctx sdk.Context, | ||
clientID string, | ||
height exported.Height, | ||
delayTimePeriod uint64, | ||
delayBlockPeriod uint64, | ||
proof []byte, | ||
path exported.Path, | ||
value []byte, | ||
) error { | ||
clientStore := l.storeProvider.ClientStore(ctx, clientID) | ||
clientState, found := getClientState(clientStore, l.cdc) | ||
if !found { | ||
return errorsmod.Wrap(clienttypes.ErrClientNotFound, clientID) | ||
} | ||
|
||
// execute custom 07-celestia verify membership logic | ||
return clientState.VerifyMembership(ctx, clientStore, l.cdc, height, delayTimePeriod, delayBlockPeriod, proof, path, value) | ||
crodriguezvega marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
// VerifyNonMembership obtains the client state associated with the client identifier and calls into the clientState.VerifyNonMembership method. | ||
// | ||
// CONTRACT: clientID is validated in 02-client router, thus clientID is assumed here to have the format 07-celestia-{n}. | ||
func (LightClientModule) VerifyNonMembership( | ||
ctx sdk.Context, | ||
clientID string, | ||
height exported.Height, | ||
delayTimePeriod uint64, | ||
delayBlockPeriod uint64, | ||
proof []byte, | ||
path exported.Path, | ||
) error { | ||
panic("07-celestia light clients do not verify non-membership proofs") | ||
} | ||
|
||
// Status obtains the client state associated with the client identifier and calls into the clientState.Status method. | ||
// | ||
// CONTRACT: clientID is validated in 02-client router, thus clientID is assumed here to have the format 07-celestia-{n}. | ||
func (l LightClientModule) Status(ctx sdk.Context, clientID string) exported.Status { | ||
clientStore := l.storeProvider.ClientStore(ctx, clientID) | ||
clientState, found := getClientState(clientStore, l.cdc) | ||
if !found { | ||
return exported.Unknown | ||
} | ||
|
||
return clientState.BaseClient.Status(ctx, clientStore, l.cdc) | ||
} | ||
|
||
// LatestHeight returns the latest height for the client state for the given client identifier. | ||
// If no client is present for the provided client identifier a zero value height is returned. | ||
// | ||
// CONTRACT: clientID is validated in 02-client router, thus clientID is assumed here to have the format 07-celestia-{n}. | ||
func (l LightClientModule) LatestHeight(ctx sdk.Context, clientID string) exported.Height { | ||
clientStore := l.storeProvider.ClientStore(ctx, clientID) | ||
|
||
clientState, found := getClientState(clientStore, l.cdc) | ||
if !found { | ||
return clienttypes.ZeroHeight() | ||
} | ||
|
||
return clientState.BaseClient.LatestHeight | ||
} | ||
|
||
// TimestampAtHeight obtains the client state associated with the client identifier and calls into the clientState.GetTimestampAtHeight method. | ||
// | ||
// CONTRACT: clientID is validated in 02-client router, thus clientID is assumed here to have the format 07-celestia-{n}. | ||
func (l LightClientModule) TimestampAtHeight( | ||
ctx sdk.Context, | ||
clientID string, | ||
height exported.Height, | ||
) (uint64, error) { | ||
clientStore := l.storeProvider.ClientStore(ctx, clientID) | ||
clientState, found := getClientState(clientStore, l.cdc) | ||
if !found { | ||
return 0, errorsmod.Wrap(clienttypes.ErrClientNotFound, clientID) | ||
} | ||
|
||
return clientState.BaseClient.GetTimestampAtHeight(ctx, clientStore, l.cdc, height) | ||
} | ||
|
||
// RecoverClient asserts that the substitute client is a celestia client. It obtains the client state associated with the | ||
// subject client and calls into the subjectClientState.CheckSubstituteAndUpdateState method. | ||
// | ||
// CONTRACT: clientID is validated in 02-client router, thus clientID is assumed here to have the format 07-celestia-{n}. | ||
func (l LightClientModule) RecoverClient(ctx sdk.Context, clientID, substituteClientID string) error { | ||
substituteClientType, _, err := clienttypes.ParseClientIdentifier(substituteClientID) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if substituteClientType != ModuleName { | ||
return errorsmod.Wrapf(clienttypes.ErrInvalidClientType, "expected: %s, got: %s", ModuleName, substituteClientType) | ||
} | ||
|
||
clientStore := l.storeProvider.ClientStore(ctx, clientID) | ||
clientState, found := getClientState(clientStore, l.cdc) | ||
if !found { | ||
return errorsmod.Wrap(clienttypes.ErrClientNotFound, clientID) | ||
} | ||
|
||
substituteClientStore := l.storeProvider.ClientStore(ctx, substituteClientID) | ||
substituteClient, found := getClientState(substituteClientStore, l.cdc) | ||
if !found { | ||
return errorsmod.Wrap(clienttypes.ErrClientNotFound, substituteClientID) | ||
} | ||
|
||
return clientState.BaseClient.CheckSubstituteAndUpdateState(ctx, l.cdc, clientStore, substituteClientStore, substituteClient.BaseClient) | ||
} | ||
|
||
// VerifyUpgradeAndUpdateState obtains the client state associated with the client identifier and calls into the clientState.VerifyUpgradeAndUpdateState method. | ||
// The new client and consensus states will be unmarshaled and an error is returned if the new client state is not at a height greater | ||
// than the existing client. | ||
// | ||
// CONTRACT: clientID is validated in 02-client router, thus clientID is assumed here to have the format 07-celestia-{n}. | ||
func (LightClientModule) VerifyUpgradeAndUpdateState( | ||
ctx sdk.Context, | ||
clientID string, | ||
newClient []byte, | ||
newConsState []byte, | ||
upgradeClientProof, | ||
upgradeConsensusStateProof []byte, | ||
) error { | ||
// TODO: do we need to implement this? | ||
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. Question: do we need to implement this functionality? 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. We can add an issue for this and discuss later with celestia team |
||
panic("unimplemented") | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Temporary hack to be able to run the tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Opened #6061.