diff --git a/test/Makefile b/test/Makefile index 32a2a89df..8ae85ba90 100644 --- a/test/Makefile +++ b/test/Makefile @@ -103,6 +103,11 @@ test-e2e-fork12-pessimistic: stop ./run-e2e.sh fork12 pessimistic bats bats/pp/bridge-e2e.bats bats/pp/e2e-pp.bats +.PHONY: test-e2e-fork12-multi-pessimistic +test-e2e-fork12-multi-pessimistic: stop + ./run-e2e-multi_pp.sh + bats bats/pp-multi + .PHONY: stop stop: kurtosis clean --all diff --git a/test/bats/pp-multi/bridge-l2_to_l2-e2e.bats b/test/bats/pp-multi/bridge-l2_to_l2-e2e.bats new file mode 100644 index 000000000..79d2601cf --- /dev/null +++ b/test/bats/pp-multi/bridge-l2_to_l2-e2e.bats @@ -0,0 +1,49 @@ +# based on: https://github.com/0xPolygon/kurtosis-cdk/blob/jhilliard/multi-pp-testing/multi-pp-test.sh.md + +setup() { + load '../../helpers/common-multi_cdk-setup' + _common_multi_setup + load '../../helpers/common' + load '../../helpers/lxly-bridge-test' + + #add_cdk_network2_to_agglayer + #fund_claim_tx_manager + #mint_pol_token + + ether_value=${ETHER_VALUE:-"0.0200000054"} + amount=$(cast to-wei $ether_value ether) + native_token_addr="0x0000000000000000000000000000000000000000" + readonly sender_private_key=${SENDER_PRIVATE_KEY:-"12d7de8621a77640c9241b2595ba78ce443d05e94090365ab3bb5e19df82c625"} + readonly sender_addr="$(cast wallet address --private-key $sender_private_key)" + # Params for lxly-bridge functions + is_forced=${IS_FORCED:-"true"} + bridge_addr=$bridge_address + meta_bytes=${META_BYTES:-"0x1234"} + destination_addr=$target_address + timeout="120" + claim_frequency="10" + + gas_price=$(cast gas-price --rpc-url "$l2_rpc_url") +} + +@test "Test L2 to L2 bridge" { + echo "=== Running LxLy bridge eth L1 to L2(PP1)" >&3 + destination_net=$l2_pp1b_network_id + bridge_asset "$native_token_addr" "$l1_rpc_url" + + echo "=== Running LxLy claim L1 to L2(PP1) for $bridge_tx_hash" >&3 + run claim_tx_hash "$timeout" "$bridge_tx_hash" "$destination_addr" "$l2_pp1_url" "$l2_pp1b_url" + assert_success + + echo "=== Running LxLy bridge L2(PP1) to L2(PP2)" >&3 + destination_net=$l2_pp2b_network_id + amount=$(date +%s) + bridge_asset "$native_token_addr" "$l2_pp1_url" + + echo "=== Running LxLy claim L2(PP1) to L2(PP2) for: $bridge_tx_hash" >&3 + # Buscar en pp1b dest_net=2 + # Must read the merkel proof from PP1 + run claim_tx_hash "$timeout" "$bridge_tx_hash" "$destination_addr" "$l2_pp2_url" "$l2_pp1b_url" + assert_success + +} \ No newline at end of file diff --git a/test/helpers/common-multi_cdk-setup.bash b/test/helpers/common-multi_cdk-setup.bash new file mode 100644 index 000000000..baf48a6b4 --- /dev/null +++ b/test/helpers/common-multi_cdk-setup.bash @@ -0,0 +1,81 @@ +#!/usr/bin/env bash + +_common_multi_setup() { + load '../../helpers/common-setup' + _common_setup + # generated with cast wallet new + readonly target_address=0xbecE3a31343c6019CDE0D5a4dF2AF8Df17ebcB0f + readonly target_private_key=0x51caa196504216b1730280feb63ddd8c5ae194d13e57e58d559f1f1dc3eda7c9 + + kurtosis service exec $enclave contracts-001 "cat /opt/zkevm/combined-001.json" | tail -n +2 | jq '.' > combined-001.json + kurtosis service exec $enclave contracts-002 "cat /opt/zkevm/combined-002.json" | tail -n +2 | jq '.' > combined-002.json + kurtosis service exec $enclave contracts-002 "cat /opt/zkevm-contracts/deployment/v2/create_rollup_parameters.json" | tail -n +2 | jq -r '.gasTokenAddress' > gas-token-address.json + + readonly private_key="0x12d7de8621a77640c9241b2595ba78ce443d05e94090365ab3bb5e19df82c625" + readonly eth_address=$(cast wallet address --private-key $private_key) + readonly l1_rpc_url=http://$(kurtosis port print $enclave el-1-geth-lighthouse rpc) + readonly l2_pp1_url=$(kurtosis port print $enclave cdk-erigon-rpc-001 rpc) + readonly l2_pp2_url=$(kurtosis port print $enclave cdk-erigon-rpc-002 rpc) + readonly bridge_address=$(cat combined-001.json | jq -r .polygonZkEVMBridgeAddress) + readonly pol_address=$(cat combined-001.json | jq -r .polTokenAddress) + readonly gas_token_address=$(&3 + echo "=== POL address=$pol_address ===" >&3 + echo "=== Gas token address=$gas_token_address ===" >&3 + echo "=== L1 network id=$l1_rpc_network_id ===" >&3 + echo "=== L2 PP1 network id=$l2_pp1b_network_id ===" >&3 + echo "=== L2 PP2 network id=$l2_pp2b_network_id ===" >&3 + echo "=== L1 RPC URL=$l1_rpc_url ===" >&3 + echo "=== L2 PP1 URL=$l2_pp1_url ===" >&3 + echo "=== L2 PP2 URL=$l2_pp2_url ===" >&3 + echo "=== L2 PP1B URL=$l2_pp1b_url ===" >&3 + echo "=== L2 PP2B URL=$l2_pp2b_url ===" >&3 + +} + +add_cdk_network2_to_agglayer(){ + echo "=== Checking if network 2 is in agglayer ===" >&3 + local _prev=$(kurtosis service exec $enclave agglayer "grep \"2 = \" /etc/zkevm/agglayer-config.toml || true" | tail -n +2) + if [ ! -z "$_prev" ]; then + echo "Network 2 already added to agglayer" >&3 + return + fi + echo "=== Adding network 2 to agglayer === ($_prev)" >&3 + exit 1 + kurtosis service exec $enclave agglayer "sed -i 's/\[proof\-signers\]/2 = \"http:\/\/cdk-erigon-rpc-002:8123\"\n\[proof-signers\]/i' /etc/zkevm/agglayer-config.toml" + kurtosis service stop $enclave agglayer + kurtosis service start $enclave agglayer +} + +fund_claim_tx_manager(){ + echo "=== Funding bridge auto-claim ===" >&3 + cast send --legacy --value 100ether --rpc-url $l2_pp1_url --private-key $private_key 0x5f5dB0D4D58310F53713eF4Df80ba6717868A9f8 + cast send --legacy --value 100ether --rpc-url $l2_pp2_url --private-key $private_key 0x93F63c24735f45Cd0266E87353071B64dd86bc05 +} + + +mint_pol_token(){ + echo "=== Mining POL ===" >&3 + cast send \ + --rpc-url $l1_rpc_url \ + --private-key $private_key \ + $pol_address \ + 'mint(address,uint256)' \ + $eth_address 10000000000000000000000 + # Allow bridge to spend it + cast send \ + --rpc-url $l1_rpc_url \ + --private-key $private_key \ + $pol_address \ + 'approve(address,uint256)' \ + $bridge_address 10000000000000000000000 +} diff --git a/test/helpers/lxly-bridge-test.bash b/test/helpers/lxly-bridge-test.bash index 53af437ec..9e84a6b74 100644 --- a/test/helpers/lxly-bridge-test.bash +++ b/test/helpers/lxly-bridge-test.bash @@ -30,6 +30,8 @@ function bridge_message() { fi } +# returns: +# - bridge_tx_hash function bridge_asset() { local token_addr="$1" local rpc_url="$2" @@ -50,13 +52,17 @@ function bridge_asset() { if [[ $dry_run == "true" ]]; then cast calldata $bridge_sig $destination_net $destination_addr $amount $token_addr $is_forced $meta_bytes else + local tmp_response_file=$(mktemp) if [[ $token_addr == "0x0000000000000000000000000000000000000000" ]]; then echo "cast send --legacy --private-key $sender_private_key --value $amount --rpc-url $rpc_url $bridge_addr $bridge_sig $destination_net $destination_addr $amount $token_addr $is_forced $meta_bytes" >&3 - cast send --legacy --private-key $sender_private_key --value $amount --rpc-url $rpc_url $bridge_addr $bridge_sig $destination_net $destination_addr $amount $token_addr $is_forced $meta_bytes + cast send --legacy --private-key $sender_private_key --value $amount --rpc-url $rpc_url $bridge_addr $bridge_sig $destination_net $destination_addr $amount $token_addr $is_forced $meta_bytes > $tmp_response_file else echo "cast send --legacy --private-key $sender_private_key --rpc-url $rpc_url $bridge_addr $bridge_sig $destination_net $destination_addr $amount $token_addr $is_forced $meta_bytes" - cast send --legacy --private-key $sender_private_key --rpc-url $rpc_url $bridge_addr $bridge_sig $destination_net $destination_addr $amount $token_addr $is_forced $meta_bytes + + cast send --legacy --private-key $sender_private_key --rpc-url $rpc_url $bridge_addr $bridge_sig $destination_net $destination_addr $amount $token_addr $is_forced $meta_bytes > $tmp_response_file fi + export bridge_tx_hash=$(grep "^transactionHash" $tmp_response_file | cut -f 2- -d ' ' | sed 's/ //g') + echo "bridge_tx_hash=$bridge_tx_hash" >&3 fi } @@ -125,6 +131,133 @@ function claim() { done < <(seq 0 $((claimable_count - 1))) } +# This function is used to claim a concrete tx hash +# global vars: +# - destination_addr + +function claim_tx_hash() { + local timeout="$1" + tx_hash="$2" + local destination_addr="$3" + local destination_rpc_url="$4" + local bridge_provide_merkel_proof="$5" + + readonly bridge_deposit_file=$(mktemp) + local ready_for_claim="false" + + local current_time=$(date +%s) + local end_time=$((start_time + timeout)) + while true; do + #echo "claim_tx_hash: curl -s \"$bridge_provide_merkel_proof/bridges/$destination_addr?limit=100&offset=0\" | jq \"[.deposits[] | select(.tx_hash == \\"$tx_hash\\" )]\"" + curl -s "$bridge_provide_merkel_proof/bridges/$destination_addr?limit=100&offset=0" | jq "[.deposits[] | select(.tx_hash == \"$tx_hash\" )]" > $bridge_deposit_file + deposit_count=$(jq '. | length' $bridge_deposit_file) + if [[ $deposit_count == 0 ]]; then + echo "...[$(date '+%Y-%m-%d %H:%M:%S')] ❌ the tx_hash [$tx_hash] not found" >&3 + sleep "$claim_frequency" + continue + fi + local ready_for_claim=$(jq '.[0].ready_for_claim' $bridge_deposit_file) + if [ $ready_for_claim != "true" ]; then + echo ".... [$(date '+%Y-%m-%d %H:%M:%S')] ⏳ the tx_hash $tx_hash is not ready for claim yet" >&3 + sleep "$claim_frequency" + continue + else + break + fi + done + # Deposit is ready for claim + echo "....[$(date '+%Y-%m-%d %H:%M:%S')] 🎉 the tx_hash $tx_hash is ready for claim!" >&3 + local curr_claim_tx_hash=$(jq '.[0].claim_tx_hash' $bridge_deposit_file) + if [ $curr_claim_tx_hash != "\"\"" ]; then + echo "....[$(date '+%Y-%m-%d %H:%M:%S')] 🎉 the tx_hash $tx_hash is already claimed" >&3 + exit 0 + fi + local curr_deposit_cnt=$(jq '.[0].deposit_cnt' $bridge_deposit_file) + local curr_network_id=$(jq '.[0].network_id' $bridge_deposit_file) + readonly current_deposit=$(mktemp) + jq '.[(0|tonumber)]' $bridge_deposit_file | tee $current_deposit + readonly current_proof=$(mktemp) + echo ".... requesting merkel proof for $tx_hash deposit_cnt=$curr_deposit_cnt" >&3 + request_merkel_proof "$curr_deposit_cnt" "$curr_network_id" "$bridge_provide_merkel_proof" "$current_proof" + echo ".... requesting claim for $tx_hash" >&3 + request_claim $current_deposit $current_proof $destination_rpc_url + + # clean up temp files + rm $current_deposit + rm $current_proof + rm $bridge_deposit_file + +} +function request_merkel_proof(){ + local curr_deposit_cnt="$1" + local curr_network_id="$2" + local bridge_provide_merkel_proof="$3" + local result_proof_file="$4" + curl -s "$bridge_provide_merkel_proof/merkle-proof?deposit_cnt=$curr_deposit_cnt&net_id=$curr_network_id" | jq '.' > $result_proof_file + echo "request_merkel_proof: $result_proof_file" +} + +# This function is used to claim a concrete tx hash +# global vars: +# -dry_run +# -gas_price +# -sender_private_key +# -bridge_addr +function request_claim(){ + local deposit_file="$1" + local proof_file="$2" + local destination_rpc_url="$3" + + local leaf_type=$(jq -r '.leaf_type' $deposit_file) + local claim_sig="claimAsset(bytes32[32],bytes32[32],uint256,bytes32,bytes32,uint32,address,uint32,address,uint256,bytes)" + + if [[ $leaf_type != "0" ]]; then + claim_sig="claimMessage(bytes32[32],bytes32[32],uint256,bytes32,bytes32,uint32,address,uint32,address,uint256,bytes)" + fi + + local in_merkle_proof="$(jq -r -c '.proof.merkle_proof' $proof_file | tr -d '"')" + local in_rollup_merkle_proof="$(jq -r -c '.proof.rollup_merkle_proof' $proof_file | tr -d '"')" + local in_global_index=$(jq -r '.global_index' $deposit_file) + local in_main_exit_root=$(jq -r '.proof.main_exit_root' $proof_file) + local in_rollup_exit_root=$(jq -r '.proof.rollup_exit_root' $proof_file) + local in_orig_net=$(jq -r '.orig_net' $deposit_file) + local in_orig_addr=$(jq -r '.orig_addr' $deposit_file) + local in_dest_net=$(jq -r '.dest_net' $deposit_file) + local in_dest_addr=$(jq -r '.dest_addr' $deposit_file) + local in_amount=$(jq -r '.amount' $deposit_file) + local in_metadata=$(jq -r '.metadata' $deposit_file) + if [[ $dry_run == "true" ]]; then + cast calldata $claim_sig "$in_merkle_proof" "$in_rollup_merkle_proof" $in_global_index $in_main_exit_root $in_rollup_exit_root $in_orig_net $in_orig_addr $in_dest_net $in_dest_addr $in_amount $in_metadata + else + local comp_gas_price=$(bc -l <<< "$gas_price * 1.5" | sed 's/\..*//') + if [[ $? -ne 0 ]]; then + echo "Failed to calculate gas price" >&3 + exit 1 + fi + + echo "cast send --legacy --gas-price $comp_gas_price --rpc-url $destination_rpc_url --private-key $sender_private_key $bridge_addr \"$claim_sig\" \"$in_merkle_proof\" \"$in_rollup_merkle_proof\" $in_global_index $in_main_exit_root $in_rollup_exit_root $in_orig_net $in_orig_addr $in_dest_net $in_dest_addr $in_amount $in_metadata" + local tmp_response=$(mktemp) + cast send --legacy --gas-price $comp_gas_price \ + --rpc-url $destination_rpc_url \ + --private-key $sender_private_key \ + $bridge_addr "$claim_sig" "$in_merkle_proof" "$in_rollup_merkle_proof" $in_global_index $in_main_exit_root $in_rollup_exit_root $in_orig_net $in_orig_addr $in_dest_net $in_dest_addr $in_amount $in_metadata 2> $tmp_response || check_claim_revert_code $tmp_response + echo "finish $tmp_res" >&3 + fi +} + +function check_claim_revert_code(){ + local file_curl_reponse="$1" + # 0x646cf558 -> AlreadyClaimed() + echo "check revert " + cat $file_curl_reponse + cat $file_curl_reponse | grep "0x646cf558" > /dev/null + if [ $? -eq 0 ]; then + echo "....[$(date '+%Y-%m-%d %H:%M:%S')] 🎉 deposit is already claimed (revert code 0x646cf558)" >&3 + return 0 + fi + return 1 +} + function wait_for_claim() { local timeout="$1" # timeout (in seconds) local claim_frequency="$2" # claim frequency (in seconds) diff --git a/test/run-e2e-multi_pp.sh b/test/run-e2e-multi_pp.sh index fae88ce48..a1747f6cf 100755 --- a/test/run-e2e-multi_pp.sh +++ b/test/run-e2e-multi_pp.sh @@ -1,6 +1,15 @@ #!/bin/bash source $(dirname $0)/scripts/env.sh +function log_error() { + echo -e "\033[0;31mError: $*" "\033[0m" +} + +function log_fatal() { + log_error $* + exit 1 +} + function ok_or_fatal(){ if [ $? -ne 0 ]; then log_fatal $* @@ -27,6 +36,10 @@ function resolve_template(){ echo "rendering $_TEMPLATE_FILE to temp file $_TEMP_FILE" go run ../scripts/run_template.go $_TEMPLATE_FILE > $_TEMP_FILE ok_or_fatal "Failed to render template $_TEMPLATE_FILE" + grep "" "$_TEMP_FILE" + if [ $? -eq 0 ]; then + log_fatal "Failed to render template $_TEMPLATE_FILE. missing values" + fi eval $_RESULT_VARNAME="$_TEMP_FILE" } @@ -54,4 +67,4 @@ kurtosis run --enclave $KURTOSIS_ENCLAVE --args-file "$PP1_RENDERED_CONFIG_FILE" ok_or_fatal "Failed to run kurtosis pp1" kurtosis run --enclave $KURTOSIS_ENCLAVE --args-file "$PP2_RENDERED_CONFIG_FILE" --image-download always $KURTOSIS_FOLDER -ok_or_fatal "Failed to run kurtosis attached second cdk" \ No newline at end of file +ok_or_fatal "Failed to run kurtosis attached second cdk"