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 26 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
61 changes: 44 additions & 17 deletions x/ibc/02-client/keeper/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package keeper_test
import (
"bytes"
"fmt"
"time"

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

Expand Down Expand Up @@ -40,12 +41,12 @@ func (suite *KeeperTestSuite) TestCreateClient() {

if tc.expPanic {
suite.Require().Panics(func() {
clientState, err := ibctmtypes.Initialize(tc.clientID, tc.clientID, suite.consensusState, trustingPeriod, ubdPeriod)
clientState, err := ibctmtypes.Initialize(tc.clientID, trustingPeriod, ubdPeriod, suite.header)
suite.Require().NoError(err, "err on client state initialization")
suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState)
}, "Msg %d didn't panic: %s", i, tc.msg)
} else {
clientState, err := ibctmtypes.Initialize(tc.clientID, tc.clientID, suite.consensusState, trustingPeriod, ubdPeriod)
clientState, err := ibctmtypes.Initialize(tc.clientID, trustingPeriod, ubdPeriod, suite.header)
if tc.expPass {
suite.Require().NoError(err, "errored on initialization")
suite.Require().NotNil(clientState, "valid test case %d failed: %s", i, tc.msg)
Expand All @@ -65,46 +66,67 @@ func (suite *KeeperTestSuite) TestCreateClient() {
}

func (suite *KeeperTestSuite) TestUpdateClient() {
// Must create header creation functions since suite.header gets recreated on each test case
createValidUpdateFn := func(s *KeeperTestSuite) ibctmtypes.Header {
return ibctmtypes.CreateTestHeader(testClientID, suite.header.Height+1, suite.header.Time.Add(time.Minute),
suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal})
}
createInvalidUpdateFn := func(s *KeeperTestSuite) ibctmtypes.Header {
return ibctmtypes.CreateTestHeader(testClientID, suite.header.Height-3, suite.header.Time.Add(time.Minute),
suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal})
}
var updateHeader ibctmtypes.Header

cases := []struct {
name string
malleate func() error
expPass bool
}{
{"valid update", func() error {
clientState, err := ibctmtypes.Initialize(testClientID, testClientID, suite.consensusState, trustingPeriod, ubdPeriod)
clientState, err := ibctmtypes.Initialize(testClientID, trustingPeriod, ubdPeriod, suite.header)
if err != nil {
return err
}
_, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState)
updateHeader = createValidUpdateFn(suite)
return err
}, true},
{"client type not found", func() error {
updateHeader = createValidUpdateFn(suite)

return nil
}, false},
{"client type and header type mismatch", func() error {
suite.keeper.SetClientType(suite.ctx, testClientID, invalidClientType)
updateHeader = createValidUpdateFn(suite)

return nil
}, false},
{"client state not found", func() error {
suite.keeper.SetClientType(suite.ctx, testClientID, exported.Tendermint)
updateHeader = createValidUpdateFn(suite)

return nil
}, false},
{"frozen client", func() error {
clientState := ibctmtypes.ClientState{FrozenHeight: 1, ID: testClientID, LatestHeight: 10}
clientState := ibctmtypes.ClientState{FrozenHeight: 1, ID: testClientID, LastHeader: suite.header}
suite.keeper.SetClientState(suite.ctx, clientState)
suite.keeper.SetClientType(suite.ctx, testClientID, exported.Tendermint)
updateHeader = createValidUpdateFn(suite)

return nil
}, false},
{"invalid header", func() error {
clientState, err := ibctmtypes.Initialize(testClientID, testClientID, suite.consensusState, trustingPeriod, ubdPeriod)
clientState, err := ibctmtypes.Initialize(testClientID, trustingPeriod, ubdPeriod, suite.header)
if err != nil {
return err
}
_, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState)
if err != nil {
return err
}
suite.header.Height = suite.ctx.BlockHeight() - 1
updateHeader = createInvalidUpdateFn(suite)

return nil
}, false},
}
Expand All @@ -117,27 +139,32 @@ func (suite *KeeperTestSuite) TestUpdateClient() {
err := tc.malleate()
suite.Require().NoError(err)

err = suite.keeper.UpdateClient(suite.ctx, testClientID, suite.header)
suite.ctx = suite.ctx.WithBlockTime(updateHeader.Time.Add(time.Minute))

err = suite.keeper.UpdateClient(suite.ctx, testClientID, updateHeader)

if tc.expPass {
suite.Require().NoError(err, err)

expConsensusState := ibctmtypes.ConsensusState{
Timestamp: suite.header.Time,
Root: commitment.NewRoot(suite.header.AppHash),
ValidatorSet: suite.header.ValidatorSet,
Height: uint64(updateHeader.Height),
Timestamp: updateHeader.Time,
Root: commitment.NewRoot(updateHeader.AppHash),
ValidatorSet: updateHeader.ValidatorSet,
}

clientState, found := suite.keeper.GetClientState(suite.ctx, testClientID)
suite.Require().True(found, "valid test case %d failed: %s", i, tc.name)

consensusState, found := suite.keeper.GetClientConsensusState(suite.ctx, testClientID, uint64(suite.header.GetHeight()))
consensusState, found := suite.keeper.GetClientConsensusState(suite.ctx, testClientID, uint64(updateHeader.GetHeight()))
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)

suite.Require().True(found, "valid test case %d failed: %s", i, tc.name)
tmConsState, ok := consensusState.(ibctmtypes.ConsensusState)
suite.Require().True(ok, "consensus state is not a tendermint consensus state")
// recalculate cached totalVotingPower field for equality check
tmConsState.ValidatorSet.TotalVotingPower()

suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name)
suite.Require().Equal(suite.header.GetHeight(), clientState.GetLatestHeight(), "client state height not updated correctly on case %s", tc.name)
suite.Require().Equal(updateHeader.GetHeight(), clientState.GetLatestHeight(), "client state height not updated correctly on case %s", tc.name)
suite.Require().Equal(expConsensusState, consensusState, "consensus state should have been updated on case %s", tc.name)
} else {
suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name)
Expand Down Expand Up @@ -181,7 +208,7 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
},
func() error {
suite.consensusState.ValidatorSet = bothValSet
clientState, err := ibctmtypes.Initialize(testClientID, testClientID, suite.consensusState, trustingPeriod, ubdPeriod)
clientState, err := ibctmtypes.Initialize(testClientID, trustingPeriod, ubdPeriod, suite.header)
if err != nil {
return err
}
Expand All @@ -201,7 +228,7 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
},
func() error {
suite.consensusState.ValidatorSet = bothValSet
clientState, err := ibctmtypes.Initialize(testClientID, testClientID, suite.consensusState, trustingPeriod, ubdPeriod)
clientState, err := ibctmtypes.Initialize(testClientID, trustingPeriod, ubdPeriod, suite.header)
if err != nil {
return err
}
Expand All @@ -226,7 +253,7 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
ClientID: testClientID,
},
func() error {
clientState := ibctmtypes.ClientState{FrozenHeight: 1, ID: testClientID, LatestHeight: 10}
clientState := ibctmtypes.ClientState{FrozenHeight: 1, ID: testClientID, LastHeader: suite.header}
suite.keeper.SetClientState(suite.ctx, clientState)
return nil
},
Expand All @@ -241,7 +268,7 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
ClientID: testClientID,
},
func() error {
clientState := ibctmtypes.ClientState{FrozenHeight: 1, ID: testClientID, LatestHeight: 10}
clientState := ibctmtypes.ClientState{FrozenHeight: 1, ID: testClientID, LastHeader: suite.header}
suite.keeper.SetClientState(suite.ctx, clientState)
return nil
},
Expand All @@ -256,7 +283,7 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() {
ClientID: testClientID,
},
func() error {
clientState, err := ibctmtypes.Initialize(testClientID, testClientID, suite.consensusState, trustingPeriod, ubdPeriod)
clientState, err := ibctmtypes.Initialize(testClientID, trustingPeriod, ubdPeriod, suite.header)
if err != nil {
return err
}
Expand Down
16 changes: 9 additions & 7 deletions x/ibc/02-client/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func (suite *KeeperTestSuite) SetupTest() {
suite.privVal = tmtypes.NewMockPV()
validator := tmtypes.NewValidator(suite.privVal.GetPubKey(), 1)
suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{validator})
suite.header = ibctmtypes.CreateTestHeader(testClientID, testClientHeight+2, now2, suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal})
suite.header = ibctmtypes.CreateTestHeader(testClientID, testClientHeight, now2, suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal})
suite.consensusState = ibctmtypes.ConsensusState{
Height: testClientHeight,
Timestamp: suite.now,
Expand All @@ -82,7 +82,7 @@ func TestKeeperTestSuite(t *testing.T) {
}

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

retrievedState, found := suite.keeper.GetClientState(suite.ctx, testClientID)
Expand Down Expand Up @@ -113,9 +113,9 @@ func (suite *KeeperTestSuite) TestSetClientConsensusState() {

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

for i := range expClients {
Expand Down Expand Up @@ -155,7 +155,7 @@ func (suite KeeperTestSuite) TestGetConsensusState() {

func (suite KeeperTestSuite) TestConsensusStateHelpers() {
// initial setup
clientState, _ := ibctmtypes.Initialize(testClientID, testClientID, suite.consensusState, trustingPeriod, ubdPeriod)
clientState, _ := ibctmtypes.Initialize(testClientID, trustingPeriod, ubdPeriod, suite.header)
suite.keeper.SetClientState(suite.ctx, clientState)
suite.keeper.SetClientConsensusState(suite.ctx, testClientID, testClientHeight, suite.consensusState)

Expand All @@ -166,9 +166,11 @@ func (suite KeeperTestSuite) TestConsensusStateHelpers() {
ValidatorSet: suite.valSet,
}

header := ibctmtypes.CreateTestHeader(testClientID, testClientHeight+5, suite.header.Time.Add(time.Minute), suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal})

// mock update functionality
clientState.LastHeader = header
suite.keeper.SetClientConsensusState(suite.ctx, testClientID, testClientHeight+5, nextState)
clientState.LatestHeight += 5
suite.keeper.SetClientState(suite.ctx, clientState)

latest, ok := suite.keeper.GetLatestClientConsensusState(suite.ctx, testClientID)
Expand Down
45 changes: 32 additions & 13 deletions x/ibc/03-connection/keeper/handshake.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ func (k Keeper) ConnOpenTry(
ctx sdk.Context,
connectionID string, // desiredIdentifier
counterparty types.Counterparty, // counterpartyConnectionIdentifier, counterpartyPrefix and counterpartyClientIdentifier
clientID string,
counterpartyVersions []string,
proofInit commitment.ProofI,
proofConsensus commitment.ProofI,
proofHeight uint64,
consensusHeight uint64,
clientID string, // clientID of chainA
counterpartyVersions []string, // supported versions of chain A
proofInit commitment.ProofI, // proof that chainA stored connectionEnd in state (on ConnOpenInit)
proofConsensus commitment.ProofI, // proof that chainA stored chainB's consensus state at consensus height
proofHeight uint64, // height at which relayer constructs proof of A storing connectionEnd in state
consensusHeight uint64, // latest height of chain B which chain A has stored in its chain B client
) error {
if consensusHeight > uint64(ctx.BlockHeight()) {
return sdkerrors.Wrap(ibctypes.ErrInvalidHeight, "invalid consensus height")
Expand All @@ -78,19 +78,24 @@ func (k Keeper) ConnOpenTry(
// connection defines chain B's ConnectionEnd
connection := types.NewConnectionEnd(exported.UNINITIALIZED, clientID, counterparty, []string{version})

// Check that ChainA committed expectedConnectionEnd to its state
if err := k.VerifyConnectionState(
ctx, connection, proofHeight, proofInit, counterparty.ConnectionID,
expectedConnection,
); err != nil {
return err
}

// Check that ChainA stored the correct ConsensusState of chainB at the given consensusHeight
if err := k.VerifyClientConsensusState(
ctx, connection, consensusHeight, proofConsensus, expectedConsensusState,
); err != nil {
return err
}

// If connection already exists for connectionID, ensure that the existing connection's counterparty
// is chainA and connection is on INIT stage
// Check that existing connection version is on desired version of current handshake
previousConnection, found := k.GetConnection(ctx, connectionID)
if found && !(previousConnection.State == exported.INIT &&
previousConnection.Counterparty.ConnectionID == counterparty.ConnectionID &&
Expand All @@ -101,6 +106,7 @@ func (k Keeper) ConnOpenTry(
return sdkerrors.Wrap(types.ErrInvalidConnection, "cannot relay connection attempt")
}

// Set connection state to TRYOPEN and store in chainB state
connection.State = exported.TRYOPEN
if err := k.addConnectionToClient(ctx, clientID, connectionID); err != nil {
return sdkerrors.Wrap(err, "cannot relay connection attempt")
Expand All @@ -118,34 +124,40 @@ func (k Keeper) ConnOpenTry(
func (k Keeper) ConnOpenAck(
ctx sdk.Context,
connectionID string,
version string,
proofTry commitment.ProofI,
proofConsensus commitment.ProofI,
proofHeight uint64,
consensusHeight uint64,
version string, // version that ChainB chose in ConnOpenTry
proofTry commitment.ProofI, // proof that connectionEnd was added to ChainB state in ConnOpenTry
proofConsensus commitment.ProofI, // proof that chainB has stored ConsensusState of chainA on its client
proofHeight uint64, // height that relayer constructed proofTry
consensusHeight uint64, // latest height of chainA that chainB has stored on its chainA client
) error {
// Check that chainB client hasn't stored invalid height
if consensusHeight > uint64(ctx.BlockHeight()) {
return sdkerrors.Wrap(ibctypes.ErrInvalidHeight, "invalid consensus height")
}

// Retrieve connection
connection, found := k.GetConnection(ctx, connectionID)
if !found {
return sdkerrors.Wrap(types.ErrConnectionNotFound, "cannot relay ACK of open attempt")
}

// Check connection on ChainA is on correct state: INIT
if connection.State != exported.INIT {
return sdkerrors.Wrapf(
types.ErrInvalidConnectionState,
"connection state is not INIT (got %s)", connection.State.String(),
)
}

// Check that ChainB's proposed version is one of chainA's accepted versions
if types.LatestVersion(connection.Versions) != version {
return sdkerrors.Wrapf(
ibctypes.ErrInvalidVersion,
"connection version does't match provided one (%s ≠ %s)", types.LatestVersion(connection.Versions), version,
)
}

// Retrieve chainA's consensus state at consensusheight
expectedConsensusState, found := k.clientKeeper.GetSelfConsensusState(ctx, consensusHeight)
if !found {
return clienttypes.ErrConsensusStateNotFound
Expand All @@ -155,19 +167,22 @@ func (k Keeper) ConnOpenAck(
expectedCounterparty := types.NewCounterparty(connection.ClientID, connectionID, prefix)
expectedConnection := types.NewConnectionEnd(exported.TRYOPEN, connection.Counterparty.ClientID, expectedCounterparty, []string{version})

// Ensure that ChainB stored expected connectionEnd in its state during ConnOpenTry
if err := k.VerifyConnectionState(
ctx, connection, proofHeight, proofTry, connection.Counterparty.ConnectionID,
expectedConnection,
); err != nil {
return err
}

// Ensure that ChainB has stored the correct ConsensusState for chainA at the consensusHeight
if err := k.VerifyClientConsensusState(
ctx, connection, consensusHeight, proofConsensus, expectedConsensusState,
); err != nil {
return err
}

// Update connection state to Open
connection.State = exported.OPEN
connection.Versions = []string{version}
k.SetConnection(ctx, connectionID, connection)
Expand All @@ -182,14 +197,16 @@ func (k Keeper) ConnOpenAck(
func (k Keeper) ConnOpenConfirm(
ctx sdk.Context,
connectionID string,
proofAck commitment.ProofI,
proofHeight uint64,
proofAck commitment.ProofI, // proof that connection opened on ChainA during ConnOpenAck
proofHeight uint64, // height that relayer constructed proofAck
) error {
// Retrieve connection
connection, found := k.GetConnection(ctx, connectionID)
if !found {
return sdkerrors.Wrap(types.ErrConnectionNotFound, "cannot relay ACK of open attempt")
}

// Check that connection state on ChainB is on state: TRYOPEN
if connection.State != exported.TRYOPEN {
return sdkerrors.Wrapf(
types.ErrInvalidConnectionState,
Expand All @@ -201,13 +218,15 @@ func (k Keeper) ConnOpenConfirm(
expectedCounterparty := types.NewCounterparty(connection.ClientID, connectionID, prefix)
expectedConnection := types.NewConnectionEnd(exported.OPEN, connection.Counterparty.ClientID, expectedCounterparty, connection.Versions)

// Check that connection on ChainA is open
if err := k.VerifyConnectionState(
ctx, connection, proofHeight, proofAck, connection.Counterparty.ConnectionID,
expectedConnection,
); err != nil {
return err
}

// Update ChainB's connection to Open
connection.State = exported.OPEN
k.SetConnection(ctx, connectionID, connection)
k.Logger(ctx).Info(fmt.Sprintf("connection %s state updated: TRYOPEN -> OPEN ", connectionID))
Expand Down
Loading