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

Use Tendermint lite client verification #5666

Merged
merged 28 commits into from
Feb 21, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
7b8afe1
start ics07 integration with lite.Verify
AdityaSripal Feb 18, 2020
0732280
Merge branch 'aditya/lite-verify' of https://github.com/cosmos/cosmos…
AdityaSripal Feb 18, 2020
8f9f329
make build passes, there are still a number of tests to update
jackzampolin Feb 19, 2020
77c14a4
WIP test refactor
jackzampolin Feb 19, 2020
f9e0f30
start fixing tendermint tests
AdityaSripal Feb 19, 2020
4ac36fa
add test cases to improve coverage
AdityaSripal Feb 19, 2020
32ac85a
fix merge conflicts
AdityaSripal Feb 19, 2020
8c5d6ab
Tests compiling
jackzampolin Feb 19, 2020
b504fed
fix tests
AdityaSripal Feb 20, 2020
d99fb3c
fix merge
AdityaSripal Feb 20, 2020
07aae46
fix missed merge conflicts and fix ics2 tests
AdityaSripal Feb 20, 2020
6a17783
add comments for connection handshake code
AdityaSripal Feb 20, 2020
bc2b74d
start refactor of test code
AdityaSripal Feb 20, 2020
73a209f
complete verify_test
AdityaSripal Feb 20, 2020
433d9a3
Merge branch 'ibc-alpha' into aditya/lite-verify
cwgoes Feb 20, 2020
195afbc
Fix spacing from merge
cwgoes Feb 20, 2020
e5dd0ff
Note blocking ICS issue
cwgoes Feb 20, 2020
d95840e
fix 03-connection tests
AdityaSripal Feb 20, 2020
b03efa8
Merge branch 'aditya/lite-verify' of https://github.com/cosmos/cosmos…
AdityaSripal Feb 20, 2020
99116c5
readd slashing defensive check
AdityaSripal Feb 21, 2020
e32aa1c
fix connection tests to have right connection structure; revert incor…
AdityaSripal Feb 21, 2020
5e2cfaf
fix keeper and handshake tests
AdityaSripal Feb 21, 2020
baffeda
fix 04-channel tests
AdityaSripal Feb 21, 2020
1e035b0
fix 20-transfer tests
AdityaSripal Feb 21, 2020
faed84d
get ante test to build
AdityaSripal Feb 21, 2020
b78c92e
fixed ante tests
AdityaSripal Feb 21, 2020
0a3b207
address fede review
AdityaSripal Feb 21, 2020
d41d155
linting
AdityaSripal Feb 21, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions x/ibc/07-tendermint/misbehaviour.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
"github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types"
ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types"
lite "github.com/tendermint/tendermint/lite2"
)

// CheckMisbehaviourAndUpdateState determines whether or not two conflicting
Expand Down Expand Up @@ -78,19 +79,20 @@ func checkMisbehaviour(
return errors.New("unbonding period since last consensus state timestamp is over")
}

// Evidence is within the trusting period. ValidatorSet must have 2/3 similarity with trusted FromValidatorSet
// check that the validator sets on both headers are valid given the last trusted validatorset
// less than or equal to evidence height
if err := consensusState.ValidatorSet.VerifyFutureCommit(
evidence.Header1.ValidatorSet, evidence.ChainID,
evidence.Header1.Commit.BlockID, evidence.Header1.Height, evidence.Header1.Commit,
// TODO: - Evidence must be within trusting period
cwgoes marked this conversation as resolved.
Show resolved Hide resolved

// - ValidatorSet must have 2/3 similarity with trusted FromValidatorSet
// - ValidatorSets on both headers are valid given the last trusted ValidatorSet
if err := consensusState.ValidatorSet.VerifyCommitTrusting(
evidence.ChainID, evidence.Header1.Commit.BlockID, evidence.Header1.Height,
evidence.Header1.Commit, lite.DefaultTrustLevel,
); err != nil {
return fmt.Errorf("validator set in header 1 has too much change from last known validator set: %v", err)
}

if err := consensusState.ValidatorSet.VerifyFutureCommit(
evidence.Header2.ValidatorSet, evidence.ChainID,
evidence.Header2.Commit.BlockID, evidence.Header2.Height, evidence.Header2.Commit,
if err := consensusState.ValidatorSet.VerifyCommitTrusting(
evidence.ChainID, evidence.Header2.Commit.BlockID, evidence.Header2.Height,
evidence.Header2.Commit, lite.DefaultTrustLevel,
); err != nil {
return fmt.Errorf("validator set in header 2 has too much change from last known validator set: %v", err)
}
Expand Down
38 changes: 17 additions & 21 deletions x/ibc/07-tendermint/types/client_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,54 +30,45 @@ type ClientState struct {
TrustingPeriod time.Duration `json:"trusting_period" yaml:"trusting_period"`
cwgoes marked this conversation as resolved.
Show resolved Hide resolved
// 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"`
cwgoes marked this conversation as resolved.
Show resolved Hide resolved
// Block height when the client was frozen due to a misbehaviour
FrozenHeight uint64 `json:"frozen_height" yaml:"frozen_height"`
// Last Header that was stored by client
LastHeader Header
cwgoes marked this conversation as resolved.
Show resolved Hide resolved
}

// InitializeFromMsg creates a tendermint client state from a CreateClientMsg
func InitializeFromMsg(
msg MsgCreateClient,
) (ClientState, error) {
return Initialize(msg.GetClientID(), msg.ChainID, msg.GetConsensusState(), msg.TrustingPeriod, msg.UnbondingPeriod)
return Initialize(msg.GetClientID(), msg.TrustingPeriod, msg.UnbondingPeriod, msg.Header)
}

// 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, chainID string, consensusState clientexported.ConsensusState, trustingPeriod, ubdPeriod time.Duration,
id string, trustingPeriod, ubdPeriod time.Duration,
header Header,
) (ClientState, error) {
tmConsState, ok := consensusState.(ConsensusState)
if !ok {
return ClientState{}, errors.New("consensus state is not from Tendermint")
}
latestHeight := tmConsState.GetHeight()

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

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

// NewClientState creates a new ClientState instance
func NewClientState(
id string, chainID string, trustingPeriod, ubdPeriod time.Duration,
latestHeight uint64, latestTimestamp time.Time,
id string, trustingPeriod, ubdPeriod time.Duration,
header Header,
) ClientState {
return ClientState{
ID: id,
ChainID: chainID,
TrustingPeriod: trustingPeriod,
UnbondingPeriod: ubdPeriod,
LatestHeight: latestHeight,
LatestTimestamp: latestTimestamp,
LastHeader: header,
FrozenHeight: 0,
}
}
Expand All @@ -87,19 +78,24 @@ func (cs ClientState) GetID() string {
return cs.ID
}

// GetChainID returns the chain-id from the last header
func (cs ClientState) GetChainID() string {
return cs.LastHeader.ChainID
}

// ClientType is tendermint.
func (cs ClientState) ClientType() clientexported.ClientType {
return clientexported.Tendermint
}

// GetLatestHeight returns latest block height.
func (cs ClientState) GetLatestHeight() uint64 {
return cs.LatestHeight
return uint64(cs.LastHeader.Height)
}

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

// IsFrozen returns true if the frozen height has been set.
Expand Down Expand Up @@ -323,7 +319,7 @@ func validateVerificationArgs(
proof commitment.ProofI,
consensusState clientexported.ConsensusState,
) error {
if cs.LatestHeight < height {
if cs.GetLatestHeight() < height {
return sdkerrors.Wrap(
ibctypes.ErrInvalidHeight,
fmt.Sprintf("client state (%s) height < proof height (%d < %d)", cs.ID, cs.LatestHeight, height),
Expand Down
2 changes: 1 addition & 1 deletion x/ibc/07-tendermint/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ const (
var (
ErrInvalidTrustingPeriod = sdkerrors.Register(SubModuleName, 1, "invalid trusting period")
ErrInvalidUnbondingPeriod = sdkerrors.Register(SubModuleName, 2, "invalid unbonding period")
ErrInvalidChainID = sdkerrors.Register(SubModuleName, 3, "invalid chain ID")
ErrInvalidHeader = sdkerrors.Register(SubModuleName, 3, "invalid header")
)
35 changes: 10 additions & 25 deletions x/ibc/07-tendermint/types/header.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package types

import (
"bytes"

abci "github.com/tendermint/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"

Expand All @@ -16,6 +18,7 @@ var _ clientexported.Header = Header{}
type Header struct {
tmtypes.SignedHeader // contains the commitment root
ValidatorSet *tmtypes.ValidatorSet `json:"validator_set" yaml:"validator_set"`
NextValidatorSet *tmtypes.ValidatorSet `json:"next_validator_set" yaml:"next_validator_set"`
}

// ClientType defines that the Header is a Tendermint consensus algorithm
Expand Down Expand Up @@ -48,35 +51,17 @@ func (h Header) ValidateBasic(chainID string) error {
if h.ValidatorSet == nil {
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "validator set is nil")
}
if !bytes.Equal(h.ValidatorsHash, h.ValidatorSet.Hash()) {
return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "validator set does not match hash")
}
if !bytes.Equal(h.NextValidatorsHash, h.NextValidatorSet.Hash()) {
return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "next validator set does not match hash")
}
return nil
}

// ToABCIHeader parses the header to an ABCI header type.
// NOTE: only for testing use.
func (h Header) ToABCIHeader() abci.Header {
cwgoes marked this conversation as resolved.
Show resolved Hide resolved
return abci.Header{
Version: abci.Version{
App: h.Version.App.Uint64(),
Block: h.Version.Block.Uint64(),
},
ChainID: h.ChainID,
Height: h.Height,
Time: h.Time,
LastBlockId: abci.BlockID{
Hash: h.LastBlockID.Hash,
PartsHeader: abci.PartSetHeader{
Total: int32(h.LastBlockID.PartsHeader.Total),
Hash: h.LastBlockID.PartsHeader.Hash,
},
},
LastCommitHash: h.LastCommitHash,
DataHash: h.DataHash,
ValidatorsHash: h.ValidatorsHash,
NextValidatorsHash: h.NextValidatorsHash,
ConsensusHash: h.ConsensusHash,
AppHash: h.AppHash,
LastResultsHash: h.LastResultsHash,
EvidenceHash: h.EvidenceHash,
ProposerAddress: h.ProposerAddress,
}
return tmtypes.TM2PB.Header(h.SignedHeader.Header)
}
29 changes: 16 additions & 13 deletions x/ibc/07-tendermint/types/msgs.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package types

import (
"strings"
"time"

sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"

clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment"
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types"
)
Expand All @@ -25,22 +25,20 @@ var _ clientexported.MsgUpdateClient = MsgUpdateClient{}
// MsgCreateClient defines a message to create an IBC client
type MsgCreateClient struct {
ClientID string `json:"client_id" yaml:"client_id"`
ChainID string `json:"chain_id" yaml:"chain_id"`
ConsensusState ConsensusState `json:"consensus_state" yaml:"consensus_state"`
Header Header `json:"header" yaml:"header"`
TrustingPeriod time.Duration `json:"trusting_period" yaml:"trusting_period"`
UnbondingPeriod time.Duration `json:"unbonding_period" yaml:"unbonding_period"`
Signer sdk.AccAddress `json:"address" yaml:"address"`
}

// NewMsgCreateClient creates a new MsgCreateClient instance
func NewMsgCreateClient(
id string, chainID string, consensusState ConsensusState,
id string, header Header,
trustingPeriod, unbondingPeriod time.Duration, signer sdk.AccAddress,
) MsgCreateClient {
return MsgCreateClient{
ClientID: id,
ChainID: chainID,
ConsensusState: consensusState,
Header: header,
TrustingPeriod: trustingPeriod,
UnbondingPeriod: unbondingPeriod,
Signer: signer,
Expand All @@ -59,12 +57,6 @@ func (msg MsgCreateClient) Type() string {

// ValidateBasic implements sdk.Msg
func (msg MsgCreateClient) ValidateBasic() error {
if strings.TrimSpace(msg.ChainID) == "" {
return sdkerrors.Wrap(ErrInvalidChainID, "cannot have empty chain-id")
}
if err := msg.ConsensusState.ValidateBasic(); err != nil {
return err
}
if msg.TrustingPeriod == 0 {
return sdkerrors.Wrap(ErrInvalidTrustingPeriod, "duration cannot be 0")
}
Expand All @@ -74,6 +66,10 @@ func (msg MsgCreateClient) ValidateBasic() error {
if msg.Signer.Empty() {
return sdkerrors.ErrInvalidAddress
}
// ValidateBasic of provided header with self-attested chain-id
if msg.Header.ValidateBasic(msg.Header.ChainID) != nil {
return sdkerrors.Wrap(ErrInvalidHeader, "header failed validatebasic with its own chain-id")
}
return host.DefaultClientIdentifierValidator(msg.ClientID)
}

Expand All @@ -99,7 +95,14 @@ func (msg MsgCreateClient) GetClientType() string {

// GetConsensusState implements clientexported.MsgCreateClient
func (msg MsgCreateClient) GetConsensusState() clientexported.ConsensusState {
return msg.ConsensusState
// Construct initial consensus state from provided Header
root := commitment.NewRoot(msg.Header.AppHash)
return ConsensusState{
Timestamp: msg.Header.Time,
Root: root,
Height: uint64(msg.Header.Height),
ValidatorSet: msg.Header.ValidatorSet,
}
}

// MsgUpdateClient defines a message to update an IBC client
Expand Down
21 changes: 9 additions & 12 deletions x/ibc/07-tendermint/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
"github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types"
commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment"
lite "github.com/tendermint/tendermint/lite2"
)

// CheckValidityAndUpdateState checks if the provided header is valid and updates
Expand Down Expand Up @@ -53,7 +54,7 @@ func checkValidity(
clientState types.ClientState, header types.Header, currentTimestamp time.Time,
) error {
// assert trusting period has not yet passed
if currentTimestamp.Sub(clientState.LatestTimestamp) >= clientState.TrustingPeriod {
if currentTimestamp.Sub(clientState.GetLatestTimestamp()) >= clientState.TrustingPeriod {
return errors.New("trusting period since last client timestamp already passed")
}

Expand All @@ -66,33 +67,29 @@ func checkValidity(
}

// assert header timestamp is past current timestamp
if header.Time.Unix() <= clientState.LatestTimestamp.Unix() {
if header.Time.Unix() <= clientState.GetLatestTimestamp().Unix() {
return sdkerrors.Wrapf(
clienttypes.ErrInvalidHeader,
"header blocktime ≤ latest client state block time (%s ≤ %s)",
header.Time.String(), clientState.LatestTimestamp.String(),
header.Time.String(), clientState.GetLatestTimestamp().String(),
)
}

// assert header height is newer than any we know
if header.GetHeight() <= clientState.LatestHeight {
if header.GetHeight() <= clientState.GetLatestHeight() {
return sdkerrors.Wrapf(
clienttypes.ErrInvalidHeader,
"header height ≤ latest client state height (%d ≤ %d)", header.GetHeight(), clientState.LatestHeight,
"header height ≤ latest client state height (%d ≤ %d)", header.GetHeight(), clientState.GetLatestHeight(),
)
}

// basic consistency check
if err := header.ValidateBasic(clientState.ChainID); err != nil {
return err
}

return header.ValidatorSet.VerifyCommit(header.ChainID, header.Commit.BlockID, header.Height, header.Commit)
return lite.Verify(clientState.GetChainID(), &clientState.LastHeader.SignedHeader, clientState.LastHeader.NextValidatorSet,
&header.SignedHeader, header.ValidatorSet, clientState.TrustingPeriod, currentTimestamp, lite.DefaultTrustLevel)
}

// update the consensus state from a new header
func update(clientState types.ClientState, header types.Header) (types.ClientState, types.ConsensusState) {
clientState.LatestHeight = header.GetHeight()
clientState.LastHeader = header
consensusState := types.ConsensusState{
Timestamp: header.Time,
Root: commitment.NewRoot(header.AppHash),
Expand Down