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
}
// TODO: retrieve epoch-number from chain-id
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"
// Hardcode 0 as epoch number for now
// TODO: Retrieve epoch from chain-id
epoch := types.ParseChainID(ctx.ChainID())
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)

View File

@ -7,6 +7,7 @@ import (
tmtypes "github.com/tendermint/tendermint/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"
localhosttypes "github.com/cosmos/cosmos-sdk/x/ibc/09-localhost/types"
commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types"
@ -60,11 +61,17 @@ func (suite *KeeperTestSuite) TestCreateClient() {
func (suite *KeeperTestSuite) TestUpdateClientTendermint() {
// Must create header creation functions since suite.header gets recreated on each test case
createFutureUpdateFn := func(s *KeeperTestSuite) *ibctmtypes.Header {
return ibctmtypes.CreateTestHeader(testChainID, 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})
}
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})
}
var (
@ -270,6 +277,7 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
altTime := suite.ctx.BlockTime().Add(time.Minute)
heightPlus3 := types.NewHeight(0, height+3)
heightPlus5 := types.NewHeight(0, height+5)
testCases := []struct {
name string
@ -280,8 +288,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
{
"trusting period misbehavior should pass",
&ibctmtypes.Misbehaviour{
Header1: ibctmtypes.CreateTestHeader(testChainID, height, height, altTime, bothValSet, bothValSet, bothSigners),
Header2: ibctmtypes.CreateTestHeader(testChainID, height, height, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners),
Header1: ibctmtypes.CreateTestHeader(testChainID, testClientHeight, testClientHeight, altTime, bothValSet, bothValSet, bothSigners),
Header2: ibctmtypes.CreateTestHeader(testChainID, testClientHeight, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners),
ChainId: testChainID,
ClientId: testClientID,
},
@ -297,8 +305,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
{
"misbehavior at later height should pass",
&ibctmtypes.Misbehaviour{
Header1: ibctmtypes.CreateTestHeader(testChainID, height+5, height, altTime, bothValSet, valSet, bothSigners),
Header2: ibctmtypes.CreateTestHeader(testChainID, height+5, height, suite.ctx.BlockTime(), bothValSet, valSet, bothSigners),
Header1: ibctmtypes.CreateTestHeader(testChainID, heightPlus5, testClientHeight, altTime, bothValSet, valSet, bothSigners),
Header2: ibctmtypes.CreateTestHeader(testChainID, heightPlus5, testClientHeight, suite.ctx.BlockTime(), bothValSet, valSet, bothSigners),
ChainId: testChainID,
ClientId: testClientID,
},
@ -324,8 +332,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
{
"misbehavior at later height with different trusted heights should pass",
&ibctmtypes.Misbehaviour{
Header1: ibctmtypes.CreateTestHeader(testChainID, height+5, height, altTime, bothValSet, valSet, bothSigners),
Header2: ibctmtypes.CreateTestHeader(testChainID, height+5, height+3, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners),
Header1: ibctmtypes.CreateTestHeader(testChainID, heightPlus5, testClientHeight, altTime, bothValSet, valSet, bothSigners),
Header2: ibctmtypes.CreateTestHeader(testChainID, heightPlus5, heightPlus3, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners),
ChainId: testChainID,
ClientId: testClientID,
},
@ -351,8 +359,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
{
"trusted ConsensusState1 not found",
&ibctmtypes.Misbehaviour{
Header1: ibctmtypes.CreateTestHeader(testChainID, height+5, height+3, altTime, bothValSet, bothValSet, bothSigners),
Header2: ibctmtypes.CreateTestHeader(testChainID, height+5, height, suite.ctx.BlockTime(), bothValSet, valSet, bothSigners),
Header1: ibctmtypes.CreateTestHeader(testChainID, heightPlus5, heightPlus3, altTime, bothValSet, bothValSet, bothSigners),
Header2: ibctmtypes.CreateTestHeader(testChainID, heightPlus5, testClientHeight, suite.ctx.BlockTime(), bothValSet, valSet, bothSigners),
ChainId: testChainID,
ClientId: testClientID,
},
@ -368,8 +376,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
{
"trusted ConsensusState2 not found",
&ibctmtypes.Misbehaviour{
Header1: ibctmtypes.CreateTestHeader(testChainID, height+5, height, altTime, bothValSet, valSet, bothSigners),
Header2: ibctmtypes.CreateTestHeader(testChainID, height+5, height+3, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners),
Header1: ibctmtypes.CreateTestHeader(testChainID, heightPlus5, testClientHeight, altTime, bothValSet, valSet, bothSigners),
Header2: ibctmtypes.CreateTestHeader(testChainID, heightPlus5, heightPlus3, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners),
ChainId: testChainID,
ClientId: testClientID,
},
@ -391,8 +399,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
{
"client already frozen at earlier height",
&ibctmtypes.Misbehaviour{
Header1: ibctmtypes.CreateTestHeader(testChainID, height, height, altTime, bothValSet, bothValSet, bothSigners),
Header2: ibctmtypes.CreateTestHeader(testChainID, height, height, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners),
Header1: ibctmtypes.CreateTestHeader(testChainID, testClientHeight, testClientHeight, altTime, bothValSet, bothValSet, bothSigners),
Header2: ibctmtypes.CreateTestHeader(testChainID, testClientHeight, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners),
ChainId: testChainID,
ClientId: testClientID,
},
@ -411,8 +419,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
{
"misbehaviour check failed",
&ibctmtypes.Misbehaviour{
Header1: ibctmtypes.CreateTestHeader(testChainID, height, height, altTime, bothValSet, bothValSet, bothSigners),
Header2: ibctmtypes.CreateTestHeader(testChainID, height, height, suite.ctx.BlockTime(), altValSet, bothValSet, altSigners),
Header1: ibctmtypes.CreateTestHeader(testChainID, testClientHeight, testClientHeight, altTime, bothValSet, bothValSet, bothSigners),
Header2: ibctmtypes.CreateTestHeader(testChainID, testClientHeight, testClientHeight, suite.ctx.BlockTime(), altValSet, bothValSet, altSigners),
ChainId: testChainID,
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
// 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) {
// TODO: check self chain-id against epoch number
selfHeight, ok := height.(types.Height)
if !ok {
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
}
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
// 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 {
tmClient, ok := clientState.(*ibctmtypes.ClientState)
if !ok {
@ -230,9 +232,15 @@ func (k Keeper) ValidateSelfClient(ctx sdk.Context, clientState exported.ClientS
ctx.ChainID(), tmClient.ChainId)
}
// For now, assume epoch number is zero
// TODO: Retrieve epoch number from chain-id
selfHeight := types.NewHeight(0, uint64(ctx.BlockHeight()))
epoch := types.ParseChainID(ctx.ChainID())
// 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) {
return sdkerrors.Wrapf(types.ErrInvalidClient, "client has LatestHeight %d greater than chain height %d",
tmClient.LatestHeight, ctx.BlockHeight())

View File

@ -26,7 +26,8 @@ import (
)
const (
testChainID = "gaiahub"
testChainID = "gaiahub-0"
testChainIDEpoch1 = "gaiahub-1"
testClientID = "gaiachain"
testClientID2 = "ethbridge"
@ -40,6 +41,7 @@ const (
)
var testClientHeight = types.NewHeight(0, 5)
var testClientHeightEpoch1 = types.NewHeight(1, 5)
type KeeperTestSuite struct {
suite.Suite
@ -83,10 +85,12 @@ func (suite *KeeperTestSuite) SetupTest() {
pubKey, err := suite.privVal.GetPubKey()
suite.Require().NoError(err)
testClientHeightMinus1 := types.NewHeight(0, height-1)
validator := tmtypes.NewValidator(pubKey, 1)
suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{validator})
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)
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),
false,
},
{
"invalid client epoch",
ibctmtypes.NewClientState(testChainIDEpoch1, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeightEpoch1, commitmenttypes.GetSDKSpecs(), false, false),
false,
},
{
"invalid proof specs",
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)
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})
// mock update functionality

View File

@ -53,7 +53,8 @@ func TestValidateGenesis(t *testing.T) {
val := tmtypes.NewValidator(pubKey, 10)
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 {
name string

View File

@ -2,6 +2,7 @@ package types
import (
"fmt"
"regexp"
"strconv"
"strings"
@ -12,6 +13,11 @@ import (
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.
func ZeroHeight() Height {
return Height{}
@ -35,7 +41,7 @@ func (h Height) GetEpochHeight() uint64 {
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
// -1 if a < b
// 0 if a = b
@ -143,8 +149,42 @@ func ParseHeight(heightStr string) (Height, error) {
return NewHeight(epochNumber, epochHeight), nil
}
// GetSelfHeight is a utility function that returns self height given context
// TODO: Retrieve epoch-number from chain-id
func GetSelfHeight(ctx sdk.Context) Height {
return NewHeight(0, uint64(ctx.BlockHeight()))
// SetEpochNumber takes a chainID in valid epoch format and swaps the epoch number
// in the chainID with the given epoch number.
func SetEpochNumber(chainID string, epoch uint64) (string, error) {
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")
})
}
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() {
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)
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)
height := types.NewHeight(0, uint64(suite.chainA.CurrentHeader.Height))
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)
msg, err = types.NewMsgSubmitMisbehaviour("tendermint", misbehaviour, suite.chainA.SenderAccount.GetAddress())
@ -410,8 +412,10 @@ func (suite *TypesTestSuite) TestMsgSubmitMisbehaviour_ValidateBasic() {
{
"valid - tendermint misbehaviour",
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)
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)
height := types.NewHeight(0, uint64(suite.chainA.CurrentHeader.Height))
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)
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)
}
// TODO: retrieve epoch number from chain-id
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)
// TODO: retrieve epoch number from chain-id
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
// header is nil.
//
// TODO: return clienttypes.Height once interface changes
func (h Header) GetHeight() exported.Height {
if h.Header == nil {
return clienttypes.ZeroHeight()
}
// Enforce clienttypes.Height to use 0 epoch number
// TODO: Retrieve epoch number from chain-id
return clienttypes.NewHeight(0, uint64(h.Header.Height))
epoch := clienttypes.ParseChainID(h.Header.ChainID)
return clienttypes.NewHeight(epoch, uint64(h.Header.Height))
}
// 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()
ageBlocks = int64(cs.LatestHeight.EpochHeight - infractionHeight)
} 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
// so we disable the block check by setting ageBlocks to 0 and only
// rely on the time expiry check with ageDuration
@ -136,15 +136,22 @@ func checkMisbehaviourHeader(
if currentTimestamp.Sub(consState.Timestamp) >= clientState.UnbondingPeriod {
return sdkerrors.Wrapf(
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,
)
}
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
// - ValidatorSets on both headers are valid given the last trusted ValidatorSet
if err := tmTrustedValset.VerifyCommitLightTrusting(
clientState.GetChainID(), tmCommit, clientState.TrustLevel.ToTendermint(),
chainID, tmCommit, clientState.TrustLevel.ToTendermint(),
); err != nil {
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),
height,
&types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
ChainId: chainID,
ClientId: chainID,
},
@ -74,8 +74,88 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
heightMinus1,
&types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight-1, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight-1, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
Header1: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, 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,
ClientId: chainID,
},
@ -90,46 +170,14 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
height,
&types.Misbehaviour{
Header1: types.CreateTestHeader("ethermint", int64(height.EpochHeight), int64(height.EpochHeight), suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader("ethermint", int64(height.EpochHeight), int64(height.EpochHeight), suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
Header1: types.CreateTestHeader("ethermint", height, height, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader("ethermint", height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
ChainId: "ethermint",
ClientId: chainID,
},
suite.now,
false,
},
{
"valid misbehavior misbehaviour with different trusted heights",
types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), 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",
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),
heightMinus3,
&types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight-1, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners),
Header1: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners),
ChainId: chainID,
ClientId: chainID,
},
@ -154,8 +202,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
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, bothValSet, bothSigners),
Header1: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, height, heightMinus3, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
ChainId: chainID,
ClientId: chainID,
},
@ -170,8 +218,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
height,
&types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
ChainId: chainID,
ClientId: chainID,
},
@ -186,8 +234,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
height,
&types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight-1, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
Header1: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
ChainId: chainID,
ClientId: chainID,
},
@ -213,8 +261,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
height,
&types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
ChainId: chainID,
ClientId: chainID,
},
@ -229,8 +277,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
height,
&types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
ChainId: chainID,
ClientId: chainID,
},
@ -245,8 +293,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
height,
&types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight-1, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight-1, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
Header1: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, height, heightMinus1, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
ChainId: chainID,
ClientId: chainID,
},
@ -261,8 +309,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
height,
&types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight-1, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
Header1: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
ChainId: chainID,
ClientId: chainID,
},
@ -277,8 +325,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
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),
Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, suite.valSet, bothSigners),
Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners),
ChainId: chainID,
ClientId: chainID,
},
@ -293,8 +341,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
height,
&types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now, altValSet, bothValSet, altSigners),
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
Header1: types.CreateTestHeader(chainID, height, height, suite.now, altValSet, bothValSet, altSigners),
Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners),
ChainId: chainID,
ClientId: chainID,
},
@ -309,8 +357,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
height,
&types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now.Add(time.Minute), altValSet, bothValSet, altSigners),
Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, bothValSet, bothSigners),
Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), altValSet, bothValSet, altSigners),
ChainId: chainID,
ClientId: chainID,
},
@ -325,8 +373,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() {
types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash),
height,
&types.Misbehaviour{
Header1: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now, altValSet, bothValSet, altSigners),
Header2: types.CreateTestHeader(chainID, epochHeight, epochHeight, suite.now.Add(time.Minute), altValSet, bothValSet, altSigners),
Header1: types.CreateTestHeader(chainID, height, height, suite.now, altValSet, bothValSet, altSigners),
Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), altValSet, bothValSet, altSigners),
ChainId: chainID,
ClientId: chainID,
},

View File

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

View File

@ -20,6 +20,8 @@ import (
const (
chainID = "gaia"
chainIDEpoch0 = "gaia-epoch-0"
chainIDEpoch1 = "gaia-epoch-1"
clientID = "gaiamainnet"
trustingPeriod time.Duration = time.Hour * 24 * 7 * 2
ubdPeriod time.Duration = time.Hour * 24 * 7 * 3
@ -72,12 +74,12 @@ func (suite *TendermintTestSuite) SetupTest() {
pubKey, err := suite.privVal.GetPubKey()
suite.Require().NoError(err)
epochHeight := int64(height.EpochHeight)
heightMinus1 := clienttypes.NewHeight(0, height.EpochHeight-1)
val := tmtypes.NewValidator(pubKey, 10)
suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{val})
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})
}

View File

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

View File

@ -21,7 +21,8 @@ import (
// It returns an error if:
// - the client or header provided are not parseable to tendermint types
// - 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 timestamp is past the trusting period in relation to the consensus state
// - 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 future height, the consensus state is created and the client state is updated to reflect
// 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
// in the [Tendermint spec](https://github.com/tendermint/spec/blob/master/spec/consensus/light-client.md).
func (cs ClientState) CheckHeaderAndUpdateState(
@ -91,6 +94,16 @@ func checkValidity(
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)
if err != nil {
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) {
return sdkerrors.Wrapf(
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,
}
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
// - asserts trusting period not passed
// - assert header timestamp is not past the trusting period
// - assert header timestamp is past latest stored consensus state timestamp
// - assert that a TrustLevel proportion of TrustedValidators signed new Commit
err = light.Verify(
clientState.GetChainID(), &signedHeader,
chainID, &signedHeader,
tmTrustedValidators, tmSignedHeader, tmValidatorSet,
clientState.TrustingPeriod, currentTimestamp, clientState.MaxClockDrift, clientState.TrustLevel.ToTendermint(),
)

View File

@ -26,6 +26,10 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
suite.Require().NoError(err)
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)
heightPlus5 := clienttypes.NewHeight(height.EpochNumber, height.EpochHeight+5)
@ -54,7 +58,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
setup: func() {
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)
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
},
expPass: true,
@ -64,7 +68,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
setup: func() {
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)
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
},
expPass: true,
@ -74,7 +78,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
setup: func() {
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())
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
},
expPass: true,
@ -85,7 +89,17 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
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)
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
},
expPass: true,
@ -95,7 +109,27 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
setup: func() {
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)
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
},
expPass: false,
@ -105,7 +139,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
setup: func() {
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)
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
},
expPass: false,
@ -115,7 +149,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
setup: func() {
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())
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
},
expPass: false,
@ -125,7 +159,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
setup: func() {
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)
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
},
expPass: false,
@ -135,7 +169,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
setup: func() {
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)
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
},
expPass: false,
@ -145,7 +179,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
setup: func() {
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)
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
currentTime = suite.now.Add(trustingPeriod)
},
@ -156,7 +190,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
setup: func() {
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)
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
},
expPass: false,
@ -166,7 +200,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
setup: func() {
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)
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
},
expPass: false,
@ -176,7 +210,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() {
setup: func() {
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)
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
newHeader.SignedHeader.Commit.Height = epochHeight - 1
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)
consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash)
// 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
},
expPass: false,

View File

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

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.
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
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)
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
// 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
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