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"
"sort"
proto "github.com/gogo/protobuf/proto"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/types"
proto "github.com/gogo/protobuf/proto"
)
var _ types.UnpackInterfacesMessage = GenesisState{}

View File

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

View File

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

View File

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

View File

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

View File

@ -142,15 +142,16 @@ func QueryNodeConsensusState(clientCtx client.Context) (ibctmtypes.ConsensusStat
return ibctmtypes.ConsensusState{}, 0, err
}
validators, err := node.Validators(&height, 0, 10000)
nextHeight := height + 1
nextVals, err := node.Validators(&nextHeight, 0, 10000)
if err != nil {
return ibctmtypes.ConsensusState{}, 0, err
}
state := ibctmtypes.ConsensusState{
Timestamp: commit.Time,
Root: commitmenttypes.NewMerkleRoot(commit.AppHash),
ValidatorSet: tmtypes.NewValidatorSet(validators.Validators),
Timestamp: commit.Time,
Root: commitmenttypes.NewMerkleRoot(commit.AppHash),
NextValidatorsHash: tmtypes.NewValidatorSet(nextVals.Validators).Hash(),
}
return state, height, nil

View File

@ -71,9 +71,15 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.H
switch clientType {
case exported.Tendermint:
trustedConsState, found := k.GetClientConsensusStateLTE(ctx, clientID, header.GetHeight())
tmHeader, ok := header.(ibctmtypes.Header)
if !ok {
err = sdkerrors.Wrapf(types.ErrInvalidHeader, "expected tendermint header: %T, got header type: %T", ibctmtypes.Header{}, header)
break
}
// Get the consensus state at the trusted height of header
trustedConsState, found := k.GetClientConsensusState(ctx, clientID, tmHeader.TrustedHeight)
if !found {
return nil, sdkerrors.Wrapf(types.ErrConsensusStateNotFound, "could not find consensus state less than header height: %d to verify header against", header.GetHeight())
return nil, sdkerrors.Wrapf(types.ErrConsensusStateNotFound, "could not find consensus state for trusted header height: %d to verify header against for clientID: %s", tmHeader.TrustedHeight, clientID)
}
clientState, consensusState, err = tendermint.CheckValidityAndUpdateState(
clientState, trustedConsState, header, ctx.BlockTime(),
@ -127,14 +133,15 @@ func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, misbehaviour ex
return sdkerrors.Wrapf(types.ErrClientNotFound, "cannot check misbehaviour for client with ID %s", misbehaviour.GetClientID())
}
consensusState, found := k.GetClientConsensusStateLTE(ctx, misbehaviour.GetClientID(), uint64(misbehaviour.GetHeight()))
if !found {
return sdkerrors.Wrapf(types.ErrConsensusStateNotFound, "cannot check misbehaviour for client with ID %s", misbehaviour.GetClientID())
}
var err error
switch e := misbehaviour.(type) {
case ibctmtypes.Evidence:
// Get ConsensusState at TrustedHeight
consensusState, found := k.GetClientConsensusState(ctx, misbehaviour.GetClientID(), e.Header1.TrustedHeight)
if !found {
return sdkerrors.Wrapf(types.ErrConsensusStateNotFound, "cannot check misbehaviour for client with ID %s", misbehaviour.GetClientID())
}
clientState, err = tendermint.CheckMisbehaviourAndUpdateState(
clientState, consensusState, misbehaviour, consensusState.GetHeight(), ctx.BlockTime(), ctx.ConsensusParams(),
)

View File

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

View File

@ -5,7 +5,6 @@ import (
"strings"
"github.com/tendermint/tendermint/libs/log"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/store/prefix"
@ -15,7 +14,6 @@ import (
ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types"
commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types"
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)
// Keeper represents a type that grants read and write permissions to any client
@ -208,14 +206,11 @@ func (k Keeper) GetSelfConsensusState(ctx sdk.Context, height uint64) (exported.
return nil, false
}
valSet := stakingtypes.Validators(histInfo.Valset)
consensusState := ibctmtypes.ConsensusState{
Height: height,
Timestamp: histInfo.Header.Time,
Root: commitmenttypes.NewMerkleRoot(histInfo.Header.AppHash),
NextValidatorsHash: histInfo.Header.NextValidatorsHash,
ValidatorSet: tmtypes.NewValidatorSet(valSet.ToTmValidators()),
}
return consensusState, true
}

View File

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

View File

@ -36,7 +36,7 @@ func TestValidateGenesis(t *testing.T) {
val := tmtypes.NewValidator(pubKey, 10)
valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{val})
header := ibctmtypes.CreateTestHeader(chainID, height, now, valSet, []tmtypes.PrivValidator{privVal})
header := ibctmtypes.CreateTestHeader(chainID, height, height-1, now, valSet, valSet, []tmtypes.PrivValidator{privVal})
testCases := []struct {
name string
@ -64,7 +64,7 @@ func TestValidateGenesis(t *testing.T) {
clientID,
[]exported.ConsensusState{
ibctmtypes.NewConsensusState(
header.Time, commitmenttypes.NewMerkleRoot(header.AppHash), header.GetHeight(), header.ValidatorSet.Hash(), header.ValidatorSet,
header.Time, commitmenttypes.NewMerkleRoot(header.AppHash), header.GetHeight(), header.NextValidatorsHash,
),
},
},
@ -89,7 +89,7 @@ func TestValidateGenesis(t *testing.T) {
clientID,
[]exported.ConsensusState{
ibctmtypes.NewConsensusState(
header.Time, commitmenttypes.NewMerkleRoot(header.AppHash), header.GetHeight(), header.ValidatorSet.Hash(), header.ValidatorSet,
header.Time, commitmenttypes.NewMerkleRoot(header.AppHash), header.GetHeight(), header.NextValidatorsHash,
),
},
},
@ -129,7 +129,7 @@ func TestValidateGenesis(t *testing.T) {
"CLIENTID2",
[]exported.ConsensusState{
ibctmtypes.NewConsensusState(
header.Time, commitmenttypes.NewMerkleRoot(header.AppHash), 0, header.ValidatorSet.Hash(), header.ValidatorSet,
header.Time, commitmenttypes.NewMerkleRoot(header.AppHash), 0, header.NextValidatorsHash,
),
},
},
@ -154,7 +154,7 @@ func TestValidateGenesis(t *testing.T) {
clientID,
[]exported.ConsensusState{
ibctmtypes.NewConsensusState(
header.Time, commitmenttypes.NewMerkleRoot(header.AppHash), 0, header.ValidatorSet.Hash(), header.ValidatorSet,
header.Time, commitmenttypes.NewMerkleRoot(header.AppHash), 0, header.NextValidatorsHash,
),
},
),

View File

@ -1,6 +1,7 @@
package tendermint
import (
"bytes"
"time"
abci "github.com/tendermint/tendermint/abci/types"
@ -66,7 +67,20 @@ func checkMisbehaviour(
infractionHeight := evidence.GetHeight()
infractionTime := evidence.GetTime()
ageDuration := currentTimestamp.Sub(infractionTime)
ageBlocks := height - uint64(infractionHeight)
ageBlocks := uint64(infractionHeight) - height
// assert that trustedVals is NextValidators of last trusted header
// to do this, we check that trustedVals.Hash() == consState.NextValidatorsHash
trustedValsetHash1 := evidence.Header1.TrustedValidators.Hash()
trustedValsetHash2 := evidence.Header2.TrustedValidators.Hash()
if !bytes.Equal(consensusState.NextValidatorsHash, trustedValsetHash1) || !bytes.Equal(consensusState.NextValidatorsHash, trustedValsetHash2) {
return sdkerrors.Wrapf(
types.ErrInvalidValidatorSet,
"header's trusted validators %s, does not hash to either consensus state's trusted validator set. Expected: %X, got: TrustedValSet Header1 %X, TrustedValSet Header2 %X",
evidence.Header1.TrustedValidators, consensusState.NextValidatorsHash, trustedValsetHash1, trustedValsetHash2,
)
}
// Reject misbehaviour if the age is too old. Evidence is considered stale
// if the difference in time and number of blocks is greater than the allowed
@ -108,18 +122,18 @@ func checkMisbehaviour(
// - ValidatorSet must have 2/3 similarity with trusted FromValidatorSet
// - ValidatorSets on both headers are valid given the last trusted ValidatorSet
if err := consensusState.ValidatorSet.VerifyCommitLightTrusting(
if err := evidence.Header1.TrustedValidators.VerifyCommitLightTrusting(
evidence.ChainID, evidence.Header1.Commit.BlockID, evidence.Header1.Height,
evidence.Header1.Commit, clientState.TrustLevel.ToTendermint(),
); err != nil {
return sdkerrors.Wrapf(clienttypes.ErrInvalidEvidence, "validator set in header 1 has too much change from last known validator set: %v", err)
return sdkerrors.Wrapf(clienttypes.ErrInvalidEvidence, "validator set in header 1 has too much change from trusted validator set: %v", err)
}
if err := consensusState.ValidatorSet.VerifyCommitLightTrusting(
if err := evidence.Header2.TrustedValidators.VerifyCommitLightTrusting(
evidence.ChainID, evidence.Header2.Commit.BlockID, evidence.Header2.Height,
evidence.Header2.Commit, clientState.TrustLevel.ToTendermint(),
); err != nil {
return sdkerrors.Wrapf(clienttypes.ErrInvalidEvidence, "validator set in header 2 has too much change from last known validator set: %v", err)
return sdkerrors.Wrapf(clienttypes.ErrInvalidEvidence, "validator set in header 2 has too much change from trusted validator set: %v", err)
}
return nil

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,7 +5,7 @@ import (
)
const (
SubModuleName = "tendermint"
SubModuleName = "tendermint-client"
)
// IBC tendermint client sentinel errors
@ -19,4 +19,5 @@ var (
ErrTrustingPeriodExpired = sdkerrors.Register(SubModuleName, 8, "time since latest trusted state has passed the trusting period")
ErrUnbondingPeriodExpired = sdkerrors.Register(SubModuleName, 9, "time since latest trusted state has passed the unbonding period")
ErrInvalidProofSpecs = sdkerrors.Register(SubModuleName, 10, "invalid proof specs")
ErrInvalidValidatorSet = sdkerrors.Register(SubModuleName, 11, "invalid validator set")
)

View File

@ -1,6 +1,7 @@
package types
import (
"bytes"
"math"
"time"
@ -85,6 +86,17 @@ func (ev Evidence) GetTime() time.Time {
// ValidateBasic implements Evidence interface
func (ev Evidence) ValidateBasic() error {
if ev.Header1.TrustedHeight != ev.Header2.TrustedHeight {
return sdkerrors.Wrapf(ErrInvalidHeaderHeight, "evidence headers must share the same trusted height. got height1: %d, height2: %d",
ev.Header1.TrustedHeight, ev.Header2.TrustedHeight)
}
if !bytes.Equal(ev.Header1.TrustedValidators.Hash(), ev.Header2.TrustedValidators.Hash()) {
return sdkerrors.Wrapf(ErrInvalidValidatorSet, "trusted validators on both submitted headers must be the same. Got valset1: %s, valset2: %s",
ev.Header1.TrustedValidators, ev.Header2.TrustedValidators)
}
if ev.Header1.TrustedValidators == nil {
return sdkerrors.Wrap(ErrInvalidValidatorSet, "trusted validator set cannot be empty")
}
if err := host.ClientIdentifierValidator(ev.ClientID); err != nil {
return sdkerrors.Wrap(err, "evidence client ID is invalid")
}

View File

@ -17,7 +17,7 @@ func (suite *TendermintTestSuite) TestEvidence() {
ev := ibctmtypes.Evidence{
Header1: suite.header,
Header2: ibctmtypes.CreateTestHeader(chainID, height, suite.now, suite.valSet, signers),
Header2: ibctmtypes.CreateTestHeader(chainID, height, height-1, suite.now, suite.valSet, suite.valSet, signers),
ChainID: chainID,
ClientID: "gaiamainnet",
}
@ -67,18 +67,40 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() {
"valid evidence",
ibctmtypes.Evidence{
Header1: suite.header,
Header2: ibctmtypes.CreateTestHeader(chainID, height, suite.now.Add(time.Minute), suite.valSet, signers),
Header2: ibctmtypes.CreateTestHeader(chainID, height, height-1, suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers),
ChainID: chainID,
ClientID: "gaiamainnet",
},
func(ev *ibctmtypes.Evidence) error { return nil },
true,
},
{
"trusted heights don't match",
ibctmtypes.Evidence{
Header1: suite.header,
Header2: ibctmtypes.CreateTestHeader(chainID, height, height-2, suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers),
ChainID: chainID,
ClientID: "gaiamainnet",
},
func(ev *ibctmtypes.Evidence) error { return nil },
false,
},
{
"trusted valsets don't match",
ibctmtypes.Evidence{
Header1: suite.header,
Header2: ibctmtypes.CreateTestHeader(chainID, height, height-1, suite.now.Add(time.Minute), suite.valSet, bothValSet, signers),
ChainID: chainID,
ClientID: "gaiamainnet",
},
func(ev *ibctmtypes.Evidence) error { return nil },
false,
},
{
"invalid client ID ",
ibctmtypes.Evidence{
Header1: suite.header,
Header2: ibctmtypes.CreateTestHeader(chainID, height, suite.now, suite.valSet, signers),
Header2: ibctmtypes.CreateTestHeader(chainID, height, height-1, suite.now, suite.valSet, suite.valSet, signers),
ChainID: chainID,
ClientID: "GAIA",
},
@ -89,7 +111,7 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() {
"wrong chainID on header1",
ibctmtypes.Evidence{
Header1: suite.header,
Header2: ibctmtypes.CreateTestHeader("ethermint", height, suite.now, suite.valSet, signers),
Header2: ibctmtypes.CreateTestHeader("ethermint", height, height-1, suite.now, suite.valSet, suite.valSet, signers),
ChainID: "ethermint",
ClientID: "gaiamainnet",
},
@ -100,7 +122,7 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() {
"wrong chainID on header2",
ibctmtypes.Evidence{
Header1: suite.header,
Header2: ibctmtypes.CreateTestHeader("ethermint", height, suite.now, suite.valSet, signers),
Header2: ibctmtypes.CreateTestHeader("ethermint", height, height-1, suite.now, suite.valSet, suite.valSet, signers),
ChainID: chainID,
ClientID: "gaiamainnet",
},
@ -111,7 +133,7 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() {
"mismatched heights",
ibctmtypes.Evidence{
Header1: suite.header,
Header2: ibctmtypes.CreateTestHeader(chainID, 6, suite.now, suite.valSet, signers),
Header2: ibctmtypes.CreateTestHeader(chainID, 6, 4, suite.now, suite.valSet, suite.valSet, signers),
ChainID: chainID,
ClientID: "gaiamainnet",
},
@ -132,7 +154,7 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() {
{
"header 1 doesn't have 2/3 majority",
ibctmtypes.Evidence{
Header1: ibctmtypes.CreateTestHeader(chainID, height, suite.now, bothValSet, bothSigners),
Header1: ibctmtypes.CreateTestHeader(chainID, height, height-1, suite.now, bothValSet, suite.valSet, bothSigners),
Header2: suite.header,
ChainID: chainID,
ClientID: "gaiamainnet",
@ -150,7 +172,7 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() {
"header 2 doesn't have 2/3 majority",
ibctmtypes.Evidence{
Header1: suite.header,
Header2: ibctmtypes.CreateTestHeader(chainID, height, suite.now, bothValSet, bothSigners),
Header2: ibctmtypes.CreateTestHeader(chainID, height, height-1, suite.now, bothValSet, suite.valSet, bothSigners),
ChainID: chainID,
ClientID: "gaiamainnet",
},
@ -167,7 +189,7 @@ func (suite *TendermintTestSuite) TestEvidenceValidateBasic() {
"validators sign off on wrong commit",
ibctmtypes.Evidence{
Header1: suite.header,
Header2: ibctmtypes.CreateTestHeader(chainID, height, suite.now, bothValSet, bothSigners),
Header2: ibctmtypes.CreateTestHeader(chainID, height, height-1, suite.now, bothValSet, suite.valSet, bothSigners),
ChainID: chainID,
ClientID: "gaiamainnet",
},

View File

@ -14,10 +14,20 @@ import (
var _ clientexported.Header = Header{}
// Header defines the Tendermint consensus Header
// Header defines the Tendermint client consensus Header.
// It encapsulates all the information necessary to update from a trusted Tendermint ConsensusState.
// The inclusion of TrustedHeight and TrustedValidators allows this update to process correctly, so long
// as the ConsensusState for the TrustedHeight exists, this removes race conditions among relayers
// The SignedHeader and ValidatorSet are the new untrusted update fields for the client.
// The TrustedHeight is the height of a stored ConsensusState on the client that will be used to verify the new untrusted header.
// The Trusted ConsensusState must be within the unbonding period of current time in order to correctly verify,
// and the TrustedValidators must hash to TrustedConsensusState.NextValidatorsHash since that is the last trusted validator set
// at the TrustedHeight.
type Header struct {
tmtypes.SignedHeader `json:"signed_header" yaml:"signed_header"` // contains the commitment root
ValidatorSet *tmtypes.ValidatorSet `json:"validator_set" yaml:"validator_set"`
ValidatorSet *tmtypes.ValidatorSet `json:"validator_set" yaml:"validator_set"` // the validator set that signed Header
TrustedHeight uint64 `json:"trusted_height" yaml:"trusted_height"` // the height of a trusted header seen by client less than or equal to Header
TrustedValidators *tmtypes.ValidatorSet `json:"trusted_vals" yaml:"trusted_vals"` // the last trusted validator set at trusted height
}
// ClientType defines that the Header is a Tendermint consensus algorithm
@ -25,13 +35,13 @@ func (h Header) ClientType() clientexported.ClientType {
return clientexported.Tendermint
}
// ConsensusState returns the consensus state associated with the header
// ConsensusState returns the updated consensus state associated with the header
func (h Header) ConsensusState() ConsensusState {
return ConsensusState{
Height: uint64(h.Height),
Timestamp: h.Time,
Root: commitmenttypes.NewMerkleRoot(h.AppHash),
ValidatorSet: h.ValidatorSet,
Height: uint64(h.Height),
Timestamp: h.Time,
Root: commitmenttypes.NewMerkleRoot(h.AppHash),
NextValidatorsHash: h.NextValidatorsHash,
}
}
@ -44,10 +54,18 @@ func (h Header) GetHeight() uint64 {
// ValidateBasic calls the SignedHeader ValidateBasic function
// and checks that validatorsets are not nil
// NOTE: TrustedHeight and TrustedValidators may be empty when creating client
// with MsgCreateClient
func (h Header) ValidateBasic(chainID string) error {
if err := h.SignedHeader.ValidateBasic(chainID); err != nil {
return sdkerrors.Wrap(err, "header failed basic validation")
}
// TrustedHeight is less than Header for updates
// and less than or equal to Header for misbehaviour
if h.TrustedHeight > uint64(h.Height) {
return sdkerrors.Wrapf(ErrInvalidHeaderHeight, "TrustedHeight %d must be less than or equal to header height %d",
h.TrustedHeight, h.Height)
}
if h.ValidatorSet == nil {
return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "validator set is nil")
}

View File

@ -14,7 +14,7 @@ func (suite *TendermintTestSuite) TestHeaderValidateBasic() {
}{
{"valid header", suite.header, chainID, true},
{"signed header basic validation failed", suite.header, "chainID", false},
{"validator set nil", ibctmtypes.Header{suite.header.SignedHeader, nil}, chainID, false},
{"validator set nil", ibctmtypes.Header{suite.header.SignedHeader, nil, 0, suite.valSet}, chainID, false},
}
suite.Require().Equal(clientexported.Tendermint, suite.header.ClientType())

View File

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

View File

@ -14,7 +14,7 @@ import (
func (suite *TendermintTestSuite) TestMsgCreateClientValidateBasic() {
privKey := secp256k1.GenPrivKey()
signer := sdk.AccAddress(privKey.PubKey().Address())
invalidHeader := types.CreateTestHeader(suite.header.ChainID, height, suite.now, suite.valSet, []tmtypes.PrivValidator{suite.privVal})
invalidHeader := types.CreateTestHeader(suite.header.ChainID, height, 0, suite.now, suite.valSet, nil, []tmtypes.PrivValidator{suite.privVal})
invalidHeader.ValidatorSet = nil
cases := []struct {

View File

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

View File

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

View File

@ -1,6 +1,7 @@
package tendermint
import (
"bytes"
"time"
lite "github.com/tendermint/tendermint/lite2"
@ -66,9 +67,21 @@ func CheckValidityAndUpdateState(
}
// checkValidity checks if the Tendermint header is valid.
// CONTRACT: consState.Height == header.TrustedHeight
func checkValidity(
clientState *types.ClientState, consState types.ConsensusState, header types.Header, currentTimestamp time.Time,
clientState *types.ClientState, consState types.ConsensusState,
header types.Header, currentTimestamp time.Time,
) error {
// assert that trustedVals is NextValidators of last trusted header
// to do this, we check that trustedVals.Hash() == consState.NextValidatorsHash
tvalHash := header.TrustedValidators.Hash()
if !bytes.Equal(consState.NextValidatorsHash, tvalHash) {
return sdkerrors.Wrapf(
types.ErrInvalidValidatorSet,
"trusted validators %s, does not hash to latest trusted validators. Expected: %X, got: %X",
header.TrustedValidators, consState.NextValidatorsHash, tvalHash,
)
}
// assert trusting period has not yet passed
if currentTimestamp.Sub(consState.Timestamp) >= clientState.TrustingPeriod {
return sdkerrors.Wrapf(
@ -114,10 +127,10 @@ func checkValidity(
Header: &trustedHeader,
}
// Verify next header with the last header's validatorset as trusted validatorset
// Verify next header with the passed-in trustedVals
err := lite.Verify(
clientState.GetChainID(), &signedHeader,
consState.ValidatorSet, &header.SignedHeader, header.ValidatorSet,
header.TrustedValidators, &header.SignedHeader, header.ValidatorSet,
clientState.TrustingPeriod, currentTimestamp, clientState.MaxClockDrift, clientState.TrustLevel.ToTendermint(),
)
if err != nil {
@ -136,7 +149,6 @@ func update(clientState *types.ClientState, header types.Header) (*types.ClientS
Timestamp: header.Time,
Root: commitmenttypes.NewMerkleRoot(header.AppHash),
NextValidatorsHash: header.NextValidatorsHash,
ValidatorSet: header.ValidatorSet,
}
return clientState, consensusState

View File

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

View File

@ -41,7 +41,7 @@ func (suite *IBCTestSuite) TestValidateGenesis() {
clientID,
[]exported.ConsensusState{
ibctmtypes.NewConsensusState(
suite.header.Time, commitmenttypes.NewMerkleRoot(suite.header.AppHash), suite.header.GetHeight(), suite.header.ValidatorSet.Hash(), suite.header.ValidatorSet,
suite.header.Time, commitmenttypes.NewMerkleRoot(suite.header.AppHash), suite.header.GetHeight(), suite.header.NextValidatorsHash,
),
},
),

View File

@ -62,7 +62,7 @@ func (suite *IBCTestSuite) SetupTest() {
val := tmtypes.NewValidator(pubKey, 10)
valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{val})
suite.header = ibctmtypes.CreateTestHeader(chainID, height, now, valSet, []tmtypes.PrivValidator{privVal})
suite.header = ibctmtypes.CreateTestHeader(chainID, height, height-1, now, valSet, valSet, []tmtypes.PrivValidator{privVal})
suite.cdc = suite.app.Codec()
suite.ctx = suite.app.BaseApp.NewContext(isCheckTx, abci.Header{})

View File

@ -19,6 +19,7 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
@ -32,6 +33,7 @@ import (
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
"github.com/cosmos/cosmos-sdk/x/ibc/keeper"
"github.com/cosmos/cosmos-sdk/x/ibc/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)
// Default params constants used to create a TM client
@ -244,6 +246,24 @@ func (chain *TestChain) GetClientState(clientID string) clientexported.ClientSta
return clientState
}
// GetConsensusState retrieves the consensus state for the provided clientID and height.
// It will return a success boolean depending on if consensus state exists or not.
func (chain *TestChain) GetConsensusState(clientID string, height uint64) (clientexported.ConsensusState, bool) {
return chain.App.IBCKeeper.ClientKeeper.GetClientConsensusState(chain.GetContext(), clientID, height)
}
// GetValsAtHeight will return the validator set of the chain at a given height. It will return
// a success boolean depending on if the validator set exists or not at that height.
func (chain *TestChain) GetValsAtHeight(height int64) (*tmtypes.ValidatorSet, bool) {
histInfo, ok := chain.App.StakingKeeper.GetHistoricalInfo(chain.GetContext(), height)
if !ok {
return nil, false
}
valSet := stakingtypes.Validators(histInfo.Valset)
return tmtypes.NewValidatorSet(valSet.ToTmValidators()), true
}
// GetConnection retrieves an IBC Connection for the provided TestConnection. The
// connection is expected to exist otherwise testing will fail.
func (chain *TestChain) GetConnection(testConnection *TestConnection) connectiontypes.ConnectionEnd {
@ -331,9 +351,36 @@ func (chain *TestChain) CreateTMClient(counterparty *TestChain, clientID string)
// UpdateTMClient will construct and execute a 07-tendermint MsgUpdateClient. The counterparty
// client will be updated on the (target) chain.
// UpdateTMClient mocks the relayer flow necessary for updating a Tendermint client
func (chain *TestChain) UpdateTMClient(counterparty *TestChain, clientID string) error {
header := counterparty.LastHeader
// Relayer must query for LatestHeight on client to get TrustedHeight
trustedHeight := chain.GetClientState(clientID).GetLatestHeight()
var (
trustedVals *tmtypes.ValidatorSet
ok bool
)
// Once we get TrustedHeight from client, we must query the validators from the counterparty chain
// If the LatestHeight == LastHeader.Height, then TrustedValidators are current validators
// If LatestHeight < LastHeader.Height, we can query the historical validator set from HistoricalInfo
if trustedHeight == uint64(counterparty.LastHeader.Height) {
trustedVals = counterparty.Vals
} else {
// NOTE: We need to get validators from counterparty at height: trustedHeight+1
// since the last trusted validators for a header at height h
// is the NextValidators at h+1 committed to in header h by
// NextValidatorsHash
trustedVals, ok = counterparty.GetValsAtHeight(int64(trustedHeight + 1))
if !ok {
return sdkerrors.Wrapf(ibctmtypes.ErrInvalidHeaderHeight, "could not retrieve trusted validators at trustedHeight: %d", trustedHeight)
}
}
// inject trusted fields into last header
header.TrustedHeight = trustedHeight
header.TrustedValidators = trustedVals
msg := ibctmtypes.NewMsgUpdateClient(
clientID, counterparty.LastHeader,
clientID, header,
chain.SenderAccount.GetAddress(),
)
@ -373,6 +420,8 @@ func (chain *TestChain) CreateTMClientHeader() ibctmtypes.Header {
Commit: commit,
}
// Do not set trusted field here, these fields can be inserted before relaying messages to a client.
// The relayer is responsible for querying client and injecting appropriate trusted fields.
return ibctmtypes.Header{
SignedHeader: signedHeader,
ValidatorSet: chain.Vals,