| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 |
- #!/usr/bin/env bash
- # Overview
- # This script simulates unusual, dangerous messages being emitted by the Core Bridge.
- # Its purpose is to be used as an integration test for the Transfer Verifier. The
- # Transfer Verifier can be set up as standalone binary to monitor the core bridge.
- # Once this is done, this script can be used to simulate dangerous scenarios. The
- # Transfer Verifier should pick up on this behaviour and log errors accordingly.
- #
- # Strategy
- # The code below sets up several contract and wallet addresses that match
- # the state of the devnet created by Tilt. Using the anvil and cast tools
- # from foundry, it impersonates the Token Bridge, setting its address
- # as the sender for messages to the core bridge. It sends messages that
- # contain token transfer data. The Core Bridge emits a Message Publication
- # as a result. However, no actual funds have been sent from a user into the
- # Token Bridge. This is the danger that Transfer Verifier should detect.
- #
- # Coverage
- # The code currently sends both a Transfer and Transfer With Payload type.
- # As a result, exactly 2 violations should be detected by the Transfer Verifier.
- # It should report an error that says that a message was sent without any
- # transfers or deposits in its receipt. (Compare this with
- # devnet/tx-verifier-monitor/monitor.sh)
- set -euo pipefail
- RPC="${RPC_URL:-ws://eth-devnet:8545}"
- # mainnet values
- # export CORE_CONTRACT="0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B"
- # export TOKEN_BRIDGE_CONTRACT="0x3ee18B2214AFF97000D974cf647E7C347E8fa585"
- # TODO these could be CLI params from the sh/devnet script
- CORE_BRIDGE_CONTRACT=0xC89Ce4735882C9F0f0FE26686c53074E09B0D550
- TOKEN_BRIDGE_CONTRACT=0x0290FB167208Af455bB137780163b7B7a9a10C16
- MNEMONIC=0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d
- ERC20_ADDR="0x47bdB2D7d6528C760b6f228b3B8F9F650169a10f" # Test token A
- VALUE="1000" # Wei value sent as msg.value
- TRANSFER_AMOUNT="10"
- # This account is generated by anvil and can be confirmed by running `anvil --accounts=13`.
- # The accounts at other indices are used by other tests in the test suite, so
- # account[13] is used here to help encapsulate the tests.
- ANVIL_USER="0x64E078A8Aa15A41B85890265648e965De686bAE6"
- ETH_WHALE="${ANVIL_USER}"
- FROM="${ETH_WHALE}"
- # Anvil user1 normalized to Wormhole size. (The value itself it unchecked but must have this format.)
- RECIPIENT="0x00000000000000000000000064E078A8Aa15A41B85890265648e965De686bAE6"
- NONCE="234" # arbitrary
- # Build the payloads for token transfers. The payloads are built using
- # arrays of strings and are concatenated together in a loop. Also, the
- # data is declared on multiple lines in 32 bytes chunks. This isn't
- # necessary but it makes the data a little easier to grok and work with.
- #
- # The data is pulled from an arbitrary LogMessagePublished event on
- # etherscan. Metadata and fees are commented out, leaving only the payload.
- # Note that the first non-commented byte corresponds to the payload type.
- # (https://wormhole.com/docs/learn/infrastructure/vaas/#payload-types)
- declare -a TRANSFER_SLOTS=(
- # "0000000000000000000000000000000000000000000000000000000000077009"
- # "0000000000000000000000000000000000000000000000000000000067be1656"
- # "0000000000000000000000000000000000000000000000000000000000000080"
- # "0000000000000000000000000000000000000000000000000000000000000001"
- # "0000000000000000000000000000000000000000000000000000000000000085"
- "01000000000000000000000000000000000000000000000000000006c0260fe4"
- "5f0000000000000000000000006b0b3a982b4634ac68dd83a4dbf02311ce3241"
- "810002a5f3c5bf0f76bb899300b7ec6345ed3740f22fd4bb79501bc6204938fc"
- "3a6c780001000000000000000000000000000000000000000000000000000000"
- "0000000000000000000000000000000000000000000000000000000000000000"
- )
- # Note that the token address of `ERC20_ADDR` appears within the payload
- # but is not aligned with the slot size, i.e. the last byte of
- # the address is found on the following line. This is not ideal to work
- # with but it matches how the core bridge actually works.
- declare -a TRANSFER_WITH_PAYLOAD_SLOTS=(
- # "0000000000000000000000000000000000000000000000000000000000055baf"
- # "0000000000000000000000000000000000000000000000000000000000000000"
- # "0000000000000000000000000000000000000000000000000000000000000080"
- # "0000000000000000000000000000000000000000000000000000000000000001"
- # "00000000000000000000000000000000000000000000000000000000000000ae"
- "030000000000000000000000000000000000000000000000000000000005f5e1"
- "000000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c5"
- "9900020000000000000000000000000000000000000000000000000000000000"
- "000816001000000000000000000000000044eca3f6295d6d559ca1d99a5ef5a8"
- "f72b4160f10001010200c91f01004554480044eca3f6295d6d559ca1d99a5ef5"
- "a8f72b4160f10000000000000000000000000000000000000000000000000000"
- )
- # Build payloads by joining the arrays of hex strings.
- TRANSFER_DATA="0x"
- TRANSFER_WITH_PAYLOAD_DATA="0x"
- for i in "${TRANSFER_SLOTS[@]}"
- do
- TRANSFER_DATA="$TRANSFER_DATA$i"
- done
- for i in "${TRANSFER_WITH_PAYLOAD_SLOTS[@]}"
- do
- TRANSFER_WITH_PAYLOAD_DATA="$TRANSFER_WITH_PAYLOAD_DATA$i"
- done
- # Header information for beginning the test.
- echo "Beginning test with the following configuration"
- echo "- RPC=${RPC}"
- echo "- CORE_BRIDGE_CONTRACT=${CORE_BRIDGE_CONTRACT}"
- echo "- TOKEN_BRIDGE_CONTRACT=${TOKEN_BRIDGE_CONTRACT}"
- echo "- MNEMONIC=${MNEMONIC}"
- echo "- FROM=${FROM}"
- echo "- VALUE=${VALUE}"
- echo "- RECIPIENT=${RECIPIENT}"
- echo
- # Fund the token bridge from the user.
- echo "Start impersonating Anvil user: ${ANVIL_USER}"
- cast rpc \
- anvil_impersonateAccount "${ANVIL_USER}" \
- --rpc-url "${RPC}"
- # Set-up function to make sure that the token bridge in the devnet is solvent.
- echo "Funding token bridge using the user's balance"
- cast send --unlocked \
- --rpc-url "${RPC}" \
- --from $ANVIL_USER \
- --value 100000000000000 \
- ${TOKEN_BRIDGE_CONTRACT}
- echo ""
- echo "End impersonating ${ANVIL_USER}"
- cast rpc \
- anvil_stopImpersonatingAccount "${ANVIL_USER}" \
- --rpc-url "${RPC}"
- # Start the test, printing the conditions before any Token Bridge
- # activity (except the initial funding).
- BALANCE_CORE=$(cast balance --rpc-url "${RPC}" $CORE_BRIDGE_CONTRACT)
- BALANCE_TOKEN=$(cast balance --rpc-url "${RPC}" $TOKEN_BRIDGE_CONTRACT)
- BALANCE_USER=$(cast balance --rpc-url "${RPC}" $ANVIL_USER)
- echo "BALANCES:"
- echo "- CORE_BRIDGE_CONTRACT=${BALANCE_CORE}"
- echo "- TOKEN_BRIDGE_CONTRACT=${BALANCE_TOKEN}"
- echo "- ANVIL_USER=${BALANCE_USER}"
- echo
- # Simulate the Token Bridge so that it's possible to communicate with the core
- # bridge in arbitrary ways. Normally, the only path to do this would involve
- # doing a WETH Deposit or ERC20 transfer in the same transaction. This is
- # exactly the invariant that we are trying to test with Transfer Verifier. If
- # the Token Bridge can cause a Message Publication from the Core Bridge without
- # any funds being sent into in to the Token Bridge first, there's a serious
- # error.
- # Ensure that anvil is using `--auto-impersonate` or else that account
- # impersonation is enabled in your local environment. For Tilt, this should
- # be handled already by the eth-devnet pod.
- echo "Start impersonating the Token Bridge"
- cast rpc \
- --rpc-url "${RPC}" \
- anvil_impersonateAccount "${TOKEN_BRIDGE_CONTRACT}" &> /dev/null
-
- # === Test Scenario 1: Malicious call to transferTokens()
- NONCE=0
- echo "Calling publishMessage() with transferTokens() payload as ${TOKEN_BRIDGE_CONTRACT}"
- cast send --unlocked \
- --rpc-url "${RPC}" \
- --json \
- --gas-limit 10000000 \
- --priority-gas-price 1 \
- --from "${TOKEN_BRIDGE_CONTRACT}" \
- --value "0" \
- "${CORE_BRIDGE_CONTRACT}" \
- "publishMessage(uint32,bytes,uint8)" \
- $NONCE "${TRANSFER_DATA}" 1
- echo ""
- NONCE=$(($NONCE + 1))
- # === Test Scenario 2: Malicious call to transferTokensWithPayload()
- echo "Calling publishMessage() with transferTokensWithPayload() payload as ${TOKEN_BRIDGE_CONTRACT}"
- cast send --unlocked \
- --rpc-url "${RPC}" \
- --json \
- --gas-limit 10000000 \
- --priority-gas-price 1 \
- --from "${TOKEN_BRIDGE_CONTRACT}" \
- --value "0" \
- "${CORE_BRIDGE_CONTRACT}" \
- "publishMessage(uint32,bytes,uint8)" \
- 1 "${TRANSFER_WITH_PAYLOAD_DATA}" 1
- echo ""
- # Cleanup.
- cast rpc \
- --rpc-url "${RPC}" \
- anvil_stopImpersonatingAccount "${TOKEN_BRIDGE_CONTRACT}" &> /dev/null
- echo "End impersonating the Token Bridge"
- # Print balances after.
- BALANCE_CORE=$(cast balance --rpc-url "${RPC}" $CORE_BRIDGE_CONTRACT)
- BALANCE_TOKEN=$(cast balance --rpc-url "${RPC}" $TOKEN_BRIDGE_CONTRACT)
- BALANCE_USER=$(cast balance --rpc-url "${RPC}" $ANVIL_USER)
- echo "BALANCES:"
- echo "- CORE_BRIDGE_CONTRACT=${BALANCE_CORE}"
- echo "- TOKEN_BRIDGE_CONTRACT=${BALANCE_TOKEN}"
- echo "- ANVIL_USER=${BALANCE_USER}"
- echo
- # TODO add the 'multicall' scenario encoded in the forge script
- echo "Done Transfer Verifier integration test. Exiting"
|