Skip to content

Commit

Permalink
Implement Connection-specified delay (#8069)
Browse files Browse the repository at this point in the history
* start with initialization and metadata

* start by fixing delay period during connection handshake

* fix connection handshake tests

* add delay period logic to packet verification

* proto format

* fix err issue

* appease linter

* document upgrade special case

* Update x/ibc/light-clients/07-tendermint/types/client_state.go

Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com>

* Update x/ibc/light-clients/07-tendermint/types/store.go

* add sanity check

* fix build

* fix tests

Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com>
Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: Colin Axner <colinaxner@berkeley.edu>
  • Loading branch information
5 people authored Dec 7, 2020
1 parent de11477 commit 971d253
Show file tree
Hide file tree
Showing 48 changed files with 1,852 additions and 318 deletions.
27 changes: 24 additions & 3 deletions proto/ibc/core/client/v1/genesis.proto
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,30 @@ message GenesisState {
(gogoproto.castrepeated) = "ClientsConsensusStates",
(gogoproto.moretags) = "yaml:\"clients_consensus\""
];
Params params = 3 [(gogoproto.nullable) = false];
// metadata from each client
repeated IdentifiedGenesisMetadata clients_metadata = 3
[(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"clients_metadata\""];
Params params = 4 [(gogoproto.nullable) = false];
// create localhost on initialization
bool create_localhost = 4 [(gogoproto.moretags) = "yaml:\"create_localhost\""];
bool create_localhost = 5 [(gogoproto.moretags) = "yaml:\"create_localhost\""];
// the sequence for the next generated client identifier
uint64 next_client_sequence = 5 [(gogoproto.moretags) = "yaml:\"next_client_sequence\""];
uint64 next_client_sequence = 6 [(gogoproto.moretags) = "yaml:\"next_client_sequence\""];
}

// GenesisMetadata defines the genesis type for metadata that clients may return
// with ExportMetadata
message GenesisMetadata {
option (gogoproto.goproto_getters) = false;

// store key of metadata without clientID-prefix
bytes key = 1;
// metadata value
bytes value = 2;
}

// IdentifiedGenesisMetadata has the client metadata with the corresponding client id.
message IdentifiedGenesisMetadata {
string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""];
repeated GenesisMetadata client_metadata = 2
[(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"client_metadata\""];
}
12 changes: 9 additions & 3 deletions proto/ibc/core/connection/v1/connection.proto
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,23 @@ import "ibc/core/commitment/v1/commitment.proto";
// https://github.com/cosmos/ics/tree/master/spec/ics-003-connection-semantics#data-structures

// ConnectionEnd defines a stateful object on a chain connected to another
// separate one. NOTE: there must only be 2 defined ConnectionEnds to establish
// separate one.
// NOTE: there must only be 2 defined ConnectionEnds to establish
// a connection between two chains.
message ConnectionEnd {
option (gogoproto.goproto_getters) = false;
// client associated with this connection.
string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""];
// IBC version which can be utilised to determine encodings or protocols for
// channels or packets utilising this connection
// channels or packets utilising this connection.
repeated Version versions = 2;
// current state of the connection end.
State state = 3;
// counterparty chain associated with this connection.
Counterparty counterparty = 4 [(gogoproto.nullable) = false];
// delay period that must pass before a consensus state can be used for packet-verification
// NOTE: delay period logic is only implemented by some clients.
uint64 delay_period = 5 [(gogoproto.moretags) = "yaml:\"delay_period\""];
}

// IdentifiedConnection defines a connection with additional connection
Expand All @@ -40,6 +44,8 @@ message IdentifiedConnection {
State state = 4;
// counterparty chain associated with this connection.
Counterparty counterparty = 5 [(gogoproto.nullable) = false];
// delay period associated with this connection.
uint64 delay_period = 6 [(gogoproto.moretags) = "yaml:\"delay_period\""];
}

// State defines if a connection is in one of the following states:
Expand Down Expand Up @@ -68,7 +74,7 @@ message Counterparty {
// identifies the connection end on the counterparty chain associated with a
// given connection.
string connection_id = 2 [(gogoproto.moretags) = "yaml:\"connection_id\""];
// commitment merkle prefix of the counterparty chain
// commitment merkle prefix of the counterparty chain.
ibc.core.commitment.v1.MerklePrefix prefix = 3 [(gogoproto.nullable) = false];
}

Expand Down
18 changes: 10 additions & 8 deletions proto/ibc/core/connection/v1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ message MsgConnectionOpenInit {
string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""];
Counterparty counterparty = 2 [(gogoproto.nullable) = false];
Version version = 3;
string signer = 4;
uint64 delay_period = 4 [(gogoproto.moretags) = "yaml:\"delay_period\""];
string signer = 5;
}

// MsgConnectionOpenInitResponse defines the Msg/ConnectionOpenInit response type.
Expand All @@ -50,19 +51,20 @@ message MsgConnectionOpenTry {
string previous_connection_id = 2 [(gogoproto.moretags) = "yaml:\"previous_connection_id\""];
google.protobuf.Any client_state = 3 [(gogoproto.moretags) = "yaml:\"client_state\""];
Counterparty counterparty = 4 [(gogoproto.nullable) = false];
repeated Version counterparty_versions = 5 [(gogoproto.moretags) = "yaml:\"counterparty_versions\""];
ibc.core.client.v1.Height proof_height = 6
uint64 delay_period = 5 [(gogoproto.moretags) = "yaml:\"delay_period\""];
repeated Version counterparty_versions = 6 [(gogoproto.moretags) = "yaml:\"counterparty_versions\""];
ibc.core.client.v1.Height proof_height = 7
[(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false];
// proof of the initialization the connection on Chain A: `UNITIALIZED ->
// INIT`
bytes proof_init = 7 [(gogoproto.moretags) = "yaml:\"proof_init\""];
bytes proof_init = 8 [(gogoproto.moretags) = "yaml:\"proof_init\""];
// proof of client state included in message
bytes proof_client = 8 [(gogoproto.moretags) = "yaml:\"proof_client\""];
bytes proof_client = 9 [(gogoproto.moretags) = "yaml:\"proof_client\""];
// proof of client consensus state
bytes proof_consensus = 9 [(gogoproto.moretags) = "yaml:\"proof_consensus\""];
ibc.core.client.v1.Height consensus_height = 10
bytes proof_consensus = 10 [(gogoproto.moretags) = "yaml:\"proof_consensus\""];
ibc.core.client.v1.Height consensus_height = 11
[(gogoproto.moretags) = "yaml:\"consensus_height\"", (gogoproto.nullable) = false];
string signer = 11;
string signer = 12;
}

// MsgConnectionOpenTryResponse defines the Msg/ConnectionOpenTry response type.
Expand Down
14 changes: 13 additions & 1 deletion x/ibc/core/02-client/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ import (
func InitGenesis(ctx sdk.Context, k keeper.Keeper, gs types.GenesisState) {
k.SetParams(ctx, gs.Params)

// Set all client metadata first. This will allow client keeper to overwrite client and consensus state keys
// if clients accidentally write to ClientKeeper reserved keys.
if len(gs.ClientsMetadata) != 0 {
k.SetAllClientMetadata(ctx, gs.ClientsMetadata)
}

for _, client := range gs.Clients {
cs, ok := client.ClientState.GetCachedValue().(exported.ClientState)
if !ok {
Expand Down Expand Up @@ -48,8 +54,14 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, gs types.GenesisState) {
// NOTE: CreateLocalhost should always be false on export since a
// created localhost will be included in the exported clients.
func ExportGenesis(ctx sdk.Context, k keeper.Keeper) types.GenesisState {
genClients := k.GetAllGenesisClients(ctx)
clientsMetadata, err := k.GetAllClientMetadata(ctx, genClients)
if err != nil {
panic(err)
}
return types.GenesisState{
Clients: k.GetAllGenesisClients(ctx),
Clients: genClients,
ClientsMetadata: clientsMetadata,
ClientsConsensus: k.GetAllConsensusStates(ctx),
Params: k.GetParams(ctx),
CreateLocalhost: false,
Expand Down
12 changes: 9 additions & 3 deletions x/ibc/core/02-client/keeper/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import (

// CreateClient creates a new client state and populates it with a given consensus
// state as defined in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#create
//
// CONTRACT: ClientState was constructed correctly from given initial consensusState
func (k Keeper) CreateClient(
ctx sdk.Context, clientState exported.ClientState, consensusState exported.ConsensusState,
) (string, error) {
Expand All @@ -27,12 +25,20 @@ func (k Keeper) CreateClient(

clientID := k.GenerateClientIdentifier(ctx, clientState.ClientType())

k.SetClientState(ctx, clientID, clientState)
k.Logger(ctx).Info("client created at height", "client-id", clientID, "height", clientState.GetLatestHeight().String())

// verifies initial consensus state against client state and initializes client store with any client-specific metadata
// e.g. set ProcessedTime in Tendermint clients
if err := clientState.Initialize(ctx, k.cdc, k.ClientStore(ctx, clientID), consensusState); err != nil {
return "", err
}

// check if consensus state is nil in case the created client is Localhost
if consensusState != nil {
k.SetClientConsensusState(ctx, clientID, clientState.GetLatestHeight(), consensusState)
}

k.SetClientState(ctx, clientID, clientState)
k.Logger(ctx).Info("client created at height", "client-id", clientID, "height", clientState.GetLatestHeight().String())

defer func() {
Expand Down
43 changes: 43 additions & 0 deletions x/ibc/core/02-client/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,49 @@ func (k Keeper) GetAllGenesisClients(ctx sdk.Context) types.IdentifiedClientStat
return genClients.Sort()
}

// GetAllClientMetadata will take a list of IdentifiedClientState and return a list
// of IdentifiedGenesisMetadata necessary for exporting and importing client metadata
// into the client store.
func (k Keeper) GetAllClientMetadata(ctx sdk.Context, genClients []types.IdentifiedClientState) ([]types.IdentifiedGenesisMetadata, error) {
genMetadata := make([]types.IdentifiedGenesisMetadata, 0)
for _, ic := range genClients {
cs, err := types.UnpackClientState(ic.ClientState)
if err != nil {
return nil, err
}
gms := cs.ExportMetadata(k.ClientStore(ctx, ic.ClientId))
if len(gms) == 0 {
continue
}
clientMetadata := make([]types.GenesisMetadata, len(gms))
for i, metadata := range gms {
cmd, ok := metadata.(types.GenesisMetadata)
if !ok {
return nil, sdkerrors.Wrapf(types.ErrInvalidClientMetadata, "expected metadata type: %T, got: %T",
types.GenesisMetadata{}, cmd)
}
clientMetadata[i] = cmd
}
genMetadata = append(genMetadata, types.NewIdentifiedGenesisMetadata(
ic.ClientId,
clientMetadata,
))
}
return genMetadata, nil
}

// SetAllClientMetadata takes a list of IdentifiedGenesisMetadata and stores all of the metadata in the client store at the appropriate paths.
func (k Keeper) SetAllClientMetadata(ctx sdk.Context, genMetadata []types.IdentifiedGenesisMetadata) {
for _, igm := range genMetadata {
// create client store
store := k.ClientStore(ctx, igm.ClientId)
// set all metadata kv pairs in client store
for _, md := range igm.ClientMetadata {
store.Set(md.GetKey(), md.GetValue())
}
}
}

// GetAllConsensusStates returns all stored client consensus states.
func (k Keeper) GetAllConsensusStates(ctx sdk.Context) types.ClientsConsensusStates {
clientConsStates := make(types.ClientsConsensusStates, 0)
Expand Down
31 changes: 31 additions & 0 deletions x/ibc/core/02-client/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,37 @@ func (suite KeeperTestSuite) TestGetAllGenesisClients() {
suite.Require().Equal(expGenClients.Sort(), genClients)
}

func (suite KeeperTestSuite) TestGetAllGenesisMetadata() {
expectedGenMetadata := []types.IdentifiedGenesisMetadata{
types.NewIdentifiedGenesisMetadata(
"clientA",
[]types.GenesisMetadata{
types.NewGenesisMetadata(ibctmtypes.ProcessedTimeKey(types.NewHeight(0, 1)), []byte("foo")),
types.NewGenesisMetadata(ibctmtypes.ProcessedTimeKey(types.NewHeight(0, 2)), []byte("bar")),
types.NewGenesisMetadata(ibctmtypes.ProcessedTimeKey(types.NewHeight(0, 3)), []byte("baz")),
},
),
types.NewIdentifiedGenesisMetadata(
"clientB",
[]types.GenesisMetadata{
types.NewGenesisMetadata(ibctmtypes.ProcessedTimeKey(types.NewHeight(1, 100)), []byte("val1")),
types.NewGenesisMetadata(ibctmtypes.ProcessedTimeKey(types.NewHeight(2, 300)), []byte("val2")),
},
),
}

genClients := []types.IdentifiedClientState{
types.NewIdentifiedClientState("clientA", &ibctmtypes.ClientState{}), types.NewIdentifiedClientState("clientB", &ibctmtypes.ClientState{}),
types.NewIdentifiedClientState("clientC", &ibctmtypes.ClientState{}), types.NewIdentifiedClientState("clientD", &localhosttypes.ClientState{}),
}

suite.chainA.App.IBCKeeper.ClientKeeper.SetAllClientMetadata(suite.chainA.GetContext(), expectedGenMetadata)

actualGenMetadata, err := suite.chainA.App.IBCKeeper.ClientKeeper.GetAllClientMetadata(suite.chainA.GetContext(), genClients)
suite.Require().NoError(err, "get client metadata returned error unexpectedly")
suite.Require().Equal(expectedGenMetadata, actualGenMetadata, "retrieved metadata is unexpected")
}

func (suite KeeperTestSuite) TestGetConsensusState() {
suite.ctx = suite.ctx.WithBlockHeight(10)
cases := []struct {
Expand Down
39 changes: 20 additions & 19 deletions x/ibc/core/02-client/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,24 @@ var (
ErrInvalidClient = sdkerrors.Register(SubModuleName, 3, "light client is invalid")
ErrClientNotFound = sdkerrors.Register(SubModuleName, 4, "light client not found")
ErrClientFrozen = sdkerrors.Register(SubModuleName, 5, "light client is frozen due to misbehaviour")
ErrConsensusStateNotFound = sdkerrors.Register(SubModuleName, 6, "consensus state not found")
ErrInvalidConsensus = sdkerrors.Register(SubModuleName, 7, "invalid consensus state")
ErrClientTypeNotFound = sdkerrors.Register(SubModuleName, 8, "client type not found")
ErrInvalidClientType = sdkerrors.Register(SubModuleName, 9, "invalid client type")
ErrRootNotFound = sdkerrors.Register(SubModuleName, 10, "commitment root not found")
ErrInvalidHeader = sdkerrors.Register(SubModuleName, 11, "invalid client header")
ErrInvalidMisbehaviour = sdkerrors.Register(SubModuleName, 12, "invalid light client misbehaviour")
ErrFailedClientStateVerification = sdkerrors.Register(SubModuleName, 13, "client state verification failed")
ErrFailedClientConsensusStateVerification = sdkerrors.Register(SubModuleName, 14, "client consensus state verification failed")
ErrFailedConnectionStateVerification = sdkerrors.Register(SubModuleName, 15, "connection state verification failed")
ErrFailedChannelStateVerification = sdkerrors.Register(SubModuleName, 16, "channel state verification failed")
ErrFailedPacketCommitmentVerification = sdkerrors.Register(SubModuleName, 17, "packet commitment verification failed")
ErrFailedPacketAckVerification = sdkerrors.Register(SubModuleName, 18, "packet acknowledgement verification failed")
ErrFailedPacketReceiptVerification = sdkerrors.Register(SubModuleName, 19, "packet receipt verification failed")
ErrFailedNextSeqRecvVerification = sdkerrors.Register(SubModuleName, 20, "next sequence receive verification failed")
ErrSelfConsensusStateNotFound = sdkerrors.Register(SubModuleName, 21, "self consensus state not found")
ErrUpdateClientFailed = sdkerrors.Register(SubModuleName, 22, "unable to update light client")
ErrInvalidUpdateClientProposal = sdkerrors.Register(SubModuleName, 23, "invalid update client proposal")
ErrInvalidUpgradeClient = sdkerrors.Register(SubModuleName, 24, "invalid client upgrade")
ErrInvalidClientMetadata = sdkerrors.Register(SubModuleName, 6, "invalid client metadata")
ErrConsensusStateNotFound = sdkerrors.Register(SubModuleName, 7, "consensus state not found")
ErrInvalidConsensus = sdkerrors.Register(SubModuleName, 8, "invalid consensus state")
ErrClientTypeNotFound = sdkerrors.Register(SubModuleName, 9, "client type not found")
ErrInvalidClientType = sdkerrors.Register(SubModuleName, 10, "invalid client type")
ErrRootNotFound = sdkerrors.Register(SubModuleName, 11, "commitment root not found")
ErrInvalidHeader = sdkerrors.Register(SubModuleName, 12, "invalid client header")
ErrInvalidMisbehaviour = sdkerrors.Register(SubModuleName, 13, "invalid light client misbehaviour")
ErrFailedClientStateVerification = sdkerrors.Register(SubModuleName, 14, "client state verification failed")
ErrFailedClientConsensusStateVerification = sdkerrors.Register(SubModuleName, 15, "client consensus state verification failed")
ErrFailedConnectionStateVerification = sdkerrors.Register(SubModuleName, 16, "connection state verification failed")
ErrFailedChannelStateVerification = sdkerrors.Register(SubModuleName, 17, "channel state verification failed")
ErrFailedPacketCommitmentVerification = sdkerrors.Register(SubModuleName, 18, "packet commitment verification failed")
ErrFailedPacketAckVerification = sdkerrors.Register(SubModuleName, 19, "packet acknowledgement verification failed")
ErrFailedPacketReceiptVerification = sdkerrors.Register(SubModuleName, 20, "packet receipt verification failed")
ErrFailedNextSeqRecvVerification = sdkerrors.Register(SubModuleName, 21, "next sequence receive verification failed")
ErrSelfConsensusStateNotFound = sdkerrors.Register(SubModuleName, 22, "self consensus state not found")
ErrUpdateClientFailed = sdkerrors.Register(SubModuleName, 23, "unable to update light client")
ErrInvalidUpdateClientProposal = sdkerrors.Register(SubModuleName, 24, "invalid update client proposal")
ErrInvalidUpgradeClient = sdkerrors.Register(SubModuleName, 25, "invalid client upgrade")
)
Loading

0 comments on commit 971d253

Please sign in to comment.