mripper #111
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
| name: mripper | |
| on: | |
| schedule: | |
| - cron: "0 15 * * *" | |
| workflow_dispatch: | |
| inputs: | |
| image: | |
| description: "GCP image" | |
| required: false | |
| default: "projects/ubuntu-os-cloud/global/images/ubuntu-2404-noble-amd64-v20260225" | |
| type: string | |
| permissions: | |
| contents: write | |
| env: | |
| PROJECT: flashbots-tests | |
| IMAGE: ${{ inputs.image || 'projects/ubuntu-os-cloud/global/images/ubuntu-2404-noble-amd64-v20260225' }} | |
| NETWORK: default | |
| SUBNET: default | |
| jobs: | |
| rip: | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - zone: us-east4-a | |
| machine_type: c3-standard-4 | |
| - zone: us-east4-a | |
| machine_type: c3-standard-8 | |
| - zone: us-east4-a | |
| machine_type: c3-standard-22 | |
| - zone: us-east4-a | |
| machine_type: c3-standard-44 | |
| - zone: us-east5-b | |
| machine_type: c3-standard-4 | |
| # No quota | |
| # - zone: us-east5-b | |
| # machine_type: c3-standard-22 | |
| # - zone: us-east5-b | |
| # machine_type: c3-standard-44 | |
| - zone: europe-west4-a | |
| machine_type: c3-standard-4 | |
| - zone: europe-west4-a | |
| machine_type: c3-standard-22 | |
| - zone: europe-west4-a | |
| machine_type: c3-standard-44 | |
| - zone: asia-northeast1-b | |
| machine_type: c3-standard-4 | |
| - zone: asia-northeast1-b | |
| machine_type: c3-standard-22 | |
| - zone: asia-northeast1-b | |
| machine_type: c3-standard-44 | |
| runs-on: ubuntu-latest | |
| name: "${{ matrix.zone }} / ${{ matrix.machine_type }}" | |
| env: | |
| INSTANCE_NAME: "mr-${{ matrix.zone }}-${{ matrix.machine_type }}-${{ github.run_id }}" | |
| ZONE: ${{ matrix.zone }} | |
| MACHINE_TYPE: ${{ matrix.machine_type }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Authenticate to GCP | |
| uses: google-github-actions/auth@v3 | |
| with: | |
| credentials_json: "${{ secrets.GCP_SA_KEY }}" | |
| - name: Set up gcloud | |
| uses: google-github-actions/setup-gcloud@v3 | |
| - name: Create VM | |
| run: | | |
| FIREWALL_RULE="${INSTANCE_NAME}-ssh" | |
| echo "FIREWALL_RULE=${FIREWALL_RULE}" >> "$GITHUB_ENV" | |
| gcloud compute instances create "$INSTANCE_NAME" \ | |
| --project="$PROJECT" \ | |
| --zone="$ZONE" \ | |
| --machine-type="$MACHINE_TYPE" \ | |
| --image="$IMAGE" \ | |
| --network="$NETWORK" \ | |
| --subnet="$SUBNET" \ | |
| --confidential-compute-type=TDX \ | |
| --maintenance-policy=TERMINATE \ | |
| --no-shielded-secure-boot \ | |
| --no-shielded-vtpm \ | |
| --boot-disk-size=10GB \ | |
| --boot-disk-type=pd-balanced \ | |
| --tags="$INSTANCE_NAME" | |
| gcloud compute firewall-rules create "$FIREWALL_RULE" \ | |
| --project="$PROJECT" \ | |
| --network="$NETWORK" \ | |
| --allow=tcp:22 \ | |
| --target-tags="$INSTANCE_NAME" \ | |
| --direction=INGRESS | |
| - name: Wait for SSH | |
| run: | | |
| for i in $(seq 1 60); do | |
| if gcloud compute ssh "$INSTANCE_NAME" \ | |
| --project="$PROJECT" --zone="$ZONE" \ | |
| --command="true" --quiet 2>/dev/null; then | |
| echo "SSH is ready" | |
| exit 0 | |
| fi | |
| sleep 5 | |
| done | |
| echo "::error::Timed out waiting for SSH" | |
| exit 1 | |
| - name: Run computed measurement | |
| run: | | |
| gcloud compute scp ./rip.sh "${INSTANCE_NAME}:/tmp/rip.sh" \ | |
| --project="$PROJECT" --zone="$ZONE" --quiet | |
| gcloud compute ssh "$INSTANCE_NAME" \ | |
| --project="$PROJECT" --zone="$ZONE" \ | |
| --command="chmod +x /tmp/rip.sh && sudo /tmp/rip.sh computed" | |
| - name: Fetch computed measurement | |
| run: | | |
| mkdir -p mr | |
| gcloud compute scp "${INSTANCE_NAME}:/tmp/dcap-computed.json" ./dcap-computed.json \ | |
| --project="$PROJECT" --zone="$ZONE" --quiet | |
| - name: Compare with existing measurement | |
| id: compare | |
| run: | | |
| PREFIX="${ZONE}_${MACHINE_TYPE}" | |
| FILES=$(ls mr/${PREFIX}_*.json 2>/dev/null || true) | |
| if [[ -z "$FILES" ]]; then | |
| echo "No existing measurements found for ${PREFIX}" | |
| echo "changed=true" >> "$GITHUB_OUTPUT" | |
| # All registers are new | |
| jq -r 'keys[]' ./dcap-computed.json | tr '\n' ',' | sed 's/,$//' > /tmp/changed-registers.txt | |
| else | |
| MATCHED="" | |
| for f in $FILES; do | |
| if diff -q <(jq -S . "$f") <(jq -S . ./dcap-computed.json) >/dev/null 2>&1; then | |
| MATCHED="$f" | |
| break | |
| fi | |
| done | |
| if [[ -n "$MATCHED" ]]; then | |
| echo "Computed DCAP measurement MATCHES existing: $MATCHED" | |
| echo "changed=false" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "Computed DCAP measurement DIFFERS from all existing measurements" | |
| echo "changed=true" >> "$GITHUB_OUTPUT" | |
| # Diff against the latest file to identify which registers changed | |
| LATEST=$(ls -t mr/${PREFIX}_*.json | head -1) | |
| CHANGED_REGS="" | |
| for key in $(jq -r 'keys[]' ./dcap-computed.json); do | |
| OLD=$(jq -S --arg k "$key" '.[$k]' "$LATEST") | |
| NEW=$(jq -S --arg k "$key" '.[$k]' ./dcap-computed.json) | |
| if [[ "$OLD" != "$NEW" ]]; then | |
| CHANGED_REGS="${CHANGED_REGS:+${CHANGED_REGS},}${key}" | |
| fi | |
| done | |
| echo "${CHANGED_REGS:-unknown}" > /tmp/changed-registers.txt | |
| fi | |
| fi | |
| - name: Run raw measurement | |
| if: steps.compare.outputs.changed == 'true' | |
| run: | | |
| gcloud compute ssh "$INSTANCE_NAME" \ | |
| --project="$PROJECT" --zone="$ZONE" \ | |
| --command="sudo /tmp/rip.sh raw" | |
| - name: Fetch raw measurement | |
| if: steps.compare.outputs.changed == 'true' | |
| run: | | |
| mkdir -p dcap-raw | |
| gcloud compute scp "${INSTANCE_NAME}:/tmp/dcap-raw" ./dcap-raw-result \ | |
| --project="$PROJECT" --zone="$ZONE" --quiet | |
| - name: Save results | |
| if: steps.compare.outputs.changed == 'true' | |
| run: | | |
| TIMESTAMP=$(date +%Y-%m-%dT%H%M%S) | |
| PREFIX="${ZONE}_${MACHINE_TYPE}" | |
| MR_FILE="mr/${PREFIX}_${TIMESTAMP}.json" | |
| RAW_FILE="dcap-raw/${PREFIX}_${TIMESTAMP}" | |
| cp ./dcap-computed.json "$MR_FILE" | |
| cp ./dcap-raw-result "$RAW_FILE" | |
| # Save paths for the change summary artifact | |
| echo "$MR_FILE" > /tmp/mr-path.txt | |
| echo "$RAW_FILE" > /tmp/raw-path.txt | |
| - name: Upload change summary | |
| if: steps.compare.outputs.changed == 'true' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: "change-${{ matrix.zone }}-${{ matrix.machine_type }}" | |
| path: | | |
| /tmp/changed-registers.txt | |
| /tmp/mr-path.txt | |
| /tmp/raw-path.txt | |
| - name: Commit and push | |
| if: steps.compare.outputs.changed == 'true' | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| PREFIX="${ZONE}_${MACHINE_TYPE}" | |
| git add "mr/${PREFIX}_"*.json "dcap-raw/${PREFIX}_"* | |
| if git diff --cached --quiet; then | |
| echo "No changes to commit" | |
| else | |
| git commit -m "mripper: new measurement for ${PREFIX}" | |
| for i in 1 2 3 4 5; do | |
| git pull --rebase && git push && exit 0 | |
| sleep $((i * 2)) | |
| done | |
| echo "::error::Failed to push after 5 attempts" | |
| exit 1 | |
| fi | |
| - name: Cleanup VM | |
| if: always() | |
| run: | | |
| gcloud compute firewall-rules delete "$FIREWALL_RULE" \ | |
| --project="$PROJECT" --quiet 2>/dev/null || true | |
| gcloud compute instances delete "$INSTANCE_NAME" \ | |
| --project="$PROJECT" --zone="$ZONE" --quiet 2>/dev/null || true | |
| notify: | |
| needs: rip | |
| if: always() | |
| runs-on: ubuntu-latest | |
| name: Slack notification | |
| steps: | |
| - name: Download change summaries | |
| uses: actions/download-artifact@v8 | |
| with: | |
| pattern: "change-*" | |
| path: changes | |
| continue-on-error: true | |
| - name: Build and send Slack message | |
| env: | |
| SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} | |
| run: | | |
| # Check if any changes were detected | |
| if [[ ! -d changes ]] || [[ -z "$(ls changes/)" ]]; then | |
| echo "No measurement changes detected, skipping notification" | |
| exit 0 | |
| fi | |
| # Build the details section from each artifact | |
| # Artifact dirs are named: change-{zone}-{machine_type} | |
| DETAILS="" | |
| for dir in changes/change-*; do | |
| [[ -d "$dir" ]] || continue | |
| # Parse zone and machine type from directory name | |
| NAME=$(basename "$dir") | |
| # Strip "change-" prefix | |
| COMBO="${NAME#change-}" | |
| # Split on last hyphen-delimited segment that starts with c3 | |
| # e.g. us-east4-a-c3-standard-4 -> zone=us-east4-a machine=c3-standard-4 | |
| ZONE=$(echo "$COMBO" | sed 's/-\(c3-.*\)$//') | |
| MACHINE=$(echo "$COMBO" | grep -o 'c3-.*') | |
| REGISTERS=$(cat "$dir/changed-registers.txt" 2>/dev/null || echo "unknown") | |
| MR_PATH=$(cat "$dir/mr-path.txt" 2>/dev/null || true) | |
| RAW_PATH=$(cat "$dir/raw-path.txt" 2>/dev/null || true) | |
| REPO_URL="${{ github.server_url }}/${{ github.repository }}/blob/main" | |
| LINKS="" | |
| [[ -n "$MR_PATH" ]] && LINKS="<${REPO_URL}/${MR_PATH}|computed>" | |
| [[ -n "$RAW_PATH" ]] && LINKS="${LINKS:+${LINKS}, }<${REPO_URL}/${RAW_PATH}|raw>" | |
| DETAILS="${DETAILS}\n• *${ZONE}* / \`${MACHINE}\` — changed: \`${REGISTERS}\` (${LINKS})" | |
| done | |
| if [[ -z "$DETAILS" ]]; then | |
| echo "No change details found, skipping notification" | |
| exit 0 | |
| fi | |
| # Send Slack message | |
| PAYLOAD=$(cat <<SLACK | |
| { | |
| "text": "Measurements changed after a TCB update", | |
| "blocks": [ | |
| { | |
| "type": "header", | |
| "text": { | |
| "type": "plain_text", | |
| "text": ":warning: Measurements changed after a TCB update" | |
| } | |
| }, | |
| { | |
| "type": "section", | |
| "text": { | |
| "type": "mrkdwn", | |
| "text": "New DCAP measurements were detected that differ from all previously recorded values. Consider updating your offline measurement tools to support the new TCB.\n${DETAILS}" | |
| } | |
| }, | |
| { | |
| "type": "context", | |
| "elements": [ | |
| { | |
| "type": "mrkdwn", | |
| "text": "<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View workflow run>" | |
| } | |
| ] | |
| } | |
| ] | |
| } | |
| SLACK | |
| ) | |
| curl -sf -X POST -H 'Content-type: application/json' --data "$PAYLOAD" "$SLACK_WEBHOOK_URL" |