From 1c82e19272726fdb68b343f18b6961f1c793e063 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 12 Jan 2023 10:38:26 +0000 Subject: [PATCH 01/19] Starts on README --- testutil/simibc/README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 testutil/simibc/README.md diff --git a/testutil/simibc/README.md b/testutil/simibc/README.md new file mode 100644 index 0000000000..e69de29bb2 From 489df6049522d54543a0aa1dd3864f2693f9042b Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 12 Jan 2023 10:38:34 +0000 Subject: [PATCH 02/19] Adds README content --- testutil/simibc/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/testutil/simibc/README.md b/testutil/simibc/README.md index e69de29bb2..23a286636b 100644 --- a/testutil/simibc/README.md +++ b/testutil/simibc/README.md @@ -0,0 +1,7 @@ +# simibc + +This is a collection of utilities based on [ibc-go/testing](https://github.com/cosmos/ibc-go/tree/main/testing) which make it easier to write test scenarios involving precise orderings of + +- BeginBlock, EndBlock on each IBC connected chain +- Packet delivery +- Updating the client From a24b7892333b54def62b66e36cad5017a134bd7c Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 12 Jan 2023 10:38:43 +0000 Subject: [PATCH 03/19] Improves chain_util docstrings --- testutil/simibc/chain_util.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/testutil/simibc/chain_util.go b/testutil/simibc/chain_util.go index 299d54ca58..f2fce172df 100644 --- a/testutil/simibc/chain_util.go +++ b/testutil/simibc/chain_util.go @@ -11,7 +11,7 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" ) -// BeginBlock updates the current header and calls the app.BeginBlock() method. +// BeginBlock updates the current header and calls the app.BeginBlock method. // The new block height is the previous block height + 1. // The new block time is the previous block time + dt. func BeginBlock(c *ibctesting.TestChain, dt time.Duration) { @@ -28,7 +28,12 @@ func BeginBlock(c *ibctesting.TestChain, dt time.Duration) { _ = c.App.BeginBlock(abci.RequestBeginBlock{Header: c.CurrentHeader}) } -// EndBlock and calls the preCommitCallback before the app.Commit() is called. +// EndBlock calls app.EndBlock and executes preCommitCallback BEFORE calling app.Commit +// The callback is useful for testing purposes to execute arbitrary code before the +// chain sdk context is cleared in .Commit(). +// For example, app.EndBlock may lead to a new state, which you would like to query +// to check that it is correct. However, the sdk context is cleared after .Commit(), +// so you can query the state inside the callback. func EndBlock(c *ibctesting.TestChain, preCommitCallback func()) (*ibctmtypes.Header, []channeltypes.Packet) { ebRes := c.App.EndBlock(abci.RequestEndBlock{Height: c.CurrentHeader.Height}) From b85bf00125364328d235d694574e03ab9ffd0dfa Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 12 Jan 2023 10:46:02 +0000 Subject: [PATCH 04/19] Checkpoint pre revert chainID --- testutil/simibc/ordered_link.go | 39 +++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/testutil/simibc/ordered_link.go b/testutil/simibc/ordered_link.go index 85beee8f75..5275fd69fa 100644 --- a/testutil/simibc/ordered_link.go +++ b/testutil/simibc/ordered_link.go @@ -2,6 +2,8 @@ package simibc import channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" +type ChainID string + // Ack represents and ack committed to block state type Ack struct { Ack []byte @@ -19,31 +21,32 @@ type Packet struct { // allows fine-grained control over delivery of acks and packets // to mimic a real relaying relationship between two chains. type OrderedLink struct { - OutboxPackets map[string][]Packet - OutboxAcks map[string][]Ack + OutboxPackets map[ChainID][]Packet + OutboxAcks map[ChainID][]Ack } // MakeOrderedLink creates a new empty network link. func MakeOrderedLink() OrderedLink { return OrderedLink{ - OutboxPackets: map[string][]Packet{}, - OutboxAcks: map[string][]Ack{}, + OutboxPackets: map[ChainID][]Packet{}, + OutboxAcks: map[ChainID][]Ack{}, } } // AddPacket adds an outbound packet from the sender to the counterparty. -func (n OrderedLink) AddPacket(sender string, packet channeltypes.Packet) { +func (n OrderedLink) AddPacket(sender ChainID, packet channeltypes.Packet) { n.OutboxPackets[sender] = append(n.OutboxPackets[sender], Packet{packet, 0}) } // AddAck adds an outbound ack, for future delivery to the sender of the packet // being acked. -func (n OrderedLink) AddAck(sender string, ack []byte, packet channeltypes.Packet) { +func (n OrderedLink) AddAck(sender ChainID, ack []byte, packet channeltypes.Packet) { n.OutboxAcks[sender] = append(n.OutboxAcks[sender], Ack{ack, packet, 0}) } -// ConsumePackets returns and internally delets all packets with 2 or more commits. -func (n OrderedLink) ConsumePackets(sender string, num int) []Packet { +// ConsumePackets returns the first num packets with 2 or more commits. Returned +// packets are removed from the outbox and will not be returned again (consumed). +func (n OrderedLink) ConsumePackets(sender ChainID, num int) []Packet { ret := []Packet{} sz := len(n.OutboxPackets[sender]) if sz < num { @@ -60,8 +63,9 @@ func (n OrderedLink) ConsumePackets(sender string, num int) []Packet { return ret } -// ConsumeAcks returns and internally deletes all acks with 2 or more commits. -func (n OrderedLink) ConsumeAcks(sender string, num int) []Ack { +// ConsumerAcks returns the first num packets with 2 or more commits. Returned +// acks are removed from the outbox and will not be returned again (consumed). +func (n OrderedLink) ConsumeAcks(sender ChainID, num int) []Ack { ret := []Ack{} sz := len(n.OutboxAcks[sender]) if sz < num { @@ -78,12 +82,15 @@ func (n OrderedLink) ConsumeAcks(sender string, num int) []Ack { return ret } -// Commit marks a block commit for a chain and will make its outbound -// packets visible as per the functioning of ibc. In practice it takes -// 2 commits for a packet to become available for delivery, due to the -// need for the light client to have block h + 1 for a packet in block -// h. -func (n OrderedLink) Commit(sender string) { +// Commit marks a block commit, increasing the commit count for all +// packets and acks in the sender's outbox. +// When a packet or ack has 2 or more commits, it is available for +// delivery to the counterparty chain. +// Note that 2 commits are necessary instead of 1: +// - 1st commit is necessary for the packet to included in the block +// - 2nd commit is necessary because in practice the ibc light client +// needs to have block h + 1 to be able to verify the packet in block h. +func (n OrderedLink) Commit(sender ChainID) { for i := range n.OutboxPackets[sender] { n.OutboxPackets[sender][i].Commits += 1 } From 60d9b3acfb95831decfaa610896cdb544ff17722 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 12 Jan 2023 10:57:35 +0000 Subject: [PATCH 05/19] Revert to string in ordered link --- testutil/simibc/ordered_link.go | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/testutil/simibc/ordered_link.go b/testutil/simibc/ordered_link.go index 5275fd69fa..0e7dab4c50 100644 --- a/testutil/simibc/ordered_link.go +++ b/testutil/simibc/ordered_link.go @@ -2,8 +2,6 @@ package simibc import channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" -type ChainID string - // Ack represents and ack committed to block state type Ack struct { Ack []byte @@ -21,32 +19,32 @@ type Packet struct { // allows fine-grained control over delivery of acks and packets // to mimic a real relaying relationship between two chains. type OrderedLink struct { - OutboxPackets map[ChainID][]Packet - OutboxAcks map[ChainID][]Ack + OutboxPackets map[string][]Packet + OutboxAcks map[string][]Ack } // MakeOrderedLink creates a new empty network link. func MakeOrderedLink() OrderedLink { return OrderedLink{ - OutboxPackets: map[ChainID][]Packet{}, - OutboxAcks: map[ChainID][]Ack{}, + OutboxPackets: map[string][]Packet{}, + OutboxAcks: map[string][]Ack{}, } } // AddPacket adds an outbound packet from the sender to the counterparty. -func (n OrderedLink) AddPacket(sender ChainID, packet channeltypes.Packet) { +func (n OrderedLink) AddPacket(sender string, packet channeltypes.Packet) { n.OutboxPackets[sender] = append(n.OutboxPackets[sender], Packet{packet, 0}) } // AddAck adds an outbound ack, for future delivery to the sender of the packet // being acked. -func (n OrderedLink) AddAck(sender ChainID, ack []byte, packet channeltypes.Packet) { +func (n OrderedLink) AddAck(sender string, ack []byte, packet channeltypes.Packet) { n.OutboxAcks[sender] = append(n.OutboxAcks[sender], Ack{ack, packet, 0}) } // ConsumePackets returns the first num packets with 2 or more commits. Returned // packets are removed from the outbox and will not be returned again (consumed). -func (n OrderedLink) ConsumePackets(sender ChainID, num int) []Packet { +func (n OrderedLink) ConsumePackets(sender string, num int) []Packet { ret := []Packet{} sz := len(n.OutboxPackets[sender]) if sz < num { @@ -65,7 +63,7 @@ func (n OrderedLink) ConsumePackets(sender ChainID, num int) []Packet { // ConsumerAcks returns the first num packets with 2 or more commits. Returned // acks are removed from the outbox and will not be returned again (consumed). -func (n OrderedLink) ConsumeAcks(sender ChainID, num int) []Ack { +func (n OrderedLink) ConsumeAcks(sender string, num int) []Ack { ret := []Ack{} sz := len(n.OutboxAcks[sender]) if sz < num { @@ -90,7 +88,7 @@ func (n OrderedLink) ConsumeAcks(sender ChainID, num int) []Ack { // - 1st commit is necessary for the packet to included in the block // - 2nd commit is necessary because in practice the ibc light client // needs to have block h + 1 to be able to verify the packet in block h. -func (n OrderedLink) Commit(sender ChainID) { +func (n OrderedLink) Commit(sender string) { for i := range n.OutboxPackets[sender] { n.OutboxPackets[sender][i].Commits += 1 } From 485f2fec84d146e69ba06bccc14c5bb6821afca4 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 12 Jan 2023 10:57:39 +0000 Subject: [PATCH 06/19] Improves README --- testutil/simibc/README.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/testutil/simibc/README.md b/testutil/simibc/README.md index 23a286636b..6de634cf33 100644 --- a/testutil/simibc/README.md +++ b/testutil/simibc/README.md @@ -1,7 +1,19 @@ # simibc -This is a collection of utilities based on [ibc-go/testing](https://github.com/cosmos/ibc-go/tree/main/testing) which make it easier to write test scenarios involving precise orderings of +## What is this? + +A collection of utilities based on [ibc-go/testing](https://github.com/cosmos/ibc-go/tree/main/testing) which make it easier to write test scenarios involving precise orderings of - BeginBlock, EndBlock on each IBC connected chain - Packet delivery - Updating the client + +## Why is this useful? + +It is very hard to reason about tests written using vanilla [ibc-go/testing](https://github.com/cosmos/ibc-go/tree/main/testing) because the methods included in that library have many side effects. For example, that library has a notion of global time, so calling EndBlock on one chain will influence the future block times of another chain. As another example, sending a packet from chain A to B will automatically progress the block height on chain A. These behaviors make it very hard to understand, especially if your applications have business logic in BeginBlock or EndBlock. + +The utilities in simibc do not have any side effects, making it very easy to understand what is happening. It also makes it very easy to write data driven tests (like table tests, model based tests or property based tests). + +## How do I use this? + +Please see the function docstrings and the example test to get an idea of how you could use this package. This README is intentionally short because it is easier to maintain code and docstrings instead of markdown. From 740ba392abf961e469ffa4dc242d57f224586b41 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 12 Jan 2023 11:05:07 +0000 Subject: [PATCH 07/19] Clarify chain util docstrings --- testutil/simibc/chain_util.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/testutil/simibc/chain_util.go b/testutil/simibc/chain_util.go index f2fce172df..efe2b3453c 100644 --- a/testutil/simibc/chain_util.go +++ b/testutil/simibc/chain_util.go @@ -14,6 +14,8 @@ import ( // BeginBlock updates the current header and calls the app.BeginBlock method. // The new block height is the previous block height + 1. // The new block time is the previous block time + dt. +// +// NOTE: this method may be used independently of the rest of simibc. func BeginBlock(c *ibctesting.TestChain, dt time.Duration) { c.CurrentHeader = tmproto.Header{ @@ -34,6 +36,8 @@ func BeginBlock(c *ibctesting.TestChain, dt time.Duration) { // For example, app.EndBlock may lead to a new state, which you would like to query // to check that it is correct. However, the sdk context is cleared after .Commit(), // so you can query the state inside the callback. +// +// NOTE: this method may be used independently of the rest of simibc. func EndBlock(c *ibctesting.TestChain, preCommitCallback func()) (*ibctmtypes.Header, []channeltypes.Packet) { ebRes := c.App.EndBlock(abci.RequestEndBlock{Height: c.CurrentHeader.Height}) From 288a91978d0f8b1fe11ca9af5033e97f1703c30e Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 12 Jan 2023 11:10:12 +0000 Subject: [PATCH 08/19] Improves ordered link comments --- testutil/simibc/ordered_link.go | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/testutil/simibc/ordered_link.go b/testutil/simibc/ordered_link.go index 0e7dab4c50..2d278eab02 100644 --- a/testutil/simibc/ordered_link.go +++ b/testutil/simibc/ordered_link.go @@ -2,22 +2,26 @@ package simibc import channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" -// Ack represents and ack committed to block state +// Ack represents a (sent) ack committed to block state type Ack struct { - Ack []byte - Packet channeltypes.Packet + Ack []byte + // The packet to which this ack is a response + Packet channeltypes.Packet + // The number of App.Commits that have occurred since this ack was sent + // For example, if the ack was sent at height h, and the blockchain + // has headers ..., h, h+1, h+2 then Commits = 3 Commits int } -// Packet represents a packet committed to block state +// Packet represents a (sent) packet committed to block state type Packet struct { - Packet channeltypes.Packet + Packet channeltypes.Packet + // The number of App.Commits that have occurred since this packet was sent + // For example, if the ack was sent at height h, and the blockchain + // has headers ..., h, h+1, h+2 then Commits = 3 Commits int } -// OrderedLink contains outboxes of packets and acknowledgements and -// allows fine-grained control over delivery of acks and packets -// to mimic a real relaying relationship between two chains. type OrderedLink struct { OutboxPackets map[string][]Packet OutboxAcks map[string][]Ack From c560e3b8fb4b2e38827d6f75ea73eb2eac0fd73c Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 12 Jan 2023 11:12:21 +0000 Subject: [PATCH 09/19] Renames simibc ordered link to ordered outbox --- tests/difference/core/driver/setup.go | 2 +- testutil/simibc/ordered_link.go | 16 ++++++++-------- testutil/simibc/relayed_path.go | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/difference/core/driver/setup.go b/tests/difference/core/driver/setup.go index 39a9ffe67c..e91f1dcead 100644 --- a/tests/difference/core/driver/setup.go +++ b/tests/difference/core/driver/setup.go @@ -49,7 +49,7 @@ import ( type Builder struct { suite *suite.Suite - link simibc.OrderedLink + link simibc.OrderedOutbox path *ibctesting.Path coordinator *ibctesting.Coordinator clientHeaders map[string][]*ibctmtypes.Header diff --git a/testutil/simibc/ordered_link.go b/testutil/simibc/ordered_link.go index 2d278eab02..bde0f1e12e 100644 --- a/testutil/simibc/ordered_link.go +++ b/testutil/simibc/ordered_link.go @@ -22,33 +22,33 @@ type Packet struct { Commits int } -type OrderedLink struct { +type OrderedOutbox struct { OutboxPackets map[string][]Packet OutboxAcks map[string][]Ack } // MakeOrderedLink creates a new empty network link. -func MakeOrderedLink() OrderedLink { - return OrderedLink{ +func MakeOrderedLink() OrderedOutbox { + return OrderedOutbox{ OutboxPackets: map[string][]Packet{}, OutboxAcks: map[string][]Ack{}, } } // AddPacket adds an outbound packet from the sender to the counterparty. -func (n OrderedLink) AddPacket(sender string, packet channeltypes.Packet) { +func (n OrderedOutbox) AddPacket(sender string, packet channeltypes.Packet) { n.OutboxPackets[sender] = append(n.OutboxPackets[sender], Packet{packet, 0}) } // AddAck adds an outbound ack, for future delivery to the sender of the packet // being acked. -func (n OrderedLink) AddAck(sender string, ack []byte, packet channeltypes.Packet) { +func (n OrderedOutbox) AddAck(sender string, ack []byte, packet channeltypes.Packet) { n.OutboxAcks[sender] = append(n.OutboxAcks[sender], Ack{ack, packet, 0}) } // ConsumePackets returns the first num packets with 2 or more commits. Returned // packets are removed from the outbox and will not be returned again (consumed). -func (n OrderedLink) ConsumePackets(sender string, num int) []Packet { +func (n OrderedOutbox) ConsumePackets(sender string, num int) []Packet { ret := []Packet{} sz := len(n.OutboxPackets[sender]) if sz < num { @@ -67,7 +67,7 @@ func (n OrderedLink) ConsumePackets(sender string, num int) []Packet { // ConsumerAcks returns the first num packets with 2 or more commits. Returned // acks are removed from the outbox and will not be returned again (consumed). -func (n OrderedLink) ConsumeAcks(sender string, num int) []Ack { +func (n OrderedOutbox) ConsumeAcks(sender string, num int) []Ack { ret := []Ack{} sz := len(n.OutboxAcks[sender]) if sz < num { @@ -92,7 +92,7 @@ func (n OrderedLink) ConsumeAcks(sender string, num int) []Ack { // - 1st commit is necessary for the packet to included in the block // - 2nd commit is necessary because in practice the ibc light client // needs to have block h + 1 to be able to verify the packet in block h. -func (n OrderedLink) Commit(sender string) { +func (n OrderedOutbox) Commit(sender string) { for i := range n.OutboxPackets[sender] { n.OutboxPackets[sender][i].Commits += 1 } diff --git a/testutil/simibc/relayed_path.go b/testutil/simibc/relayed_path.go index 804af4d247..5a7a1ca858 100644 --- a/testutil/simibc/relayed_path.go +++ b/testutil/simibc/relayed_path.go @@ -16,7 +16,7 @@ type RelayedPath struct { t *testing.T path *ibctesting.Path clientHeaders map[string][]*ibctmtypes.Header - Link OrderedLink + Link OrderedOutbox } // MakeRelayedPath returns an initialized RelayedPath From bab8d21cc0052545334b039cd3e445eaf51aaf65 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 12 Jan 2023 11:22:03 +0000 Subject: [PATCH 10/19] Adds doc.go --- testutil/simibc/doc.go | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 testutil/simibc/doc.go diff --git a/testutil/simibc/doc.go b/testutil/simibc/doc.go new file mode 100644 index 0000000000..b0a62559fd --- /dev/null +++ b/testutil/simibc/doc.go @@ -0,0 +1,4 @@ +/* +simibc is a collection of utilities wrapping the ibc-go testing framework which make is easier to write test scenarios involving precise orders of packet and ack delivery and calls to BeginBlock and EndBlock. +*/ +package simibc From ec9b28db2e8d209ef407759b1e5c90b2e050dbc1 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 12 Jan 2023 11:22:10 +0000 Subject: [PATCH 11/19] Bump OrderedOutbox --- testutil/simibc/ordered_link.go | 12 ++++++++++-- testutil/simibc/relayed_path.go | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/testutil/simibc/ordered_link.go b/testutil/simibc/ordered_link.go index bde0f1e12e..46a78a7140 100644 --- a/testutil/simibc/ordered_link.go +++ b/testutil/simibc/ordered_link.go @@ -22,13 +22,21 @@ type Packet struct { Commits int } +// OrderedOutbox is a collection of packets and acks that have been sent +// by different chains, but have not yet been delivered to their target. +// The methods take care of bookkeeping, making it easier to simulate +// a real relayed IBC connection. +// +// Each sent packet or ack can be added here. When a sufficient number of +// block commits have followed each sent packet or ack, they can be consumed: +// delivered to their target. type OrderedOutbox struct { OutboxPackets map[string][]Packet OutboxAcks map[string][]Ack } -// MakeOrderedLink creates a new empty network link. -func MakeOrderedLink() OrderedOutbox { +// MakeOrderedOutbox creates a new empty network link. +func MakeOrderedOutbox() OrderedOutbox { return OrderedOutbox{ OutboxPackets: map[string][]Packet{}, OutboxAcks: map[string][]Ack{}, diff --git a/testutil/simibc/relayed_path.go b/testutil/simibc/relayed_path.go index 5a7a1ca858..828361304a 100644 --- a/testutil/simibc/relayed_path.go +++ b/testutil/simibc/relayed_path.go @@ -25,7 +25,7 @@ func MakeRelayedPath(t *testing.T, path *ibctesting.Path) RelayedPath { t: t, clientHeaders: map[string][]*ibctmtypes.Header{}, path: path, - Link: MakeOrderedLink(), + Link: MakeOrderedOutbox(), } } From 62bf4d99982dd08f3f2764824680a72c9d991445 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 12 Jan 2023 11:24:19 +0000 Subject: [PATCH 12/19] Bump OrderedOutbox --- testutil/simibc/ordered_link.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/testutil/simibc/ordered_link.go b/testutil/simibc/ordered_link.go index 46a78a7140..63f1d428b2 100644 --- a/testutil/simibc/ordered_link.go +++ b/testutil/simibc/ordered_link.go @@ -30,12 +30,14 @@ type Packet struct { // Each sent packet or ack can be added here. When a sufficient number of // block commits have followed each sent packet or ack, they can be consumed: // delivered to their target. +// +// NOTE: OrderedOutbox may be used independently of the rest of simibc. type OrderedOutbox struct { OutboxPackets map[string][]Packet OutboxAcks map[string][]Ack } -// MakeOrderedOutbox creates a new empty network link. +// MakeOrderedOutbox creates a new empty OrderedOutbox. func MakeOrderedOutbox() OrderedOutbox { return OrderedOutbox{ OutboxPackets: map[string][]Packet{}, @@ -43,13 +45,12 @@ func MakeOrderedOutbox() OrderedOutbox { } } -// AddPacket adds an outbound packet from the sender to the counterparty. +// AddPacket adds an outbound packet from the sender. func (n OrderedOutbox) AddPacket(sender string, packet channeltypes.Packet) { n.OutboxPackets[sender] = append(n.OutboxPackets[sender], Packet{packet, 0}) } -// AddAck adds an outbound ack, for future delivery to the sender of the packet -// being acked. +// AddAck adds an outbound ack from the sender. The ack is a response to the packet. func (n OrderedOutbox) AddAck(sender string, ack []byte, packet channeltypes.Packet) { n.OutboxAcks[sender] = append(n.OutboxAcks[sender], Ack{ack, packet, 0}) } From 4a1f51f025d4efabb920f765e186593579959ff3 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 12 Jan 2023 11:25:54 +0000 Subject: [PATCH 13/19] bump ordered outbox --- testutil/simibc/{ordered_link.go => ordered_outbox.go} | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) rename testutil/simibc/{ordered_link.go => ordered_outbox.go} (94%) diff --git a/testutil/simibc/ordered_link.go b/testutil/simibc/ordered_outbox.go similarity index 94% rename from testutil/simibc/ordered_link.go rename to testutil/simibc/ordered_outbox.go index 63f1d428b2..b8eca12ddb 100644 --- a/testutil/simibc/ordered_link.go +++ b/testutil/simibc/ordered_outbox.go @@ -22,17 +22,19 @@ type Packet struct { Commits int } -// OrderedOutbox is a collection of packets and acks that have been sent +// OrderedOutbox is a collection of ORDERED packets and acks that have been sent // by different chains, but have not yet been delivered to their target. // The methods take care of bookkeeping, making it easier to simulate // a real relayed IBC connection. // // Each sent packet or ack can be added here. When a sufficient number of // block commits have followed each sent packet or ack, they can be consumed: -// delivered to their target. +// delivered to their target. Since the sequences are ordered, this is useful +// for testing ORDERED ibc channels. // // NOTE: OrderedOutbox may be used independently of the rest of simibc. type OrderedOutbox struct { + // An ordered sequence of packets OutboxPackets map[string][]Packet OutboxAcks map[string][]Ack } From 48e401701b2681abbd46a774d7dae619c3e64b83 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 12 Jan 2023 11:37:35 +0000 Subject: [PATCH 14/19] Bump ordered outbox --- testutil/simibc/ordered_outbox.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testutil/simibc/ordered_outbox.go b/testutil/simibc/ordered_outbox.go index b8eca12ddb..21aac3f6bf 100644 --- a/testutil/simibc/ordered_outbox.go +++ b/testutil/simibc/ordered_outbox.go @@ -32,7 +32,7 @@ type Packet struct { // delivered to their target. Since the sequences are ordered, this is useful // for testing ORDERED ibc channels. // -// NOTE: OrderedOutbox may be used independently of the rest of simibc. +// NOTE: OrderedOutbox MAY be used independently of the rest of simibc. type OrderedOutbox struct { // An ordered sequence of packets OutboxPackets map[string][]Packet From a590a5393f91b33163911a0e1df3c39f307bf40e Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 12 Jan 2023 11:37:39 +0000 Subject: [PATCH 15/19] Bump relay util --- testutil/simibc/relay_util.go | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/testutil/simibc/relay_util.go b/testutil/simibc/relay_util.go index a89f9f09b6..81c088c6a6 100644 --- a/testutil/simibc/relay_util.go +++ b/testutil/simibc/relay_util.go @@ -13,8 +13,13 @@ import ( tmtypes "github.com/tendermint/tendermint/types" ) -// UpdateReceiverClient is used to send a header to the receiving endpoint and update -// the client of the respective chain. +// UpdateReceiverClient DELIVERs a header to the receiving endpoint +// and update the respective client of the receiving chain. +// +// The header is a header of the sender chain. The receiver chain +// must have a client of the sender chain that it can update. +// +// NOTE: this function MAY be used independently of the rest of simibc. func UpdateReceiverClient(sender *ibctesting.Endpoint, receiver *ibctesting.Endpoint, header *ibctmtypes.Header) (err error) { header, err = constructTMHeader(receiver.Chain, header, sender.Chain, receiver.ClientID, clienttypes.ZeroHeight()) @@ -55,7 +60,12 @@ func UpdateReceiverClient(sender *ibctesting.Endpoint, receiver *ibctesting.Endp return nil } -// Try to receive a packet on receiver. Returns ack. +// TryRecvPacket will try once to DELIVER a packet from sender to receiver. If successful, +// it will return the acknowledgement bytes. +// +// The packet must be sent from the sender chain to the receiver chain, and the +// receiver chain must have a client for the sender chain which has been updated +// to a recent height of the sender chain so that it can verify the packet. func TryRecvPacket(sender *ibctesting.Endpoint, receiver *ibctesting.Endpoint, packet channeltypes.Packet) (ack []byte, err error) { packetKey := host.PacketCommitmentKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) proof, proofHeight := sender.Chain.QueryProof(packetKey) @@ -93,7 +103,12 @@ func TryRecvPacket(sender *ibctesting.Endpoint, receiver *ibctesting.Endpoint, p return ack, nil } -// Try to receive an ack on receiver. +// TryRecvAck will try once to DELIVER an ack from sender to receiver. +// +// The ack must have been sent from the sender to the receiver, in response +// to packet which was previously delivered from the receiver to the sender. +// The receiver chain must have a client for the sender chain which has been +// updated to a recent height of the sender chain so that it can verify the packet. func TryRecvAck(sender *ibctesting.Endpoint, receiver *ibctesting.Endpoint, packet channeltypes.Packet, ack []byte) (err error) { p := packet packetKey := host.PacketAcknowledgementKey(p.GetDestPort(), p.GetDestChannel(), p.GetSequence()) @@ -126,8 +141,7 @@ func TryRecvAck(sender *ibctesting.Endpoint, receiver *ibctesting.Endpoint, pack return nil } -// constructTMHeader will augment a valid 07-tendermint Header with data needed to update -// light client. +// constructTMHeader is a helper function func constructTMHeader(chain *ibctesting.TestChain, header *ibctmtypes.Header, counterparty *ibctesting.TestChain, clientID string, trustedHeight clienttypes.Height) (*ibctmtypes.Header, error) { // Relayer must query for LatestHeight on client to get TrustedHeight if the trusted height is not set if trustedHeight.IsZero() { From 79975b4c7fa06e1dc62eab01d68ab869ca506698 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 12 Jan 2023 11:44:40 +0000 Subject: [PATCH 16/19] Refactors relay --- tests/difference/core/driver/setup.go | 2 +- testutil/simibc/relay_util.go | 33 +++++++++++++-------------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/tests/difference/core/driver/setup.go b/tests/difference/core/driver/setup.go index e91f1dcead..ec74e60ee2 100644 --- a/tests/difference/core/driver/setup.go +++ b/tests/difference/core/driver/setup.go @@ -485,7 +485,7 @@ func (b *Builder) createConsumerGenesis(tmConfig *ibctesting.TendermintConfig) * } func (b *Builder) createLink() { - b.link = simibc.MakeOrderedLink() + b.link = simibc.MakeOrderedOutbox() // init utility data structures b.mustBeginBlock = map[string]bool{P: true, C: true} b.clientHeaders = map[string][]*ibctmtypes.Header{} diff --git a/testutil/simibc/relay_util.go b/testutil/simibc/relay_util.go index 81c088c6a6..478650e5c9 100644 --- a/testutil/simibc/relay_util.go +++ b/testutil/simibc/relay_util.go @@ -22,7 +22,7 @@ import ( // NOTE: this function MAY be used independently of the rest of simibc. func UpdateReceiverClient(sender *ibctesting.Endpoint, receiver *ibctesting.Endpoint, header *ibctmtypes.Header) (err error) { - header, err = constructTMHeader(receiver.Chain, header, sender.Chain, receiver.ClientID, clienttypes.ZeroHeight()) + err = augmentHeader(receiver.Chain, sender.Chain, receiver.ClientID, header) if err != nil { return err @@ -141,12 +141,12 @@ func TryRecvAck(sender *ibctesting.Endpoint, receiver *ibctesting.Endpoint, pack return nil } -// constructTMHeader is a helper function -func constructTMHeader(chain *ibctesting.TestChain, header *ibctmtypes.Header, counterparty *ibctesting.TestChain, clientID string, trustedHeight clienttypes.Height) (*ibctmtypes.Header, error) { - // Relayer must query for LatestHeight on client to get TrustedHeight if the trusted height is not set - if trustedHeight.IsZero() { - trustedHeight = chain.GetClientState(clientID).GetLatestHeight().(clienttypes.Height) - } +// augmentHeader is a helper that augments the header with the height and validators that are most recently trusted +// by the receiver chain. +func augmentHeader(receiver *ibctesting.TestChain, sender *ibctesting.TestChain, clientID string, header *ibctmtypes.Header) error { + + trustedHeight := receiver.GetClientState(clientID).GetLatestHeight().(clienttypes.Height) + var ( tmTrustedVals *tmtypes.ValidatorSet ok bool @@ -154,27 +154,26 @@ func constructTMHeader(chain *ibctesting.TestChain, header *ibctmtypes.Header, c // Once we get TrustedHeight from client, we must query the validators from the counterparty chain // If the LatestHeight == LastHeader.Height, then TrustedValidators are current validators // If LatestHeight < LastHeader.Height, we can query the historical validator set from HistoricalInfo - if trustedHeight == counterparty.LastHeader.GetHeight() { - tmTrustedVals = counterparty.Vals + if trustedHeight == sender.LastHeader.GetHeight() { + tmTrustedVals = sender.Vals } else { // NOTE: We need to get validators from counterparty at height: trustedHeight+1 // since the last trusted validators for a header at height h // is the NextValidators at h+1 committed to in header h by // NextValidatorsHash - tmTrustedVals, ok = counterparty.GetValsAtHeight(int64(trustedHeight.RevisionHeight + 1)) + tmTrustedVals, ok = sender.GetValsAtHeight(int64(trustedHeight.RevisionHeight + 1)) if !ok { - return nil, sdkerrors.Wrapf(ibctmtypes.ErrInvalidHeaderHeight, "could not retrieve trusted validators at trustedHeight: %d", trustedHeight) + return sdkerrors.Wrapf(ibctmtypes.ErrInvalidHeaderHeight, "could not retrieve trusted validators at trustedHeight: %d", trustedHeight) } } - // inject trusted fields into last header - // for now assume revision number is 0 - header.TrustedHeight = trustedHeight - trustedVals, err := tmTrustedVals.ToProto() if err != nil { - return nil, err + return err } + // inject trusted fields into last header + // for now assume revision number is 0 + header.TrustedHeight = trustedHeight header.TrustedValidators = trustedVals - return header, nil + return nil } From b8242f7c83128311a5aa6494e7ccfe28ed1d0bcb Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 12 Jan 2023 11:45:38 +0000 Subject: [PATCH 17/19] bump relay --- testutil/simibc/relay_util.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/testutil/simibc/relay_util.go b/testutil/simibc/relay_util.go index 478650e5c9..939f0db0e1 100644 --- a/testutil/simibc/relay_util.go +++ b/testutil/simibc/relay_util.go @@ -22,7 +22,7 @@ import ( // NOTE: this function MAY be used independently of the rest of simibc. func UpdateReceiverClient(sender *ibctesting.Endpoint, receiver *ibctesting.Endpoint, header *ibctmtypes.Header) (err error) { - err = augmentHeader(receiver.Chain, sender.Chain, receiver.ClientID, header) + err = augmentHeader(sender.Chain, receiver.Chain, receiver.ClientID, header) if err != nil { return err @@ -142,8 +142,8 @@ func TryRecvAck(sender *ibctesting.Endpoint, receiver *ibctesting.Endpoint, pack } // augmentHeader is a helper that augments the header with the height and validators that are most recently trusted -// by the receiver chain. -func augmentHeader(receiver *ibctesting.TestChain, sender *ibctesting.TestChain, clientID string, header *ibctmtypes.Header) error { +// by the receiver chain. If there is an error, the header will not be modified. +func augmentHeader(sender *ibctesting.TestChain, receiver *ibctesting.TestChain, clientID string, header *ibctmtypes.Header) error { trustedHeight := receiver.GetClientState(clientID).GetLatestHeight().(clienttypes.Height) From 95caf83bb85ce150113db08dce3073115bc5d4eb Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 12 Jan 2023 12:26:47 +0000 Subject: [PATCH 18/19] Bump many docstrings --- tests/difference/core/driver/core_test.go | 10 +- testutil/simibc/README.md | 2 +- testutil/simibc/ordered_outbox.go | 5 +- testutil/simibc/relayed_path.go | 112 ++++++++++++++-------- 4 files changed, 81 insertions(+), 48 deletions(-) diff --git a/tests/difference/core/driver/core_test.go b/tests/difference/core/driver/core_test.go index 25752bcf06..56672140d3 100644 --- a/tests/difference/core/driver/core_test.go +++ b/tests/difference/core/driver/core_test.go @@ -189,7 +189,7 @@ func (s *CoreSuite) consumerSlash(val sdk.ConsAddress, h int64, isDowntime bool) if e.Type == channeltypes.EventTypeSendPacket { packet, err := ibctestingcore.ReconstructPacketFromEvent(e) s.Require().NoError(err) - s.simibc.Link.AddPacket(s.chainID(C), packet) + s.simibc.Outboxes.AddPacket(s.chainID(C), packet) } } } @@ -422,10 +422,10 @@ func (s *CoreSuite) TestAssumptions() { s.Require().Equal(s.offsetHeight, s.height(C)-1) // Network is empty - s.Require().Empty(s.simibc.Link.OutboxPackets[P]) - s.Require().Empty(s.simibc.Link.OutboxPackets[C]) - s.Require().Empty(s.simibc.Link.OutboxAcks[P]) - s.Require().Empty(s.simibc.Link.OutboxAcks[C]) + s.Require().Empty(s.simibc.Outboxes.OutboxPackets[P]) + s.Require().Empty(s.simibc.Outboxes.OutboxPackets[C]) + s.Require().Empty(s.simibc.Outboxes.OutboxAcks[P]) + s.Require().Empty(s.simibc.Outboxes.OutboxAcks[C]) } // Test a set of traces diff --git a/testutil/simibc/README.md b/testutil/simibc/README.md index 6de634cf33..14d342ebd9 100644 --- a/testutil/simibc/README.md +++ b/testutil/simibc/README.md @@ -16,4 +16,4 @@ The utilities in simibc do not have any side effects, making it very easy to und ## How do I use this? -Please see the function docstrings and the example test to get an idea of how you could use this package. This README is intentionally short because it is easier to maintain code and docstrings instead of markdown. +Please see the function docstrings to get an idea of how you could use this package. This README is intentionally short because it is easier to maintain code and docstrings instead of markdown. diff --git a/testutil/simibc/ordered_outbox.go b/testutil/simibc/ordered_outbox.go index 21aac3f6bf..7a00e1b1be 100644 --- a/testutil/simibc/ordered_outbox.go +++ b/testutil/simibc/ordered_outbox.go @@ -34,9 +34,10 @@ type Packet struct { // // NOTE: OrderedOutbox MAY be used independently of the rest of simibc. type OrderedOutbox struct { - // An ordered sequence of packets + // An ordered sequence of packets from each sender OutboxPackets map[string][]Packet - OutboxAcks map[string][]Ack + // An ordered sequence of acks from each sender + OutboxAcks map[string][]Ack } // MakeOrderedOutbox creates a new empty OrderedOutbox. diff --git a/testutil/simibc/relayed_path.go b/testutil/simibc/relayed_path.go index 828361304a..8c41e4c3b8 100644 --- a/testutil/simibc/relayed_path.go +++ b/testutil/simibc/relayed_path.go @@ -9,27 +9,38 @@ import ( ibctesting "github.com/cosmos/interchain-security/legacy_ibc_testing/testing" ) -// RelayedPath augments ibctesting.Path, giving fine-grained control -// over delivery of client updates, packet and ack delivery, and -// chain time and height progression. +// RelayedPath is a wrapper around ibctesting.Path gives fine-grained +// control over delivery packets and acks, and client updates. Specifically, +// the path represents a bidirectional ORDERED channel between two chains. +// It is possible to control the precise order that packets and acks are +// delivered, and the precise independent and relative order and timing of +// new blocks on each chain. type RelayedPath struct { - t *testing.T - path *ibctesting.Path + t *testing.T + path *ibctesting.Path + // clientHeaders is a map from chainID to an ordered list of headers that + // have been committed to that chain. The headers are used to update the + // client of the counterparty chain. clientHeaders map[string][]*ibctmtypes.Header - Link OrderedOutbox + // TODO: Make this private and expose methods to add packets and acks. + // Currently, packets and acks are added directly to the outboxes, + // but we should hide this implementation detail. + Outboxes OrderedOutbox } -// MakeRelayedPath returns an initialized RelayedPath +// MakeRelayedPath returns an initialized RelayedPath without any +// packets, acks or headers. Requires a fully initialised path where +// the connection and any channel handshakes have been COMPLETED. func MakeRelayedPath(t *testing.T, path *ibctesting.Path) RelayedPath { return RelayedPath{ t: t, clientHeaders: map[string][]*ibctmtypes.Header{}, path: path, - Link: MakeOrderedOutbox(), + Outboxes: MakeOrderedOutbox(), } } -// Chain returns the chain with ChainID +// Chain returns the chain with chainID func (f *RelayedPath) Chain(chainID string) *ibctesting.TestChain { if f.path.EndpointA.Chain.ChainID == chainID { return f.path.EndpointA.Chain @@ -37,78 +48,99 @@ func (f *RelayedPath) Chain(chainID string) *ibctesting.TestChain { if f.path.EndpointB.Chain.ChainID == chainID { return f.path.EndpointB.Chain } - f.t.Fatal("chain") + f.t.Fatal("no chain found in relayed path with chainID: ", chainID) return nil } -// UpdateClient will update the chain light client with -// each header added to the counterparty chain since the last -// call. +// UpdateClient updates the chain with the latest sequence +// of available headers committed by the counterparty chain since +// the last call to UpdateClient (or all for the first call). func (f *RelayedPath) UpdateClient(chainID string) { - for _, header := range f.clientHeaders[f.other(chainID)] { - err := UpdateReceiverClient(f.endpoint(f.other(chainID)), f.endpoint(chainID), header) + for _, header := range f.clientHeaders[f.counterparty(chainID)] { + err := UpdateReceiverClient(f.endpoint(f.counterparty(chainID)), f.endpoint(chainID), header) if err != nil { - f.t.Fatal("UpdateClient") + f.t.Fatal("in relayed path could not update client of chain: ", chainID, " with header: ", header, " err: ", err) } } - f.clientHeaders[f.other(chainID)] = []*ibctmtypes.Header{} + f.clientHeaders[f.counterparty(chainID)] = []*ibctmtypes.Header{} } -// DeliverPackets delivers packets to chain -// A real relayer will relay packets from one chain to another chain -// in two steps. First it will observe sufficiently committed outbound -// packets on the sender chain. Second, it will submit transactions -// containing those packets to the receiver chain. -// This method simulates the second step: sufficiently committed -// packets that have been already added to the OrderedLink will be -// delivered. It is necessary to add outbound packets to the link -// separately. +// DeliverPackets delivers UP TO packets to the chain which have been +// sent to it by the counterparty chain and are ready to be delivered. +// +// A packet is ready to be delivered if the sender chain has progressed +// a sufficient number of blocks since the packet was sent. This is because +// all sent packets must be committed to block state before they can be queried. +// Additionally, in practice, light clients require a header (h+1) to deliver a +// packet sent in header h. +// +// In order to deliver packets, the chain must have an up-to-date client +// of the counterparty chain. Ie. UpdateClient should be called before this. func (f *RelayedPath) DeliverPackets(chainID string, num int) { - for _, p := range f.Link.ConsumePackets(f.other(chainID), num) { - ack, err := TryRecvPacket(f.endpoint(f.other(chainID)), f.endpoint(chainID), p.Packet) + for _, p := range f.Outboxes.ConsumePackets(f.counterparty(chainID), num) { + ack, err := TryRecvPacket(f.endpoint(f.counterparty(chainID)), f.endpoint(chainID), p.Packet) if err != nil { f.t.Fatal("deliver") } - f.Link.AddAck(chainID, ack, p.Packet) + f.Outboxes.AddAck(chainID, ack, p.Packet) } } -// DeliverAcks delivers acks to chain +// DeliverPackets delivers UP TO acks to the chain which have been +// sent to it by the counterparty chain and are ready to be delivered. +// +// An ack is ready to be delivered if the sender chain has progressed +// a sufficient number of blocks since the ack was sent. This is because +// all sent acks must be committed to block state before they can be queried. +// Additionally, in practice, light clients require a header (h+1) to deliver +// an ack sent in header h. +// +// In order to deliver acks, the chain must have an up-to-date client +// of the counterparty chain. Ie. UpdateClient should be called before this. func (f *RelayedPath) DeliverAcks(chainID string, num int) { - for _, ack := range f.Link.ConsumeAcks(f.other(chainID), num) { - err := TryRecvAck(f.endpoint(f.other(chainID)), f.endpoint(chainID), ack.Packet, ack.Ack) + for _, ack := range f.Outboxes.ConsumeAcks(f.counterparty(chainID), num) { + err := TryRecvAck(f.endpoint(f.counterparty(chainID)), f.endpoint(chainID), ack.Packet, ack.Ack) if err != nil { f.t.Fatal("deliverAcks") } } } -// EndAndBeginBlock calls EndBlock and commits block state, and then increments time and calls -// BeginBlock, for the chain. preCommitCallback is called after EndBlock and before Commit, -// allowing access to the sdk.Context after EndBlock. +// EndAndBeginBlock calls EndBlock and commits block state, storing the header which can later +// be used to update the client on the counterparty chain. After committing, the chain local +// time progresses by dt, and BeginBlock is called with a header timestamped for the new time. +// +// preCommitCallback is called after EndBlock and before Commit, allowing arbitrary access to +// the sdk.Context after EndBlock. The callback is useful for testing purposes to execute +// arbitrary code before the chain sdk context is cleared in .Commit(). +// For example, app.EndBlock may lead to a new state, which you would like to query +// to check that it is correct. However, the sdk context is cleared after .Commit(), +// so you can query the state inside the callback. func (f *RelayedPath) EndAndBeginBlock(chainID string, dt time.Duration, preCommitCallback func()) { c := f.Chain(chainID) header, packets := EndBlock(c, preCommitCallback) f.clientHeaders[chainID] = append(f.clientHeaders[chainID], header) for _, p := range packets { - f.Link.AddPacket(chainID, p) + f.Outboxes.AddPacket(chainID, p) } - f.Link.Commit(chainID) + f.Outboxes.Commit(chainID) BeginBlock(c, dt) } -func (f *RelayedPath) other(chainID string) string { +// counterparty is a helper returning the counterparty chainID +func (f *RelayedPath) counterparty(chainID string) string { if f.path.EndpointA.Chain.ChainID == chainID { return f.path.EndpointB.Chain.ChainID } if f.path.EndpointB.Chain.ChainID == chainID { return f.path.EndpointA.Chain.ChainID } - f.t.Fatal("other") + f.t.Fatal("no chain found in relayed path with chainID: ", chainID) return "" } +// endpoint is a helper returning the endpoint for the chain func (f *RelayedPath) endpoint(chainID string) *ibctesting.Endpoint { if chainID == f.path.EndpointA.Chain.ChainID { return f.path.EndpointA @@ -116,6 +148,6 @@ func (f *RelayedPath) endpoint(chainID string) *ibctesting.Endpoint { if chainID == f.path.EndpointB.Chain.ChainID { return f.path.EndpointB } - f.t.Fatal("endpoint") + f.t.Fatal("no chain found in relayed path with chainID: ", chainID) return nil } From a5b78e375e089773d65f75636e9c36dc0b3a1b9b Mon Sep 17 00:00:00 2001 From: Shawn Marshall-Spitzbart <44221603+smarshall-spitzbart@users.noreply.github.com> Date: Mon, 23 Jan 2023 10:11:30 -0800 Subject: [PATCH 19/19] fix ibc v3->v4 for new file --- testutil/simibc/ordered_outbox.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testutil/simibc/ordered_outbox.go b/testutil/simibc/ordered_outbox.go index 7a00e1b1be..a408b0b9cc 100644 --- a/testutil/simibc/ordered_outbox.go +++ b/testutil/simibc/ordered_outbox.go @@ -1,6 +1,6 @@ package simibc -import channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" +import channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" // Ack represents a (sent) ack committed to block state type Ack struct {