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 //}