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

x/ibc: Implement Timeout Timestamp #6022

Merged
merged 15 commits into from
Apr 22, 2020
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
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
3 changes: 3 additions & 0 deletions x/ibc/02-client/exported/exported.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ type ConsensusState interface {
// which is used for key-value pair verification.
GetRoot() commitmentexported.Root

// GetTimestamp returns the timestamp (in nanoseconds) of the consensus state
GetTimestamp() uint64

ValidateBasic() error
}

Expand Down
17 changes: 17 additions & 0 deletions x/ibc/03-connection/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,23 @@ func (k Keeper) SetConnection(ctx sdk.Context, connectionID string, connection t
store.Set(ibctypes.KeyConnection(connectionID), bz)
}

// GetTimestampAtHeight returns the timestamp in nanoseconds of the consensus state at the
// given height.
func (k Keeper) GetTimestampAtHeight(ctx sdk.Context, connection types.ConnectionEnd, height uint64) (uint64, error) {
consensusState, found := k.clientKeeper.GetClientConsensusState(
ctx, connection.GetClientID(), height,
)

if !found {
return 0, sdkerrors.Wrapf(
clienttypes.ErrConsensusStateNotFound,
"clientID (%s), height (%d)", connection.GetClientID(), height,
)
}

return consensusState.GetTimestamp(), nil
}

// GetClientConnectionPaths returns all the connection paths stored under a
// particular client
func (k Keeper) GetClientConnectionPaths(ctx sdk.Context, clientID string) ([]string, bool) {
Expand Down
47 changes: 44 additions & 3 deletions x/ibc/03-connection/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ const (

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

nextTimestamp = 10 // increment used for the next header's timestamp
)

var (
timestamp = time.Now() // starting timestamp for the client test chain
)

type KeeperTestSuite struct {
Expand Down Expand Up @@ -123,6 +129,42 @@ func (suite KeeperTestSuite) TestGetAllConnections() {
suite.Require().ElementsMatch(expConnections, connections)
}

// TestGetTimestampAtHeight verifies if the clients on each chain return the correct timestamp
// for the other chain.
func (suite *KeeperTestSuite) TestGetTimestampAtHeight() {
cases := []struct {
msg string
malleate func()
expPass bool
}{
{"verification success", func() {
suite.chainA.CreateClient(suite.chainB)
}, true},
{"client state not found", func() {}, false},
}

for i, tc := range cases {
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
suite.SetupTest() // reset

tc.malleate()
// create and store a connection to chainB on chainA
connection := suite.chainA.createConnection(testConnectionIDA, testConnectionIDB, testClientIDB, testClientIDA, exported.OPEN)

actualTimestamp, err := suite.chainA.App.IBCKeeper.ConnectionKeeper.GetTimestampAtHeight(
suite.chainA.GetContext(), connection, uint64(suite.chainB.Header.Height),
)

if tc.expPass {
suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg)
suite.Require().EqualValues(uint64(suite.chainB.Header.Time.UnixNano()), actualTimestamp)
} else {
suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg)
}
})
}
}

// TestChain is a testing struct that wraps a simapp with the latest Header, Vals and Signers
// It also contains a field called ClientID. This is the clientID that *other* chains use
// to refer to this TestChain. For simplicity's sake it is also the chainID on the TestChain Header
Expand All @@ -139,9 +181,8 @@ func NewTestChain(clientID string) *TestChain {
validator := tmtypes.NewValidator(privVal.GetPubKey(), 1)
valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator})
signers := []tmtypes.PrivValidator{privVal}
now := time.Now()

header := ibctmtypes.CreateTestHeader(clientID, 1, now, valSet, signers)
header := ibctmtypes.CreateTestHeader(clientID, 1, timestamp, valSet, signers)

return &TestChain{
ClientID: clientID,
Expand Down Expand Up @@ -314,7 +355,7 @@ func (chain *TestChain) createChannel(

func nextHeader(chain *TestChain) ibctmtypes.Header {
return ibctmtypes.CreateTestHeader(chain.Header.ChainID, chain.Header.Height+1,
time.Now(), chain.Vals, chain.Signers)
chain.Header.Time.Add(nextTimestamp), chain.Vals, chain.Signers)
}

func prefixedClientKey(clientID string, key []byte) []byte {
Expand Down
5 changes: 3 additions & 2 deletions x/ibc/04-channel/client/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
// QueryPacket returns a packet from the store
func QueryPacket(
ctx context.CLIContext, portID, channelID string,
sequence, timeout uint64, prove bool,
sequence, timeoutHeight, timeoutTimestamp uint64, prove bool,
) (types.PacketResponse, error) {
req := abci.RequestQuery{
Path: "store/ibc/key",
Expand Down Expand Up @@ -39,7 +39,8 @@ func QueryPacket(
channelID,
destPortID,
destChannelID,
timeout,
timeoutHeight,
timeoutTimestamp,
)

// FIXME: res.Height+1 is hack, fix later
Expand Down
1 change: 1 addition & 0 deletions x/ibc/04-channel/exported/exported.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type CounterpartyI interface {
type PacketI interface {
GetSequence() uint64
GetTimeoutHeight() uint64
GetTimeoutTimestamp() uint64
GetSourcePort() string
GetSourceChannel() string
GetDestPort() string
Expand Down
11 changes: 11 additions & 0 deletions x/ibc/04-channel/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ const (

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

timeoutHeight = 100
timeoutTimestamp = 100
disabledTimeoutTimestamp = 0
disabledTimeoutHeight = 0
)

type KeeperTestSuite struct {
Expand Down Expand Up @@ -193,6 +198,12 @@ func commitNBlocks(chain *TestChain, n int) {
}
}

// commit current block and start the next block with the provided time
func commitBlockWithNewTimestamp(chain *TestChain, timestamp int64) {
chain.App.Commit()
chain.App.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: chain.App.LastBlockHeight() + 1, Time: time.Unix(timestamp, 0)}})
}

// nolint: unused
func queryProof(chain *TestChain, key []byte) (commitmenttypes.MerkleProof, uint64) {
res := chain.App.Query(abci.RequestQuery{
Expand Down
50 changes: 41 additions & 9 deletions x/ibc/04-channel/keeper/packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package keeper
import (
"bytes"
"fmt"
"time"

sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
Expand All @@ -16,7 +17,7 @@ import (
ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types"
)

// SendPacket is called by a module in order to send an IBC packet on a channel
// SendPacket is called by a module in order to send an IBC packet on a channel
// end owned by the calling module to the corresponding module on the counterparty
// chain.
func (k Keeper) SendPacket(
Expand Down Expand Up @@ -77,8 +78,24 @@ func (k Keeper) SendPacket(
}

// check if packet timeouted on the receiving chain
if clientState.GetLatestHeight() >= packet.GetTimeoutHeight() {
return sdkerrors.Wrap(types.ErrPacketTimeout, "timeout already passed ond the receiving chain")
latestHeight := clientState.GetLatestHeight()
AdityaSripal marked this conversation as resolved.
Show resolved Hide resolved
if packet.GetTimeoutHeight() != 0 && latestHeight >= packet.GetTimeoutHeight() {
return sdkerrors.Wrapf(
types.ErrPacketTimeout,
"receiving chain block height >= packet timeout height (%d >= %d)", latestHeight, packet.GetTimeoutHeight(),
)
}

latestTimestamp, err := k.connectionKeeper.GetTimestampAtHeight(ctx, connectionEnd, latestHeight)
if err != nil {
return err
}

if packet.GetTimeoutTimestamp() != 0 && latestTimestamp >= packet.GetTimeoutTimestamp() {
return sdkerrors.Wrapf(
types.ErrPacketTimeout,
"receiving chain block timestamp >= packet timeout timestamp (%s >= %s)", time.Unix(0, int64(latestTimestamp)), time.Unix(0, int64(packet.GetTimeoutTimestamp())),
)
}

nextSequenceSend, found := k.GetNextSequenceSend(ctx, packet.GetSourcePort(), packet.GetSourceChannel())
Expand All @@ -103,7 +120,8 @@ func (k Keeper) SendPacket(
sdk.NewEvent(
types.EventTypeSendPacket,
sdk.NewAttribute(types.AttributeKeyData, string(packet.GetData())),
sdk.NewAttribute(types.AttributeKeyTimeout, fmt.Sprintf("%d", packet.GetTimeoutHeight())),
sdk.NewAttribute(types.AttributeKeyTimeoutHeight, fmt.Sprintf("%d", packet.GetTimeoutHeight())),
sdk.NewAttribute(types.AttributeKeyTimeoutTimestamp, fmt.Sprintf("%d", packet.GetTimeoutTimestamp())),
sdk.NewAttribute(types.AttributeKeySequence, fmt.Sprintf("%d", packet.GetSequence())),
sdk.NewAttribute(types.AttributeKeySrcPort, packet.GetSourcePort()),
sdk.NewAttribute(types.AttributeKeySrcChannel, packet.GetSourceChannel()),
Expand Down Expand Up @@ -167,8 +185,19 @@ func (k Keeper) RecvPacket(
}

// check if packet timeouted by comparing it with the latest height of the chain
if uint64(ctx.BlockHeight()) >= packet.GetTimeoutHeight() {
return nil, types.ErrPacketTimeout
if packet.GetTimeoutHeight() != 0 && uint64(ctx.BlockHeight()) >= packet.GetTimeoutHeight() {
return nil, sdkerrors.Wrapf(
types.ErrPacketTimeout,
"block height >= packet timeout height (%d >= %d)", uint64(ctx.BlockHeight()), packet.GetTimeoutHeight(),
)
}

// check if packet timeouted by comparing it with the latest timestamp of the chain
if packet.GetTimeoutTimestamp() != 0 && uint64(ctx.BlockTime().UnixNano()) >= packet.GetTimeoutTimestamp() {
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
return nil, sdkerrors.Wrapf(
types.ErrPacketTimeout,
"block timestamp >= packet timeout timestamp (%s >= %s)", ctx.BlockTime(), time.Unix(0, int64(packet.GetTimeoutTimestamp())),
)
}

if err := k.connectionKeeper.VerifyPacketCommitment(
Expand Down Expand Up @@ -243,7 +272,8 @@ func (k Keeper) PacketExecuted(
types.EventTypeRecvPacket,
sdk.NewAttribute(types.AttributeKeyData, string(packet.GetData())),
sdk.NewAttribute(types.AttributeKeyAck, string(acknowledgement)),
sdk.NewAttribute(types.AttributeKeyTimeout, fmt.Sprintf("%d", packet.GetTimeoutHeight())),
sdk.NewAttribute(types.AttributeKeyTimeoutHeight, fmt.Sprintf("%d", packet.GetTimeoutHeight())),
sdk.NewAttribute(types.AttributeKeyTimeoutTimestamp, fmt.Sprintf("%d", packet.GetTimeoutTimestamp())),
sdk.NewAttribute(types.AttributeKeySequence, fmt.Sprintf("%d", packet.GetSequence())),
sdk.NewAttribute(types.AttributeKeySrcPort, packet.GetSourcePort()),
sdk.NewAttribute(types.AttributeKeySrcChannel, packet.GetSourceChannel()),
Expand Down Expand Up @@ -330,7 +360,8 @@ func (k Keeper) AcknowledgePacket(
ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
types.EventTypeAcknowledgePacket,
sdk.NewAttribute(types.AttributeKeyTimeout, fmt.Sprintf("%d", packet.GetTimeoutHeight())),
sdk.NewAttribute(types.AttributeKeyTimeoutHeight, fmt.Sprintf("%d", packet.GetTimeoutHeight())),
sdk.NewAttribute(types.AttributeKeyTimeoutTimestamp, fmt.Sprintf("%d", packet.GetTimeoutTimestamp())),
sdk.NewAttribute(types.AttributeKeySequence, fmt.Sprintf("%d", packet.GetSequence())),
sdk.NewAttribute(types.AttributeKeySrcPort, packet.GetSourcePort()),
sdk.NewAttribute(types.AttributeKeySrcChannel, packet.GetSourceChannel()),
Expand Down Expand Up @@ -445,7 +476,8 @@ func (k Keeper) CleanupPacket(
ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
types.EventTypeCleanupPacket,
sdk.NewAttribute(types.AttributeKeyTimeout, fmt.Sprintf("%d", packet.GetTimeoutHeight())),
sdk.NewAttribute(types.AttributeKeyTimeoutHeight, fmt.Sprintf("%d", packet.GetTimeoutHeight())),
sdk.NewAttribute(types.AttributeKeyTimeoutTimestamp, fmt.Sprintf("%d", packet.GetTimeoutTimestamp())),
sdk.NewAttribute(types.AttributeKeySequence, fmt.Sprintf("%d", packet.GetSequence())),
sdk.NewAttribute(types.AttributeKeySrcPort, packet.GetSourcePort()),
sdk.NewAttribute(types.AttributeKeySrcChannel, packet.GetSourceChannel()),
Expand Down
Loading