github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/wasm/relay_pingpong_test.go (about)

     1  package wasm_test
     2  
     3  //import (
     4  //	"encoding/json"
     5  //	"fmt"
     6  //	"testing"
     7  //
     8  //	ibctransfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types"
     9  //	ibctesting "github.com/cosmos/ibc-go/v3/testing"
    10  //
    11  //	wasmvm "github.com/CosmWasm/wasmvm"
    12  //	wasmvmtypes "github.com/CosmWasm/wasmvm/types"
    13  //	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/prefix"
    14  //	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    15  //	clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types"
    16  //	channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types"
    17  //	"github.com/stretchr/testify/assert"
    18  //	"github.com/stretchr/testify/require"
    19  //
    20  //	wasmibctesting "github.com/fibonacci-chain/fbc/x/wasm/ibctesting"
    21  //	wasmkeeper "github.com/fibonacci-chain/fbc/x/wasm/keeper"
    22  //	"github.com/fibonacci-chain/fbc/x/wasm/keeper/wasmtesting"
    23  //	wasmtypes "github.com/fibonacci-chain/fbc/x/wasm/types"
    24  //)
    25  //
    26  //const (
    27  //	ping = "ping"
    28  //	pong = "pong"
    29  //)
    30  //
    31  //var doNotTimeout = clienttypes.NewHeight(1, 1111111)
    32  //
    33  //func TestPinPong(t *testing.T) {
    34  //	// custom IBC protocol example
    35  //	// scenario: given two chains,
    36  //	//           with a contract on chain A and chain B
    37  //	//           when a ibc packet comes in, the contract responds with a new packet containing
    38  //	//	         either ping or pong
    39  //
    40  //	pingContract := &player{t: t, actor: ping}
    41  //	pongContract := &player{t: t, actor: pong}
    42  //
    43  //	var (
    44  //		chainAOpts = []wasmkeeper.Option{
    45  //			wasmkeeper.WithWasmEngine(
    46  //				wasmtesting.NewIBCContractMockWasmer(pingContract)),
    47  //		}
    48  //		chainBOpts = []wasmkeeper.Option{wasmkeeper.WithWasmEngine(
    49  //			wasmtesting.NewIBCContractMockWasmer(pongContract),
    50  //		)}
    51  //		coordinator = wasmibctesting.NewCoordinator(t, 2, chainAOpts, chainBOpts)
    52  //		chainA      = coordinator.GetChain(wasmibctesting.GetChainID(0))
    53  //		chainB      = coordinator.GetChain(wasmibctesting.GetChainID(1))
    54  //	)
    55  //	_ = chainB.SeedNewContractInstance() // skip 1 instance so that addresses are not the same
    56  //	var (
    57  //		pingContractAddr = chainA.SeedNewContractInstance()
    58  //		pongContractAddr = chainB.SeedNewContractInstance()
    59  //	)
    60  //	require.NotEqual(t, pingContractAddr, pongContractAddr)
    61  //	coordinator.CommitBlock(chainA, chainB)
    62  //
    63  //	pingContract.chain = chainA
    64  //	pingContract.contractAddr = pingContractAddr
    65  //
    66  //	pongContract.chain = chainB
    67  //	pongContract.contractAddr = pongContractAddr
    68  //
    69  //	var (
    70  //		sourcePortID       = wasmkeeper.PortIDForContract(pingContractAddr)
    71  //		counterpartyPortID = wasmkeeper.PortIDForContract(pongContractAddr)
    72  //	)
    73  //
    74  //	path := wasmibctesting.NewPath(chainA, chainB)
    75  //	path.EndpointA.ChannelConfig = &ibctesting.ChannelConfig{
    76  //		PortID:  sourcePortID,
    77  //		Version: ibctransfertypes.Version,
    78  //		Order:   channeltypes.ORDERED,
    79  //	}
    80  //	path.EndpointB.ChannelConfig = &ibctesting.ChannelConfig{
    81  //		PortID:  counterpartyPortID,
    82  //		Version: ibctransfertypes.Version,
    83  //		Order:   channeltypes.ORDERED,
    84  //	}
    85  //	coordinator.SetupConnections(path)
    86  //	coordinator.CreateChannels(path)
    87  //
    88  //	// trigger start game via execute
    89  //	const startValue uint64 = 100
    90  //	const rounds = 3
    91  //	s := startGame{
    92  //		ChannelID: path.EndpointA.ChannelID,
    93  //		Value:     startValue,
    94  //	}
    95  //	startMsg := &wasmtypes.MsgExecuteContract{
    96  //		Sender:   chainA.SenderAccount.GetAddress().String(),
    97  //		Contract: pingContractAddr.String(),
    98  //		Msg:      s.GetBytes(),
    99  //	}
   100  //	// on chain A
   101  //	_, err := path.EndpointA.Chain.SendMsgs(startMsg)
   102  //	require.NoError(t, err)
   103  //
   104  //	// when some rounds are played
   105  //	for i := 1; i <= rounds; i++ {
   106  //		t.Logf("++ round: %d\n", i)
   107  //
   108  //		require.Len(t, chainA.PendingSendPackets, 1)
   109  //		err := coordinator.RelayAndAckPendingPackets(path)
   110  //		require.NoError(t, err)
   111  //
   112  //		// switch side
   113  //		require.Len(t, chainB.PendingSendPackets, 1)
   114  //		err = coordinator.RelayAndAckPendingPackets(path.Invert())
   115  //		require.NoError(t, err)
   116  //	}
   117  //
   118  //	// then receive/response state is as expected
   119  //	assert.Equal(t, startValue+rounds, pingContract.QueryState(lastBallSentKey))
   120  //	assert.Equal(t, uint64(rounds), pingContract.QueryState(lastBallReceivedKey))
   121  //	assert.Equal(t, uint64(rounds+1), pingContract.QueryState(sentBallsCountKey))
   122  //	assert.Equal(t, uint64(rounds), pingContract.QueryState(receivedBallsCountKey))
   123  //	assert.Equal(t, uint64(rounds), pingContract.QueryState(confirmedBallsCountKey))
   124  //
   125  //	assert.Equal(t, uint64(rounds), pongContract.QueryState(lastBallSentKey))
   126  //	assert.Equal(t, startValue+rounds-1, pongContract.QueryState(lastBallReceivedKey))
   127  //	assert.Equal(t, uint64(rounds), pongContract.QueryState(sentBallsCountKey))
   128  //	assert.Equal(t, uint64(rounds), pongContract.QueryState(receivedBallsCountKey))
   129  //	assert.Equal(t, uint64(rounds), pongContract.QueryState(confirmedBallsCountKey))
   130  //}
   131  //
   132  //var _ wasmtesting.IBCContractCallbacks = &player{}
   133  //
   134  //// player is a (mock) contract that sends and receives ibc packages
   135  //type player struct {
   136  //	t            *testing.T
   137  //	chain        *wasmibctesting.TestChain
   138  //	contractAddr sdk.AccAddress
   139  //	actor        string // either ping or pong
   140  //	execCalls    int    // number of calls to Execute method (checkTx + deliverTx)
   141  //}
   142  //
   143  //// Execute starts the ping pong game
   144  //// Contracts finds all connected channels and broadcasts a ping message
   145  //func (p *player) Execute(code wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, executeMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) {
   146  //	p.execCalls++
   147  //	// start game
   148  //	var start startGame
   149  //	if err := json.Unmarshal(executeMsg, &start); err != nil {
   150  //		return nil, 0, err
   151  //	}
   152  //
   153  //	if start.MaxValue != 0 {
   154  //		store.Set(maxValueKey, sdk.Uint64ToBigEndian(start.MaxValue))
   155  //	}
   156  //	service := NewHit(p.actor, start.Value)
   157  //	p.t.Logf("[%s] starting game with: %d: %v\n", p.actor, start.Value, service)
   158  //
   159  //	p.incrementCounter(sentBallsCountKey, store)
   160  //	store.Set(lastBallSentKey, sdk.Uint64ToBigEndian(start.Value))
   161  //	return &wasmvmtypes.Response{
   162  //		Messages: []wasmvmtypes.SubMsg{
   163  //			{
   164  //				Msg: wasmvmtypes.CosmosMsg{
   165  //					IBC: &wasmvmtypes.IBCMsg{
   166  //						SendPacket: &wasmvmtypes.SendPacketMsg{
   167  //							ChannelID: start.ChannelID,
   168  //							Data:      service.GetBytes(),
   169  //							Timeout: wasmvmtypes.IBCTimeout{Block: &wasmvmtypes.IBCTimeoutBlock{
   170  //								Revision: doNotTimeout.RevisionNumber,
   171  //								Height:   doNotTimeout.RevisionHeight,
   172  //							}},
   173  //						},
   174  //					},
   175  //				},
   176  //				ReplyOn: wasmvmtypes.ReplyNever,
   177  //			},
   178  //		},
   179  //	}, 0, nil
   180  //}
   181  //
   182  //// OnIBCChannelOpen ensures to accept only configured version
   183  //func (p player) IBCChannelOpen(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelOpenMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBC3ChannelOpenResponse, uint64, error) {
   184  //	if msg.GetChannel().Version != p.actor {
   185  //		return &wasmvmtypes.IBC3ChannelOpenResponse{}, 0, nil
   186  //	}
   187  //	return &wasmvmtypes.IBC3ChannelOpenResponse{}, 0, nil
   188  //}
   189  //
   190  //// OnIBCChannelConnect persists connection endpoints
   191  //func (p player) IBCChannelConnect(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelConnectMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) {
   192  //	p.storeEndpoint(store, msg.GetChannel())
   193  //	return &wasmvmtypes.IBCBasicResponse{}, 0, nil
   194  //}
   195  //
   196  //// connectedChannelsModel is a simple persistence model to store endpoint addresses within the contract's store
   197  //type connectedChannelsModel struct {
   198  //	Our   wasmvmtypes.IBCEndpoint
   199  //	Their wasmvmtypes.IBCEndpoint
   200  //}
   201  //
   202  //var ( // store keys
   203  //	ibcEndpointsKey = []byte("ibc-endpoints")
   204  //	maxValueKey     = []byte("max-value")
   205  //)
   206  //
   207  //func (p player) loadEndpoints(store prefix.Store, channelID string) *connectedChannelsModel {
   208  //	var counterparties []connectedChannelsModel
   209  //	if bz := store.Get(ibcEndpointsKey); bz != nil {
   210  //		require.NoError(p.t, json.Unmarshal(bz, &counterparties))
   211  //	}
   212  //	for _, v := range counterparties {
   213  //		if v.Our.ChannelID == channelID {
   214  //			return &v
   215  //		}
   216  //	}
   217  //	p.t.Fatalf("no counterparty found for channel %q", channelID)
   218  //	return nil
   219  //}
   220  //
   221  //func (p player) storeEndpoint(store wasmvm.KVStore, channel wasmvmtypes.IBCChannel) {
   222  //	var counterparties []connectedChannelsModel
   223  //	if b := store.Get(ibcEndpointsKey); b != nil {
   224  //		require.NoError(p.t, json.Unmarshal(b, &counterparties))
   225  //	}
   226  //	counterparties = append(counterparties, connectedChannelsModel{Our: channel.Endpoint, Their: channel.CounterpartyEndpoint})
   227  //	bz, err := json.Marshal(&counterparties)
   228  //	require.NoError(p.t, err)
   229  //	store.Set(ibcEndpointsKey, bz)
   230  //}
   231  //
   232  //func (p player) IBCChannelClose(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelCloseMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) {
   233  //	panic("implement me")
   234  //}
   235  //
   236  //var ( // store keys
   237  //	lastBallSentKey        = []byte("lastBallSent")
   238  //	lastBallReceivedKey    = []byte("lastBallReceived")
   239  //	sentBallsCountKey      = []byte("sentBalls")
   240  //	receivedBallsCountKey  = []byte("recvBalls")
   241  //	confirmedBallsCountKey = []byte("confBalls")
   242  //)
   243  //
   244  //// IBCPacketReceive receives the hit and serves a response hit via `wasmvmtypes.IBCPacket`
   245  //func (p player) IBCPacketReceive(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketReceiveMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCReceiveResult, uint64, error) {
   246  //	// parse received data and store
   247  //	packet := msg.Packet
   248  //	var receivedBall hit
   249  //	if err := json.Unmarshal(packet.Data, &receivedBall); err != nil {
   250  //		return &wasmvmtypes.IBCReceiveResult{
   251  //			Ok: &wasmvmtypes.IBCReceiveResponse{
   252  //				Acknowledgement: hitAcknowledgement{Error: err.Error()}.GetBytes(),
   253  //			},
   254  //			// no hit msg, we stop the game
   255  //		}, 0, nil
   256  //	}
   257  //	p.incrementCounter(receivedBallsCountKey, store)
   258  //
   259  //	otherCount := receivedBall[counterParty(p.actor)]
   260  //	store.Set(lastBallReceivedKey, sdk.Uint64ToBigEndian(otherCount))
   261  //
   262  //	if maxVal := store.Get(maxValueKey); maxVal != nil && otherCount > sdk.BigEndianToUint64(maxVal) {
   263  //		errMsg := fmt.Sprintf("max value exceeded: %d got %d", sdk.BigEndianToUint64(maxVal), otherCount)
   264  //		return &wasmvmtypes.IBCReceiveResult{Ok: &wasmvmtypes.IBCReceiveResponse{
   265  //			Acknowledgement: receivedBall.BuildError(errMsg).GetBytes(),
   266  //		}}, 0, nil
   267  //	}
   268  //
   269  //	nextValue := p.incrementCounter(lastBallSentKey, store)
   270  //	newHit := NewHit(p.actor, nextValue)
   271  //	respHit := &wasmvmtypes.IBCMsg{SendPacket: &wasmvmtypes.SendPacketMsg{
   272  //		ChannelID: packet.Src.ChannelID,
   273  //		Data:      newHit.GetBytes(),
   274  //		Timeout: wasmvmtypes.IBCTimeout{Block: &wasmvmtypes.IBCTimeoutBlock{
   275  //			Revision: doNotTimeout.RevisionNumber,
   276  //			Height:   doNotTimeout.RevisionHeight,
   277  //		}},
   278  //	}}
   279  //	p.incrementCounter(sentBallsCountKey, store)
   280  //	p.t.Logf("[%s] received %d, returning %d: %v\n", p.actor, otherCount, nextValue, newHit)
   281  //
   282  //	return &wasmvmtypes.IBCReceiveResult{
   283  //		Ok: &wasmvmtypes.IBCReceiveResponse{
   284  //			Acknowledgement: receivedBall.BuildAck().GetBytes(),
   285  //			Messages:        []wasmvmtypes.SubMsg{{Msg: wasmvmtypes.CosmosMsg{IBC: respHit}, ReplyOn: wasmvmtypes.ReplyNever}},
   286  //		},
   287  //	}, 0, nil
   288  //}
   289  //
   290  //// OnIBCPacketAcknowledgement handles the packet acknowledgment frame. Stops the game on an any error
   291  //func (p player) IBCPacketAck(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketAckMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) {
   292  //	// parse received data and store
   293  //	var sentBall hit
   294  //	if err := json.Unmarshal(msg.OriginalPacket.Data, &sentBall); err != nil {
   295  //		return nil, 0, err
   296  //	}
   297  //
   298  //	var ack hitAcknowledgement
   299  //	if err := json.Unmarshal(msg.Acknowledgement.Data, &ack); err != nil {
   300  //		return nil, 0, err
   301  //	}
   302  //	if ack.Success != nil {
   303  //		confirmedCount := sentBall[p.actor]
   304  //		p.t.Logf("[%s] acknowledged %d: %v\n", p.actor, confirmedCount, sentBall)
   305  //	} else {
   306  //		p.t.Logf("[%s] received app layer error: %s\n", p.actor, ack.Error)
   307  //	}
   308  //
   309  //	p.incrementCounter(confirmedBallsCountKey, store)
   310  //	return &wasmvmtypes.IBCBasicResponse{}, 0, nil
   311  //}
   312  //
   313  //func (p player) IBCPacketTimeout(codeID wasmvm.Checksum, env wasmvmtypes.Env, packet wasmvmtypes.IBCPacketTimeoutMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) {
   314  //	panic("implement me")
   315  //}
   316  //
   317  //func (p player) incrementCounter(key []byte, store wasmvm.KVStore) uint64 {
   318  //	var count uint64
   319  //	bz := store.Get(key)
   320  //	if bz != nil {
   321  //		count = sdk.BigEndianToUint64(bz)
   322  //	}
   323  //	count++
   324  //	store.Set(key, sdk.Uint64ToBigEndian(count))
   325  //	return count
   326  //}
   327  //
   328  //func (p player) QueryState(key []byte) uint64 {
   329  //	raw := p.chain.GetTestSupport().WasmKeeper().QueryRaw(p.chain.GetContext(), p.contractAddr, key)
   330  //	return sdk.BigEndianToUint64(raw)
   331  //}
   332  //
   333  //func counterParty(s string) string {
   334  //	switch s {
   335  //	case ping:
   336  //		return pong
   337  //	case pong:
   338  //		return ping
   339  //	default:
   340  //		panic(fmt.Sprintf("unsupported: %q", s))
   341  //	}
   342  //}
   343  //
   344  //// hit is ibc packet payload
   345  //type hit map[string]uint64
   346  //
   347  //func NewHit(player string, count uint64) hit {
   348  //	return map[string]uint64{
   349  //		player: count,
   350  //	}
   351  //}
   352  //
   353  //func (h hit) GetBytes() []byte {
   354  //	b, err := json.Marshal(h)
   355  //	if err != nil {
   356  //		panic(err)
   357  //	}
   358  //	return b
   359  //}
   360  //
   361  //func (h hit) String() string {
   362  //	return fmt.Sprintf("Ball %s", string(h.GetBytes()))
   363  //}
   364  //
   365  //func (h hit) BuildAck() hitAcknowledgement {
   366  //	return hitAcknowledgement{Success: &h}
   367  //}
   368  //
   369  //func (h hit) BuildError(errMsg string) hitAcknowledgement {
   370  //	return hitAcknowledgement{Error: errMsg}
   371  //}
   372  //
   373  //// hitAcknowledgement is ibc acknowledgment payload
   374  //type hitAcknowledgement struct {
   375  //	Error   string `json:"error,omitempty"`
   376  //	Success *hit   `json:"success,omitempty"`
   377  //}
   378  //
   379  //func (a hitAcknowledgement) GetBytes() []byte {
   380  //	b, err := json.Marshal(a)
   381  //	if err != nil {
   382  //		panic(err)
   383  //	}
   384  //	return b
   385  //}
   386  //
   387  //// startGame is an execute message payload
   388  //type startGame struct {
   389  //	ChannelID string
   390  //	Value     uint64
   391  //	// limit above the game is aborted
   392  //	MaxValue uint64 `json:"max_value,omitempty"`
   393  //}
   394  //
   395  //func (g startGame) GetBytes() wasmtypes.RawContractMessage {
   396  //	b, err := json.Marshal(g)
   397  //	if err != nil {
   398  //		panic(err)
   399  //	}
   400  //	return b
   401  //}