ibc: modular client messages (#7160)

* ibc: modular client messages

* more updates

* remove client messages

* fixes

* fix tests

* lint

* Apply suggestions from code review

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

* codec.go tests

* cleanup exported

* MsgCreateClient test

* msg tests

* Apply suggestions from code review

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

* fix tests

* proto files update

* errors

* address comments

* spec changes

* add sanity check test cases

* add additional sanity check

* fix expPass

* fix build and remove unnecessary test since validatebasic is checked in the msg

Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com>
Co-authored-by: Aditya Sripal <adityasripal@gmail.com>
Co-authored-by: Colin Axner <colinaxner@berkeley.edu>
This commit is contained in:
Federico Kunze 2020-09-01 12:40:31 +02:00 committed by GitHub
parent 1c9158b746
commit 0fffaf589b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 2113 additions and 986 deletions

View File

@ -6,30 +6,75 @@ option go_package = "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types";
import "gogoproto/gogo.proto";
import "google/protobuf/any.proto";
// IdentifiedClientState defines a client state with additional client identifier field.
// IdentifiedClientState defines a client state with additional client
// identifier field.
message IdentifiedClientState {
// client identifier
string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""];
// client state
google.protobuf.Any client_state = 2 [(gogoproto.moretags) = "yaml:\"client_state\""];
google.protobuf.Any client_state = 2
[(gogoproto.moretags) = "yaml:\"client_state\""];
}
// ClientConsensusStates defines all the stored consensus states for a given client.
// ClientConsensusStates defines all the stored consensus states for a given
// client.
message ClientConsensusStates {
// client identifier
string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""];
// consensus states associated with the client
repeated google.protobuf.Any consensus_states = 2 [(gogoproto.moretags) = "yaml:\"consensus_states\""];
repeated google.protobuf.Any consensus_states = 2
[(gogoproto.moretags) = "yaml:\"consensus_states\""];
}
// MsgCreateClient defines a message to create an IBC client
message MsgCreateClient {
// client unique identifier
string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""];
// light client state
google.protobuf.Any client_state = 2
[(gogoproto.moretags) = "yaml:\"client_state\""];
// consensus state associated with the client that corresponds to a given
// height.
google.protobuf.Any consensus_state = 3
[(gogoproto.moretags) = "yaml:\"consensus_state\""];
// signer address
bytes signer = 4
[(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"];
}
// MsgUpdateClient defines an sdk.Msg to update a IBC client state using
// the given header.
message MsgUpdateClient {
// client unique identifier
string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""];
// header to update the light client
google.protobuf.Any header = 2;
// signer address
bytes signer = 3
[(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"];
}
// MsgSubmitMisbehaviour defines an sdk.Msg type that submits Evidence for
// light client misbehaviour.
message MsgSubmitMisbehaviour {
// client unique identifier
string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""];
// misbehaviour used for freezing the light client
google.protobuf.Any misbehaviour = 2;
// signer address
bytes signer = 3
[(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"];
}
// Height is a monotonically increasing data type
// that can be compared against another Height for the purposes of updating and freezing clients
// that can be compared against another Height for the purposes of updating and
// freezing clients
//
// Normally the EpochHeight is incremented at each height while keeping epoch number the same
// However some consensus algorithms may choose to reset the height in certain conditions
// e.g. hard forks, state-machine breaking changes
// In these cases, the epoch number is incremented so that height continues to be monitonically increasing
// even as the EpochHeight gets reset
// Normally the EpochHeight is incremented at each height while keeping epoch
// number the same However some consensus algorithms may choose to reset the
// height in certain conditions e.g. hard forks, state-machine breaking changes
// In these cases, the epoch number is incremented so that height continues to
// be monitonically increasing even as the EpochHeight gets reset
message Height {
option (gogoproto.goproto_stringer) = false;

View File

@ -117,6 +117,12 @@ var (
// the signer info doesn't match the account's actual sequence number.
ErrWrongSequence = Register(RootCodespace, 32, "incorrect account sequence")
// ErrPackAny defines an error when packing a protobuf message to Any fails.
ErrPackAny = Register(RootCodespace, 33, "failed packing protobuf message to Any")
// ErrUnpackAny defines an error when unpacking a protobuf message from Any fails.
ErrUnpackAny = Register(RootCodespace, 34, "failed unpacking protobuf message from Any")
// ErrPanic is only set when we recover from a panic, so we know to
// redact potentially sensitive system info
ErrPanic = Register(UndefinedCodespace, 111222, "panic")

View File

@ -132,6 +132,9 @@ type ConsensusState interface {
ValidateBasic() error
}
// TypeClientMisbehaviour is the shared evidence misbehaviour type
const TypeClientMisbehaviour string = "client_misbehaviour"
// Misbehaviour defines counterparty misbehaviour for a specific consensus type
type Misbehaviour interface {
ClientType() ClientType
@ -147,39 +150,7 @@ type Misbehaviour interface {
type Header interface {
ClientType() ClientType
GetHeight() uint64
}
// message and evidence types for the IBC client
const (
TypeMsgCreateClient string = "create_client"
TypeMsgUpdateClient string = "update_client"
TypeMsgSubmitClientMisbehaviour string = "submit_client_misbehaviour"
TypeEvidenceClientMisbehaviour string = "client_misbehaviour"
)
// MsgCreateClient defines the msg interface that the
// CreateClient Handler expects
type MsgCreateClient interface {
sdk.Msg
GetClientID() string
GetClientType() string
GetConsensusState() ConsensusState
InitializeClientState() ClientState
}
// MsgUpdateClient defines the msg interface that the
// UpdateClient Handler expects
type MsgUpdateClient interface {
sdk.Msg
GetClientID() string
GetHeader() Header
}
// MsgSubmitMisbehaviour defines the msg interface that the
// SubmitMisbehaviour Handler expects
type MsgSubmitMisbehaviour interface {
sdk.Msg
GetMisbehaviour() Misbehaviour
ValidateBasic() error
}
// Height is a wrapper interface over clienttypes.Height
@ -212,6 +183,8 @@ const (
func (ct ClientType) String() string {
switch ct {
case SoloMachine:
return ClientTypeSoloMachine
case Tendermint:
return ClientTypeTendermint
case Localhost:
@ -247,6 +220,8 @@ func (ct *ClientType) UnmarshalJSON(data []byte) error {
// type. It returns 0 if the type is not found/registered.
func ClientTypeFromString(clientType string) ClientType {
switch clientType {
case ClientTypeSoloMachine:
return SoloMachine
case ClientTypeTendermint:
return Tendermint
case ClientTypeLocalHost:

View File

@ -12,7 +12,9 @@ func TestClientTypeString(t *testing.T) {
name string
clientType ClientType
}{
{"solomachine client", ClientTypeSoloMachine, SoloMachine},
{"tendermint client", ClientTypeTendermint, Tendermint},
{"localhost client", ClientTypeLocalHost, Localhost},
{"empty type", "", 0},
}
@ -30,7 +32,9 @@ func TestClientTypeMarshalJSON(t *testing.T) {
clientType ClientType
expectPass bool
}{
{"tendermint client should have passed", ClientTypeTendermint, Tendermint, true},
{"solomachine client", ClientTypeSoloMachine, SoloMachine, true},
{"tendermint client", ClientTypeTendermint, Tendermint, true},
{"localhost client", ClientTypeLocalHost, Localhost, true},
{"empty type should have failed", "", 0, false},
}

View File

@ -5,37 +5,23 @@ import (
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"
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/keeper"
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
localhosttypes "github.com/cosmos/cosmos-sdk/x/ibc/09-localhost/types"
)
// HandleMsgCreateClient defines the sdk.Handler for MsgCreateClient
func HandleMsgCreateClient(ctx sdk.Context, k keeper.Keeper, msg exported.MsgCreateClient) (*sdk.Result, error) {
var (
consensusHeight uint64
clientState exported.ClientState
)
switch msg.(type) {
// localhost is a special case that must initialize client state
// from context and not from msg
case *localhosttypes.MsgCreateClient:
selfHeight := types.NewHeight(0, uint64(ctx.BlockHeight()))
clientState = localhosttypes.NewClientState(ctx.ChainID(), selfHeight)
// Localhost consensus height is chain's blockheight
consensusHeight = uint64(ctx.BlockHeight())
default:
clientState = msg.InitializeClientState()
if consState := msg.GetConsensusState(); consState != nil {
consensusHeight = consState.GetHeight()
}
func HandleMsgCreateClient(ctx sdk.Context, k keeper.Keeper, msg *types.MsgCreateClient) (*sdk.Result, error) {
clientState, err := types.UnpackClientState(msg.ClientState)
if err != nil {
return nil, err
}
_, err := k.CreateClient(
ctx, msg.GetClientID(), clientState, msg.GetConsensusState(),
)
consensusState, err := types.UnpackConsensusState(msg.ConsensusState)
if err != nil {
return nil, err
}
_, err = k.CreateClient(ctx, msg.ClientId, clientState, consensusState)
if err != nil {
return nil, err
}
@ -43,9 +29,9 @@ func HandleMsgCreateClient(ctx sdk.Context, k keeper.Keeper, msg exported.MsgCre
ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
types.EventTypeCreateClient,
sdk.NewAttribute(types.AttributeKeyClientID, msg.GetClientID()),
sdk.NewAttribute(types.AttributeKeyClientType, msg.GetClientType()),
sdk.NewAttribute(types.AttributeKeyConsensusHeight, fmt.Sprintf("%d", consensusHeight)),
sdk.NewAttribute(types.AttributeKeyClientID, msg.ClientId),
sdk.NewAttribute(types.AttributeKeyClientType, clientState.ClientType().String()),
sdk.NewAttribute(types.AttributeKeyConsensusHeight, fmt.Sprintf("%d", consensusState.GetHeight())),
),
sdk.NewEvent(
sdk.EventTypeMessage,
@ -59,8 +45,13 @@ func HandleMsgCreateClient(ctx sdk.Context, k keeper.Keeper, msg exported.MsgCre
}
// HandleMsgUpdateClient defines the sdk.Handler for MsgUpdateClient
func HandleMsgUpdateClient(ctx sdk.Context, k keeper.Keeper, msg exported.MsgUpdateClient) (*sdk.Result, error) {
_, err := k.UpdateClient(ctx, msg.GetClientID(), msg.GetHeader())
func HandleMsgUpdateClient(ctx sdk.Context, k keeper.Keeper, msg *types.MsgUpdateClient) (*sdk.Result, error) {
header, err := types.UnpackHeader(msg.Header)
if err != nil {
return nil, err
}
_, err = k.UpdateClient(ctx, msg.ClientId, header)
if err != nil {
return nil, err
}
@ -79,13 +70,9 @@ func HandleMsgUpdateClient(ctx sdk.Context, k keeper.Keeper, msg exported.MsgUpd
// HandleMsgSubmitMisbehaviour defines the Evidence module handler for submitting a
// light client misbehaviour.
func HandleMsgSubmitMisbehaviour(ctx sdk.Context, k keeper.Keeper, msg exported.MsgSubmitMisbehaviour) (*sdk.Result, error) {
misbehaviour := msg.GetMisbehaviour()
if misbehaviour == nil {
return nil, sdkerrors.Wrapf(types.ErrInvalidMisbehaviour, "misbehaviour is nil")
}
if err := misbehaviour.ValidateBasic(); err != nil {
func HandleMsgSubmitMisbehaviour(ctx sdk.Context, k keeper.Keeper, msg *types.MsgSubmitMisbehaviour) (*sdk.Result, error) {
misbehaviour, err := types.UnpackMisbehaviour(msg.Misbehaviour)
if err != nil {
return nil, err
}
@ -96,7 +83,7 @@ func HandleMsgSubmitMisbehaviour(ctx sdk.Context, k keeper.Keeper, msg exported.
ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeSubmitMisbehaviour,
sdk.NewAttribute(types.AttributeKeyClientID, misbehaviour.GetClientID()),
sdk.NewAttribute(types.AttributeKeyClientID, msg.ClientId),
sdk.NewAttribute(types.AttributeKeyClientType, misbehaviour.ClientType().String()),
sdk.NewAttribute(types.AttributeKeyConsensusHeight, fmt.Sprintf("%d", misbehaviour.GetHeight())),
),

View File

@ -1,47 +0,0 @@
package client_test
import (
client "github.com/cosmos/cosmos-sdk/x/ibc/02-client"
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
localhosttypes "github.com/cosmos/cosmos-sdk/x/ibc/09-localhost/types"
)
func (suite *ClientTestSuite) TestHandleCreateClientLocalHost() {
cases := []struct {
name string
clientID string
msg exported.MsgCreateClient
expPass bool
}{
{
"tendermint client",
"gaiamainnet",
suite.chainA.ConstructMsgCreateClient(suite.chainB, "gaiamainnet"),
true,
},
{
"client already exists",
exported.ClientTypeLocalHost,
&localhosttypes.MsgCreateClient{suite.chainA.SenderAccount.GetAddress()},
false,
},
}
for _, tc := range cases {
_, err := client.HandleMsgCreateClient(
suite.chainA.GetContext(),
suite.chainA.App.IBCKeeper.ClientKeeper,
tc.msg,
)
if tc.expPass {
suite.Require().NoError(err, "expected test case %s to pass, got error %v", tc.name, err)
clientState, ok := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientState(suite.chainA.GetContext(), tc.clientID)
suite.Require().True(ok, "could not retrieve clientState")
suite.Require().NotNil(clientState, "clientstate is nil")
} else {
suite.Require().Error(err, "invalid test case %s passed", tc.name)
}
}
}

View File

@ -102,12 +102,8 @@ func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, misbehaviour ex
if !found {
return sdkerrors.Wrapf(types.ErrClientNotFound, "cannot check misbehaviour for client with ID %s", misbehaviour.GetClientID())
}
if err := misbehaviour.ValidateBasic(); err != nil {
return sdkerrors.Wrap(err, "IBC misbehaviour failed validate basic")
}
clientState, err := clientState.CheckMisbehaviourAndUpdateState(ctx, k.cdc, k.ClientStore(ctx, misbehaviour.GetClientID()), misbehaviour)
if err != nil {
return err
}

View File

@ -346,23 +346,6 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
},
true,
},
{
"misbehaviour fails validatebasic",
&ibctmtypes.Misbehaviour{
Header1: ibctmtypes.CreateTestHeader(testChainID, height+1, height, altTime, bothValSet, bothValSet, bothSigners),
Header2: ibctmtypes.CreateTestHeader(testChainID, height, height, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners),
ChainId: testChainID,
ClientId: testClientID,
},
func() error {
suite.consensusState.NextValidatorsHash = bothValsHash
clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs())
_, err := suite.keeper.CreateClient(suite.ctx, testClientID, clientState, suite.consensusState)
return err
},
false,
},
{
"trusted ConsensusState1 not found",
&ibctmtypes.Misbehaviour{

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,12 @@
package types
import (
"fmt"
proto "github.com/gogo/protobuf/proto"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/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"
)
@ -24,6 +24,16 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) {
"cosmos_sdk.ibc.v1.client.Header",
(*exported.Header)(nil),
)
registry.RegisterInterface(
"cosmos_sdk.ibc.v1.client.Misbehaviour",
(*exported.Misbehaviour)(nil),
)
registry.RegisterImplementations(
(*sdk.Msg)(nil),
&MsgCreateClient{},
&MsgUpdateClient{},
&MsgSubmitMisbehaviour{},
)
}
var (
@ -41,33 +51,27 @@ var (
func PackClientState(clientState exported.ClientState) (*codectypes.Any, error) {
msg, ok := clientState.(proto.Message)
if !ok {
return nil, fmt.Errorf("cannot proto marshal %T", clientState)
return nil, sdkerrors.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", clientState)
}
anyClientState, err := codectypes.NewAnyWithValue(msg)
if err != nil {
return nil, err
return nil, sdkerrors.Wrap(sdkerrors.ErrPackAny, err.Error())
}
return anyClientState, nil
}
// MustPackClientState calls PackClientState and panics on error.
func MustPackClientState(clientState exported.ClientState) *codectypes.Any {
anyClientState, err := PackClientState(clientState)
if err != nil {
panic(err)
}
return anyClientState
}
// UnpackClientState unpacks an Any into a ClientState. It returns an error if the
// client state can't be unpacked into a ClientState.
func UnpackClientState(any *codectypes.Any) (exported.ClientState, error) {
if any == nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrUnpackAny, "protobuf Any message cannot be nil")
}
clientState, ok := any.GetCachedValue().(exported.ClientState)
if !ok {
return nil, fmt.Errorf("cannot unpack Any into ClientState %T", any)
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnpackAny, "cannot unpack Any into ClientState %T", any)
}
return clientState, nil
@ -79,12 +83,12 @@ func UnpackClientState(any *codectypes.Any) (exported.ClientState, error) {
func PackConsensusState(consensusState exported.ConsensusState) (*codectypes.Any, error) {
msg, ok := consensusState.(proto.Message)
if !ok {
return nil, fmt.Errorf("cannot proto marshal %T", consensusState)
return nil, sdkerrors.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", consensusState)
}
anyConsensusState, err := codectypes.NewAnyWithValue(msg)
if err != nil {
return nil, err
return nil, sdkerrors.Wrap(sdkerrors.ErrPackAny, err.Error())
}
return anyConsensusState, nil
@ -103,10 +107,78 @@ func MustPackConsensusState(consensusState exported.ConsensusState) *codectypes.
// UnpackConsensusState unpacks an Any into a ConsensusState. It returns an error if the
// consensus state can't be unpacked into a ConsensusState.
func UnpackConsensusState(any *codectypes.Any) (exported.ConsensusState, error) {
if any == nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrUnpackAny, "protobuf Any message cannot be nil")
}
consensusState, ok := any.GetCachedValue().(exported.ConsensusState)
if !ok {
return nil, fmt.Errorf("cannot unpack Any into ConsensusState %T", any)
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnpackAny, "cannot unpack Any into ConsensusState %T", any)
}
return consensusState, nil
}
// PackHeader constructs a new Any packed with the given header value. It returns
// an error if the header can't be casted to a protobuf message or if the concrete
// implemention is not registered to the protobuf codec.
func PackHeader(header exported.Header) (*codectypes.Any, error) {
msg, ok := header.(proto.Message)
if !ok {
return nil, sdkerrors.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", header)
}
anyHeader, err := codectypes.NewAnyWithValue(msg)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrPackAny, err.Error())
}
return anyHeader, nil
}
// UnpackHeader unpacks an Any into a Header. It returns an error if the
// consensus state can't be unpacked into a Header.
func UnpackHeader(any *codectypes.Any) (exported.Header, error) {
if any == nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrUnpackAny, "protobuf Any message cannot be nil")
}
header, ok := any.GetCachedValue().(exported.Header)
if !ok {
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnpackAny, "cannot unpack Any into Header %T", any)
}
return header, nil
}
// PackMisbehaviour constructs a new Any packed with the given misbehaviour value. It returns
// an error if the misbehaviour can't be casted to a protobuf message or if the concrete
// implemention is not registered to the protobuf codec.
func PackMisbehaviour(misbehaviour exported.Misbehaviour) (*codectypes.Any, error) {
msg, ok := misbehaviour.(proto.Message)
if !ok {
return nil, sdkerrors.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", misbehaviour)
}
anyMisbhaviour, err := codectypes.NewAnyWithValue(msg)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrPackAny, err.Error())
}
return anyMisbhaviour, nil
}
// UnpackMisbehaviour unpacks an Any into a Misbehaviour. It returns an error if the
// Any can't be unpacked into a Misbehaviour.
func UnpackMisbehaviour(any *codectypes.Any) (exported.Misbehaviour, error) {
if any == nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrUnpackAny, "protobuf Any message cannot be nil")
}
misbehaviour, ok := any.GetCachedValue().(exported.Misbehaviour)
if !ok {
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnpackAny, "cannot unpack Any into Misbehaviour %T", any)
}
return misbehaviour, nil
}

View File

@ -2,41 +2,218 @@ package types_test
import (
"testing"
"time"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"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"
localhosttypes "github.com/cosmos/cosmos-sdk/x/ibc/09-localhost/types"
commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types"
ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing"
"github.com/stretchr/testify/require"
)
type caseAny struct {
name string
any *codectypes.Any
expPass bool
}
func TestPackClientState(t *testing.T) {
clientState := localhosttypes.NewClientState(chainID, clientHeight)
testCases := []struct {
name string
clientState exported.ClientState
expPass bool
}{
{
"solo machine client",
ibctesting.NewSolomachine(t, "solomachine").ClientState(),
true,
},
{
"tendermint client",
ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs()),
true,
},
{
"localhost client",
localhosttypes.NewClientState(chainID, clientHeight),
true,
},
{
"nil",
nil,
false,
},
}
clientAny, err := types.PackClientState(clientState)
require.NoError(t, err, "pack clientstate should not return error")
testCasesAny := []caseAny{}
cs, err := types.UnpackClientState(clientAny)
require.NoError(t, err, "unpack clientstate should not return error")
for _, tc := range testCases {
clientAny, err := types.PackClientState(tc.clientState)
if tc.expPass {
require.NoError(t, err, tc.name)
} else {
require.Error(t, err, tc.name)
}
require.Equal(t, clientState, cs, "client states are not equal after packing and unpacking")
testCasesAny = append(testCasesAny, caseAny{tc.name, clientAny, tc.expPass})
}
_, err = types.PackClientState(nil)
require.Error(t, err, "did not error after packing nil")
for i, tc := range testCasesAny {
cs, err := types.UnpackClientState(tc.any)
if tc.expPass {
require.NoError(t, err, tc.name)
require.Equal(t, testCases[i].clientState, cs, tc.name)
} else {
require.Error(t, err, tc.name)
}
}
}
func TestPackConsensusState(t *testing.T) {
consensusState := ibctmtypes.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte("root")), clientHeight, []byte("nextvalshash"))
chain := ibctesting.NewTestChain(t, "cosmoshub")
consensusAny, err := types.PackConsensusState(consensusState)
require.NoError(t, err, "pack consensusstate should not return error")
testCases := []struct {
name string
consensusState exported.ConsensusState
expPass bool
}{
{
"solo machine consensus",
ibctesting.NewSolomachine(t, "solomachine").ConsensusState(),
true,
},
{
"tendermint consensus",
chain.LastHeader.ConsensusState(),
true,
},
{
"nil",
nil,
false,
},
}
cs, err := types.UnpackConsensusState(consensusAny)
require.NoError(t, err, "unpack consensusstate should not return error")
testCasesAny := []caseAny{}
require.Equal(t, consensusState, cs, "consensus states are not equal after packing and unpacking")
for _, tc := range testCases {
clientAny, err := types.PackConsensusState(tc.consensusState)
if tc.expPass {
require.NoError(t, err, tc.name)
} else {
require.Error(t, err, tc.name)
}
testCasesAny = append(testCasesAny, caseAny{tc.name, clientAny, tc.expPass})
}
_, err = types.PackConsensusState(nil)
require.Error(t, err, "did not error after packing nil")
for i, tc := range testCasesAny {
cs, err := types.UnpackConsensusState(tc.any)
if tc.expPass {
require.NoError(t, err, tc.name)
require.Equal(t, testCases[i].consensusState, cs, tc.name)
} else {
require.Error(t, err, tc.name)
}
}
}
func TestPackHeader(t *testing.T) {
chain := ibctesting.NewTestChain(t, "cosmoshub")
testCases := []struct {
name string
header exported.Header
expPass bool
}{
{
"solo machine header",
ibctesting.NewSolomachine(t, "solomachine").CreateHeader(),
true,
},
{
"tendermint header",
chain.LastHeader,
true,
},
{
"nil",
nil,
false,
},
}
testCasesAny := []caseAny{}
for _, tc := range testCases {
clientAny, err := types.PackHeader(tc.header)
if tc.expPass {
require.NoError(t, err, tc.name)
} else {
require.Error(t, err, tc.name)
}
testCasesAny = append(testCasesAny, caseAny{tc.name, clientAny, tc.expPass})
}
for i, tc := range testCasesAny {
cs, err := types.UnpackHeader(tc.any)
if tc.expPass {
require.NoError(t, err, tc.name)
require.Equal(t, testCases[i].header, cs, tc.name)
} else {
require.Error(t, err, tc.name)
}
}
}
func TestPackMisbehaviour(t *testing.T) {
chain := ibctesting.NewTestChain(t, "cosmoshub")
testCases := []struct {
name string
misbehaviour exported.Misbehaviour
expPass bool
}{
{
"solo machine misbehaviour",
ibctesting.NewSolomachine(t, "solomachine").CreateMisbehaviour(),
true,
},
{
"tendermint misbehaviour",
ibctmtypes.NewMisbehaviour("tendermint", chain.ChainID, chain.LastHeader, chain.LastHeader),
true,
},
{
"nil",
nil,
false,
},
}
testCasesAny := []caseAny{}
for _, tc := range testCases {
clientAny, err := types.PackMisbehaviour(tc.misbehaviour)
if tc.expPass {
require.NoError(t, err, tc.name)
} else {
require.Error(t, err, tc.name)
}
testCasesAny = append(testCasesAny, caseAny{tc.name, clientAny, tc.expPass})
}
for i, tc := range testCasesAny {
cs, err := types.UnpackMisbehaviour(tc.any)
if tc.expPass {
require.NoError(t, err, tc.name)
require.Equal(t, testCases[i].misbehaviour, cs, tc.name)
} else {
require.Error(t, err, tc.name)
}
}
}

View File

@ -0,0 +1,197 @@
package types
import (
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"
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
)
// message types for the IBC client
const (
TypeMsgCreateClient string = "create_client"
TypeMsgUpdateClient string = "update_client"
TypeMsgSubmitMisbehaviour string = "submit_misbehaviour"
)
var (
_ sdk.Msg = &MsgCreateClient{}
_ sdk.Msg = &MsgUpdateClient{}
_ sdk.Msg = &MsgSubmitMisbehaviour{}
)
// NewMsgCreateClient creates a new MsgCreateClient instance
func NewMsgCreateClient(
id string, clientState exported.ClientState, consensusState exported.ConsensusState, signer sdk.AccAddress,
) (*MsgCreateClient, error) {
anyClientState, err := PackClientState(clientState)
if err != nil {
return nil, err
}
anyConsensusState, err := PackConsensusState(consensusState)
if err != nil {
return nil, err
}
return &MsgCreateClient{
ClientId: id,
ClientState: anyClientState,
ConsensusState: anyConsensusState,
Signer: signer,
}, nil
}
// Route implements sdk.Msg
func (msg MsgCreateClient) Route() string {
return host.RouterKey
}
// Type implements sdk.Msg
func (msg MsgCreateClient) Type() string {
return TypeMsgCreateClient
}
// ValidateBasic implements sdk.Msg
func (msg MsgCreateClient) ValidateBasic() error {
if msg.Signer.Empty() {
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "signer address cannot be empty")
}
clientState, err := UnpackClientState(msg.ClientState)
if err != nil {
return err
}
if err := clientState.Validate(); err != nil {
return err
}
if clientState.ClientType() == exported.Localhost || msg.ClientId == exported.ClientTypeLocalHost {
return sdkerrors.Wrap(ErrInvalidClient, "localhost client can only be created on chain initialization")
}
consensusState, err := UnpackConsensusState(msg.ConsensusState)
if err != nil {
return err
}
if err := consensusState.ValidateBasic(); err != nil {
return err
}
return host.ClientIdentifierValidator(msg.ClientId)
}
// GetSignBytes implements sdk.Msg
func (msg MsgCreateClient) GetSignBytes() []byte {
return sdk.MustSortJSON(SubModuleCdc.MustMarshalJSON(&msg))
}
// GetSigners implements sdk.Msg
func (msg MsgCreateClient) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{msg.Signer}
}
// NewMsgUpdateClient creates a new MsgUpdateClient instance
func NewMsgUpdateClient(id string, header exported.Header, signer sdk.AccAddress) (*MsgUpdateClient, error) {
anyHeader, err := PackHeader(header)
if err != nil {
return nil, err
}
return &MsgUpdateClient{
ClientId: id,
Header: anyHeader,
Signer: signer,
}, nil
}
// Route implements sdk.Msg
func (msg MsgUpdateClient) Route() string {
return host.RouterKey
}
// Type implements sdk.Msg
func (msg MsgUpdateClient) Type() string {
return TypeMsgUpdateClient
}
// ValidateBasic implements sdk.Msg
func (msg MsgUpdateClient) ValidateBasic() error {
if msg.Signer.Empty() {
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "signer address cannot be empty")
}
header, err := UnpackHeader(msg.Header)
if err != nil {
return err
}
if err := header.ValidateBasic(); err != nil {
return err
}
if msg.ClientId == exported.ClientTypeLocalHost {
return sdkerrors.Wrap(ErrInvalidClient, "localhost client is only updated on ABCI BeginBlock")
}
return host.ClientIdentifierValidator(msg.ClientId)
}
// GetSignBytes implements sdk.Msg
func (msg MsgUpdateClient) GetSignBytes() []byte {
return sdk.MustSortJSON(SubModuleCdc.MustMarshalJSON(&msg))
}
// GetSigners implements sdk.Msg
func (msg MsgUpdateClient) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{msg.Signer}
}
// NewMsgSubmitMisbehaviour creates a new MsgSubmitMisbehaviour instance.
func NewMsgSubmitMisbehaviour(clientID string, misbehaviour exported.Misbehaviour, signer sdk.AccAddress) (*MsgSubmitMisbehaviour, error) {
anyMisbehaviour, err := PackMisbehaviour(misbehaviour)
if err != nil {
return nil, err
}
return &MsgSubmitMisbehaviour{
ClientId: clientID,
Misbehaviour: anyMisbehaviour,
Signer: signer,
}, nil
}
// Route returns the MsgSubmitClientMisbehaviour's route.
func (msg MsgSubmitMisbehaviour) Route() string { return host.RouterKey }
// Type returns the MsgSubmitMisbehaviour's type.
func (msg MsgSubmitMisbehaviour) Type() string {
return TypeMsgSubmitMisbehaviour
}
// ValidateBasic performs basic (non-state-dependant) validation on a MsgSubmitMisbehaviour.
func (msg MsgSubmitMisbehaviour) ValidateBasic() error {
if msg.Signer.Empty() {
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "signer address cannot be empty")
}
misbehaviour, err := UnpackMisbehaviour(msg.Misbehaviour)
if err != nil {
return err
}
if err := misbehaviour.ValidateBasic(); err != nil {
return err
}
if misbehaviour.GetClientID() != msg.ClientId {
return sdkerrors.Wrapf(
ErrInvalidMisbehaviour,
"misbehaviour client-id doesn't match client-id from message (%s ≠ %s)",
misbehaviour.GetClientID(), msg.ClientId,
)
}
return host.ClientIdentifierValidator(msg.ClientId)
}
// GetSignBytes returns the raw bytes a signer is expected to sign when submitting
// a MsgSubmitMisbehaviour message.
func (msg MsgSubmitMisbehaviour) GetSignBytes() []byte {
return sdk.MustSortJSON(SubModuleCdc.MustMarshalJSON(&msg))
}
// GetSigners returns the single expected signer for a MsgSubmitMisbehaviour.
func (msg MsgSubmitMisbehaviour) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{msg.Signer}
}

View File

@ -0,0 +1,316 @@
package types_test
import (
"testing"
"time"
"github.com/stretchr/testify/suite"
"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"
localhosttypes "github.com/cosmos/cosmos-sdk/x/ibc/09-localhost/types"
commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types"
solomachinetypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/solomachine/types"
ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing"
)
type TypesTestSuite struct {
suite.Suite
chain *ibctesting.TestChain
}
func (suite *TypesTestSuite) SetupTest() {
coordinator := ibctesting.NewCoordinator(suite.T(), 1)
suite.chain = coordinator.GetChain(ibctesting.GetChainID(0))
}
func TestTypesTestSuite(t *testing.T) {
suite.Run(t, new(TypesTestSuite))
}
func (suite *TypesTestSuite) TestMsgCreateClient_ValidateBasic() {
var (
msg = &types.MsgCreateClient{}
err error
)
cases := []struct {
name string
malleate func()
expPass bool
}{
{
"invalid client-id",
func() {
msg.ClientId = ""
},
false,
},
{
"valid - tendermint client",
func() {
tendermintClient := ibctmtypes.NewClientState(suite.chain.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs())
msg, err = types.NewMsgCreateClient("tendermint", tendermintClient, suite.chain.CreateTMClientHeader().ConsensusState(), suite.chain.SenderAccount.GetAddress())
suite.Require().NoError(err)
},
true,
},
{
"invalid tendermint client",
func() {
msg, err = types.NewMsgCreateClient("tendermint", &ibctmtypes.ClientState{}, suite.chain.CreateTMClientHeader().ConsensusState(), suite.chain.SenderAccount.GetAddress())
suite.Require().NoError(err)
},
false,
},
{
"failed to unpack client",
func() {
msg.ClientState = nil
},
false,
},
{
"failed to unpack consensus state",
func() {
tendermintClient := ibctmtypes.NewClientState(suite.chain.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs())
msg, err = types.NewMsgCreateClient("tendermint", tendermintClient, suite.chain.CreateTMClientHeader().ConsensusState(), suite.chain.SenderAccount.GetAddress())
suite.Require().NoError(err)
msg.ConsensusState = nil
},
false,
},
{
"invalid signer",
func() {
msg.Signer = nil
},
false,
},
{
"valid - solomachine client",
func() {
soloMachine := ibctesting.NewSolomachine(suite.T(), "solomachine")
msg, err = types.NewMsgCreateClient(soloMachine.ClientID, soloMachine.ClientState(), soloMachine.ConsensusState(), suite.chain.SenderAccount.GetAddress())
suite.Require().NoError(err)
},
true,
},
{
"invalid solomachine client",
func() {
soloMachine := ibctesting.NewSolomachine(suite.T(), "solomachine")
msg, err = types.NewMsgCreateClient(soloMachine.ClientID, &solomachinetypes.ClientState{}, soloMachine.ConsensusState(), suite.chain.SenderAccount.GetAddress())
suite.Require().NoError(err)
},
false,
},
{
"invalid solomachine consensus state",
func() {
soloMachine := ibctesting.NewSolomachine(suite.T(), "solomachine")
msg, err = types.NewMsgCreateClient(soloMachine.ClientID, soloMachine.ClientState(), &solomachinetypes.ConsensusState{}, suite.chain.SenderAccount.GetAddress())
suite.Require().NoError(err)
},
false,
},
{
"unsupported - localhost client",
func() {
localhostClient := localhosttypes.NewClientState(suite.chain.ChainID, types.NewHeight(0, uint64(suite.chain.LastHeader.Header.Height)))
msg, err = types.NewMsgCreateClient("localhost", localhostClient, suite.chain.LastHeader.ConsensusState(), suite.chain.SenderAccount.GetAddress())
suite.Require().NoError(err)
},
false,
},
}
for _, tc := range cases {
tc.malleate()
err = msg.ValidateBasic()
if tc.expPass {
suite.Require().NoError(err, tc.name)
} else {
suite.Require().Error(err, tc.name)
}
}
}
func (suite *TypesTestSuite) TestMsgUpdateClient_ValidateBasic() {
var (
msg = &types.MsgUpdateClient{}
err error
)
cases := []struct {
name string
malleate func()
expPass bool
}{
{
"invalid client-id",
func() {
msg.ClientId = ""
},
false,
},
{
"valid - tendermint header",
func() {
msg, err = types.NewMsgUpdateClient("tendermint", suite.chain.CreateTMClientHeader(), suite.chain.SenderAccount.GetAddress())
suite.Require().NoError(err)
},
true,
},
{
"invalid tendermint header",
func() {
msg, err = types.NewMsgUpdateClient("tendermint", &ibctmtypes.Header{}, suite.chain.SenderAccount.GetAddress())
suite.Require().NoError(err)
},
false,
},
{
"failed to unpack header",
func() {
msg.Header = nil
},
false,
},
{
"invalid signer",
func() {
msg.Signer = nil
},
false,
},
{
"valid - solomachine header",
func() {
soloMachine := ibctesting.NewSolomachine(suite.T(), "solomachine")
msg, err = types.NewMsgUpdateClient(soloMachine.ClientID, soloMachine.CreateHeader(), suite.chain.SenderAccount.GetAddress())
suite.Require().NoError(err)
},
true,
},
{
"invalid solomachine header",
func() {
msg, err = types.NewMsgUpdateClient("solomachine", &solomachinetypes.Header{}, suite.chain.SenderAccount.GetAddress())
suite.Require().NoError(err)
},
false,
},
{
"unsupported - localhost",
func() {
msg, err = types.NewMsgUpdateClient(exported.ClientTypeLocalHost, suite.chain.CreateTMClientHeader(), suite.chain.SenderAccount.GetAddress())
suite.Require().NoError(err)
},
false,
},
}
for _, tc := range cases {
tc.malleate()
err = msg.ValidateBasic()
if tc.expPass {
suite.Require().NoError(err, tc.name)
} else {
suite.Require().Error(err, tc.name)
}
}
}
func (suite *TypesTestSuite) TestMsgSubmitMisbehaviour_ValidateBasic() {
var (
msg = &types.MsgSubmitMisbehaviour{}
err error
)
cases := []struct {
name string
malleate func()
expPass bool
}{
{
"invalid client-id",
func() {
msg.ClientId = ""
},
false,
},
{
"valid - tendermint misbehaviour",
func() {
header1 := ibctmtypes.CreateTestHeader(suite.chain.ChainID, suite.chain.CurrentHeader.Height, suite.chain.CurrentHeader.Height-1, suite.chain.CurrentHeader.Time, suite.chain.Vals, suite.chain.Vals, suite.chain.Signers)
header2 := ibctmtypes.CreateTestHeader(suite.chain.ChainID, suite.chain.CurrentHeader.Height, suite.chain.CurrentHeader.Height-1, suite.chain.CurrentHeader.Time.Add(time.Minute), suite.chain.Vals, suite.chain.Vals, suite.chain.Signers)
misbehaviour := ibctmtypes.NewMisbehaviour("tendermint", suite.chain.ChainID, header1, header2)
msg, err = types.NewMsgSubmitMisbehaviour("tendermint", misbehaviour, suite.chain.SenderAccount.GetAddress())
suite.Require().NoError(err)
},
true,
},
{
"invalid tendermint misbehaviour",
func() {
msg, err = types.NewMsgSubmitMisbehaviour("tendermint", &ibctmtypes.Misbehaviour{}, suite.chain.SenderAccount.GetAddress())
suite.Require().NoError(err)
},
false,
},
{
"failed to unpack misbehaviourt",
func() {
msg.Misbehaviour = nil
},
false,
},
{
"invalid signer",
func() {
msg.Signer = nil
},
false,
},
{
"valid - solomachine misbehaviour",
func() {
soloMachine := ibctesting.NewSolomachine(suite.T(), "solomachine")
msg, err = types.NewMsgSubmitMisbehaviour(soloMachine.ClientID, soloMachine.CreateMisbehaviour(), suite.chain.SenderAccount.GetAddress())
suite.Require().NoError(err)
},
true,
},
{
"invalid solomachine misbehaviour",
func() {
msg, err = types.NewMsgSubmitMisbehaviour("solomachine", &solomachinetypes.Misbehaviour{}, suite.chain.SenderAccount.GetAddress())
suite.Require().NoError(err)
},
false,
},
{
"client-id mismatch",
func() {
soloMachineMisbehaviour := ibctesting.NewSolomachine(suite.T(), "solomachine").CreateMisbehaviour()
msg, err = types.NewMsgSubmitMisbehaviour("external", soloMachineMisbehaviour, suite.chain.SenderAccount.GetAddress())
suite.Require().NoError(err)
},
false,
},
}
for _, tc := range cases {
tc.malleate()
err = msg.ValidateBasic()
if tc.expPass {
suite.Require().NoError(err, tc.name)
} else {
suite.Require().Error(err, tc.name)
}
}
}

View File

@ -17,6 +17,7 @@ import (
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/version"
clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
"github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types"
commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types"
)
@ -110,10 +111,24 @@ func NewCreateClientCmd() *cobra.Command {
}
}
msg := types.NewMsgCreateClient(
clientID, header, trustLevel, trustingPeriod, ubdPeriod, maxClockDrift, specs, clientCtx.GetFromAddress(),
// validate header
if err := header.ValidateBasic(); err != nil {
return err
}
clientState := types.NewClientState(
header.GetHeader().GetChainID(), trustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(0, header.GetHeight()), specs,
)
consensusState := header.ConsensusState()
msg, err := clienttypes.NewMsgCreateClient(
clientID, clientState, consensusState, clientCtx.GetFromAddress(),
)
if err != nil {
return err
}
if err := msg.ValidateBasic(); err != nil {
return err
}
@ -164,7 +179,11 @@ func NewUpdateClientCmd() *cobra.Command {
}
}
msg := types.NewMsgUpdateClient(clientID, header, clientCtx.GetFromAddress())
msg, err := clienttypes.NewMsgUpdateClient(clientID, header, clientCtx.GetFromAddress())
if err != nil {
return err
}
if err := msg.ValidateBasic(); err != nil {
return err
}
@ -212,7 +231,11 @@ func NewSubmitMisbehaviourCmd() *cobra.Command {
}
}
msg := types.NewMsgSubmitClientMisbehaviour(m, clientCtx.GetFromAddress())
msg, err := clienttypes.NewMsgSubmitMisbehaviour(m.ClientId, m, clientCtx.GetFromAddress())
if err != nil {
return err
}
if err := msg.ValidateBasic(); err != nil {
return err
}

View File

@ -21,6 +21,10 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) {
(*clientexported.Misbehaviour)(nil),
&Misbehaviour{},
)
registry.RegisterImplementations(
(*clientexported.Misbehaviour)(nil),
&Misbehaviour{},
)
}
var (

View File

@ -55,7 +55,7 @@ func (h Header) GetTime() time.Time {
// that validatorsets are not nil.
// NOTE: TrustedHeight and TrustedValidators may be empty when creating client
// with MsgCreateClient
func (h Header) ValidateBasic(chainID string) error {
func (h Header) ValidateBasic() error {
if h.SignedHeader == nil {
return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "tendermint signed header cannot be nil")
}
@ -66,7 +66,7 @@ func (h Header) ValidateBasic(chainID string) error {
if err != nil {
return sdkerrors.Wrap(err, "header is not a tendermint header")
}
if err := tmSignedHeader.ValidateBasic(chainID); err != nil {
if err := tmSignedHeader.ValidateBasic(h.Header.GetChainID()); err != nil {
return sdkerrors.Wrap(err, "header failed basic validation")
}

View File

@ -3,6 +3,8 @@ package types_test
import (
"time"
tmprotocrypto "github.com/tendermint/tendermint/proto/tendermint/crypto"
clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
"github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types"
@ -26,8 +28,7 @@ func (suite *TendermintTestSuite) TestGetTime() {
func (suite *TendermintTestSuite) TestHeaderValidateBasic() {
var (
header *types.Header
chainID string
header *types.Header
)
testCases := []struct {
name string
@ -41,9 +42,12 @@ func (suite *TendermintTestSuite) TestHeaderValidateBasic() {
{"signed header is nil", func() {
header.SignedHeader = nil
}, false},
{"SignedHeaderFromProto failed", func() {
header.SignedHeader.Commit.Height = -1
}, false},
{"signed header failed tendermint ValidateBasic", func() {
header = suite.chainA.LastHeader
chainID = "chainid"
header.SignedHeader.Commit = nil
}, false},
{"trusted height is greater than header height", func() {
header.TrustedHeight = clienttypes.NewHeight(0, header.GetHeight()+1)
@ -51,6 +55,9 @@ func (suite *TendermintTestSuite) TestHeaderValidateBasic() {
{"validator set nil", func() {
header.ValidatorSet = nil
}, false},
{"ValidatorSetFromProto failed", func() {
header.ValidatorSet.Validators[0].PubKey = tmprotocrypto.PublicKey{}
}, false},
{"header validator hash does not equal hash of validator set", func() {
// use chainB's randomly generated validator set
header.ValidatorSet = suite.chainB.LastHeader.ValidatorSet
@ -65,12 +72,11 @@ func (suite *TendermintTestSuite) TestHeaderValidateBasic() {
suite.Run(tc.name, func() {
suite.SetupTest()
chainID = suite.chainA.ChainID // must be explicitly changed in malleate
header = suite.chainA.LastHeader // must be explicitly changed in malleate
tc.malleate()
err := header.ValidateBasic(chainID)
err := header.ValidateBasic()
if tc.expPass {
suite.Require().NoError(err)

View File

@ -91,13 +91,13 @@ func (misbehaviour Misbehaviour) ValidateBasic() error {
}
// ValidateBasic on both validators
if err := misbehaviour.Header1.ValidateBasic(misbehaviour.ChainId); err != nil {
if err := misbehaviour.Header1.ValidateBasic(); err != nil {
return sdkerrors.Wrap(
clienttypes.ErrInvalidMisbehaviour,
sdkerrors.Wrap(err, "header 1 failed validation").Error(),
)
}
if err := misbehaviour.Header2.ValidateBasic(misbehaviour.ChainId); err != nil {
if err := misbehaviour.Header2.ValidateBasic(); err != nil {
return sdkerrors.Wrap(
clienttypes.ErrInvalidMisbehaviour,
sdkerrors.Wrap(err, "header 2 failed validation").Error(),
@ -119,7 +119,7 @@ func (misbehaviour Misbehaviour) ValidateBasic() error {
// Ensure that Commit Hashes are different
if blockID1.Equals(*blockID2) {
return sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "headers blockIDs are not equal")
return sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "headers blockIDs are equal")
}
if err := ValidCommit(misbehaviour.ChainId, misbehaviour.Header1.Commit, misbehaviour.Header1.ValidatorSet); err != nil {
return err

View File

@ -75,6 +75,20 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
suite.now,
true,
},
{
"invalid misbehavior misbehaviour from different chain",
types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()),
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), height, bothValsHash),
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), height, bothValsHash),
&types.Misbehaviour{
Header1: types.CreateTestHeader("ethermint", int64(height.EpochHeight), int64(height.EpochHeight), suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader("ethermint", int64(height.EpochHeight), int64(height.EpochHeight), suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
ChainId: "ethermint",
ClientId: chainID,
},
suite.now,
false,
},
{
"valid misbehavior misbehaviour with different trusted heights",
types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()),

View File

@ -166,6 +166,17 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() {
func(misbehaviour *types.Misbehaviour) error { return nil },
false,
},
{
"wrong chainID in misbehaviour",
&types.Misbehaviour{
Header1: suite.header,
Header2: types.CreateTestHeader(chainID, int64(height.EpochHeight), int64(height.EpochHeight-1), suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers),
ChainId: "ethermint",
ClientId: clientID,
},
func(misbehaviour *types.Misbehaviour) error { return nil },
false,
},
{
"mismatched heights",
&types.Misbehaviour{

View File

@ -1,231 +0,0 @@
package types
import (
"time"
ics23 "github.com/confio/ics23/go"
"github.com/tendermint/tendermint/light"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types"
clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types"
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
)
var (
_ clientexported.MsgCreateClient = (*MsgCreateClient)(nil)
_ clientexported.MsgUpdateClient = (*MsgUpdateClient)(nil)
_ clientexported.MsgSubmitMisbehaviour = (*MsgSubmitClientMisbehaviour)(nil)
)
// NewMsgCreateClient creates a new MsgCreateClient instance
func NewMsgCreateClient(
id string, header *Header, trustLevel Fraction,
trustingPeriod, unbondingPeriod, maxClockDrift time.Duration,
specs []*ics23.ProofSpec, signer sdk.AccAddress,
) *MsgCreateClient {
return &MsgCreateClient{
ClientId: id,
Header: header,
TrustLevel: trustLevel,
TrustingPeriod: trustingPeriod,
UnbondingPeriod: unbondingPeriod,
MaxClockDrift: maxClockDrift,
ProofSpecs: specs,
Signer: signer,
}
}
// Route implements sdk.Msg
func (msg MsgCreateClient) Route() string {
return host.RouterKey
}
// Type implements sdk.Msg
func (msg MsgCreateClient) Type() string {
return clientexported.TypeMsgCreateClient
}
// ValidateBasic implements sdk.Msg
func (msg MsgCreateClient) ValidateBasic() error {
if msg.TrustingPeriod == 0 {
return sdkerrors.Wrap(ErrInvalidTrustingPeriod, "duration cannot be 0")
}
if err := light.ValidateTrustLevel(msg.TrustLevel.ToTendermint()); err != nil {
return sdkerrors.Wrap(err, "invalid trust level for tendermint light client")
}
if msg.UnbondingPeriod == 0 {
return sdkerrors.Wrap(ErrInvalidUnbondingPeriod, "duration cannot be 0")
}
if msg.Signer.Empty() {
return sdkerrors.ErrInvalidAddress
}
if msg.Header == nil || msg.Header.SignedHeader == nil || msg.Header.SignedHeader.Header == nil {
return sdkerrors.Wrap(ErrInvalidHeader, "header cannot be nil")
}
// ValidateBasic of provided header with self-attested chain-id
if err := msg.Header.ValidateBasic(msg.Header.Header.GetChainID()); err != nil {
return sdkerrors.Wrapf(ErrInvalidHeader, "header failed validatebasic with its own chain-id: %v", err)
}
if msg.TrustingPeriod >= msg.UnbondingPeriod {
return sdkerrors.Wrapf(
ErrInvalidTrustingPeriod,
"trusting period (%s) should be < unbonding period (%s)", msg.TrustingPeriod, msg.UnbondingPeriod,
)
}
// Validate ProofSpecs
if msg.ProofSpecs == nil {
return sdkerrors.Wrap(ErrInvalidProofSpecs, "proof specs cannot be nil")
}
for _, spec := range msg.ProofSpecs {
if spec == nil {
return sdkerrors.Wrap(ErrInvalidProofSpecs, "proof spec cannot be nil")
}
}
return host.ClientIdentifierValidator(msg.ClientId)
}
// GetSignBytes implements sdk.Msg
func (msg MsgCreateClient) GetSignBytes() []byte {
return sdk.MustSortJSON(SubModuleCdc.MustMarshalJSON(&msg))
}
// GetSigners implements sdk.Msg
func (msg MsgCreateClient) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{msg.Signer}
}
// GetClientID implements clientexported.MsgCreateClient
func (msg MsgCreateClient) GetClientID() string {
return msg.ClientId
}
// GetClientType implements clientexported.MsgCreateClient
func (msg MsgCreateClient) GetClientType() string {
return clientexported.ClientTypeTendermint
}
// GetConsensusState implements clientexported.MsgCreateClient
func (msg MsgCreateClient) GetConsensusState() clientexported.ConsensusState {
// Construct initial consensus state from provided Header
root := commitmenttypes.NewMerkleRoot(msg.Header.Header.GetAppHash())
// NOTE: Using 0 for epoch number for now
// TODO: Use clienttypes.Height in Header once Header.GetHeight returns *clienttypes.Height
return &ConsensusState{
Timestamp: msg.Header.GetTime(),
Root: root,
Height: clienttypes.NewHeight(0, msg.Header.GetHeight()),
NextValidatorsHash: msg.Header.Header.NextValidatorsHash,
}
}
// InitializeFromMsg creates a tendermint client state from a CreateClientMsg
func (msg MsgCreateClient) InitializeClientState() clientexported.ClientState {
return NewClientState(msg.Header.Header.GetChainID(), msg.TrustLevel,
msg.TrustingPeriod, msg.UnbondingPeriod, msg.MaxClockDrift,
clienttypes.NewHeight(0, msg.Header.GetHeight()), msg.ProofSpecs,
)
}
// NewMsgUpdateClient creates a new MsgUpdateClient instance
func NewMsgUpdateClient(id string, header *Header, signer sdk.AccAddress) *MsgUpdateClient {
return &MsgUpdateClient{
ClientId: id,
Header: header,
Signer: signer,
}
}
// Route implements sdk.Msg
func (msg MsgUpdateClient) Route() string {
return host.RouterKey
}
// Type implements sdk.Msg
func (msg MsgUpdateClient) Type() string {
return clientexported.TypeMsgUpdateClient
}
// ValidateBasic implements sdk.Msg
func (msg MsgUpdateClient) ValidateBasic() error {
if msg.Signer.Empty() {
return sdkerrors.ErrInvalidAddress
}
if msg.Header == nil || msg.Header.SignedHeader == nil || msg.Header.Header == nil {
return sdkerrors.Wrap(ErrInvalidHeader, "header cannot be nil")
}
// ValidateBasic of provided header with self-attested chain-id
if err := msg.Header.ValidateBasic(msg.Header.Header.GetChainID()); err != nil {
return err
}
return host.ClientIdentifierValidator(msg.ClientId)
}
// GetSignBytes implements sdk.Msg
func (msg MsgUpdateClient) GetSignBytes() []byte {
return sdk.MustSortJSON(SubModuleCdc.MustMarshalJSON(&msg))
}
// GetSigners implements sdk.Msg
func (msg MsgUpdateClient) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{msg.Signer}
}
// GetClientID implements clientexported.MsgUpdateClient
func (msg MsgUpdateClient) GetClientID() string {
return msg.ClientId
}
// GetHeader implements clientexported.MsgUpdateClient
func (msg MsgUpdateClient) GetHeader() clientexported.Header {
return msg.Header
}
// NewMsgSubmitClientMisbehaviour creates a new MsgSubmitClientMisbehaviour
// instance.
func NewMsgSubmitClientMisbehaviour(m *Misbehaviour, s sdk.AccAddress) *MsgSubmitClientMisbehaviour {
return &MsgSubmitClientMisbehaviour{Misbehaviour: m, Signer: s}
}
// Route returns the MsgSubmitClientMisbehaviour's route.
func (msg MsgSubmitClientMisbehaviour) Route() string { return host.RouterKey }
// Type returns the MsgSubmitClientMisbehaviour's type.
func (msg MsgSubmitClientMisbehaviour) Type() string {
return clientexported.TypeMsgSubmitClientMisbehaviour
}
// ValidateBasic performs basic (non-state-dependant) validation on a MsgSubmitClientMisbehaviour.
func (msg MsgSubmitClientMisbehaviour) ValidateBasic() error {
if msg.Misbehaviour == nil {
return sdkerrors.Wrap(evidencetypes.ErrInvalidEvidence, "missing evidence")
}
if err := msg.Misbehaviour.ValidateBasic(); err != nil {
return err
}
if msg.Signer.Empty() {
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Signer.String())
}
return nil
}
// GetSignBytes returns the raw bytes a signer is expected to sign when submitting
// a MsgSubmitClientMisbehaviour message.
func (msg MsgSubmitClientMisbehaviour) GetSignBytes() []byte {
return sdk.MustSortJSON(SubModuleCdc.MustMarshalJSON(&msg))
}
// GetSigners returns the single expected signer for a MsgSubmitClientMisbehaviour.
func (msg MsgSubmitClientMisbehaviour) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{msg.Signer}
}
func (msg MsgSubmitClientMisbehaviour) GetMisbehaviour() clientexported.Misbehaviour {
return msg.Misbehaviour
}

View File

@ -1,78 +0,0 @@
package types_test
import (
ics23 "github.com/confio/ics23/go"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
types "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types"
commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
)
func (suite *TendermintTestSuite) TestMsgCreateClientValidateBasic() {
privKey := secp256k1.GenPrivKey()
signer := sdk.AccAddress(privKey.PubKey().Address())
invalidHeader := types.CreateTestHeader(suite.header.Header.GetChainID(), int64(height.EpochHeight), 0, suite.now, suite.valSet, nil, []tmtypes.PrivValidator{suite.privVal})
invalidHeader.ValidatorSet = nil
cases := []struct {
msg *types.MsgCreateClient
expPass bool
errMsg string
}{
{types.NewMsgCreateClient(exported.ClientTypeTendermint, suite.header, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, commitmenttypes.GetSDKSpecs(), signer), true, "success msg should pass"},
{types.NewMsgCreateClient("(BADCHAIN)", suite.header, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, commitmenttypes.GetSDKSpecs(), signer), false, "invalid client id passed"},
{types.NewMsgCreateClient(exported.ClientTypeTendermint, suite.header, types.Fraction{Numerator: 0, Denominator: 1}, trustingPeriod, ubdPeriod, maxClockDrift, commitmenttypes.GetSDKSpecs(), signer), false, "invalid trust level"},
{types.NewMsgCreateClient(exported.ClientTypeTendermint, suite.header, types.DefaultTrustLevel, 0, ubdPeriod, maxClockDrift, commitmenttypes.GetSDKSpecs(), signer), false, "zero trusting period passed"},
{types.NewMsgCreateClient(exported.ClientTypeTendermint, suite.header, types.DefaultTrustLevel, trustingPeriod, 0, maxClockDrift, commitmenttypes.GetSDKSpecs(), signer), false, "zero unbonding period passed"},
{types.NewMsgCreateClient(exported.ClientTypeTendermint, suite.header, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, commitmenttypes.GetSDKSpecs(), nil), false, "Empty address passed"},
{types.NewMsgCreateClient(exported.ClientTypeTendermint, &types.Header{}, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, commitmenttypes.GetSDKSpecs(), signer), false, "empty header"},
{types.NewMsgCreateClient(exported.ClientTypeTendermint, nil, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, commitmenttypes.GetSDKSpecs(), signer), false, "nil header"},
{types.NewMsgCreateClient(exported.ClientTypeTendermint, &types.Header{SignedHeader: nil}, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, commitmenttypes.GetSDKSpecs(), signer), false, "nil header"},
{types.NewMsgCreateClient(exported.ClientTypeTendermint, &types.Header{SignedHeader: &tmproto.SignedHeader{Header: nil}}, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, commitmenttypes.GetSDKSpecs(), signer), false, "nil header"},
{types.NewMsgCreateClient(exported.ClientTypeTendermint, invalidHeader, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, commitmenttypes.GetSDKSpecs(), signer), false, "invalid header"},
{types.NewMsgCreateClient(exported.ClientTypeTendermint, suite.header, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, []*ics23.ProofSpec{nil}, signer), false, "invalid proof specs"},
{types.NewMsgCreateClient(exported.ClientTypeTendermint, suite.header, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, nil, signer), false, "nil proof specs"},
{types.NewMsgCreateClient(exported.ClientTypeTendermint, suite.header, types.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, commitmenttypes.GetSDKSpecs(), signer), false, "trusting period not less than unbonding period"},
}
for i, tc := range cases {
err := tc.msg.ValidateBasic()
if tc.expPass {
suite.Require().NoError(err, "Msg %d failed: %v", i, tc.errMsg)
} else {
suite.Require().Error(err, "Invalid Msg %d passed: %s", i, tc.errMsg)
}
}
}
func (suite *TendermintTestSuite) TestMsgUpdateClient() {
privKey := secp256k1.GenPrivKey()
signer := sdk.AccAddress(privKey.PubKey().Address())
cases := []struct {
msg *types.MsgUpdateClient
expPass bool
errMsg string
}{
{types.NewMsgUpdateClient(exported.ClientTypeTendermint, suite.header, signer), true, "success msg should pass"},
{types.NewMsgUpdateClient("(badClient)", &types.Header{}, signer), false, "invalid client id passed"},
{types.NewMsgUpdateClient(exported.ClientTypeTendermint, suite.header, nil), false, "Empty address passed"},
{types.NewMsgUpdateClient(exported.ClientTypeTendermint, &types.Header{}, nil), false, "empty Header"},
{types.NewMsgUpdateClient(exported.ClientTypeTendermint, nil, nil), false, "nil header"},
{types.NewMsgUpdateClient(exported.ClientTypeTendermint, &types.Header{SignedHeader: nil}, nil), false, "nil signed header"},
{types.NewMsgUpdateClient(exported.ClientTypeTendermint, &types.Header{SignedHeader: &tmproto.SignedHeader{Header: nil}}, nil), false, "nil tendermint header"},
}
for i, tc := range cases {
err := tc.msg.ValidateBasic()
if tc.expPass {
suite.Require().NoError(err, "Msg %d failed: %v", i, err)
} else {
suite.Require().Error(err, "Invalid Msg %d passed: %s", i, tc.errMsg)
}
}
}

View File

@ -87,6 +87,16 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
},
expPass: true,
},
{
name: "unsuccessful update with incorrect header chain-id",
setup: func() {
clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs())
consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), height, suite.valsHash)
newHeader = types.CreateTestHeader("ethermint", int64(height.EpochHeight+1), int64(height.EpochHeight), suite.headerTime, suite.valSet, suite.valSet, signers)
currentTime = suite.now
},
expPass: false,
},
{
name: "unsuccessful update with next height: update header mismatches nextValSetHash",
setup: func() {

View File

@ -1,23 +0,0 @@
package cli
import (
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/x/ibc/09-localhost/types"
)
// NewTxCmd returns a root CLI command handler for all ibc localhost transaction commands.
func NewTxCmd() *cobra.Command {
txCmd := &cobra.Command{
Use: types.SubModuleName,
Short: "Localhost client transaction subcommands",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
}
txCmd.AddCommand(
NewCreateClientCmd(),
)
return txCmd
}

View File

@ -1,44 +0,0 @@
package cli
import (
"fmt"
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/version"
"github.com/cosmos/cosmos-sdk/x/ibc/09-localhost/types"
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
)
// NewCreateClientCmd defines the command to create a new IBC Loopback Client as defined
// in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#create
func NewCreateClientCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "create",
Short: "create new localhost client",
Long: "create new localhost (loopback) client",
Example: fmt.Sprintf("%s tx %s %s create --from node0 --home ../node0/<app>cli --chain-id $CID", version.AppName, host.ModuleName, types.SubModuleName),
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, _ []string) error {
clientCtx := client.GetClientContextFromCmd(cmd)
clientCtx, err := client.ReadTxCommandFlags(clientCtx, cmd.Flags())
if err != nil {
return err
}
msg := types.NewMsgCreateClient(clientCtx.GetFromAddress())
if err := msg.ValidateBasic(); err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}
flags.AddTxFlagsToCmd(cmd)
return cmd
}

View File

@ -1,9 +1,6 @@
package localhost
import (
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/x/ibc/09-localhost/client/cli"
"github.com/cosmos/cosmos-sdk/x/ibc/09-localhost/types"
)
@ -11,8 +8,3 @@ import (
func Name() string {
return types.SubModuleName
}
// GetTxCmd returns the root tx command for the IBC localhost client
func GetTxCmd() *cobra.Command {
return cli.NewTxCmd()
}

View File

@ -3,17 +3,12 @@ package types
import (
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
)
// RegisterInterfaces register the ibc interfaces submodule implementations to protobuf
// Any.
func RegisterInterfaces(registry codectypes.InterfaceRegistry) {
registry.RegisterImplementations(
(*sdk.Msg)(nil),
&MsgCreateClient{},
)
registry.RegisterImplementations(
(*clientexported.ClientState)(nil),
&ClientState{},

View File

@ -1,69 +0,0 @@
package types
import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
)
var (
_ clientexported.MsgCreateClient = (*MsgCreateClient)(nil)
)
// NewMsgCreateClient creates a new MsgCreateClient instance
func NewMsgCreateClient(signer sdk.AccAddress) *MsgCreateClient {
return &MsgCreateClient{
Signer: signer,
}
}
// Route implements sdk.Msg
func (msg MsgCreateClient) Route() string {
return host.RouterKey
}
// Type implements sdk.Msg
func (msg MsgCreateClient) Type() string {
return clientexported.TypeMsgCreateClient
}
// ValidateBasic implements sdk.Msg
func (msg MsgCreateClient) ValidateBasic() error {
if msg.Signer.Empty() {
return sdkerrors.ErrInvalidAddress
}
return nil
}
// GetSignBytes implements sdk.Msg
func (msg MsgCreateClient) GetSignBytes() []byte {
return sdk.MustSortJSON(SubModuleCdc.MustMarshalJSON(&msg))
}
// GetSigners implements sdk.Msg
func (msg MsgCreateClient) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{msg.Signer}
}
// GetClientID implements clientexported.MsgCreateClient
func (msg MsgCreateClient) GetClientID() string {
return clientexported.ClientTypeLocalHost
}
// GetClientType implements clientexported.MsgCreateClient
func (msg MsgCreateClient) GetClientType() string {
return clientexported.ClientTypeLocalHost
}
// GetConsensusState implements clientexported.MsgCreateClient
func (msg MsgCreateClient) GetConsensusState() clientexported.ConsensusState {
return nil
}
// InitializeClientState implements clientexported.MsgCreateClient
// localhost is a special case that require the running chain's context to initialize
// the client state, thus this function is a no-op
func (msg MsgCreateClient) InitializeClientState() clientexported.ClientState {
return nil
}

View File

@ -8,8 +8,8 @@ import (
connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection"
channel "github.com/cosmos/cosmos-sdk/x/ibc/04-channel"
tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint"
localhost "github.com/cosmos/cosmos-sdk/x/ibc/09-localhost"
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
"github.com/cosmos/cosmos-sdk/x/ibc/light-clients/solomachine"
)
// GetTxCmd returns the transaction commands for this module
@ -23,8 +23,8 @@ func GetTxCmd() *cobra.Command {
}
ibcTxCmd.AddCommand(
solomachine.GetTxCmd(),
tendermint.GetTxCmd(),
localhost.GetTxCmd(),
connection.GetTxCmd(),
channel.GetTxCmd(),
)

View File

@ -4,7 +4,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
client "github.com/cosmos/cosmos-sdk/x/ibc/02-client"
clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection"
connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types"
channel "github.com/cosmos/cosmos-sdk/x/ibc/04-channel"
@ -20,13 +20,13 @@ func NewHandler(k keeper.Keeper) sdk.Handler {
switch msg := msg.(type) {
// IBC client msg interface types
case clientexported.MsgCreateClient:
case *clienttypes.MsgCreateClient:
return client.HandleMsgCreateClient(ctx, k.ClientKeeper, msg)
case clientexported.MsgUpdateClient:
case *clienttypes.MsgUpdateClient:
return client.HandleMsgUpdateClient(ctx, k.ClientKeeper, msg)
case clientexported.MsgSubmitMisbehaviour:
case *clienttypes.MsgSubmitMisbehaviour:
return client.HandleMsgSubmitMisbehaviour(ctx, k.ClientKeeper, msg)
// IBC connection msgs

View File

@ -12,6 +12,7 @@ import (
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/version"
clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
"github.com/cosmos/cosmos-sdk/x/ibc/light-clients/solomachine/types"
)
@ -46,7 +47,11 @@ func NewCreateClientCmd() *cobra.Command {
}
}
msg := types.NewMsgCreateClient(clientID, consensusState)
clientState := types.NewClientState(consensusState)
msg, err := clienttypes.NewMsgCreateClient(clientID, clientState, consensusState, clientCtx.GetFromAddress())
if err != nil {
return err
}
if err := msg.ValidateBasic(); err != nil {
return err
@ -88,7 +93,11 @@ func NewUpdateClientCmd() *cobra.Command {
}
}
msg := types.NewMsgUpdateClient(clientID, header)
msg, err := clienttypes.NewMsgUpdateClient(clientID, header, clientCtx.GetFromAddress())
if err != nil {
return err
}
if err := msg.ValidateBasic(); err != nil {
return err
}
@ -128,7 +137,11 @@ func NewSubmitMisbehaviourCmd() *cobra.Command {
}
}
msg := types.NewMsgSubmitClientMisbehaviour(m, clientCtx.GetFromAddress())
msg, err := clienttypes.NewMsgSubmitMisbehaviour(m.ClientId, m, clientCtx.GetFromAddress())
if err != nil {
return err
}
if err := msg.ValidateBasic(); err != nil {
return err
}

View File

@ -57,6 +57,9 @@ func (cs ClientState) GetProofSpecs() []*ics23.ProofSpec {
// Validate performs basic validation of the client state fields.
func (cs ClientState) Validate() error {
if cs.ConsensusState == nil {
return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "consensus state cannot be nil")
}
return cs.ConsensusState.ValidateBasic()
}

View File

@ -33,6 +33,11 @@ func (suite *SoloMachineTestSuite) TestClientStateValidateBasic() {
suite.solomachine.ClientState(),
true,
},
{
"empty ClientState",
&types.ClientState{},
false,
},
{
"sequence is zero",
types.NewClientState(&types.ConsensusState{0, suite.solomachine.ConsensusState().PublicKey, suite.solomachine.Time}),

View File

@ -3,19 +3,12 @@ package types
import (
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
)
// RegisterInterfaces register the ibc channel submodule interfaces to protobuf
// Any.
func RegisterInterfaces(registry codectypes.InterfaceRegistry) {
registry.RegisterImplementations(
(*sdk.Msg)(nil),
&MsgCreateClient{},
&MsgUpdateClient{},
&MsgSubmitClientMisbehaviour{},
)
registry.RegisterImplementations(
(*clientexported.ClientState)(nil),
&ClientState{},
@ -24,6 +17,14 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) {
(*clientexported.ConsensusState)(nil),
&ConsensusState{},
)
registry.RegisterImplementations(
(*clientexported.Header)(nil),
&Header{},
)
registry.RegisterImplementations(
(*clientexported.Misbehaviour)(nil),
&Misbehaviour{},
)
}
var (

View File

@ -27,7 +27,7 @@ func (misbehaviour Misbehaviour) GetClientID() string {
// Type implements Evidence interface.
func (misbehaviour Misbehaviour) Type() string {
return clientexported.TypeEvidenceClientMisbehaviour
return clientexported.TypeClientMisbehaviour
}
// String implements Evidence interface.

View File

@ -1,165 +0,0 @@
package types
import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
)
var (
_ clientexported.MsgCreateClient = &MsgCreateClient{}
_ clientexported.MsgUpdateClient = &MsgUpdateClient{}
_ clientexported.MsgSubmitMisbehaviour = &MsgSubmitClientMisbehaviour{}
)
// NewMsgCreateClient creates a new MsgCreateClient instance
func NewMsgCreateClient(id string, consensusState *ConsensusState) *MsgCreateClient {
return &MsgCreateClient{
ClientId: id,
ConsensusState: consensusState,
}
}
// Route implements sdk.Msg
func (msg MsgCreateClient) Route() string {
return host.RouterKey
}
// Type implements sdk.Msg
func (msg MsgCreateClient) Type() string {
return clientexported.TypeMsgCreateClient
}
// ValidateBasic implements sdk.Msg
func (msg MsgCreateClient) ValidateBasic() error {
if msg.ConsensusState == nil {
return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "consensus state cannot be nil")
}
if err := msg.ConsensusState.ValidateBasic(); err != nil {
return err
}
return host.ClientIdentifierValidator(msg.ClientId)
}
// GetSignBytes implements sdk.Msg
func (msg MsgCreateClient) GetSignBytes() []byte {
return sdk.MustSortJSON(SubModuleCdc.MustMarshalJSON(&msg))
}
// GetSigners implements sdk.Msg
func (msg MsgCreateClient) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{sdk.AccAddress(msg.ConsensusState.GetPubKey().Address())}
}
// GetClientID implements clientexported.MsgCreateClient
func (msg MsgCreateClient) GetClientID() string {
return msg.ClientId
}
// GetClientType implements clientexported.MsgCreateClient
func (msg MsgCreateClient) GetClientType() string {
return clientexported.ClientTypeSoloMachine
}
// GetConsensusState implements clientexported.MsgCreateClient
func (msg MsgCreateClient) GetConsensusState() clientexported.ConsensusState {
return msg.ConsensusState
}
// InitializeFromMsg creates a solo machine client state from a MsgCreateClient
func (msg MsgCreateClient) InitializeClientState() clientexported.ClientState {
return NewClientState(msg.ConsensusState)
}
// NewMsgUpdateClient creates a new MsgUpdateClient instance
func NewMsgUpdateClient(id string, header *Header) *MsgUpdateClient {
return &MsgUpdateClient{
ClientId: id,
Header: header,
}
}
// Route implements sdk.Msg
func (msg MsgUpdateClient) Route() string {
return host.RouterKey
}
// Type implements sdk.Msg
func (msg MsgUpdateClient) Type() string {
return clientexported.TypeMsgUpdateClient
}
// ValidateBasic implements sdk.Msg
func (msg MsgUpdateClient) ValidateBasic() error {
if msg.Header == nil {
return sdkerrors.Wrap(ErrInvalidHeader, "header cannot be nil")
}
if err := msg.Header.ValidateBasic(); err != nil {
return err
}
return host.ClientIdentifierValidator(msg.ClientId)
}
// GetSignBytes implements sdk.Msg
func (msg MsgUpdateClient) GetSignBytes() []byte {
return sdk.MustSortJSON(SubModuleCdc.MustMarshalJSON(&msg))
}
// GetSigners implements sdk.Msg
func (msg MsgUpdateClient) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{sdk.AccAddress(msg.Header.GetPubKey().Address())}
}
// GetClientID implements clientexported.MsgUpdateClient
func (msg MsgUpdateClient) GetClientID() string {
return msg.ClientId
}
// GetHeader implements clientexported.MsgUpdateClient
func (msg MsgUpdateClient) GetHeader() clientexported.Header {
return msg.Header
}
// NewMsgSubmitClientMisbehaviour creates a new MsgSubmitClientMisbehaviour
// instance.
func NewMsgSubmitClientMisbehaviour(m *Misbehaviour, s sdk.AccAddress) *MsgSubmitClientMisbehaviour {
return &MsgSubmitClientMisbehaviour{Misbehaviour: m, Signer: s}
}
// Route returns the MsgSubmitClientMisbehaviour's route.
func (msg MsgSubmitClientMisbehaviour) Route() string { return host.RouterKey }
// Type returns the MsgSubmitClientMisbehaviour's type.
func (msg MsgSubmitClientMisbehaviour) Type() string {
return clientexported.TypeMsgSubmitClientMisbehaviour
}
// ValidateBasic performs basic (non-state-dependent) validation on a MsgSubmitClientMisbehaviour.
func (msg MsgSubmitClientMisbehaviour) ValidateBasic() error {
if err := msg.Misbehaviour.ValidateBasic(); err != nil {
return err
}
if msg.Signer.Empty() {
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "signer address cannot be empty")
}
return nil
}
// GetSignBytes returns the raw bytes a signer is expected to sign when submitting
// a MsgSubmitClientMisbehaviour message.
func (msg MsgSubmitClientMisbehaviour) GetSignBytes() []byte {
return sdk.MustSortJSON(SubModuleCdc.MustMarshalJSON(&msg))
}
// GetSigners returns the single expected signer for a MsgSubmitClientMisbehaviour.
func (msg MsgSubmitClientMisbehaviour) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{msg.Signer}
}
func (msg MsgSubmitClientMisbehaviour) GetMisbehaviour() clientexported.Misbehaviour {
return msg.Misbehaviour
}

View File

@ -1,56 +0,0 @@
package types_test
import (
"github.com/cosmos/cosmos-sdk/x/ibc/light-clients/solomachine/types"
)
func (suite *SoloMachineTestSuite) TestMsgCreateClientValidateBasic() {
cases := []struct {
name string
msg *types.MsgCreateClient
expPass bool
}{
{"valid msg", types.NewMsgCreateClient(suite.solomachine.ClientID, suite.solomachine.ConsensusState()), true},
{"invalid client id", types.NewMsgCreateClient("(BADCLIENTID)", suite.solomachine.ConsensusState()), false},
{"nil consensus state", types.NewMsgCreateClient(suite.solomachine.ClientID, nil), false},
{"invalid consensus state with zero sequence", types.NewMsgCreateClient(suite.solomachine.ClientID, &types.ConsensusState{0, suite.solomachine.ConsensusState().PublicKey, suite.solomachine.Time}), false},
{"invalid consensus state with zero timestamp", types.NewMsgCreateClient(suite.solomachine.ClientID, &types.ConsensusState{1, suite.solomachine.ConsensusState().PublicKey, 0}), false},
{"invalid consensus state with nil pubkey", types.NewMsgCreateClient(suite.solomachine.ClientID, &types.ConsensusState{suite.solomachine.Sequence, nil, suite.solomachine.Time}), false},
}
for i, tc := range cases {
err := tc.msg.ValidateBasic()
if tc.expPass {
suite.Require().NoError(err, "Msg %d failed: %v", i, tc.name)
} else {
suite.Require().Error(err, "Invalid Msg %d passed: %s", i, tc.name)
}
}
}
func (suite *SoloMachineTestSuite) TestMsgUpdateClientValidateBasic() {
header := suite.solomachine.CreateHeader()
cases := []struct {
name string
msg *types.MsgUpdateClient
expPass bool
}{
{"valid msg", types.NewMsgUpdateClient(suite.solomachine.ClientID, header), true},
{"invalid client id", types.NewMsgUpdateClient("(BADCLIENTID)", header), false},
{"nil header", types.NewMsgUpdateClient(suite.solomachine.ClientID, nil), false},
{"invalid header - sequence is zero", types.NewMsgUpdateClient(suite.solomachine.ClientID, &types.Header{0, header.Signature, header.NewPublicKey}), false},
{"invalid header - signature is empty", types.NewMsgUpdateClient(suite.solomachine.ClientID, &types.Header{header.Sequence, []byte{}, header.NewPublicKey}), false},
{"invalid header - pubkey is empty", types.NewMsgUpdateClient(suite.solomachine.ClientID, &types.Header{header.Sequence, header.Signature, nil}), false},
}
for i, tc := range cases {
err := tc.msg.ValidateBasic()
if tc.expPass {
suite.Require().NoError(err, "Msg %d failed: %v", i, tc.name)
} else {
suite.Require().Error(err, "Invalid Msg %d passed: %s", i, tc.name)
}
}
}

View File

@ -15,17 +15,17 @@ A light client is created using the `MsgCreateClient`.
```go
type MsgCreateClient struct {
ClientId string
ClientType string
ConsensusState ConsensusState
ClientState *types.Any // proto-packed client state
ConsensusState *types.Any // proto-packed consensus state
Signer sdk.AccAddress
}
```
This message is expected to fail if:
- `ClientId` is invalid (not alphanumeric or not within 10-20 characters)
- `ClientType` is not registered
- `ConsensusState` is empty
- `ClientId` is invalid (see naming requirements)
- `ClientState` is empty or invalid
- `ConsensusState` is empty or invalid
- `Signer` is empty
- A light client with the provided id and type already exist
@ -40,7 +40,7 @@ A light client is updated with a new header using the `MsgUpdateClient`.
```go
type MsgUpdateClient struct {
ClientId string
Header Header
Header *types.Any // proto-packed header
Signer sdk.AccAddress
}
```
@ -48,15 +48,38 @@ type MsgUpdateClient struct {
This message is expected to fail if:
- `ClientId` is invalid (not alphanumeric or not within 10-20 characters)
- `Header` is empty
- `Header` is empty or invalid
- `Signer` is empty
- A Client hasn't been created for the given ID
- A `ClientState` hasn't been created for the given ID
- the header's client type is different from the registered one
- the client is frozen due to misbehaviour and cannot be updated
The message validates the header and updates the consensus state with the new
height, commitment root and validator sets, which are then stored.
### MsgSubmitMisbehaviour
Submit a evidence of light client misbehaviour to freeze the client state and prevent additional packets from being relayed.
```go
type MsgSubmitMisbehaviour struct {
ClientId string
Misbehaviour *types.Any // proto-packed misbehaviour
Signer sdk.AccAddress
}
```
This message is expected to fail if:
- `ClientId` is invalid (not alphanumeric or not within 10-20 characters)
- `Misbehaviour` is empty or invalid
- `Signer` is empty
- A `ClientState` hasn't been created for the given ID
- `Misbehaviour` check failed
The message validates the header and updates the consensus state with the new
height, commitment root and validator sets, which are then stored.
## ICS 03 - Connection
### MsgConnectionOpenInit
@ -89,26 +112,27 @@ using the `MsgConnectionOpenTry`.
```go
type MsgConnectionOpenTry struct {
ClientId string
ConnectionId string
ClientId string
ConnectionId string
ClientState *types.Any // proto-packed counterparty client
Counterparty Counterparty
CounterpartyVersions []string
ProofHeight uint64
ProofInit []byte
Counterparty Counterparty
CounterpartyVersions []string
ProofHeight uint64
ProofInit []byte
ProofClient []byte
ProofConsensus []byte
ProofConsensus []byte
ConsensusHeight uint64
Signer sdk.AccAddress
Signer sdk.AccAddress
}
```
This message is expected to fail if:
- `ClientId` is invalid (see naming requirements)
- `ConnectionId` is invalid (see naming requirements)
- `ClientState` is not a valid client of the executing chain
- `Counterparty` is empty
- `CounterpartyVersions` is empty
- `CounterpartyVersions` is empty
- `ProofHeight` is zero
- `ProofInit` is empty
- `ProofClient` is empty
@ -130,19 +154,20 @@ using the `MsgConnectionOpenAck`.
```go
type MsgConnectionOpenAck struct {
ConnectionId string
Version string
ConnectionId string
Version string
ClientState *types.Any // proto-packed counterparty client
ProofHeight uint64
ProofTry []byte
ProofHeight uint64
ProofTry []byte
ProofClient []byte
ProofConsensus []byte
ConsensusHeight uint64
Signer sdk.AccAddress
ProofConsensus []byte
ConsensusHeight uint64
Signer sdk.AccAddress
}
```
This message is expected to fail if:
- `ConnectionId` is invalid (see naming requirements)
- `Version` is empty
- `ClientState` is not a valid client of the executing chain
@ -165,14 +190,15 @@ the `MsgConnectionOpenConfirm`.
```go
type MsgConnectionOpenConfirm struct {
ConnectionId string
ProofAck []byte
ProofHeight uint64
Signer sdk.AccAddress
ConnectionId string
ProofAck []byte
ProofHeight uint64
Signer sdk.AccAddress
}
```
This message is expected to fail if:
- `ConnectionId` is invalid (see naming requirements)
- `ProofAck` is empty
- `ProofHeight` is zero
@ -199,18 +225,19 @@ type MsgChannelOpenInit struct {
```
This message is expected to fail if:
- `PortId` is invalid (see naming requirements)
- `ChannelId` is invalid (see naming requirements)
- `Channel` is empty
- `Signer` is empty
- A Channel End exists for the given Channel ID and Port ID
The message creates a channel on chain A with an INIT state for the given Channel ID
The message creates a channel on chain A with an INIT state for the given Channel ID
and Port ID.
### MsgChannelOpenTry
A channel handshake initialization attempt is acknowledged by a chain B using
A channel handshake initialization attempt is acknowledged by a chain B using
the `MsgChannelOpenTry` message.
```go
@ -226,6 +253,7 @@ type MsgChannelOpenTry struct {
```
This message is expected to fail if:
- `PortId` is invalid (see naming requirements)
- `ChannelId` is invalid (see naming requirements)
- `Channel` is empty
@ -245,16 +273,17 @@ A channel handshake is opened by a chain A using the `MsgChannelOpenAck` message
```go
type MsgChannelOpenAck struct {
PortId string
ChannelId string
CounterpartyVersion string
PortId string
ChannelId string
CounterpartyVersion string
ProofTry []byte
ProofHeight uint64
Signer sdk.AccAddress
Signer sdk.AccAddress
}
```
This message is expected to fail if:
- `PortId` is invalid (see naming requirements)
- `ChannelId` is invalid (see naming requirements)
- `CounterpartyVersion` is empty
@ -272,15 +301,16 @@ message.
```go
type MsgChannelOpenConfirm struct {
PortId string
ChannelId string
PortId string
ChannelId string
ProofAck []byte
ProofHeight uint64
Signer sdk.AccAddress
Signer sdk.AccAddress
}
```
This message is expected to fail if:
- `PortId` is invalid (see naming requirements)
- `ChannelId` is invalid (see naming requirements)
- `ProofAck` is empty
@ -296,13 +326,14 @@ A channel is closed on chain A using the `MsgChannelCloseInit`.
```go
type MsgChannelCloseInit struct {
PortId string
ChannelId string
Signer sdk.AccAddress
PortId string
ChannelId string
Signer sdk.AccAddress
}
```
This message is expected to fail if:
- `PortId` is invalid (see naming requirements)
- `ChannelId` is invalid (see naming requirements)
- `Signer` is empty
@ -316,15 +347,16 @@ A channel is closed on chain B using the `MsgChannelCloseConfirm`.
```go
type MsgChannelCloseConfirm struct {
PortId string
ChannelId string
PortId string
ChannelId string
ProofInit []byte
ProofHeight uint64
Signer sdk.AccAddress
ProofHeight uint64
Signer sdk.AccAddress
}
```
This message is expected to fail if:
- `PortId` is invalid (see naming requirements)
- `ChannelId` is invalid (see naming requirements)
- `ProofInit` is empty
@ -344,11 +376,12 @@ type MsgRecvPacket struct {
Packet Packet
Proof []byte
ProofHeight uint64
Signer sdk.AccAddress
Signer sdk.AccAddress
}
```
This message is expected to fail if:
- `Proof` is empty
- `ProofHeight` is zero
- `Signer` is empty
@ -363,7 +396,7 @@ A packet is timed out on chain A using the `MsgTimeout`.
```go
type MsgTimeout struct {
Packet Packet
Packet Packet
Proof []byte
ProofHeight uint64
NextSequenceRecv uint64
@ -372,6 +405,7 @@ type MsgTimeout struct {
```
This message is expected to fail if:
- `Proof` is empty
- `ProofHeight` is zero
- `NextSequenceRecv` is zero
@ -388,7 +422,7 @@ the `MsgTimeoutOnClose`.
```go
type MsgTimeoutOnClose struct {
Packet Packet
Packet Packet
Proof []byte
ProofClose []byte
ProofHeight uint64
@ -398,6 +432,7 @@ type MsgTimeoutOnClose struct {
```
This message is expected to fail if:
- `Proof` is empty
- `ProofClose` is empty
- `ProofHeight` is zero
@ -413,7 +448,7 @@ The message times out a packet on chain B.
A packet is acknowledged on chain A using the `MsgAcknowledgement`.
```go
```go
type MsgAcknowledgement struct {
Packet Packet
Acknowledgement []byte
@ -424,6 +459,7 @@ type MsgAcknowledgement struct {
```
This message is expected to fail if:
- `Proof` is empty
- `ProofHeight` is zero
- `Signer` is empty
@ -432,4 +468,3 @@ This message is expected to fail if:
- `Proof` does not prove that the counterparty received the `Packet`.
The message receives a packet on chain A.

View File

@ -123,8 +123,9 @@ func NewTestChain(t *testing.T, chainID string) *TestChain {
// create current header and call begin block
header := tmproto.Header{
Height: 1,
Time: globalStartTime,
ChainID: chainID,
Height: 1,
Time: globalStartTime,
}
txConfig := simapp.MakeEncodingConfig().TxConfig
@ -367,19 +368,40 @@ func (chain *TestChain) GetFirstTestConnection(clientID, counterpartyClientID st
return chain.ConstructNextTestConnection(clientID, counterpartyClientID)
}
func (chain *TestChain) ConstructMsgCreateClient(counterparty *TestChain, clientID string) clientexported.MsgCreateClient {
return ibctmtypes.NewMsgCreateClient(
clientID, counterparty.LastHeader,
DefaultTrustLevel, TrustingPeriod, UnbondingPeriod, MaxClockDrift,
commitmenttypes.GetSDKSpecs(), chain.SenderAccount.GetAddress(),
// ConstructMsgCreateClient constructs a message to create a new client state (tendermint or solomachine).
func (chain *TestChain) ConstructMsgCreateClient(counterparty *TestChain, clientID string, clientType string) *clienttypes.MsgCreateClient {
var (
clientState clientexported.ClientState
consensusState clientexported.ConsensusState
)
switch clientType {
case clientexported.ClientTypeTendermint:
clientState = ibctmtypes.NewClientState(
counterparty.ChainID, DefaultTrustLevel, TrustingPeriod, UnbondingPeriod, MaxClockDrift,
clienttypes.NewHeight(0, counterparty.LastHeader.GetHeight()), commitmenttypes.GetSDKSpecs(),
)
consensusState = counterparty.LastHeader.ConsensusState()
case clientexported.ClientTypeSoloMachine:
solo := NewSolomachine(chain.t, clientID)
clientState = solo.ClientState()
consensusState = solo.ConsensusState()
default:
chain.t.Fatalf("unsupported client state type %s", clientType)
}
msg, err := clienttypes.NewMsgCreateClient(
clientID, clientState, consensusState, chain.SenderAccount.GetAddress(),
)
require.NoError(chain.t, err)
return msg
}
// CreateTMClient will construct and execute a 07-tendermint MsgCreateClient. A counterparty
// client will be created on the (target) chain.
func (chain *TestChain) CreateTMClient(counterparty *TestChain, clientID string) error {
// construct MsgCreateClient using counterparty
msg := chain.ConstructMsgCreateClient(counterparty, clientID)
msg := chain.ConstructMsgCreateClient(counterparty, clientID, clientexported.ClientTypeTendermint)
return chain.sendMsgs(msg)
}
@ -420,10 +442,11 @@ func (chain *TestChain) UpdateTMClient(counterparty *TestChain, clientID string)
}
header.TrustedValidators = trustedVals
msg := ibctmtypes.NewMsgUpdateClient(
msg, err := clienttypes.NewMsgUpdateClient(
clientID, header,
chain.SenderAccount.GetAddress(),
)
require.NoError(chain.t, err)
return chain.sendMsgs(msg)
}