From cc184dc66d06f5a589c704ebd5ed0e5423f72b9d Mon Sep 17 00:00:00 2001 From: chendechen Date: Mon, 12 Oct 2026 17:57:44 +0800 Subject: [PATCH] fix: complete OP pre-bedrock error tracing --- .github/workflows/build.yaml | 128 ++++++++++++++++++++++++++ .github/workflows/geth-lint-build.yml | 115 ----------------------- .github/workflows/geth-release.yml | 76 --------------- l2geth/Dockerfile | 8 +- l2geth/core/vm/evm.go | 26 ++++++ l2geth/eth/api_debank.go | 14 ++- l2geth/go.mod | 2 +- l2geth/go.sum | 4 +- 8 files changed, 169 insertions(+), 204 deletions(-) create mode 100644 .github/workflows/build.yaml delete mode 100644 .github/workflows/geth-lint-build.yml delete mode 100644 .github/workflows/geth-release.yml diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000000..65048362d1 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,128 @@ +name: Build artifacts + +on: + pull_request: + branches: + - debank + - op-prebedrock + release: + types: + - created + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + AWS_REGION: us-east-1 + REGISTRY: public.ecr.aws/b2h7a5c4/chaintable + IMAGE: op-geth + DOCKERFILE: l2geth/Dockerfile + +jobs: + build-artifacts: + name: build-${{ matrix.arch }} + strategy: + fail-fast: false + matrix: + include: + - platform: linux/amd64 + runner: ubuntu2404-linux-x86-64-16c + arch: amd64 + - platform: linux/arm64 + runner: ubuntu2204-arm64-16c + arch: arm64 + runs-on: ${{ matrix.runner }} + + permissions: + contents: read + id-token: write + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + + - name: Set env (determine image tag) + run: | + if [[ "$GITHUB_EVENT_NAME" == "release" ]]; then + REVISION="${GITHUB_REF#refs/tags/}" + else + REVISION="$(git rev-parse --short=7 HEAD)" + fi + REVISION="prebedrock-${REVISION}" + echo "REVISION=${REVISION}" >> "$GITHUB_ENV" + echo "Building commit $(git rev-parse HEAD) as ${REVISION}" + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ secrets.AWS_ROLE_ARN }} + aws-region: ${{ env.AWS_REGION }} + + - name: Login to Amazon ECR + uses: aws-actions/amazon-ecr-login@v2 + with: + registry-type: public + + - name: Set up Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push image + uses: docker/build-push-action@v6 + with: + file: ${{ env.DOCKERFILE }} + context: . + platforms: ${{ matrix.platform }} + push: true + tags: ${{ env.REGISTRY }}/${{ env.IMAGE }}:${{ matrix.arch }}-${{ env.REVISION }} + provenance: false + + - name: Check geth version + run: | + docker run --rm --pull=always --platform=${{ matrix.platform }} "${REGISTRY}/${IMAGE}:${{ matrix.arch }}-${REVISION}" version + + manifest: + name: merge-manifest + needs: build-artifacts + runs-on: ubuntu2404-linux-x86-64-16c + + permissions: + contents: read + id-token: write + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + + - name: Set env (determine image tag) + run: | + if [[ "$GITHUB_EVENT_NAME" == "release" ]]; then + REVISION="${GITHUB_REF#refs/tags/}" + else + REVISION="$(git rev-parse --short=7 HEAD)" + fi + REVISION="prebedrock-${REVISION}" + echo "REVISION=${REVISION}" >> "$GITHUB_ENV" + echo "Publishing commit $(git rev-parse HEAD) as ${REVISION}" + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ secrets.AWS_ROLE_ARN }} + aws-region: ${{ env.AWS_REGION }} + + - name: Login to Amazon ECR + uses: aws-actions/amazon-ecr-login@v2 + with: + registry-type: public + + - name: Set up Buildx + uses: docker/setup-buildx-action@v3 + + - name: Merge manifest + run: | + docker buildx imagetools create --tag "${REGISTRY}/${IMAGE}:${REVISION}" "${REGISTRY}/${IMAGE}:amd64-${REVISION}" "${REGISTRY}/${IMAGE}:arm64-${REVISION}" + docker buildx imagetools inspect "${REGISTRY}/${IMAGE}:${REVISION}" diff --git a/.github/workflows/geth-lint-build.yml b/.github/workflows/geth-lint-build.yml deleted file mode 100644 index 83e3d27400..0000000000 --- a/.github/workflows/geth-lint-build.yml +++ /dev/null @@ -1,115 +0,0 @@ -name: l2geth lint and build - -on: - pull_request: - branches: - - 'debank' - workflow_dispatch: - -defaults: - run: - working-directory: ./l2geth - -env: - REGISTRY: 294354037686.dkr.ecr.ap-northeast-1.amazonaws.com - IMAGE: op-l2geth - DOCKERFILE: Dockerfile - -jobs: - lint: - runs-on: [self-hosted, general] - - steps: - - name: Install Go - uses: actions/setup-go@v6 - with: - go-version: 1.23.x - - - name: Checkout code - uses: actions/checkout@v2 - with: - ref: ${{ github.event.pull_request.head.sha }} - - #- name: Lint - #run: | - #make lint - - build: - strategy: - matrix: - arch: [amd64, arm64] - runs-on: [self-hosted, "${{ matrix.arch }}"] - - needs: lint - - steps: - - name: Install Go - uses: actions/setup-go@v2 - with: - go-version: 1.23.x - - - name: Checkout code - uses: actions/checkout@v2 - with: - ref: ${{ github.event.pull_request.head.sha }} - - - uses: tibdex/github-app-token@v1 - id: generate-token - with: - app_id: ${{ secrets.RELEASE_MANAGER_APP_ID }} - private_key: ${{ secrets.RELEASE_MANAGER_APP_SECRET }} - - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v4 - with: - aws-region: ap-northeast-1 - - - name: Login to Amazon ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v2 - - - name: Set env - run: | - # echo "REVISION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV - echo "REVISION=${GITHUB_SHA::8}" >> $GITHUB_ENV - - - name: Docker build and push - run: | - docker build -t ${REGISTRY}/${IMAGE}:${{ matrix.arch }}-${REVISION} --build-arg ACCESS_TOKEN=${{ steps.generate-token.outputs.token }} -f ${DOCKERFILE} .. - docker push ${REGISTRY}/${IMAGE}:${{ matrix.arch }}-${REVISION} - docker run --name dryrun --rm ${REGISTRY}/${IMAGE}:${{ matrix.arch }}-${REVISION} version - docker rmi ${REGISTRY}/${IMAGE}:${{ matrix.arch }}-${REVISION} - - - name: Lark Notification - continue-on-error: true - run: | - notiv3 "Blockchain Optimism latest image pushed 🎉" ${REGISTRY}/${IMAGE}:${{ matrix.arch }}-${REVISION} - - - merge-docker-manifest: - runs-on: [self-hosted, general] - needs: build - steps: - - name: Checkout code - uses: actions/checkout@v2 - with: - ref: ${{ github.event.pull_request.head.sha }} - - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v4 - with: - aws-region: ap-northeast-1 - - - name: Login to Amazon ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v2 - - - name: Set env - run: | - echo "REVISION=${GITHUB_SHA::8}" >> $GITHUB_ENV - - - name: Merge manifest - run: | - docker pull --platform=linux/amd64 ${REGISTRY}/${IMAGE}:amd64-${REVISION} - docker pull --platform=linux/arm64 ${REGISTRY}/${IMAGE}:arm64-${REVISION} - docker buildx imagetools create --tag ${REGISTRY}/${IMAGE}:${REVISION} ${REGISTRY}/${IMAGE}:amd64-${REVISION} ${REGISTRY}/${IMAGE}:arm64-${REVISION} \ No newline at end of file diff --git a/.github/workflows/geth-release.yml b/.github/workflows/geth-release.yml deleted file mode 100644 index b826bfee78..0000000000 --- a/.github/workflows/geth-release.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: l2geth release - -on: - release: - types: - - created - workflow_dispatch: - -defaults: - run: - working-directory: ./l2geth - -env: - REGISTRY: 294354037686.dkr.ecr.ap-northeast-1.amazonaws.com - IMAGE: op-l2geth - DOCKERFILE: Dockerfile - -jobs: - release: - strategy: - matrix: - arch: [amd64, arm64] - runs-on: [self-hosted, "${{ matrix.arch }}"] - - steps: - - name: Install Go - uses: actions/setup-go@v2 - with: - go-version: 1.23.x - - - name: Checkout code - uses: actions/checkout@v6 - with: - ref: ${{ github.event.pull_request.head.sha }} - - - uses: tibdex/github-app-token@v1 - id: generate-token - with: - app_id: ${{ secrets.RELEASE_MANAGER_APP_ID }} - private_key: ${{ secrets.RELEASE_MANAGER_APP_SECRET }} - - - name: Set env - run: | - echo "REVISION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV - - - name: Docker build and push - run: | - docker build -t ${REGISTRY}/${IMAGE}:${{ matrix.arch }}-${REVISION} --build-arg ACCESS_TOKEN=${{ steps.generate-token.outputs.token }} -f ${DOCKERFILE} .. - docker push ${REGISTRY}/${IMAGE}:${{ matrix.arch }}-${REVISION} - docker run --name dryrun --rm ${REGISTRY}/${IMAGE}:${{ matrix.arch }}-${REVISION} version - docker rmi ${REGISTRY}/${IMAGE}:${{ matrix.arch }}-${REVISION} - - - name: Lark Notification - continue-on-error: true - run: | - notiv3 "Blockchain Optimism latest image pushed 🎉" ${REGISTRY}/${IMAGE}:${{ matrix.arch }}-${REVISION} - - - merge-docker-manifest: - runs-on: [self-hosted, general] - needs: release - steps: - - name: Checkout code - uses: actions/checkout@v2 - with: - ref: ${{ github.event.pull_request.head.sha }} - - - name: Set env - run: | - echo "REVISION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV - - - name: Merge manifest - run: | - docker pull --platform=linux/amd64 ${REGISTRY}/${IMAGE}:amd64-${REVISION} - docker pull --platform=linux/arm64 ${REGISTRY}/${IMAGE}:arm64-${REVISION} - docker buildx imagetools create --tag ${REGISTRY}/${IMAGE}:${REVISION} ${REGISTRY}/${IMAGE}:amd64-${REVISION} ${REGISTRY}/${IMAGE}:arm64-${REVISION} \ No newline at end of file diff --git a/l2geth/Dockerfile b/l2geth/Dockerfile index 4539a16a8b..57c546df23 100644 --- a/l2geth/Dockerfile +++ b/l2geth/Dockerfile @@ -1,14 +1,8 @@ # Build Geth in a stock Go builder container -FROM golang:1.23-alpine as builder +FROM golang:1.23-alpine AS builder RUN apk add --no-cache make gcc musl-dev linux-headers git -ARG ACCESS_TOKEN - -RUN git config --global url."https://x-access-token:${ACCESS_TOKEN}@github.com".insteadOf "https://github.com" - -ENV GOPRIVATE=github.com/Chaintable/* - COPY ./l2geth /app/l2geth WORKDIR /app/l2geth diff --git a/l2geth/core/vm/evm.go b/l2geth/core/vm/evm.go index b21693342f..a2eb31f9a5 100644 --- a/l2geth/core/vm/evm.go +++ b/l2geth/core/vm/evm.go @@ -199,6 +199,21 @@ func (evm *EVM) Interpreter() Interpreter { return evm.interpreter } +// captureFailedCall balances the tracing hooks for failures before execution +// starts. Keep the original validation order, return values and gas accounting. +func (evm *EVM) captureFailedCall(typ OpCode, from, to common.Address, input []byte, gas uint64, value *big.Int, output []byte, gasUsed uint64, err error) { + if !evm.vmConfig.Debug { + return + } + if evm.depth == 0 && (typ == CALL || typ == CREATE) { + evm.vmConfig.Tracer.CaptureStart(evm, from, to, typ == CREATE, input, gas, value) + evm.vmConfig.Tracer.CaptureEnd(output, gasUsed, 0, err) + } else { + evm.vmConfig.Tracer.CaptureEnter(typ, from, to, input, gas, value) + evm.vmConfig.Tracer.CaptureExit(output, gasUsed, err) + } +} + // Call executes the contract associated with the addr with the given input as // parameters. It also handles any necessary value transfer required and takes // the necessary steps to create accounts and reverses the state in case of an @@ -224,10 +239,12 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { + evm.captureFailedCall(CALL, caller.Address(), addr, input, gas, value, nil, 0, ErrDepth) return nil, gas, ErrDepth } // Fail if we're trying to transfer more than the available balance if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { + evm.captureFailedCall(CALL, caller.Address(), addr, input, gas, value, nil, 0, ErrInsufficientBalance) return nil, gas, ErrInsufficientBalance } @@ -312,10 +329,12 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { + evm.captureFailedCall(CALLCODE, caller.Address(), addr, input, gas, value, nil, 0, ErrDepth) return nil, gas, ErrDepth } // Fail if we're trying to transfer more than the available balance if !evm.CanTransfer(evm.StateDB, caller.Address(), value) { + evm.captureFailedCall(CALLCODE, caller.Address(), addr, input, gas, value, nil, 0, ErrInsufficientBalance) return nil, gas, ErrInsufficientBalance } @@ -356,6 +375,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by } // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { + evm.captureFailedCall(DELEGATECALL, caller.Address(), addr, input, gas, nil, nil, 0, ErrDepth) return nil, gas, ErrDepth } @@ -395,6 +415,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte } // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { + evm.captureFailedCall(STATICCALL, caller.Address(), addr, input, gas, nil, nil, 0, ErrDepth) return nil, gas, ErrDepth } @@ -450,9 +471,11 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // Depth check execution. Fail if we're trying to execute above the // limit. if evm.depth > int(params.CallCreateDepth) { + evm.captureFailedCall(CREATE, caller.Address(), address, codeAndHash.code, gas, value, nil, 0, ErrDepth) return nil, common.Address{}, gas, ErrDepth } if !evm.CanTransfer(evm.StateDB, caller.Address(), value) { + evm.captureFailedCall(CREATE, caller.Address(), address, codeAndHash.code, gas, value, nil, 0, ErrInsufficientBalance) return nil, common.Address{}, gas, ErrInsufficientBalance } if rcfg.UsingOVM { @@ -464,8 +487,10 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, ret, err := util.EncodeSolidityError(solerr) if err != nil { // If we're unable to properly encode the error then just return the original message. + evm.captureFailedCall(CREATE, caller.Address(), address, codeAndHash.code, gas, value, nil, 0, solerr) return nil, common.Address{}, gas, solerr } + evm.captureFailedCall(CREATE, caller.Address(), address, codeAndHash.code, gas, value, ret, 0, errExecutionReverted) return ret, common.Address{}, gas, errExecutionReverted } @@ -489,6 +514,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // Ensure there's no existing contract already at the designated address contractHash := evm.StateDB.GetCodeHash(address) if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) { + evm.captureFailedCall(CREATE, caller.Address(), address, codeAndHash.code, gas, value, nil, gas, ErrContractAddressCollision) return nil, common.Address{}, 0, ErrContractAddressCollision } // Create a new account on the state diff --git a/l2geth/eth/api_debank.go b/l2geth/eth/api_debank.go index 4766eae21c..b7414b3b59 100644 --- a/l2geth/eth/api_debank.go +++ b/l2geth/eth/api_debank.go @@ -59,8 +59,8 @@ func (api *DebankAPI) DebankBlock(ctx context.Context, blockNr rpc.BlockNumber) return nil, fmt.Errorf("failed to get state at %s: %v", parentBlock.Root().Hex(), err) } receipts := blockchain.GetReceiptsByHash(block.Hash()) - if len(receipts) == 0 && len(block.Transactions()) > 0 { - return nil, fmt.Errorf("receipts not found for block #%d", blockNr) + if len(receipts) != len(block.Transactions()) { + return nil, fmt.Errorf("receipt count mismatch for block #%d: got %d, want %d", blockNr, len(receipts), len(block.Transactions())) } // 使用 Pipeline 的 RPCTracer @@ -72,6 +72,9 @@ func (api *DebankAPI) DebankBlock(ctx context.Context, blockNr rpc.BlockNumber) gasPool := new(core.GasPool).AddGas(block.GasLimit()) for i, tx := range block.Transactions() { + if receipts[i] == nil { + return nil, fmt.Errorf("receipt missing for tx %d (%s)", i, tx.Hash().Hex()) + } msg, err := tx.AsMessage(signer) if err != nil { return nil, fmt.Errorf("failed to convert tx %d to message: %v", i, err) @@ -85,7 +88,12 @@ func (api *DebankAPI) DebankBlock(ctx context.Context, blockNr rpc.BlockNumber) vmenv := vm.NewEVM(evmContext, statedb, chainConfig, vmConfig) _, _, _, err = core.ApplyMessage(vmenv, msg, gasPool) - tracer.OnTxEnd(receipts[i], err) + if err != nil { + return nil, fmt.Errorf("failed to replay tx %d (%s): %w", i, tx.Hash().Hex(), err) + } + if err := tracer.OnTxEnd(receipts[i], nil); err != nil { + return nil, fmt.Errorf("invalid trace for tx %d (%s): %w", i, tx.Hash().Hex(), err) + } statedb.Finalise(chainConfig.IsEIP158(block.Number())) } diff --git a/l2geth/go.mod b/l2geth/go.mod index 5619301c35..3f11ccfe2e 100644 --- a/l2geth/go.mod +++ b/l2geth/go.mod @@ -73,7 +73,7 @@ require ( gotest.tools/v3 v3.0.3 // indirect ) -require github.com/Chaintable/pipeline v0.0.64-0.20260428082642-1513439e7f02 +require github.com/Chaintable/pipeline v0.0.64-0.20261012064422-67ae274c3d3e require ( github.com/Azure/azure-pipeline-go v0.2.2 // indirect diff --git a/l2geth/go.sum b/l2geth/go.sum index 962b32524a..b367ea1451 100644 --- a/l2geth/go.sum +++ b/l2geth/go.sum @@ -45,8 +45,8 @@ github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUM github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/Chaintable/pipeline v0.0.64-0.20260428082642-1513439e7f02 h1:l/1J24duXWxaz8k1NlSLWY8V6Wzuhl2D2I6sKAAWC38= -github.com/Chaintable/pipeline v0.0.64-0.20260428082642-1513439e7f02/go.mod h1:eTSxKLlwF59ObeYrlagJ7xcu0gazl2juePnCIzMfug8= +github.com/Chaintable/pipeline v0.0.64-0.20261012064422-67ae274c3d3e h1:WIIniSz5PA5NSPZ0SWFr37GQpMAGOSw4pbKR6xNk4pc= +github.com/Chaintable/pipeline v0.0.64-0.20261012064422-67ae274c3d3e/go.mod h1:eTSxKLlwF59ObeYrlagJ7xcu0gazl2juePnCIzMfug8= github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=