ibc_receiver_test.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. package ictest
  2. import (
  3. "context"
  4. "encoding/base64"
  5. "encoding/json"
  6. "fmt"
  7. "strconv"
  8. "testing"
  9. "github.com/docker/docker/client"
  10. "github.com/strangelove-ventures/interchaintest/v4"
  11. "github.com/strangelove-ventures/interchaintest/v4/chain/cosmos"
  12. "github.com/strangelove-ventures/interchaintest/v4/chain/cosmos/wasm"
  13. "github.com/strangelove-ventures/interchaintest/v4/ibc"
  14. "github.com/strangelove-ventures/interchaintest/v4/relayer"
  15. "github.com/strangelove-ventures/interchaintest/v4/testreporter"
  16. "github.com/strangelove-ventures/interchaintest/v4/testutil"
  17. "go.uber.org/zap/zaptest"
  18. "github.com/stretchr/testify/require"
  19. "github.com/wormhole-foundation/wormchain/interchaintest/guardians"
  20. "github.com/wormhole-foundation/wormchain/interchaintest/helpers"
  21. "github.com/wormhole-foundation/wormchain/interchaintest/helpers/wormchain_ibc_receiver"
  22. "github.com/wormhole-foundation/wormchain/interchaintest/helpers/wormhole_ibc"
  23. "github.com/wormhole-foundation/wormhole/sdk/vaa"
  24. )
  25. const CUSTOM_IBC_VERSION string = "ibc-wormhole-v1"
  26. func createChains(t *testing.T, wormchainVersion string, guardians guardians.ValSet) []ibc.Chain {
  27. numWormchainVals := len(guardians.Vals)
  28. wormchainConfig.Images[0].Version = wormchainVersion
  29. // Create chain factory with wormchain
  30. wormchainConfig.ModifyGenesis = ModifyGenesis(votingPeriod, maxDepositPeriod, guardians, false)
  31. cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{
  32. {
  33. ChainName: "wormchain",
  34. ChainConfig: wormchainConfig,
  35. NumValidators: &numWormchainVals,
  36. NumFullNodes: &numFullNodes,
  37. },
  38. {
  39. Name: "osmosis",
  40. Version: "v15.1.2",
  41. ChainConfig: ibc.ChainConfig{
  42. ChainID: "osmosis-1002", // hardcoded handling in osmosis binary for osmosis-1, so need to override to something different.
  43. GasPrices: "1.0uosmo",
  44. EncodingConfig: wasm.WasmEncoding(),
  45. },
  46. },
  47. })
  48. // Get chains from the chain factory
  49. chains, err := cf.Chains(t.Name())
  50. require.NoError(t, err)
  51. return chains
  52. }
  53. func buildInterchain(t *testing.T, chains []ibc.Chain) (context.Context, ibc.Relayer, *testreporter.RelayerExecReporter, *client.Client) {
  54. // Create a new Interchain object which describes the chains, relayers, and IBC connections we want to use
  55. ic := interchaintest.NewInterchain()
  56. for _, chain := range chains {
  57. ic.AddChain(chain)
  58. }
  59. rep := testreporter.NewNopReporter()
  60. eRep := rep.RelayerExecReporter(t)
  61. wormOsmoPath := "wormosmo"
  62. ctx := context.Background()
  63. client, network := interchaintest.DockerSetup(t)
  64. r := interchaintest.NewBuiltinRelayerFactory(ibc.CosmosRly, zaptest.NewLogger(t),
  65. relayer.StartupFlags("-b", "100"),
  66. relayer.CustomDockerImage("ghcr.io/cosmos/relayer", "v2.5.2", "100:1000")).Build(
  67. t, client, network)
  68. ic.AddRelayer(r, "relayer")
  69. ic.AddLink(interchaintest.InterchainLink{
  70. Chain1: chains[1], // Osmosis
  71. Chain2: chains[0], // Wormchain
  72. Relayer: r,
  73. Path: wormOsmoPath,
  74. })
  75. err := ic.Build(ctx, eRep, interchaintest.InterchainBuildOptions{
  76. TestName: t.Name(),
  77. Client: client,
  78. NetworkID: network,
  79. SkipPathCreation: false,
  80. BlockDatabaseFile: interchaintest.DefaultBlockDatabaseFilepath(),
  81. })
  82. require.NoError(t, err)
  83. t.Cleanup(func() {
  84. _ = ic.Close()
  85. })
  86. // Start the relayer
  87. err = r.StartRelayer(ctx, eRep, wormOsmoPath)
  88. require.NoError(t, err)
  89. //interchaintest.TempDir(sui)
  90. t.Cleanup(
  91. func() {
  92. err := r.StopRelayer(ctx, eRep)
  93. if err != nil {
  94. t.Logf("an error occured while stopping the relayer: %s", err)
  95. }
  96. },
  97. )
  98. return ctx, r, eRep, client
  99. }
  100. func TestIbcReceiverHappyPath(t *testing.T) {
  101. // Base setup
  102. numVals := 2
  103. guardians := guardians.CreateValSet(t, numVals)
  104. chains := createChains(t, "v2.24.2", *guardians)
  105. ctx, r, eRep, _ := buildInterchain(t, chains)
  106. // Chains
  107. wormchain := chains[0].(*cosmos.CosmosChain)
  108. osmosis := chains[1].(*cosmos.CosmosChain)
  109. // Instantiate the wormchain-ibc-receiver and wormhole-ibc contracts
  110. wormchainReceiverContractInfo, osmosisSenderContractInfo := instantiateWormholeIbcContracts(t, ctx, wormchain, osmosis, guardians)
  111. // Spin up a new channel for the contracts to communicate over (this new channel will need to be whitelisted on the wormhole-ibc contract)
  112. err := r.LinkPath(ctx, eRep, "wormosmo", ibc.CreateChannelOptions{
  113. SourcePortName: osmosisSenderContractInfo.ContractInfo.IbcPortID,
  114. DestPortName: wormchainReceiverContractInfo.ContractInfo.IbcPortID,
  115. Order: ibc.Unordered,
  116. Version: CUSTOM_IBC_VERSION,
  117. }, ibc.CreateClientOptions{
  118. TrustingPeriod: "112h",
  119. })
  120. require.NoError(t, err)
  121. err = r.StopRelayer(ctx, eRep)
  122. require.NoError(t, err)
  123. err = r.StartRelayer(ctx, eRep, "wormosmo")
  124. require.NoError(t, err)
  125. // Get the new wormchain channel to receive messages from the osmosis contract
  126. wormholeChannelId := helpers.FindOpenChannelByVersion(t, ctx, eRep, r, wormchain, CUSTOM_IBC_VERSION).ChannelID
  127. // This is the channel we will send packets on from to wormhole from osmosis ibc contract
  128. osmosisChannelId := helpers.FindOpenChannelByVersion(t, ctx, eRep, r, osmosis, CUSTOM_IBC_VERSION).ChannelID
  129. // Add the new channel to the wormchain-ibc-receiver contract
  130. upgradeChainChannelVaa := wormchain_ibc_receiver.SubmitIbcReceiverUpdateChannelChainMsg(t,
  131. vaa.ChainID(OsmoChainID), wormholeChannelId,
  132. guardians)
  133. _, err = wormchain.ExecuteContract(ctx, "faucet", wormchainReceiverContractInfo.Address, upgradeChainChannelVaa)
  134. require.NoError(t, err)
  135. // Add the new channel to the osmosis wormhole-ibc contract
  136. upgradeChainChannelVaa = wormhole_ibc.SubmitWormholeIbcUpdateChannelChainMsg(t,
  137. vaa.ChainID(vaa.ChainIDWormchain), osmosisChannelId,
  138. guardians)
  139. _, err = osmosis.ExecuteContract(ctx, "faucet", osmosisSenderContractInfo.Address, upgradeChainChannelVaa)
  140. require.NoError(t, err)
  141. // Send a VAA from osmosis to wormhole
  142. postMessage := wormhole_ibc.ExecuteMsg{
  143. SubmitVAA: nil,
  144. PostMessage: &wormhole_ibc.ExecuteMsg_PostMessage{
  145. Message: wormhole_ibc.Binary(base64.StdEncoding.EncodeToString([]byte("080000000901007bfa71192f886ab6819fa4862e34b4d178962958d9b2e3d9437338c9e5fde1443b809d2886eaa69e0f0158ea517675d96243c9209c3fe1d94d5b19866654c6980000000b150000000500020001020304000000000000000000000000000000000000000000000000000000000000000000000a0261626364"))),
  146. Nonce: 0,
  147. },
  148. SubmitUpdateChannelChain: nil,
  149. }
  150. postMessageJson, err := json.Marshal(postMessage)
  151. require.NoError(t, err)
  152. postMessageTxHash, err := osmosis.ExecuteContract(ctx, "faucet", osmosisSenderContractInfo.Address,
  153. string(postMessageJson))
  154. require.NoError(t, err, "failed to execute wormhole-ibc post message")
  155. ibcTx, err := helpers.GetIBCTx(osmosis, postMessageTxHash)
  156. require.NoError(t, err, "failed to get ibc tx")
  157. // Poll for the receiver acknowledgement so that we can see if the packet was processed successfully
  158. osmosisAck, err := testutil.PollForAck(ctx, osmosis, ibcTx.Height, ibcTx.Height+10, ibcTx.Packet)
  159. require.NoError(t, err, "failed to poll for acknowledgement")
  160. var parsedAck wormchain_ibc_receiver.ReceiverAck
  161. err = json.Unmarshal(osmosisAck.Acknowledgement, &parsedAck)
  162. require.NoError(t, err, "failed to unmarshal acknowledgement")
  163. require.True(t, parsedAck.IsOk(), "receiver acknowledgement should be ok to signify that it was processed successfully")
  164. }
  165. func TestIbcReceiverWithoutReceiverWhitelist(t *testing.T) {
  166. // Base setup
  167. numVals := 2
  168. guardians := guardians.CreateValSet(t, numVals)
  169. chains := createChains(t, "v2.24.2", *guardians)
  170. ctx, r, eRep, _ := buildInterchain(t, chains)
  171. // Chains
  172. wormchain := chains[0].(*cosmos.CosmosChain)
  173. osmosis := chains[1].(*cosmos.CosmosChain)
  174. // Instantiate the wormchain-ibc-receiver and wormhole-ibc contracts
  175. wormchainReceiverContractInfo, osmosisSenderContractInfo := instantiateWormholeIbcContracts(t, ctx, wormchain, osmosis, guardians)
  176. // Spin up a new channel for the contracts to communicate over (this new channel will need to be whitelisted on the wormhole-ibc contract)
  177. err := r.LinkPath(ctx, eRep, "wormosmo", ibc.CreateChannelOptions{
  178. SourcePortName: osmosisSenderContractInfo.ContractInfo.IbcPortID,
  179. DestPortName: wormchainReceiverContractInfo.ContractInfo.IbcPortID,
  180. Order: ibc.Unordered,
  181. Version: CUSTOM_IBC_VERSION,
  182. }, ibc.CreateClientOptions{
  183. TrustingPeriod: "112h",
  184. })
  185. require.NoError(t, err)
  186. err = r.StopRelayer(ctx, eRep)
  187. require.NoError(t, err)
  188. err = r.StartRelayer(ctx, eRep, "wormosmo")
  189. require.NoError(t, err)
  190. // This is the channel we will send packets on from Osmosis to wormhole from the osmosis ibc contract
  191. osmosisChannelId := helpers.FindOpenChannelByVersion(t, ctx, eRep, r, osmosis, CUSTOM_IBC_VERSION).ChannelID
  192. // SKIP UPGRADING THE WORMCHAIN IBC RECEIVER CONTRACT TO TEST THAT THE POST MESSAGE STILL COMPLETES
  193. // Add the new channel to the osmosis wormhole-ibc contract
  194. upgradeChainChannelVaa := wormhole_ibc.SubmitWormholeIbcUpdateChannelChainMsg(t,
  195. vaa.ChainID(vaa.ChainIDWormchain), osmosisChannelId,
  196. guardians)
  197. _, err = osmosis.ExecuteContract(ctx, "faucet", osmosisSenderContractInfo.Address, upgradeChainChannelVaa)
  198. require.NoError(t, err)
  199. // Send a VAA from osmosis to wormhole
  200. postMessage := wormhole_ibc.ExecuteMsg{
  201. SubmitVAA: nil,
  202. PostMessage: &wormhole_ibc.ExecuteMsg_PostMessage{
  203. Message: wormhole_ibc.Binary(base64.StdEncoding.EncodeToString([]byte("080000000901007bfa71192f886ab6819fa4862e34b4d178962958d9b2e3d9437338c9e5fde1443b809d2886eaa69e0f0158ea517675d96243c9209c3fe1d94d5b19866654c6980000000b150000000500020001020304000000000000000000000000000000000000000000000000000000000000000000000a0261626364"))),
  204. Nonce: 0,
  205. },
  206. SubmitUpdateChannelChain: nil,
  207. }
  208. postMessageJson, err := json.Marshal(postMessage)
  209. require.NoError(t, err)
  210. postMessageTxHash, err := osmosis.ExecuteContract(ctx, "faucet", osmosisSenderContractInfo.Address,
  211. string(postMessageJson))
  212. require.NoError(t, err)
  213. ibcTx, err := helpers.GetIBCTx(osmosis, postMessageTxHash)
  214. require.NoError(t, err)
  215. // Poll for the receiver acknowledgement so that we can see if the packet was processed successfully
  216. osmosisAck, err := testutil.PollForAck(ctx, osmosis, ibcTx.Height, ibcTx.Height+10, ibcTx.Packet)
  217. require.NoError(t, err)
  218. var parsedAck wormchain_ibc_receiver.ReceiverAck
  219. err = json.Unmarshal(osmosisAck.Acknowledgement, &parsedAck)
  220. require.NoError(t, err)
  221. require.True(t, parsedAck.IsOk(), "receiver acknowledgement should be ok to signify that it was processed successfully")
  222. }
  223. func TestIbcReceiverWormholeIbcState(t *testing.T) {
  224. // Base setup
  225. numVals := 2
  226. guardians := guardians.CreateValSet(t, numVals)
  227. chains := createChains(t, "v2.24.2", *guardians)
  228. ctx, r, eRep, _ := buildInterchain(t, chains)
  229. // Chains
  230. wormchain := chains[0].(*cosmos.CosmosChain)
  231. osmosis := chains[1].(*cosmos.CosmosChain)
  232. // Instantiate the wormchain-ibc-receiver and wormhole-ibc contracts
  233. wormchainReceiverContractInfo, osmosisSenderContractInfo := instantiateWormholeIbcContracts(t, ctx, wormchain, osmosis, guardians)
  234. // Spin up a new channel for the contracts to communicate over (this new channel will need to be whitelisted on the wormhole-ibc contract)
  235. err := r.LinkPath(ctx, eRep, "wormosmo", ibc.CreateChannelOptions{
  236. SourcePortName: osmosisSenderContractInfo.ContractInfo.IbcPortID,
  237. DestPortName: wormchainReceiverContractInfo.ContractInfo.IbcPortID,
  238. Order: ibc.Unordered,
  239. Version: CUSTOM_IBC_VERSION,
  240. }, ibc.CreateClientOptions{
  241. TrustingPeriod: "112h",
  242. })
  243. require.NoError(t, err)
  244. err = r.StopRelayer(ctx, eRep)
  245. require.NoError(t, err)
  246. err = r.StartRelayer(ctx, eRep, "wormosmo")
  247. require.NoError(t, err)
  248. // Get the new wormchain channel to receive messages from the osmosis contract
  249. wormholeChannelId := helpers.FindOpenChannelByVersion(t, ctx, eRep, r, wormchain, CUSTOM_IBC_VERSION).ChannelID
  250. // This is the channel we will send packets on from to wormhole from osmosis ibc contract
  251. _ = helpers.FindOpenChannelByVersion(t, ctx, eRep, r, osmosis, CUSTOM_IBC_VERSION).ChannelID
  252. // Add the new channel to the wormchain-ibc-receiver contract
  253. upgradeChainChannelVaa := wormchain_ibc_receiver.SubmitIbcReceiverUpdateChannelChainMsg(t,
  254. vaa.ChainID(OsmoChainID), wormholeChannelId,
  255. guardians)
  256. _, err = wormchain.ExecuteContract(ctx, "faucet", wormchainReceiverContractInfo.Address, upgradeChainChannelVaa)
  257. require.NoError(t, err)
  258. // SKIPPING ADDING THE NEW CHANNEL TO THE WORMHOLE-IBC CONTRACT TO TEST THAT THE POST MESSAGE WILL NOT BE SENT
  259. // Send a VAA from osmosis to wormhole
  260. postMessage := wormhole_ibc.ExecuteMsg{
  261. SubmitVAA: nil,
  262. PostMessage: &wormhole_ibc.ExecuteMsg_PostMessage{
  263. Message: wormhole_ibc.Binary(base64.StdEncoding.EncodeToString([]byte("080000000901007bfa71192f886ab6819fa4862e34b4d178962958d9b2e3d9437338c9e5fde1443b809d2886eaa69e0f0158ea517675d96243c9209c3fe1d94d5b19866654c6980000000b150000000500020001020304000000000000000000000000000000000000000000000000000000000000000000000a0261626364"))),
  264. Nonce: 0,
  265. },
  266. SubmitUpdateChannelChain: nil,
  267. }
  268. postMessageJson, err := json.Marshal(postMessage)
  269. require.NoError(t, err)
  270. _, err = osmosis.ExecuteContract(ctx, "faucet", osmosisSenderContractInfo.Address,
  271. string(postMessageJson))
  272. require.Error(t, err, "post message should fail since the wormhole-ibc contract does not have the new channel whitelisted")
  273. }
  274. func instantiateWormholeIbcContracts(t *testing.T, ctx context.Context,
  275. wormchain *cosmos.CosmosChain,
  276. remoteChain *cosmos.CosmosChain,
  277. guardians *guardians.ValSet) (helpers.ContractInfoResponse, helpers.ContractInfoResponse) {
  278. // Instantiate the Wormchain core contract
  279. coreInstantiateMsg := helpers.CoreContractInstantiateMsg(t, wormchainConfig, vaa.ChainIDWormchain, guardians)
  280. wormchainCoreContractInfo := helpers.StoreAndInstantiateWormholeContract(t, ctx, wormchain, "faucet", "./contracts/wormhole_core.wasm", "wormhole_core", coreInstantiateMsg, guardians)
  281. // Store wormhole-ibc-receiver contract on wormchain
  282. ibcReceiverContractCodeId := helpers.StoreContract(t, ctx, wormchain, "faucet", "./contracts/wormchain_ibc_receiver.wasm", guardians)
  283. ibcReceiverCodeId, err := strconv.ParseUint(ibcReceiverContractCodeId, 10, 32)
  284. require.NoError(t, err)
  285. // Migrate the core wormchain core contract to the ibc variant
  286. helpers.MigrateContract(t, ctx, wormchain, "faucet", wormchainCoreContractInfo.Address, fmt.Sprint(ibcReceiverCodeId), "{}", guardians)
  287. // Get the port id for the wormchain-ibc-receiver contract
  288. wormchainReceiverContractInfo := helpers.QueryContractInfo(t, wormchain, ctx, wormchainCoreContractInfo.Address)
  289. require.NotEmpty(t, wormchainReceiverContractInfo.ContractInfo.IbcPortID, "wormchain (wormchain-ibc-receiver) contract port id is nil")
  290. // Store and instantiate wormhole-ibc contract on osmosis
  291. senderInstantiateMsg := helpers.CoreContractInstantiateMsg(t, wormchainConfig, vaa.ChainIDWormchain, guardians)
  292. senderCodeId, err := remoteChain.StoreContract(ctx, "faucet", "./contracts/wormhole_ibc.wasm")
  293. require.NoError(t, err)
  294. senderContractAddr, err := remoteChain.InstantiateContract(ctx, "faucet", senderCodeId, senderInstantiateMsg, true)
  295. require.NoError(t, err)
  296. senderContractInfo := helpers.QueryContractInfo(t, remoteChain, ctx, senderContractAddr)
  297. require.NotEmpty(t, senderContractInfo.ContractInfo.IbcPortID, "sender (wormhole-ibc) contract port id is nil")
  298. return wormchainReceiverContractInfo, senderContractInfo
  299. }