Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ICS07 follow up changes #5606

Merged
merged 13 commits into from
Feb 18, 2020
5 changes: 3 additions & 2 deletions x/ibc/02-client/client/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,9 @@ func QueryNodeConsensusState(cliCtx context.CLIContext) (tendermint.ConsensusSta
}

state := tendermint.ConsensusState{
Root: commitment.NewRoot(commit.AppHash),
ValidatorSetHash: tmtypes.NewValidatorSet(validators.Validators).Hash(),
Timestamp: commit.Time,
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
Root: commitment.NewRoot(commit.AppHash),
ValidatorSet: tmtypes.NewValidatorSet(validators.Validators),
}

return state, height, nil
Expand Down
8 changes: 5 additions & 3 deletions x/ibc/02-client/keeper/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,11 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.H

switch clientType {
case exported.Tendermint:
clientState, consensusState, err = tendermint.CheckValidityAndUpdateState(clientState, header, ctx.ChainID())
clientState, consensusState, err = tendermint.CheckValidityAndUpdateState(
clientState, header, ctx.ChainID(), ctx.BlockTime(),
)
default:
return sdkerrors.Wrapf(types.ErrInvalidClientType, "cannot update client with ID %s", clientID)
err = types.ErrInvalidClientType
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
}

if err != nil {
Expand Down Expand Up @@ -122,7 +124,7 @@ func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, misbehaviour ex
switch e := misbehaviour.(type) {
case tendermint.Evidence:
clientState, err = tendermint.CheckMisbehaviourAndUpdateState(
clientState, consensusState, misbehaviour, uint64(misbehaviour.GetHeight()),
clientState, consensusState, misbehaviour, uint64(misbehaviour.GetHeight()), ctx.BlockTime(),
)

default:
Expand Down
33 changes: 16 additions & 17 deletions x/ibc/02-client/keeper/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,9 @@ func (suite *KeeperTestSuite) TestUpdateClient() {

if tc.expPass {
expConsensusState := tendermint.ConsensusState{
Root: commitment.NewRoot(suite.header.AppHash),
ValidatorSetHash: suite.header.ValidatorSet.Hash(),
Timestamp: suite.header.Time,
Root: commitment.NewRoot(suite.header.AppHash),
ValidatorSet: suite.header.ValidatorSet,
}

clientState, found := suite.keeper.GetClientState(suite.ctx, testClientID)
Expand Down Expand Up @@ -153,14 +154,13 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
{
"trusting period misbehavior should pass",
tendermint.Evidence{
Header1: tendermint.CreateTestHeader(testClientID, testClientHeight, bothValSet, suite.valSet, bothSigners),
Header2: tendermint.CreateTestHeader(testClientID, testClientHeight, bothValSet, bothValSet, bothSigners),
FromValidatorSet: bothValSet,
ChainID: testClientID,
ClientID: testClientID,
Header1: tendermint.CreateTestHeader(testClientID, testClientHeight, suite.ctx.BlockTime(), bothValSet, suite.valSet, bothSigners),
Header2: tendermint.CreateTestHeader(testClientID, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners),
ChainID: testClientID,
ClientID: testClientID,
},
func() error {
suite.consensusState.ValidatorSetHash = bothValSet.Hash()
suite.consensusState.ValidatorSet = bothValSet
_, err := suite.keeper.CreateClient(suite.ctx, testClientID, exported.Tendermint, suite.consensusState)
return err
},
Expand All @@ -175,8 +175,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
{
"consensus state not found",
tendermint.Evidence{
Header1: tendermint.CreateTestHeader(testClientID, testClientHeight, bothValSet, suite.valSet, bothSigners),
Header2: tendermint.CreateTestHeader(testClientID, testClientHeight, bothValSet, bothValSet, bothSigners),
Header1: tendermint.CreateTestHeader(testClientID, testClientHeight, suite.ctx.BlockTime(), bothValSet, suite.valSet, bothSigners),
Header2: tendermint.CreateTestHeader(testClientID, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners),
ChainID: testClientID,
ClientID: testClientID,
},
Expand All @@ -190,8 +190,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
{
"consensus state not found",
tendermint.Evidence{
Header1: tendermint.CreateTestHeader(testClientID, testClientHeight, bothValSet, suite.valSet, bothSigners),
Header2: tendermint.CreateTestHeader(testClientID, testClientHeight, bothValSet, bothValSet, bothSigners),
Header1: tendermint.CreateTestHeader(testClientID, testClientHeight, suite.ctx.BlockTime(), bothValSet, suite.valSet, bothSigners),
Header2: tendermint.CreateTestHeader(testClientID, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners),
ChainID: testClientID,
ClientID: testClientID,
},
Expand All @@ -205,11 +205,10 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
{
"misbehaviour check failed",
tendermint.Evidence{
Header1: tendermint.CreateTestHeader(testClientID, testClientHeight, bothValSet, bothValSet, bothSigners),
Header2: tendermint.CreateTestHeader(testClientID, testClientHeight, altValSet, bothValSet, altSigners),
FromValidatorSet: bothValSet,
ChainID: testClientID,
ClientID: testClientID,
Header1: tendermint.CreateTestHeader(testClientID, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners),
Header2: tendermint.CreateTestHeader(testClientID, testClientHeight, suite.ctx.BlockTime(), altValSet, bothValSet, altSigners),
ChainID: testClientID,
ClientID: testClientID,
},
func() error {
_, err := suite.keeper.CreateClient(suite.ctx, testClientID, exported.Tendermint, suite.consensusState)
Expand Down
28 changes: 23 additions & 5 deletions x/ibc/02-client/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package keeper

import (
"fmt"
"time"

"github.com/tendermint/tendermint/libs/log"
tmmath "github.com/tendermint/tendermint/libs/math"
tmtypes "github.com/tendermint/tendermint/types"

"github.com/cosmos/cosmos-sdk/codec"
Expand All @@ -15,6 +17,10 @@ import (
ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types"
)

// DefaultTrustingPeriodConstant defines the default constant for the client
// initialization trusting period
var defaultTrustingPeriodConstant tmmath.Fraction = tmmath.Fraction{Numerator: 2, Denominator: 3}

// Keeper represents a type that grants read and write permissions to any client
// state information
type Keeper struct {
Expand Down Expand Up @@ -104,8 +110,9 @@ func (k Keeper) GetSelfConsensusState(ctx sdk.Context, height uint64) (exported.
}

consensusState := tendermint.ConsensusState{
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
Root: commitment.NewRoot(histInfo.Header.AppHash),
ValidatorSetHash: tmtypes.NewValidatorSet(histInfo.ValSet.ToTmValidators()).Hash(),
Timestamp: ctx.BlockTime(),
Root: commitment.NewRoot(histInfo.Header.AppHash),
ValidatorSet: tmtypes.NewValidatorSet(histInfo.ValSet.ToTmValidators()),
}
return consensusState, true
}
Expand Down Expand Up @@ -143,14 +150,25 @@ func (k Keeper) initialize(
ctx sdk.Context, clientID string, clientType exported.ClientType,
consensusState exported.ConsensusState,
) (exported.ClientState, error) {
var clientState exported.ClientState
height := uint64(ctx.BlockHeight())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should not be using our own chain's BlockHeight here. Should be using the other chain's height.

Fixed this on my branch by adding Height field and GetHeight method to ConsensusState


var (
clientState exported.ClientState
err error
)
switch clientType {
case exported.Tendermint:
clientState = tendermint.NewClientState(clientID, height)
unbondingPeriod := k.stakingKeeper.UnbondingTime(ctx)
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
trustingPeriod := time.Duration(defaultTrustingPeriodConstant.Numerator) * unbondingPeriod / time.Duration(defaultTrustingPeriodConstant.Denominator)
clientState, err = tendermint.Initialize(
clientID, consensusState, trustingPeriod, unbondingPeriod, height,
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
)
default:
return nil, types.ErrInvalidClientType
err = types.ErrInvalidClientType
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
}

if err != nil {
return nil, err
}

k.SetClientConsensusState(ctx, clientID, height, consensusState)
Expand Down
32 changes: 20 additions & 12 deletions x/ibc/02-client/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package keeper_test
import (
"math/rand"
"testing"
"time"

"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"

abci "github.com/tendermint/tendermint/abci/types"
Expand All @@ -26,6 +26,9 @@ const (
testClientID3 = "ethermint"

testClientHeight = 5

trustingPeriod time.Duration = time.Hour * 24 * 7 * 2
ubdPeriod time.Duration = time.Hour * 24 * 7 * 3
)

type KeeperTestSuite struct {
Expand All @@ -38,22 +41,26 @@ type KeeperTestSuite struct {
header tendermint.Header
valSet *tmtypes.ValidatorSet
privVal tmtypes.PrivValidator
now time.Time
}

func (suite *KeeperTestSuite) SetupTest() {
isCheckTx := false
suite.now = time.Date(2020, 1, 2, 0, 0, 0, 0, time.UTC)
now2 := suite.now.Add(time.Duration(time.Hour * 1))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unnecessary conversion (from unconvert)

app := simapp.Setup(isCheckTx)

suite.cdc = app.Codec()
suite.ctx = app.BaseApp.NewContext(isCheckTx, abci.Header{Height: testClientHeight, ChainID: testClientID})
suite.ctx = app.BaseApp.NewContext(isCheckTx, abci.Header{Height: testClientHeight, ChainID: testClientID, Time: now2})
suite.keeper = &app.IBCKeeper.ClientKeeper
suite.privVal = tmtypes.NewMockPV()
validator := tmtypes.NewValidator(suite.privVal.GetPubKey(), 1)
suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{validator})
suite.header = tendermint.CreateTestHeader(testClientID, testClientHeight, suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal})
suite.header = tendermint.CreateTestHeader(testClientID, testClientHeight+2, now2, suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal})
suite.consensusState = tendermint.ConsensusState{
Root: commitment.NewRoot([]byte("hash")),
ValidatorSetHash: suite.valSet.Hash(),
Timestamp: suite.now,
Root: commitment.NewRoot([]byte("hash")),
ValidatorSet: suite.valSet,
}

var validators staking.Validators
Expand All @@ -74,7 +81,7 @@ func TestKeeperTestSuite(t *testing.T) {
}

func (suite *KeeperTestSuite) TestSetClientState() {
clientState := tendermint.NewClientState(testClientID, testClientHeight)
clientState := tendermint.NewClientState(testClientID, trustingPeriod, ubdPeriod, testClientHeight, suite.now)
suite.keeper.SetClientState(suite.ctx, clientState)

retrievedState, found := suite.keeper.GetClientState(suite.ctx, testClientID)
Expand All @@ -94,17 +101,18 @@ func (suite *KeeperTestSuite) TestSetClientConsensusState() {
suite.keeper.SetClientConsensusState(suite.ctx, testClientID, testClientHeight, suite.consensusState)

retrievedConsState, found := suite.keeper.GetClientConsensusState(suite.ctx, testClientID, testClientHeight)

suite.Require().True(found, "GetConsensusState failed")
tmConsState, _ := retrievedConsState.(tendermint.ConsensusState)
require.Equal(suite.T(), suite.consensusState, tmConsState, "ConsensusState not stored correctly")

tmConsState, ok := retrievedConsState.(tendermint.ConsensusState)
suite.Require().True(ok)
suite.Require().Equal(suite.consensusState, tmConsState, "ConsensusState not stored correctly")
}

func (suite KeeperTestSuite) TestGetAllClients() {
expClients := []exported.ClientState{
tendermint.NewClientState(testClientID2, testClientHeight),
tendermint.NewClientState(testClientID3, testClientHeight),
tendermint.NewClientState(testClientID, testClientHeight),
tendermint.NewClientState(testClientID2, trustingPeriod, ubdPeriod, testClientHeight, suite.now),
tendermint.NewClientState(testClientID3, trustingPeriod, ubdPeriod, testClientHeight, suite.now),
tendermint.NewClientState(testClientID, trustingPeriod, ubdPeriod, testClientHeight, suite.now),
}

for i := range expClients {
Expand Down
3 changes: 3 additions & 0 deletions x/ibc/02-client/types/expected_keepers.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package types

import (
"time"

sdk "github.com/cosmos/cosmos-sdk/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)

// StakingKeeper expected staking keeper
type StakingKeeper interface {
GetHistoricalInfo(ctx sdk.Context, height int64) (stakingtypes.HistoricalInfo, bool)
UnbondingTime(ctx sdk.Context) time.Duration
}
11 changes: 9 additions & 2 deletions x/ibc/02-client/types/msgs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package types_test

import (
"testing"
"time"

"github.com/stretchr/testify/require"

"github.com/tendermint/tendermint/crypto/secp256k1"
tmtypes "github.com/tendermint/tendermint/types"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
Expand All @@ -15,9 +17,14 @@ import (
)

func TestMsgCreateClientValidateBasic(t *testing.T) {
validator := tmtypes.NewValidator(tmtypes.NewMockPV().GetPubKey(), 1)
valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator})

now := time.Date(2020, 1, 2, 0, 0, 0, 0, time.UTC)
cs := tendermint.ConsensusState{
Root: commitment.NewRoot([]byte("root")),
ValidatorSetHash: []byte("hash"),
Timestamp: now,
Root: commitment.NewRoot([]byte("root")),
ValidatorSet: valSet,
}
privKey := secp256k1.GenPrivKey()
signer := sdk.AccAddress(privKey.PubKey().Address())
Expand Down
49 changes: 45 additions & 4 deletions x/ibc/07-tendermint/client_state.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package tendermint

import (
"errors"
"fmt"
"time"

"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand All @@ -21,18 +23,52 @@ var _ clientexported.ClientState = ClientState{}
type ClientState struct {
// Client ID
ID string `json:"id" yaml:"id"`
// Duration of the period since the LastestTimestamp during which the
// submitted headers are valid for upgrade
TrustingPeriod time.Duration `json:"trusting_period" yaml:"trusting_period"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need both trusting and unbonding? Can't we default trusting to a fraction of the unbonding period?

// Duration of the staking unbonding period
UnbondingPeriod time.Duration `json:"unbonding_period" yaml:"unbonding_period"`
// Latest block height
LatestHeight uint64 `json:"latest_height" yaml:"latest_height"`
// Latest block time
LatestTimestamp time.Time `json:"latest_time" yaml:"latest_time"`
// Block height when the client was frozen due to a misbehaviour
FrozenHeight uint64 `json:"frozen_height" yaml:"frozen_height"`
}

// Initialize creates a client state and validates its contents, checking that
// the provided consensus state is from the same client type.
func Initialize(
id string, consensusState clientexported.ConsensusState, trustingPeriod, ubdPeriod time.Duration,
latestHeight uint64,
) (ClientState, error) {
tmConsState, ok := consensusState.(ConsensusState)
if !ok {
return ClientState{}, errors.New("consensus state is not from Tendermint")
}

if trustingPeriod >= ubdPeriod {
return ClientState{}, errors.New("trusting period should be < unbonding period")
}

clientState := NewClientState(
id, trustingPeriod, ubdPeriod, latestHeight, tmConsState.Timestamp,
)
return clientState, nil
}

// NewClientState creates a new ClientState instance
func NewClientState(id string, latestHeight uint64) ClientState {
func NewClientState(
id string, trustingPeriod, ubdPeriod time.Duration,
latestHeight uint64, latestTimestamp time.Time,
) ClientState {
return ClientState{
ID: id,
LatestHeight: latestHeight,
FrozenHeight: 0,
ID: id,
TrustingPeriod: trustingPeriod,
UnbondingPeriod: ubdPeriod,
LatestHeight: latestHeight,
LatestTimestamp: latestTimestamp,
FrozenHeight: 0,
}
}

Expand All @@ -51,6 +87,11 @@ func (cs ClientState) GetLatestHeight() uint64 {
return cs.LatestHeight
}

// GetLatestTimestamp returns latest block time.
func (cs ClientState) GetLatestTimestamp() time.Time {
return cs.LatestTimestamp
}

// IsFrozen returns true if the frozen height has been set.
func (cs ClientState) IsFrozen() bool {
return cs.FrozenHeight != 0
Expand Down
Loading