Skip to content

Commit

Permalink
x/ibc: implement 09-localhost per specification (#5769)
Browse files Browse the repository at this point in the history
Signed-off-by: Gregory Hill <gregorydhill@outlook.com>
  • Loading branch information
gregdhill authored Apr 17, 2020
1 parent 968fb1f commit de00a7f
Show file tree
Hide file tree
Showing 12 changed files with 887 additions and 2 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,9 @@ information on how to implement the new `Keyring` interface.
* [ICS 020 - Fungible Token Transfer](https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer) module
* [ICS 023 - Vector Commitments](https://github.com/cosmos/ics/tree/master/spec/ics-023-vector-commitments) subpackage
* (ibc/ante) Implement IBC `AnteHandler` as per [ADR 15 - IBC Packet Receiver](https://github.com/cosmos/tree/master/docs/architecture/adr-015-ibc-packet-receiver.md).
* (x/capability) [\#5828](https://github.com/cosmos/cosmos-sdk/pull/5828) Capability module integration as outlined in [ADR 3 - Dynamic Capability Store](https://github.com/cosmos/tree/master/docs/architecture/adr-003-dynamic-capability-store.md).
* (x/params) [\#6005](https://github.com/cosmos/cosmos-sdk/pull/6005) Add new CLI command for querying raw x/params parameters by subspace and key.
* (x/capability) [\#5828](https://github.com/cosmos/cosmos-sdk/pull/5828) Capability module integration as outlined in [ADR 3 - Dynamic Capability Store](https://github.com/cosmos/tree/master/docs/architecture/adr-003-dynamic-capability-store.md).
* (x/params) [\#6005](https://github.com/cosmos/cosmos-sdk/pull/6005) Add new CLI command for querying raw x/params parameters by subspace and key.
* (x/ibc) [\#5769] Implementation of localhost client.

### Bug Fixes

Expand Down
1 change: 1 addition & 0 deletions x/ibc/02-client/exported/exported.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ type ClientType byte
// available client types
const (
Tendermint ClientType = iota + 1 // 1
Localhost
)

// string representation of the client types
Expand Down
3 changes: 3 additions & 0 deletions x/ibc/02-client/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types"
localhost "github.com/cosmos/cosmos-sdk/x/ibc/09-localhost"
)

// HandleMsgCreateClient defines the sdk.Handler for MsgCreateClient
Expand All @@ -27,6 +28,8 @@ func HandleMsgCreateClient(ctx sdk.Context, k Keeper, msg exported.MsgCreateClie
if err != nil {
return nil, err
}
case exported.Localhost:
clientState = localhost.NewClientState(ctx.MultiStore().GetKVStore(k.GetStoreKey()))
default:
return nil, sdkerrors.Wrap(ErrInvalidClientType, msg.GetClientType())
}
Expand Down
4 changes: 4 additions & 0 deletions x/ibc/02-client/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("module", fmt.Sprintf("x/%s/%s", ibctypes.ModuleName, types.SubModuleName))
}

func (k Keeper) GetStoreKey() sdk.StoreKey {
return k.storeKey
}

// GetClientState gets a particular client from the store
func (k Keeper) GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) {
store := k.clientStore(ctx, clientID)
Expand Down
277 changes: 277 additions & 0 deletions x/ibc/09-localhost/client_state.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,277 @@
package localhost

import (
"bytes"
"encoding/binary"
"fmt"

"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported"
channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported"
commitmentexported "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/exported"
commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types"
ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types"
)

var _ clientexported.ClientState = ClientState{}

// ClientState requires (read-only) access to keys outside the client prefix.
type ClientState struct {
ctx sdk.Context
store types.KVStore
}

// NewClientState creates a new ClientState instance
func NewClientState(store types.KVStore) ClientState {
return ClientState{
store: store,
}
}

// WithContext updates the client state context to provide the chain ID and latest height
func (cs *ClientState) WithContext(ctx sdk.Context) {
cs.ctx = ctx
}

// GetID returns the loop-back client state identifier.
func (cs ClientState) GetID() string {
return clientexported.Localhost.String()
}

// GetChainID returns an empty string
func (cs ClientState) GetChainID() string {
return cs.ctx.ChainID()
}

// ClientType is localhost.
func (cs ClientState) ClientType() clientexported.ClientType {
return clientexported.Localhost
}

// GetLatestHeight returns the block height from the stored context.
func (cs ClientState) GetLatestHeight() uint64 {
return uint64(cs.ctx.BlockHeight())
}

// IsFrozen returns false.
func (cs ClientState) IsFrozen() bool {
return false
}

// VerifyClientConsensusState verifies a proof of the consensus
// state of the loop-back client.
// VerifyClientConsensusState verifies a proof of the consensus state of the
// Tendermint client stored on the target machine.
func (cs ClientState) VerifyClientConsensusState(
cdc *codec.Codec,
_ commitmentexported.Root,
height uint64,
_ string,
consensusHeight uint64,
prefix commitmentexported.Prefix,
_ commitmentexported.Proof,
consensusState clientexported.ConsensusState,
) error {
path, err := commitmenttypes.ApplyPrefix(prefix, consensusStatePath(cs.GetID()))
if err != nil {
return err
}

data := cs.store.Get([]byte(path.String()))
if len(data) == 0 {
return sdkerrors.Wrap(clienttypes.ErrFailedClientConsensusStateVerification, "not found")
}

var prevConsensusState exported.ConsensusState
cdc.MustUnmarshalBinaryBare(data, &prevConsensusState)
if consensusState != prevConsensusState {
return sdkerrors.Wrap(clienttypes.ErrFailedClientConsensusStateVerification, "not equal")
}

return nil
}

// VerifyConnectionState verifies a proof of the connection state of the
// specified connection end stored locally.
func (cs ClientState) VerifyConnectionState(
cdc *codec.Codec,
_ uint64,
prefix commitmentexported.Prefix,
_ commitmentexported.Proof,
connectionID string,
connectionEnd connectionexported.ConnectionI,
_ clientexported.ConsensusState,
) error {
path, err := commitmenttypes.ApplyPrefix(prefix, ibctypes.ConnectionPath(connectionID))
if err != nil {
return err
}

bz := cs.store.Get([]byte(path.String()))
if bz == nil {
return sdkerrors.Wrap(clienttypes.ErrFailedConnectionStateVerification, "not found")
}

var prevConnectionState connectionexported.ConnectionI
cdc.MustUnmarshalBinaryBare(bz, &prevConnectionState)
if connectionEnd != prevConnectionState {
return sdkerrors.Wrap(clienttypes.ErrFailedConnectionStateVerification, "not equal")
}

return nil
}

// VerifyChannelState verifies a proof of the channel state of the specified
// channel end, under the specified port, stored on the local machine.
func (cs ClientState) VerifyChannelState(
cdc *codec.Codec,
_ uint64,
prefix commitmentexported.Prefix,
_ commitmentexported.Proof,
portID,
channelID string,
channel channelexported.ChannelI,
_ clientexported.ConsensusState,
) error {
path, err := commitmenttypes.ApplyPrefix(prefix, ibctypes.ChannelPath(portID, channelID))
if err != nil {
return err
}

bz := cs.store.Get([]byte(path.String()))
if bz == nil {
return sdkerrors.Wrap(clienttypes.ErrFailedChannelStateVerification, "not found")
}

var prevChannelState channelexported.ChannelI
cdc.MustUnmarshalBinaryBare(bz, &prevChannelState)
if channel != prevChannelState {
return sdkerrors.Wrap(clienttypes.ErrFailedChannelStateVerification, "not equal")
}

return nil
}

// VerifyPacketCommitment verifies a proof of an outgoing packet commitment at
// the specified port, specified channel, and specified sequence.
func (cs ClientState) VerifyPacketCommitment(
_ uint64,
prefix commitmentexported.Prefix,
_ commitmentexported.Proof,
portID,
channelID string,
sequence uint64,
commitmentBytes []byte,
_ clientexported.ConsensusState,
) error {
path, err := commitmenttypes.ApplyPrefix(prefix, ibctypes.PacketCommitmentPath(portID, channelID, sequence))
if err != nil {
return err
}

data := cs.store.Get([]byte(path.String()))
if len(data) == 0 {
return sdkerrors.Wrap(clienttypes.ErrFailedPacketCommitmentVerification, "not found")
}

if !bytes.Equal(data, commitmentBytes) {
return sdkerrors.Wrap(clienttypes.ErrFailedPacketCommitmentVerification, "not equal")
}

return nil
}

// VerifyPacketAcknowledgement verifies a proof of an incoming packet
// acknowledgement at the specified port, specified channel, and specified sequence.
func (cs ClientState) VerifyPacketAcknowledgement(
_ uint64,
prefix commitmentexported.Prefix,
_ commitmentexported.Proof,
portID,
channelID string,
sequence uint64,
acknowledgement []byte,
_ clientexported.ConsensusState,
) error {
path, err := commitmenttypes.ApplyPrefix(prefix, ibctypes.PacketAcknowledgementPath(portID, channelID, sequence))
if err != nil {
return err
}

data := cs.store.Get([]byte(path.String()))
if len(data) == 0 {
return sdkerrors.Wrap(clienttypes.ErrFailedPacketAckVerification, "not found")
}

if !bytes.Equal(data, acknowledgement) {
return sdkerrors.Wrap(clienttypes.ErrFailedPacketAckVerification, "not equal")
}

return nil
}

// VerifyPacketAcknowledgementAbsence verifies a proof of the absence of an
// incoming packet acknowledgement at the specified port, specified channel, and
// specified sequence.
func (cs ClientState) VerifyPacketAcknowledgementAbsence(
_ uint64,
prefix commitmentexported.Prefix,
_ commitmentexported.Proof,
portID,
channelID string,
sequence uint64,
_ clientexported.ConsensusState,
) error {
path, err := commitmenttypes.ApplyPrefix(prefix, ibctypes.PacketAcknowledgementPath(portID, channelID, sequence))
if err != nil {
return err
}

data := cs.store.Get([]byte(path.String()))
if data != nil {
return sdkerrors.Wrap(clienttypes.ErrFailedPacketAckAbsenceVerification, "expected no ack absence")
}

return nil
}

// VerifyNextSequenceRecv verifies a proof of the next sequence number to be
// received of the specified channel at the specified port.
func (cs ClientState) VerifyNextSequenceRecv(
_ uint64,
prefix commitmentexported.Prefix,
_ commitmentexported.Proof,
portID,
channelID string,
nextSequenceRecv uint64,
_ clientexported.ConsensusState,
) error {
path, err := commitmenttypes.ApplyPrefix(prefix, ibctypes.NextSequenceRecvPath(portID, channelID))
if err != nil {
return err
}

data := cs.store.Get([]byte(path.String()))
if len(data) == 0 {
return sdkerrors.Wrap(clienttypes.ErrFailedNextSeqRecvVerification, "not found")
}

prevSequenceRecv := binary.BigEndian.Uint64(data)
if prevSequenceRecv != nextSequenceRecv {
return sdkerrors.Wrap(clienttypes.ErrFailedNextSeqRecvVerification, "not equal")
}

return nil
}

// consensusStatePath takes an Identifier and returns a Path under which to
// store the consensus state of a client.
func consensusStatePath(clientID string) string {
return fmt.Sprintf("consensusState/%s", clientID)
}
Loading

0 comments on commit de00a7f

Please sign in to comment.