From e3391ff447299b8e6ce51c38035087ef05944ed1 Mon Sep 17 00:00:00 2001 From: Aditya Date: Thu, 6 Aug 2020 04:32:19 -0400 Subject: [PATCH] Remove ValidatorSet from ConsensusState (#6942) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix stash merge * fix build errors * fix tendermint types test * fix tendermint tests * fix client tests * fix rest of ibc tests * include TrustedHeight in Header * fix all tests * fix all tests * remove validatorshash from consensus state * lint * add evidence checks * Apply suggestions from code review Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * refix build * remove redundant hashing in tests * complete rest of minor review requests * make format * suite.valsetHash * fix test * Update x/ibc/07-tendermint/misbehaviour.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Co-authored-by: Federico Kunze --- x/auth/types/genesis.go | 3 +- x/auth/types/genesis_test.go | 2 +- x/evidence/genesis.go | 3 +- x/evidence/types/genesis.go | 3 +- x/evidence/types/genesis_test.go | 4 +- x/ibc/02-client/client/utils/utils.go | 9 +- x/ibc/02-client/keeper/client.go | 21 ++-- x/ibc/02-client/keeper/client_test.go | 75 ++++++++++----- x/ibc/02-client/keeper/keeper.go | 5 - x/ibc/02-client/keeper/keeper_test.go | 31 +++--- x/ibc/02-client/types/genesis_test.go | 10 +- x/ibc/07-tendermint/misbehaviour.go | 24 ++++- x/ibc/07-tendermint/misbehaviour_test.go | 96 +++++++++++-------- x/ibc/07-tendermint/tendermint_test.go | 5 +- .../07-tendermint/types/client_state_test.go | 28 +++--- x/ibc/07-tendermint/types/consensus_state.go | 7 +- .../types/consensus_state_test.go | 51 +++++----- x/ibc/07-tendermint/types/errors.go | 3 +- x/ibc/07-tendermint/types/evidence.go | 12 +++ x/ibc/07-tendermint/types/evidence_test.go | 40 ++++++-- x/ibc/07-tendermint/types/header.go | 32 +++++-- x/ibc/07-tendermint/types/header_test.go | 2 +- x/ibc/07-tendermint/types/msgs.go | 1 - x/ibc/07-tendermint/types/msgs_test.go | 2 +- x/ibc/07-tendermint/types/tendermint_test.go | 5 +- x/ibc/07-tendermint/types/test_utils.go | 8 +- x/ibc/07-tendermint/update.go | 20 +++- x/ibc/07-tendermint/update_test.go | 59 +++++++----- x/ibc/genesis_test.go | 2 +- x/ibc/ibc_test.go | 2 +- x/ibc/testing/chain.go | 51 +++++++++- 31 files changed, 400 insertions(+), 216 deletions(-) diff --git a/x/auth/types/genesis.go b/x/auth/types/genesis.go index 7f76f22f93..51e7871500 100644 --- a/x/auth/types/genesis.go +++ b/x/auth/types/genesis.go @@ -5,9 +5,10 @@ import ( "fmt" "sort" + proto "github.com/gogo/protobuf/proto" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/types" - proto "github.com/gogo/protobuf/proto" ) var _ types.UnpackInterfacesMessage = GenesisState{} diff --git a/x/auth/types/genesis_test.go b/x/auth/types/genesis_test.go index 783e44fd0f..2c4d5cea00 100644 --- a/x/auth/types/genesis_test.go +++ b/x/auth/types/genesis_test.go @@ -93,7 +93,7 @@ func TestPackAccountsAny(t *testing.T) { { "expected genesis account", func() { - accounts = []*codectypes.Any{&codectypes.Any{}} + accounts = []*codectypes.Any{{}} }, false, }, diff --git a/x/evidence/genesis.go b/x/evidence/genesis.go index a2625ea349..6ca5d43158 100644 --- a/x/evidence/genesis.go +++ b/x/evidence/genesis.go @@ -3,12 +3,13 @@ package evidence import ( "fmt" + "github.com/gogo/protobuf/proto" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/evidence/exported" "github.com/cosmos/cosmos-sdk/x/evidence/keeper" "github.com/cosmos/cosmos-sdk/x/evidence/types" - "github.com/gogo/protobuf/proto" ) // InitGenesis initializes the evidence module's state from a provided genesis diff --git a/x/evidence/types/genesis.go b/x/evidence/types/genesis.go index 6f25accd30..4ccd924c4b 100644 --- a/x/evidence/types/genesis.go +++ b/x/evidence/types/genesis.go @@ -3,9 +3,10 @@ package types import ( "fmt" + proto "github.com/gogo/protobuf/proto" + "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/x/evidence/exported" - proto "github.com/gogo/protobuf/proto" ) var _ types.UnpackInterfacesMessage = GenesisState{} diff --git a/x/evidence/types/genesis_test.go b/x/evidence/types/genesis_test.go index 7526fdd183..4aed3366eb 100644 --- a/x/evidence/types/genesis_test.go +++ b/x/evidence/types/genesis_test.go @@ -105,7 +105,7 @@ func TestGenesisStateValidate(t *testing.T) { "expected evidence", func() { genesisState = types.GenesisState{ - Evidence: []*codectypes.Any{&codectypes.Any{}}, + Evidence: []*codectypes.Any{{}}, } }, false, @@ -127,7 +127,7 @@ func TestGenesisStateValidate(t *testing.T) { func TestUnpackInterfaces(t *testing.T) { var gs = types.GenesisState{ - Evidence: []*codectypes.Any{&codectypes.Any{}}, + Evidence: []*codectypes.Any{{}}, } testCases := []struct { diff --git a/x/ibc/02-client/client/utils/utils.go b/x/ibc/02-client/client/utils/utils.go index 4430d05fee..e47a4890e9 100644 --- a/x/ibc/02-client/client/utils/utils.go +++ b/x/ibc/02-client/client/utils/utils.go @@ -142,15 +142,16 @@ func QueryNodeConsensusState(clientCtx client.Context) (ibctmtypes.ConsensusStat return ibctmtypes.ConsensusState{}, 0, err } - validators, err := node.Validators(&height, 0, 10000) + nextHeight := height + 1 + nextVals, err := node.Validators(&nextHeight, 0, 10000) if err != nil { return ibctmtypes.ConsensusState{}, 0, err } state := ibctmtypes.ConsensusState{ - Timestamp: commit.Time, - Root: commitmenttypes.NewMerkleRoot(commit.AppHash), - ValidatorSet: tmtypes.NewValidatorSet(validators.Validators), + Timestamp: commit.Time, + Root: commitmenttypes.NewMerkleRoot(commit.AppHash), + NextValidatorsHash: tmtypes.NewValidatorSet(nextVals.Validators).Hash(), } return state, height, nil diff --git a/x/ibc/02-client/keeper/client.go b/x/ibc/02-client/keeper/client.go index 96e51da3b9..3ab40ceeeb 100644 --- a/x/ibc/02-client/keeper/client.go +++ b/x/ibc/02-client/keeper/client.go @@ -71,9 +71,15 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.H switch clientType { case exported.Tendermint: - trustedConsState, found := k.GetClientConsensusStateLTE(ctx, clientID, header.GetHeight()) + tmHeader, ok := header.(ibctmtypes.Header) + if !ok { + err = sdkerrors.Wrapf(types.ErrInvalidHeader, "expected tendermint header: %T, got header type: %T", ibctmtypes.Header{}, header) + break + } + // Get the consensus state at the trusted height of header + trustedConsState, found := k.GetClientConsensusState(ctx, clientID, tmHeader.TrustedHeight) if !found { - return nil, sdkerrors.Wrapf(types.ErrConsensusStateNotFound, "could not find consensus state less than header height: %d to verify header against", header.GetHeight()) + return nil, sdkerrors.Wrapf(types.ErrConsensusStateNotFound, "could not find consensus state for trusted header height: %d to verify header against for clientID: %s", tmHeader.TrustedHeight, clientID) } clientState, consensusState, err = tendermint.CheckValidityAndUpdateState( clientState, trustedConsState, header, ctx.BlockTime(), @@ -127,14 +133,15 @@ func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, misbehaviour ex return sdkerrors.Wrapf(types.ErrClientNotFound, "cannot check misbehaviour for client with ID %s", misbehaviour.GetClientID()) } - consensusState, found := k.GetClientConsensusStateLTE(ctx, misbehaviour.GetClientID(), uint64(misbehaviour.GetHeight())) - if !found { - return sdkerrors.Wrapf(types.ErrConsensusStateNotFound, "cannot check misbehaviour for client with ID %s", misbehaviour.GetClientID()) - } - var err error switch e := misbehaviour.(type) { case ibctmtypes.Evidence: + // Get ConsensusState at TrustedHeight + consensusState, found := k.GetClientConsensusState(ctx, misbehaviour.GetClientID(), e.Header1.TrustedHeight) + if !found { + return sdkerrors.Wrapf(types.ErrConsensusStateNotFound, "cannot check misbehaviour for client with ID %s", misbehaviour.GetClientID()) + } + clientState, err = tendermint.CheckMisbehaviourAndUpdateState( clientState, consensusState, misbehaviour, consensusState.GetHeight(), ctx.BlockTime(), ctx.ConsensusParams(), ) diff --git a/x/ibc/02-client/keeper/client_test.go b/x/ibc/02-client/keeper/client_test.go index 3d87540beb..05ffd1e730 100644 --- a/x/ibc/02-client/keeper/client_test.go +++ b/x/ibc/02-client/keeper/client_test.go @@ -60,12 +60,12 @@ func (suite *KeeperTestSuite) TestCreateClient() { func (suite *KeeperTestSuite) TestUpdateClientTendermint() { // Must create header creation functions since suite.header gets recreated on each test case createFutureUpdateFn := func(s *KeeperTestSuite) ibctmtypes.Header { - return ibctmtypes.CreateTestHeader(testChainID, suite.header.Height+1, suite.header.Time.Add(time.Minute), - suite.valSet, []tmtypes.PrivValidator{suite.privVal}) + return ibctmtypes.CreateTestHeader(testChainID, suite.header.Height+3, suite.header.Height, suite.header.Time.Add(time.Hour), + suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) } createPastUpdateFn := func(s *KeeperTestSuite) ibctmtypes.Header { - return ibctmtypes.CreateTestHeader(testChainID, suite.header.Height-3, suite.header.Time, - suite.valSet, []tmtypes.PrivValidator{suite.privVal}) + return ibctmtypes.CreateTestHeader(testChainID, suite.header.Height-2, suite.header.Height-4, suite.header.Time, + suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) } var ( updateHeader ibctmtypes.Header @@ -80,6 +80,18 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { {"valid update", func() error { clientState = ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs()) _, err := suite.keeper.CreateClient(suite.ctx, testClientID, clientState, suite.consensusState) + + // store intermediate consensus state to check that trustedHeight does not need to be highest consensus state before header height + intermediateConsState := ibctmtypes.ConsensusState{ + Height: testClientHeight + 1, + Timestamp: suite.now.Add(time.Minute), + NextValidatorsHash: suite.valSetHash, + } + suite.keeper.SetClientConsensusState(suite.ctx, testClientID, testClientHeight+1, intermediateConsState) + + clientState.LatestHeight = testClientHeight + 1 + suite.keeper.SetClientState(suite.ctx, testClientID, clientState) + updateHeader = createFutureUpdateFn(suite) return err }, true}, @@ -92,11 +104,18 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { prevConsState := ibctmtypes.ConsensusState{ Height: 1, Timestamp: suite.past, - NextValidatorsHash: suite.valSet.Hash(), - ValidatorSet: suite.valSet, + NextValidatorsHash: suite.valSetHash, } suite.keeper.SetClientConsensusState(suite.ctx, testClientID, 1, prevConsState) + // store intermediate consensus state to check that trustedHeight does not need to be hightest consensus state before header height + intermediateConsState := ibctmtypes.ConsensusState{ + Height: 2, + Timestamp: suite.past.Add(time.Minute), + NextValidatorsHash: suite.valSetHash, + } + suite.keeper.SetClientConsensusState(suite.ctx, testClientID, 2, intermediateConsState) + // updateHeader will fill in consensus state between prevConsState and suite.consState // clientState should not be updated updateHeader = createPastUpdateFn(suite) @@ -145,8 +164,7 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { prevConsState := ibctmtypes.ConsensusState{ Height: 1, Timestamp: suite.past, - NextValidatorsHash: suite.valSet.Hash(), - ValidatorSet: suite.valSet, + NextValidatorsHash: suite.valSetHash, } suite.keeper.SetClientConsensusState(suite.ctx, testClientID, 1, prevConsState) @@ -186,7 +204,6 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { Timestamp: updateHeader.Time, Root: commitmenttypes.NewMerkleRoot(updateHeader.AppHash), NextValidatorsHash: updateHeader.NextValidatorsHash, - ValidatorSet: updateHeader.ValidatorSet, } newClientState, found := suite.keeper.GetClientState(suite.ctx, testClientID) @@ -194,11 +211,6 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { consensusState, found := suite.keeper.GetClientConsensusState(suite.ctx, testClientID, updateHeader.GetHeight()) suite.Require().True(found, "valid test case %d failed: %s", i, tc.name) - tmConsState, ok := consensusState.(ibctmtypes.ConsensusState) - suite.Require().True(ok, "consensus state is not a tendermint consensus state") - // recalculate cached totalVotingPower field for equality check - tmConsState.ValidatorSet.TotalVotingPower() - // check returned client state is same as client state in store suite.Require().Equal(updatedClientState, newClientState, "updatedClient state not persisted correctly") @@ -263,13 +275,13 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { { "trusting period misbehavior should pass", ibctmtypes.Evidence{ - Header1: ibctmtypes.CreateTestHeader(testClientID, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothSigners), - Header2: ibctmtypes.CreateTestHeader(testClientID, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothSigners), + Header1: ibctmtypes.CreateTestHeader(testClientID, testClientHeight, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), + Header2: ibctmtypes.CreateTestHeader(testClientID, testClientHeight, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), ChainID: testClientID, ClientID: testClientID, }, func() error { - suite.consensusState.ValidatorSet = bothValSet + suite.consensusState.NextValidatorsHash = bothValSet.Hash() clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs()) _, err := suite.keeper.CreateClient(suite.ctx, testClientID, clientState, suite.consensusState) @@ -280,16 +292,27 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { { "misbehavior at later height should pass", ibctmtypes.Evidence{ - Header1: ibctmtypes.CreateTestHeader(testClientID, testClientHeight+5, suite.ctx.BlockTime(), bothValSet, bothSigners), - Header2: ibctmtypes.CreateTestHeader(testClientID, testClientHeight+5, suite.ctx.BlockTime(), bothValSet, bothSigners), + Header1: ibctmtypes.CreateTestHeader(testClientID, testClientHeight+5, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), + Header2: ibctmtypes.CreateTestHeader(testClientID, testClientHeight+5, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), ChainID: testClientID, ClientID: testClientID, }, func() error { - suite.consensusState.ValidatorSet = bothValSet + suite.consensusState.NextValidatorsHash = bothValSet.Hash() clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs()) _, err := suite.keeper.CreateClient(suite.ctx, testClientID, clientState, suite.consensusState) + // store intermediate consensus state to check that trustedHeight does not need to be highest consensus state before header height + intermediateConsState := ibctmtypes.ConsensusState{ + Height: testClientHeight + 3, + Timestamp: suite.now.Add(time.Minute), + NextValidatorsHash: suite.valSetHash, + } + suite.keeper.SetClientConsensusState(suite.ctx, testClientID, testClientHeight+3, intermediateConsState) + + clientState.LatestHeight = testClientHeight + 3 + suite.keeper.SetClientState(suite.ctx, testClientID, clientState) + return err }, true, @@ -303,8 +326,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { { "consensus state not found", ibctmtypes.Evidence{ - Header1: ibctmtypes.CreateTestHeader(testClientID, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothSigners), - Header2: ibctmtypes.CreateTestHeader(testClientID, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothSigners), + Header1: ibctmtypes.CreateTestHeader(testClientID, testClientHeight, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), + Header2: ibctmtypes.CreateTestHeader(testClientID, testClientHeight, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), ChainID: testClientID, ClientID: testClientID, }, @@ -318,8 +341,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { { "consensus state not found", ibctmtypes.Evidence{ - Header1: ibctmtypes.CreateTestHeader(testClientID, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothSigners), - Header2: ibctmtypes.CreateTestHeader(testClientID, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothSigners), + Header1: ibctmtypes.CreateTestHeader(testClientID, testClientHeight, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), + Header2: ibctmtypes.CreateTestHeader(testClientID, testClientHeight, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), ChainID: testClientID, ClientID: testClientID, }, @@ -333,8 +356,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { { "misbehaviour check failed", ibctmtypes.Evidence{ - Header1: ibctmtypes.CreateTestHeader(testClientID, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothSigners), - Header2: ibctmtypes.CreateTestHeader(testClientID, testClientHeight, suite.ctx.BlockTime(), altValSet, altSigners), + Header1: ibctmtypes.CreateTestHeader(testClientID, testClientHeight, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), + Header2: ibctmtypes.CreateTestHeader(testClientID, testClientHeight, testClientHeight, suite.ctx.BlockTime(), altValSet, bothValSet, altSigners), ChainID: testClientID, ClientID: testClientID, }, diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index f60baef62c..549a3531df 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -5,7 +5,6 @@ import ( "strings" "github.com/tendermint/tendermint/libs/log" - tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/prefix" @@ -15,7 +14,6 @@ import ( ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types" commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) // Keeper represents a type that grants read and write permissions to any client @@ -208,14 +206,11 @@ func (k Keeper) GetSelfConsensusState(ctx sdk.Context, height uint64) (exported. return nil, false } - valSet := stakingtypes.Validators(histInfo.Valset) - consensusState := ibctmtypes.ConsensusState{ Height: height, Timestamp: histInfo.Header.Time, Root: commitmenttypes.NewMerkleRoot(histInfo.Header.AppHash), NextValidatorsHash: histInfo.Header.NextValidatorsHash, - ValidatorSet: tmtypes.NewValidatorSet(valSet.ToTmValidators()), } return consensusState, true } diff --git a/x/ibc/02-client/keeper/keeper_test.go b/x/ibc/02-client/keeper/keeper_test.go index 6853b06531..9e3d26f781 100644 --- a/x/ibc/02-client/keeper/keeper_test.go +++ b/x/ibc/02-client/keeper/keeper_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/suite" abci "github.com/tendermint/tendermint/abci/types" + tmbytes "github.com/tendermint/tendermint/libs/bytes" tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/codec" @@ -45,6 +46,7 @@ type KeeperTestSuite struct { consensusState ibctmtypes.ConsensusState header ibctmtypes.Header valSet *tmtypes.ValidatorSet + valSetHash tmbytes.HexBytes privVal tmtypes.PrivValidator now time.Time past time.Time @@ -68,13 +70,13 @@ func (suite *KeeperTestSuite) SetupTest() { validator := tmtypes.NewValidator(pubKey, 1) suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) - suite.header = ibctmtypes.CreateTestHeader(testChainID, testClientHeight, now2, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) + suite.valSetHash = suite.valSet.Hash() + suite.header = ibctmtypes.CreateTestHeader(testChainID, testClientHeight, testClientHeight-1, now2, suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) suite.consensusState = ibctmtypes.ConsensusState{ Height: testClientHeight, Timestamp: suite.now, Root: commitmenttypes.NewMerkleRoot([]byte("hash")), - NextValidatorsHash: suite.valSet.Hash(), - ValidatorSet: suite.valSet, + NextValidatorsHash: suite.valSetHash, } var validators stakingtypes.Validators @@ -119,8 +121,6 @@ func (suite *KeeperTestSuite) TestSetClientConsensusState() { suite.Require().True(found, "GetConsensusState failed") tmConsState, ok := retrievedConsState.(ibctmtypes.ConsensusState) - // recalculate cached totalVotingPower field for equality check - tmConsState.ValidatorSet.TotalVotingPower() suite.Require().True(ok) suite.Require().Equal(suite.consensusState, tmConsState, "ConsensusState not stored correctly") } @@ -210,13 +210,14 @@ func (suite KeeperTestSuite) TestConsensusStateHelpers() { suite.keeper.SetClientConsensusState(suite.ctx, testClientID, testClientHeight, suite.consensusState) nextState := ibctmtypes.ConsensusState{ - Height: testClientHeight + 5, - Timestamp: suite.now, - Root: commitmenttypes.NewMerkleRoot([]byte("next")), - ValidatorSet: suite.valSet, + Height: testClientHeight + 5, + Timestamp: suite.now, + Root: commitmenttypes.NewMerkleRoot([]byte("next")), + NextValidatorsHash: suite.valSetHash, } - header := ibctmtypes.CreateTestHeader(testClientID, testClientHeight+5, suite.header.Time.Add(time.Minute), suite.valSet, []tmtypes.PrivValidator{suite.privVal}) + header := ibctmtypes.CreateTestHeader(testClientID, testClientHeight+5, testClientHeight, suite.header.Time.Add(time.Minute), + suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) // mock update functionality clientState.LatestHeight = uint64(header.Height) @@ -224,15 +225,11 @@ func (suite KeeperTestSuite) TestConsensusStateHelpers() { suite.keeper.SetClientState(suite.ctx, testClientID, clientState) latest, ok := suite.keeper.GetLatestClientConsensusState(suite.ctx, testClientID) - // recalculate cached totalVotingPower for equality check - latest.(ibctmtypes.ConsensusState).ValidatorSet.TotalVotingPower() suite.Require().True(ok) suite.Require().Equal(nextState, latest, "Latest client not returned correctly") // Should return existing consensusState at latestClientHeight lte, ok := suite.keeper.GetClientConsensusStateLTE(suite.ctx, testClientID, testClientHeight+3) - // recalculate cached totalVotingPower for equality check - lte.(ibctmtypes.ConsensusState).ValidatorSet.TotalVotingPower() suite.Require().True(ok) suite.Require().Equal(suite.consensusState, lte, "LTE helper function did not return latest client state below height: %d", testClientHeight+3) } @@ -243,10 +240,10 @@ func (suite KeeperTestSuite) TestGetAllConsensusStates() { testClientID, []exported.ConsensusState{ ibctmtypes.NewConsensusState( - suite.consensusState.Timestamp, commitmenttypes.NewMerkleRoot([]byte("hash")), suite.consensusState.GetHeight(), nil, &tmtypes.ValidatorSet{}, + suite.consensusState.Timestamp, commitmenttypes.NewMerkleRoot([]byte("hash")), suite.consensusState.GetHeight(), nil, ), ibctmtypes.NewConsensusState( - suite.consensusState.Timestamp.Add(time.Minute), commitmenttypes.NewMerkleRoot([]byte("app_hash")), suite.consensusState.GetHeight()+1, nil, &tmtypes.ValidatorSet{}, + suite.consensusState.Timestamp.Add(time.Minute), commitmenttypes.NewMerkleRoot([]byte("app_hash")), suite.consensusState.GetHeight()+1, nil, ), }, ), @@ -254,7 +251,7 @@ func (suite KeeperTestSuite) TestGetAllConsensusStates() { testClientID2, []exported.ConsensusState{ ibctmtypes.NewConsensusState( - suite.consensusState.Timestamp.Add(2*time.Minute), commitmenttypes.NewMerkleRoot([]byte("app_hash_2")), suite.consensusState.GetHeight()+2, nil, &tmtypes.ValidatorSet{}, + suite.consensusState.Timestamp.Add(2*time.Minute), commitmenttypes.NewMerkleRoot([]byte("app_hash_2")), suite.consensusState.GetHeight()+2, nil, ), }, ), diff --git a/x/ibc/02-client/types/genesis_test.go b/x/ibc/02-client/types/genesis_test.go index f0a8808686..fc358c3ce0 100644 --- a/x/ibc/02-client/types/genesis_test.go +++ b/x/ibc/02-client/types/genesis_test.go @@ -36,7 +36,7 @@ func TestValidateGenesis(t *testing.T) { val := tmtypes.NewValidator(pubKey, 10) valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{val}) - header := ibctmtypes.CreateTestHeader(chainID, height, now, valSet, []tmtypes.PrivValidator{privVal}) + header := ibctmtypes.CreateTestHeader(chainID, height, height-1, now, valSet, valSet, []tmtypes.PrivValidator{privVal}) testCases := []struct { name string @@ -64,7 +64,7 @@ func TestValidateGenesis(t *testing.T) { clientID, []exported.ConsensusState{ ibctmtypes.NewConsensusState( - header.Time, commitmenttypes.NewMerkleRoot(header.AppHash), header.GetHeight(), header.ValidatorSet.Hash(), header.ValidatorSet, + header.Time, commitmenttypes.NewMerkleRoot(header.AppHash), header.GetHeight(), header.NextValidatorsHash, ), }, }, @@ -89,7 +89,7 @@ func TestValidateGenesis(t *testing.T) { clientID, []exported.ConsensusState{ ibctmtypes.NewConsensusState( - header.Time, commitmenttypes.NewMerkleRoot(header.AppHash), header.GetHeight(), header.ValidatorSet.Hash(), header.ValidatorSet, + header.Time, commitmenttypes.NewMerkleRoot(header.AppHash), header.GetHeight(), header.NextValidatorsHash, ), }, }, @@ -129,7 +129,7 @@ func TestValidateGenesis(t *testing.T) { "CLIENTID2", []exported.ConsensusState{ ibctmtypes.NewConsensusState( - header.Time, commitmenttypes.NewMerkleRoot(header.AppHash), 0, header.ValidatorSet.Hash(), header.ValidatorSet, + header.Time, commitmenttypes.NewMerkleRoot(header.AppHash), 0, header.NextValidatorsHash, ), }, }, @@ -154,7 +154,7 @@ func TestValidateGenesis(t *testing.T) { clientID, []exported.ConsensusState{ ibctmtypes.NewConsensusState( - header.Time, commitmenttypes.NewMerkleRoot(header.AppHash), 0, header.ValidatorSet.Hash(), header.ValidatorSet, + header.Time, commitmenttypes.NewMerkleRoot(header.AppHash), 0, header.NextValidatorsHash, ), }, ), diff --git a/x/ibc/07-tendermint/misbehaviour.go b/x/ibc/07-tendermint/misbehaviour.go index 1e28afec3a..62373cfa3b 100644 --- a/x/ibc/07-tendermint/misbehaviour.go +++ b/x/ibc/07-tendermint/misbehaviour.go @@ -1,6 +1,7 @@ package tendermint import ( + "bytes" "time" abci "github.com/tendermint/tendermint/abci/types" @@ -66,7 +67,20 @@ func checkMisbehaviour( infractionHeight := evidence.GetHeight() infractionTime := evidence.GetTime() ageDuration := currentTimestamp.Sub(infractionTime) - ageBlocks := height - uint64(infractionHeight) + ageBlocks := uint64(infractionHeight) - height + + // assert that trustedVals is NextValidators of last trusted header + // to do this, we check that trustedVals.Hash() == consState.NextValidatorsHash + trustedValsetHash1 := evidence.Header1.TrustedValidators.Hash() + trustedValsetHash2 := evidence.Header2.TrustedValidators.Hash() + + if !bytes.Equal(consensusState.NextValidatorsHash, trustedValsetHash1) || !bytes.Equal(consensusState.NextValidatorsHash, trustedValsetHash2) { + return sdkerrors.Wrapf( + types.ErrInvalidValidatorSet, + "header's trusted validators %s, does not hash to either consensus state's trusted validator set. Expected: %X, got: TrustedValSet Header1 %X, TrustedValSet Header2 %X", + evidence.Header1.TrustedValidators, consensusState.NextValidatorsHash, trustedValsetHash1, trustedValsetHash2, + ) + } // Reject misbehaviour if the age is too old. Evidence is considered stale // if the difference in time and number of blocks is greater than the allowed @@ -108,18 +122,18 @@ func checkMisbehaviour( // - ValidatorSet must have 2/3 similarity with trusted FromValidatorSet // - ValidatorSets on both headers are valid given the last trusted ValidatorSet - if err := consensusState.ValidatorSet.VerifyCommitLightTrusting( + if err := evidence.Header1.TrustedValidators.VerifyCommitLightTrusting( evidence.ChainID, evidence.Header1.Commit.BlockID, evidence.Header1.Height, evidence.Header1.Commit, clientState.TrustLevel.ToTendermint(), ); err != nil { - return sdkerrors.Wrapf(clienttypes.ErrInvalidEvidence, "validator set in header 1 has too much change from last known validator set: %v", err) + return sdkerrors.Wrapf(clienttypes.ErrInvalidEvidence, "validator set in header 1 has too much change from trusted validator set: %v", err) } - if err := consensusState.ValidatorSet.VerifyCommitLightTrusting( + if err := evidence.Header2.TrustedValidators.VerifyCommitLightTrusting( evidence.ChainID, evidence.Header2.Commit.BlockID, evidence.Header2.Height, evidence.Header2.Commit, clientState.TrustLevel.ToTendermint(), ); err != nil { - return sdkerrors.Wrapf(clienttypes.ErrInvalidEvidence, "validator set in header 2 has too much change from last known validator set: %v", err) + return sdkerrors.Wrapf(clienttypes.ErrInvalidEvidence, "validator set in header 2 has too much change from trusted validator set: %v", err) } return nil diff --git a/x/ibc/07-tendermint/misbehaviour_test.go b/x/ibc/07-tendermint/misbehaviour_test.go index c364103a93..fc586998c0 100644 --- a/x/ibc/07-tendermint/misbehaviour_test.go +++ b/x/ibc/07-tendermint/misbehaviour_test.go @@ -24,6 +24,7 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() { // Create bothValSet with both suite validator and altVal bothValSet := tmtypes.NewValidatorSet(append(suite.valSet.Validators, altVal)) + bothValsHash := bothValSet.Hash() // Create alternative validator set with only altVal altValSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{altVal}) @@ -54,10 +55,10 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() { { "valid misbehavior evidence", types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), - types.ConsensusState{Timestamp: suite.now, Root: commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), ValidatorSet: bothValSet}, + types.ConsensusState{Timestamp: suite.now, Root: commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), NextValidatorsHash: bothValsHash}, types.Evidence{ - Header1: types.CreateTestHeader(chainID, height, suite.now, bothValSet, bothSigners), - Header2: types.CreateTestHeader(chainID, height, suite.now.Add(time.Minute), bothValSet, bothSigners), + Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, bothValSet, bothSigners), + Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), ChainID: chainID, ClientID: chainID, }, @@ -69,10 +70,10 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() { { "valid misbehavior at height greater than last consensusState", types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), - types.ConsensusState{Timestamp: suite.now, Height: height - 1, Root: commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), ValidatorSet: bothValSet}, + types.ConsensusState{Timestamp: suite.now, Height: height - 1, Root: commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), NextValidatorsHash: bothValsHash}, types.Evidence{ - Header1: types.CreateTestHeader(chainID, height, suite.now, bothValSet, bothSigners), - Header2: types.CreateTestHeader(chainID, height, suite.now.Add(time.Minute), bothValSet, bothSigners), + Header1: types.CreateTestHeader(chainID, height, height-1, suite.now, bothValSet, bothValSet, bothSigners), + Header2: types.CreateTestHeader(chainID, height, height-1, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), ChainID: chainID, ClientID: chainID, }, @@ -84,25 +85,25 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() { { "consensus state's valset hash different from evidence should still pass", types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), - types.ConsensusState{Timestamp: suite.now, Height: height - 1, Root: commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), ValidatorSet: suite.valSet}, + types.ConsensusState{Timestamp: suite.now, Height: height, Root: commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), NextValidatorsHash: suite.valsHash}, types.Evidence{ - Header1: types.CreateTestHeader(chainID, height, suite.now, bothValSet, bothSigners), - Header2: types.CreateTestHeader(chainID, height, suite.now.Add(time.Minute), bothValSet, bothSigners), + Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, suite.valSet, bothSigners), + Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), ChainID: chainID, ClientID: chainID, }, simapp.DefaultConsensusParams, - height - 1, + height, suite.now, true, }, { "invalid tendermint client state", nil, - types.ConsensusState{Timestamp: suite.now, Root: commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), ValidatorSet: bothValSet}, + types.ConsensusState{Timestamp: suite.now, Root: commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), NextValidatorsHash: bothValsHash}, types.Evidence{ - Header1: types.CreateTestHeader(chainID, height, suite.now, altValSet, altSigners), - Header2: types.CreateTestHeader(chainID, height, suite.now.Add(time.Minute), bothValSet, bothSigners), + Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, bothValSet, bothSigners), + Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, altValSet, bothSigners), ChainID: chainID, ClientID: chainID, }, @@ -114,10 +115,10 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() { { "already frozen client state", types.ClientState{FrozenHeight: 1}, - types.ConsensusState{Timestamp: suite.now, Root: commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), ValidatorSet: bothValSet}, + types.ConsensusState{Timestamp: suite.now, Root: commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), NextValidatorsHash: bothValsHash}, types.Evidence{ - Header1: types.CreateTestHeader(chainID, height, suite.now, bothValSet, bothSigners), - Header2: types.CreateTestHeader(chainID, height, suite.now.Add(time.Minute), bothValSet, bothSigners), + Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, bothValSet, bothSigners), + Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), ChainID: chainID, ClientID: chainID, }, @@ -131,8 +132,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() { types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), nil, types.Evidence{ - Header1: types.CreateTestHeader(chainID, height, suite.now, altValSet, altSigners), - Header2: types.CreateTestHeader(chainID, height, suite.now.Add(time.Minute), bothValSet, bothSigners), + Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, bothValSet, bothSigners), + Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), ChainID: chainID, ClientID: chainID, }, @@ -144,7 +145,7 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() { { "invalid tendermint misbehaviour evidence", types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), - types.ConsensusState{Timestamp: suite.now, Root: commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), ValidatorSet: bothValSet}, + types.ConsensusState{Timestamp: suite.now, Root: commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), NextValidatorsHash: bothValsHash}, nil, simapp.DefaultConsensusParams, height, @@ -154,25 +155,27 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() { { "rejected misbehaviour due to expired age", types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), - types.ConsensusState{Timestamp: suite.now, Root: commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), ValidatorSet: bothValSet}, + types.ConsensusState{Timestamp: suite.now, Root: commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), NextValidatorsHash: bothValsHash}, types.Evidence{ - Header1: types.CreateTestHeader(chainID, height, suite.now, bothValSet, bothSigners), - Header2: types.CreateTestHeader(chainID, height, suite.now.Add(time.Minute), bothValSet, bothSigners), + Header1: types.CreateTestHeader(chainID, int64(2*height+uint64(simapp.DefaultConsensusParams.Evidence.MaxAgeNumBlocks)), height, + suite.now, bothValSet, bothValSet, bothSigners), + Header2: types.CreateTestHeader(chainID, int64(2*height+uint64(simapp.DefaultConsensusParams.Evidence.MaxAgeNumBlocks)), height, + suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), ChainID: chainID, ClientID: chainID, }, simapp.DefaultConsensusParams, - 2*height + uint64(simapp.DefaultConsensusParams.Evidence.MaxAgeNumBlocks), + height, suite.now.Add(2 * time.Minute).Add(simapp.DefaultConsensusParams.Evidence.MaxAgeDuration), false, }, { - "provided height ≠ header height", + "provided height > header height", types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), - types.ConsensusState{Timestamp: suite.now, Root: commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), ValidatorSet: bothValSet}, + types.ConsensusState{Timestamp: suite.now, Root: commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), NextValidatorsHash: bothValsHash}, types.Evidence{ - Header1: types.CreateTestHeader(chainID, height, suite.now, bothValSet, bothSigners), - Header2: types.CreateTestHeader(chainID, height, suite.now.Add(time.Minute), bothValSet, bothSigners), + Header1: types.CreateTestHeader(chainID, height, height-1, suite.now, bothValSet, bothValSet, bothSigners), + Header2: types.CreateTestHeader(chainID, height, height-1, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), ChainID: chainID, ClientID: chainID, }, @@ -184,10 +187,25 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() { { "unbonding period expired", types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), - types.ConsensusState{Timestamp: time.Time{}, Root: commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), ValidatorSet: bothValSet}, + types.ConsensusState{Timestamp: time.Time{}, Root: commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), NextValidatorsHash: bothValsHash}, types.Evidence{ - Header1: types.CreateTestHeader(chainID, height, suite.now, bothValSet, bothSigners), - Header2: types.CreateTestHeader(chainID, height, suite.now.Add(time.Minute), bothValSet, bothSigners), + Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, bothValSet, bothSigners), + Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + ChainID: chainID, + ClientID: chainID, + }, + simapp.DefaultConsensusParams, + height, + suite.now, + false, + }, + { + "trusted validators is incorrect for given consensus state", + types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), + types.ConsensusState{Timestamp: suite.now, Root: commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), NextValidatorsHash: bothValsHash}, + types.Evidence{ + Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, suite.valSet, bothSigners), + Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), ChainID: chainID, ClientID: chainID, }, @@ -199,10 +217,10 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() { { "first valset has too much change", types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), - types.ConsensusState{Timestamp: suite.now, Root: commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), ValidatorSet: bothValSet}, + types.ConsensusState{Timestamp: suite.now, Root: commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), NextValidatorsHash: bothValsHash}, types.Evidence{ - Header1: types.CreateTestHeader(chainID, height, suite.now, altValSet, altSigners), - Header2: types.CreateTestHeader(chainID, height, suite.now.Add(time.Minute), bothValSet, bothSigners), + Header1: types.CreateTestHeader(chainID, height, height, suite.now, altValSet, bothValSet, altSigners), + Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), ChainID: chainID, ClientID: chainID, }, @@ -214,10 +232,10 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() { { "second valset has too much change", types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), - types.ConsensusState{Timestamp: suite.now, Root: commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), ValidatorSet: bothValSet}, + types.ConsensusState{Timestamp: suite.now, Root: commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), NextValidatorsHash: bothValsHash}, types.Evidence{ - Header1: types.CreateTestHeader(chainID, height, suite.now, bothValSet, bothSigners), - Header2: types.CreateTestHeader(chainID, height, suite.now.Add(time.Minute), altValSet, altSigners), + Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, bothValSet, bothSigners), + Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), altValSet, bothValSet, altSigners), ChainID: chainID, ClientID: chainID, }, @@ -229,10 +247,10 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() { { "both valsets have too much change", types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), - types.ConsensusState{Timestamp: suite.now, Root: commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), ValidatorSet: bothValSet}, + types.ConsensusState{Timestamp: suite.now, Root: commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), NextValidatorsHash: bothValsHash}, types.Evidence{ - Header1: types.CreateTestHeader(chainID, height, suite.now, altValSet, altSigners), - Header2: types.CreateTestHeader(chainID, height, suite.now.Add(time.Minute), altValSet, altSigners), + Header1: types.CreateTestHeader(chainID, height, height, suite.now, altValSet, bothValSet, altSigners), + Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), altValSet, bothValSet, altSigners), ChainID: chainID, ClientID: chainID, }, diff --git a/x/ibc/07-tendermint/tendermint_test.go b/x/ibc/07-tendermint/tendermint_test.go index a2bf6bfd48..d6e7e1bb17 100644 --- a/x/ibc/07-tendermint/tendermint_test.go +++ b/x/ibc/07-tendermint/tendermint_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/suite" + tmbytes "github.com/tendermint/tendermint/libs/bytes" tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/codec" @@ -29,6 +30,7 @@ type TendermintTestSuite struct { signers []tmtypes.PrivValidator privVal tmtypes.PrivValidator valSet *tmtypes.ValidatorSet + valsHash tmbytes.HexBytes header ibctmtypes.Header now time.Time clientTime time.Time @@ -55,11 +57,12 @@ func (suite *TendermintTestSuite) SetupTest() { val := tmtypes.NewValidator(pubKey, 10) suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{val}) + suite.valsHash = suite.valSet.Hash() // Suite header is intended to be header passed in for initial ClientState // Thus it should have same height and time as ClientState // Note: default header has the same validator set suite.valSet as next validators set - suite.header = ibctmtypes.CreateTestHeader(chainID, height, suite.clientTime, suite.valSet, suite.signers) + suite.header = ibctmtypes.CreateTestHeader(chainID, height, height-1, suite.clientTime, suite.valSet, suite.valSet, suite.signers) } func TestTendermintTestSuite(t *testing.T) { diff --git a/x/ibc/07-tendermint/types/client_state_test.go b/x/ibc/07-tendermint/types/client_state_test.go index 79e8b0009a..4558708f9c 100644 --- a/x/ibc/07-tendermint/types/client_state_test.go +++ b/x/ibc/07-tendermint/types/client_state_test.go @@ -136,8 +136,8 @@ func (suite *TendermintTestSuite) TestVerifyClientConsensusState() { name: "proof verification failed", clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), consensusState: ibctmtypes.ConsensusState{ - Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), - ValidatorSet: suite.valSet, + Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), + NextValidatorsHash: suite.valsHash, }, prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), proof: []byte{}, @@ -219,8 +219,8 @@ func (suite *TendermintTestSuite) TestVerifyConnectionState() { clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), connection: conn, consensusState: ibctmtypes.ConsensusState{ - Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), - ValidatorSet: suite.valSet, + Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), + NextValidatorsHash: suite.valsHash, }, prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), proof: []byte{}, @@ -302,8 +302,8 @@ func (suite *TendermintTestSuite) TestVerifyChannelState() { clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), channel: ch, consensusState: ibctmtypes.ConsensusState{ - Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), - ValidatorSet: suite.valSet, + Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), + NextValidatorsHash: suite.valsHash, }, prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), proof: []byte{}, @@ -382,8 +382,8 @@ func (suite *TendermintTestSuite) TestVerifyPacketCommitment() { clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), commitment: []byte{}, consensusState: ibctmtypes.ConsensusState{ - Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), - ValidatorSet: suite.valSet, + Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), + NextValidatorsHash: suite.valsHash, }, prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), proof: []byte{}, @@ -462,8 +462,8 @@ func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgement() { clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), ack: []byte{}, consensusState: ibctmtypes.ConsensusState{ - Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), - ValidatorSet: suite.valSet, + Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), + NextValidatorsHash: suite.valsHash, }, prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), proof: []byte{}, @@ -537,8 +537,8 @@ func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgementAbsence() { name: "proof verification failed", clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), consensusState: ibctmtypes.ConsensusState{ - Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), - ValidatorSet: suite.valSet, + Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), + NextValidatorsHash: suite.valsHash, }, prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), proof: []byte{}, @@ -612,8 +612,8 @@ func (suite *TendermintTestSuite) TestVerifyNextSeqRecv() { name: "proof verification failed", clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), consensusState: ibctmtypes.ConsensusState{ - Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), - ValidatorSet: suite.valSet, + Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), + NextValidatorsHash: suite.valsHash, }, prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), proof: []byte{}, diff --git a/x/ibc/07-tendermint/types/consensus_state.go b/x/ibc/07-tendermint/types/consensus_state.go index f601c03b2e..b2a5426113 100644 --- a/x/ibc/07-tendermint/types/consensus_state.go +++ b/x/ibc/07-tendermint/types/consensus_state.go @@ -18,20 +18,18 @@ type ConsensusState struct { Root commitmentexported.Root `json:"root" yaml:"root"` Height uint64 `json:"height" yaml:"height"` NextValidatorsHash tmbytes.HexBytes `json:"next_validators_hash"` // validators hash for the next block - ValidatorSet *tmtypes.ValidatorSet `json:"validator_set" yaml:"validator_set"` } // NewConsensusState creates a new ConsensusState instance. func NewConsensusState( timestamp time.Time, root commitmentexported.Root, height uint64, - nextValsHash tmbytes.HexBytes, valset *tmtypes.ValidatorSet, + nextValsHash tmbytes.HexBytes, ) ConsensusState { return ConsensusState{ Timestamp: timestamp, Root: root, Height: height, NextValidatorsHash: nextValsHash, - ValidatorSet: valset, } } @@ -60,9 +58,6 @@ func (cs ConsensusState) ValidateBasic() error { if cs.Root == nil || cs.Root.Empty() { return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "root cannot be empty") } - if cs.ValidatorSet == nil { - return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "validator set cannot be nil") - } if err := tmtypes.ValidateHash(cs.NextValidatorsHash); err != nil { return sdkerrors.Wrap(err, "next validators hash is invalid") } diff --git a/x/ibc/07-tendermint/types/consensus_state_test.go b/x/ibc/07-tendermint/types/consensus_state_test.go index ac1229b405..2fdce5b209 100644 --- a/x/ibc/07-tendermint/types/consensus_state_test.go +++ b/x/ibc/07-tendermint/types/consensus_state_test.go @@ -16,50 +16,51 @@ func (suite *TendermintTestSuite) TestConsensusStateValidateBasic() { }{ {"success", ibctmtypes.ConsensusState{ - Timestamp: suite.now, - Height: height, - Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")), - ValidatorSet: suite.valSet, + Timestamp: suite.now, + Height: height, + Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")), + NextValidatorsHash: suite.valsHash, }, true}, {"root is nil", ibctmtypes.ConsensusState{ - Timestamp: suite.now, - Height: height, - Root: nil, - ValidatorSet: suite.valSet, + Timestamp: suite.now, + Height: height, + Root: nil, + NextValidatorsHash: suite.valsHash, }, false}, {"root is empty", ibctmtypes.ConsensusState{ - Timestamp: suite.now, - Height: height, - Root: commitmenttypes.MerkleRoot{}, - ValidatorSet: suite.valSet, + Timestamp: suite.now, + Height: height, + Root: commitmenttypes.MerkleRoot{}, + NextValidatorsHash: suite.valsHash, }, false}, - {"valset is nil", + {"nextvalshash is invalid", ibctmtypes.ConsensusState{ - Timestamp: suite.now, - Height: height, - Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")), - ValidatorSet: nil, + Timestamp: suite.now, + Height: height, + Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")), + NextValidatorsHash: []byte("hi"), }, false}, + {"height is 0", ibctmtypes.ConsensusState{ - Timestamp: suite.now, - Height: 0, - Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")), - ValidatorSet: suite.valSet, + Timestamp: suite.now, + Height: 0, + Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")), + NextValidatorsHash: suite.valsHash, }, false}, {"timestamp is zero", ibctmtypes.ConsensusState{ - Timestamp: time.Time{}, - Height: height, - Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")), - ValidatorSet: suite.valSet, + Timestamp: time.Time{}, + Height: height, + Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")), + NextValidatorsHash: suite.valsHash, }, false}, } diff --git a/x/ibc/07-tendermint/types/errors.go b/x/ibc/07-tendermint/types/errors.go index dd6a3f79c8..9683dbf3cf 100644 --- a/x/ibc/07-tendermint/types/errors.go +++ b/x/ibc/07-tendermint/types/errors.go @@ -5,7 +5,7 @@ import ( ) const ( - SubModuleName = "tendermint" + SubModuleName = "tendermint-client" ) // IBC tendermint client sentinel errors @@ -19,4 +19,5 @@ var ( ErrTrustingPeriodExpired = sdkerrors.Register(SubModuleName, 8, "time since latest trusted state has passed the trusting period") ErrUnbondingPeriodExpired = sdkerrors.Register(SubModuleName, 9, "time since latest trusted state has passed the unbonding period") ErrInvalidProofSpecs = sdkerrors.Register(SubModuleName, 10, "invalid proof specs") + ErrInvalidValidatorSet = sdkerrors.Register(SubModuleName, 11, "invalid validator set") ) diff --git a/x/ibc/07-tendermint/types/evidence.go b/x/ibc/07-tendermint/types/evidence.go index b29c05e7bb..cdb638f5c9 100644 --- a/x/ibc/07-tendermint/types/evidence.go +++ b/x/ibc/07-tendermint/types/evidence.go @@ -1,6 +1,7 @@ package types import ( + "bytes" "math" "time" @@ -85,6 +86,17 @@ func (ev Evidence) GetTime() time.Time { // ValidateBasic implements Evidence interface func (ev Evidence) ValidateBasic() error { + if ev.Header1.TrustedHeight != ev.Header2.TrustedHeight { + return sdkerrors.Wrapf(ErrInvalidHeaderHeight, "evidence headers must share the same trusted height. got height1: %d, height2: %d", + ev.Header1.TrustedHeight, ev.Header2.TrustedHeight) + } + if !bytes.Equal(ev.Header1.TrustedValidators.Hash(), ev.Header2.TrustedValidators.Hash()) { + return sdkerrors.Wrapf(ErrInvalidValidatorSet, "trusted validators on both submitted headers must be the same. Got valset1: %s, valset2: %s", + ev.Header1.TrustedValidators, ev.Header2.TrustedValidators) + } + if ev.Header1.TrustedValidators == nil { + return sdkerrors.Wrap(ErrInvalidValidatorSet, "trusted validator set cannot be empty") + } if err := host.ClientIdentifierValidator(ev.ClientID); err != nil { return sdkerrors.Wrap(err, "evidence client ID is invalid") } diff --git a/x/ibc/07-tendermint/types/evidence_test.go b/x/ibc/07-tendermint/types/evidence_test.go index 0277b147b9..9e99b3bed0 100644 --- a/x/ibc/07-tendermint/types/evidence_test.go +++ b/x/ibc/07-tendermint/types/evidence_test.go @@ -17,7 +17,7 @@ func (suite *TendermintTestSuite) TestEvidence() { ev := ibctmtypes.Evidence{ Header1: suite.header, - Header2: ibctmtypes.CreateTestHeader(chainID, height, suite.now, suite.valSet, signers), + Header2: ibctmtypes.CreateTestHeader(chainID, height, height-1, suite.now, suite.valSet, suite.valSet, signers), ChainID: chainID, ClientID: "gaiamainnet", } @@ -67,18 +67,40 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() { "valid evidence", ibctmtypes.Evidence{ Header1: suite.header, - Header2: ibctmtypes.CreateTestHeader(chainID, height, suite.now.Add(time.Minute), suite.valSet, signers), + Header2: ibctmtypes.CreateTestHeader(chainID, height, height-1, suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers), ChainID: chainID, ClientID: "gaiamainnet", }, func(ev *ibctmtypes.Evidence) error { return nil }, true, }, + { + "trusted heights don't match", + ibctmtypes.Evidence{ + Header1: suite.header, + Header2: ibctmtypes.CreateTestHeader(chainID, height, height-2, suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers), + ChainID: chainID, + ClientID: "gaiamainnet", + }, + func(ev *ibctmtypes.Evidence) error { return nil }, + false, + }, + { + "trusted valsets don't match", + ibctmtypes.Evidence{ + Header1: suite.header, + Header2: ibctmtypes.CreateTestHeader(chainID, height, height-1, suite.now.Add(time.Minute), suite.valSet, bothValSet, signers), + ChainID: chainID, + ClientID: "gaiamainnet", + }, + func(ev *ibctmtypes.Evidence) error { return nil }, + false, + }, { "invalid client ID ", ibctmtypes.Evidence{ Header1: suite.header, - Header2: ibctmtypes.CreateTestHeader(chainID, height, suite.now, suite.valSet, signers), + Header2: ibctmtypes.CreateTestHeader(chainID, height, height-1, suite.now, suite.valSet, suite.valSet, signers), ChainID: chainID, ClientID: "GAIA", }, @@ -89,7 +111,7 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() { "wrong chainID on header1", ibctmtypes.Evidence{ Header1: suite.header, - Header2: ibctmtypes.CreateTestHeader("ethermint", height, suite.now, suite.valSet, signers), + Header2: ibctmtypes.CreateTestHeader("ethermint", height, height-1, suite.now, suite.valSet, suite.valSet, signers), ChainID: "ethermint", ClientID: "gaiamainnet", }, @@ -100,7 +122,7 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() { "wrong chainID on header2", ibctmtypes.Evidence{ Header1: suite.header, - Header2: ibctmtypes.CreateTestHeader("ethermint", height, suite.now, suite.valSet, signers), + Header2: ibctmtypes.CreateTestHeader("ethermint", height, height-1, suite.now, suite.valSet, suite.valSet, signers), ChainID: chainID, ClientID: "gaiamainnet", }, @@ -111,7 +133,7 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() { "mismatched heights", ibctmtypes.Evidence{ Header1: suite.header, - Header2: ibctmtypes.CreateTestHeader(chainID, 6, suite.now, suite.valSet, signers), + Header2: ibctmtypes.CreateTestHeader(chainID, 6, 4, suite.now, suite.valSet, suite.valSet, signers), ChainID: chainID, ClientID: "gaiamainnet", }, @@ -132,7 +154,7 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() { { "header 1 doesn't have 2/3 majority", ibctmtypes.Evidence{ - Header1: ibctmtypes.CreateTestHeader(chainID, height, suite.now, bothValSet, bothSigners), + Header1: ibctmtypes.CreateTestHeader(chainID, height, height-1, suite.now, bothValSet, suite.valSet, bothSigners), Header2: suite.header, ChainID: chainID, ClientID: "gaiamainnet", @@ -150,7 +172,7 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() { "header 2 doesn't have 2/3 majority", ibctmtypes.Evidence{ Header1: suite.header, - Header2: ibctmtypes.CreateTestHeader(chainID, height, suite.now, bothValSet, bothSigners), + Header2: ibctmtypes.CreateTestHeader(chainID, height, height-1, suite.now, bothValSet, suite.valSet, bothSigners), ChainID: chainID, ClientID: "gaiamainnet", }, @@ -167,7 +189,7 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() { "validators sign off on wrong commit", ibctmtypes.Evidence{ Header1: suite.header, - Header2: ibctmtypes.CreateTestHeader(chainID, height, suite.now, bothValSet, bothSigners), + Header2: ibctmtypes.CreateTestHeader(chainID, height, height-1, suite.now, bothValSet, suite.valSet, bothSigners), ChainID: chainID, ClientID: "gaiamainnet", }, diff --git a/x/ibc/07-tendermint/types/header.go b/x/ibc/07-tendermint/types/header.go index d82df6c3ce..89003415ec 100644 --- a/x/ibc/07-tendermint/types/header.go +++ b/x/ibc/07-tendermint/types/header.go @@ -14,10 +14,20 @@ import ( var _ clientexported.Header = Header{} -// Header defines the Tendermint consensus Header +// Header defines the Tendermint client consensus Header. +// It encapsulates all the information necessary to update from a trusted Tendermint ConsensusState. +// The inclusion of TrustedHeight and TrustedValidators allows this update to process correctly, so long +// as the ConsensusState for the TrustedHeight exists, this removes race conditions among relayers +// The SignedHeader and ValidatorSet are the new untrusted update fields for the client. +// The TrustedHeight is the height of a stored ConsensusState on the client that will be used to verify the new untrusted header. +// The Trusted ConsensusState must be within the unbonding period of current time in order to correctly verify, +// and the TrustedValidators must hash to TrustedConsensusState.NextValidatorsHash since that is the last trusted validator set +// at the TrustedHeight. type Header struct { tmtypes.SignedHeader `json:"signed_header" yaml:"signed_header"` // contains the commitment root - ValidatorSet *tmtypes.ValidatorSet `json:"validator_set" yaml:"validator_set"` + ValidatorSet *tmtypes.ValidatorSet `json:"validator_set" yaml:"validator_set"` // the validator set that signed Header + TrustedHeight uint64 `json:"trusted_height" yaml:"trusted_height"` // the height of a trusted header seen by client less than or equal to Header + TrustedValidators *tmtypes.ValidatorSet `json:"trusted_vals" yaml:"trusted_vals"` // the last trusted validator set at trusted height } // ClientType defines that the Header is a Tendermint consensus algorithm @@ -25,13 +35,13 @@ func (h Header) ClientType() clientexported.ClientType { return clientexported.Tendermint } -// ConsensusState returns the consensus state associated with the header +// ConsensusState returns the updated consensus state associated with the header func (h Header) ConsensusState() ConsensusState { return ConsensusState{ - Height: uint64(h.Height), - Timestamp: h.Time, - Root: commitmenttypes.NewMerkleRoot(h.AppHash), - ValidatorSet: h.ValidatorSet, + Height: uint64(h.Height), + Timestamp: h.Time, + Root: commitmenttypes.NewMerkleRoot(h.AppHash), + NextValidatorsHash: h.NextValidatorsHash, } } @@ -44,10 +54,18 @@ func (h Header) GetHeight() uint64 { // ValidateBasic calls the SignedHeader ValidateBasic function // and checks that validatorsets are not nil +// NOTE: TrustedHeight and TrustedValidators may be empty when creating client +// with MsgCreateClient func (h Header) ValidateBasic(chainID string) error { if err := h.SignedHeader.ValidateBasic(chainID); err != nil { return sdkerrors.Wrap(err, "header failed basic validation") } + // TrustedHeight is less than Header for updates + // and less than or equal to Header for misbehaviour + if h.TrustedHeight > uint64(h.Height) { + return sdkerrors.Wrapf(ErrInvalidHeaderHeight, "TrustedHeight %d must be less than or equal to header height %d", + h.TrustedHeight, h.Height) + } if h.ValidatorSet == nil { return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "validator set is nil") } diff --git a/x/ibc/07-tendermint/types/header_test.go b/x/ibc/07-tendermint/types/header_test.go index c5c004028a..c801c5ddc7 100644 --- a/x/ibc/07-tendermint/types/header_test.go +++ b/x/ibc/07-tendermint/types/header_test.go @@ -14,7 +14,7 @@ func (suite *TendermintTestSuite) TestHeaderValidateBasic() { }{ {"valid header", suite.header, chainID, true}, {"signed header basic validation failed", suite.header, "chainID", false}, - {"validator set nil", ibctmtypes.Header{suite.header.SignedHeader, nil}, chainID, false}, + {"validator set nil", ibctmtypes.Header{suite.header.SignedHeader, nil, 0, suite.valSet}, chainID, false}, } suite.Require().Equal(clientexported.Tendermint, suite.header.ClientType()) diff --git a/x/ibc/07-tendermint/types/msgs.go b/x/ibc/07-tendermint/types/msgs.go index 342fcd4a3f..372d4cc787 100644 --- a/x/ibc/07-tendermint/types/msgs.go +++ b/x/ibc/07-tendermint/types/msgs.go @@ -145,7 +145,6 @@ func (msg MsgCreateClient) GetConsensusState() clientexported.ConsensusState { Root: root, Height: uint64(msg.Header.Height), NextValidatorsHash: msg.Header.NextValidatorsHash, - ValidatorSet: msg.Header.ValidatorSet, } } diff --git a/x/ibc/07-tendermint/types/msgs_test.go b/x/ibc/07-tendermint/types/msgs_test.go index 29a783e22e..8c516ec287 100644 --- a/x/ibc/07-tendermint/types/msgs_test.go +++ b/x/ibc/07-tendermint/types/msgs_test.go @@ -14,7 +14,7 @@ import ( func (suite *TendermintTestSuite) TestMsgCreateClientValidateBasic() { privKey := secp256k1.GenPrivKey() signer := sdk.AccAddress(privKey.PubKey().Address()) - invalidHeader := types.CreateTestHeader(suite.header.ChainID, height, suite.now, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) + invalidHeader := types.CreateTestHeader(suite.header.ChainID, height, 0, suite.now, suite.valSet, nil, []tmtypes.PrivValidator{suite.privVal}) invalidHeader.ValidatorSet = nil cases := []struct { diff --git a/x/ibc/07-tendermint/types/tendermint_test.go b/x/ibc/07-tendermint/types/tendermint_test.go index 395d10d285..ea0e7ce7f1 100644 --- a/x/ibc/07-tendermint/types/tendermint_test.go +++ b/x/ibc/07-tendermint/types/tendermint_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/suite" abci "github.com/tendermint/tendermint/abci/types" + tmbytes "github.com/tendermint/tendermint/libs/bytes" tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/codec" @@ -31,6 +32,7 @@ type TendermintTestSuite struct { cdc codec.Marshaler privVal tmtypes.PrivValidator valSet *tmtypes.ValidatorSet + valsHash tmbytes.HexBytes header ibctmtypes.Header now time.Time } @@ -50,7 +52,8 @@ func (suite *TendermintTestSuite) SetupTest() { val := tmtypes.NewValidator(pubKey, 10) suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{val}) - suite.header = ibctmtypes.CreateTestHeader(chainID, height, suite.now, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) + suite.valsHash = suite.valSet.Hash() + suite.header = ibctmtypes.CreateTestHeader(chainID, height, height-1, suite.now, suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) suite.ctx = app.BaseApp.NewContext(checkTx, abci.Header{Height: 1, Time: suite.now}) } diff --git a/x/ibc/07-tendermint/types/test_utils.go b/x/ibc/07-tendermint/types/test_utils.go index 316a44ff7a..125b53c5b1 100644 --- a/x/ibc/07-tendermint/types/test_utils.go +++ b/x/ibc/07-tendermint/types/test_utils.go @@ -22,7 +22,7 @@ func MakeBlockID(hash []byte, partSetSize int, partSetHash []byte) tmtypes.Block } // CreateTestHeader creates a mock header for testing only. -func CreateTestHeader(chainID string, height int64, timestamp time.Time, valSet *tmtypes.ValidatorSet, signers []tmtypes.PrivValidator) Header { +func CreateTestHeader(chainID string, height, trustedHeight int64, timestamp time.Time, valSet, trustedVals *tmtypes.ValidatorSet, signers []tmtypes.PrivValidator) Header { vsetHash := valSet.Hash() tmHeader := tmtypes.Header{ Version: version.Consensus{Block: 2, App: 2}, @@ -54,7 +54,9 @@ func CreateTestHeader(chainID string, height int64, timestamp time.Time, valSet } return Header{ - SignedHeader: signedHeader, - ValidatorSet: valSet, + SignedHeader: signedHeader, + ValidatorSet: valSet, + TrustedHeight: uint64(trustedHeight), + TrustedValidators: trustedVals, } } diff --git a/x/ibc/07-tendermint/update.go b/x/ibc/07-tendermint/update.go index bb95cddbd3..e36cf0d775 100644 --- a/x/ibc/07-tendermint/update.go +++ b/x/ibc/07-tendermint/update.go @@ -1,6 +1,7 @@ package tendermint import ( + "bytes" "time" lite "github.com/tendermint/tendermint/lite2" @@ -66,9 +67,21 @@ func CheckValidityAndUpdateState( } // checkValidity checks if the Tendermint header is valid. +// CONTRACT: consState.Height == header.TrustedHeight func checkValidity( - clientState *types.ClientState, consState types.ConsensusState, header types.Header, currentTimestamp time.Time, + clientState *types.ClientState, consState types.ConsensusState, + header types.Header, currentTimestamp time.Time, ) error { + // assert that trustedVals is NextValidators of last trusted header + // to do this, we check that trustedVals.Hash() == consState.NextValidatorsHash + tvalHash := header.TrustedValidators.Hash() + if !bytes.Equal(consState.NextValidatorsHash, tvalHash) { + return sdkerrors.Wrapf( + types.ErrInvalidValidatorSet, + "trusted validators %s, does not hash to latest trusted validators. Expected: %X, got: %X", + header.TrustedValidators, consState.NextValidatorsHash, tvalHash, + ) + } // assert trusting period has not yet passed if currentTimestamp.Sub(consState.Timestamp) >= clientState.TrustingPeriod { return sdkerrors.Wrapf( @@ -114,10 +127,10 @@ func checkValidity( Header: &trustedHeader, } - // Verify next header with the last header's validatorset as trusted validatorset + // Verify next header with the passed-in trustedVals err := lite.Verify( clientState.GetChainID(), &signedHeader, - consState.ValidatorSet, &header.SignedHeader, header.ValidatorSet, + header.TrustedValidators, &header.SignedHeader, header.ValidatorSet, clientState.TrustingPeriod, currentTimestamp, clientState.MaxClockDrift, clientState.TrustLevel.ToTendermint(), ) if err != nil { @@ -136,7 +149,6 @@ func update(clientState *types.ClientState, header types.Header) (*types.ClientS Timestamp: header.Time, Root: commitmenttypes.NewMerkleRoot(header.AppHash), NextValidatorsHash: header.NextValidatorsHash, - ValidatorSet: header.ValidatorSet, } return clientState, consensusState diff --git a/x/ibc/07-tendermint/update_test.go b/x/ibc/07-tendermint/update_test.go index 9f392ae95b..c782519de9 100644 --- a/x/ibc/07-tendermint/update_test.go +++ b/x/ibc/07-tendermint/update_test.go @@ -55,8 +55,8 @@ func (suite *TendermintTestSuite) TestCheckValidity() { name: "successful update with next height and same validator set", setup: func() { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, suite.valSet.Hash(), suite.valSet) - newHeader = types.CreateTestHeader(chainID, height+1, suite.headerTime, suite.valSet, signers) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, suite.valsHash) + newHeader = types.CreateTestHeader(chainID, height+1, height, suite.headerTime, suite.valSet, suite.valSet, signers) currentTime = suite.now }, expPass: true, @@ -65,8 +65,8 @@ func (suite *TendermintTestSuite) TestCheckValidity() { name: "successful update with future height and different validator set", setup: func() { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, suite.valSet.Hash(), suite.valSet) - newHeader = types.CreateTestHeader(chainID, height+5, suite.headerTime, bothValSet, bothSigners) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, suite.valsHash) + newHeader = types.CreateTestHeader(chainID, height+5, height, suite.headerTime, bothValSet, suite.valSet, bothSigners) currentTime = suite.now }, expPass: true, @@ -75,8 +75,8 @@ func (suite *TendermintTestSuite) TestCheckValidity() { name: "successful update with next height and different validator set", setup: func() { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, bothValSet.Hash(), suite.valSet) - newHeader = types.CreateTestHeader(chainID, height+1, suite.headerTime, bothValSet, bothSigners) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, bothValSet.Hash()) + newHeader = types.CreateTestHeader(chainID, height+1, height, suite.headerTime, bothValSet, bothValSet, bothSigners) currentTime = suite.now }, expPass: true, @@ -85,8 +85,8 @@ func (suite *TendermintTestSuite) TestCheckValidity() { name: "successful update for a previous height", setup: func() { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height-3, bothValSet.Hash(), suite.valSet) - newHeader = types.CreateTestHeader(chainID, height-1, suite.headerTime, bothValSet, bothSigners) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height-3, suite.valsHash) + newHeader = types.CreateTestHeader(chainID, height-1, height-3, suite.headerTime, bothValSet, suite.valSet, bothSigners) currentTime = suite.now }, expPass: true, @@ -95,8 +95,8 @@ func (suite *TendermintTestSuite) TestCheckValidity() { name: "unsuccessful update with next height: update header mismatches nextValSetHash", setup: func() { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, suite.valSet.Hash(), suite.valSet) - newHeader = types.CreateTestHeader(chainID, height+1, suite.headerTime, bothValSet, bothSigners) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, suite.valsHash) + newHeader = types.CreateTestHeader(chainID, height+1, height, suite.headerTime, bothValSet, suite.valSet, bothSigners) currentTime = suite.now }, expPass: false, @@ -105,8 +105,8 @@ func (suite *TendermintTestSuite) TestCheckValidity() { name: "unsuccessful update with next height: update header mismatches different nextValSetHash", setup: func() { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, bothValSet.Hash(), suite.valSet) - newHeader = types.CreateTestHeader(chainID, height+1, suite.headerTime, suite.valSet, signers) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, bothValSet.Hash()) + newHeader = types.CreateTestHeader(chainID, height+1, height, suite.headerTime, suite.valSet, bothValSet, signers) currentTime = suite.now }, expPass: false, @@ -115,8 +115,18 @@ func (suite *TendermintTestSuite) TestCheckValidity() { name: "unsuccessful update with future height: too much change in validator set", setup: func() { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, suite.valSet.Hash(), suite.valSet) - newHeader = types.CreateTestHeader(chainID, height+5, suite.headerTime, altValSet, altSigners) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, suite.valsHash) + newHeader = types.CreateTestHeader(chainID, height+5, height, suite.headerTime, altValSet, suite.valSet, altSigners) + currentTime = suite.now + }, + expPass: false, + }, + { + name: "unsuccessful updates, passed in incorrect trusted validators for given consensus state", + setup: func() { + clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, suite.valsHash) + newHeader = types.CreateTestHeader(chainID, height+5, height, suite.headerTime, bothValSet, bothValSet, bothSigners) currentTime = suite.now }, expPass: false, @@ -125,8 +135,8 @@ func (suite *TendermintTestSuite) TestCheckValidity() { name: "unsuccessful update: trusting period has passed since last client timestamp", setup: func() { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, suite.valSet.Hash(), suite.valSet) - newHeader = types.CreateTestHeader(chainID, height+1, suite.headerTime, suite.valSet, signers) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, suite.valsHash) + newHeader = types.CreateTestHeader(chainID, height+1, height, suite.headerTime, suite.valSet, suite.valSet, signers) // make current time pass trusting period from last timestamp on clientstate currentTime = suite.now.Add(trustingPeriod) }, @@ -136,8 +146,8 @@ func (suite *TendermintTestSuite) TestCheckValidity() { name: "unsuccessful update: header timestamp is past current timestamp", setup: func() { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, suite.valSet.Hash(), suite.valSet) - newHeader = types.CreateTestHeader(chainID, height+1, suite.now.Add(time.Minute), suite.valSet, signers) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, suite.valsHash) + newHeader = types.CreateTestHeader(chainID, height+1, height, suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers) currentTime = suite.now }, expPass: false, @@ -146,8 +156,8 @@ func (suite *TendermintTestSuite) TestCheckValidity() { name: "unsuccessful update: header timestamp is not past last client timestamp", setup: func() { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, suite.valSet.Hash(), suite.valSet) - newHeader = types.CreateTestHeader(chainID, height+1, suite.clientTime, suite.valSet, signers) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, suite.valsHash) + newHeader = types.CreateTestHeader(chainID, height+1, height, suite.clientTime, suite.valSet, suite.valSet, signers) currentTime = suite.now }, expPass: false, @@ -156,8 +166,8 @@ func (suite *TendermintTestSuite) TestCheckValidity() { name: "header basic validation failed", setup: func() { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, suite.valSet.Hash(), suite.valSet) - newHeader = types.CreateTestHeader(chainID, height+1, suite.headerTime, suite.valSet, signers) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, suite.valsHash) + newHeader = types.CreateTestHeader(chainID, height+1, height, suite.headerTime, suite.valSet, suite.valSet, signers) // cause new header to fail validatebasic by changing commit height to mismatch header height newHeader.SignedHeader.Commit.Height = height - 1 currentTime = suite.now @@ -168,9 +178,9 @@ func (suite *TendermintTestSuite) TestCheckValidity() { name: "header height < consensus height", setup: func() { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height+5, commitmenttypes.GetSDKSpecs()) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, suite.valSet.Hash(), suite.valSet) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, suite.valsHash) // Make new header at height less than latest client state - newHeader = types.CreateTestHeader(chainID, height-1, suite.headerTime, suite.valSet, signers) + newHeader = types.CreateTestHeader(chainID, height-1, height, suite.headerTime, suite.valSet, suite.valSet, signers) currentTime = suite.now }, expPass: false, @@ -187,7 +197,6 @@ func (suite *TendermintTestSuite) TestCheckValidity() { Timestamp: newHeader.Time, Root: commitmenttypes.NewMerkleRoot(newHeader.AppHash), NextValidatorsHash: newHeader.NextValidatorsHash, - ValidatorSet: newHeader.ValidatorSet, } newClientState, consensusState, err := tendermint.CheckValidityAndUpdateState(clientState, consensusState, newHeader, currentTime) diff --git a/x/ibc/genesis_test.go b/x/ibc/genesis_test.go index 8c52104236..ef4865ce87 100644 --- a/x/ibc/genesis_test.go +++ b/x/ibc/genesis_test.go @@ -41,7 +41,7 @@ func (suite *IBCTestSuite) TestValidateGenesis() { clientID, []exported.ConsensusState{ ibctmtypes.NewConsensusState( - suite.header.Time, commitmenttypes.NewMerkleRoot(suite.header.AppHash), suite.header.GetHeight(), suite.header.ValidatorSet.Hash(), suite.header.ValidatorSet, + suite.header.Time, commitmenttypes.NewMerkleRoot(suite.header.AppHash), suite.header.GetHeight(), suite.header.NextValidatorsHash, ), }, ), diff --git a/x/ibc/ibc_test.go b/x/ibc/ibc_test.go index 4ad12b3cdf..0b97ef6698 100644 --- a/x/ibc/ibc_test.go +++ b/x/ibc/ibc_test.go @@ -62,7 +62,7 @@ func (suite *IBCTestSuite) SetupTest() { val := tmtypes.NewValidator(pubKey, 10) valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{val}) - suite.header = ibctmtypes.CreateTestHeader(chainID, height, now, valSet, []tmtypes.PrivValidator{privVal}) + suite.header = ibctmtypes.CreateTestHeader(chainID, height, height-1, now, valSet, valSet, []tmtypes.PrivValidator{privVal}) suite.cdc = suite.app.Codec() suite.ctx = suite.app.BaseApp.NewContext(isCheckTx, abci.Header{}) diff --git a/x/ibc/testing/chain.go b/x/ibc/testing/chain.go index 4bad5eb3df..51e4fe6711 100644 --- a/x/ibc/testing/chain.go +++ b/x/ibc/testing/chain.go @@ -19,6 +19,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" @@ -32,6 +33,7 @@ import ( host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" "github.com/cosmos/cosmos-sdk/x/ibc/keeper" "github.com/cosmos/cosmos-sdk/x/ibc/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) // Default params constants used to create a TM client @@ -244,6 +246,24 @@ func (chain *TestChain) GetClientState(clientID string) clientexported.ClientSta return clientState } +// GetConsensusState retrieves the consensus state for the provided clientID and height. +// It will return a success boolean depending on if consensus state exists or not. +func (chain *TestChain) GetConsensusState(clientID string, height uint64) (clientexported.ConsensusState, bool) { + return chain.App.IBCKeeper.ClientKeeper.GetClientConsensusState(chain.GetContext(), clientID, height) +} + +// GetValsAtHeight will return the validator set of the chain at a given height. It will return +// a success boolean depending on if the validator set exists or not at that height. +func (chain *TestChain) GetValsAtHeight(height int64) (*tmtypes.ValidatorSet, bool) { + histInfo, ok := chain.App.StakingKeeper.GetHistoricalInfo(chain.GetContext(), height) + if !ok { + return nil, false + } + + valSet := stakingtypes.Validators(histInfo.Valset) + return tmtypes.NewValidatorSet(valSet.ToTmValidators()), true +} + // GetConnection retrieves an IBC Connection for the provided TestConnection. The // connection is expected to exist otherwise testing will fail. func (chain *TestChain) GetConnection(testConnection *TestConnection) connectiontypes.ConnectionEnd { @@ -331,9 +351,36 @@ func (chain *TestChain) CreateTMClient(counterparty *TestChain, clientID string) // UpdateTMClient will construct and execute a 07-tendermint MsgUpdateClient. The counterparty // client will be updated on the (target) chain. +// UpdateTMClient mocks the relayer flow necessary for updating a Tendermint client func (chain *TestChain) UpdateTMClient(counterparty *TestChain, clientID string) error { + header := counterparty.LastHeader + // Relayer must query for LatestHeight on client to get TrustedHeight + trustedHeight := chain.GetClientState(clientID).GetLatestHeight() + var ( + trustedVals *tmtypes.ValidatorSet + ok bool + ) + // Once we get TrustedHeight from client, we must query the validators from the counterparty chain + // If the LatestHeight == LastHeader.Height, then TrustedValidators are current validators + // If LatestHeight < LastHeader.Height, we can query the historical validator set from HistoricalInfo + if trustedHeight == uint64(counterparty.LastHeader.Height) { + trustedVals = counterparty.Vals + } else { + // NOTE: We need to get validators from counterparty at height: trustedHeight+1 + // since the last trusted validators for a header at height h + // is the NextValidators at h+1 committed to in header h by + // NextValidatorsHash + trustedVals, ok = counterparty.GetValsAtHeight(int64(trustedHeight + 1)) + if !ok { + return sdkerrors.Wrapf(ibctmtypes.ErrInvalidHeaderHeight, "could not retrieve trusted validators at trustedHeight: %d", trustedHeight) + } + } + // inject trusted fields into last header + header.TrustedHeight = trustedHeight + header.TrustedValidators = trustedVals + msg := ibctmtypes.NewMsgUpdateClient( - clientID, counterparty.LastHeader, + clientID, header, chain.SenderAccount.GetAddress(), ) @@ -373,6 +420,8 @@ func (chain *TestChain) CreateTMClientHeader() ibctmtypes.Header { Commit: commit, } + // Do not set trusted field here, these fields can be inserted before relaying messages to a client. + // The relayer is responsible for querying client and injecting appropriate trusted fields. return ibctmtypes.Header{ SignedHeader: signedHeader, ValidatorSet: chain.Vals,