Retrieve Epoch from ChainID (#7280)

* add functions to parse epoch from height

* remove 0 epoch hardcoding

* fix tendermint tests

* fix tests

* start update docs

* progress

* better parsing of chainID

* fix update and misbehaviour logic and add tests

* update docs

* docfix

* Apply suggestions from code review

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

* Update x/ibc/07-tendermint/types/misbehaviour_handle_test.go

* change self checks to use epochs

* address rest of reviews

* rename epoch number to version in docs

* wrap up rest of TODOs

* Update x/ibc/02-client/genesis.go

* add self validate test

* Apply suggestions from code review

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

* fix godoc

Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com>
This commit is contained in:
Aditya 2020-09-15 17:09:48 -04:00 committed by GitHub
parent 5dc434fbca
commit 984c2d4179
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 405 additions and 160 deletions

View File

@ -115,7 +115,6 @@ func QueryConsensusStateABCI(
return nil, err return nil, err
} }
// TODO: retrieve epoch-number from chain-id
return types.NewQueryConsensusStateResponse(clientID, anyConsensusState, proofBz, proofHeight), nil return types.NewQueryConsensusStateResponse(clientID, anyConsensusState, proofBz, proofHeight), nil
} }

View File

@ -45,10 +45,9 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, gs types.GenesisState) {
} }
// client id is always "localhost" // client id is always "localhost"
// Hardcode 0 as epoch number for now epoch := types.ParseChainID(ctx.ChainID())
// TODO: Retrieve epoch from chain-id
clientState := localhosttypes.NewClientState( clientState := localhosttypes.NewClientState(
ctx.ChainID(), types.NewHeight(0, uint64(ctx.BlockHeight())), ctx.ChainID(), types.NewHeight(epoch, uint64(ctx.BlockHeight())),
) )
_, err := k.CreateClient(ctx, exported.ClientTypeLocalHost, clientState, nil) _, err := k.CreateClient(ctx, exported.ClientTypeLocalHost, clientState, nil)

View File

@ -7,6 +7,7 @@ import (
tmtypes "github.com/tendermint/tendermint/types" tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types" ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types"
localhosttypes "github.com/cosmos/cosmos-sdk/x/ibc/09-localhost/types" localhosttypes "github.com/cosmos/cosmos-sdk/x/ibc/09-localhost/types"
commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types"
@ -60,11 +61,17 @@ 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, int64(suite.header.GetHeight().GetEpochHeight()+3), int64(suite.header.GetHeight().GetEpochHeight()), suite.header.Header.Time.Add(time.Hour), heightPlus3 := clienttypes.NewHeight(suite.header.GetHeight().GetEpochNumber(), suite.header.GetHeight().GetEpochHeight()+3)
height := suite.header.GetHeight().(clienttypes.Height)
return ibctmtypes.CreateTestHeader(testChainID, heightPlus3, height, suite.header.Header.Time.Add(time.Hour),
suite.valSet, 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, int64(suite.header.GetHeight().GetEpochHeight()-2), int64(suite.header.GetHeight().GetEpochHeight())-4, suite.header.Header.Time, heightMinus2 := clienttypes.NewHeight(suite.header.GetHeight().GetEpochNumber(), suite.header.GetHeight().GetEpochHeight()-2)
heightMinus4 := clienttypes.NewHeight(suite.header.GetHeight().GetEpochNumber(), suite.header.GetHeight().GetEpochHeight()-4)
return ibctmtypes.CreateTestHeader(testChainID, heightMinus2, heightMinus4, suite.header.Header.Time,
suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal})
} }
var ( var (
@ -270,6 +277,7 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
altTime := suite.ctx.BlockTime().Add(time.Minute) altTime := suite.ctx.BlockTime().Add(time.Minute)
heightPlus3 := types.NewHeight(0, height+3) heightPlus3 := types.NewHeight(0, height+3)
heightPlus5 := types.NewHeight(0, height+5)
testCases := []struct { testCases := []struct {
name string name string
@ -280,8 +288,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
{ {
"trusting period misbehavior should pass", "trusting period misbehavior should pass",
&ibctmtypes.Misbehaviour{ &ibctmtypes.Misbehaviour{
Header1: ibctmtypes.CreateTestHeader(testChainID, height, height, altTime, bothValSet, bothValSet, bothSigners), Header1: ibctmtypes.CreateTestHeader(testChainID, testClientHeight, testClientHeight, altTime, bothValSet, bothValSet, bothSigners),
Header2: ibctmtypes.CreateTestHeader(testChainID, height, height, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), Header2: ibctmtypes.CreateTestHeader(testChainID, testClientHeight, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners),
ChainId: testChainID, ChainId: testChainID,
ClientId: testClientID, ClientId: testClientID,
}, },
@ -297,8 +305,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
{ {
"misbehavior at later height should pass", "misbehavior at later height should pass",
&ibctmtypes.Misbehaviour{ &ibctmtypes.Misbehaviour{
Header1: ibctmtypes.CreateTestHeader(testChainID, height+5, height, altTime, bothValSet, valSet, bothSigners), Header1: ibctmtypes.CreateTestHeader(testChainID, heightPlus5, testClientHeight, altTime, bothValSet, valSet, bothSigners),
Header2: ibctmtypes.CreateTestHeader(testChainID, height+5, height, suite.ctx.BlockTime(), bothValSet, valSet, bothSigners), Header2: ibctmtypes.CreateTestHeader(testChainID, heightPlus5, testClientHeight, suite.ctx.BlockTime(), bothValSet, valSet, bothSigners),
ChainId: testChainID, ChainId: testChainID,
ClientId: testClientID, ClientId: testClientID,
}, },
@ -324,8 +332,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
{ {
"misbehavior at later height with different trusted heights should pass", "misbehavior at later height with different trusted heights should pass",
&ibctmtypes.Misbehaviour{ &ibctmtypes.Misbehaviour{
Header1: ibctmtypes.CreateTestHeader(testChainID, height+5, height, altTime, bothValSet, valSet, bothSigners), Header1: ibctmtypes.CreateTestHeader(testChainID, heightPlus5, testClientHeight, altTime, bothValSet, valSet, bothSigners),
Header2: ibctmtypes.CreateTestHeader(testChainID, height+5, height+3, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), Header2: ibctmtypes.CreateTestHeader(testChainID, heightPlus5, heightPlus3, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners),
ChainId: testChainID, ChainId: testChainID,
ClientId: testClientID, ClientId: testClientID,
}, },
@ -351,8 +359,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
{ {
"trusted ConsensusState1 not found", "trusted ConsensusState1 not found",
&ibctmtypes.Misbehaviour{ &ibctmtypes.Misbehaviour{
Header1: ibctmtypes.CreateTestHeader(testChainID, height+5, height+3, altTime, bothValSet, bothValSet, bothSigners), Header1: ibctmtypes.CreateTestHeader(testChainID, heightPlus5, heightPlus3, altTime, bothValSet, bothValSet, bothSigners),
Header2: ibctmtypes.CreateTestHeader(testChainID, height+5, height, suite.ctx.BlockTime(), bothValSet, valSet, bothSigners), Header2: ibctmtypes.CreateTestHeader(testChainID, heightPlus5, testClientHeight, suite.ctx.BlockTime(), bothValSet, valSet, bothSigners),
ChainId: testChainID, ChainId: testChainID,
ClientId: testClientID, ClientId: testClientID,
}, },
@ -368,8 +376,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
{ {
"trusted ConsensusState2 not found", "trusted ConsensusState2 not found",
&ibctmtypes.Misbehaviour{ &ibctmtypes.Misbehaviour{
Header1: ibctmtypes.CreateTestHeader(testChainID, height+5, height, altTime, bothValSet, valSet, bothSigners), Header1: ibctmtypes.CreateTestHeader(testChainID, heightPlus5, testClientHeight, altTime, bothValSet, valSet, bothSigners),
Header2: ibctmtypes.CreateTestHeader(testChainID, height+5, height+3, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), Header2: ibctmtypes.CreateTestHeader(testChainID, heightPlus5, heightPlus3, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners),
ChainId: testChainID, ChainId: testChainID,
ClientId: testClientID, ClientId: testClientID,
}, },
@ -391,8 +399,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
{ {
"client already frozen at earlier height", "client already frozen at earlier height",
&ibctmtypes.Misbehaviour{ &ibctmtypes.Misbehaviour{
Header1: ibctmtypes.CreateTestHeader(testChainID, height, height, altTime, bothValSet, bothValSet, bothSigners), Header1: ibctmtypes.CreateTestHeader(testChainID, testClientHeight, testClientHeight, altTime, bothValSet, bothValSet, bothSigners),
Header2: ibctmtypes.CreateTestHeader(testChainID, height, height, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), Header2: ibctmtypes.CreateTestHeader(testChainID, testClientHeight, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners),
ChainId: testChainID, ChainId: testChainID,
ClientId: testClientID, ClientId: testClientID,
}, },
@ -411,8 +419,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
{ {
"misbehaviour check failed", "misbehaviour check failed",
&ibctmtypes.Misbehaviour{ &ibctmtypes.Misbehaviour{
Header1: ibctmtypes.CreateTestHeader(testChainID, height, height, altTime, bothValSet, bothValSet, bothSigners), Header1: ibctmtypes.CreateTestHeader(testChainID, testClientHeight, testClientHeight, altTime, bothValSet, bothValSet, bothSigners),
Header2: ibctmtypes.CreateTestHeader(testChainID, height, height, suite.ctx.BlockTime(), altValSet, bothValSet, altSigners), Header2: ibctmtypes.CreateTestHeader(testChainID, testClientHeight, testClientHeight, suite.ctx.BlockTime(), altValSet, bothValSet, altSigners),
ChainId: testChainID, ChainId: testChainID,
ClientId: testClientID, ClientId: testClientID,
}, },

View File

@ -189,14 +189,15 @@ func (k Keeper) GetClientConsensusStateLTE(ctx sdk.Context, clientID string, max
// GetSelfConsensusState introspects the (self) past historical info at a given height // GetSelfConsensusState introspects the (self) past historical info at a given height
// and returns the expected consensus state at that height. // and returns the expected consensus state at that height.
// TODO: Replace height with *clienttypes.Height once interfaces change // For now, can only retrieve self consensus states for the current epoch
func (k Keeper) GetSelfConsensusState(ctx sdk.Context, height exported.Height) (exported.ConsensusState, bool) { func (k Keeper) GetSelfConsensusState(ctx sdk.Context, height exported.Height) (exported.ConsensusState, bool) {
// TODO: check self chain-id against epoch number
selfHeight, ok := height.(types.Height) selfHeight, ok := height.(types.Height)
if !ok { if !ok {
return nil, false return nil, false
} }
if selfHeight.EpochNumber != 0 { // check that height epoch matches chainID epoch
epoch := types.ParseChainID(ctx.ChainID())
if epoch != height.GetEpochNumber() {
return nil, false return nil, false
} }
histInfo, found := k.stakingKeeper.GetHistoricalInfo(ctx, int64(selfHeight.EpochHeight)) histInfo, found := k.stakingKeeper.GetHistoricalInfo(ctx, int64(selfHeight.EpochHeight))
@ -214,6 +215,7 @@ func (k Keeper) GetSelfConsensusState(ctx sdk.Context, height exported.Height) (
// ValidateSelfClient validates the client parameters for a client of the running chain // ValidateSelfClient validates the client parameters for a client of the running chain
// This function is only used to validate the client state the counterparty stores for this chain // This function is only used to validate the client state the counterparty stores for this chain
// Client must be in same epoch as the executing chain
func (k Keeper) ValidateSelfClient(ctx sdk.Context, clientState exported.ClientState) error { func (k Keeper) ValidateSelfClient(ctx sdk.Context, clientState exported.ClientState) error {
tmClient, ok := clientState.(*ibctmtypes.ClientState) tmClient, ok := clientState.(*ibctmtypes.ClientState)
if !ok { if !ok {
@ -230,9 +232,15 @@ func (k Keeper) ValidateSelfClient(ctx sdk.Context, clientState exported.ClientS
ctx.ChainID(), tmClient.ChainId) ctx.ChainID(), tmClient.ChainId)
} }
// For now, assume epoch number is zero epoch := types.ParseChainID(ctx.ChainID())
// TODO: Retrieve epoch number from chain-id
selfHeight := types.NewHeight(0, uint64(ctx.BlockHeight())) // client must be in the same epoch as executing chain
if tmClient.LatestHeight.EpochNumber != epoch {
return sdkerrors.Wrapf(types.ErrInvalidClient, "client is not in the same epoch as the chain. expected epoch: %d, got: %d",
tmClient.LatestHeight.EpochNumber, epoch)
}
selfHeight := types.NewHeight(epoch, uint64(ctx.BlockHeight()))
if tmClient.LatestHeight.GT(selfHeight) { if tmClient.LatestHeight.GT(selfHeight) {
return sdkerrors.Wrapf(types.ErrInvalidClient, "client has LatestHeight %d greater than chain height %d", return sdkerrors.Wrapf(types.ErrInvalidClient, "client has LatestHeight %d greater than chain height %d",
tmClient.LatestHeight, ctx.BlockHeight()) tmClient.LatestHeight, ctx.BlockHeight())

View File

@ -26,7 +26,8 @@ import (
) )
const ( const (
testChainID = "gaiahub" testChainID = "gaiahub-0"
testChainIDEpoch1 = "gaiahub-1"
testClientID = "gaiachain" testClientID = "gaiachain"
testClientID2 = "ethbridge" testClientID2 = "ethbridge"
@ -40,6 +41,7 @@ const (
) )
var testClientHeight = types.NewHeight(0, 5) var testClientHeight = types.NewHeight(0, 5)
var testClientHeightEpoch1 = types.NewHeight(1, 5)
type KeeperTestSuite struct { type KeeperTestSuite struct {
suite.Suite suite.Suite
@ -83,10 +85,12 @@ func (suite *KeeperTestSuite) SetupTest() {
pubKey, err := suite.privVal.GetPubKey() pubKey, err := suite.privVal.GetPubKey()
suite.Require().NoError(err) suite.Require().NoError(err)
testClientHeightMinus1 := types.NewHeight(0, height-1)
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.valSetHash = suite.valSet.Hash() suite.valSetHash = suite.valSet.Hash()
suite.header = ibctmtypes.CreateTestHeader(testChainID, height, height-1, now2, suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) suite.header = ibctmtypes.CreateTestHeader(testChainID, testClientHeight, testClientHeightMinus1, now2, suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal})
suite.consensusState = ibctmtypes.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot([]byte("hash")), suite.valSetHash) suite.consensusState = ibctmtypes.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot([]byte("hash")), suite.valSetHash)
var validators stakingtypes.Validators var validators stakingtypes.Validators
@ -170,6 +174,11 @@ func (suite *KeeperTestSuite) TestValidateSelfClient() {
ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.NewHeight(0, testClientHeight.EpochHeight+10), commitmenttypes.GetSDKSpecs(), false, false), ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.NewHeight(0, testClientHeight.EpochHeight+10), commitmenttypes.GetSDKSpecs(), false, false),
false, false,
}, },
{
"invalid client epoch",
ibctmtypes.NewClientState(testChainIDEpoch1, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeightEpoch1, commitmenttypes.GetSDKSpecs(), false, false),
false,
},
{ {
"invalid proof specs", "invalid proof specs",
ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, nil, false, false), ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, nil, false, false),
@ -291,7 +300,9 @@ func (suite KeeperTestSuite) TestConsensusStateHelpers() {
nextState := ibctmtypes.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot([]byte("next")), suite.valSetHash) nextState := ibctmtypes.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot([]byte("next")), suite.valSetHash)
header := ibctmtypes.CreateTestHeader(testClientID, height+5, height, suite.header.Header.Time.Add(time.Minute), testClientHeightPlus5 := types.NewHeight(0, height+5)
header := ibctmtypes.CreateTestHeader(testClientID, testClientHeightPlus5, testClientHeight, suite.header.Header.Time.Add(time.Minute),
suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal})
// mock update functionality // mock update functionality

View File

@ -53,7 +53,8 @@ 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, height-1, now, valSet, valSet, []tmtypes.PrivValidator{privVal}) heightMinus1 := types.NewHeight(0, height-1)
header := ibctmtypes.CreateTestHeader(chainID, clientHeight, heightMinus1, now, valSet, valSet, []tmtypes.PrivValidator{privVal})
testCases := []struct { testCases := []struct {
name string name string

View File

@ -2,6 +2,7 @@ package types
import ( import (
"fmt" "fmt"
"regexp"
"strconv" "strconv"
"strings" "strings"
@ -12,6 +13,11 @@ import (
var _ exported.Height = (*Height)(nil) var _ exported.Height = (*Height)(nil)
// IsEpochFormat checks if a chainID is in the format required for parsing epochs
// The chainID must be in the form: `{chainID}-{version}
// 24-host may enforce stricter checks on chainID
var IsEpochFormat = regexp.MustCompile(`^.+[^-]-{1}[1-9][0-9]*$`).MatchString
// ZeroHeight is a helper function which returns an uninitialized height. // ZeroHeight is a helper function which returns an uninitialized height.
func ZeroHeight() Height { func ZeroHeight() Height {
return Height{} return Height{}
@ -35,7 +41,7 @@ func (h Height) GetEpochHeight() uint64 {
return h.EpochHeight return h.EpochHeight
} }
/// Compare implements a method to compare two heights. When comparing two heights a, b // Compare implements a method to compare two heights. When comparing two heights a, b
// we can call a.Compare(b) which will return // we can call a.Compare(b) which will return
// -1 if a < b // -1 if a < b
// 0 if a = b // 0 if a = b
@ -143,8 +149,42 @@ func ParseHeight(heightStr string) (Height, error) {
return NewHeight(epochNumber, epochHeight), nil return NewHeight(epochNumber, epochHeight), nil
} }
// GetSelfHeight is a utility function that returns self height given context // SetEpochNumber takes a chainID in valid epoch format and swaps the epoch number
// TODO: Retrieve epoch-number from chain-id // in the chainID with the given epoch number.
func GetSelfHeight(ctx sdk.Context) Height { func SetEpochNumber(chainID string, epoch uint64) (string, error) {
return NewHeight(0, uint64(ctx.BlockHeight())) if !IsEpochFormat(chainID) {
return "", sdkerrors.Wrapf(
sdkerrors.ErrInvalidChainID, "chainID is not in epoch format: %s", chainID,
)
}
splitStr := strings.Split(chainID, "-")
// swap out epoch number with given epoch
splitStr[len(splitStr)-1] = strconv.Itoa(int(epoch))
return strings.Join(splitStr, "-"), nil
}
// ParseChainID is a utility function that returns an epoch number from the given ChainID.
// ParseChainID attempts to parse a chain id in the format: `{chainID}-{version}`
// and return the epochnumber as a uint64.
// If the chainID is not in the expected format, a default epoch value of 0 is returned.
func ParseChainID(chainID string) uint64 {
if !IsEpochFormat(chainID) {
// chainID is not in epoch format, return 0 as default
return 0
}
splitStr := strings.Split(chainID, "-")
epoch, err := strconv.ParseUint(splitStr[len(splitStr)-1], 10, 64)
// sanity check: error should always be nil since regex only allows numbers in last element
if err != nil {
panic(fmt.Sprintf("regex allowed non-number value as last split element for chainID: %s", chainID))
}
return epoch
}
// GetSelfHeight is a utility function that returns self height given context
// Epoch number is retrieved from ctx.ChainID()
func GetSelfHeight(ctx sdk.Context) Height {
epoch := ParseChainID(ctx.ChainID())
return NewHeight(epoch, uint64(ctx.BlockHeight()))
} }

View File

@ -93,3 +93,56 @@ func (suite *TypesTestSuite) TestMustParseHeight() {
types.MustParseHeight("0-0") types.MustParseHeight("0-0")
}) })
} }
func TestParseChainID(t *testing.T) {
cases := []struct {
chainID string
epoch uint64
formatted bool
}{
{"gaiamainnet-3", 3, true},
{"gaia-mainnet-40", 40, true},
{"gaiamainnet-3-39", 39, true},
{"gaiamainnet--", 0, false},
{"gaiamainnet-03", 0, false},
{"gaiamainnet--4", 0, false},
{"gaiamainnet-3.4", 0, false},
{"gaiamainnet", 0, false},
}
for i, tc := range cases {
require.Equal(t, tc.formatted, types.IsEpochFormat(tc.chainID), "case %d does not match expected format", i)
epoch := types.ParseChainID(tc.chainID)
require.Equal(t, tc.epoch, epoch, "case %d returns incorrect epoch", i)
}
}
func TestSetEpochNumber(t *testing.T) {
// Test SetEpochNumber
chainID, err := types.SetEpochNumber("gaiamainnet", 3)
require.Error(t, err, "invalid epoch format passed SetEpochNumber")
require.Equal(t, "", chainID, "invalid epoch format returned non-empty string on SetEpochNumber")
chainID = "gaiamainnet-3"
chainID, err = types.SetEpochNumber(chainID, 4)
require.NoError(t, err, "valid epoch format failed SetEpochNumber")
require.Equal(t, "gaiamainnet-4", chainID, "valid epoch format returned incorrect string on SetEpochNumber")
}
func (suite *TypesTestSuite) TestSelfHeight() {
ctx := suite.chainA.GetContext()
// Test default epoch
ctx = ctx.WithChainID("gaiamainnet")
ctx = ctx.WithBlockHeight(10)
height := types.GetSelfHeight(ctx)
suite.Require().Equal(types.NewHeight(0, 10), height, "default self height failed")
// Test successful epoch format
ctx = ctx.WithChainID("gaiamainnet-3")
ctx = ctx.WithBlockHeight(18)
height = types.GetSelfHeight(ctx)
suite.Require().Equal(types.NewHeight(3, 18), height, "valid self height failed")
}

View File

@ -354,8 +354,10 @@ func (suite *TypesTestSuite) TestMarshalMsgSubmitMisbehaviour() {
}, },
{ {
"tendermint client", func() { "tendermint client", func() {
header1 := ibctmtypes.CreateTestHeader(suite.chainA.ChainID, suite.chainA.CurrentHeader.Height, suite.chainA.CurrentHeader.Height-1, suite.chainA.CurrentHeader.Time, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) height := types.NewHeight(0, uint64(suite.chainA.CurrentHeader.Height))
header2 := ibctmtypes.CreateTestHeader(suite.chainA.ChainID, suite.chainA.CurrentHeader.Height, suite.chainA.CurrentHeader.Height-1, suite.chainA.CurrentHeader.Time.Add(time.Minute), suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) heightMinus1 := types.NewHeight(0, uint64(suite.chainA.CurrentHeader.Height)-1)
header1 := ibctmtypes.CreateTestHeader(suite.chainA.ChainID, height, heightMinus1, suite.chainA.CurrentHeader.Time, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers)
header2 := ibctmtypes.CreateTestHeader(suite.chainA.ChainID, height, heightMinus1, suite.chainA.CurrentHeader.Time.Add(time.Minute), suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers)
misbehaviour := ibctmtypes.NewMisbehaviour("tendermint", suite.chainA.ChainID, header1, header2) misbehaviour := ibctmtypes.NewMisbehaviour("tendermint", suite.chainA.ChainID, header1, header2)
msg, err = types.NewMsgSubmitMisbehaviour("tendermint", misbehaviour, suite.chainA.SenderAccount.GetAddress()) msg, err = types.NewMsgSubmitMisbehaviour("tendermint", misbehaviour, suite.chainA.SenderAccount.GetAddress())
@ -410,8 +412,10 @@ func (suite *TypesTestSuite) TestMsgSubmitMisbehaviour_ValidateBasic() {
{ {
"valid - tendermint misbehaviour", "valid - tendermint misbehaviour",
func() { func() {
header1 := ibctmtypes.CreateTestHeader(suite.chainA.ChainID, suite.chainA.CurrentHeader.Height, suite.chainA.CurrentHeader.Height-1, suite.chainA.CurrentHeader.Time, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) height := types.NewHeight(0, uint64(suite.chainA.CurrentHeader.Height))
header2 := ibctmtypes.CreateTestHeader(suite.chainA.ChainID, suite.chainA.CurrentHeader.Height, suite.chainA.CurrentHeader.Height-1, suite.chainA.CurrentHeader.Time.Add(time.Minute), suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) heightMinus1 := types.NewHeight(0, uint64(suite.chainA.CurrentHeader.Height)-1)
header1 := ibctmtypes.CreateTestHeader(suite.chainA.ChainID, height, heightMinus1, suite.chainA.CurrentHeader.Time, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers)
header2 := ibctmtypes.CreateTestHeader(suite.chainA.ChainID, height, heightMinus1, suite.chainA.CurrentHeader.Time.Add(time.Minute), suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers)
misbehaviour := ibctmtypes.NewMisbehaviour("tendermint", suite.chainA.ChainID, header1, header2) misbehaviour := ibctmtypes.NewMisbehaviour("tendermint", suite.chainA.ChainID, header1, header2)
msg, err = types.NewMsgSubmitMisbehaviour("tendermint", misbehaviour, suite.chainA.SenderAccount.GetAddress()) msg, err = types.NewMsgSubmitMisbehaviour("tendermint", misbehaviour, suite.chainA.SenderAccount.GetAddress())

View File

@ -51,7 +51,6 @@ func queryPacketCommitmentABCI(
return nil, sdkerrors.Wrapf(types.ErrPacketCommitmentNotFound, "portID (%s), channelID (%s), sequence (%d)", portID, channelID, sequence) return nil, sdkerrors.Wrapf(types.ErrPacketCommitmentNotFound, "portID (%s), channelID (%s), sequence (%d)", portID, channelID, sequence)
} }
// TODO: retrieve epoch number from chain-id
return types.NewQueryPacketCommitmentResponse(portID, channelID, sequence, value, proofBz, proofHeight), nil return types.NewQueryPacketCommitmentResponse(portID, channelID, sequence, value, proofBz, proofHeight), nil
} }
@ -230,6 +229,5 @@ func queryNextSequenceRecvABCI(clientCtx client.Context, portID, channelID strin
sequence := binary.BigEndian.Uint64(value) sequence := binary.BigEndian.Uint64(value)
// TODO: retrieve epoch number from chain-id
return types.NewQueryNextSequenceReceiveResponse(portID, channelID, sequence, proofBz, proofHeight), nil return types.NewQueryNextSequenceReceiveResponse(portID, channelID, sequence, proofBz, proofHeight), nil
} }

View File

@ -30,16 +30,12 @@ func (h Header) ConsensusState() *ConsensusState {
// GetHeight returns the current height. It returns 0 if the tendermint // GetHeight returns the current height. It returns 0 if the tendermint
// header is nil. // header is nil.
//
// TODO: return clienttypes.Height once interface changes
func (h Header) GetHeight() exported.Height { func (h Header) GetHeight() exported.Height {
if h.Header == nil { if h.Header == nil {
return clienttypes.ZeroHeight() return clienttypes.ZeroHeight()
} }
epoch := clienttypes.ParseChainID(h.Header.ChainID)
// Enforce clienttypes.Height to use 0 epoch number return clienttypes.NewHeight(epoch, uint64(h.Header.Height))
// TODO: Retrieve epoch number from chain-id
return clienttypes.NewHeight(0, uint64(h.Header.Height))
} }
// GetTime returns the current block timestamp. It returns a zero time if // GetTime returns the current block timestamp. It returns a zero time if

View File

@ -62,7 +62,7 @@ func (cs ClientState) CheckMisbehaviourAndUpdateState(
infractionHeight := tmMisbehaviour.GetHeight().GetEpochHeight() infractionHeight := tmMisbehaviour.GetHeight().GetEpochHeight()
ageBlocks = int64(cs.LatestHeight.EpochHeight - infractionHeight) ageBlocks = int64(cs.LatestHeight.EpochHeight - infractionHeight)
} else { } else {
// if the misbehaviour is from a previous epoch, then the epoch-height // if the misbehaviour is from a different epoch, then the epoch-height
// of misbehaviour has no correlation with the current epoch-height // of misbehaviour has no correlation with the current epoch-height
// so we disable the block check by setting ageBlocks to 0 and only // so we disable the block check by setting ageBlocks to 0 and only
// rely on the time expiry check with ageDuration // rely on the time expiry check with ageDuration
@ -136,15 +136,22 @@ func checkMisbehaviourHeader(
if currentTimestamp.Sub(consState.Timestamp) >= clientState.UnbondingPeriod { if currentTimestamp.Sub(consState.Timestamp) >= clientState.UnbondingPeriod {
return sdkerrors.Wrapf( return sdkerrors.Wrapf(
ErrUnbondingPeriodExpired, ErrUnbondingPeriodExpired,
"current timestamp minus the latest consensus state timestamp is greater than or equal to the unbonding period (%s >= %s)", "current timestamp minus the latest consensus state timestamp is greater than or equal to the unbonding period (%d >= %d)",
currentTimestamp.Sub(consState.Timestamp), clientState.UnbondingPeriod, currentTimestamp.Sub(consState.Timestamp), clientState.UnbondingPeriod,
) )
} }
chainID := clientState.GetChainID()
// If chainID is in epoch format, then set epoch number of chainID with the epoch number
// of the misbehaviour header
if clienttypes.IsEpochFormat(chainID) {
chainID, _ = clienttypes.SetEpochNumber(chainID, header.GetHeight().GetEpochNumber())
}
// - 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 := tmTrustedValset.VerifyCommitLightTrusting( if err := tmTrustedValset.VerifyCommitLightTrusting(
clientState.GetChainID(), tmCommit, clientState.TrustLevel.ToTendermint(), chainID, tmCommit, clientState.TrustLevel.ToTendermint(),
); err != nil { ); err != nil {
return sdkerrors.Wrapf(clienttypes.ErrInvalidMisbehaviour, "validator set in header has too much change from trusted validator set: %v", err) return sdkerrors.Wrapf(clienttypes.ErrInvalidMisbehaviour, "validator set in header has too much change from trusted validator set: %v", err)
} }

View File

@ -58,8 +58,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
height, height,
&types.Misbehaviour{ &types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now, bothValSet, bothValSet, bothSigners), Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
ChainId: chainID, ChainId: chainID,
ClientId: chainID, ClientId: chainID,
}, },
@ -74,8 +74,88 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
heightMinus1, heightMinus1,
&types.Misbehaviour{ &types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight-1, suite.now, bothValSet, bothValSet, bothSigners), Header1: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight-1, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), Header2: types.CreateTestHeader(chainID, height, heightMinus1, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
ChainId: chainID,
ClientId: chainID,
},
suite.now,
true,
},
{
"valid misbehaviour with different trusted heights",
types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false),
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
heightMinus1,
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash),
heightMinus3,
&types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, height, heightMinus3, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners),
ChainId: chainID,
ClientId: chainID,
},
suite.now,
true,
},
{
"valid misbehaviour at a previous epoch",
types.NewClientState(chainIDEpoch1, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(1, 1), commitmenttypes.GetSDKSpecs(), false, false),
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
heightMinus1,
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash),
heightMinus3,
&types.Misbehaviour{
Header1: types.CreateTestHeader(chainIDEpoch0, height, heightMinus1, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainIDEpoch0, height, heightMinus3, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners),
ChainId: chainIDEpoch0,
ClientId: chainID,
},
suite.now,
true,
},
{
"valid misbehaviour at a future epoch",
types.NewClientState(chainIDEpoch0, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false),
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
heightMinus1,
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash),
heightMinus3,
&types.Misbehaviour{
Header1: types.CreateTestHeader(chainIDEpoch0, clienttypes.NewHeight(1, 3), heightMinus1, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainIDEpoch0, clienttypes.NewHeight(1, 3), heightMinus3, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners),
ChainId: chainIDEpoch0,
ClientId: chainID,
},
suite.now,
true,
},
{
"valid misbehaviour with trusted heights at a previous epoch",
types.NewClientState(chainIDEpoch1, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(1, 1), commitmenttypes.GetSDKSpecs(), false, false),
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
heightMinus1,
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash),
heightMinus3,
&types.Misbehaviour{
Header1: types.CreateTestHeader(chainIDEpoch1, clienttypes.NewHeight(1, 1), heightMinus1, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainIDEpoch1, clienttypes.NewHeight(1, 1), heightMinus3, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners),
ChainId: chainIDEpoch1,
ClientId: chainID,
},
suite.now,
true,
},
{
"consensus state's valset hash different from misbehaviour should still pass",
types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false),
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash),
height,
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash),
height,
&types.Misbehaviour{
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,
}, },
@ -90,46 +170,14 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
height, height,
&types.Misbehaviour{ &types.Misbehaviour{
Header1: types.CreateTestHeader("ethermint", int64(height.EpochHeight), int64(height.EpochHeight), suite.now, bothValSet, bothValSet, bothSigners), Header1: types.CreateTestHeader("ethermint", height, height, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader("ethermint", int64(height.EpochHeight), int64(height.EpochHeight), suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), Header2: types.CreateTestHeader("ethermint", height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
ChainId: "ethermint", ChainId: "ethermint",
ClientId: chainID, ClientId: chainID,
}, },
suite.now, suite.now,
false, false,
}, },
{
"valid misbehavior misbehaviour with different trusted heights",
types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false),
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
heightMinus1,
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash),
heightMinus3,
&types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight-1, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight-3, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners),
ChainId: chainID,
ClientId: chainID,
},
suite.now,
true,
},
{
"consensus state's valset hash different from misbehaviour should still pass",
types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false),
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash),
height,
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash),
height,
&types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now, bothValSet, suite.valSet, bothSigners),
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners),
ChainId: chainID,
ClientId: chainID,
},
suite.now,
true,
},
{ {
"invalid misbehavior misbehaviour with trusted height different from trusted consensus state", "invalid misbehavior misbehaviour with trusted height different from trusted consensus state",
types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false), types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false),
@ -138,8 +186,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash),
heightMinus3, heightMinus3,
&types.Misbehaviour{ &types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight-1, suite.now, bothValSet, bothValSet, bothSigners), Header1: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now.Add(time.Minute), 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,
}, },
@ -154,8 +202,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash),
heightMinus3, heightMinus3,
&types.Misbehaviour{ &types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight-1, suite.now, bothValSet, bothValSet, bothSigners), Header1: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight-3, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), Header2: types.CreateTestHeader(chainID, height, heightMinus3, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
ChainId: chainID, ChainId: chainID,
ClientId: chainID, ClientId: chainID,
}, },
@ -170,8 +218,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
height, height,
&types.Misbehaviour{ &types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now, bothValSet, bothValSet, bothSigners), Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
ChainId: chainID, ChainId: chainID,
ClientId: chainID, ClientId: chainID,
}, },
@ -186,8 +234,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
height, height,
&types.Misbehaviour{ &types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight-1, suite.now, bothValSet, bothValSet, bothSigners), Header1: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
ChainId: chainID, ChainId: chainID,
ClientId: chainID, ClientId: chainID,
}, },
@ -213,8 +261,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
height, height,
&types.Misbehaviour{ &types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now, bothValSet, bothValSet, bothSigners), Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
ChainId: chainID, ChainId: chainID,
ClientId: chainID, ClientId: chainID,
}, },
@ -229,8 +277,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
height, height,
&types.Misbehaviour{ &types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now, bothValSet, bothValSet, bothSigners), Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
ChainId: chainID, ChainId: chainID,
ClientId: chainID, ClientId: chainID,
}, },
@ -245,8 +293,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
height, height,
&types.Misbehaviour{ &types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight-1, suite.now, bothValSet, bothValSet, bothSigners), Header1: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight-1, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), Header2: types.CreateTestHeader(chainID, height, heightMinus1, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
ChainId: chainID, ChainId: chainID,
ClientId: chainID, ClientId: chainID,
}, },
@ -261,8 +309,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
height, height,
&types.Misbehaviour{ &types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight-1, suite.now, bothValSet, bothValSet, bothSigners), Header1: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
ChainId: chainID, ChainId: chainID,
ClientId: chainID, ClientId: chainID,
}, },
@ -277,8 +325,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
height, height,
&types.Misbehaviour{ &types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now, bothValSet, suite.valSet, bothSigners), Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, suite.valSet, bothSigners),
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now.Add(time.Minute), 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,
}, },
@ -293,8 +341,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
height, height,
&types.Misbehaviour{ &types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now, altValSet, bothValSet, altSigners), Header1: types.CreateTestHeader(chainID, height, height, suite.now, altValSet, bothValSet, altSigners),
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
ChainId: chainID, ChainId: chainID,
ClientId: chainID, ClientId: chainID,
}, },
@ -309,8 +357,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
height, height,
&types.Misbehaviour{ &types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now, bothValSet, bothValSet, bothSigners), Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now.Add(time.Minute), altValSet, bothValSet, altSigners), Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), altValSet, bothValSet, altSigners),
ChainId: chainID, ChainId: chainID,
ClientId: chainID, ClientId: chainID,
}, },
@ -325,8 +373,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
height, height,
&types.Misbehaviour{ &types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now, altValSet, bothValSet, altSigners), Header1: types.CreateTestHeader(chainID, height, height, suite.now, altValSet, bothValSet, altSigners),
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now.Add(time.Minute), altValSet, bothValSet, 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

@ -7,6 +7,7 @@ import (
tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
tmtypes "github.com/tendermint/tendermint/types" tmtypes "github.com/tendermint/tendermint/types"
clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
"github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types" "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types"
"github.com/cosmos/cosmos-sdk/x/ibc/exported" "github.com/cosmos/cosmos-sdk/x/ibc/exported"
ibctestingmock "github.com/cosmos/cosmos-sdk/x/ibc/testing/mock" ibctestingmock "github.com/cosmos/cosmos-sdk/x/ibc/testing/mock"
@ -14,11 +15,11 @@ import (
func (suite *TendermintTestSuite) TestMisbehaviour() { func (suite *TendermintTestSuite) TestMisbehaviour() {
signers := []tmtypes.PrivValidator{suite.privVal} signers := []tmtypes.PrivValidator{suite.privVal}
epochHeight := int64(height.EpochHeight) heightMinus1 := clienttypes.NewHeight(0, height.EpochHeight-1)
misbehaviour := &types.Misbehaviour{ misbehaviour := &types.Misbehaviour{
Header1: suite.header, Header1: suite.header,
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight-1, suite.now, suite.valSet, suite.valSet, signers), Header2: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, suite.valSet, suite.valSet, signers),
ChainId: chainID, ChainId: chainID,
ClientId: clientID, ClientId: clientID,
} }
@ -50,6 +51,8 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() {
altSigners := []tmtypes.PrivValidator{altPrivVal} altSigners := []tmtypes.PrivValidator{altPrivVal}
heightMinus1 := clienttypes.NewHeight(0, height.EpochHeight-1)
testCases := []struct { testCases := []struct {
name string name string
misbehaviour *types.Misbehaviour misbehaviour *types.Misbehaviour
@ -60,7 +63,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() {
"valid misbehaviour", "valid misbehaviour",
&types.Misbehaviour{ &types.Misbehaviour{
Header1: suite.header, Header1: suite.header,
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight-1, suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers), Header2: types.CreateTestHeader(chainID, height, heightMinus1, suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers),
ChainId: chainID, ChainId: chainID,
ClientId: clientID, ClientId: clientID,
}, },
@ -83,7 +86,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() {
"valid misbehaviour with different trusted headers", "valid misbehaviour with different trusted headers",
&types.Misbehaviour{ &types.Misbehaviour{
Header1: suite.header, Header1: suite.header,
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight-3, suite.now.Add(time.Minute), suite.valSet, bothValSet, signers), Header2: types.CreateTestHeader(chainID, height, clienttypes.NewHeight(0, height.EpochHeight-3), suite.now.Add(time.Minute), suite.valSet, bothValSet, signers),
ChainId: chainID, ChainId: chainID,
ClientId: clientID, ClientId: clientID,
}, },
@ -93,7 +96,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() {
{ {
"trusted height is 0 in Header1", "trusted height is 0 in Header1",
&types.Misbehaviour{ &types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, 0, suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers), Header1: types.CreateTestHeader(chainID, height, clienttypes.ZeroHeight(), suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers),
Header2: suite.header, Header2: suite.header,
ChainId: chainID, ChainId: chainID,
ClientId: clientID, ClientId: clientID,
@ -105,7 +108,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() {
"trusted height is 0 in Header2", "trusted height is 0 in Header2",
&types.Misbehaviour{ &types.Misbehaviour{
Header1: suite.header, Header1: suite.header,
Header2: types.CreateTestHeader(chainID, epochHeight, 0, suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers), Header2: types.CreateTestHeader(chainID, height, clienttypes.ZeroHeight(), suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers),
ChainId: chainID, ChainId: chainID,
ClientId: clientID, ClientId: clientID,
}, },
@ -115,7 +118,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() {
{ {
"trusted valset is nil in Header1", "trusted valset is nil in Header1",
&types.Misbehaviour{ &types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight-1, suite.now.Add(time.Minute), suite.valSet, nil, signers), Header1: types.CreateTestHeader(chainID, height, heightMinus1, suite.now.Add(time.Minute), suite.valSet, nil, signers),
Header2: suite.header, Header2: suite.header,
ChainId: chainID, ChainId: chainID,
ClientId: clientID, ClientId: clientID,
@ -127,7 +130,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() {
"trusted valset is nil in Header2", "trusted valset is nil in Header2",
&types.Misbehaviour{ &types.Misbehaviour{
Header1: suite.header, Header1: suite.header,
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight-1, suite.now.Add(time.Minute), suite.valSet, nil, signers), Header2: types.CreateTestHeader(chainID, height, heightMinus1, suite.now.Add(time.Minute), suite.valSet, nil, signers),
ChainId: chainID, ChainId: chainID,
ClientId: clientID, ClientId: clientID,
}, },
@ -138,7 +141,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() {
"invalid client ID ", "invalid client ID ",
&types.Misbehaviour{ &types.Misbehaviour{
Header1: suite.header, Header1: suite.header,
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight-1, suite.now, suite.valSet, suite.valSet, signers), Header2: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, suite.valSet, suite.valSet, signers),
ChainId: chainID, ChainId: chainID,
ClientId: "GAIA", ClientId: "GAIA",
}, },
@ -149,7 +152,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() {
"wrong chainID on header1", "wrong chainID on header1",
&types.Misbehaviour{ &types.Misbehaviour{
Header1: suite.header, Header1: suite.header,
Header2: types.CreateTestHeader("ethermint", epochHeight, epochHeight-1, suite.now, suite.valSet, suite.valSet, signers), Header2: types.CreateTestHeader("ethermint", height, heightMinus1, suite.now, suite.valSet, suite.valSet, signers),
ChainId: "ethermint", ChainId: "ethermint",
ClientId: clientID, ClientId: clientID,
}, },
@ -160,7 +163,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() {
"wrong chainID on header2", "wrong chainID on header2",
&types.Misbehaviour{ &types.Misbehaviour{
Header1: suite.header, Header1: suite.header,
Header2: types.CreateTestHeader("ethermint", epochHeight, epochHeight-1, suite.now, suite.valSet, suite.valSet, signers), Header2: types.CreateTestHeader("ethermint", height, heightMinus1, suite.now, suite.valSet, suite.valSet, signers),
ChainId: chainID, ChainId: chainID,
ClientId: clientID, ClientId: clientID,
}, },
@ -171,7 +174,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() {
"wrong chainID in misbehaviour", "wrong chainID in misbehaviour",
&types.Misbehaviour{ &types.Misbehaviour{
Header1: suite.header, Header1: suite.header,
Header2: types.CreateTestHeader(chainID, int64(height.EpochHeight), int64(height.EpochHeight-1), suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers), Header2: types.CreateTestHeader(chainID, height, heightMinus1, suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers),
ChainId: "ethermint", ChainId: "ethermint",
ClientId: clientID, ClientId: clientID,
}, },
@ -182,7 +185,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() {
"mismatched heights", "mismatched heights",
&types.Misbehaviour{ &types.Misbehaviour{
Header1: suite.header, Header1: suite.header,
Header2: types.CreateTestHeader(chainID, 6, 4, suite.now, suite.valSet, suite.valSet, signers), Header2: types.CreateTestHeader(chainID, clienttypes.NewHeight(0, 6), clienttypes.NewHeight(0, 4), suite.now, suite.valSet, suite.valSet, signers),
ChainId: chainID, ChainId: chainID,
ClientId: clientID, ClientId: clientID,
}, },
@ -203,7 +206,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() {
{ {
"header 1 doesn't have 2/3 majority", "header 1 doesn't have 2/3 majority",
&types.Misbehaviour{ &types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight-1, suite.now, bothValSet, suite.valSet, bothSigners), Header1: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, bothValSet, suite.valSet, bothSigners),
Header2: suite.header, Header2: suite.header,
ChainId: chainID, ChainId: chainID,
ClientId: clientID, ClientId: clientID,
@ -226,7 +229,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() {
"header 2 doesn't have 2/3 majority", "header 2 doesn't have 2/3 majority",
&types.Misbehaviour{ &types.Misbehaviour{
Header1: suite.header, Header1: suite.header,
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight-1, suite.now, bothValSet, suite.valSet, bothSigners), Header2: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, bothValSet, suite.valSet, bothSigners),
ChainId: chainID, ChainId: chainID,
ClientId: clientID, ClientId: clientID,
}, },
@ -248,7 +251,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() {
"validators sign off on wrong commit", "validators sign off on wrong commit",
&types.Misbehaviour{ &types.Misbehaviour{
Header1: suite.header, Header1: suite.header,
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight-1, suite.now, bothValSet, suite.valSet, bothSigners), Header2: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, bothValSet, suite.valSet, bothSigners),
ChainId: chainID, ChainId: chainID,
ClientId: clientID, ClientId: clientID,
}, },

View File

@ -20,6 +20,8 @@ import (
const ( const (
chainID = "gaia" chainID = "gaia"
chainIDEpoch0 = "gaia-epoch-0"
chainIDEpoch1 = "gaia-epoch-1"
clientID = "gaiamainnet" clientID = "gaiamainnet"
trustingPeriod time.Duration = time.Hour * 24 * 7 * 2 trustingPeriod time.Duration = time.Hour * 24 * 7 * 2
ubdPeriod time.Duration = time.Hour * 24 * 7 * 3 ubdPeriod time.Duration = time.Hour * 24 * 7 * 3
@ -72,12 +74,12 @@ func (suite *TendermintTestSuite) SetupTest() {
pubKey, err := suite.privVal.GetPubKey() pubKey, err := suite.privVal.GetPubKey()
suite.Require().NoError(err) suite.Require().NoError(err)
epochHeight := int64(height.EpochHeight) heightMinus1 := clienttypes.NewHeight(0, height.EpochHeight-1)
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.valsHash = suite.valSet.Hash()
suite.header = ibctmtypes.CreateTestHeader(chainID, epochHeight, epochHeight-1, suite.now, suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) suite.header = ibctmtypes.CreateTestHeader(chainID, height, heightMinus1, suite.now, suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal})
suite.ctx = app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1, Time: suite.now}) suite.ctx = app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1, Time: suite.now})
} }

View File

@ -24,16 +24,17 @@ func MakeBlockID(hash []byte, partSetSize uint32, partSetHash []byte) tmtypes.Bl
} }
// CreateTestHeader creates a mock header for testing only. // CreateTestHeader creates a mock header for testing only.
func CreateTestHeader(chainID string, height, trustedHeight int64, timestamp time.Time, tmValSet, tmTrustedVals *tmtypes.ValidatorSet, signers []tmtypes.PrivValidator) *Header { func CreateTestHeader(chainID string, height, trustedHeight clienttypes.Height, timestamp time.Time, tmValSet, tmTrustedVals *tmtypes.ValidatorSet, signers []tmtypes.PrivValidator) *Header {
var ( var (
valSet *tmproto.ValidatorSet valSet *tmproto.ValidatorSet
trustedVals *tmproto.ValidatorSet trustedVals *tmproto.ValidatorSet
) )
vsetHash := tmValSet.Hash() vsetHash := tmValSet.Hash()
blockHeight := int64(height.EpochHeight)
tmHeader := tmtypes.Header{ tmHeader := tmtypes.Header{
Version: version.Consensus{Block: 2, App: 2}, Version: version.Consensus{Block: 2, App: 2},
ChainID: chainID, ChainID: chainID,
Height: height, Height: blockHeight,
Time: timestamp, Time: timestamp,
LastBlockID: MakeBlockID(make([]byte, tmhash.Size), 10_000, make([]byte, tmhash.Size)), LastBlockID: MakeBlockID(make([]byte, tmhash.Size), 10_000, make([]byte, tmhash.Size)),
LastCommitHash: tmhash.Sum([]byte("last_commit_hash")), LastCommitHash: tmhash.Sum([]byte("last_commit_hash")),
@ -49,8 +50,8 @@ func CreateTestHeader(chainID string, height, trustedHeight int64, timestamp tim
hhash := tmHeader.Hash() hhash := tmHeader.Hash()
blockID := MakeBlockID(hhash, 3, tmhash.Sum([]byte("part_set"))) blockID := MakeBlockID(hhash, 3, tmhash.Sum([]byte("part_set")))
voteSet := tmtypes.NewVoteSet(chainID, height, 1, tmproto.PrecommitType, tmValSet) voteSet := tmtypes.NewVoteSet(chainID, blockHeight, 1, tmproto.PrecommitType, tmValSet)
commit, err := tmtypes.MakeCommit(blockID, height, 1, voteSet, signers, timestamp) commit, err := tmtypes.MakeCommit(blockID, blockHeight, 1, voteSet, signers, timestamp)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -77,7 +78,7 @@ func CreateTestHeader(chainID string, height, trustedHeight int64, timestamp tim
return &Header{ return &Header{
SignedHeader: &signedHeader, SignedHeader: &signedHeader,
ValidatorSet: valSet, ValidatorSet: valSet,
TrustedHeight: clienttypes.NewHeight(0, uint64(trustedHeight)), TrustedHeight: trustedHeight,
TrustedValidators: trustedVals, TrustedValidators: trustedVals,
} }
} }

View File

@ -21,7 +21,8 @@ import (
// It returns an error if: // It returns an error if:
// - the client or header provided are not parseable to tendermint types // - the client or header provided are not parseable to tendermint types
// - the header is invalid // - the header is invalid
// - header height is less than or equal to the consensus state height // - header height is less than or equal to the trusted header height
// - header epoch is not equal to trusted header epoch
// - header valset commit verification fails // - header valset commit verification fails
// - header timestamp is past the trusting period in relation to the consensus state // - header timestamp is past the trusting period in relation to the consensus state
// - header timestamp is less than or equal to the consensus state timestamp // - header timestamp is less than or equal to the consensus state timestamp
@ -32,6 +33,8 @@ import (
// If we are updating to a past height, a consensus state is created for that height to be persisted in client store // If we are updating to a past height, a consensus state is created for that height to be persisted in client store
// If we are updating to a future height, the consensus state is created and the client state is updated to reflect // If we are updating to a future height, the consensus state is created and the client state is updated to reflect
// the new latest height // the new latest height
// UpdateClient must only be used to update within a single epoch, thus header epoch number and trusted height's epoch
// number must be the same. To update to a new epoch, use a separate upgrade path
// Tendermint client validity checking uses the bisection algorithm described // Tendermint client validity checking uses the bisection algorithm described
// in the [Tendermint spec](https://github.com/tendermint/spec/blob/master/spec/consensus/light-client.md). // in the [Tendermint spec](https://github.com/tendermint/spec/blob/master/spec/consensus/light-client.md).
func (cs ClientState) CheckHeaderAndUpdateState( func (cs ClientState) CheckHeaderAndUpdateState(
@ -91,6 +94,16 @@ func checkValidity(
return err return err
} }
// UpdateClient only accepts updates with a header at the same epoch
// as the trusted consensus state
if header.GetHeight().GetEpochNumber() != header.TrustedHeight.EpochNumber {
return sdkerrors.Wrapf(
ErrInvalidHeaderHeight,
"header height epoch %d does not match trusted header epoch %d",
header.GetHeight().GetEpochNumber(), header.TrustedHeight.EpochNumber,
)
}
tmTrustedValidators, err := tmtypes.ValidatorSetFromProto(header.TrustedValidators) tmTrustedValidators, err := tmtypes.ValidatorSetFromProto(header.TrustedValidators)
if err != nil { if err != nil {
return sdkerrors.Wrap(err, "trusted validator set in not tendermint validator set type") return sdkerrors.Wrap(err, "trusted validator set in not tendermint validator set type")
@ -110,7 +123,7 @@ func checkValidity(
if header.GetHeight().LTE(header.TrustedHeight) { if header.GetHeight().LTE(header.TrustedHeight) {
return sdkerrors.Wrapf( return sdkerrors.Wrapf(
clienttypes.ErrInvalidHeader, clienttypes.ErrInvalidHeader,
"header height ≤ consensus state height (%d ≤ %d)", header.GetHeight(), header.TrustedHeight, "header height ≤ consensus state height (%s ≤ %s)", header.GetHeight(), header.TrustedHeight,
) )
} }
@ -125,13 +138,24 @@ func checkValidity(
Header: &trustedHeader, Header: &trustedHeader,
} }
chainID := clientState.GetChainID()
// If chainID is in epoch format, then set epoch number of chainID with the epoch number
// of the header we are verifying
// This is useful if the update is at a previous epoch rather than an update to the latest epoch
// of the client.
// The chainID must be set correctly for the previous epoch before attempting verification.
// Updates for previous epochs are not supported if the chainID is not in epoch format.
if clienttypes.IsEpochFormat(chainID) {
chainID, _ = clienttypes.SetEpochNumber(chainID, header.GetHeight().GetEpochNumber())
}
// Verify next header with the passed-in trustedVals // Verify next header with the passed-in trustedVals
// - asserts trusting period not passed // - asserts trusting period not passed
// - assert header timestamp is not past the trusting period // - assert header timestamp is not past the trusting period
// - assert header timestamp is past latest stored consensus state timestamp // - assert header timestamp is past latest stored consensus state timestamp
// - assert that a TrustLevel proportion of TrustedValidators signed new Commit // - assert that a TrustLevel proportion of TrustedValidators signed new Commit
err = light.Verify( err = light.Verify(
clientState.GetChainID(), &signedHeader, chainID, &signedHeader,
tmTrustedValidators, tmSignedHeader, tmValidatorSet, tmTrustedValidators, tmSignedHeader, tmValidatorSet,
clientState.TrustingPeriod, currentTimestamp, clientState.MaxClockDrift, clientState.TrustLevel.ToTendermint(), clientState.TrustingPeriod, currentTimestamp, clientState.MaxClockDrift, clientState.TrustLevel.ToTendermint(),
) )

View File

@ -26,6 +26,10 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
suite.Require().NoError(err) suite.Require().NoError(err)
epochHeight := int64(height.EpochHeight) epochHeight := int64(height.EpochHeight)
// create modified heights to use for test-cases
heightPlus1 := clienttypes.NewHeight(height.EpochNumber, height.EpochHeight+1)
heightMinus1 := clienttypes.NewHeight(height.EpochNumber, height.EpochHeight-1)
heightMinus3 := clienttypes.NewHeight(height.EpochNumber, height.EpochHeight-3) heightMinus3 := clienttypes.NewHeight(height.EpochNumber, height.EpochHeight-3)
heightPlus5 := clienttypes.NewHeight(height.EpochNumber, height.EpochHeight+5) heightPlus5 := clienttypes.NewHeight(height.EpochNumber, height.EpochHeight+5)
@ -54,7 +58,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
setup: func() { setup: func() {
clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false) clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false)
consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash)
newHeader = types.CreateTestHeader(chainID, epochHeight+1, epochHeight, suite.headerTime, suite.valSet, suite.valSet, signers) newHeader = types.CreateTestHeader(chainID, heightPlus1, height, suite.headerTime, suite.valSet, suite.valSet, signers)
currentTime = suite.now currentTime = suite.now
}, },
expPass: true, expPass: true,
@ -64,7 +68,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
setup: func() { setup: func() {
clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false) clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false)
consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash)
newHeader = types.CreateTestHeader(chainID, epochHeight+5, epochHeight, suite.headerTime, bothValSet, suite.valSet, bothSigners) newHeader = types.CreateTestHeader(chainID, heightPlus5, height, suite.headerTime, bothValSet, suite.valSet, bothSigners)
currentTime = suite.now currentTime = suite.now
}, },
expPass: true, expPass: true,
@ -74,7 +78,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
setup: func() { setup: func() {
clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false) clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false)
consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), bothValSet.Hash()) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), bothValSet.Hash())
newHeader = types.CreateTestHeader(chainID, epochHeight+1, epochHeight, suite.headerTime, bothValSet, bothValSet, bothSigners) newHeader = types.CreateTestHeader(chainID, heightPlus1, height, suite.headerTime, bothValSet, bothValSet, bothSigners)
currentTime = suite.now currentTime = suite.now
}, },
expPass: true, expPass: true,
@ -85,7 +89,17 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false) clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false)
consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash)
consStateHeight = heightMinus3 consStateHeight = heightMinus3
newHeader = types.CreateTestHeader(chainID, epochHeight-1, epochHeight-3, suite.headerTime, bothValSet, suite.valSet, bothSigners) newHeader = types.CreateTestHeader(chainID, heightMinus1, heightMinus3, suite.headerTime, bothValSet, suite.valSet, bothSigners)
currentTime = suite.now
},
expPass: true,
},
{
name: "successful update for a previous epoch",
setup: func() {
clientState = types.NewClientState(chainIDEpoch1, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false)
consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash)
newHeader = types.CreateTestHeader(chainIDEpoch0, height, heightMinus3, suite.headerTime, bothValSet, suite.valSet, bothSigners)
currentTime = suite.now currentTime = suite.now
}, },
expPass: true, expPass: true,
@ -95,7 +109,27 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
setup: func() { setup: func() {
clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false) clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false)
consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash)
newHeader = types.CreateTestHeader("ethermint", int64(height.EpochHeight+1), int64(height.EpochHeight), suite.headerTime, suite.valSet, suite.valSet, signers) newHeader = types.CreateTestHeader("ethermint", heightPlus1, height, suite.headerTime, suite.valSet, suite.valSet, signers)
currentTime = suite.now
},
expPass: false,
},
{
name: "unsuccessful update to a future epoch",
setup: func() {
clientState = types.NewClientState(chainIDEpoch0, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false)
consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash)
newHeader = types.CreateTestHeader(chainIDEpoch1, clienttypes.NewHeight(1, 1), height, suite.headerTime, suite.valSet, suite.valSet, signers)
currentTime = suite.now
},
expPass: false,
},
{
name: "unsuccessful update: header height epoch and trusted height epoch mismatch",
setup: func() {
clientState = types.NewClientState(chainIDEpoch1, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(1, 1), commitmenttypes.GetSDKSpecs(), false, false)
consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash)
newHeader = types.CreateTestHeader(chainIDEpoch1, clienttypes.NewHeight(1, 3), height, suite.headerTime, suite.valSet, suite.valSet, signers)
currentTime = suite.now currentTime = suite.now
}, },
expPass: false, expPass: false,
@ -105,7 +139,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
setup: func() { setup: func() {
clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false) clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false)
consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash)
newHeader = types.CreateTestHeader(chainID, epochHeight+1, epochHeight, suite.headerTime, bothValSet, suite.valSet, bothSigners) newHeader = types.CreateTestHeader(chainID, heightPlus1, height, suite.headerTime, bothValSet, suite.valSet, bothSigners)
currentTime = suite.now currentTime = suite.now
}, },
expPass: false, expPass: false,
@ -115,7 +149,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
setup: func() { setup: func() {
clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false) clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false)
consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), bothValSet.Hash()) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), bothValSet.Hash())
newHeader = types.CreateTestHeader(chainID, epochHeight+1, epochHeight, suite.headerTime, suite.valSet, bothValSet, signers) newHeader = types.CreateTestHeader(chainID, heightPlus1, height, suite.headerTime, suite.valSet, bothValSet, signers)
currentTime = suite.now currentTime = suite.now
}, },
expPass: false, expPass: false,
@ -125,7 +159,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
setup: func() { setup: func() {
clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false) clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false)
consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash)
newHeader = types.CreateTestHeader(chainID, epochHeight+5, epochHeight, suite.headerTime, altValSet, suite.valSet, altSigners) newHeader = types.CreateTestHeader(chainID, heightPlus5, height, suite.headerTime, altValSet, suite.valSet, altSigners)
currentTime = suite.now currentTime = suite.now
}, },
expPass: false, expPass: false,
@ -135,7 +169,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
setup: func() { setup: func() {
clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false) clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false)
consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash)
newHeader = types.CreateTestHeader(chainID, epochHeight+5, epochHeight, suite.headerTime, bothValSet, bothValSet, bothSigners) newHeader = types.CreateTestHeader(chainID, heightPlus5, height, suite.headerTime, bothValSet, bothValSet, bothSigners)
currentTime = suite.now currentTime = suite.now
}, },
expPass: false, expPass: false,
@ -145,7 +179,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
setup: func() { setup: func() {
clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false) clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false)
consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash)
newHeader = types.CreateTestHeader(chainID, epochHeight+1, epochHeight, suite.headerTime, suite.valSet, suite.valSet, signers) newHeader = types.CreateTestHeader(chainID, heightPlus1, 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)
}, },
@ -156,7 +190,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
setup: func() { setup: func() {
clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false) clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false)
consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash)
newHeader = types.CreateTestHeader(chainID, epochHeight+1, epochHeight, suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers) newHeader = types.CreateTestHeader(chainID, heightPlus1, height, suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers)
currentTime = suite.now currentTime = suite.now
}, },
expPass: false, expPass: false,
@ -166,7 +200,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
setup: func() { setup: func() {
clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false) clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false)
consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash)
newHeader = types.CreateTestHeader(chainID, epochHeight+1, epochHeight, suite.clientTime, suite.valSet, suite.valSet, signers) newHeader = types.CreateTestHeader(chainID, heightPlus1, height, suite.clientTime, suite.valSet, suite.valSet, signers)
currentTime = suite.now currentTime = suite.now
}, },
expPass: false, expPass: false,
@ -176,7 +210,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
setup: func() { setup: func() {
clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false) clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), false, false)
consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash)
newHeader = types.CreateTestHeader(chainID, epochHeight+1, epochHeight, suite.headerTime, suite.valSet, suite.valSet, signers) newHeader = types.CreateTestHeader(chainID, heightPlus1, 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 = epochHeight - 1 newHeader.SignedHeader.Commit.Height = epochHeight - 1
currentTime = suite.now currentTime = suite.now
@ -189,7 +223,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, heightPlus5, commitmenttypes.GetSDKSpecs(), false, false) clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, heightPlus5, commitmenttypes.GetSDKSpecs(), false, false)
consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), 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, epochHeight-1, epochHeight, suite.headerTime, suite.valSet, suite.valSet, signers) newHeader = types.CreateTestHeader(chainID, heightMinus1, height, suite.headerTime, suite.valSet, suite.valSet, signers)
currentTime = suite.now currentTime = suite.now
}, },
expPass: false, expPass: false,

View File

@ -75,9 +75,8 @@ func (cs *ClientState) CheckHeaderAndUpdateState(
) (exported.ClientState, exported.ConsensusState, error) { ) (exported.ClientState, exported.ConsensusState, error) {
// use the chain ID from context since the localhost client is from the running chain (i.e self). // use the chain ID from context since the localhost client is from the running chain (i.e self).
cs.ChainId = ctx.ChainID() cs.ChainId = ctx.ChainID()
// Hardcode 0 for epoch number for now epoch := clienttypes.ParseChainID(cs.ChainId)
// TODO: Retrieve epoch number from chain-id cs.Height = clienttypes.NewHeight(epoch, uint64(ctx.BlockHeight()))
cs.Height = clienttypes.NewHeight(0, uint64(ctx.BlockHeight()))
return cs, nil, nil return cs, nil, nil
} }

View File

@ -40,6 +40,6 @@ func QueryTendermintProof(clientCtx client.Context, key []byte) ([]byte, []byte,
return nil, nil, clienttypes.Height{}, err return nil, nil, clienttypes.Height{}, err
} }
// TODO: retrieve epoch number from chain-id epoch := clienttypes.ParseChainID(clientCtx.ChainID)
return res.Value, proofBz, clienttypes.NewHeight(0, uint64(res.Height)+1), nil return res.Value, proofBz, clienttypes.NewHeight(epoch, uint64(res.Height)+1), nil
} }

View File

@ -63,7 +63,9 @@ 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, height-1, now, valSet, valSet, []tmtypes.PrivValidator{privVal}) clientHeightMinus1 := clienttypes.NewHeight(0, height-1)
suite.header = ibctmtypes.CreateTestHeader(chainID, clientHeight, clientHeightMinus1, now, valSet, valSet, []tmtypes.PrivValidator{privVal})
suite.ctx = suite.app.BaseApp.NewContext(isCheckTx, tmproto.Header{}) suite.ctx = suite.app.BaseApp.NewContext(isCheckTx, tmproto.Header{})
} }

View File

@ -38,7 +38,13 @@ given by the chain's chainID, and the epoch height given by the Tendermint block
and resets its block-height, it is responsible for updating its chain-id to increment the epoch number. and resets its block-height, it is responsible for updating its chain-id to increment the epoch number.
IBC Tendermint clients then verifies the epoch number against their `ChainId` and treat the `EpochHeight` as the Tendermint block-height. IBC Tendermint clients then verifies the epoch number against their `ChainId` and treat the `EpochHeight` as the Tendermint block-height.
TODO: Explain how to structure chain-id to make epoch-number parsable Tendermint chains wishing to use epochs to maintain persistent IBC connections even across height-resetting upgrades must format their chain-ids
in the following manner: `{chainID}-{version}`. On any height-resetting upgrade, the chainID **MUST** be updated with a higher epoch number
than the previous value.
Ex:
Before upgrade ChainID: `gaiamainnet-3`
After upgrade ChainID: `gaiamainnet-4`
Clients that do not require epochs, such as the solo-machine client, simply hardcode `0` into the epoch number whenever they Clients that do not require epochs, such as the solo-machine client, simply hardcode `0` into the epoch number whenever they
need to return an IBC height when implementing IBC interfaces and use the `EpochHeight` exclusively. need to return an IBC height when implementing IBC interfaces and use the `EpochHeight` exclusively.

View File

@ -181,10 +181,12 @@ func (chain *TestChain) QueryProof(key []byte) ([]byte, clienttypes.Height) {
proof, err := chain.App.AppCodec().MarshalBinaryBare(&merkleProof) proof, err := chain.App.AppCodec().MarshalBinaryBare(&merkleProof)
require.NoError(chain.t, err) require.NoError(chain.t, err)
epoch := clienttypes.ParseChainID(chain.ChainID)
// proof height + 1 is returned as the proof created corresponds to the height the proof // proof height + 1 is returned as the proof created corresponds to the height the proof
// was created in the IAVL tree. Tendermint and subsequently the clients that rely on it // was created in the IAVL tree. Tendermint and subsequently the clients that rely on it
// have heights 1 above the IAVL tree. Thus we return proof height + 1 // have heights 1 above the IAVL tree. Thus we return proof height + 1
return proof, clienttypes.NewHeight(0, uint64(res.Height)+1) return proof, clienttypes.NewHeight(epoch, uint64(res.Height)+1)
} }
// QueryClientStateProof performs and abci query for a client state // QueryClientStateProof performs and abci query for a client state