Remove ValidatorSet from ConsensusState (#6942)

* 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 <federico.kunze94@gmail.com>
This commit is contained in:
Aditya 2020-08-06 04:32:19 -04:00 committed by GitHub
parent 04cb1fb05f
commit e3391ff447
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 400 additions and 216 deletions

View File

@ -5,9 +5,10 @@ import (
"fmt" "fmt"
"sort" "sort"
proto "github.com/gogo/protobuf/proto"
"github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/codec/types"
proto "github.com/gogo/protobuf/proto"
) )
var _ types.UnpackInterfacesMessage = GenesisState{} var _ types.UnpackInterfacesMessage = GenesisState{}

View File

@ -93,7 +93,7 @@ func TestPackAccountsAny(t *testing.T) {
{ {
"expected genesis account", "expected genesis account",
func() { func() {
accounts = []*codectypes.Any{&codectypes.Any{}} accounts = []*codectypes.Any{{}}
}, },
false, false,
}, },

View File

@ -3,12 +3,13 @@ package evidence
import ( import (
"fmt" "fmt"
"github.com/gogo/protobuf/proto"
codectypes "github.com/cosmos/cosmos-sdk/codec/types" codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/evidence/exported" "github.com/cosmos/cosmos-sdk/x/evidence/exported"
"github.com/cosmos/cosmos-sdk/x/evidence/keeper" "github.com/cosmos/cosmos-sdk/x/evidence/keeper"
"github.com/cosmos/cosmos-sdk/x/evidence/types" "github.com/cosmos/cosmos-sdk/x/evidence/types"
"github.com/gogo/protobuf/proto"
) )
// InitGenesis initializes the evidence module's state from a provided genesis // InitGenesis initializes the evidence module's state from a provided genesis

View File

@ -3,9 +3,10 @@ package types
import ( import (
"fmt" "fmt"
proto "github.com/gogo/protobuf/proto"
"github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/x/evidence/exported" "github.com/cosmos/cosmos-sdk/x/evidence/exported"
proto "github.com/gogo/protobuf/proto"
) )
var _ types.UnpackInterfacesMessage = GenesisState{} var _ types.UnpackInterfacesMessage = GenesisState{}

View File

@ -105,7 +105,7 @@ func TestGenesisStateValidate(t *testing.T) {
"expected evidence", "expected evidence",
func() { func() {
genesisState = types.GenesisState{ genesisState = types.GenesisState{
Evidence: []*codectypes.Any{&codectypes.Any{}}, Evidence: []*codectypes.Any{{}},
} }
}, },
false, false,
@ -127,7 +127,7 @@ func TestGenesisStateValidate(t *testing.T) {
func TestUnpackInterfaces(t *testing.T) { func TestUnpackInterfaces(t *testing.T) {
var gs = types.GenesisState{ var gs = types.GenesisState{
Evidence: []*codectypes.Any{&codectypes.Any{}}, Evidence: []*codectypes.Any{{}},
} }
testCases := []struct { testCases := []struct {

View File

@ -142,15 +142,16 @@ func QueryNodeConsensusState(clientCtx client.Context) (ibctmtypes.ConsensusStat
return ibctmtypes.ConsensusState{}, 0, err 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 { if err != nil {
return ibctmtypes.ConsensusState{}, 0, err return ibctmtypes.ConsensusState{}, 0, err
} }
state := ibctmtypes.ConsensusState{ state := ibctmtypes.ConsensusState{
Timestamp: commit.Time, Timestamp: commit.Time,
Root: commitmenttypes.NewMerkleRoot(commit.AppHash), Root: commitmenttypes.NewMerkleRoot(commit.AppHash),
ValidatorSet: tmtypes.NewValidatorSet(validators.Validators), NextValidatorsHash: tmtypes.NewValidatorSet(nextVals.Validators).Hash(),
} }
return state, height, nil return state, height, nil

View File

@ -71,9 +71,15 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.H
switch clientType { switch clientType {
case exported.Tendermint: 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 { 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, consensusState, err = tendermint.CheckValidityAndUpdateState(
clientState, trustedConsState, header, ctx.BlockTime(), 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()) 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 var err error
switch e := misbehaviour.(type) { switch e := misbehaviour.(type) {
case ibctmtypes.Evidence: 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, err = tendermint.CheckMisbehaviourAndUpdateState(
clientState, consensusState, misbehaviour, consensusState.GetHeight(), ctx.BlockTime(), ctx.ConsensusParams(), clientState, consensusState, misbehaviour, consensusState.GetHeight(), ctx.BlockTime(), ctx.ConsensusParams(),
) )

View File

@ -60,12 +60,12 @@ func (suite *KeeperTestSuite) TestCreateClient() {
func (suite *KeeperTestSuite) TestUpdateClientTendermint() { func (suite *KeeperTestSuite) TestUpdateClientTendermint() {
// Must create header creation functions since suite.header gets recreated on each test case // Must create header creation functions since suite.header gets recreated on each test case
createFutureUpdateFn := func(s *KeeperTestSuite) ibctmtypes.Header { createFutureUpdateFn := func(s *KeeperTestSuite) ibctmtypes.Header {
return ibctmtypes.CreateTestHeader(testChainID, suite.header.Height+1, suite.header.Time.Add(time.Minute), return ibctmtypes.CreateTestHeader(testChainID, suite.header.Height+3, suite.header.Height, suite.header.Time.Add(time.Hour),
suite.valSet, []tmtypes.PrivValidator{suite.privVal}) suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal})
} }
createPastUpdateFn := func(s *KeeperTestSuite) ibctmtypes.Header { createPastUpdateFn := func(s *KeeperTestSuite) ibctmtypes.Header {
return ibctmtypes.CreateTestHeader(testChainID, suite.header.Height-3, suite.header.Time, return ibctmtypes.CreateTestHeader(testChainID, suite.header.Height-2, suite.header.Height-4, suite.header.Time,
suite.valSet, []tmtypes.PrivValidator{suite.privVal}) suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal})
} }
var ( var (
updateHeader ibctmtypes.Header updateHeader ibctmtypes.Header
@ -80,6 +80,18 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() {
{"valid update", func() error { {"valid update", func() error {
clientState = ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs()) clientState = ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs())
_, err := suite.keeper.CreateClient(suite.ctx, testClientID, clientState, suite.consensusState) _, 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) updateHeader = createFutureUpdateFn(suite)
return err return err
}, true}, }, true},
@ -92,11 +104,18 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() {
prevConsState := ibctmtypes.ConsensusState{ prevConsState := ibctmtypes.ConsensusState{
Height: 1, Height: 1,
Timestamp: suite.past, Timestamp: suite.past,
NextValidatorsHash: suite.valSet.Hash(), NextValidatorsHash: suite.valSetHash,
ValidatorSet: suite.valSet,
} }
suite.keeper.SetClientConsensusState(suite.ctx, testClientID, 1, prevConsState) 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 // updateHeader will fill in consensus state between prevConsState and suite.consState
// clientState should not be updated // clientState should not be updated
updateHeader = createPastUpdateFn(suite) updateHeader = createPastUpdateFn(suite)
@ -145,8 +164,7 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() {
prevConsState := ibctmtypes.ConsensusState{ prevConsState := ibctmtypes.ConsensusState{
Height: 1, Height: 1,
Timestamp: suite.past, Timestamp: suite.past,
NextValidatorsHash: suite.valSet.Hash(), NextValidatorsHash: suite.valSetHash,
ValidatorSet: suite.valSet,
} }
suite.keeper.SetClientConsensusState(suite.ctx, testClientID, 1, prevConsState) suite.keeper.SetClientConsensusState(suite.ctx, testClientID, 1, prevConsState)
@ -186,7 +204,6 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() {
Timestamp: updateHeader.Time, Timestamp: updateHeader.Time,
Root: commitmenttypes.NewMerkleRoot(updateHeader.AppHash), Root: commitmenttypes.NewMerkleRoot(updateHeader.AppHash),
NextValidatorsHash: updateHeader.NextValidatorsHash, NextValidatorsHash: updateHeader.NextValidatorsHash,
ValidatorSet: updateHeader.ValidatorSet,
} }
newClientState, found := suite.keeper.GetClientState(suite.ctx, testClientID) 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()) consensusState, found := suite.keeper.GetClientConsensusState(suite.ctx, testClientID, updateHeader.GetHeight())
suite.Require().True(found, "valid test case %d failed: %s", i, tc.name) 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 // check returned client state is same as client state in store
suite.Require().Equal(updatedClientState, newClientState, "updatedClient state not persisted correctly") suite.Require().Equal(updatedClientState, newClientState, "updatedClient state not persisted correctly")
@ -263,13 +275,13 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
{ {
"trusting period misbehavior should pass", "trusting period misbehavior should pass",
ibctmtypes.Evidence{ ibctmtypes.Evidence{
Header1: 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, suite.ctx.BlockTime(), bothValSet, bothSigners), Header2: ibctmtypes.CreateTestHeader(testClientID, testClientHeight, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners),
ChainID: testClientID, ChainID: testClientID,
ClientID: testClientID, ClientID: testClientID,
}, },
func() error { func() error {
suite.consensusState.ValidatorSet = bothValSet suite.consensusState.NextValidatorsHash = bothValSet.Hash()
clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs()) clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs())
_, err := suite.keeper.CreateClient(suite.ctx, testClientID, clientState, suite.consensusState) _, err := suite.keeper.CreateClient(suite.ctx, testClientID, clientState, suite.consensusState)
@ -280,16 +292,27 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
{ {
"misbehavior at later height should pass", "misbehavior at later height should pass",
ibctmtypes.Evidence{ ibctmtypes.Evidence{
Header1: 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, suite.ctx.BlockTime(), bothValSet, bothSigners), Header2: ibctmtypes.CreateTestHeader(testClientID, testClientHeight+5, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners),
ChainID: testClientID, ChainID: testClientID,
ClientID: testClientID, ClientID: testClientID,
}, },
func() error { func() error {
suite.consensusState.ValidatorSet = bothValSet suite.consensusState.NextValidatorsHash = bothValSet.Hash()
clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs()) clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs())
_, err := suite.keeper.CreateClient(suite.ctx, testClientID, clientState, suite.consensusState) _, 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 return err
}, },
true, true,
@ -303,8 +326,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
{ {
"consensus state not found", "consensus state not found",
ibctmtypes.Evidence{ ibctmtypes.Evidence{
Header1: 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, suite.ctx.BlockTime(), bothValSet, bothSigners), Header2: ibctmtypes.CreateTestHeader(testClientID, testClientHeight, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners),
ChainID: testClientID, ChainID: testClientID,
ClientID: testClientID, ClientID: testClientID,
}, },
@ -318,8 +341,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
{ {
"consensus state not found", "consensus state not found",
ibctmtypes.Evidence{ ibctmtypes.Evidence{
Header1: 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, suite.ctx.BlockTime(), bothValSet, bothSigners), Header2: ibctmtypes.CreateTestHeader(testClientID, testClientHeight, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners),
ChainID: testClientID, ChainID: testClientID,
ClientID: testClientID, ClientID: testClientID,
}, },
@ -333,8 +356,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
{ {
"misbehaviour check failed", "misbehaviour check failed",
ibctmtypes.Evidence{ ibctmtypes.Evidence{
Header1: 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, suite.ctx.BlockTime(), altValSet, altSigners), Header2: ibctmtypes.CreateTestHeader(testClientID, testClientHeight, testClientHeight, suite.ctx.BlockTime(), altValSet, bothValSet, altSigners),
ChainID: testClientID, ChainID: testClientID,
ClientID: testClientID, ClientID: testClientID,
}, },

View File

@ -5,7 +5,6 @@ import (
"strings" "strings"
"github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/libs/log"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/store/prefix" "github.com/cosmos/cosmos-sdk/store/prefix"
@ -15,7 +14,6 @@ import (
ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types" ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types"
commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types"
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" 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 // 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 return nil, false
} }
valSet := stakingtypes.Validators(histInfo.Valset)
consensusState := ibctmtypes.ConsensusState{ consensusState := ibctmtypes.ConsensusState{
Height: height, Height: height,
Timestamp: histInfo.Header.Time, Timestamp: histInfo.Header.Time,
Root: commitmenttypes.NewMerkleRoot(histInfo.Header.AppHash), Root: commitmenttypes.NewMerkleRoot(histInfo.Header.AppHash),
NextValidatorsHash: histInfo.Header.NextValidatorsHash, NextValidatorsHash: histInfo.Header.NextValidatorsHash,
ValidatorSet: tmtypes.NewValidatorSet(valSet.ToTmValidators()),
} }
return consensusState, true return consensusState, true
} }

View File

@ -8,6 +8,7 @@ import (
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
tmbytes "github.com/tendermint/tendermint/libs/bytes"
tmtypes "github.com/tendermint/tendermint/types" tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec"
@ -45,6 +46,7 @@ type KeeperTestSuite struct {
consensusState ibctmtypes.ConsensusState consensusState ibctmtypes.ConsensusState
header ibctmtypes.Header header ibctmtypes.Header
valSet *tmtypes.ValidatorSet valSet *tmtypes.ValidatorSet
valSetHash tmbytes.HexBytes
privVal tmtypes.PrivValidator privVal tmtypes.PrivValidator
now time.Time now time.Time
past time.Time past time.Time
@ -68,13 +70,13 @@ func (suite *KeeperTestSuite) SetupTest() {
validator := tmtypes.NewValidator(pubKey, 1) validator := tmtypes.NewValidator(pubKey, 1)
suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) 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{ suite.consensusState = ibctmtypes.ConsensusState{
Height: testClientHeight, Height: testClientHeight,
Timestamp: suite.now, Timestamp: suite.now,
Root: commitmenttypes.NewMerkleRoot([]byte("hash")), Root: commitmenttypes.NewMerkleRoot([]byte("hash")),
NextValidatorsHash: suite.valSet.Hash(), NextValidatorsHash: suite.valSetHash,
ValidatorSet: suite.valSet,
} }
var validators stakingtypes.Validators var validators stakingtypes.Validators
@ -119,8 +121,6 @@ func (suite *KeeperTestSuite) TestSetClientConsensusState() {
suite.Require().True(found, "GetConsensusState failed") suite.Require().True(found, "GetConsensusState failed")
tmConsState, ok := retrievedConsState.(ibctmtypes.ConsensusState) tmConsState, ok := retrievedConsState.(ibctmtypes.ConsensusState)
// recalculate cached totalVotingPower field for equality check
tmConsState.ValidatorSet.TotalVotingPower()
suite.Require().True(ok) suite.Require().True(ok)
suite.Require().Equal(suite.consensusState, tmConsState, "ConsensusState not stored correctly") 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) suite.keeper.SetClientConsensusState(suite.ctx, testClientID, testClientHeight, suite.consensusState)
nextState := ibctmtypes.ConsensusState{ nextState := ibctmtypes.ConsensusState{
Height: testClientHeight + 5, Height: testClientHeight + 5,
Timestamp: suite.now, Timestamp: suite.now,
Root: commitmenttypes.NewMerkleRoot([]byte("next")), Root: commitmenttypes.NewMerkleRoot([]byte("next")),
ValidatorSet: suite.valSet, 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 // mock update functionality
clientState.LatestHeight = uint64(header.Height) clientState.LatestHeight = uint64(header.Height)
@ -224,15 +225,11 @@ func (suite KeeperTestSuite) TestConsensusStateHelpers() {
suite.keeper.SetClientState(suite.ctx, testClientID, clientState) suite.keeper.SetClientState(suite.ctx, testClientID, clientState)
latest, ok := suite.keeper.GetLatestClientConsensusState(suite.ctx, testClientID) 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().True(ok)
suite.Require().Equal(nextState, latest, "Latest client not returned correctly") suite.Require().Equal(nextState, latest, "Latest client not returned correctly")
// Should return existing consensusState at latestClientHeight // Should return existing consensusState at latestClientHeight
lte, ok := suite.keeper.GetClientConsensusStateLTE(suite.ctx, testClientID, testClientHeight+3) 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().True(ok)
suite.Require().Equal(suite.consensusState, lte, "LTE helper function did not return latest client state below height: %d", testClientHeight+3) 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, testClientID,
[]exported.ConsensusState{ []exported.ConsensusState{
ibctmtypes.NewConsensusState( 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( 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, testClientID2,
[]exported.ConsensusState{ []exported.ConsensusState{
ibctmtypes.NewConsensusState( 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,
), ),
}, },
), ),

View File

@ -36,7 +36,7 @@ func TestValidateGenesis(t *testing.T) {
val := tmtypes.NewValidator(pubKey, 10) val := tmtypes.NewValidator(pubKey, 10)
valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{val}) 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 { testCases := []struct {
name string name string
@ -64,7 +64,7 @@ func TestValidateGenesis(t *testing.T) {
clientID, clientID,
[]exported.ConsensusState{ []exported.ConsensusState{
ibctmtypes.NewConsensusState( 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, clientID,
[]exported.ConsensusState{ []exported.ConsensusState{
ibctmtypes.NewConsensusState( 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", "CLIENTID2",
[]exported.ConsensusState{ []exported.ConsensusState{
ibctmtypes.NewConsensusState( 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, clientID,
[]exported.ConsensusState{ []exported.ConsensusState{
ibctmtypes.NewConsensusState( ibctmtypes.NewConsensusState(
header.Time, commitmenttypes.NewMerkleRoot(header.AppHash), 0, header.ValidatorSet.Hash(), header.ValidatorSet, header.Time, commitmenttypes.NewMerkleRoot(header.AppHash), 0, header.NextValidatorsHash,
), ),
}, },
), ),

View File

@ -1,6 +1,7 @@
package tendermint package tendermint
import ( import (
"bytes"
"time" "time"
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
@ -66,7 +67,20 @@ func checkMisbehaviour(
infractionHeight := evidence.GetHeight() infractionHeight := evidence.GetHeight()
infractionTime := evidence.GetTime() infractionTime := evidence.GetTime()
ageDuration := currentTimestamp.Sub(infractionTime) 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 // 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 // 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 // - ValidatorSet must have 2/3 similarity with trusted FromValidatorSet
// - ValidatorSets on both headers are valid given the last trusted ValidatorSet // - 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.ChainID, evidence.Header1.Commit.BlockID, evidence.Header1.Height,
evidence.Header1.Commit, clientState.TrustLevel.ToTendermint(), evidence.Header1.Commit, clientState.TrustLevel.ToTendermint(),
); err != nil { ); 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.ChainID, evidence.Header2.Commit.BlockID, evidence.Header2.Height,
evidence.Header2.Commit, clientState.TrustLevel.ToTendermint(), evidence.Header2.Commit, clientState.TrustLevel.ToTendermint(),
); err != nil { ); 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 return nil

View File

@ -24,6 +24,7 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() {
// Create bothValSet with both suite validator and altVal // Create bothValSet with both suite validator and altVal
bothValSet := tmtypes.NewValidatorSet(append(suite.valSet.Validators, altVal)) bothValSet := tmtypes.NewValidatorSet(append(suite.valSet.Validators, altVal))
bothValsHash := bothValSet.Hash()
// Create alternative validator set with only altVal // Create alternative validator set with only altVal
altValSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{altVal}) altValSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{altVal})
@ -54,10 +55,10 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() {
{ {
"valid misbehavior evidence", "valid misbehavior evidence",
types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), 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{ types.Evidence{
Header1: types.CreateTestHeader(chainID, height, suite.now, bothValSet, bothSigners), Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, height, suite.now.Add(time.Minute), bothValSet, bothSigners), Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
ChainID: chainID, ChainID: chainID,
ClientID: chainID, ClientID: chainID,
}, },
@ -69,10 +70,10 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() {
{ {
"valid misbehavior at height greater than last consensusState", "valid misbehavior at height greater than last consensusState",
types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), 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{ types.Evidence{
Header1: types.CreateTestHeader(chainID, height, suite.now, bothValSet, bothSigners), Header1: types.CreateTestHeader(chainID, height, height-1, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, height, suite.now.Add(time.Minute), bothValSet, bothSigners), Header2: types.CreateTestHeader(chainID, height, height-1, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
ChainID: chainID, ChainID: chainID,
ClientID: chainID, ClientID: chainID,
}, },
@ -84,25 +85,25 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() {
{ {
"consensus state's valset hash different from evidence should still pass", "consensus state's valset hash different from evidence should still pass",
types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), 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{ types.Evidence{
Header1: types.CreateTestHeader(chainID, height, suite.now, bothValSet, bothSigners), Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, suite.valSet, bothSigners),
Header2: types.CreateTestHeader(chainID, height, suite.now.Add(time.Minute), bothValSet, bothSigners), Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners),
ChainID: chainID, ChainID: chainID,
ClientID: chainID, ClientID: chainID,
}, },
simapp.DefaultConsensusParams, simapp.DefaultConsensusParams,
height - 1, height,
suite.now, suite.now,
true, true,
}, },
{ {
"invalid tendermint client state", "invalid tendermint client state",
nil, 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{ types.Evidence{
Header1: types.CreateTestHeader(chainID, height, suite.now, altValSet, altSigners), Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, height, suite.now.Add(time.Minute), bothValSet, bothSigners), Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, altValSet, bothSigners),
ChainID: chainID, ChainID: chainID,
ClientID: chainID, ClientID: chainID,
}, },
@ -114,10 +115,10 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() {
{ {
"already frozen client state", "already frozen client state",
types.ClientState{FrozenHeight: 1}, 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{ types.Evidence{
Header1: types.CreateTestHeader(chainID, height, suite.now, bothValSet, bothSigners), Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, height, suite.now.Add(time.Minute), bothValSet, bothSigners), Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
ChainID: chainID, ChainID: chainID,
ClientID: chainID, ClientID: chainID,
}, },
@ -131,8 +132,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() {
types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()),
nil, nil,
types.Evidence{ types.Evidence{
Header1: types.CreateTestHeader(chainID, height, suite.now, altValSet, altSigners), Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, height, suite.now.Add(time.Minute), bothValSet, bothSigners), Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
ChainID: chainID, ChainID: chainID,
ClientID: chainID, ClientID: chainID,
}, },
@ -144,7 +145,7 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() {
{ {
"invalid tendermint misbehaviour evidence", "invalid tendermint misbehaviour evidence",
types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), 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, nil,
simapp.DefaultConsensusParams, simapp.DefaultConsensusParams,
height, height,
@ -154,25 +155,27 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() {
{ {
"rejected misbehaviour due to expired age", "rejected misbehaviour due to expired age",
types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), 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{ types.Evidence{
Header1: types.CreateTestHeader(chainID, height, suite.now, bothValSet, bothSigners), Header1: types.CreateTestHeader(chainID, int64(2*height+uint64(simapp.DefaultConsensusParams.Evidence.MaxAgeNumBlocks)), height,
Header2: types.CreateTestHeader(chainID, height, suite.now.Add(time.Minute), bothValSet, bothSigners), 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, ChainID: chainID,
ClientID: chainID, ClientID: chainID,
}, },
simapp.DefaultConsensusParams, simapp.DefaultConsensusParams,
2*height + uint64(simapp.DefaultConsensusParams.Evidence.MaxAgeNumBlocks), height,
suite.now.Add(2 * time.Minute).Add(simapp.DefaultConsensusParams.Evidence.MaxAgeDuration), suite.now.Add(2 * time.Minute).Add(simapp.DefaultConsensusParams.Evidence.MaxAgeDuration),
false, false,
}, },
{ {
"provided height header height", "provided height > header height",
types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), 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{ types.Evidence{
Header1: types.CreateTestHeader(chainID, height, suite.now, bothValSet, bothSigners), Header1: types.CreateTestHeader(chainID, height, height-1, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, height, suite.now.Add(time.Minute), bothValSet, bothSigners), Header2: types.CreateTestHeader(chainID, height, height-1, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
ChainID: chainID, ChainID: chainID,
ClientID: chainID, ClientID: chainID,
}, },
@ -184,10 +187,25 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() {
{ {
"unbonding period expired", "unbonding period expired",
types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), 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{ types.Evidence{
Header1: types.CreateTestHeader(chainID, height, suite.now, bothValSet, bothSigners), Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, height, suite.now.Add(time.Minute), 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, ChainID: chainID,
ClientID: chainID, ClientID: chainID,
}, },
@ -199,10 +217,10 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() {
{ {
"first valset has too much change", "first valset has too much change",
types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), 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{ types.Evidence{
Header1: types.CreateTestHeader(chainID, height, suite.now, altValSet, altSigners), Header1: types.CreateTestHeader(chainID, height, height, suite.now, altValSet, bothValSet, altSigners),
Header2: types.CreateTestHeader(chainID, height, suite.now.Add(time.Minute), bothValSet, bothSigners), Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
ChainID: chainID, ChainID: chainID,
ClientID: chainID, ClientID: chainID,
}, },
@ -214,10 +232,10 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() {
{ {
"second valset has too much change", "second valset has too much change",
types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), 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{ types.Evidence{
Header1: types.CreateTestHeader(chainID, height, suite.now, bothValSet, bothSigners), Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, height, suite.now.Add(time.Minute), altValSet, altSigners), Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), altValSet, bothValSet, altSigners),
ChainID: chainID, ChainID: chainID,
ClientID: chainID, ClientID: chainID,
}, },
@ -229,10 +247,10 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviour() {
{ {
"both valsets have too much change", "both valsets have too much change",
types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), 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{ types.Evidence{
Header1: types.CreateTestHeader(chainID, height, suite.now, altValSet, altSigners), Header1: types.CreateTestHeader(chainID, height, height, suite.now, altValSet, bothValSet, altSigners),
Header2: types.CreateTestHeader(chainID, height, suite.now.Add(time.Minute), altValSet, altSigners), Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), altValSet, bothValSet, altSigners),
ChainID: chainID, ChainID: chainID,
ClientID: chainID, ClientID: chainID,
}, },

View File

@ -6,6 +6,7 @@ import (
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
tmbytes "github.com/tendermint/tendermint/libs/bytes"
tmtypes "github.com/tendermint/tendermint/types" tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec"
@ -29,6 +30,7 @@ type TendermintTestSuite struct {
signers []tmtypes.PrivValidator signers []tmtypes.PrivValidator
privVal tmtypes.PrivValidator privVal tmtypes.PrivValidator
valSet *tmtypes.ValidatorSet valSet *tmtypes.ValidatorSet
valsHash tmbytes.HexBytes
header ibctmtypes.Header header ibctmtypes.Header
now time.Time now time.Time
clientTime time.Time clientTime time.Time
@ -55,11 +57,12 @@ func (suite *TendermintTestSuite) SetupTest() {
val := tmtypes.NewValidator(pubKey, 10) val := tmtypes.NewValidator(pubKey, 10)
suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{val}) suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{val})
suite.valsHash = suite.valSet.Hash()
// Suite header is intended to be header passed in for initial ClientState // Suite header is intended to be header passed in for initial ClientState
// Thus it should have same height and time as 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 // 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) { func TestTendermintTestSuite(t *testing.T) {

View File

@ -136,8 +136,8 @@ func (suite *TendermintTestSuite) TestVerifyClientConsensusState() {
name: "proof verification failed", name: "proof verification failed",
clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()),
consensusState: ibctmtypes.ConsensusState{ consensusState: ibctmtypes.ConsensusState{
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
ValidatorSet: suite.valSet, NextValidatorsHash: suite.valsHash,
}, },
prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")),
proof: []byte{}, proof: []byte{},
@ -219,8 +219,8 @@ func (suite *TendermintTestSuite) TestVerifyConnectionState() {
clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()),
connection: conn, connection: conn,
consensusState: ibctmtypes.ConsensusState{ consensusState: ibctmtypes.ConsensusState{
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
ValidatorSet: suite.valSet, NextValidatorsHash: suite.valsHash,
}, },
prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")),
proof: []byte{}, proof: []byte{},
@ -302,8 +302,8 @@ func (suite *TendermintTestSuite) TestVerifyChannelState() {
clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()),
channel: ch, channel: ch,
consensusState: ibctmtypes.ConsensusState{ consensusState: ibctmtypes.ConsensusState{
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
ValidatorSet: suite.valSet, NextValidatorsHash: suite.valsHash,
}, },
prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")),
proof: []byte{}, proof: []byte{},
@ -382,8 +382,8 @@ func (suite *TendermintTestSuite) TestVerifyPacketCommitment() {
clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()),
commitment: []byte{}, commitment: []byte{},
consensusState: ibctmtypes.ConsensusState{ consensusState: ibctmtypes.ConsensusState{
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
ValidatorSet: suite.valSet, NextValidatorsHash: suite.valsHash,
}, },
prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")),
proof: []byte{}, proof: []byte{},
@ -462,8 +462,8 @@ func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgement() {
clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()),
ack: []byte{}, ack: []byte{},
consensusState: ibctmtypes.ConsensusState{ consensusState: ibctmtypes.ConsensusState{
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
ValidatorSet: suite.valSet, NextValidatorsHash: suite.valsHash,
}, },
prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")),
proof: []byte{}, proof: []byte{},
@ -537,8 +537,8 @@ func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgementAbsence() {
name: "proof verification failed", name: "proof verification failed",
clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()),
consensusState: ibctmtypes.ConsensusState{ consensusState: ibctmtypes.ConsensusState{
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
ValidatorSet: suite.valSet, NextValidatorsHash: suite.valsHash,
}, },
prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")),
proof: []byte{}, proof: []byte{},
@ -612,8 +612,8 @@ func (suite *TendermintTestSuite) TestVerifyNextSeqRecv() {
name: "proof verification failed", name: "proof verification failed",
clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()),
consensusState: ibctmtypes.ConsensusState{ consensusState: ibctmtypes.ConsensusState{
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
ValidatorSet: suite.valSet, NextValidatorsHash: suite.valsHash,
}, },
prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")),
proof: []byte{}, proof: []byte{},

View File

@ -18,20 +18,18 @@ type ConsensusState struct {
Root commitmentexported.Root `json:"root" yaml:"root"` Root commitmentexported.Root `json:"root" yaml:"root"`
Height uint64 `json:"height" yaml:"height"` Height uint64 `json:"height" yaml:"height"`
NextValidatorsHash tmbytes.HexBytes `json:"next_validators_hash"` // validators hash for the next block 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. // NewConsensusState creates a new ConsensusState instance.
func NewConsensusState( func NewConsensusState(
timestamp time.Time, root commitmentexported.Root, height uint64, timestamp time.Time, root commitmentexported.Root, height uint64,
nextValsHash tmbytes.HexBytes, valset *tmtypes.ValidatorSet, nextValsHash tmbytes.HexBytes,
) ConsensusState { ) ConsensusState {
return ConsensusState{ return ConsensusState{
Timestamp: timestamp, Timestamp: timestamp,
Root: root, Root: root,
Height: height, Height: height,
NextValidatorsHash: nextValsHash, NextValidatorsHash: nextValsHash,
ValidatorSet: valset,
} }
} }
@ -60,9 +58,6 @@ func (cs ConsensusState) ValidateBasic() error {
if cs.Root == nil || cs.Root.Empty() { if cs.Root == nil || cs.Root.Empty() {
return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "root cannot be 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 { if err := tmtypes.ValidateHash(cs.NextValidatorsHash); err != nil {
return sdkerrors.Wrap(err, "next validators hash is invalid") return sdkerrors.Wrap(err, "next validators hash is invalid")
} }

View File

@ -16,50 +16,51 @@ func (suite *TendermintTestSuite) TestConsensusStateValidateBasic() {
}{ }{
{"success", {"success",
ibctmtypes.ConsensusState{ ibctmtypes.ConsensusState{
Timestamp: suite.now, Timestamp: suite.now,
Height: height, Height: height,
Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")), Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")),
ValidatorSet: suite.valSet, NextValidatorsHash: suite.valsHash,
}, },
true}, true},
{"root is nil", {"root is nil",
ibctmtypes.ConsensusState{ ibctmtypes.ConsensusState{
Timestamp: suite.now, Timestamp: suite.now,
Height: height, Height: height,
Root: nil, Root: nil,
ValidatorSet: suite.valSet, NextValidatorsHash: suite.valsHash,
}, },
false}, false},
{"root is empty", {"root is empty",
ibctmtypes.ConsensusState{ ibctmtypes.ConsensusState{
Timestamp: suite.now, Timestamp: suite.now,
Height: height, Height: height,
Root: commitmenttypes.MerkleRoot{}, Root: commitmenttypes.MerkleRoot{},
ValidatorSet: suite.valSet, NextValidatorsHash: suite.valsHash,
}, },
false}, false},
{"valset is nil", {"nextvalshash is invalid",
ibctmtypes.ConsensusState{ ibctmtypes.ConsensusState{
Timestamp: suite.now, Timestamp: suite.now,
Height: height, Height: height,
Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")), Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")),
ValidatorSet: nil, NextValidatorsHash: []byte("hi"),
}, },
false}, false},
{"height is 0", {"height is 0",
ibctmtypes.ConsensusState{ ibctmtypes.ConsensusState{
Timestamp: suite.now, Timestamp: suite.now,
Height: 0, Height: 0,
Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")), Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")),
ValidatorSet: suite.valSet, NextValidatorsHash: suite.valsHash,
}, },
false}, false},
{"timestamp is zero", {"timestamp is zero",
ibctmtypes.ConsensusState{ ibctmtypes.ConsensusState{
Timestamp: time.Time{}, Timestamp: time.Time{},
Height: height, Height: height,
Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")), Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")),
ValidatorSet: suite.valSet, NextValidatorsHash: suite.valsHash,
}, },
false}, false},
} }

View File

@ -5,7 +5,7 @@ import (
) )
const ( const (
SubModuleName = "tendermint" SubModuleName = "tendermint-client"
) )
// IBC tendermint client sentinel errors // 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") 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") ErrUnbondingPeriodExpired = sdkerrors.Register(SubModuleName, 9, "time since latest trusted state has passed the unbonding period")
ErrInvalidProofSpecs = sdkerrors.Register(SubModuleName, 10, "invalid proof specs") ErrInvalidProofSpecs = sdkerrors.Register(SubModuleName, 10, "invalid proof specs")
ErrInvalidValidatorSet = sdkerrors.Register(SubModuleName, 11, "invalid validator set")
) )

View File

@ -1,6 +1,7 @@
package types package types
import ( import (
"bytes"
"math" "math"
"time" "time"
@ -85,6 +86,17 @@ func (ev Evidence) GetTime() time.Time {
// ValidateBasic implements Evidence interface // ValidateBasic implements Evidence interface
func (ev Evidence) ValidateBasic() error { 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 { if err := host.ClientIdentifierValidator(ev.ClientID); err != nil {
return sdkerrors.Wrap(err, "evidence client ID is invalid") return sdkerrors.Wrap(err, "evidence client ID is invalid")
} }

View File

@ -17,7 +17,7 @@ func (suite *TendermintTestSuite) TestEvidence() {
ev := ibctmtypes.Evidence{ ev := ibctmtypes.Evidence{
Header1: suite.header, 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, ChainID: chainID,
ClientID: "gaiamainnet", ClientID: "gaiamainnet",
} }
@ -67,18 +67,40 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() {
"valid evidence", "valid evidence",
ibctmtypes.Evidence{ ibctmtypes.Evidence{
Header1: suite.header, 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, ChainID: chainID,
ClientID: "gaiamainnet", ClientID: "gaiamainnet",
}, },
func(ev *ibctmtypes.Evidence) error { return nil }, func(ev *ibctmtypes.Evidence) error { return nil },
true, 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 ", "invalid client ID ",
ibctmtypes.Evidence{ ibctmtypes.Evidence{
Header1: suite.header, 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, ChainID: chainID,
ClientID: "GAIA", ClientID: "GAIA",
}, },
@ -89,7 +111,7 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() {
"wrong chainID on header1", "wrong chainID on header1",
ibctmtypes.Evidence{ ibctmtypes.Evidence{
Header1: suite.header, 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", ChainID: "ethermint",
ClientID: "gaiamainnet", ClientID: "gaiamainnet",
}, },
@ -100,7 +122,7 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() {
"wrong chainID on header2", "wrong chainID on header2",
ibctmtypes.Evidence{ ibctmtypes.Evidence{
Header1: suite.header, 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, ChainID: chainID,
ClientID: "gaiamainnet", ClientID: "gaiamainnet",
}, },
@ -111,7 +133,7 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() {
"mismatched heights", "mismatched heights",
ibctmtypes.Evidence{ ibctmtypes.Evidence{
Header1: suite.header, 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, ChainID: chainID,
ClientID: "gaiamainnet", ClientID: "gaiamainnet",
}, },
@ -132,7 +154,7 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() {
{ {
"header 1 doesn't have 2/3 majority", "header 1 doesn't have 2/3 majority",
ibctmtypes.Evidence{ 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, Header2: suite.header,
ChainID: chainID, ChainID: chainID,
ClientID: "gaiamainnet", ClientID: "gaiamainnet",
@ -150,7 +172,7 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() {
"header 2 doesn't have 2/3 majority", "header 2 doesn't have 2/3 majority",
ibctmtypes.Evidence{ ibctmtypes.Evidence{
Header1: suite.header, 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, ChainID: chainID,
ClientID: "gaiamainnet", ClientID: "gaiamainnet",
}, },
@ -167,7 +189,7 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() {
"validators sign off on wrong commit", "validators sign off on wrong commit",
ibctmtypes.Evidence{ ibctmtypes.Evidence{
Header1: suite.header, 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, ChainID: chainID,
ClientID: "gaiamainnet", ClientID: "gaiamainnet",
}, },

View File

@ -14,10 +14,20 @@ import (
var _ clientexported.Header = Header{} 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 { type Header struct {
tmtypes.SignedHeader `json:"signed_header" yaml:"signed_header"` // contains the commitment root 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 // ClientType defines that the Header is a Tendermint consensus algorithm
@ -25,13 +35,13 @@ func (h Header) ClientType() clientexported.ClientType {
return clientexported.Tendermint 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 { func (h Header) ConsensusState() ConsensusState {
return ConsensusState{ return ConsensusState{
Height: uint64(h.Height), Height: uint64(h.Height),
Timestamp: h.Time, Timestamp: h.Time,
Root: commitmenttypes.NewMerkleRoot(h.AppHash), Root: commitmenttypes.NewMerkleRoot(h.AppHash),
ValidatorSet: h.ValidatorSet, NextValidatorsHash: h.NextValidatorsHash,
} }
} }
@ -44,10 +54,18 @@ func (h Header) GetHeight() uint64 {
// ValidateBasic calls the SignedHeader ValidateBasic function // ValidateBasic calls the SignedHeader ValidateBasic function
// and checks that validatorsets are not nil // 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 { func (h Header) ValidateBasic(chainID string) error {
if err := h.SignedHeader.ValidateBasic(chainID); err != nil { if err := h.SignedHeader.ValidateBasic(chainID); err != nil {
return sdkerrors.Wrap(err, "header failed basic validation") 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 { if h.ValidatorSet == nil {
return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "validator set is nil") return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "validator set is nil")
} }

View File

@ -14,7 +14,7 @@ func (suite *TendermintTestSuite) TestHeaderValidateBasic() {
}{ }{
{"valid header", suite.header, chainID, true}, {"valid header", suite.header, chainID, true},
{"signed header basic validation failed", suite.header, "chainID", false}, {"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()) suite.Require().Equal(clientexported.Tendermint, suite.header.ClientType())

View File

@ -145,7 +145,6 @@ func (msg MsgCreateClient) GetConsensusState() clientexported.ConsensusState {
Root: root, Root: root,
Height: uint64(msg.Header.Height), Height: uint64(msg.Header.Height),
NextValidatorsHash: msg.Header.NextValidatorsHash, NextValidatorsHash: msg.Header.NextValidatorsHash,
ValidatorSet: msg.Header.ValidatorSet,
} }
} }

View File

@ -14,7 +14,7 @@ import (
func (suite *TendermintTestSuite) TestMsgCreateClientValidateBasic() { func (suite *TendermintTestSuite) TestMsgCreateClientValidateBasic() {
privKey := secp256k1.GenPrivKey() privKey := secp256k1.GenPrivKey()
signer := sdk.AccAddress(privKey.PubKey().Address()) 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 invalidHeader.ValidatorSet = nil
cases := []struct { cases := []struct {

View File

@ -7,6 +7,7 @@ import (
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
tmbytes "github.com/tendermint/tendermint/libs/bytes"
tmtypes "github.com/tendermint/tendermint/types" tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec"
@ -31,6 +32,7 @@ type TendermintTestSuite struct {
cdc codec.Marshaler cdc codec.Marshaler
privVal tmtypes.PrivValidator privVal tmtypes.PrivValidator
valSet *tmtypes.ValidatorSet valSet *tmtypes.ValidatorSet
valsHash tmbytes.HexBytes
header ibctmtypes.Header header ibctmtypes.Header
now time.Time now time.Time
} }
@ -50,7 +52,8 @@ func (suite *TendermintTestSuite) SetupTest() {
val := tmtypes.NewValidator(pubKey, 10) val := tmtypes.NewValidator(pubKey, 10)
suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{val}) 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}) suite.ctx = app.BaseApp.NewContext(checkTx, abci.Header{Height: 1, Time: suite.now})
} }

View File

@ -22,7 +22,7 @@ func MakeBlockID(hash []byte, partSetSize int, partSetHash []byte) tmtypes.Block
} }
// CreateTestHeader creates a mock header for testing only. // 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() vsetHash := valSet.Hash()
tmHeader := tmtypes.Header{ tmHeader := tmtypes.Header{
Version: version.Consensus{Block: 2, App: 2}, Version: version.Consensus{Block: 2, App: 2},
@ -54,7 +54,9 @@ func CreateTestHeader(chainID string, height int64, timestamp time.Time, valSet
} }
return Header{ return Header{
SignedHeader: signedHeader, SignedHeader: signedHeader,
ValidatorSet: valSet, ValidatorSet: valSet,
TrustedHeight: uint64(trustedHeight),
TrustedValidators: trustedVals,
} }
} }

View File

@ -1,6 +1,7 @@
package tendermint package tendermint
import ( import (
"bytes"
"time" "time"
lite "github.com/tendermint/tendermint/lite2" lite "github.com/tendermint/tendermint/lite2"
@ -66,9 +67,21 @@ func CheckValidityAndUpdateState(
} }
// checkValidity checks if the Tendermint header is valid. // checkValidity checks if the Tendermint header is valid.
// CONTRACT: consState.Height == header.TrustedHeight
func checkValidity( 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 { ) 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 // assert trusting period has not yet passed
if currentTimestamp.Sub(consState.Timestamp) >= clientState.TrustingPeriod { if currentTimestamp.Sub(consState.Timestamp) >= clientState.TrustingPeriod {
return sdkerrors.Wrapf( return sdkerrors.Wrapf(
@ -114,10 +127,10 @@ func checkValidity(
Header: &trustedHeader, 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( err := lite.Verify(
clientState.GetChainID(), &signedHeader, clientState.GetChainID(), &signedHeader,
consState.ValidatorSet, &header.SignedHeader, header.ValidatorSet, header.TrustedValidators, &header.SignedHeader, header.ValidatorSet,
clientState.TrustingPeriod, currentTimestamp, clientState.MaxClockDrift, clientState.TrustLevel.ToTendermint(), clientState.TrustingPeriod, currentTimestamp, clientState.MaxClockDrift, clientState.TrustLevel.ToTendermint(),
) )
if err != nil { if err != nil {
@ -136,7 +149,6 @@ func update(clientState *types.ClientState, header types.Header) (*types.ClientS
Timestamp: header.Time, Timestamp: header.Time,
Root: commitmenttypes.NewMerkleRoot(header.AppHash), Root: commitmenttypes.NewMerkleRoot(header.AppHash),
NextValidatorsHash: header.NextValidatorsHash, NextValidatorsHash: header.NextValidatorsHash,
ValidatorSet: header.ValidatorSet,
} }
return clientState, consensusState return clientState, consensusState

View File

@ -55,8 +55,8 @@ func (suite *TendermintTestSuite) TestCheckValidity() {
name: "successful update with next height and same validator set", name: "successful update with next height and same validator set",
setup: func() { setup: func() {
clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()) 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) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, suite.valsHash)
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 currentTime = suite.now
}, },
expPass: true, expPass: true,
@ -65,8 +65,8 @@ func (suite *TendermintTestSuite) TestCheckValidity() {
name: "successful update with future height and different validator set", name: "successful update with future height and different validator set",
setup: func() { setup: func() {
clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()) 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) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, suite.valsHash)
newHeader = types.CreateTestHeader(chainID, height+5, suite.headerTime, bothValSet, bothSigners) newHeader = types.CreateTestHeader(chainID, height+5, height, suite.headerTime, bothValSet, suite.valSet, bothSigners)
currentTime = suite.now currentTime = suite.now
}, },
expPass: true, expPass: true,
@ -75,8 +75,8 @@ func (suite *TendermintTestSuite) TestCheckValidity() {
name: "successful update with next height and different validator set", name: "successful update with next height and different validator set",
setup: func() { setup: func() {
clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()) 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) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, bothValSet.Hash())
newHeader = types.CreateTestHeader(chainID, height+1, suite.headerTime, bothValSet, bothSigners) newHeader = types.CreateTestHeader(chainID, height+1, height, suite.headerTime, bothValSet, bothValSet, bothSigners)
currentTime = suite.now currentTime = suite.now
}, },
expPass: true, expPass: true,
@ -85,8 +85,8 @@ func (suite *TendermintTestSuite) TestCheckValidity() {
name: "successful update for a previous height", name: "successful update for a previous height",
setup: func() { setup: func() {
clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()) 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) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height-3, suite.valsHash)
newHeader = types.CreateTestHeader(chainID, height-1, suite.headerTime, bothValSet, bothSigners) newHeader = types.CreateTestHeader(chainID, height-1, height-3, suite.headerTime, bothValSet, suite.valSet, bothSigners)
currentTime = suite.now currentTime = suite.now
}, },
expPass: true, expPass: true,
@ -95,8 +95,8 @@ func (suite *TendermintTestSuite) TestCheckValidity() {
name: "unsuccessful update with next height: update header mismatches nextValSetHash", name: "unsuccessful update with next height: update header mismatches nextValSetHash",
setup: func() { setup: func() {
clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()) 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) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, suite.valsHash)
newHeader = types.CreateTestHeader(chainID, height+1, suite.headerTime, bothValSet, bothSigners) newHeader = types.CreateTestHeader(chainID, height+1, height, suite.headerTime, bothValSet, suite.valSet, bothSigners)
currentTime = suite.now currentTime = suite.now
}, },
expPass: false, expPass: false,
@ -105,8 +105,8 @@ func (suite *TendermintTestSuite) TestCheckValidity() {
name: "unsuccessful update with next height: update header mismatches different nextValSetHash", name: "unsuccessful update with next height: update header mismatches different nextValSetHash",
setup: func() { setup: func() {
clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()) 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) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, bothValSet.Hash())
newHeader = types.CreateTestHeader(chainID, height+1, suite.headerTime, suite.valSet, signers) newHeader = types.CreateTestHeader(chainID, height+1, height, suite.headerTime, suite.valSet, bothValSet, signers)
currentTime = suite.now currentTime = suite.now
}, },
expPass: false, expPass: false,
@ -115,8 +115,18 @@ func (suite *TendermintTestSuite) TestCheckValidity() {
name: "unsuccessful update with future height: too much change in validator set", name: "unsuccessful update with future height: too much change in validator set",
setup: func() { setup: func() {
clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()) 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) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, suite.valsHash)
newHeader = types.CreateTestHeader(chainID, height+5, suite.headerTime, altValSet, altSigners) 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 currentTime = suite.now
}, },
expPass: false, expPass: false,
@ -125,8 +135,8 @@ func (suite *TendermintTestSuite) TestCheckValidity() {
name: "unsuccessful update: trusting period has passed since last client timestamp", name: "unsuccessful update: trusting period has passed since last client timestamp",
setup: func() { setup: func() {
clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()) 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) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, suite.valsHash)
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)
// make current time pass trusting period from last timestamp on clientstate // make current time pass trusting period from last timestamp on clientstate
currentTime = suite.now.Add(trustingPeriod) currentTime = suite.now.Add(trustingPeriod)
}, },
@ -136,8 +146,8 @@ func (suite *TendermintTestSuite) TestCheckValidity() {
name: "unsuccessful update: header timestamp is past current timestamp", name: "unsuccessful update: header timestamp is past current timestamp",
setup: func() { setup: func() {
clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()) 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) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, suite.valsHash)
newHeader = types.CreateTestHeader(chainID, height+1, suite.now.Add(time.Minute), suite.valSet, signers) newHeader = types.CreateTestHeader(chainID, height+1, height, suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers)
currentTime = suite.now currentTime = suite.now
}, },
expPass: false, expPass: false,
@ -146,8 +156,8 @@ func (suite *TendermintTestSuite) TestCheckValidity() {
name: "unsuccessful update: header timestamp is not past last client timestamp", name: "unsuccessful update: header timestamp is not past last client timestamp",
setup: func() { setup: func() {
clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()) 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) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, suite.valsHash)
newHeader = types.CreateTestHeader(chainID, height+1, suite.clientTime, suite.valSet, signers) newHeader = types.CreateTestHeader(chainID, height+1, height, suite.clientTime, suite.valSet, suite.valSet, signers)
currentTime = suite.now currentTime = suite.now
}, },
expPass: false, expPass: false,
@ -156,8 +166,8 @@ func (suite *TendermintTestSuite) TestCheckValidity() {
name: "header basic validation failed", name: "header basic validation failed",
setup: func() { setup: func() {
clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()) 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) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.AppHash), height, suite.valsHash)
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)
// cause new header to fail validatebasic by changing commit height to mismatch header height // cause new header to fail validatebasic by changing commit height to mismatch header height
newHeader.SignedHeader.Commit.Height = height - 1 newHeader.SignedHeader.Commit.Height = height - 1
currentTime = suite.now currentTime = suite.now
@ -168,9 +178,9 @@ func (suite *TendermintTestSuite) TestCheckValidity() {
name: "header height < consensus height", name: "header height < consensus height",
setup: func() { setup: func() {
clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height+5, commitmenttypes.GetSDKSpecs()) 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 // 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 currentTime = suite.now
}, },
expPass: false, expPass: false,
@ -187,7 +197,6 @@ func (suite *TendermintTestSuite) TestCheckValidity() {
Timestamp: newHeader.Time, Timestamp: newHeader.Time,
Root: commitmenttypes.NewMerkleRoot(newHeader.AppHash), Root: commitmenttypes.NewMerkleRoot(newHeader.AppHash),
NextValidatorsHash: newHeader.NextValidatorsHash, NextValidatorsHash: newHeader.NextValidatorsHash,
ValidatorSet: newHeader.ValidatorSet,
} }
newClientState, consensusState, err := tendermint.CheckValidityAndUpdateState(clientState, consensusState, newHeader, currentTime) newClientState, consensusState, err := tendermint.CheckValidityAndUpdateState(clientState, consensusState, newHeader, currentTime)

View File

@ -41,7 +41,7 @@ func (suite *IBCTestSuite) TestValidateGenesis() {
clientID, clientID,
[]exported.ConsensusState{ []exported.ConsensusState{
ibctmtypes.NewConsensusState( 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,
), ),
}, },
), ),

View File

@ -62,7 +62,7 @@ func (suite *IBCTestSuite) SetupTest() {
val := tmtypes.NewValidator(pubKey, 10) val := tmtypes.NewValidator(pubKey, 10)
valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{val}) 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.cdc = suite.app.Codec()
suite.ctx = suite.app.BaseApp.NewContext(isCheckTx, abci.Header{}) suite.ctx = suite.app.BaseApp.NewContext(isCheckTx, abci.Header{})

View File

@ -19,6 +19,7 @@ import (
"github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/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" 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/keeper"
"github.com/cosmos/cosmos-sdk/x/ibc/types" "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 // Default params constants used to create a TM client
@ -244,6 +246,24 @@ func (chain *TestChain) GetClientState(clientID string) clientexported.ClientSta
return clientState 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 // GetConnection retrieves an IBC Connection for the provided TestConnection. The
// connection is expected to exist otherwise testing will fail. // connection is expected to exist otherwise testing will fail.
func (chain *TestChain) GetConnection(testConnection *TestConnection) connectiontypes.ConnectionEnd { 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 // UpdateTMClient will construct and execute a 07-tendermint MsgUpdateClient. The counterparty
// client will be updated on the (target) chain. // 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 { 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( msg := ibctmtypes.NewMsgUpdateClient(
clientID, counterparty.LastHeader, clientID, header,
chain.SenderAccount.GetAddress(), chain.SenderAccount.GetAddress(),
) )
@ -373,6 +420,8 @@ func (chain *TestChain) CreateTMClientHeader() ibctmtypes.Header {
Commit: commit, 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{ return ibctmtypes.Header{
SignedHeader: signedHeader, SignedHeader: signedHeader,
ValidatorSet: chain.Vals, ValidatorSet: chain.Vals,