tx-verifier-evm-tests.sh 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. #!/usr/bin/env bash
  2. # Overview
  3. # This script simulates unusual, dangerous messages being emitted by the Core Bridge.
  4. # Its purpose is to be used as an integration test for the Transfer Verifier. The
  5. # Transfer Verifier can be set up as standalone binary to monitor the core bridge.
  6. # Once this is done, this script can be used to simulate dangerous scenarios. The
  7. # Transfer Verifier should pick up on this behaviour and log errors accordingly.
  8. #
  9. # Strategy
  10. # The code below sets up several contract and wallet addresses that match
  11. # the state of the devnet created by Tilt. Using the anvil and cast tools
  12. # from foundry, it impersonates the Token Bridge, setting its address
  13. # as the sender for messages to the core bridge. It sends messages that
  14. # contain token transfer data. The Core Bridge emits a Message Publication
  15. # as a result. However, no actual funds have been sent from a user into the
  16. # Token Bridge. This is the danger that Transfer Verifier should detect.
  17. #
  18. # Coverage
  19. # The code currently sends both a Transfer and Transfer With Payload type.
  20. # As a result, exactly 2 violations should be detected by the Transfer Verifier.
  21. # It should report an error that says that a message was sent without any
  22. # transfers or deposits in its receipt. (Compare this with
  23. # devnet/tx-verifier-monitor/monitor.sh)
  24. set -euo pipefail
  25. RPC="${RPC_URL:-ws://eth-devnet:8545}"
  26. # mainnet values
  27. # export CORE_CONTRACT="0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B"
  28. # export TOKEN_BRIDGE_CONTRACT="0x3ee18B2214AFF97000D974cf647E7C347E8fa585"
  29. # TODO these could be CLI params from the sh/devnet script
  30. CORE_BRIDGE_CONTRACT=0xC89Ce4735882C9F0f0FE26686c53074E09B0D550
  31. TOKEN_BRIDGE_CONTRACT=0x0290FB167208Af455bB137780163b7B7a9a10C16
  32. MNEMONIC=0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d
  33. ERC20_ADDR="0x47bdB2D7d6528C760b6f228b3B8F9F650169a10f" # Test token A
  34. VALUE="1000" # Wei value sent as msg.value
  35. TRANSFER_AMOUNT="10"
  36. # This account is generated by anvil and can be confirmed by running `anvil --accounts=13`.
  37. # The accounts at other indices are used by other tests in the test suite, so
  38. # account[13] is used here to help encapsulate the tests.
  39. ANVIL_USER="0x64E078A8Aa15A41B85890265648e965De686bAE6"
  40. ETH_WHALE="${ANVIL_USER}"
  41. FROM="${ETH_WHALE}"
  42. # Anvil user1 normalized to Wormhole size. (The value itself it unchecked but must have this format.)
  43. RECIPIENT="0x00000000000000000000000064E078A8Aa15A41B85890265648e965De686bAE6"
  44. NONCE="234" # arbitrary
  45. # Build the payloads for token transfers. The payloads are built using
  46. # arrays of strings and are concatenated together in a loop. Also, the
  47. # data is declared on multiple lines in 32 bytes chunks. This isn't
  48. # necessary but it makes the data a little easier to grok and work with.
  49. #
  50. # The data is pulled from an arbitrary LogMessagePublished event on
  51. # etherscan. Metadata and fees are commented out, leaving only the payload.
  52. # Note that the first non-commented byte corresponds to the payload type.
  53. # (https://wormhole.com/docs/learn/infrastructure/vaas/#payload-types)
  54. declare -a TRANSFER_SLOTS=(
  55. # "0000000000000000000000000000000000000000000000000000000000077009"
  56. # "0000000000000000000000000000000000000000000000000000000067be1656"
  57. # "0000000000000000000000000000000000000000000000000000000000000080"
  58. # "0000000000000000000000000000000000000000000000000000000000000001"
  59. # "0000000000000000000000000000000000000000000000000000000000000085"
  60. "01000000000000000000000000000000000000000000000000000006c0260fe4"
  61. "5f0000000000000000000000006b0b3a982b4634ac68dd83a4dbf02311ce3241"
  62. "810002a5f3c5bf0f76bb899300b7ec6345ed3740f22fd4bb79501bc6204938fc"
  63. "3a6c780001000000000000000000000000000000000000000000000000000000"
  64. "0000000000000000000000000000000000000000000000000000000000000000"
  65. )
  66. # Note that the token address of `ERC20_ADDR` appears within the payload
  67. # but is not aligned with the slot size, i.e. the last byte of
  68. # the address is found on the following line. This is not ideal to work
  69. # with but it matches how the core bridge actually works.
  70. declare -a TRANSFER_WITH_PAYLOAD_SLOTS=(
  71. # "0000000000000000000000000000000000000000000000000000000000055baf"
  72. # "0000000000000000000000000000000000000000000000000000000000000000"
  73. # "0000000000000000000000000000000000000000000000000000000000000080"
  74. # "0000000000000000000000000000000000000000000000000000000000000001"
  75. # "00000000000000000000000000000000000000000000000000000000000000ae"
  76. "030000000000000000000000000000000000000000000000000000000005f5e1"
  77. "000000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c5"
  78. "9900020000000000000000000000000000000000000000000000000000000000"
  79. "000816001000000000000000000000000044eca3f6295d6d559ca1d99a5ef5a8"
  80. "f72b4160f10001010200c91f01004554480044eca3f6295d6d559ca1d99a5ef5"
  81. "a8f72b4160f10000000000000000000000000000000000000000000000000000"
  82. )
  83. # Build payloads by joining the arrays of hex strings.
  84. TRANSFER_DATA="0x"
  85. TRANSFER_WITH_PAYLOAD_DATA="0x"
  86. for i in "${TRANSFER_SLOTS[@]}"
  87. do
  88. TRANSFER_DATA="$TRANSFER_DATA$i"
  89. done
  90. for i in "${TRANSFER_WITH_PAYLOAD_SLOTS[@]}"
  91. do
  92. TRANSFER_WITH_PAYLOAD_DATA="$TRANSFER_WITH_PAYLOAD_DATA$i"
  93. done
  94. # Header information for beginning the test.
  95. echo "Beginning test with the following configuration"
  96. echo "- RPC=${RPC}"
  97. echo "- CORE_BRIDGE_CONTRACT=${CORE_BRIDGE_CONTRACT}"
  98. echo "- TOKEN_BRIDGE_CONTRACT=${TOKEN_BRIDGE_CONTRACT}"
  99. echo "- MNEMONIC=${MNEMONIC}"
  100. echo "- FROM=${FROM}"
  101. echo "- VALUE=${VALUE}"
  102. echo "- RECIPIENT=${RECIPIENT}"
  103. echo
  104. # Fund the token bridge from the user.
  105. echo "Start impersonating Anvil user: ${ANVIL_USER}"
  106. cast rpc \
  107. anvil_impersonateAccount "${ANVIL_USER}" \
  108. --rpc-url "${RPC}"
  109. # Set-up function to make sure that the token bridge in the devnet is solvent.
  110. echo "Funding token bridge using the user's balance"
  111. cast send --unlocked \
  112. --rpc-url "${RPC}" \
  113. --from $ANVIL_USER \
  114. --value 100000000000000 \
  115. ${TOKEN_BRIDGE_CONTRACT}
  116. echo ""
  117. echo "End impersonating ${ANVIL_USER}"
  118. cast rpc \
  119. anvil_stopImpersonatingAccount "${ANVIL_USER}" \
  120. --rpc-url "${RPC}"
  121. # Start the test, printing the conditions before any Token Bridge
  122. # activity (except the initial funding).
  123. BALANCE_CORE=$(cast balance --rpc-url "${RPC}" $CORE_BRIDGE_CONTRACT)
  124. BALANCE_TOKEN=$(cast balance --rpc-url "${RPC}" $TOKEN_BRIDGE_CONTRACT)
  125. BALANCE_USER=$(cast balance --rpc-url "${RPC}" $ANVIL_USER)
  126. echo "BALANCES:"
  127. echo "- CORE_BRIDGE_CONTRACT=${BALANCE_CORE}"
  128. echo "- TOKEN_BRIDGE_CONTRACT=${BALANCE_TOKEN}"
  129. echo "- ANVIL_USER=${BALANCE_USER}"
  130. echo
  131. # Simulate the Token Bridge so that it's possible to communicate with the core
  132. # bridge in arbitrary ways. Normally, the only path to do this would involve
  133. # doing a WETH Deposit or ERC20 transfer in the same transaction. This is
  134. # exactly the invariant that we are trying to test with Transfer Verifier. If
  135. # the Token Bridge can cause a Message Publication from the Core Bridge without
  136. # any funds being sent into in to the Token Bridge first, there's a serious
  137. # error.
  138. # Ensure that anvil is using `--auto-impersonate` or else that account
  139. # impersonation is enabled in your local environment. For Tilt, this should
  140. # be handled already by the eth-devnet pod.
  141. echo "Start impersonating the Token Bridge"
  142. cast rpc \
  143. --rpc-url "${RPC}" \
  144. anvil_impersonateAccount "${TOKEN_BRIDGE_CONTRACT}" &> /dev/null
  145. # === Test Scenario 1: Malicious call to transferTokens()
  146. NONCE=0
  147. echo "Calling publishMessage() with transferTokens() payload as ${TOKEN_BRIDGE_CONTRACT}"
  148. cast send --unlocked \
  149. --rpc-url "${RPC}" \
  150. --json \
  151. --gas-limit 10000000 \
  152. --priority-gas-price 1 \
  153. --from "${TOKEN_BRIDGE_CONTRACT}" \
  154. --value "0" \
  155. "${CORE_BRIDGE_CONTRACT}" \
  156. "publishMessage(uint32,bytes,uint8)" \
  157. $NONCE "${TRANSFER_DATA}" 1
  158. echo ""
  159. NONCE=$(($NONCE + 1))
  160. # === Test Scenario 2: Malicious call to transferTokensWithPayload()
  161. echo "Calling publishMessage() with transferTokensWithPayload() payload as ${TOKEN_BRIDGE_CONTRACT}"
  162. cast send --unlocked \
  163. --rpc-url "${RPC}" \
  164. --json \
  165. --gas-limit 10000000 \
  166. --priority-gas-price 1 \
  167. --from "${TOKEN_BRIDGE_CONTRACT}" \
  168. --value "0" \
  169. "${CORE_BRIDGE_CONTRACT}" \
  170. "publishMessage(uint32,bytes,uint8)" \
  171. 1 "${TRANSFER_WITH_PAYLOAD_DATA}" 1
  172. echo ""
  173. # Cleanup.
  174. cast rpc \
  175. --rpc-url "${RPC}" \
  176. anvil_stopImpersonatingAccount "${TOKEN_BRIDGE_CONTRACT}" &> /dev/null
  177. echo "End impersonating the Token Bridge"
  178. # Print balances after.
  179. BALANCE_CORE=$(cast balance --rpc-url "${RPC}" $CORE_BRIDGE_CONTRACT)
  180. BALANCE_TOKEN=$(cast balance --rpc-url "${RPC}" $TOKEN_BRIDGE_CONTRACT)
  181. BALANCE_USER=$(cast balance --rpc-url "${RPC}" $ANVIL_USER)
  182. echo "BALANCES:"
  183. echo "- CORE_BRIDGE_CONTRACT=${BALANCE_CORE}"
  184. echo "- TOKEN_BRIDGE_CONTRACT=${BALANCE_TOKEN}"
  185. echo "- ANVIL_USER=${BALANCE_USER}"
  186. echo
  187. # TODO add the 'multicall' scenario encoded in the forge script
  188. echo "Done Transfer Verifier integration test. Exiting"