-
Notifications
You must be signed in to change notification settings - Fork 878
test(flatkv): add EVM migration docker integration coverage #3400
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
e664f79
test(flatkv): add EVM import docker integration coverage
blindchaser 5c2fa4e
test(flatkv): cover KVImporter concurrency paths and large imports
blindchaser 3f9a4e3
test(flatkv): cover KVImporter backpressure path explicitly
blindchaser ab39a58
fix(flatkv): do not finalize partial imports on failure
blindchaser d0cb6e2
fix(integration): wait for EVM RPC after FlatKV import restart
blindchaser e0b0388
Merge branch 'main' into yiren/flatkv-docker-testings
blindchaser 268cd4b
refactor(seidb): scope Importer.Err() to *flatkv.KVImporter
blindchaser File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
206 changes: 206 additions & 0 deletions
206
integration_test/contracts/deploy_flatkv_evm_fixture.sh
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,206 @@ | ||
| #!/bin/bash | ||
|
|
||
| set -euo pipefail | ||
|
|
||
| export PATH="$PATH:/root/.foundry/bin:/root/go/bin" | ||
|
|
||
| RPC_URL=${EVM_RPC_URL:-http://localhost:8545} | ||
| FROM=${FLATKV_EVM_FIXTURE_FROM:-admin} | ||
| PASSWORD=${FLATKV_EVM_FIXTURE_PASSWORD:-12345678} | ||
| CHAIN_ID=${FLATKV_EVM_FIXTURE_CHAIN_ID:-sei} | ||
| RECIPIENT_ADDR=${FLATKV_EVM_FIXTURE_RECIPIENT:-0x70997970C51812dc3A010C7d01b50e0d17dc79C8} | ||
| MISSING_ADDR=${FLATKV_EVM_FIXTURE_MISSING:-0xc1cadaffffffffffffffffffffffffffffffffff} | ||
| TRANSFER_VALUE_WEI=${FLATKV_EVM_FIXTURE_TRANSFER_VALUE_WEI:-1} | ||
| KEYRING_ARGS=() | ||
| if [ -n "${FLATKV_EVM_FIXTURE_KEYRING_BACKEND:-}" ]; then | ||
| KEYRING_ARGS+=(--keyring-backend "$FLATKV_EVM_FIXTURE_KEYRING_BACKEND") | ||
| fi | ||
|
|
||
| # Constructor: | ||
| # sstore(0, 42) | ||
| # return runtime bytecode that returns 42 for any call. | ||
| STORAGE_CONTRACT_INIT_CODE=0x602a600055600a600f600039600a6000f3602a60005260206000f3 | ||
| STORAGE_SLOT_ZERO=0x0000000000000000000000000000000000000000000000000000000000000000 | ||
|
|
||
| seihome=$(git rev-parse --show-toplevel) | ||
| out_dir="$seihome/integration_test/contracts" | ||
|
|
||
| write_fixture() { | ||
| local name=$1 | ||
| local value=$2 | ||
| printf "%s\n" "$value" > "$out_dir/$name" | ||
| } | ||
|
|
||
| run_seid() { | ||
| printf "%s\n" "$PASSWORD" | seid "$@" | ||
| } | ||
|
|
||
| wait_for_evm_rpc() { | ||
| local timeout=120 | ||
| local elapsed=0 | ||
| until cast block-number --rpc-url "$RPC_URL" >/dev/null 2>&1; do | ||
| if [ "$elapsed" -ge "$timeout" ]; then | ||
| echo "EVM RPC did not become ready within ${timeout}s" >&2 | ||
| exit 1 | ||
| fi | ||
| sleep 2 | ||
| elapsed=$((elapsed + 2)) | ||
| done | ||
| } | ||
|
|
||
| block_number() { | ||
| cast block-number --rpc-url "$RPC_URL" | ||
| } | ||
|
|
||
| query_balance() { | ||
| cast balance "$1" --block "$2" --rpc-url "$RPC_URL" | ||
| } | ||
|
|
||
| query_balance_hex() { | ||
| cast to-hex "$(query_balance "$1" "$2")" | ||
| } | ||
|
|
||
| query_storage() { | ||
| cast storage "$1" "$2" --block "$3" --rpc-url "$RPC_URL" | ||
| } | ||
|
|
||
| query_code() { | ||
| cast code "$1" --block "$2" --rpc-url "$RPC_URL" | ||
| } | ||
|
|
||
| extract_tx_hash() { | ||
| grep -oE '0x[a-fA-F0-9]{64}' | head -1 | ||
| } | ||
|
|
||
| wait_for_receipt() { | ||
| local tx_hash=$1 | ||
| local timeout=${2:-60} | ||
| local elapsed=0 | ||
| local response | ||
|
|
||
| until [ "$elapsed" -ge "$timeout" ]; do | ||
| response=$(curl -s -X POST -H "Content-Type: application/json" \ | ||
| -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"eth_getTransactionReceipt\",\"params\":[\"$tx_hash\"]}" \ | ||
| "$RPC_URL" || true) | ||
| if printf "%s\n" "$response" | jq -e '.result != null' >/dev/null 2>&1; then | ||
| printf "%s\n" "$response" | ||
| return 0 | ||
| fi | ||
| sleep 1 | ||
| elapsed=$((elapsed + 1)) | ||
| done | ||
|
|
||
| echo "Timed out waiting for EVM receipt $tx_hash" >&2 | ||
| return 1 | ||
| } | ||
|
|
||
| require_success_receipt() { | ||
| local name=$1 | ||
| local receipt=$2 | ||
| local status | ||
| status=$(printf "%s\n" "$receipt" | jq -r '.result.status // empty') | ||
| if [ "$status" != "0x1" ] && [ "$status" != "1" ]; then | ||
| echo "FlatKV EVM $name failed:" >&2 | ||
| printf "%s\n" "$receipt" >&2 | ||
| exit 1 | ||
| fi | ||
| } | ||
|
|
||
| echo "Generating FlatKV EVM historical fixture via $RPC_URL..." | ||
| wait_for_evm_rpc | ||
|
|
||
| initial_height=$(block_number) | ||
| write_fixture "flatkv_evm_initial_block_height.txt" "$initial_height" | ||
| write_fixture "flatkv_evm_recipient_addr.txt" "$RECIPIENT_ADDR" | ||
| write_fixture "flatkv_evm_missing_addr.txt" "$MISSING_ADDR" | ||
| write_fixture "flatkv_evm_storage_slot.txt" "$STORAGE_SLOT_ZERO" | ||
|
|
||
| run_seid tx evm associate-address \ | ||
| --from "$FROM" \ | ||
| "${KEYRING_ARGS[@]}" \ | ||
| --chain-id "$CHAIN_ID" \ | ||
| -b block \ | ||
| -y >/tmp/flatkv_evm_associate.out 2>&1 || true | ||
|
|
||
| echo "Sending native EVM transfer to create/update recipient account..." | ||
| if ! transfer_out=$(run_seid tx evm send "$RECIPIENT_ADDR" "$TRANSFER_VALUE_WEI" \ | ||
| --from "$FROM" \ | ||
| "${KEYRING_ARGS[@]}" \ | ||
| --chain-id "$CHAIN_ID" \ | ||
| --evm-rpc "$RPC_URL" \ | ||
| -b sync \ | ||
| -y 2>&1); then | ||
| echo "FlatKV EVM transfer command failed:" >&2 | ||
| printf "%s\n" "$transfer_out" >&2 | ||
| exit 1 | ||
| fi | ||
| printf "%s\n" "$transfer_out" >/tmp/flatkv_evm_transfer.out | ||
| transfer_tx=$(printf "%s\n" "$transfer_out" | extract_tx_hash || true) | ||
| if [ -z "$transfer_tx" ]; then | ||
| echo "Failed to extract FlatKV EVM transfer tx hash:" >&2 | ||
| printf "%s\n" "$transfer_out" >&2 | ||
| exit 1 | ||
| fi | ||
| transfer_receipt=$(wait_for_receipt "$transfer_tx") | ||
| require_success_receipt "transfer" "$transfer_receipt" | ||
| printf "%s\n" "$transfer_receipt" >/tmp/flatkv_evm_transfer_receipt.json | ||
|
|
||
| balance_height=$(block_number) | ||
| balance_expected=$(query_balance_hex "$RECIPIENT_ADDR" "$balance_height") | ||
| write_fixture "flatkv_evm_balance_block_height.txt" "$balance_height" | ||
| write_fixture "flatkv_evm_balance_expected.txt" "$balance_expected" | ||
|
|
||
| echo "Deploying storage/code fixture contract..." | ||
| contract_hex_file=/tmp/flatkv_evm_storage_contract.hex | ||
| printf "%s" "${STORAGE_CONTRACT_INIT_CODE#0x}" > "$contract_hex_file" | ||
| if ! deploy_out=$(run_seid tx evm deploy "$contract_hex_file" \ | ||
| --from "$FROM" \ | ||
| "${KEYRING_ARGS[@]}" \ | ||
| --chain-id "$CHAIN_ID" \ | ||
| --evm-rpc "$RPC_URL" \ | ||
| -b sync \ | ||
| -y 2>&1); then | ||
| echo "FlatKV EVM deploy command failed:" >&2 | ||
| printf "%s\n" "$deploy_out" >&2 | ||
| exit 1 | ||
| fi | ||
| deploy_tx=$(printf "%s\n" "$deploy_out" | extract_tx_hash || true) | ||
| if [ -z "$deploy_tx" ]; then | ||
| echo "Failed to extract FlatKV EVM deploy tx hash:" >&2 | ||
| printf "%s\n" "$deploy_out" >&2 | ||
| exit 1 | ||
| fi | ||
| deploy_receipt=$(wait_for_receipt "$deploy_tx") | ||
| require_success_receipt "contract deployment" "$deploy_receipt" | ||
| printf "%s\n" "$deploy_receipt" > "$out_dir/flatkv_evm_deploy_receipt.json" | ||
|
|
||
| contract_addr=$(printf "%s\n" "$deploy_receipt" | jq -r '.result.contractAddress // empty') | ||
| if [ -z "$contract_addr" ] || [ "$contract_addr" = "null" ]; then | ||
| contract_addr=$(printf "%s\n" "$deploy_out" | sed -n 's/^Deployed to: //p' | tail -1) | ||
| fi | ||
| if [ -z "$contract_addr" ] || [ "$contract_addr" = "null" ]; then | ||
| echo "Failed to extract contract address from deploy receipt:" >&2 | ||
| printf "%s\n" "$deploy_receipt" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| contract_height=$(block_number) | ||
| storage_expected=$(query_storage "$contract_addr" "$STORAGE_SLOT_ZERO" "$contract_height") | ||
| code_expected=$(query_code "$contract_addr" "$contract_height") | ||
|
|
||
| write_fixture "flatkv_evm_contract_addr.txt" "$contract_addr" | ||
| write_fixture "flatkv_evm_contract_block_height.txt" "$contract_height" | ||
| write_fixture "flatkv_evm_storage_expected.txt" "$storage_expected" | ||
| write_fixture "flatkv_evm_code_expected.txt" "$code_expected" | ||
|
|
||
| missing_balance_expected=$(query_balance_hex "$MISSING_ADDR" "$contract_height") | ||
| missing_storage_expected=$(query_storage "$MISSING_ADDR" "$STORAGE_SLOT_ZERO" "$contract_height") | ||
| write_fixture "flatkv_evm_missing_balance_expected.txt" "$missing_balance_expected" | ||
| write_fixture "flatkv_evm_missing_storage_expected.txt" "$missing_storage_expected" | ||
|
|
||
| latest_height=$(block_number) | ||
| write_fixture "flatkv_evm_latest_fixture_block_height.txt" "$latest_height" | ||
|
|
||
| echo "FlatKV EVM fixture generated:" | ||
| echo " recipient=$RECIPIENT_ADDR balance_height=$balance_height balance=$balance_expected" | ||
| echo " contract=$contract_addr contract_height=$contract_height storage=$storage_expected" |
145 changes: 145 additions & 0 deletions
145
integration_test/contracts/import_flatkv_evm_cluster.sh
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,145 @@ | ||
| #!/bin/bash | ||
|
|
||
| set -euo pipefail | ||
|
|
||
| PROJECT_ROOT=$(git rev-parse --show-toplevel) | ||
| NODE_COUNT=${FLATKV_EVM_IMPORT_NODE_COUNT:-4} | ||
|
|
||
| dump_node_log() { | ||
| local node=$1 | ||
| echo "==================== ${node} seid log (last 200 lines) ====================" >&2 | ||
| local node_id=${node#sei-node-} | ||
| docker exec "$node" tail -200 "/sei-protocol/sei-chain/build/generated/logs/seid-${node_id}.log" >&2 || true | ||
| echo "==================== ${node} end log ====================" >&2 | ||
| } | ||
|
|
||
| wait_for_height() { | ||
| local min_height=$1 | ||
| local timeout=${2:-180} | ||
| local elapsed=0 | ||
| local height=0 | ||
|
|
||
| until [ "$elapsed" -ge "$timeout" ]; do | ||
| height=$(docker exec sei-node-0 build/seid status 2>/dev/null | jq -r ".SyncInfo.latest_block_height // 0" || echo 0) | ||
| if [ "$height" -gt "$min_height" ]; then | ||
| echo "sei-node-0 reached height $height" | ||
| return 0 | ||
| fi | ||
| echo "Still waiting for sei-node-0 to advance past height $min_height (height=$height elapsed=${elapsed}s/${timeout}s)" | ||
| sleep 5 | ||
| elapsed=$((elapsed + 5)) | ||
| done | ||
|
|
||
| echo "Timed out waiting for sei-node-0 to advance past height $min_height (last height: $height)" >&2 | ||
| for i in $(seq 0 $((NODE_COUNT - 1))); do | ||
| dump_node_log "sei-node-$i" | ||
| done | ||
| return 1 | ||
| } | ||
|
|
||
| # wait_for_evm_rpc polls each node's EVM HTTP endpoint until it responds, so | ||
| # the post-restart flatkv_evm_test.yaml run can't race the seid restart and | ||
| # hit connection refused on http://localhost:8545. Tendermint typically | ||
| # advances a height or two before the in-process EVM RPC server finishes | ||
| # binding 8545, so wait_for_height alone is not a sufficient readiness gate | ||
| # for the next test phase. | ||
| wait_for_evm_rpc() { | ||
| local timeout=${1:-120} | ||
| for i in $(seq 0 $((NODE_COUNT - 1))); do | ||
| local node="sei-node-$i" | ||
| local elapsed=0 | ||
| until docker exec "$node" bash -lc 'curl -sf -o /dev/null -X POST -H "Content-Type: application/json" -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"eth_blockNumber\",\"params\":[]}" http://localhost:8545'; do | ||
| if [ "$elapsed" -ge "$timeout" ]; then | ||
| echo "EVM RPC on $node did not become ready within ${timeout}s after restart" >&2 | ||
| dump_node_log "$node" | ||
| return 1 | ||
| fi | ||
| echo "Waiting for EVM RPC on $node (elapsed=${elapsed}s/${timeout}s)" | ||
| sleep 3 | ||
| elapsed=$((elapsed + 3)) | ||
| done | ||
| echo "EVM RPC on $node is responding" | ||
| done | ||
| } | ||
|
|
||
| echo "Building seidb import tool..." | ||
| # Go lives at /usr/local/go/bin/go in the container (see docker/localnode/Dockerfile) | ||
| # but is not on the default PATH for non-interactive shells, so call it absolutely. | ||
| GO_BIN=${GO_BIN:-/usr/local/go/bin/go} | ||
| docker exec -e GOPROXY="${GOPROXY:-https://proxy.golang.org,direct}" sei-node-0 bash -c "cd /sei-protocol/sei-chain && $GO_BIN build -o build/seidb ./sei-db/tools/cmd/seidb" | ||
|
|
||
| start_height=$(docker exec sei-node-0 build/seid status | jq -r ".SyncInfo.latest_block_height") | ||
| echo "Stopping seid processes at height $start_height..." | ||
| for i in $(seq 0 $((NODE_COUNT - 1))); do | ||
| docker exec "sei-node-$i" pkill -f "seid start" >/dev/null 2>&1 || true | ||
| done | ||
|
|
||
| echo "Waiting for seid processes to stop..." | ||
| for i in $(seq 0 $((NODE_COUNT - 1))); do | ||
| stopped=false | ||
| for _ in $(seq 1 30); do | ||
| if ! docker exec "sei-node-$i" pgrep -f "seid start" >/dev/null 2>&1; then | ||
| stopped=true | ||
| break | ||
| fi | ||
| sleep 1 | ||
| done | ||
| if [ "$stopped" != "true" ]; then | ||
| echo "sei-node-$i did not stop within 30s" >&2 | ||
| exit 1 | ||
| fi | ||
| done | ||
|
|
||
| echo "Importing evm module from memiavl into FlatKV on all validators..." | ||
| for i in $(seq 0 $((NODE_COUNT - 1))); do | ||
| docker exec "sei-node-$i" bash -lc "cd /sei-protocol/sei-chain && build/seidb import-flatkv-from-memiavl --modules=evm --data-dir /root/.sei/data" | ||
| done | ||
|
|
||
| echo "Applying GIGA_STORAGE config and restarting seid processes..." | ||
| for i in $(seq 0 $((NODE_COUNT - 1))); do | ||
| docker exec -e GIGA_STORAGE=true "sei-node-$i" /usr/bin/config_override.sh | ||
| # The import tool moves only SC-layer EVM data into FlatKV. SS history | ||
| # for EVM stays in the existing combined cosmos pebbledb, so we must keep | ||
| # evm-ss-split=false to avoid the rootmulti startup panic: | ||
| # "EVM SS directory ... does not exist but Cosmos SS already has history". | ||
| # Switching the SS layer to split mode mid-life requires a separate state-sync | ||
| # workflow which is out of scope for this SC import test. | ||
| docker exec "sei-node-$i" sed -i 's/evm-ss-split = true/evm-ss-split = false/' /root/.sei/config/app.toml | ||
| # Lattice hash must also stay off across the import boundary. Pre-import | ||
| # the chain ran without FlatKV, so tendermint persisted AppHash = memiavl-only | ||
| # for all blocks up to the import height. Turning sc-enable-lattice-hash | ||
| # on now would fold the FlatKV LtHash into the AppHash and the replay check | ||
| # at startup would fail with "state.AppHash does not match AppHash after replay". | ||
| # dual_write does not require lattice hash (see sei-db/config/toml_test.go); | ||
| # only split_write does. A real production rollout would coordinate this | ||
| # transition via a chain upgrade at an agreed height. | ||
| docker exec "sei-node-$i" sed -i 's/sc-enable-lattice-hash = true/sc-enable-lattice-hash = false/' /root/.sei/config/app.toml | ||
| done | ||
| # `docker exec -d` is required: start_sei.sh backgrounds seid then exits, and a | ||
| # non-detached docker exec session would close stdout/stderr, killing seid. | ||
| # See integration_test/autobahn/autobahn_test.go::restartNode for the precedent. | ||
| for i in $(seq 0 $((NODE_COUNT - 1))); do | ||
| docker exec -d -e "ID=$i" "sei-node-$i" /usr/bin/start_sei.sh | ||
| done | ||
|
|
||
| # Confirm each seid actually came up before waiting on block production, so a | ||
| # crash on startup is reported promptly instead of after the 4 minute timeout. | ||
| sleep 5 | ||
| for i in $(seq 0 $((NODE_COUNT - 1))); do | ||
| if ! docker exec "sei-node-$i" pgrep -f "seid start" >/dev/null 2>&1; then | ||
| echo "ERROR: sei-node-$i did not stay running after restart" >&2 | ||
| dump_node_log "sei-node-$i" | ||
| exit 1 | ||
| fi | ||
| done | ||
|
|
||
| wait_for_height "$start_height" 240 | ||
|
|
||
| # Tendermint advancing past start_height does NOT imply the in-process EVM | ||
| # RPC HTTP server has finished binding 8545. The downstream | ||
| # integration_test/seidb/flatkv_evm_test.yaml docker-execs `cast` against | ||
| # http://localhost:8545; gate on that endpoint explicitly so it can't race | ||
| # the seid restart. | ||
| wait_for_evm_rpc 120 | ||
|
|
||
| echo "FlatKV EVM import completed for $NODE_COUNT validators in $PROJECT_ROOT" | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seidb binary built only on node-0 but executed on all nodes
High Severity
The
seidbtool is compiled viadocker execonly onsei-node-0, but the import loop at lines 94–96 runsbuild/seidb import-flatkv-from-memiavlon every node (sei-node-0throughsei-node-3). If the containers don't share the/sei-protocol/sei-chain/build/directory (i.e., each has its own filesystem layer), the binary won't exist on nodes 1–3 and the import will fail with a "command not found" or "no such file" error.Additional Locations (1)
integration_test/contracts/import_flatkv_evm_cluster.sh#L93-L96Reviewed by Cursor Bugbot for commit 268cd4b. Configure here.