simibc

package
v2.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 16, 2023 License: Apache-2.0 Imports: 15 Imported by: 0

README

simibc

What is this?

A collection of utilities based on ibc-go/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 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 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.

Documentation

Overview

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.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BeginBlock

func BeginBlock(c *ibctesting.TestChain, dt time.Duration)

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 EndBlock

func EndBlock(c *ibctesting.TestChain, preCommitCallback func()) (*ibctmtypes.Header, []channeltypes.Packet)

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.

NOTE: this method may be used independently of the rest of simibc.

func TryRecvAck

func TryRecvAck(sender *ibctesting.Endpoint, receiver *ibctesting.Endpoint, packet channeltypes.Packet, ack []byte) (err error)

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 TryRecvPacket

func TryRecvPacket(sender *ibctesting.Endpoint, receiver *ibctesting.Endpoint, packet channeltypes.Packet) (ack []byte, err error)

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 UpdateReceiverClient

func UpdateReceiverClient(sender *ibctesting.Endpoint, receiver *ibctesting.Endpoint, header *ibctmtypes.Header) (err error)

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.

Types

type Ack

type Ack struct {
	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
}

Ack represents a (sent) ack committed to block state

type OrderedOutbox

type OrderedOutbox struct {
	// An ordered sequence of packets from each sender
	OutboxPackets map[string][]Packet
	// An ordered sequence of acks from each sender
	OutboxAcks map[string][]Ack
}

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. Since the sequences are ordered, this is useful for testing ORDERED ibc channels.

NOTE: OrderedOutbox MAY be used independently of the rest of simibc.

func MakeOrderedOutbox

func MakeOrderedOutbox() OrderedOutbox

MakeOrderedOutbox creates a new empty OrderedOutbox.

func (OrderedOutbox) AddAck

func (n OrderedOutbox) AddAck(sender string, ack []byte, packet channeltypes.Packet)

AddAck adds an outbound ack from the sender. The ack is a response to the packet.

func (OrderedOutbox) AddPacket

func (n OrderedOutbox) AddPacket(sender string, packet channeltypes.Packet)

AddPacket adds an outbound packet from the sender.

func (OrderedOutbox) Commit

func (n OrderedOutbox) 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 (OrderedOutbox) ConsumeAcks

func (n OrderedOutbox) 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 (OrderedOutbox) ConsumePackets

func (n OrderedOutbox) 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).

type Packet

type Packet struct {
	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
}

Packet represents a (sent) packet committed to block state

type RelayedPath

type RelayedPath struct {

	// 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
	// contains filtered or unexported fields
}

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.

func MakeRelayedPath

func MakeRelayedPath(t *testing.T, path *ibctesting.Path) 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 (*RelayedPath) Chain

func (f *RelayedPath) Chain(chainID string) *ibctesting.TestChain

Chain returns the chain with chainID

func (*RelayedPath) DeliverAcks

func (f *RelayedPath) DeliverAcks(chainID string, num int)

DeliverPackets delivers UP TO <num> 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 (*RelayedPath) DeliverPackets

func (f *RelayedPath) DeliverPackets(chainID string, num int)

DeliverPackets delivers UP TO <num> 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 (*RelayedPath) EndAndBeginBlock

func (f *RelayedPath) EndAndBeginBlock(chainID string, dt time.Duration, preCommitCallback func())

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 (*RelayedPath) UpdateClient

func (f *RelayedPath) UpdateClient(chainID string)

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).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL