diff --git a/.github/workflows/rpc-comparison.yaml b/.github/workflows/rpc-comparison.yaml new file mode 100644 index 00000000000..2b06af057f5 --- /dev/null +++ b/.github/workflows/rpc-comparison.yaml @@ -0,0 +1,261 @@ +name: '[JSON-RPC] Compare Nethermind between clients and versions' + +on: + workflow_dispatch: + inputs: + allowed_ips: + type: string + description: "A comma-separated list of ips allowed to connect to the node" + default: '' + required: false + branch_to_compare: + type: string + description: "A space-separated list of branches. If empty, then selected branch will be used. If multiple specified, those will be compared to themselves." + default: "" + required: false + compare_with: + type: string + description: "A space-separated list of additional comparers. If empty, then nothing else will be added to comparision. Possible options: 'INFURA_ENDPOINT', 'NETHERMIND_ARCHIVE_ENDPOINT'" + default: "" + required: false + +jobs: + create_main_node: + name: Create node from current branch + uses: ./.github/workflows/run-a-single-node-from-branch.yml + secrets: inherit + with: + additional_options: '{"timeout":"12", "default_dockerfile":"Dockerfile", "default_dockerfile_build_type":"release", "ssh_keys":"", "allowed_ips":"${{ inputs.allowed_ips }}"}' + non_validator_mode: false + additional_nethermind_flags: Pruning.Mode=None JsonRpc.EnabledModules=[Eth,Subscribe,Trace,TxPool,Web3,Personal,Proof,Net,Parity,Health,Rpc,Debug,Admin] + nethermind_repo_ref: ${{ github.ref }} + custom_run_id: ${{ github.run_id }} + + create_compare_node: + name: Create node from branch to compare + uses: ./.github/workflows/run-a-single-node-from-branch.yml + if: inputs.branch_to_compare != '' + secrets: inherit + with: + additional_options: '{"timeout":"12", "default_dockerfile":"Dockerfile", "default_dockerfile_build_type":"release", "ssh_keys":"", "allowed_ips":"${{ inputs.allowed_ips }}"}' + non_validator_mode: false + additional_nethermind_flags: Pruning.Mode=None JsonRpc.EnabledModules=[Eth,Subscribe,Trace,TxPool,Web3,Personal,Proof,Net,Parity,Health,Rpc,Debug,Admin] + nethermind_repo_ref: ${{ inputs.branch_to_compare }} + custom_run_id: ${{ github.run_id }} + + aggregate_rpcs: + name: Collect all RPC Urls and pass it further + runs-on: ubuntu-latest + needs: [create_main_node, create_compare_node] + outputs: + rpc_urls: ${{ steps.process_artifacts.outputs.rpc_urls }} + steps: + + - name: Prepare clean main ref + id: prepare_main_ref + run: | + REF_NAME=${{ github.ref }} + CLEAN_REF=$(echo "${REF_NAME/refs\/heads\//}" | sed 's/[^a-zA-Z0-9._-]/-/g') + echo "CLEAN_MAIN_REF=$CLEAN_REF" >> $GITHUB_ENV + + - name: Prepare clean compare ref + id: prepare_compare_ref + if: inputs.branch_to_compare != '' + run: | + REF_NAME=${{ inputs.nethermind_repo_ref }} + CLEAN_REF=$(echo "${REF_NAME/refs\/heads\//}" | sed 's/[^a-zA-Z0-9._-]/-/g') + echo "CLEAN_COMPARE_REF=$CLEAN_REF" >> $GITHUB_ENV + + - name: Download RPC Artifact for current branch + uses: actions/download-artifact@v3 + with: + name: rpc-url___${{ env.CLEAN_MAIN_REF }}___${{ github.run_id }} + path: artifacts + + - name: Download RPC Artifact for branch to compare + if: inputs.branch_to_compare != '' + uses: actions/download-artifact@v3 + with: + name: rpc-url___${{ env.CLEAN_COMPARE_REF }}___${{ github.run_id }} + path: artifacts + + - name: Process Artifacts Content + id: process_artifacts + run: | + rpc_urls="" + main_branch_file="rpc_url%${{ env.CLEAN_MAIN_REF }}%${{ github.run_id }}.txt" + compare_branch_file="rpc_url%${{ env.CLEAN_COMPARE_REF }}%${{ github.run_id }}" + + ls artifacts + # Check and add the main branch file + if [ -f "artifacts/$main_branch_file" ]; then + url_content=$(cat "artifacts/$main_branch_file") + echo $url_content + rpc_urls+="${{ env.CLEAN_MAIN_REF }}::$url_content," + fi + + # Check and add the compare branch file + if [ -f "artifacts/$compare_branch_file" ]; then + url_content=$(cat "artifacts/$compare_branch_file") + echo $url_content + rpc_urls+="${{ env.CLEAN_COMPARE_REF }}::$url_content," + fi + + rpc_urls=${rpc_urls%,} + echo $rpc_urls + echo "rpc_urls=$rpc_urls" >> $GITHUB_OUTPUT + + wait_for_node_to_sync: + name: Wait for the nodes to sync + runs-on: ubuntu-latest + needs: [aggregate_rpcs] + timeout-minutes: 600 + steps: + - uses: actions/checkout@v3 + - name: Install WireGuard + run: | + sudo apt update + sudo apt install -y wireguard resolvconf + sudo mkdir -p /etc/wireguard + envsubst < scripts/wireguard.conf.template > wg0.conf + sudo wg-quick up ./wg0.conf + env: + WIREGUARD_PRIVKEY: '${{ secrets.WIREGUARD_PRIVKEY }}' + WIREGUARD_ADDRESS: '${{ secrets.WIREGUARD_ADDRESS }}' + WIREGUARD_DNS: '${{ secrets.WIREGUARD_DNS }}' + WIREGUARD_PUBKEY: '${{ secrets.WIREGUARD_PUBKEY }}' + WIREGUARD_PRESHAREDKEY: '${{ secrets.WIREGUARD_PRESHAREDKEY }}' + WIREGUARD_ALLOWED_IPS: '${{ secrets.WIREGUARD_ALLOWED_IPS }}' + WIREGUARD_SERVER_IP: '${{ secrets.WIREGUARD_SERVER_IP }}' + WIREGUARD_SERVER_PORT: '${{ secrets.WIREGUARD_SERVER_PORT }}' + + - name: Wait for the nodes to sync + timeout-minutes: 600 + run: | + # Assuming rpc_urls_str is a comma-separated string of URLs + rpc_urls_str="${{ needs.aggregate_rpcs.outputs.rpc_urls }}" + IFS=',' read -r -a rpc_urls_array <<< "$rpc_urls_str" + + # Loop through the array and strip the branch prefix + processed_rpc_urls=() + for url_entry in "${rpc_urls_array[@]}"; do + processed_url="${url_entry#*::}" # Remove everything up to and including "::" + processed_rpc_urls+=("$processed_url") + done + + sync_complete_flag=0 + + check_sync() { + rpc_url=$1 + while curl -X POST --data '{"jsonrpc":"2.0","method":"eth_syncing","params":[],"id":1}' "$rpc_url" | jq -e '.result'; do + echo "Still waiting for node to be synced at RPC: $rpc_url." + sleep 60 + done + echo "Node at $rpc_url synced." + sync_complete_flag=$((sync_complete_flag+1)) + } + + # Just an RPC warmup - to make sure no faulty info reached a check + sleep 60 + + # Iterate over the processed array of URLs + for url in "${processed_rpc_urls[@]}"; do + check_sync "$url" + done + + + compare: + name: Compare JSON-RPC responses between clients and versions + runs-on: ubuntu-latest + needs: [wait_for_node_to_sync, aggregate_rpcs] + steps: + - uses: actions/checkout@v3 + - name: Install flood + run: pip install git+https://github.com/piwonskp/flood.git + + - name: Install WireGuard + run: | + sudo apt update + sudo apt install -y wireguard resolvconf + sudo mkdir -p /etc/wireguard + envsubst < scripts/wireguard.conf.template > wg0.conf + sudo wg-quick up ./wg0.conf + env: + WIREGUARD_PRIVKEY: '${{ secrets.WIREGUARD_PRIVKEY }}' + WIREGUARD_ADDRESS: '${{ secrets.WIREGUARD_ADDRESS }}' + WIREGUARD_DNS: '${{ secrets.WIREGUARD_DNS }}' + WIREGUARD_PUBKEY: '${{ secrets.WIREGUARD_PUBKEY }}' + WIREGUARD_PRESHAREDKEY: '${{ secrets.WIREGUARD_PRESHAREDKEY }}' + WIREGUARD_ALLOWED_IPS: '${{ secrets.WIREGUARD_ALLOWED_IPS }}' + WIREGUARD_SERVER_IP: '${{ secrets.WIREGUARD_SERVER_IP }}' + WIREGUARD_SERVER_PORT: '${{ secrets.WIREGUARD_SERVER_PORT }}' + + - name: Prepare Comparison Flags + id: prep_comparison + env: + INFURA_ENDPOINT: '${{ secrets.INFURA_ENDPOINT }}' + NETHERMIND_ARCHIVE_ENDPOINT: '${{ secrets.NETHERMIND_ARCHIVE_ENDPOINT }}' + run: | + others_str="${{ github.event.inputs.compare_with }}" + rpc_urls_str="${{ needs.aggregate_rpcs.outputs.rpc_urls }}" + + IFS=',' read -r -a rpc_urls_array <<< "$rpc_urls_str" + unset IFS + IFS=' ' read -r -a others_str_array <<< "$others_str" + unset IFS + + # Construct nethermind_urls_str as an array + nethermind_urls_str=() + for url_entry in "${rpc_urls_array[@]}"; do + branch="${url_entry%%::*}" # Extract the branch part + url="${url_entry#*::}" # Extract the URL part + nethermind_urls_str+=("nethermind_$branch=$url") + done + + # Construct others_prepared_str as an array + others_prepared_str=() + for other in "${others_str_array[@]}"; do + if [[ "$other" == "INFURA_ENDPOINT" ]]; then + others_prepared_str+=("$other=${INFURA_ENDPOINT}") + elif [[ "$other" == "NETHERMIND_ARCHIVE_ENDPOINT" ]]; then + others_prepared_str+=("$other=${NETHERMIND_ARCHIVE_ENDPOINT}") + fi + done + + # Flag for comparing to other branch + if [ ${#nethermind_urls_str[@]} -gt 1 ]; then + echo "compare_to_other_branch=true" >> $GITHUB_ENV + echo "compare_to_other_branch_params=${nethermind_urls_str[0]} ${nethermind_urls_str[1]}" >> $GITHUB_ENV + else + echo "compare_to_other_branch=false" >> $GITHUB_ENV + fi + + # Flags for comparing to INFURA and ARCHIVE endpoints + compare_to_infura=false + compare_to_archive=false + for (( j = 0; j < ${#others_prepared_str[@]}; j++ )); do + if [[ "${others_prepared_str[j]}" == "INFURA_ENDPOINT=${INFURA_ENDPOINT}" ]]; then + compare_to_infura=true + echo "compare_to_infura_params=${nethermind_urls_str[0]} ${others_prepared_str[j]}" >> $GITHUB_ENV + elif [[ "${others_prepared_str[j]}" == "NETHERMIND_ARCHIVE_ENDPOINT=${NETHERMIND_ARCHIVE_ENDPOINT}" ]]; then + compare_to_archive=true + echo "compare_to_archive_params=${nethermind_urls_str[0]} ${others_prepared_str[j]}" >> $GITHUB_ENV + fi + done + echo "compare_to_infura=$compare_to_infura" >> $GITHUB_ENV + echo "compare_to_archive=$compare_to_archive" >> $GITHUB_ENV + + - name: Compare to Other Branch + if: env.compare_to_other_branch == 'true' + run: | + flood all ${compare_to_other_branch_params} --equality + + - name: Compare to INFURA Endpoint + if: env.compare_to_infura == 'true' + run: | + flood all ${compare_to_infura_params} --equality + + - name: Compare to Nethermind Archive Endpoint + if: env.compare_to_archive == 'true' + run: | + flood all ${compare_to_archive_params} --equality diff --git a/.github/workflows/run-a-single-node-from-branch.yml b/.github/workflows/run-a-single-node-from-branch.yml index 79d37bd69c5..5caf4594466 100644 --- a/.github/workflows/run-a-single-node-from-branch.yml +++ b/.github/workflows/run-a-single-node-from-branch.yml @@ -56,6 +56,73 @@ on: additional_options: description: "A Json property which allows to customize node even more" default: '{"timeout":"24", "default_dockerfile":"Dockerfile", "default_dockerfile_build_type":"release", "ssh_keys":"", "allowed_ips":""}' + workflow_call: + inputs: + smoke_tests_ref: + description: "Ref of the smoke tests repository to be used for smoke tests" + default: "main" + required: false + type: string + nethermind_repo_ref: + description: "Ref of the nethermind repo in case it is needed to be changed" + default: "" + required: false + type: string + custom_run_id: + description: "Run ID used for artifacts save" + default: "" + required: false + type: string + network: + description: "Select a network on which You want to run a node" + default: "mainnet" + required: false + type: string + cl_client: + description: "Select Consensus Layer Client to run node against" + default: "lighthouse" + required: false + type: string + cl_custom_image: + description: "In case of need to run non-default cl image (different than actually supported by Sedge) put it in there" + default: "" + required: false + type: string + config: + description: "Select a config file which will be selected for tests." + default: "default.json" + required: false + type: string + non_validator_mode: + description: "If checked, node will be started in NonValidator mode (OldBodies and oldReceipts will not be synced)" + default: true + type: boolean + additional_nethermind_flags: + type: string + description: "Provide any additional flags to the Nethermind in space-separated format. Example: \"JsonRpc.Enabled=false Sync.SnapSync=false\"." + default: "" + required: false + additional_cl_flags: + type: string + description: "Provide any additional flags to the CL client in space-separated format. Example: \"clflag1=1 clflag2=2\"." + default: "" + required: false + additional_options: + type: string + description: "A Json property which allows to customize node even more" + default: '{"timeout":"24", "default_dockerfile":"Dockerfile", "default_dockerfile_build_type":"release", "ssh_keys":"", "allowed_ips":""}' + required: false + outputs: + base_tag: + description: "" + value: ${{ jobs.create_docker_image.outputs.base_tag }} + run_id: + description: "" + value: ${{ jobs.trigger_node_and_vm_creation.outputs.run_id }} + rpc_url: + description: "" + value: ${{ jobs.trigger_node_and_vm_creation.outputs.rpc_url }} + jobs: create_docker_image: @@ -64,12 +131,14 @@ jobs: base_tag: ${{ steps.set-base-tag.outputs.base_tag }} steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 + with: + ref: ${{ inputs.nethermind_repo_ref }} - name: Prepare docker tag id: prepare_ref run: | - REF_NAME=${{ github.ref }} + REF_NAME=${{ inputs.nethermind_repo_ref || github.ref }} CLEAN_REF=$(echo "${REF_NAME/refs\/heads\//}" | sed 's/[^a-zA-Z0-9._-]/-/g') echo "CLEAN_REF=$CLEAN_REF" >> $GITHUB_ENV @@ -99,8 +168,17 @@ jobs: - name: Check if master branch and default additional_options id: check_conditions run: | + ref="${{ inputs.nethermind_repo_ref || github.ref }}" + if [ -z "$ref" ]; then + ref="${{ github.ref }}" + fi + # Append "refs/heads/" prefix if it's not already there + if [[ $ref != refs/heads/* ]]; then + ref="refs/heads/$ref" + fi + if - [[ "${{ github.ref }}" == "refs/heads/master" ]] && + [[ "$ref" == "refs/heads/master" ]] && [[ "${{ steps.extract_dockerfile.outputs.dockerfile }}" == "Dockerfile" ]] && [[ "${{ steps.extract_dockerfile.outputs.build-config }}" == "release" ]]; then echo "skip_docker_build=true" >> $GITHUB_OUTPUT @@ -144,12 +222,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 + with: + ref: ${{ inputs.nethermind_repo_ref }} - name: Prepare docker tag id: prepare_ref run: | - REF_NAME=${{ github.ref }} + REF_NAME=${{ inputs.nethermind_repo_ref || github.ref }} CLEAN_REF=$(echo "${REF_NAME/refs\/heads\//}" | sed 's/[^a-zA-Z0-9._-]/-/g') echo "CLEAN_REF=$CLEAN_REF" >> $GITHUB_ENV @@ -206,4 +286,20 @@ jobs: - name: Display machine specs content run: | FILE=$(ls downloaded-artifacts/machine-details | head -n 1) - cat "downloaded-artifacts/machine-details/$FILE" + cat "downloaded-artifacts/machine-details/$FILE" | tee spec-output.txt + rpc_url=$(grep -oP '\s{2,3}RPC URL: \K.+' spec-output.txt) + echo "RPC URL extracted is: $rpc_url" + echo "RPC_URL=$rpc_url" >> $GITHUB_ENV + + - name: Save RPC URL to file + if: inputs.custom_run_id != '' + run: | + workflow_id=${{ inputs.custom_run_id }} + echo "${{ env.RPC_URL }}" > rpc_url%${{ env.CLEAN_REF }}%${{ inputs.custom_run_id }}.txt + + - name: Upload RPC URL + uses: actions/upload-artifact@v3 + if: inputs.custom_run_id != '' + with: + name: rpc-url___${{ env.CLEAN_REF }}___${{ inputs.custom_run_id }} + path: rpc_url%${{ env.CLEAN_REF }}%${{ inputs.custom_run_id }}.txt diff --git a/.gitignore b/.gitignore index cd679f81dab..3e95fc13ce6 100644 --- a/.gitignore +++ b/.gitignore @@ -403,3 +403,4 @@ FodyWeavers.xsd ## Nethermind keystore/ +/.githooks diff --git a/scripts/wait-for-workflow-completed.sh b/scripts/wait-for-workflow-completed.sh index 37bcc0d0870..a724801b3c1 100644 --- a/scripts/wait-for-workflow-completed.sh +++ b/scripts/wait-for-workflow-completed.sh @@ -43,7 +43,7 @@ else fi run_id=$(echo "$response" | \ jq -r --arg ref "$(echo "$REF" | sed 's/refs\/heads\///')" --arg current_time "$current_time" \ - '.workflow_runs[] | select(.head_branch == $ref and .created_at >= $current_time) | .id') + '.workflow_runs[] | select(.head_branch == $ref and .created_at >= $current_time) | .id' | sort -r | head -n 1) if [ -n "$run_id" ]; then echo "🎉 Workflow triggered! Run ID: $run_id" break @@ -75,6 +75,8 @@ while true; do exit 1 else echo "✅ The workflow completed successfully! Exiting." + echo "👀 Check workflow details at: https://github.com/${ORG_NAME}/${REPO_NAME}/actions/runs/$run_id" + echo "run_id=$run_id" >> $GITHUB_OUTPUT break fi fi diff --git a/scripts/wireguard.conf.template b/scripts/wireguard.conf.template new file mode 100644 index 00000000000..4d8bce58fca --- /dev/null +++ b/scripts/wireguard.conf.template @@ -0,0 +1,10 @@ +[Interface] +PrivateKey = ${WIREGUARD_PRIVKEY} +Address = ${WIREGUARD_ADDRESS} +DNS = ${WIREGUARD_DNS} + +[Peer] +PublicKey = ${WIREGUARD_PUBKEY} +PresharedKey = ${WIREGUARD_PRESHAREDKEY} +AllowedIPs = ${WIREGUARD_ALLOWED_IPS} +Endpoint = ${WIREGUARD_SERVER_IP}:${WIREGUARD_SERVER_PORT}