mirror of
https://github.com/scroll-tech/scroll.git
synced 2026-01-12 07:28:08 -05:00
Compare commits
3 Commits
v4.0.8
...
local_disa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d10816de6a | ||
|
|
c3b1713d21 | ||
|
|
87974b560a |
14
.github/pull_request_template.md
vendored
14
.github/pull_request_template.md
vendored
@@ -1,9 +1,9 @@
|
||||
### Purpose or design rationale of this PR
|
||||
## 1. Purpose or design rationale of this PR
|
||||
|
||||
*Describe your change. Make sure to answer these three questions: What does this PR do? Why does it do it? How does it do it?*
|
||||
...
|
||||
|
||||
|
||||
### PR title
|
||||
## 2. PR title
|
||||
|
||||
Your PR title must follow [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/#summary) (as we are doing squash merge for each PR), so it must start with one of the following [types](https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#type):
|
||||
|
||||
@@ -18,17 +18,17 @@ Your PR title must follow [conventional commits](https://www.conventionalcommits
|
||||
- [ ] test: Adding missing tests or correcting existing tests
|
||||
|
||||
|
||||
### Deployment tag versioning
|
||||
## 3. Deployment tag versioning
|
||||
|
||||
Has `tag` in `common/version.go` been updated?
|
||||
|
||||
- [ ] No, this PR doesn't involve a new deployment, git tag, docker image tag
|
||||
- [ ] This PR doesn't involve a new deployment, git tag, docker image tag
|
||||
- [ ] Yes
|
||||
|
||||
|
||||
### Breaking change label
|
||||
## 4. Breaking change label
|
||||
|
||||
Does this PR have the `breaking-change` label?
|
||||
|
||||
- [ ] No, this PR is not a breaking change
|
||||
- [ ] This PR is not a breaking change
|
||||
- [ ] Yes
|
||||
|
||||
96
.github/workflows/bridge.yml
vendored
96
.github/workflows/bridge.yml
vendored
@@ -9,8 +9,6 @@ on:
|
||||
- alpha
|
||||
paths:
|
||||
- 'bridge/**'
|
||||
- 'common/**'
|
||||
- 'database/**'
|
||||
- '.github/workflows/bridge.yml'
|
||||
pull_request:
|
||||
types:
|
||||
@@ -20,61 +18,14 @@ on:
|
||||
- ready_for_review
|
||||
paths:
|
||||
- 'bridge/**'
|
||||
- 'common/**'
|
||||
- 'database/**'
|
||||
- '.github/workflows/bridge.yml'
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: 'bridge'
|
||||
|
||||
jobs:
|
||||
check:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.19.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install Solc
|
||||
uses: supplypike/setup-bin@v3
|
||||
with:
|
||||
uri: 'https://github.com/ethereum/solidity/releases/download/v0.8.16/solc-static-linux'
|
||||
name: 'solc'
|
||||
version: '0.8.16'
|
||||
- name: Install Geth Tools
|
||||
uses: gacts/install-geth-tools@v1
|
||||
- name: Lint
|
||||
working-directory: 'bridge'
|
||||
run: |
|
||||
rm -rf $HOME/.cache/golangci-lint
|
||||
make mock_abi
|
||||
make lint
|
||||
goimports-lint:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.19.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install goimports
|
||||
run: go install golang.org/x/tools/cmd/goimports
|
||||
- name: Run goimports lint
|
||||
run: goimports -local scroll-tech/bridge/ -w .
|
||||
working-directory: 'bridge'
|
||||
- name: Run go mod tidy
|
||||
run: go mod tidy
|
||||
working-directory: 'bridge'
|
||||
# If there are any diffs from goimports or go mod tidy, fail.
|
||||
- name: Verify no changes from goimports and go mod tidy
|
||||
working-directory: 'bridge'
|
||||
run: |
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
exit 1
|
||||
fi
|
||||
tests:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -92,24 +43,31 @@ jobs:
|
||||
version: '0.8.16'
|
||||
- name: Install Geth Tools
|
||||
uses: gacts/install-geth-tools@v1
|
||||
- name: Build prerequisites
|
||||
- name: Lint
|
||||
run: |
|
||||
make dev_docker
|
||||
make -C bridge mock_abi
|
||||
- name: Build bridge binaries
|
||||
working-directory: 'bridge'
|
||||
run: |
|
||||
make bridge_bins
|
||||
- name: Test bridge packages
|
||||
working-directory: 'bridge'
|
||||
run: |
|
||||
go test -v -race -gcflags="-l" -ldflags="-s=false" -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
rm -rf $HOME/.cache/golangci-lint
|
||||
make mock_abi
|
||||
make lint
|
||||
goimports-lint:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
flags: bridge
|
||||
go-version: 1.18.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install goimports
|
||||
run: go install golang.org/x/tools/cmd/goimports
|
||||
- run: goimports -local scroll-tech/bridge/ -w .
|
||||
- run: go mod tidy
|
||||
# If there are any diffs from goimports or go mod tidy, fail.
|
||||
- name: Verify no changes from goimports and go mod tidy
|
||||
run: |
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
exit 1
|
||||
fi
|
||||
# docker-build:
|
||||
# if: github.event.pull_request.draft == false
|
||||
# runs-on: ubuntu-latest
|
||||
|
||||
15
.github/workflows/bridge_history_api.yml
vendored
15
.github/workflows/bridge_history_api.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: BridgeHistoryAPI
|
||||
name: BridgeHistoryApi
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
# - name: Install Go
|
||||
# uses: actions/setup-go@v2
|
||||
# with:
|
||||
# go-version: 1.19.x
|
||||
# go-version: 1.20.x
|
||||
# - name: Checkout code
|
||||
# uses: actions/checkout@v2
|
||||
# - name: Lint
|
||||
@@ -46,19 +46,13 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.19.x
|
||||
go-version: 1.20.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Test
|
||||
run: |
|
||||
go get ./...
|
||||
make test
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
with:
|
||||
flags: bridge-history-api
|
||||
goimports-lint:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
@@ -66,7 +60,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.19.x
|
||||
go-version: 1.20.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install goimports
|
||||
@@ -79,3 +73,4 @@ jobs:
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
49
.github/workflows/common.yml
vendored
49
.github/workflows/common.yml
vendored
@@ -20,6 +20,10 @@ on:
|
||||
- 'common/**'
|
||||
- '.github/workflows/common.yml'
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: 'common'
|
||||
|
||||
jobs:
|
||||
check:
|
||||
if: github.event.pull_request.draft == false
|
||||
@@ -33,7 +37,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.19.x
|
||||
go-version: 1.18.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Cache cargo
|
||||
@@ -41,7 +45,6 @@ jobs:
|
||||
with:
|
||||
workspaces: "common/libzkp/impl -> target"
|
||||
- name: Lint
|
||||
working-directory: 'common'
|
||||
run: |
|
||||
rm -rf $HOME/.cache/golangci-lint
|
||||
make lint
|
||||
@@ -52,52 +55,16 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.19.x
|
||||
go-version: 1.18.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install goimports
|
||||
run: go install golang.org/x/tools/cmd/goimports
|
||||
- name: Run goimports lint
|
||||
working-directory: 'common'
|
||||
run: goimports -local scroll-tech/common/ -w .
|
||||
- name: Run go mod tidy
|
||||
working-directory: 'common'
|
||||
run: go mod tidy
|
||||
- run: goimports -local scroll-tech/common/ -w .
|
||||
- run: go mod tidy
|
||||
# If there are any diffs from goimports or go mod tidy, fail.
|
||||
- name: Verify no changes from goimports and go mod tidy
|
||||
working-directory: 'common'
|
||||
run: |
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
exit 1
|
||||
fi
|
||||
tests:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.18.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install Solc
|
||||
uses: supplypike/setup-bin@v3
|
||||
with:
|
||||
uri: 'https://github.com/ethereum/solidity/releases/download/v0.8.16/solc-static-linux'
|
||||
name: 'solc'
|
||||
version: '0.8.16'
|
||||
- name: Install Geth Tools
|
||||
uses: gacts/install-geth-tools@v1
|
||||
- name: Build prerequisites
|
||||
run: |
|
||||
make dev_docker
|
||||
- name: Test common packages
|
||||
working-directory: 'common'
|
||||
run: |
|
||||
go test -v -race -gcflags="-l" -ldflags="-s=false" -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
with:
|
||||
flags: common
|
||||
|
||||
54
.github/workflows/coordinator.yml
vendored
54
.github/workflows/coordinator.yml
vendored
@@ -9,8 +9,6 @@ on:
|
||||
- alpha
|
||||
paths:
|
||||
- 'coordinator/**'
|
||||
- 'common/**'
|
||||
- 'database/**'
|
||||
- '.github/workflows/coordinator.yml'
|
||||
pull_request:
|
||||
types:
|
||||
@@ -20,10 +18,12 @@ on:
|
||||
- ready_for_review
|
||||
paths:
|
||||
- 'coordinator/**'
|
||||
- 'common/**'
|
||||
- 'database/**'
|
||||
- '.github/workflows/coordinator.yml'
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: 'coordinator'
|
||||
|
||||
jobs:
|
||||
check:
|
||||
if: github.event.pull_request.draft == false
|
||||
@@ -37,11 +37,10 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.19.x
|
||||
go-version: 1.18.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Lint
|
||||
working-directory: 'coordinator'
|
||||
run: |
|
||||
rm -rf $HOME/.cache/golangci-lint
|
||||
make lint
|
||||
@@ -52,20 +51,15 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.19.x
|
||||
go-version: 1.18.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install goimports
|
||||
run: go install golang.org/x/tools/cmd/goimports
|
||||
- name: Run goimports lint
|
||||
working-directory: 'coordinator'
|
||||
run: goimports -local scroll-tech/coordinator/ -w .
|
||||
- name: Run go mod tidy
|
||||
working-directory: 'coordinator'
|
||||
run: go mod tidy
|
||||
- run: goimports -local scroll-tech/coordinator/ -w .
|
||||
- run: go mod tidy
|
||||
# If there are any diffs from goimports or go mod tidy, fail.
|
||||
- name: Verify no changes from goimports and go mod tidy
|
||||
working-directory: 'coordinator'
|
||||
run: |
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
exit 1
|
||||
@@ -86,35 +80,3 @@ jobs:
|
||||
# push: false
|
||||
# # cache-from: type=gha,scope=${{ github.workflow }}
|
||||
# # cache-to: type=gha,scope=${{ github.workflow }}
|
||||
tests:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.18.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install Solc
|
||||
uses: supplypike/setup-bin@v3
|
||||
with:
|
||||
uri: 'https://github.com/ethereum/solidity/releases/download/v0.8.16/solc-static-linux'
|
||||
name: 'solc'
|
||||
version: '0.8.16'
|
||||
- name: Install Geth Tools
|
||||
uses: gacts/install-geth-tools@v1
|
||||
- name: Build prerequisites
|
||||
run: |
|
||||
make dev_docker
|
||||
- name: Test coordinator packages
|
||||
working-directory: 'coordinator'
|
||||
run: |
|
||||
# go test -exec "env LD_LIBRARY_PATH=${PWD}/verifier/lib" -v -race -gcflags="-l" -ldflags="-s=false" -coverpkg="scroll-tech/coordinator" -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
go test -v -race -gcflags="-l" -ldflags="-s=false" -coverprofile=coverage.txt -covermode=atomic -tags mock_verifier ./...
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
with:
|
||||
flags: coordinator
|
||||
|
||||
51
.github/workflows/database.yml
vendored
51
.github/workflows/database.yml
vendored
@@ -9,7 +9,6 @@ on:
|
||||
- alpha
|
||||
paths:
|
||||
- 'database/**'
|
||||
- 'common/**'
|
||||
- '.github/workflows/database.yml'
|
||||
pull_request:
|
||||
types:
|
||||
@@ -19,9 +18,12 @@ on:
|
||||
- ready_for_review
|
||||
paths:
|
||||
- 'database/**'
|
||||
- 'common/**'
|
||||
- '.github/workflows/database.yml'
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: 'database'
|
||||
|
||||
jobs:
|
||||
check:
|
||||
if: github.event.pull_request.draft == false
|
||||
@@ -30,11 +32,10 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.19.x
|
||||
go-version: 1.18.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Lint
|
||||
working-directory: 'database'
|
||||
run: |
|
||||
rm -rf $HOME/.cache/golangci-lint
|
||||
make lint
|
||||
@@ -45,52 +46,16 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.19.x
|
||||
go-version: 1.18.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install goimports
|
||||
run: go install golang.org/x/tools/cmd/goimports
|
||||
- name: Run goimports lint
|
||||
working-directory: 'database'
|
||||
run: goimports -local scroll-tech/database/ -w .
|
||||
- name: Run go mod tidy
|
||||
working-directory: 'database'
|
||||
run: go mod tidy
|
||||
- run: goimports -local scroll-tech/database/ -w .
|
||||
- run: go mod tidy
|
||||
# If there are any diffs from goimports or go mod tidy, fail.
|
||||
- name: Verify no changes from goimports and go mod tidy
|
||||
working-directory: 'database'
|
||||
run: |
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
exit 1
|
||||
fi
|
||||
tests:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.18.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install Solc
|
||||
uses: supplypike/setup-bin@v3
|
||||
with:
|
||||
uri: 'https://github.com/ethereum/solidity/releases/download/v0.8.16/solc-static-linux'
|
||||
name: 'solc'
|
||||
version: '0.8.16'
|
||||
- name: Install Geth Tools
|
||||
uses: gacts/install-geth-tools@v1
|
||||
- name: Build prerequisites
|
||||
run: |
|
||||
make dev_docker
|
||||
- name: Test database packages
|
||||
working-directory: 'database'
|
||||
run: |
|
||||
go test -v -race -gcflags="-l" -ldflags="-s=false" -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
with:
|
||||
flags: database
|
||||
|
||||
43
.github/workflows/integration.yaml
vendored
43
.github/workflows/integration.yaml
vendored
@@ -1,43 +0,0 @@
|
||||
name: Integration
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- staging
|
||||
- develop
|
||||
- alpha
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- synchronize
|
||||
- ready_for_review
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.18.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install Solc
|
||||
uses: supplypike/setup-bin@v3
|
||||
with:
|
||||
uri: 'https://github.com/ethereum/solidity/releases/download/v0.8.16/solc-static-linux'
|
||||
name: 'solc'
|
||||
version: '0.8.16'
|
||||
- name: Install Geth Tools
|
||||
uses: gacts/install-geth-tools@v1
|
||||
- name: Build prerequisites
|
||||
run: |
|
||||
make dev_docker
|
||||
make -C bridge mock_abi
|
||||
make -C common/bytecode all
|
||||
- name: Run integration tests
|
||||
run: |
|
||||
go test -v -tags="mock_prover mock_verifier" -p 1 -coverprofile=coverage.txt scroll-tech/integration-test/...
|
||||
59
.github/workflows/intermediate-docker.yml
vendored
59
.github/workflows/intermediate-docker.yml
vendored
@@ -1,59 +0,0 @@
|
||||
name: Intermediate Docker
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
GO_VERSION:
|
||||
description: 'Go version'
|
||||
required: true
|
||||
type: string
|
||||
default: '1.19'
|
||||
RUST_VERSION:
|
||||
description: 'Rust toolchain version'
|
||||
required: true
|
||||
type: string
|
||||
default: 'nightly-2022-12-10'
|
||||
PYTHON_VERSION:
|
||||
description: 'Python version'
|
||||
required: false
|
||||
type: string
|
||||
default: '3.10'
|
||||
CUDA_VERSION:
|
||||
description: 'Cuda version'
|
||||
required: false
|
||||
type: string
|
||||
default: '11.7.1'
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: 'build/dockerfiles/intermediate'
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build
|
||||
run: |
|
||||
make all
|
||||
env:
|
||||
GO_VERSION: ${{ inputs.GO_VERSION }}
|
||||
RUST_VERSION: ${{ inputs.RUST_VERSION }}
|
||||
PYTHON_VERSION: ${{ inputs.PYTHON_VERSION }}
|
||||
CUDA_VERSION: ${{ inputs.CUDA_VERSION }}
|
||||
- name: Publish
|
||||
run: |
|
||||
make publish
|
||||
env:
|
||||
GO_VERSION: ${{ inputs.GO_VERSION }}
|
||||
RUST_VERSION: ${{ inputs.RUST_VERSION }}
|
||||
PYTHON_VERSION: ${{ inputs.PYTHON_VERSION }}
|
||||
CUDA_VERSION: ${{ inputs.CUDA_VERSION }}
|
||||
14
.github/workflows/roller.yml
vendored
14
.github/workflows/roller.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.19.x
|
||||
go-version: 1.18.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Cache cargo
|
||||
@@ -47,13 +47,7 @@ jobs:
|
||||
- name: Test
|
||||
run: |
|
||||
make roller
|
||||
go test -tags="mock_prover" -v -coverprofile=coverage.txt ./...
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
with:
|
||||
flags: roller
|
||||
go test -tags="mock_prover" -v ./...
|
||||
check:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
@@ -61,7 +55,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.19.x
|
||||
go-version: 1.18.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Lint
|
||||
@@ -75,7 +69,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.19.x
|
||||
go-version: 1.18.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install goimports
|
||||
|
||||
99
Jenkinsfile
vendored
Normal file
99
Jenkinsfile
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
imagePrefix = 'scrolltech'
|
||||
credentialDocker = 'dockerhub'
|
||||
|
||||
pipeline {
|
||||
agent any
|
||||
options {
|
||||
timeout (20)
|
||||
}
|
||||
tools {
|
||||
go 'go-1.18'
|
||||
nodejs "nodejs"
|
||||
}
|
||||
environment {
|
||||
GO111MODULE = 'on'
|
||||
PATH="/home/ubuntu/.cargo/bin:$PATH"
|
||||
LD_LIBRARY_PATH="$LD_LIBRARY_PATH:./coordinator/verifier/lib"
|
||||
CHAIN_ID='534353'
|
||||
// LOG_DOCKER = 'true'
|
||||
}
|
||||
stages {
|
||||
stage('Build') {
|
||||
parallel {
|
||||
stage('Build Prerequisite') {
|
||||
steps {
|
||||
sh 'make dev_docker'
|
||||
sh 'make -C bridge mock_abi'
|
||||
sh 'make -C common/bytecode all'
|
||||
}
|
||||
}
|
||||
stage('Check Bridge Compilation') {
|
||||
steps {
|
||||
sh 'make -C bridge bridge_bins'
|
||||
}
|
||||
}
|
||||
stage('Check Coordinator Compilation') {
|
||||
steps {
|
||||
sh 'export PATH=/home/ubuntu/go/bin:$PATH'
|
||||
sh 'make -C coordinator coordinator'
|
||||
}
|
||||
}
|
||||
stage('Check Database Compilation') {
|
||||
steps {
|
||||
sh 'make -C database db_cli'
|
||||
}
|
||||
}
|
||||
stage('Check Database Docker Build') {
|
||||
steps {
|
||||
sh 'make -C database docker'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Parallel Test') {
|
||||
parallel{
|
||||
stage('Race test common package') {
|
||||
steps {
|
||||
sh 'go test -v -race -coverprofile=coverage.common.txt -covermode=atomic scroll-tech/common/...'
|
||||
}
|
||||
}
|
||||
stage('Race test bridge package') {
|
||||
steps {
|
||||
sh "cd ./bridge && ../build/run_tests.sh bridge"
|
||||
}
|
||||
}
|
||||
stage('Race test coordinator package') {
|
||||
steps {
|
||||
sh 'cd ./coordinator && go test -exec "env LD_LIBRARY_PATH=${PWD}/verifier/lib" -v -race -gcflags="-l" -ldflags="-s=false" -coverpkg="scroll-tech/coordinator" -coverprofile=../coverage.coordinator.txt -covermode=atomic ./...'
|
||||
}
|
||||
}
|
||||
stage('Race test database package') {
|
||||
steps {
|
||||
sh 'go test -v -race -coverprofile=coverage.db.txt -covermode=atomic scroll-tech/database/...'
|
||||
}
|
||||
}
|
||||
stage('Integration test') {
|
||||
steps {
|
||||
sh 'go test -v -tags="mock_prover mock_verifier" -p 1 scroll-tech/integration-test/...'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Compare Coverage') {
|
||||
steps {
|
||||
sh "./build/post-test-report-coverage.sh"
|
||||
script {
|
||||
currentBuild.result = 'SUCCESS'
|
||||
}
|
||||
step([$class: 'CompareCoverageAction', publishResultAs: 'Comment', scmVars: [GIT_URL: env.GIT_URL]])
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
publishCoverage adapters: [coberturaReportAdapter(path: 'cobertura.xml', thresholds: [[thresholdTarget: 'Aggregated Report', unhealthyThreshold: 40.0]])], checksName: '', sourceFileResolver: sourceFiles('NEVER_STORE')
|
||||
cleanWs()
|
||||
slackSend(message: "${JOB_BASE_NAME} ${GIT_COMMIT} #${BUILD_NUMBER} deploy ${currentBuild.result}")
|
||||
}
|
||||
}
|
||||
}
|
||||
23
Makefile
23
Makefile
@@ -1,5 +1,7 @@
|
||||
.PHONY: check update dev_docker build_test_docker run_test_docker clean
|
||||
|
||||
ZKP_VERSION=release-1220
|
||||
|
||||
help: ## Display this help message
|
||||
@grep -h \
|
||||
-E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
|
||||
@@ -15,12 +17,12 @@ lint: ## The code's format and security checks.
|
||||
|
||||
update: ## update dependencies
|
||||
go work sync
|
||||
cd $(PWD)/bridge/ && go get -u github.com/scroll-tech/go-ethereum@scroll-v4.1.0 && go mod tidy
|
||||
cd $(PWD)/bridge/ && go get -u github.com/scroll-tech/go-ethereum@scroll-v3.1.12 && go mod tidy
|
||||
cd $(PWD)/bridge-history-api/ && go get -u github.com/ethereum/go-ethereum@latest && go mod tidy
|
||||
cd $(PWD)/common/ && go get -u github.com/scroll-tech/go-ethereum@scroll-v4.1.0 && go mod tidy
|
||||
cd $(PWD)/coordinator/ && go get -u github.com/scroll-tech/go-ethereum@scroll-v4.1.0 && go mod tidy
|
||||
cd $(PWD)/database/ && go get -u github.com/scroll-tech/go-ethereum@scroll-v4.1.0 && go mod tidy
|
||||
cd $(PWD)/roller/ && go get -u github.com/scroll-tech/go-ethereum@scroll-v4.1.0 && go mod tidy
|
||||
cd $(PWD)/common/ && go get -u github.com/scroll-tech/go-ethereum@scroll-v3.1.12 && go mod tidy
|
||||
cd $(PWD)/coordinator/ && go get -u github.com/scroll-tech/go-ethereum@scroll-v3.1.12 && go mod tidy
|
||||
cd $(PWD)/database/ && go get -u github.com/scroll-tech/go-ethereum@scroll-v3.1.12 && go mod tidy
|
||||
cd $(PWD)/roller/ && go get -u github.com/scroll-tech/go-ethereum@scroll-v3.1.12 && go mod tidy
|
||||
goimports -local $(PWD)/bridge/ -w .
|
||||
goimports -local $(PWD)/bridge-history-api/ -w .
|
||||
goimports -local $(PWD)/common/ -w .
|
||||
@@ -38,5 +40,16 @@ build_test_docker: ## build Docker image for local testing on M1/M2 Silicon Mac
|
||||
run_test_docker: ## run Docker image for local testing on M1/M2 Silicon Mac
|
||||
docker run -it --rm --name scroll_test_container --network=host -v /var/run/docker.sock:/var/run/docker.sock -v $(PWD):/go/src/app scroll_test_image
|
||||
|
||||
|
||||
test_zkp: ## Test zkp prove and verify, roller/prover generates the proof and coordinator/verifier verifies it
|
||||
mkdir -p test_params
|
||||
wget https://circuit-release.s3.us-west-2.amazonaws.com/circuit-release/${ZKP_VERSION}/test_params/params19 -O ./test_params/params19
|
||||
wget https://circuit-release.s3.us-west-2.amazonaws.com/circuit-release/${ZKP_VERSION}/test_params/params26 -O ./test_params/params26
|
||||
wget https://circuit-release.s3.us-west-2.amazonaws.com/circuit-release/${ZKP_VERSION}/test_seed -O test_seed
|
||||
rm -rf ./roller/assets/test_params && mv test_params ./roller/assets/ && mv test_seed ./roller/assets/
|
||||
cd ./roller && make test-gpu-prover
|
||||
rm -rf ./coordinator/assets/test_params && mv ./roller/assets/test_params ./coordinator/assets/ && mv ./roller/assets/agg_proof ./coordinator/assets/
|
||||
cd ./coordinator && make test-gpu-verifier
|
||||
|
||||
clean: ## Empty out the bin folder
|
||||
@rm -rf build/bin
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# Scroll Monorepo
|
||||
|
||||
[](https://codecov.io/gh/scroll-tech/scroll)
|
||||
[](https://github.com/scroll-tech/scroll/actions/workflows/contracts.yaml) [](https://github.com/scroll-tech/scroll/actions/workflows/bridge.yml) [](https://github.com/scroll-tech/scroll/actions/workflows/coordinator.yml) [](https://github.com/scroll-tech/scroll/actions/workflows/database.yml) [](https://github.com/scroll-tech/scroll/actions/workflows/common.yml) [](https://github.com/scroll-tech/scroll/actions/workflows/roller.yml)
|
||||
|
||||
## Prerequisites
|
||||
+ Go 1.19
|
||||
+ Go 1.18
|
||||
+ Rust (for version, see [rust-toolchain](./common/libzkp/impl/rust-toolchain))
|
||||
+ Hardhat / Foundry
|
||||
+ Docker
|
||||
|
||||
0
assets/.gitkeep
Normal file
0
assets/.gitkeep
Normal file
@@ -75,7 +75,7 @@ func (c *CrossMsgFetcher) Start() {
|
||||
return
|
||||
case <-tick.C:
|
||||
c.mu.Lock()
|
||||
c.forwardFetchAndSaveMissingEvents(1)
|
||||
c.forwardFetchAndSaveMissingEvents(0)
|
||||
c.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/jmoiron/sqlx"
|
||||
|
||||
backendabi "bridge-history-api/abi"
|
||||
"bridge-history-api/db"
|
||||
@@ -98,7 +99,7 @@ func L1FetchAndSaveEvents(ctx context.Context, client *ethclient.Client, databas
|
||||
log.Warn("Failed to get l1 event logs", "err", err)
|
||||
return err
|
||||
}
|
||||
depositL1CrossMsgs, relayedMsg, err := utils.ParseBackendL1EventLogs(logs)
|
||||
depositL1CrossMsgs, msgHashes, relayedMsg, err := utils.ParseBackendL1EventLogs(logs)
|
||||
if err != nil {
|
||||
log.Error("l1FetchAndSaveEvents: Failed to parse cross msg event logs", "err", err)
|
||||
return err
|
||||
@@ -119,6 +120,11 @@ func L1FetchAndSaveEvents(ctx context.Context, client *ethclient.Client, databas
|
||||
dbTx.Rollback()
|
||||
log.Crit("l1FetchAndSaveEvents: Failed to insert relayed message event logs", "err", err)
|
||||
}
|
||||
err = updateL1CrossMsgMsgHash(ctx, dbTx, database, msgHashes)
|
||||
if err != nil {
|
||||
dbTx.Rollback()
|
||||
log.Crit("l1FetchAndSaveEvents: Failed to update msgHash in L1 cross msg", "err", err)
|
||||
}
|
||||
err = dbTx.Commit()
|
||||
if err != nil {
|
||||
// if we can not insert into DB, there must something wrong, need a on-call member handle the dababase manually
|
||||
@@ -151,12 +157,11 @@ func L2FetchAndSaveEvents(ctx context.Context, client *ethclient.Client, databas
|
||||
log.Warn("Failed to get l2 event logs", "err", err)
|
||||
return err
|
||||
}
|
||||
depositL2CrossMsgs, relayedMsg, l2SentMsgs, err := utils.ParseBackendL2EventLogs(logs)
|
||||
depositL2CrossMsgs, msgHashes, relayedMsg, l2sentMsgs, err := utils.ParseBackendL2EventLogs(logs)
|
||||
if err != nil {
|
||||
log.Error("l2FetchAndSaveEvents: Failed to parse cross msg event logs", "err", err)
|
||||
return err
|
||||
}
|
||||
|
||||
dbTx, err := database.Beginx()
|
||||
if err != nil {
|
||||
log.Error("l2FetchAndSaveEvents: Failed to begin db transaction", "err", err)
|
||||
@@ -174,7 +179,13 @@ func L2FetchAndSaveEvents(ctx context.Context, client *ethclient.Client, databas
|
||||
log.Crit("l2FetchAndSaveEvents: Failed to insert relayed message event logs", "err", err)
|
||||
}
|
||||
|
||||
err = database.BatchInsertL2SentMsgDBTx(dbTx, l2SentMsgs)
|
||||
err = updateL2CrossMsgMsgHash(ctx, dbTx, database, msgHashes)
|
||||
if err != nil {
|
||||
dbTx.Rollback()
|
||||
log.Crit("l2FetchAndSaveEvents: Failed to update msgHash in L2 cross msg", "err", err)
|
||||
}
|
||||
|
||||
err = database.BatchInsertL2SentMsgDBTx(dbTx, l2sentMsgs)
|
||||
if err != nil {
|
||||
dbTx.Rollback()
|
||||
log.Crit("l2FetchAndSaveEvents: Failed to insert l2 sent message", "err", err)
|
||||
@@ -229,3 +240,25 @@ func FetchAndSaveBatchIndex(ctx context.Context, client *ethclient.Client, datab
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateL1CrossMsgMsgHash(ctx context.Context, dbTx *sqlx.Tx, database db.OrmFactory, msgHashes []utils.MsgHashWrapper) error {
|
||||
for _, msgHash := range msgHashes {
|
||||
err := database.UpdateL1CrossMsgHashDBTx(ctx, dbTx, msgHash.TxHash, msgHash.MsgHash)
|
||||
if err != nil {
|
||||
log.Error("updateL1CrossMsgMsgHash: can not update layer1 cross msg MsgHash", "layer1 hash", msgHash.TxHash, "err", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateL2CrossMsgMsgHash(ctx context.Context, dbTx *sqlx.Tx, database db.OrmFactory, msgHashes []utils.MsgHashWrapper) error {
|
||||
for _, msgHash := range msgHashes {
|
||||
err := database.UpdateL2CrossMsgHashDBTx(ctx, dbTx, msgHash.TxHash, msgHash.MsgHash)
|
||||
if err != nil {
|
||||
log.Error("updateL2CrossMsgMsgHash: can not update layer2 cross msg MsgHash", "layer2 hash", msgHash.TxHash, "err", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
create table cross_message
|
||||
(
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
msg_hash VARCHAR NOT NULL,
|
||||
msg_hash VARCHAR NOT NULL DEFAULT '',
|
||||
height BIGINT NOT NULL,
|
||||
sender VARCHAR NOT NULL,
|
||||
target VARCHAR NOT NULL,
|
||||
@@ -12,30 +12,30 @@ create table cross_message
|
||||
layer2_hash VARCHAR NOT NULL DEFAULT '',
|
||||
layer1_token VARCHAR NOT NULL DEFAULT '',
|
||||
layer2_token VARCHAR NOT NULL DEFAULT '',
|
||||
token_id BIGINT NOT NULL DEFAULT 0,
|
||||
asset SMALLINT NOT NULL,
|
||||
msg_type SMALLINT NOT NULL,
|
||||
token_ids TEXT NOT NULL DEFAULT '',
|
||||
token_amounts TEXT NOT NULL DEFAULT '',
|
||||
is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
block_timestamp TIMESTAMP(0) DEFAULT NULL,
|
||||
created_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP(0) DEFAULT NULL
|
||||
);
|
||||
|
||||
create unique index uk_msg_hash_msg_type
|
||||
on cross_message (msg_hash, msg_type) where deleted_at IS NULL;
|
||||
|
||||
comment
|
||||
on column cross_message.asset is 'ETH, ERC20, ERC721, ERC1155';
|
||||
|
||||
comment
|
||||
on column cross_message.msg_type is 'unknown, l1msg, l2msg';
|
||||
|
||||
CREATE INDEX idx_l1_msg_index ON cross_message (layer1_hash, deleted_at);
|
||||
comment
|
||||
on column cross_message.is_deleted is 'NotDeleted false, Deleted true';
|
||||
|
||||
CREATE INDEX idx_l2_msg_index ON cross_message (layer2_hash, deleted_at);
|
||||
CREATE INDEX valid_l1_msg_index ON cross_message (layer1_hash, is_deleted);
|
||||
|
||||
CREATE INDEX idx_height_msg_type_index ON cross_message (height, msg_type, deleted_at);
|
||||
CREATE INDEX valid_l2_msg_index ON cross_message (layer2_hash, is_deleted);
|
||||
|
||||
CREATE INDEX valid_height_index ON cross_message (height, msg_type, is_deleted);
|
||||
|
||||
CREATE OR REPLACE FUNCTION update_timestamp()
|
||||
RETURNS TRIGGER AS $$
|
||||
@@ -49,6 +49,22 @@ CREATE TRIGGER update_timestamp BEFORE UPDATE
|
||||
ON cross_message FOR EACH ROW EXECUTE PROCEDURE
|
||||
update_timestamp();
|
||||
|
||||
CREATE OR REPLACE FUNCTION deleted_at_trigger()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF NEW.is_deleted AND OLD.is_deleted != NEW.is_deleted THEN
|
||||
UPDATE cross_message SET deleted_at = NOW() WHERE id = NEW.id;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER deleted_at_trigger
|
||||
AFTER UPDATE ON cross_message
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION deleted_at_trigger();
|
||||
|
||||
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
|
||||
@@ -7,17 +7,17 @@ create table relayed_msg
|
||||
height BIGINT NOT NULL,
|
||||
layer1_hash VARCHAR NOT NULL DEFAULT '',
|
||||
layer2_hash VARCHAR NOT NULL DEFAULT '',
|
||||
is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP(0) DEFAULT NULL
|
||||
);
|
||||
|
||||
create unique index uk_msg_hash_l1_hash_l2_hash
|
||||
on relayed_msg (msg_hash, layer1_hash, layer2_hash) where deleted_at IS NULL;
|
||||
comment
|
||||
on column relayed_msg.is_deleted is 'NotDeleted, Deleted';
|
||||
|
||||
CREATE INDEX idx_l1_msg_relayed_msg ON relayed_msg (layer1_hash, deleted_at);
|
||||
|
||||
CREATE INDEX idx_l2_msg_relayed_msg ON relayed_msg (layer2_hash, deleted_at);
|
||||
create unique index relayed_msg_hash_uindex
|
||||
on relayed_msg (msg_hash);
|
||||
|
||||
CREATE OR REPLACE FUNCTION update_timestamp()
|
||||
RETURNS TRIGGER AS $$
|
||||
@@ -31,6 +31,22 @@ CREATE TRIGGER update_timestamp BEFORE UPDATE
|
||||
ON relayed_msg FOR EACH ROW EXECUTE PROCEDURE
|
||||
update_timestamp();
|
||||
|
||||
CREATE OR REPLACE FUNCTION deleted_at_trigger()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF NEW.is_deleted AND OLD.is_deleted != NEW.is_deleted THEN
|
||||
UPDATE relayed_msg SET deleted_at = NOW() WHERE id = NEW.id;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER deleted_at_trigger
|
||||
AFTER UPDATE ON relayed_msg
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION deleted_at_trigger();
|
||||
|
||||
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
create table l2_sent_msg
|
||||
(
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
original_sender VARCHAR NOT NULL DEFAULT '',
|
||||
sender VARCHAR NOT NULL,
|
||||
target VARCHAR NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
@@ -13,16 +12,14 @@ create table l2_sent_msg
|
||||
batch_index BIGINT NOT NULL DEFAULT 0,
|
||||
msg_proof TEXT NOT NULL DEFAULT '',
|
||||
msg_data TEXT NOT NULL DEFAULT '',
|
||||
is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP(0) DEFAULT NULL
|
||||
);
|
||||
|
||||
create unique index uk_msg_hash
|
||||
on l2_sent_msg (msg_hash) where deleted_at IS NULL;
|
||||
|
||||
create unique index uk_nonce
|
||||
on l2_sent_msg (nonce) where deleted_at IS NULL;
|
||||
comment
|
||||
on column l2_sent_msg.is_deleted is 'NotDeleted, Deleted';
|
||||
|
||||
CREATE OR REPLACE FUNCTION update_timestamp()
|
||||
RETURNS TRIGGER AS $$
|
||||
@@ -36,6 +33,22 @@ CREATE TRIGGER update_timestamp BEFORE UPDATE
|
||||
ON l2_sent_msg FOR EACH ROW EXECUTE PROCEDURE
|
||||
update_timestamp();
|
||||
|
||||
CREATE OR REPLACE FUNCTION deleted_at_trigger()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF NEW.is_deleted AND OLD.is_deleted != NEW.is_deleted THEN
|
||||
UPDATE l2_sent_msg SET deleted_at = NOW() WHERE id = NEW.id;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER deleted_at_trigger
|
||||
AFTER UPDATE ON l2_sent_msg
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION deleted_at_trigger();
|
||||
|
||||
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
|
||||
@@ -8,17 +8,12 @@ create table rollup_batch
|
||||
start_block_number BIGINT NOT NULL,
|
||||
end_block_number BIGINT NOT NULL,
|
||||
batch_hash VARCHAR NOT NULL,
|
||||
is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP(0) DEFAULT NULL
|
||||
);
|
||||
|
||||
create unique index uk_batch_index
|
||||
on rollup_batch (batch_index) where deleted_at IS NULL;
|
||||
|
||||
create unique index uk_batch_hash
|
||||
on rollup_batch (batch_hash) where deleted_at IS NULL;
|
||||
|
||||
CREATE OR REPLACE FUNCTION update_timestamp()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
@@ -31,6 +26,21 @@ CREATE TRIGGER update_timestamp BEFORE UPDATE
|
||||
ON rollup_batch FOR EACH ROW EXECUTE PROCEDURE
|
||||
update_timestamp();
|
||||
|
||||
CREATE OR REPLACE FUNCTION deleted_at_trigger()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF NEW.is_deleted AND OLD.is_deleted != NEW.is_deleted THEN
|
||||
UPDATE rollup_batch SET deleted_at = NOW() WHERE id = NEW.id;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER deleted_at_trigger
|
||||
AFTER UPDATE ON rollup_batch
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION deleted_at_trigger();
|
||||
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
|
||||
@@ -2,6 +2,7 @@ package orm
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/jmoiron/sqlx"
|
||||
@@ -39,6 +40,14 @@ func (b *rollupBatchOrm) BatchInsertRollupBatchDBTx(dbTx *sqlx.Tx, batches []*Ro
|
||||
"start_block_number": batch.StartBlockNumber,
|
||||
"end_block_number": batch.EndBlockNumber,
|
||||
}
|
||||
var exists bool
|
||||
err = dbTx.QueryRow(`SELECT EXISTS(SELECT 1 FROM rollup_batch WHERE batch_index = $1 AND NOT is_deleted)`, batch.BatchIndex).Scan(&exists)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exists {
|
||||
return fmt.Errorf("BatchInsertRollupBatchDBTx: batch index %v already exists at height %v", batch.BatchIndex, batch.CommitHeight)
|
||||
}
|
||||
}
|
||||
_, err = dbTx.NamedExec(`insert into rollup_batch(commit_height, batch_index, batch_hash, start_block_number, end_block_number) values(:commit_height, :batch_index, :batch_hash, :start_block_number, :end_block_number);`, batchMaps)
|
||||
if err != nil {
|
||||
|
||||
@@ -40,24 +40,31 @@ const (
|
||||
|
||||
// CrossMsg represents a cross message from layer 1 to layer 2
|
||||
type CrossMsg struct {
|
||||
ID uint64 `json:"id" db:"id"`
|
||||
MsgHash string `json:"msg_hash" db:"msg_hash"`
|
||||
Height uint64 `json:"height" db:"height"`
|
||||
Sender string `json:"sender" db:"sender"`
|
||||
Target string `json:"target" db:"target"`
|
||||
Amount string `json:"amount" db:"amount"`
|
||||
Layer1Hash string `json:"layer1_hash" db:"layer1_hash"`
|
||||
Layer2Hash string `json:"layer2_hash" db:"layer2_hash"`
|
||||
Layer1Token string `json:"layer1_token" db:"layer1_token"`
|
||||
Layer2Token string `json:"layer2_token" db:"layer2_token"`
|
||||
TokenIDs string `json:"token_ids" db:"token_ids"`
|
||||
TokenAmounts string `json:"token_amounts" db:"token_amounts"`
|
||||
Asset int `json:"asset" db:"asset"`
|
||||
MsgType int `json:"msg_type" db:"msg_type"`
|
||||
Timestamp *time.Time `json:"timestamp" db:"block_timestamp"`
|
||||
CreatedAt *time.Time `json:"created_at" db:"created_at"`
|
||||
UpdatedAt *time.Time `json:"updated_at" db:"updated_at"`
|
||||
DeletedAt *time.Time `json:"deleted_at" db:"deleted_at"`
|
||||
ID uint64 `json:"id" db:"id"`
|
||||
MsgHash string `json:"msg_hash" db:"msg_hash"`
|
||||
Height uint64 `json:"height" db:"height"`
|
||||
Sender string `json:"sender" db:"sender"`
|
||||
Target string `json:"target" db:"target"`
|
||||
Amount string `json:"amount" db:"amount"`
|
||||
Layer1Hash string `json:"layer1_hash" db:"layer1_hash"`
|
||||
Layer2Hash string `json:"layer2_hash" db:"layer2_hash"`
|
||||
Layer1Token string `json:"layer1_token" db:"layer1_token"`
|
||||
Layer2Token string `json:"layer2_token" db:"layer2_token"`
|
||||
TokenID uint64 `json:"token_id" db:"token_id"`
|
||||
Asset int `json:"asset" db:"asset"`
|
||||
MsgType int `json:"msg_type" db:"msg_type"`
|
||||
IsDeleted bool `json:"is_deleted" db:"is_deleted"`
|
||||
Timestamp *time.Time `json:"timestamp" db:"block_timestamp"`
|
||||
CreatedAt *time.Time `json:"created_at" db:"created_at"`
|
||||
UpdatedAt *time.Time `json:"updated_at" db:"updated_at"`
|
||||
DeletedAt *time.Time `json:"deleted_at" db:"deleted_at"`
|
||||
}
|
||||
|
||||
type RelayedMsg struct {
|
||||
MsgHash string `json:"msg_hash" db:"msg_hash"`
|
||||
Height uint64 `json:"height" db:"height"`
|
||||
Layer1Hash string `json:"layer1_hash" db:"layer1_hash"`
|
||||
Layer2Hash string `json:"layer2_hash" db:"layer2_hash"`
|
||||
}
|
||||
|
||||
// L1CrossMsgOrm provides operations on l1_cross_message table
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@@ -22,7 +23,7 @@ func NewL1CrossMsgOrm(db *sqlx.DB) L1CrossMsgOrm {
|
||||
|
||||
func (l *l1CrossMsgOrm) GetL1CrossMsgByHash(l1Hash common.Hash) (*CrossMsg, error) {
|
||||
result := &CrossMsg{}
|
||||
row := l.db.QueryRowx(`SELECT * FROM cross_message WHERE layer1_hash = $1 AND msg_type = $2 AND deleted_at IS NULL;`, l1Hash.String(), Layer1Msg)
|
||||
row := l.db.QueryRowx(`SELECT * FROM cross_message WHERE layer1_hash = $1 AND msg_type = $2 AND NOT is_deleted;`, l1Hash.String(), Layer1Msg)
|
||||
if err := row.StructScan(result); err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
@@ -36,7 +37,7 @@ func (l *l1CrossMsgOrm) GetL1CrossMsgByHash(l1Hash common.Hash) (*CrossMsg, erro
|
||||
// Warning: return empty slice if no data found
|
||||
func (l *l1CrossMsgOrm) GetL1CrossMsgsByAddress(sender common.Address) ([]*CrossMsg, error) {
|
||||
var results []*CrossMsg
|
||||
rows, err := l.db.Queryx(`SELECT * FROM cross_message WHERE sender = $1 AND msg_type = 1 AND deleted_at IS NULL;`, sender.String(), Layer1Msg)
|
||||
rows, err := l.db.Queryx(`SELECT * FROM cross_message WHERE sender = $1 AND msg_type = 1 AND NOT is_deleted;`, sender.String(), Layer1Msg)
|
||||
|
||||
for rows.Next() {
|
||||
msg := &CrossMsg{}
|
||||
@@ -65,15 +66,22 @@ func (l *l1CrossMsgOrm) BatchInsertL1CrossMsgDBTx(dbTx *sqlx.Tx, messages []*Cro
|
||||
"target": msg.Target,
|
||||
"amount": msg.Amount,
|
||||
"asset": msg.Asset,
|
||||
"msg_hash": msg.MsgHash,
|
||||
"layer1_hash": msg.Layer1Hash,
|
||||
"layer1_token": msg.Layer1Token,
|
||||
"layer2_token": msg.Layer2Token,
|
||||
"token_ids": msg.TokenIDs,
|
||||
"token_id": msg.TokenID,
|
||||
"msg_type": Layer1Msg,
|
||||
}
|
||||
var exists bool
|
||||
err = dbTx.QueryRow(`SELECT EXISTS(SELECT 1 FROM cross_message WHERE layer1_hash = $1 AND NOT is_deleted)`, msg.Layer1Hash).Scan(&exists)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exists {
|
||||
return fmt.Errorf("BatchInsertL1CrossMsgDBTx: l1 cross msg layer1Hash %v already exists at height %v", msg.Layer1Hash, msg.Height)
|
||||
}
|
||||
}
|
||||
_, err = dbTx.NamedExec(`insert into cross_message(height, sender, target, amount, asset, msg_hash, layer1_hash, layer1_token, layer2_token, token_ids, msg_type) values(:height, :sender, :target, :amount, :asset, :msg_hash, :layer1_hash, :layer1_token, :layer2_token, :token_ids, :msg_type);`, messageMaps)
|
||||
_, err = dbTx.NamedExec(`insert into cross_message(height, sender, target, asset, layer1_hash, layer1_token, layer2_token, token_id, amount, msg_type) values(:height, :sender, :target, :asset, :layer1_hash, :layer1_token, :layer2_token, :token_id, :amount, :msg_type);`, messageMaps)
|
||||
if err != nil {
|
||||
log.Error("BatchInsertL1CrossMsgDBTx: failed to insert l1 cross msgs", "err", err)
|
||||
return err
|
||||
@@ -84,7 +92,7 @@ func (l *l1CrossMsgOrm) BatchInsertL1CrossMsgDBTx(dbTx *sqlx.Tx, messages []*Cro
|
||||
|
||||
// UpdateL1CrossMsgHashDBTx update l1 cross msg hash in db, no need to check msg_type since layer1_hash wont be empty if its layer1 msg
|
||||
func (l *l1CrossMsgOrm) UpdateL1CrossMsgHashDBTx(ctx context.Context, dbTx *sqlx.Tx, l1Hash, msgHash common.Hash) error {
|
||||
if _, err := dbTx.ExecContext(ctx, l.db.Rebind("update public.cross_message set msg_hash = ? where layer1_hash = ? AND deleted_at IS NULL;"), msgHash.String(), l1Hash.String()); err != nil {
|
||||
if _, err := dbTx.ExecContext(ctx, l.db.Rebind("update public.cross_message set msg_hash = ? where layer1_hash = ? AND NOT is_deleted;"), msgHash.String(), l1Hash.String()); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -92,7 +100,7 @@ func (l *l1CrossMsgOrm) UpdateL1CrossMsgHashDBTx(ctx context.Context, dbTx *sqlx
|
||||
}
|
||||
|
||||
func (l *l1CrossMsgOrm) UpdateL1CrossMsgHash(ctx context.Context, l1Hash, msgHash common.Hash) error {
|
||||
if _, err := l.db.ExecContext(ctx, l.db.Rebind("update public.l1_cross_message set msg_hash = ? where layer1_hash = ? AND deleted_at IS NULL;"), msgHash.String(), l1Hash.String()); err != nil {
|
||||
if _, err := l.db.ExecContext(ctx, l.db.Rebind("update public.l1_cross_message set msg_hash = ? where layer1_hash = ? AND NOT is_deleted;"), msgHash.String(), l1Hash.String()); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -100,7 +108,7 @@ func (l *l1CrossMsgOrm) UpdateL1CrossMsgHash(ctx context.Context, l1Hash, msgHas
|
||||
}
|
||||
|
||||
func (l *l1CrossMsgOrm) GetLatestL1ProcessedHeight() (int64, error) {
|
||||
row := l.db.QueryRowx(`SELECT height FROM cross_message WHERE msg_type = $1 AND deleted_at IS NULL ORDER BY id DESC LIMIT 1;`, Layer1Msg)
|
||||
row := l.db.QueryRowx(`SELECT height FROM cross_message WHERE msg_type = $1 AND NOT is_deleted ORDER BY id DESC LIMIT 1;`, Layer1Msg)
|
||||
var result sql.NullInt64
|
||||
if err := row.Scan(&result); err != nil {
|
||||
if err == sql.ErrNoRows || !result.Valid {
|
||||
@@ -115,21 +123,21 @@ func (l *l1CrossMsgOrm) GetLatestL1ProcessedHeight() (int64, error) {
|
||||
}
|
||||
|
||||
func (l *l1CrossMsgOrm) DeleteL1CrossMsgAfterHeightDBTx(dbTx *sqlx.Tx, height int64) error {
|
||||
if _, err := l.db.Exec(`UPDATE cross_message SET deleted_at = current_timestamp WHERE height > $1 AND msg_type = $2;`, height, Layer1Msg); err != nil {
|
||||
if _, err := l.db.Exec(`UPDATE cross_message SET is_deleted = true WHERE height > $1 AND msg_type = $2;`, height, Layer1Msg); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *l1CrossMsgOrm) UpdateL1BlockTimestamp(height uint64, timestamp time.Time) error {
|
||||
if _, err := l.db.Exec(`UPDATE cross_message SET block_timestamp = $1 where height = $2 AND msg_type = $3 AND deleted_at IS NULL`, timestamp, height, Layer1Msg); err != nil {
|
||||
if _, err := l.db.Exec(`UPDATE cross_message SET block_timestamp = $1 where height = $2 AND msg_type = $3 AND NOT is_deleted`, timestamp, height, Layer1Msg); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *l1CrossMsgOrm) GetL1EarliestNoBlockTimestampHeight() (uint64, error) {
|
||||
row := l.db.QueryRowx(`SELECT height FROM cross_message WHERE block_timestamp IS NULL AND msg_type = $1 AND deleted_at IS NULL ORDER BY height ASC LIMIT 1;`, Layer1Msg)
|
||||
row := l.db.QueryRowx(`SELECT height FROM cross_message WHERE block_timestamp IS NULL AND msg_type = $1 AND NOT is_deleted ORDER BY height ASC LIMIT 1;`, Layer1Msg)
|
||||
var result uint64
|
||||
if err := row.Scan(&result); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@@ -22,7 +23,7 @@ func NewL2CrossMsgOrm(db *sqlx.DB) L2CrossMsgOrm {
|
||||
|
||||
func (l *l2CrossMsgOrm) GetL2CrossMsgByHash(l2Hash common.Hash) (*CrossMsg, error) {
|
||||
result := &CrossMsg{}
|
||||
row := l.db.QueryRowx(`SELECT * FROM cross_message WHERE layer2_hash = $1 AND deleted_at IS NULL;`, l2Hash.String())
|
||||
row := l.db.QueryRowx(`SELECT * FROM cross_message WHERE layer2_hash = $1 AND NOT is_deleted;`, l2Hash.String())
|
||||
if err := row.StructScan(result); err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
@@ -36,7 +37,7 @@ func (l *l2CrossMsgOrm) GetL2CrossMsgByHash(l2Hash common.Hash) (*CrossMsg, erro
|
||||
// Warning: return empty slice if no data found
|
||||
func (l *l2CrossMsgOrm) GetL2CrossMsgByAddress(sender common.Address) ([]*CrossMsg, error) {
|
||||
var results []*CrossMsg
|
||||
rows, err := l.db.Queryx(`SELECT * FROM cross_message WHERE sender = $1 AND msg_type = $2 AND deleted_at IS NULL;`, sender.String(), Layer2Msg)
|
||||
rows, err := l.db.Queryx(`SELECT * FROM cross_message WHERE sender = $1 AND msg_type = $2 AND NOT is_deleted;`, sender.String(), Layer2Msg)
|
||||
|
||||
for rows.Next() {
|
||||
msg := &CrossMsg{}
|
||||
@@ -55,7 +56,7 @@ func (l *l2CrossMsgOrm) GetL2CrossMsgByAddress(sender common.Address) ([]*CrossM
|
||||
}
|
||||
|
||||
func (l *l2CrossMsgOrm) DeleteL2CrossMsgFromHeightDBTx(dbTx *sqlx.Tx, height int64) error {
|
||||
_, err := dbTx.Exec(`UPDATE cross_message SET deleted_at = current_timestamp where height > $1 AND msg_type = $2 ;`, height, Layer2Msg)
|
||||
_, err := dbTx.Exec(`UPDATE cross_message SET is_deleted = true where height > $1 AND msg_type = $2 ;`, height, Layer2Msg)
|
||||
if err != nil {
|
||||
log.Error("DeleteL1CrossMsgAfterHeightDBTx: failed to delete", "height", height, "err", err)
|
||||
return err
|
||||
@@ -71,21 +72,29 @@ func (l *l2CrossMsgOrm) BatchInsertL2CrossMsgDBTx(dbTx *sqlx.Tx, messages []*Cro
|
||||
var err error
|
||||
messageMaps := make([]map[string]interface{}, len(messages))
|
||||
for i, msg := range messages {
|
||||
|
||||
messageMaps[i] = map[string]interface{}{
|
||||
"height": msg.Height,
|
||||
"sender": msg.Sender,
|
||||
"target": msg.Target,
|
||||
"asset": msg.Asset,
|
||||
"msg_hash": msg.MsgHash,
|
||||
"layer2_hash": msg.Layer2Hash,
|
||||
"layer1_token": msg.Layer1Token,
|
||||
"layer2_token": msg.Layer2Token,
|
||||
"token_ids": msg.TokenIDs,
|
||||
"token_id": msg.TokenID,
|
||||
"amount": msg.Amount,
|
||||
"msg_type": Layer2Msg,
|
||||
}
|
||||
var exists bool
|
||||
err = dbTx.QueryRow(`SELECT EXISTS(SELECT 1 FROM cross_message WHERE layer2_hash = $1 AND NOT is_deleted)`, msg.Layer2Hash).Scan(&exists)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exists {
|
||||
return fmt.Errorf("BatchInsertL2CrossMsgDBTx: l2 cross msg layer2Hash %v already exists at height %v", msg.Layer2Hash, msg.Height)
|
||||
}
|
||||
}
|
||||
_, err = dbTx.NamedExec(`insert into cross_message(height, sender, target, asset, msg_hash, layer2_hash, layer1_token, layer2_token, token_ids, amount, msg_type) values(:height, :sender, :target, :asset, :msg_hash, :layer2_hash, :layer1_token, :layer2_token, :token_ids, :amount, :msg_type);`, messageMaps)
|
||||
_, err = dbTx.NamedExec(`insert into cross_message(height, sender, target, asset, layer2_hash, layer1_token, layer2_token, token_id, amount, msg_type) values(:height, :sender, :target, :asset, :layer2_hash, :layer1_token, :layer2_token, :token_id, :amount, :msg_type);`, messageMaps)
|
||||
if err != nil {
|
||||
log.Error("BatchInsertL2CrossMsgDBTx: failed to insert l2 cross msgs", "err", err)
|
||||
return err
|
||||
@@ -94,21 +103,21 @@ func (l *l2CrossMsgOrm) BatchInsertL2CrossMsgDBTx(dbTx *sqlx.Tx, messages []*Cro
|
||||
}
|
||||
|
||||
func (l *l2CrossMsgOrm) UpdateL2CrossMsgHashDBTx(ctx context.Context, dbTx *sqlx.Tx, l2Hash, msgHash common.Hash) error {
|
||||
if _, err := dbTx.ExecContext(ctx, l.db.Rebind("update cross_message set msg_hash = ? where layer2_hash = ? AND deleted_at IS NULL;"), msgHash.String(), l2Hash.String()); err != nil {
|
||||
if _, err := dbTx.ExecContext(ctx, l.db.Rebind("update public.cross_message set msg_hash = ? where layer2_hash = ? AND NOT is_deleted;"), msgHash.String(), l2Hash.String()); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *l2CrossMsgOrm) UpdateL2CrossMsgHash(ctx context.Context, l2Hash, msgHash common.Hash) error {
|
||||
if _, err := l.db.ExecContext(ctx, l.db.Rebind("update cross_message set msg_hash = ? where layer2_hash = ? AND deleted_at IS NULL;"), msgHash.String(), l2Hash.String()); err != nil {
|
||||
if _, err := l.db.ExecContext(ctx, l.db.Rebind("update public.cross_message set msg_hash = ? where layer2_hash = ? AND NOT is_deleted;"), msgHash.String(), l2Hash.String()); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *l2CrossMsgOrm) GetLatestL2ProcessedHeight() (int64, error) {
|
||||
row := l.db.QueryRowx(`SELECT height FROM cross_message WHERE msg_type = $1 AND deleted_at IS NULL ORDER BY id DESC LIMIT 1;`, Layer2Msg)
|
||||
row := l.db.QueryRowx(`SELECT height FROM cross_message WHERE msg_type = $1 AND NOT is_deleted ORDER BY id DESC LIMIT 1;`, Layer2Msg)
|
||||
var result sql.NullInt64
|
||||
if err := row.Scan(&result); err != nil {
|
||||
if err == sql.ErrNoRows || !result.Valid {
|
||||
@@ -123,14 +132,14 @@ func (l *l2CrossMsgOrm) GetLatestL2ProcessedHeight() (int64, error) {
|
||||
}
|
||||
|
||||
func (l *l2CrossMsgOrm) UpdateL2BlockTimestamp(height uint64, timestamp time.Time) error {
|
||||
if _, err := l.db.Exec(`UPDATE cross_message SET block_timestamp = $1 where height = $2 AND msg_type = $3 AND deleted_at IS NULL`, timestamp, height, Layer2Msg); err != nil {
|
||||
if _, err := l.db.Exec(`UPDATE cross_message SET block_timestamp = $1 where height = $2 AND msg_type = $3 AND NOT is_deleted`, timestamp, height, Layer2Msg); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *l2CrossMsgOrm) GetL2EarliestNoBlockTimestampHeight() (uint64, error) {
|
||||
row := l.db.QueryRowx(`SELECT height FROM cross_message WHERE block_timestamp IS NULL AND msg_type = $1 AND deleted_at IS NULL ORDER BY height ASC LIMIT 1;`, Layer2Msg)
|
||||
row := l.db.QueryRowx(`SELECT height FROM cross_message WHERE block_timestamp IS NULL AND msg_type = $1 AND NOT is_deleted ORDER BY height ASC LIMIT 1;`, Layer2Msg)
|
||||
var result uint64
|
||||
if err := row.Scan(&result); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
|
||||
@@ -3,6 +3,7 @@ package orm
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
@@ -10,20 +11,20 @@ import (
|
||||
)
|
||||
|
||||
type L2SentMsg struct {
|
||||
ID uint64 `json:"id" db:"id"`
|
||||
OriginalSender string `json:"original_sender" db:"original_sender"`
|
||||
MsgHash string `json:"msg_hash" db:"msg_hash"`
|
||||
Sender string `json:"sender" db:"sender"`
|
||||
Target string `json:"target" db:"target"`
|
||||
Value string `json:"value" db:"value"`
|
||||
Height uint64 `json:"height" db:"height"`
|
||||
Nonce uint64 `json:"nonce" db:"nonce"`
|
||||
BatchIndex uint64 `json:"batch_index" db:"batch_index"`
|
||||
MsgProof string `json:"msg_proof" db:"msg_proof"`
|
||||
MsgData string `json:"msg_data" db:"msg_data"`
|
||||
CreatedAt *time.Time `json:"created_at" db:"created_at"`
|
||||
UpdatedAt *time.Time `json:"updated_at" db:"updated_at"`
|
||||
DeletedAt *time.Time `json:"deleted_at" db:"deleted_at"`
|
||||
ID uint64 `json:"id" db:"id"`
|
||||
MsgHash string `json:"msg_hash" db:"msg_hash"`
|
||||
Sender string `json:"sender" db:"sender"`
|
||||
Target string `json:"target" db:"target"`
|
||||
Value string `json:"value" db:"value"`
|
||||
Height uint64 `json:"height" db:"height"`
|
||||
Nonce uint64 `json:"nonce" db:"nonce"`
|
||||
BatchIndex uint64 `json:"batch_index" db:"batch_index"`
|
||||
MsgProof string `json:"msg_proof" db:"msg_proof"`
|
||||
MsgData string `json:"msg_data" db:"msg_data"`
|
||||
IsDeleted bool `json:"is_deleted" db:"is_deleted"`
|
||||
CreatedAt *time.Time `json:"created_at" db:"created_at"`
|
||||
UpdatedAt *time.Time `json:"updated_at" db:"updated_at"`
|
||||
DeletedAt *time.Time `json:"deleted_at" db:"deleted_at"`
|
||||
}
|
||||
|
||||
type l2SentMsgOrm struct {
|
||||
@@ -37,7 +38,7 @@ func NewL2SentMsgOrm(db *sqlx.DB) L2SentMsgOrm {
|
||||
|
||||
func (l *l2SentMsgOrm) GetL2SentMsgByHash(msgHash string) (*L2SentMsg, error) {
|
||||
result := &L2SentMsg{}
|
||||
row := l.db.QueryRowx(`SELECT * FROM l2_sent_msg WHERE msg_hash = $1 AND deleted_at IS NULL;`, msgHash)
|
||||
row := l.db.QueryRowx(`SELECT * FROM l2_sent_msg WHERE msg_hash = $1 AND NOT is_deleted;`, msgHash)
|
||||
if err := row.StructScan(result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -52,28 +53,35 @@ func (l *l2SentMsgOrm) BatchInsertL2SentMsgDBTx(dbTx *sqlx.Tx, messages []*L2Sen
|
||||
messageMaps := make([]map[string]interface{}, len(messages))
|
||||
for i, msg := range messages {
|
||||
messageMaps[i] = map[string]interface{}{
|
||||
"original_sender": msg.OriginalSender,
|
||||
"sender": msg.Sender,
|
||||
"target": msg.Target,
|
||||
"value": msg.Value,
|
||||
"msg_hash": msg.MsgHash,
|
||||
"height": msg.Height,
|
||||
"nonce": msg.Nonce,
|
||||
"batch_index": msg.BatchIndex,
|
||||
"msg_proof": msg.MsgProof,
|
||||
"msg_data": msg.MsgData,
|
||||
"sender": msg.Sender,
|
||||
"target": msg.Target,
|
||||
"value": msg.Value,
|
||||
"msg_hash": msg.MsgHash,
|
||||
"height": msg.Height,
|
||||
"nonce": msg.Nonce,
|
||||
"batch_index": msg.BatchIndex,
|
||||
"msg_proof": msg.MsgProof,
|
||||
"msg_data": msg.MsgData,
|
||||
}
|
||||
var exists bool
|
||||
err = dbTx.QueryRow(`SELECT EXISTS(SELECT 1 FROM l2_sent_msg WHERE (msg_hash = $1 OR nonce = $2) AND NOT is_deleted)`, msg.MsgHash, msg.Nonce).Scan(&exists)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exists {
|
||||
return fmt.Errorf("BatchInsertL2SentMsgDBTx: l2 sent msg_hash %v already exists at height %v", msg.MsgHash, msg.Height)
|
||||
}
|
||||
}
|
||||
_, err = dbTx.NamedExec(`insert into l2_sent_msg(original_sender, sender, target, value, msg_hash, height, nonce, batch_index, msg_proof, msg_data) values(:original_sender, :sender, :target, :value, :msg_hash, :height, :nonce, :batch_index, :msg_proof, :msg_data);`, messageMaps)
|
||||
_, err = dbTx.NamedExec(`insert into l2_sent_msg(sender, target, value, msg_hash, height, nonce, batch_index, msg_proof, msg_data) values(:sender, :target, :value, :msg_hash, :height, :nonce, :batch_index, :msg_proof, :msg_data);`, messageMaps)
|
||||
if err != nil {
|
||||
log.Error("BatchInsertL2SentMsgDBTx: failed to insert l2 sent msgs", "err", err)
|
||||
log.Error("BatchInsertL2SentMsgDBTx: failed to insert l2 sent msgs", "msg_Hash", "err", err)
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (l *l2SentMsgOrm) GetLatestSentMsgHeightOnL2() (int64, error) {
|
||||
row := l.db.QueryRow(`SELECT height FROM l2_sent_msg WHERE deleted_at IS NULL ORDER BY nonce DESC LIMIT 1;`)
|
||||
row := l.db.QueryRow(`SELECT height FROM l2_sent_msg WHERE NOT is_deleted ORDER BY nonce DESC LIMIT 1;`)
|
||||
var result sql.NullInt64
|
||||
if err := row.Scan(&result); err != nil {
|
||||
if err == sql.ErrNoRows || !result.Valid {
|
||||
@@ -88,14 +96,14 @@ func (l *l2SentMsgOrm) GetLatestSentMsgHeightOnL2() (int64, error) {
|
||||
}
|
||||
|
||||
func (l *l2SentMsgOrm) UpdateL2MessageProofInDBTx(ctx context.Context, dbTx *sqlx.Tx, msgHash string, proof string, batch_index uint64) error {
|
||||
if _, err := dbTx.ExecContext(ctx, l.db.Rebind("update l2_sent_msg set msg_proof = ?, batch_index = ? where msg_hash = ? AND deleted_at IS NULL;"), proof, batch_index, msgHash); err != nil {
|
||||
if _, err := dbTx.ExecContext(ctx, l.db.Rebind("update l2_sent_msg set msg_proof = ?, batch_index = ? where msg_hash = ? AND NOT is_deleted;"), proof, batch_index, msgHash); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *l2SentMsgOrm) GetLatestL2SentMsgBatchIndex() (int64, error) {
|
||||
row := l.db.QueryRow(`SELECT batch_index FROM l2_sent_msg WHERE batch_index != 0 AND deleted_at IS NULL ORDER BY batch_index DESC LIMIT 1;`)
|
||||
row := l.db.QueryRow(`SELECT batch_index FROM l2_sent_msg WHERE msg_proof != '' AND NOT is_deleted ORDER BY batch_index DESC LIMIT 1;`)
|
||||
var result sql.NullInt64
|
||||
if err := row.Scan(&result); err != nil {
|
||||
if err == sql.ErrNoRows || !result.Valid {
|
||||
@@ -111,7 +119,7 @@ func (l *l2SentMsgOrm) GetLatestL2SentMsgBatchIndex() (int64, error) {
|
||||
|
||||
func (l *l2SentMsgOrm) GetL2SentMsgMsgHashByHeightRange(startHeight, endHeight uint64) ([]*L2SentMsg, error) {
|
||||
var results []*L2SentMsg
|
||||
rows, err := l.db.Queryx(`SELECT * FROM l2_sent_msg WHERE height >= $1 AND height <= $2 AND deleted_at IS NULL ORDER BY nonce ASC;`, startHeight, endHeight)
|
||||
rows, err := l.db.Queryx(`SELECT * FROM l2_sent_msg WHERE height >= $1 AND height <= $2 AND NOT is_deleted ORDER BY nonce ASC;`, startHeight, endHeight)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -127,7 +135,7 @@ func (l *l2SentMsgOrm) GetL2SentMsgMsgHashByHeightRange(startHeight, endHeight u
|
||||
|
||||
func (l *l2SentMsgOrm) GetL2SentMessageByNonce(nonce uint64) (*L2SentMsg, error) {
|
||||
result := &L2SentMsg{}
|
||||
row := l.db.QueryRowx(`SELECT * FROM l2_sent_msg WHERE nonce = $1 AND deleted_at IS NULL;`, nonce)
|
||||
row := l.db.QueryRowx(`SELECT * FROM l2_sent_msg WHERE nonce = $1 AND NOT is_deleted;`, nonce)
|
||||
err := row.StructScan(result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -137,7 +145,7 @@ func (l *l2SentMsgOrm) GetL2SentMessageByNonce(nonce uint64) (*L2SentMsg, error)
|
||||
|
||||
func (l *l2SentMsgOrm) GetLatestL2SentMsgLEHeight(endBlockNumber uint64) (*L2SentMsg, error) {
|
||||
result := &L2SentMsg{}
|
||||
row := l.db.QueryRowx(`select * from l2_sent_msg where height <= $1 AND deleted_at IS NULL order by nonce desc limit 1`, endBlockNumber)
|
||||
row := l.db.QueryRowx(`select * from l2_sent_msg where height <= $1 AND NOT is_deleted order by nonce desc limit 1`, endBlockNumber)
|
||||
err := row.StructScan(result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -146,6 +154,6 @@ func (l *l2SentMsgOrm) GetLatestL2SentMsgLEHeight(endBlockNumber uint64) (*L2Sen
|
||||
}
|
||||
|
||||
func (l *l2SentMsgOrm) DeleteL2SentMsgAfterHeightDBTx(dbTx *sqlx.Tx, height int64) error {
|
||||
_, err := dbTx.Exec(`UPDATE l2_sent_msg SET deleted_at = current_timestamp WHERE height > $1;`, height)
|
||||
_, err := dbTx.Exec(`UPDATE l2_sent_msg SET is_deleted = true WHERE height > $1;`, height)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -8,13 +8,6 @@ import (
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type RelayedMsg struct {
|
||||
MsgHash string `json:"msg_hash" db:"msg_hash"`
|
||||
Height uint64 `json:"height" db:"height"`
|
||||
Layer1Hash string `json:"layer1_hash" db:"layer1_hash"`
|
||||
Layer2Hash string `json:"layer2_hash" db:"layer2_hash"`
|
||||
}
|
||||
|
||||
type relayedMsgOrm struct {
|
||||
db *sqlx.DB
|
||||
}
|
||||
@@ -40,7 +33,7 @@ func (l *relayedMsgOrm) BatchInsertRelayedMsgDBTx(dbTx *sqlx.Tx, messages []*Rel
|
||||
}
|
||||
_, err = dbTx.NamedExec(`insert into relayed_msg(msg_hash, height, layer1_hash, layer2_hash) values(:msg_hash, :height, :layer1_hash, :layer2_hash);`, messageMaps)
|
||||
if err != nil {
|
||||
log.Error("BatchInsertRelayedMsgDBTx: failed to insert relayed msgs", "err", err)
|
||||
log.Error("BatchInsertRelayedMsgDBTx: failed to insert l1 cross msgs", "msg_Hashe", "err", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -48,7 +41,7 @@ func (l *relayedMsgOrm) BatchInsertRelayedMsgDBTx(dbTx *sqlx.Tx, messages []*Rel
|
||||
|
||||
func (l *relayedMsgOrm) GetRelayedMsgByHash(msg_hash string) (*RelayedMsg, error) {
|
||||
result := &RelayedMsg{}
|
||||
row := l.db.QueryRowx(`SELECT msg_hash, height, layer1_hash, layer2_hash FROM relayed_msg WHERE msg_hash = $1 AND deleted_at IS NULL;`, msg_hash)
|
||||
row := l.db.QueryRowx(`SELECT msg_hash, height, layer1_hash, layer2_hash FROM relayed_msg WHERE msg_hash = $1 AND NOT is_deleted;`, msg_hash)
|
||||
if err := row.StructScan(result); err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
@@ -59,7 +52,7 @@ func (l *relayedMsgOrm) GetRelayedMsgByHash(msg_hash string) (*RelayedMsg, error
|
||||
}
|
||||
|
||||
func (l *relayedMsgOrm) GetLatestRelayedHeightOnL1() (int64, error) {
|
||||
row := l.db.QueryRow(`SELECT height FROM relayed_msg WHERE layer1_hash != '' AND deleted_at IS NULL ORDER BY height DESC LIMIT 1;`)
|
||||
row := l.db.QueryRow(`SELECT height FROM relayed_msg WHERE layer1_hash != '' AND NOT is_deleted ORDER BY height DESC LIMIT 1;`)
|
||||
var result sql.NullInt64
|
||||
if err := row.Scan(&result); err != nil {
|
||||
if err == sql.ErrNoRows || !result.Valid {
|
||||
@@ -74,7 +67,7 @@ func (l *relayedMsgOrm) GetLatestRelayedHeightOnL1() (int64, error) {
|
||||
}
|
||||
|
||||
func (l *relayedMsgOrm) GetLatestRelayedHeightOnL2() (int64, error) {
|
||||
row := l.db.QueryRow(`SELECT height FROM relayed_msg WHERE layer2_hash != '' AND deleted_at IS NULL ORDER BY height DESC LIMIT 1;`)
|
||||
row := l.db.QueryRow(`SELECT height FROM relayed_msg WHERE layer2_hash != '' AND NOT is_deleted ORDER BY height DESC LIMIT 1;`)
|
||||
var result sql.NullInt64
|
||||
if err := row.Scan(&result); err != nil {
|
||||
if err == sql.ErrNoRows || !result.Valid {
|
||||
@@ -89,11 +82,11 @@ func (l *relayedMsgOrm) GetLatestRelayedHeightOnL2() (int64, error) {
|
||||
}
|
||||
|
||||
func (l *relayedMsgOrm) DeleteL1RelayedHashAfterHeightDBTx(dbTx *sqlx.Tx, height int64) error {
|
||||
_, err := dbTx.Exec(`UPDATE relayed_msg SET deleted_at = current_timestamp WHERE height > $1 AND layer1_hash != '';`, height)
|
||||
_, err := dbTx.Exec(`UPDATE relayed_msg SET is_deleted = true WHERE height > $1 AND layer1_hash != '';`, height)
|
||||
return err
|
||||
}
|
||||
|
||||
func (l *relayedMsgOrm) DeleteL2RelayedHashAfterHeightDBTx(dbTx *sqlx.Tx, height int64) error {
|
||||
_, err := dbTx.Exec(`UPDATE relayed_msg SET deleted_at = current_timestamp WHERE height > $1 AND layer2_hash != '';`, height)
|
||||
_, err := dbTx.Exec(`UPDATE relayed_msg SET is_deleted = true WHERE height > $1 AND layer2_hash != '';`, height)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ func (o *ormFactory) Beginx() (*sqlx.Tx, error) {
|
||||
|
||||
func (o *ormFactory) GetTotalCrossMsgCountByAddress(sender string) (uint64, error) {
|
||||
var count uint64
|
||||
row := o.DB.QueryRowx(`SELECT COUNT(*) FROM cross_message WHERE sender = $1 AND deleted_at IS NULL;`, sender)
|
||||
row := o.DB.QueryRowx(`SELECT COUNT(*) FROM cross_message WHERE sender = $1 AND NOT is_deleted;`, sender)
|
||||
if err := row.Scan(&count); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -78,7 +78,7 @@ func (o *ormFactory) GetTotalCrossMsgCountByAddress(sender string) (uint64, erro
|
||||
func (o *ormFactory) GetCrossMsgsByAddressWithOffset(sender string, offset int64, limit int64) ([]*orm.CrossMsg, error) {
|
||||
para := sender
|
||||
var results []*orm.CrossMsg
|
||||
rows, err := o.DB.Queryx(`SELECT * FROM cross_message WHERE sender = $1 AND deleted_at IS NULL ORDER BY block_timestamp DESC NULLS FIRST, id DESC LIMIT $2 OFFSET $3;`, para, limit, offset)
|
||||
rows, err := o.DB.Queryx(`SELECT * FROM cross_message WHERE sender = $1 AND NOT is_deleted ORDER BY block_timestamp DESC NULLS FIRST, id DESC LIMIT $2 OFFSET $3;`, para, limit, offset)
|
||||
if err != nil || rows == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ func GetCrossTxClaimInfo(msgHash string, db db.OrmFactory) *UserClaimInfo {
|
||||
Value: l2sentMsg.Value,
|
||||
Nonce: strconv.FormatUint(l2sentMsg.Nonce, 10),
|
||||
Message: l2sentMsg.MsgData,
|
||||
Proof: "0x" + l2sentMsg.MsgProof,
|
||||
Proof: l2sentMsg.MsgProof,
|
||||
BatchHash: batch.BatchHash,
|
||||
BatchIndex: strconv.FormatUint(l2sentMsg.BatchIndex, 10),
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package utils
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
@@ -12,6 +13,11 @@ import (
|
||||
"bridge-history-api/db/orm"
|
||||
)
|
||||
|
||||
type MsgHashWrapper struct {
|
||||
MsgHash common.Hash
|
||||
TxHash common.Hash
|
||||
}
|
||||
|
||||
type CachedParsedTxCalldata struct {
|
||||
CallDataIndex uint64
|
||||
BatchIndices []uint64
|
||||
@@ -19,13 +25,13 @@ type CachedParsedTxCalldata struct {
|
||||
EndBlocks []uint64
|
||||
}
|
||||
|
||||
func ParseBackendL1EventLogs(logs []types.Log) ([]*orm.CrossMsg, []*orm.RelayedMsg, error) {
|
||||
func ParseBackendL1EventLogs(logs []types.Log) ([]*orm.CrossMsg, []MsgHashWrapper, []*orm.RelayedMsg, error) {
|
||||
// Need use contract abi to parse event Log
|
||||
// Can only be tested after we have our contracts set up
|
||||
|
||||
var l1CrossMsg []*orm.CrossMsg
|
||||
var relayedMsgs []*orm.RelayedMsg
|
||||
var msgHash string
|
||||
var msgHashes []MsgHashWrapper
|
||||
for _, vlog := range logs {
|
||||
switch vlog.Topics[0] {
|
||||
case backendabi.L1DepositETHSig:
|
||||
@@ -33,7 +39,7 @@ func ParseBackendL1EventLogs(logs []types.Log) ([]*orm.CrossMsg, []*orm.RelayedM
|
||||
err := UnpackLog(backendabi.L1ETHGatewayABI, &event, "DepositETH", vlog)
|
||||
if err != nil {
|
||||
log.Warn("Failed to unpack DepositETH event", "err", err)
|
||||
return l1CrossMsg, relayedMsgs, err
|
||||
return l1CrossMsg, msgHashes, relayedMsgs, err
|
||||
}
|
||||
l1CrossMsg = append(l1CrossMsg, &orm.CrossMsg{
|
||||
Height: vlog.BlockNumber,
|
||||
@@ -42,14 +48,13 @@ func ParseBackendL1EventLogs(logs []types.Log) ([]*orm.CrossMsg, []*orm.RelayedM
|
||||
Amount: event.Amount.String(),
|
||||
Asset: int(orm.ETH),
|
||||
Layer1Hash: vlog.TxHash.Hex(),
|
||||
MsgHash: msgHash,
|
||||
})
|
||||
case backendabi.L1DepositERC20Sig:
|
||||
event := backendabi.ERC20MessageEvent{}
|
||||
err := UnpackLog(backendabi.L1StandardERC20GatewayABI, &event, "DepositERC20", vlog)
|
||||
if err != nil {
|
||||
log.Warn("Failed to unpack DepositERC20 event", "err", err)
|
||||
return l1CrossMsg, relayedMsgs, err
|
||||
return l1CrossMsg, msgHashes, relayedMsgs, err
|
||||
}
|
||||
l1CrossMsg = append(l1CrossMsg, &orm.CrossMsg{
|
||||
Height: vlog.BlockNumber,
|
||||
@@ -60,14 +65,13 @@ func ParseBackendL1EventLogs(logs []types.Log) ([]*orm.CrossMsg, []*orm.RelayedM
|
||||
Layer1Hash: vlog.TxHash.Hex(),
|
||||
Layer1Token: event.L1Token.Hex(),
|
||||
Layer2Token: event.L2Token.Hex(),
|
||||
MsgHash: msgHash,
|
||||
})
|
||||
case backendabi.L1DepositERC721Sig:
|
||||
event := backendabi.ERC721MessageEvent{}
|
||||
err := UnpackLog(backendabi.L1ERC721GatewayABI, &event, "DepositERC721", vlog)
|
||||
if err != nil {
|
||||
log.Warn("Failed to unpack DepositERC721 event", "err", err)
|
||||
return l1CrossMsg, relayedMsgs, err
|
||||
return l1CrossMsg, msgHashes, relayedMsgs, err
|
||||
}
|
||||
l1CrossMsg = append(l1CrossMsg, &orm.CrossMsg{
|
||||
Height: vlog.BlockNumber,
|
||||
@@ -77,15 +81,14 @@ func ParseBackendL1EventLogs(logs []types.Log) ([]*orm.CrossMsg, []*orm.RelayedM
|
||||
Layer1Hash: vlog.TxHash.Hex(),
|
||||
Layer1Token: event.L1Token.Hex(),
|
||||
Layer2Token: event.L2Token.Hex(),
|
||||
TokenIDs: event.TokenID.String(),
|
||||
MsgHash: msgHash,
|
||||
TokenID: event.TokenID.Uint64(),
|
||||
})
|
||||
case backendabi.L1DepositERC1155Sig:
|
||||
event := backendabi.ERC1155MessageEvent{}
|
||||
err := UnpackLog(backendabi.L1ERC1155GatewayABI, &event, "DepositERC1155", vlog)
|
||||
if err != nil {
|
||||
log.Warn("Failed to unpack DepositERC1155 event", "err", err)
|
||||
return l1CrossMsg, relayedMsgs, err
|
||||
return l1CrossMsg, msgHashes, relayedMsgs, err
|
||||
}
|
||||
l1CrossMsg = append(l1CrossMsg, &orm.CrossMsg{
|
||||
Height: vlog.BlockNumber,
|
||||
@@ -95,26 +98,26 @@ func ParseBackendL1EventLogs(logs []types.Log) ([]*orm.CrossMsg, []*orm.RelayedM
|
||||
Layer1Hash: vlog.TxHash.Hex(),
|
||||
Layer1Token: event.L1Token.Hex(),
|
||||
Layer2Token: event.L2Token.Hex(),
|
||||
TokenIDs: event.TokenID.String(),
|
||||
TokenID: event.TokenID.Uint64(),
|
||||
Amount: event.Amount.String(),
|
||||
MsgHash: msgHash,
|
||||
})
|
||||
case backendabi.L1SentMessageEventSignature:
|
||||
event := backendabi.L1SentMessageEvent{}
|
||||
err := UnpackLog(backendabi.L1ScrollMessengerABI, &event, "SentMessage", vlog)
|
||||
if err != nil {
|
||||
log.Warn("Failed to unpack SentMessage event", "err", err)
|
||||
return l1CrossMsg, relayedMsgs, err
|
||||
return l1CrossMsg, msgHashes, relayedMsgs, err
|
||||
}
|
||||
// since every deposit event will emit after a sent event, so can use this msg_hash as next withdraw event's msg_hash
|
||||
msgHash = ComputeMessageHash(event.Sender, event.Target, event.Value, event.MessageNonce, event.Message).Hex()
|
||||
|
||||
msgHash := ComputeMessageHash(event.Sender, event.Target, event.Value, event.MessageNonce, event.Message)
|
||||
msgHashes = append(msgHashes, MsgHashWrapper{
|
||||
MsgHash: msgHash,
|
||||
TxHash: vlog.TxHash})
|
||||
case backendabi.L1RelayedMessageEventSignature:
|
||||
event := backendabi.L1RelayedMessageEvent{}
|
||||
err := UnpackLog(backendabi.L1ScrollMessengerABI, &event, "RelayedMessage", vlog)
|
||||
if err != nil {
|
||||
log.Warn("Failed to unpack RelayedMessage event", "err", err)
|
||||
return l1CrossMsg, relayedMsgs, err
|
||||
return l1CrossMsg, msgHashes, relayedMsgs, err
|
||||
}
|
||||
relayedMsgs = append(relayedMsgs, &orm.RelayedMsg{
|
||||
MsgHash: event.MessageHash.String(),
|
||||
@@ -125,17 +128,18 @@ func ParseBackendL1EventLogs(logs []types.Log) ([]*orm.CrossMsg, []*orm.RelayedM
|
||||
}
|
||||
|
||||
}
|
||||
return l1CrossMsg, relayedMsgs, nil
|
||||
return l1CrossMsg, msgHashes, relayedMsgs, nil
|
||||
}
|
||||
|
||||
func ParseBackendL2EventLogs(logs []types.Log) ([]*orm.CrossMsg, []*orm.RelayedMsg, []*orm.L2SentMsg, error) {
|
||||
func ParseBackendL2EventLogs(logs []types.Log) ([]*orm.CrossMsg, []MsgHashWrapper, []*orm.RelayedMsg, []*orm.L2SentMsg, error) {
|
||||
// Need use contract abi to parse event Log
|
||||
// Can only be tested after we have our contracts set up
|
||||
|
||||
var l2CrossMsg []*orm.CrossMsg
|
||||
// this is use to confirm finalized l1 msg
|
||||
var relayedMsgs []*orm.RelayedMsg
|
||||
var l2SentMsgs []*orm.L2SentMsg
|
||||
var l2SentMsg []*orm.L2SentMsg
|
||||
var msgHashes []MsgHashWrapper
|
||||
for _, vlog := range logs {
|
||||
switch vlog.Topics[0] {
|
||||
case backendabi.L2WithdrawETHSig:
|
||||
@@ -143,9 +147,8 @@ func ParseBackendL2EventLogs(logs []types.Log) ([]*orm.CrossMsg, []*orm.RelayedM
|
||||
err := UnpackLog(backendabi.L2ETHGatewayABI, &event, "WithdrawETH", vlog)
|
||||
if err != nil {
|
||||
log.Warn("Failed to unpack WithdrawETH event", "err", err)
|
||||
return l2CrossMsg, relayedMsgs, l2SentMsgs, err
|
||||
return l2CrossMsg, msgHashes, relayedMsgs, l2SentMsg, err
|
||||
}
|
||||
l2SentMsgs[len(l2SentMsgs)-1].OriginalSender = event.From.Hex()
|
||||
l2CrossMsg = append(l2CrossMsg, &orm.CrossMsg{
|
||||
Height: vlog.BlockNumber,
|
||||
Sender: event.From.String(),
|
||||
@@ -153,16 +156,14 @@ func ParseBackendL2EventLogs(logs []types.Log) ([]*orm.CrossMsg, []*orm.RelayedM
|
||||
Amount: event.Amount.String(),
|
||||
Asset: int(orm.ETH),
|
||||
Layer2Hash: vlog.TxHash.Hex(),
|
||||
MsgHash: l2SentMsgs[len(l2SentMsgs)-1].MsgHash,
|
||||
})
|
||||
case backendabi.L2WithdrawERC20Sig:
|
||||
event := backendabi.ERC20MessageEvent{}
|
||||
err := UnpackLog(backendabi.L2StandardERC20GatewayABI, &event, "WithdrawERC20", vlog)
|
||||
if err != nil {
|
||||
log.Warn("Failed to unpack WithdrawERC20 event", "err", err)
|
||||
return l2CrossMsg, relayedMsgs, l2SentMsgs, err
|
||||
return l2CrossMsg, msgHashes, relayedMsgs, l2SentMsg, err
|
||||
}
|
||||
l2SentMsgs[len(l2SentMsgs)-1].OriginalSender = event.From.Hex()
|
||||
l2CrossMsg = append(l2CrossMsg, &orm.CrossMsg{
|
||||
Height: vlog.BlockNumber,
|
||||
Sender: event.From.String(),
|
||||
@@ -178,9 +179,8 @@ func ParseBackendL2EventLogs(logs []types.Log) ([]*orm.CrossMsg, []*orm.RelayedM
|
||||
err := UnpackLog(backendabi.L2ERC721GatewayABI, &event, "WithdrawERC721", vlog)
|
||||
if err != nil {
|
||||
log.Warn("Failed to unpack WithdrawERC721 event", "err", err)
|
||||
return l2CrossMsg, relayedMsgs, l2SentMsgs, err
|
||||
return l2CrossMsg, msgHashes, relayedMsgs, l2SentMsg, err
|
||||
}
|
||||
l2SentMsgs[len(l2SentMsgs)-1].OriginalSender = event.From.Hex()
|
||||
l2CrossMsg = append(l2CrossMsg, &orm.CrossMsg{
|
||||
Height: vlog.BlockNumber,
|
||||
Sender: event.From.String(),
|
||||
@@ -189,16 +189,15 @@ func ParseBackendL2EventLogs(logs []types.Log) ([]*orm.CrossMsg, []*orm.RelayedM
|
||||
Layer2Hash: vlog.TxHash.Hex(),
|
||||
Layer1Token: event.L1Token.Hex(),
|
||||
Layer2Token: event.L2Token.Hex(),
|
||||
TokenIDs: event.TokenID.String(),
|
||||
TokenID: event.TokenID.Uint64(),
|
||||
})
|
||||
case backendabi.L2WithdrawERC1155Sig:
|
||||
event := backendabi.ERC1155MessageEvent{}
|
||||
err := UnpackLog(backendabi.L2ERC1155GatewayABI, &event, "WithdrawERC1155", vlog)
|
||||
if err != nil {
|
||||
log.Warn("Failed to unpack WithdrawERC1155 event", "err", err)
|
||||
return l2CrossMsg, relayedMsgs, l2SentMsgs, err
|
||||
return l2CrossMsg, msgHashes, relayedMsgs, l2SentMsg, err
|
||||
}
|
||||
l2SentMsgs[len(l2SentMsgs)-1].OriginalSender = event.From.Hex()
|
||||
l2CrossMsg = append(l2CrossMsg, &orm.CrossMsg{
|
||||
Height: vlog.BlockNumber,
|
||||
Sender: event.From.String(),
|
||||
@@ -207,7 +206,7 @@ func ParseBackendL2EventLogs(logs []types.Log) ([]*orm.CrossMsg, []*orm.RelayedM
|
||||
Layer2Hash: vlog.TxHash.Hex(),
|
||||
Layer1Token: event.L1Token.Hex(),
|
||||
Layer2Token: event.L2Token.Hex(),
|
||||
TokenIDs: event.TokenID.String(),
|
||||
TokenID: event.TokenID.Uint64(),
|
||||
Amount: event.Amount.String(),
|
||||
})
|
||||
case backendabi.L2SentMessageEventSignature:
|
||||
@@ -215,26 +214,27 @@ func ParseBackendL2EventLogs(logs []types.Log) ([]*orm.CrossMsg, []*orm.RelayedM
|
||||
err := UnpackLog(backendabi.L2ScrollMessengerABI, &event, "SentMessage", vlog)
|
||||
if err != nil {
|
||||
log.Warn("Failed to unpack SentMessage event", "err", err)
|
||||
return l2CrossMsg, relayedMsgs, l2SentMsgs, err
|
||||
return l2CrossMsg, msgHashes, relayedMsgs, l2SentMsg, err
|
||||
}
|
||||
// since every withdraw event will emit after a sent event, so can use this msg_hash as next withdraw event's msg_hash
|
||||
msgHash := ComputeMessageHash(event.Sender, event.Target, event.Value, event.MessageNonce, event.Message)
|
||||
l2SentMsgs = append(l2SentMsgs,
|
||||
&orm.L2SentMsg{
|
||||
Sender: event.Sender.Hex(),
|
||||
Target: event.Target.Hex(),
|
||||
Value: event.Value.String(),
|
||||
MsgHash: msgHash.Hex(),
|
||||
Height: vlog.BlockNumber,
|
||||
Nonce: event.MessageNonce.Uint64(),
|
||||
MsgData: hexutil.Encode(event.Message),
|
||||
})
|
||||
msgHashes = append(msgHashes, MsgHashWrapper{
|
||||
MsgHash: msgHash,
|
||||
TxHash: vlog.TxHash})
|
||||
l2SentMsg = append(l2SentMsg, &orm.L2SentMsg{
|
||||
Sender: event.Sender.Hex(),
|
||||
Target: event.Target.Hex(),
|
||||
Value: event.Value.String(),
|
||||
MsgHash: msgHash.Hex(),
|
||||
Height: vlog.BlockNumber,
|
||||
Nonce: event.MessageNonce.Uint64(),
|
||||
MsgData: hexutil.Encode(event.Message),
|
||||
})
|
||||
case backendabi.L2RelayedMessageEventSignature:
|
||||
event := backendabi.L2RelayedMessageEvent{}
|
||||
err := UnpackLog(backendabi.L2ScrollMessengerABI, &event, "RelayedMessage", vlog)
|
||||
if err != nil {
|
||||
log.Warn("Failed to unpack RelayedMessage event", "err", err)
|
||||
return l2CrossMsg, relayedMsgs, l2SentMsgs, err
|
||||
return l2CrossMsg, msgHashes, relayedMsgs, l2SentMsg, err
|
||||
}
|
||||
relayedMsgs = append(relayedMsgs, &orm.RelayedMsg{
|
||||
MsgHash: event.MessageHash.String(),
|
||||
@@ -244,7 +244,7 @@ func ParseBackendL2EventLogs(logs []types.Log) ([]*orm.CrossMsg, []*orm.RelayedM
|
||||
|
||||
}
|
||||
}
|
||||
return l2CrossMsg, relayedMsgs, l2SentMsgs, nil
|
||||
return l2CrossMsg, msgHashes, relayedMsgs, l2SentMsg, nil
|
||||
}
|
||||
|
||||
func ParseBatchInfoFromScrollChain(ctx context.Context, client *ethclient.Client, logs []types.Log) ([]*orm.RollupBatch, error) {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
.PHONY: lint docker clean bridge
|
||||
|
||||
IMAGE_NAME=bridge
|
||||
IMAGE_VERSION=latest
|
||||
IMAGE_VERSION=v9.9
|
||||
REPO_ROOT_DIR=./..
|
||||
|
||||
mock_abi:
|
||||
cd .. && go run github.com/scroll-tech/go-ethereum/cmd/abigen --sol ./bridge/mock_bridge/MockBridgeL1.sol --pkg mock_bridge --out ./bridge/mock_bridge/MockBridgeL1.go
|
||||
cd .. && go run github.com/scroll-tech/go-ethereum/cmd/abigen --sol ./bridge/mock_bridge/MockBridgeL2.sol --pkg mock_bridge --out ./bridge/mock_bridge/MockBridgeL2.go
|
||||
go run github.com/scroll-tech/go-ethereum/cmd/abigen --sol mock_bridge/MockBridgeL1.sol --pkg mock_bridge --out mock_bridge/MockBridgeL1.go
|
||||
go run github.com/scroll-tech/go-ethereum/cmd/abigen --sol mock_bridge/MockBridgeL2.sol --pkg mock_bridge --out mock_bridge/MockBridgeL2.go
|
||||
|
||||
bridge_bins: ## Builds the Bridge bins.
|
||||
go build -o $(PWD)/build/bin/event_watcher ./cmd/event_watcher/
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -16,9 +16,9 @@ func TestEventSignature(t *testing.T) {
|
||||
assert.Equal(L1FailedRelayedMessageEventSignature, common.HexToHash("99d0e048484baa1b1540b1367cb128acd7ab2946d1ed91ec10e3c85e4bf51b8f"))
|
||||
|
||||
assert.Equal(L1CommitBatchEventSignature, common.HexToHash("2cdc615c74452778c0fb6184735e014c13aad2b62774fe0b09bd1dcc2cc14a62"))
|
||||
assert.Equal(L1FinalizeBatchEventSignature, common.HexToHash("9d3058a3cb9739a2527f22dd9a4138065844037d3004254952e2458d808cc364"))
|
||||
assert.Equal(L1FinalizeBatchEventSignature, common.HexToHash("6be443154c959a7a1645b4392b6fa97d8e8ab6e8fd853d7085e8867083737d79"))
|
||||
|
||||
assert.Equal(L1QueueTransactionEventSignature, common.HexToHash("69cfcb8e6d4192b8aba9902243912587f37e550d75c1fa801491fce26717f37e"))
|
||||
assert.Equal(L1QueueTransactionEventSignature, common.HexToHash("bdcc7517f8fe3db6506dfd910942d0bbecaf3d6a506dadea65b0d988e75b9439"))
|
||||
|
||||
assert.Equal(L2SentMessageEventSignature, common.HexToHash("104371f3b442861a2a7b82a070afbbaab748bb13757bf47769e170e37809ec1e"))
|
||||
assert.Equal(L2RelayedMessageEventSignature, common.HexToHash("4641df4a962071e12719d8c8c8e5ac7fc4d97b927346a3d7a335b1f7517e133c"))
|
||||
@@ -35,10 +35,10 @@ func TestPackRelayL2MessageWithProof(t *testing.T) {
|
||||
assert.NoError(err)
|
||||
|
||||
proof := IL1ScrollMessengerL2MessageProof{
|
||||
BatchIndex: big.NewInt(0),
|
||||
MerkleProof: []byte{},
|
||||
BatchHash: common.Hash{},
|
||||
MerkleProof: make([]byte, 0),
|
||||
}
|
||||
_, err = l1MessengerABI.Pack("relayMessageWithProof", common.Address{}, common.Address{}, big.NewInt(0), big.NewInt(0), []byte{}, proof)
|
||||
_, err = l1MessengerABI.Pack("relayMessageWithProof", common.Address{}, common.Address{}, big.NewInt(0), big.NewInt(0), make([]byte, 0), proof)
|
||||
assert.NoError(err)
|
||||
}
|
||||
|
||||
@@ -48,12 +48,27 @@ func TestPackCommitBatch(t *testing.T) {
|
||||
scrollChainABI, err := ScrollChainMetaData.GetAbi()
|
||||
assert.NoError(err)
|
||||
|
||||
version := uint8(1)
|
||||
var parentBatchHeader []byte
|
||||
var chunks [][]byte
|
||||
var skippedL1MessageBitmap []byte
|
||||
header := IScrollChainBlockContext{
|
||||
BlockHash: common.Hash{},
|
||||
ParentHash: common.Hash{},
|
||||
BlockNumber: 0,
|
||||
Timestamp: 0,
|
||||
BaseFee: big.NewInt(0),
|
||||
GasLimit: 0,
|
||||
NumTransactions: 0,
|
||||
NumL1Messages: 0,
|
||||
}
|
||||
|
||||
_, err = scrollChainABI.Pack("commitBatch", version, parentBatchHeader, chunks, skippedL1MessageBitmap)
|
||||
batch := IScrollChainBatch{
|
||||
Blocks: []IScrollChainBlockContext{header},
|
||||
PrevStateRoot: common.Hash{},
|
||||
NewStateRoot: common.Hash{},
|
||||
WithdrawTrieRoot: common.Hash{},
|
||||
BatchIndex: 0,
|
||||
L2Transactions: make([]byte, 0),
|
||||
}
|
||||
|
||||
_, err = scrollChainABI.Pack("commitBatch", batch)
|
||||
assert.NoError(err)
|
||||
}
|
||||
|
||||
@@ -63,26 +78,14 @@ func TestPackFinalizeBatchWithProof(t *testing.T) {
|
||||
l1RollupABI, err := ScrollChainMetaData.GetAbi()
|
||||
assert.NoError(err)
|
||||
|
||||
batchHeader := []byte{}
|
||||
prevStateRoot := common.Hash{}
|
||||
postStateRoot := common.Hash{}
|
||||
withdrawRoot := common.Hash{}
|
||||
aggrProof := []byte{}
|
||||
proof := make([]*big.Int, 10)
|
||||
instance := make([]*big.Int, 10)
|
||||
for i := 0; i < 10; i++ {
|
||||
proof[i] = big.NewInt(0)
|
||||
instance[i] = big.NewInt(0)
|
||||
}
|
||||
|
||||
_, err = l1RollupABI.Pack("finalizeBatchWithProof", batchHeader, prevStateRoot, postStateRoot, withdrawRoot, aggrProof)
|
||||
assert.NoError(err)
|
||||
}
|
||||
|
||||
func TestPackImportGenesisBatch(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
l1RollupABI, err := ScrollChainMetaData.GetAbi()
|
||||
assert.NoError(err)
|
||||
|
||||
batchHeader := []byte{}
|
||||
stateRoot := common.Hash{}
|
||||
|
||||
_, err = l1RollupABI.Pack("importGenesisBatch", batchHeader, stateRoot)
|
||||
_, err = l1RollupABI.Pack("finalizeBatchWithProof", common.Hash{}, proof, instance)
|
||||
assert.NoError(err)
|
||||
}
|
||||
|
||||
@@ -92,7 +95,7 @@ func TestPackRelayL1Message(t *testing.T) {
|
||||
l2MessengerABI, err := L2ScrollMessengerMetaData.GetAbi()
|
||||
assert.NoError(err)
|
||||
|
||||
_, err = l2MessengerABI.Pack("relayMessage", common.Address{}, common.Address{}, big.NewInt(0), big.NewInt(0), []byte{})
|
||||
_, err = l2MessengerABI.Pack("relayMessage", common.Address{}, common.Address{}, big.NewInt(0), big.NewInt(0), make([]byte, 0))
|
||||
assert.NoError(err)
|
||||
}
|
||||
|
||||
@@ -123,6 +126,6 @@ func TestPackImportBlock(t *testing.T) {
|
||||
|
||||
l1BlockContainerABI := L1BlockContainerABI
|
||||
|
||||
_, err := l1BlockContainerABI.Pack("importBlockHeader", common.Hash{}, []byte{}, false)
|
||||
_, err := l1BlockContainerABI.Pack("importBlockHeader", common.Hash{}, make([]byte, 0), false)
|
||||
assert.NoError(err)
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ func action(ctx *cli.Context) error {
|
||||
log.Error("failed to create new l1 relayer", "config file", cfgFile, "error", err)
|
||||
return err
|
||||
}
|
||||
l2relayer, err := relayer.NewLayer2Relayer(ctx.Context, l2client, db, cfg.L2Config.RelayerConfig, false /* initGenesis */)
|
||||
l2relayer, err := relayer.NewLayer2Relayer(ctx.Context, l2client, db, cfg.L2Config.RelayerConfig)
|
||||
if err != nil {
|
||||
log.Error("failed to create new l2 relayer", "config file", cfgFile, "error", err)
|
||||
return err
|
||||
|
||||
@@ -62,6 +62,8 @@ func action(ctx *cli.Context) error {
|
||||
// Start metrics server.
|
||||
metrics.Serve(subCtx, ctx)
|
||||
|
||||
// Init l2geth connection
|
||||
|
||||
l1relayer, err := relayer.NewLayer1Relayer(ctx.Context, db, cfg.L1Config.RelayerConfig)
|
||||
if err != nil {
|
||||
log.Error("failed to create new l1 relayer", "config file", cfgFile, "error", err)
|
||||
|
||||
@@ -31,7 +31,6 @@ func init() {
|
||||
app.Usage = "The Scroll Rollup Relayer"
|
||||
app.Version = version.Version
|
||||
app.Flags = append(app.Flags, cutils.CommonFlags...)
|
||||
app.Flags = append(app.Flags, cutils.RollupRelayerFlags...)
|
||||
app.Commands = []*cli.Command{}
|
||||
app.Before = func(ctx *cli.Context) error {
|
||||
return cutils.LogSetup(ctx)
|
||||
@@ -71,20 +70,13 @@ func action(ctx *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
initGenesis := ctx.Bool(cutils.ImportGenesisFlag.Name)
|
||||
l2relayer, err := relayer.NewLayer2Relayer(ctx.Context, l2client, db, cfg.L2Config.RelayerConfig, initGenesis)
|
||||
l2relayer, err := relayer.NewLayer2Relayer(ctx.Context, l2client, db, cfg.L2Config.RelayerConfig)
|
||||
if err != nil {
|
||||
log.Error("failed to create l2 relayer", "config file", cfgFile, "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
chunkProposer := watcher.NewChunkProposer(subCtx, cfg.L2Config.ChunkProposerConfig, db)
|
||||
if err != nil {
|
||||
log.Error("failed to create chunkProposer", "config file", cfgFile, "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
batchProposer := watcher.NewBatchProposer(subCtx, cfg.L2Config.BatchProposerConfig, db)
|
||||
batchProposer := watcher.NewBatchProposer(subCtx, cfg.L2Config.BatchProposerConfig, l2relayer, db)
|
||||
if err != nil {
|
||||
log.Error("failed to create batchProposer", "config file", cfgFile, "error", err)
|
||||
return err
|
||||
@@ -99,14 +91,14 @@ func action(ctx *cli.Context) error {
|
||||
log.Error("failed to get block number", "err", loopErr)
|
||||
return
|
||||
}
|
||||
l2watcher.TryFetchRunningMissingBlocks(number)
|
||||
l2watcher.TryFetchRunningMissingBlocks(ctx, number)
|
||||
})
|
||||
|
||||
go cutils.Loop(subCtx, 2*time.Second, chunkProposer.TryProposeChunk)
|
||||
|
||||
go cutils.Loop(subCtx, 2*time.Second, batchProposer.TryProposeBatch)
|
||||
|
||||
go cutils.Loop(subCtx, 2*time.Second, l2relayer.ProcessPendingBatches)
|
||||
// Batch proposer loop
|
||||
go cutils.Loop(subCtx, 2*time.Second, func() {
|
||||
batchProposer.TryProposeBatch()
|
||||
batchProposer.TryCommitBatches()
|
||||
})
|
||||
|
||||
go cutils.Loop(subCtx, 2*time.Second, l2relayer.ProcessCommittedBatches)
|
||||
|
||||
|
||||
@@ -72,20 +72,18 @@
|
||||
"1414141414141414141414141414141414141414141414141414141414141414"
|
||||
]
|
||||
},
|
||||
"chunk_proposer_config": {
|
||||
"max_tx_gas_per_chunk": 1123456,
|
||||
"max_l2_tx_num_per_chunk": 1123,
|
||||
"max_l1_commit_gas_per_chunk": 11234567,
|
||||
"max_l1_commit_calldata_size_per_chunk": 112345,
|
||||
"min_l1_commit_calldata_size_per_chunk": 11234,
|
||||
"chunk_timeout_sec": 300
|
||||
},
|
||||
"batch_proposer_config": {
|
||||
"max_chunk_num_per_batch": 112,
|
||||
"max_l1_commit_gas_per_batch": 11234567,
|
||||
"max_l1_commit_calldata_size_per_batch": 112345,
|
||||
"min_chunk_num_per_batch": 11,
|
||||
"batch_timeout_sec": 300
|
||||
"proof_generation_freq": 1,
|
||||
"batch_gas_threshold": 3000000,
|
||||
"batch_tx_num_threshold": 44,
|
||||
"batch_time_sec": 300,
|
||||
"batch_commit_time_sec": 1200,
|
||||
"batch_blocks_limit": 100,
|
||||
"commit_tx_calldata_size_limit": 200000,
|
||||
"public_input_config": {
|
||||
"max_tx_num": 44,
|
||||
"padding_tx_hash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
"db_config": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module scroll-tech/bridge
|
||||
|
||||
go 1.19
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/agiledragon/gomonkey/v2 v2.9.0
|
||||
@@ -9,13 +9,14 @@ require (
|
||||
github.com/orcaman/concurrent-map v1.0.0
|
||||
github.com/orcaman/concurrent-map/v2 v2.0.1
|
||||
github.com/pressly/goose/v3 v3.7.0
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20230613025759-f055f50f9d56
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20230508165858-27a3830afa61
|
||||
github.com/smartystreets/goconvey v1.8.0
|
||||
github.com/stretchr/testify v1.8.2
|
||||
github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa
|
||||
golang.org/x/sync v0.1.0
|
||||
gorm.io/driver/postgres v1.5.0
|
||||
gorm.io/gorm v1.25.1
|
||||
modernc.org/mathutil v1.4.1
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -47,6 +48,7 @@ require (
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.14 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||
github.com/rivo/uniseg v0.4.4 // indirect
|
||||
github.com/rjeczalik/notify v0.9.1 // indirect
|
||||
github.com/rogpeppe/go-internal v1.10.0 // indirect
|
||||
@@ -60,9 +62,9 @@ require (
|
||||
github.com/tyler-smith/go-bip39 v1.1.0 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
golang.org/x/crypto v0.10.0 // indirect
|
||||
golang.org/x/sys v0.9.0 // indirect
|
||||
golang.org/x/text v0.10.0 // indirect
|
||||
golang.org/x/crypto v0.9.0 // indirect
|
||||
golang.org/x/sys v0.8.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.8.0 // indirect
|
||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
|
||||
|
||||
@@ -117,6 +117,7 @@ github.com/pressly/goose/v3 v3.7.0 h1:jblaZul15uCIEKHRu5KUdA+5wDA7E60JC0TOthdrtf
|
||||
github.com/pressly/goose/v3 v3.7.0/go.mod h1:N5gqPdIzdxf3BiPWdmoPreIwHStkxsvKWE5xjUvfYNk=
|
||||
github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
||||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
@@ -129,8 +130,8 @@ github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncj
|
||||
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20230613025759-f055f50f9d56 h1:Cqj7haxwvzI2O4n9ZZ25helShzFGCy7Z/B+FFSBFHNI=
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20230613025759-f055f50f9d56/go.mod h1:45PZqlQCqV0dU4o4+SE8LoJLEvXkK5j45ligvbih9QY=
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20230508165858-27a3830afa61 h1:4fslVpNOPLeJPYX3tivrVWgqNvChPs7/y9OqWvQSNCw=
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20230508165858-27a3830afa61/go.mod h1:45PZqlQCqV0dU4o4+SE8LoJLEvXkK5j45ligvbih9QY=
|
||||
github.com/scroll-tech/zktrie v0.5.3 h1:jjzQchGU6XPL5s1C5bwwivSadefSRuYASE9OL7UKAdE=
|
||||
github.com/scroll-tech/zktrie v0.5.3/go.mod h1:XvNo7vAk8yxNyTjBDj5WIiFzYW4bx/gJ78+NK6Zn6Uk=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
|
||||
@@ -172,8 +173,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
|
||||
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
||||
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
|
||||
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -201,8 +202,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
|
||||
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
@@ -210,8 +211,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
|
||||
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -245,6 +246,7 @@ modernc.org/cc/v3 v3.36.1 h1:CICrjwr/1M4+6OQ4HJZ/AHxjcwe67r5vPUF518MkO8A=
|
||||
modernc.org/ccgo/v3 v3.16.8 h1:G0QNlTqI5uVgczBWfGKs7B++EPwCfXPWGD2MdeKloDs=
|
||||
modernc.org/libc v1.16.19 h1:S8flPn5ZeXx6iw/8yNa986hwTQDrY8RXU7tObZuAozo=
|
||||
modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
|
||||
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.1.1 h1:bDOL0DIDLQv7bWhP3gMvIrnoFw+Eo6F7a2QK9HPDiFU=
|
||||
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||
modernc.org/sqlite v1.18.1 h1:ko32eKt3jf7eqIkCgPAeHMBXw3riNSLhl2f3loEF7o8=
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// DBConfig db config
|
||||
type DBConfig struct {
|
||||
// data source name
|
||||
@@ -9,3 +15,19 @@ type DBConfig struct {
|
||||
MaxOpenNum int `json:"maxOpenNum"`
|
||||
MaxIdleNum int `json:"maxIdleNum"`
|
||||
}
|
||||
|
||||
// NewDBConfig returns a new instance of Config.
|
||||
func NewDBConfig(file string) (*DBConfig, error) {
|
||||
buf, err := os.ReadFile(filepath.Clean(file))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg := &DBConfig{}
|
||||
err = json.Unmarshal(buf, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"github.com/scroll-tech/go-ethereum/rpc"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
|
||||
"scroll-tech/bridge/internal/types"
|
||||
)
|
||||
|
||||
// L2Config loads l2geth configuration items.
|
||||
@@ -20,27 +22,28 @@ type L2Config struct {
|
||||
WithdrawTrieRootSlot common.Hash `json:"withdraw_trie_root_slot,omitempty"`
|
||||
// The relayer config
|
||||
RelayerConfig *RelayerConfig `json:"relayer_config"`
|
||||
// The chunk_proposer config
|
||||
ChunkProposerConfig *ChunkProposerConfig `json:"chunk_proposer_config"`
|
||||
// The batch_proposer config
|
||||
BatchProposerConfig *BatchProposerConfig `json:"batch_proposer_config"`
|
||||
}
|
||||
|
||||
// ChunkProposerConfig loads chunk_proposer configuration items.
|
||||
type ChunkProposerConfig struct {
|
||||
MaxTxGasPerChunk uint64 `json:"max_tx_gas_per_chunk"`
|
||||
MaxL2TxNumPerChunk uint64 `json:"max_l2_tx_num_per_chunk"`
|
||||
MaxL1CommitGasPerChunk uint64 `json:"max_l1_commit_gas_per_chunk"`
|
||||
MaxL1CommitCalldataSizePerChunk uint64 `json:"max_l1_commit_calldata_size_per_chunk"`
|
||||
MinL1CommitCalldataSizePerChunk uint64 `json:"min_l1_commit_calldata_size_per_chunk"`
|
||||
ChunkTimeoutSec uint64 `json:"chunk_timeout_sec"`
|
||||
}
|
||||
|
||||
// BatchProposerConfig loads batch_proposer configuration items.
|
||||
// BatchProposerConfig loads l2watcher batch_proposer configuration items.
|
||||
type BatchProposerConfig struct {
|
||||
MaxChunkNumPerBatch uint64 `json:"max_chunk_num_per_batch"`
|
||||
MaxL1CommitGasPerBatch uint64 `json:"max_l1_commit_gas_per_batch"`
|
||||
MaxL1CommitCalldataSizePerBatch uint64 `json:"max_l1_commit_calldata_size_per_batch"`
|
||||
MinChunkNumPerBatch uint64 `json:"min_chunk_num_per_batch"`
|
||||
BatchTimeoutSec uint64 `json:"batch_timeout_sec"`
|
||||
// Proof generation frequency, generating proof every k blocks
|
||||
ProofGenerationFreq uint64 `json:"proof_generation_freq"`
|
||||
// Txnum threshold in a batch
|
||||
BatchTxNumThreshold uint64 `json:"batch_tx_num_threshold"`
|
||||
// Gas threshold in a batch
|
||||
BatchGasThreshold uint64 `json:"batch_gas_threshold"`
|
||||
// Time waited to generate a batch even if gas_threshold not met
|
||||
BatchTimeSec uint64 `json:"batch_time_sec"`
|
||||
// Time waited to commit batches before the calldata met CommitTxCalldataSizeLimit
|
||||
BatchCommitTimeSec uint64 `json:"batch_commit_time_sec"`
|
||||
// Max number of blocks in a batch
|
||||
BatchBlocksLimit uint64 `json:"batch_blocks_limit"`
|
||||
// Commit tx calldata size limit in bytes, target to cap the gas use of commit tx at 2M gas
|
||||
CommitTxCalldataSizeLimit uint64 `json:"commit_tx_calldata_size_limit"`
|
||||
// Commit tx calldata min size limit in bytes
|
||||
CommitTxCalldataMinSize uint64 `json:"commit_tx_calldata_min_size,omitempty"`
|
||||
// The public input hash config
|
||||
PublicInputConfig *types.PublicInputHashConfig `json:"public_input_config"`
|
||||
}
|
||||
|
||||
@@ -3,13 +3,12 @@ package relayer
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/accounts/abi"
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
"github.com/scroll-tech/go-ethereum/crypto"
|
||||
"github.com/scroll-tech/go-ethereum/ethclient"
|
||||
"github.com/scroll-tech/go-ethereum/log"
|
||||
gethMetrics "github.com/scroll-tech/go-ethereum/metrics"
|
||||
@@ -23,11 +22,14 @@ import (
|
||||
"scroll-tech/bridge/internal/controller/sender"
|
||||
"scroll-tech/bridge/internal/orm"
|
||||
bridgeTypes "scroll-tech/bridge/internal/types"
|
||||
"scroll-tech/bridge/internal/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
bridgeL2MsgsRelayedTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/msgs/relayed/total", metrics.ScrollRegistry)
|
||||
bridgeL2BatchesFinalizedTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/batches/finalized/total", metrics.ScrollRegistry)
|
||||
bridgeL2BatchesCommittedTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/batches/committed/total", metrics.ScrollRegistry)
|
||||
bridgeL2MsgsRelayedConfirmedTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/msgs/relayed/confirmed/total", metrics.ScrollRegistry)
|
||||
bridgeL2BatchesFinalizedConfirmedTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/batches/finalized/confirmed/total", metrics.ScrollRegistry)
|
||||
bridgeL2BatchesCommittedConfirmedTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/batches/committed/confirmed/total", metrics.ScrollRegistry)
|
||||
bridgeL2BatchesSkippedTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/batches/skipped/total", metrics.ScrollRegistry)
|
||||
@@ -44,10 +46,9 @@ type Layer2Relayer struct {
|
||||
|
||||
l2Client *ethclient.Client
|
||||
|
||||
db *gorm.DB
|
||||
batchOrm *orm.Batch
|
||||
chunkOrm *orm.Chunk
|
||||
l2BlockOrm *orm.L2Block
|
||||
blockBatchOrm *orm.BlockBatch
|
||||
blockTraceOrm *orm.BlockTrace
|
||||
l2MessageOrm *orm.L2Message
|
||||
|
||||
cfg *config.RelayerConfig
|
||||
|
||||
@@ -71,8 +72,8 @@ type Layer2Relayer struct {
|
||||
processingMessage sync.Map
|
||||
|
||||
// A list of processing batches commitment.
|
||||
// key(string): confirmation ID, value(string): batch hash.
|
||||
processingCommitment sync.Map
|
||||
// key(string): confirmation ID, value([]string): batch hashes.
|
||||
processingBatchesCommitment sync.Map
|
||||
|
||||
// A list of processing batch finalization.
|
||||
// key(string): confirmation ID, value(string): batch hash.
|
||||
@@ -80,7 +81,7 @@ type Layer2Relayer struct {
|
||||
}
|
||||
|
||||
// NewLayer2Relayer will return a new instance of Layer2RelayerClient
|
||||
func NewLayer2Relayer(ctx context.Context, l2Client *ethclient.Client, db *gorm.DB, cfg *config.RelayerConfig, initGenesis bool) (*Layer2Relayer, error) {
|
||||
func NewLayer2Relayer(ctx context.Context, l2Client *ethclient.Client, db *gorm.DB, cfg *config.RelayerConfig) (*Layer2Relayer, error) {
|
||||
// @todo use different sender for relayer, block commit and proof finalize
|
||||
messageSender, err := sender.NewSender(ctx, cfg.SenderConfig, cfg.MessageSenderPrivateKeys)
|
||||
if err != nil {
|
||||
@@ -117,11 +118,10 @@ func NewLayer2Relayer(ctx context.Context, l2Client *ethclient.Client, db *gorm.
|
||||
|
||||
layer2Relayer := &Layer2Relayer{
|
||||
ctx: ctx,
|
||||
db: db,
|
||||
|
||||
batchOrm: orm.NewBatch(db),
|
||||
l2BlockOrm: orm.NewL2Block(db),
|
||||
chunkOrm: orm.NewChunk(db),
|
||||
blockBatchOrm: orm.NewBlockBatch(db),
|
||||
l2MessageOrm: orm.NewL2Message(db),
|
||||
blockTraceOrm: orm.NewBlockTrace(db),
|
||||
|
||||
l2Client: l2Client,
|
||||
|
||||
@@ -139,135 +139,20 @@ func NewLayer2Relayer(ctx context.Context, l2Client *ethclient.Client, db *gorm.
|
||||
minGasPrice: minGasPrice,
|
||||
gasPriceDiff: gasPriceDiff,
|
||||
|
||||
cfg: cfg,
|
||||
processingMessage: sync.Map{},
|
||||
processingCommitment: sync.Map{},
|
||||
processingFinalization: sync.Map{},
|
||||
cfg: cfg,
|
||||
processingMessage: sync.Map{},
|
||||
processingBatchesCommitment: sync.Map{},
|
||||
processingFinalization: sync.Map{},
|
||||
}
|
||||
|
||||
// Initialize genesis before we do anything else
|
||||
if initGenesis {
|
||||
if err := layer2Relayer.initializeGenesis(); err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize and commit genesis batch, err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
go layer2Relayer.handleConfirmLoop(ctx)
|
||||
return layer2Relayer, nil
|
||||
}
|
||||
|
||||
func (r *Layer2Relayer) initializeGenesis() error {
|
||||
if count, err := r.batchOrm.GetBatchCount(r.ctx); err != nil {
|
||||
return fmt.Errorf("failed to get batch count: %v", err)
|
||||
} else if count > 0 {
|
||||
log.Info("genesis already imported", "batch count", count)
|
||||
return nil
|
||||
}
|
||||
|
||||
genesis, err := r.l2Client.HeaderByNumber(r.ctx, big.NewInt(0))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to retrieve L2 genesis header: %v", err)
|
||||
}
|
||||
|
||||
log.Info("retrieved L2 genesis header", "hash", genesis.Hash().String())
|
||||
|
||||
chunk := &bridgeTypes.Chunk{
|
||||
Blocks: []*bridgeTypes.WrappedBlock{{
|
||||
Header: genesis,
|
||||
Transactions: nil,
|
||||
WithdrawTrieRoot: common.Hash{},
|
||||
}},
|
||||
}
|
||||
|
||||
err = r.db.Transaction(func(dbTX *gorm.DB) error {
|
||||
var dbChunk *orm.Chunk
|
||||
dbChunk, err = r.chunkOrm.InsertChunk(r.ctx, chunk, dbTX)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to insert chunk: %v", err)
|
||||
}
|
||||
|
||||
if err = r.chunkOrm.UpdateProvingStatus(r.ctx, dbChunk.Hash, types.ProvingTaskVerified, dbTX); err != nil {
|
||||
return fmt.Errorf("failed to update genesis chunk proving status: %v", err)
|
||||
}
|
||||
|
||||
var batch *orm.Batch
|
||||
batch, err = r.batchOrm.InsertBatch(r.ctx, 0, 0, dbChunk.Hash, dbChunk.Hash, []*bridgeTypes.Chunk{chunk}, dbTX)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to insert batch: %v", err)
|
||||
}
|
||||
|
||||
if err = r.chunkOrm.UpdateBatchHashInRange(r.ctx, 0, 0, batch.Hash, dbTX); err != nil {
|
||||
return fmt.Errorf("failed to update batch hash for chunks: %v", err)
|
||||
}
|
||||
|
||||
if err = r.batchOrm.UpdateProvingStatus(r.ctx, batch.Hash, types.ProvingTaskVerified, dbTX); err != nil {
|
||||
return fmt.Errorf("failed to update genesis batch proving status: %v", err)
|
||||
}
|
||||
|
||||
if err = r.batchOrm.UpdateRollupStatus(r.ctx, batch.Hash, types.RollupFinalized, dbTX); err != nil {
|
||||
return fmt.Errorf("failed to update genesis batch rollup status: %v", err)
|
||||
}
|
||||
|
||||
// commit genesis batch on L1
|
||||
// note: we do this inside the DB transaction so that we can revert all DB changes if this step fails
|
||||
return r.commitGenesisBatch(batch.Hash, batch.BatchHeader, common.HexToHash(batch.StateRoot))
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("update genesis transaction failed: %v", err)
|
||||
}
|
||||
|
||||
log.Info("successfully imported genesis chunk and batch")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Layer2Relayer) commitGenesisBatch(batchHash string, batchHeader []byte, stateRoot common.Hash) error {
|
||||
// encode "importGenesisBatch" transaction calldata
|
||||
calldata, err := r.l1RollupABI.Pack("importGenesisBatch", batchHeader, stateRoot)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to pack importGenesisBatch with batch header: %v and state root: %v. error: %v", common.Bytes2Hex(batchHeader), stateRoot, err)
|
||||
}
|
||||
|
||||
// submit genesis batch to L1 rollup contract
|
||||
txHash, err := r.rollupSender.SendTransaction(batchHash, &r.cfg.RollupContractAddress, big.NewInt(0), calldata, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to send import genesis batch tx to L1, error: %v", err)
|
||||
}
|
||||
log.Info("importGenesisBatch transaction sent", "contract", r.cfg.RollupContractAddress, "txHash", txHash.String(), "batchHash", batchHash)
|
||||
|
||||
// wait for confirmation
|
||||
// we assume that no other transactions are sent before initializeGenesis completes
|
||||
ticker := time.NewTicker(10 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
// print progress
|
||||
case <-ticker.C:
|
||||
log.Info("Waiting for confirmation", "pending count", r.rollupSender.PendingCount())
|
||||
|
||||
// timeout
|
||||
case <-time.After(5 * time.Minute):
|
||||
return fmt.Errorf("import genesis timeout after 5 minutes, original txHash: %v", txHash.String())
|
||||
|
||||
// handle confirmation
|
||||
case confirmation := <-r.rollupSender.ConfirmChan():
|
||||
if confirmation.ID != batchHash {
|
||||
return fmt.Errorf("unexpected import genesis confirmation id, expected: %v, got: %v", batchHash, confirmation.ID)
|
||||
}
|
||||
if !confirmation.IsSuccessful {
|
||||
return fmt.Errorf("import genesis batch tx failed")
|
||||
}
|
||||
log.Info("Successfully committed genesis batch on L1", "txHash", confirmation.TxHash.String())
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
const processMsgLimit = 100
|
||||
|
||||
// ProcessGasPriceOracle imports gas price to layer1
|
||||
func (r *Layer2Relayer) ProcessGasPriceOracle() {
|
||||
batch, err := r.batchOrm.GetLatestBatch(r.ctx)
|
||||
batch, err := r.blockBatchOrm.GetLatestBatch()
|
||||
if err != nil {
|
||||
log.Error("Failed to GetLatestBatch", "err", err)
|
||||
return
|
||||
@@ -298,7 +183,7 @@ func (r *Layer2Relayer) ProcessGasPriceOracle() {
|
||||
return
|
||||
}
|
||||
|
||||
err = r.batchOrm.UpdateL2GasOracleStatusAndOracleTxHash(r.ctx, batch.Hash, types.GasOracleImporting, hash.String())
|
||||
err = r.blockBatchOrm.UpdateL2GasOracleStatusAndOracleTxHash(r.ctx, batch.Hash, types.GasOracleImporting, hash.String())
|
||||
if err != nil {
|
||||
log.Error("UpdateGasOracleStatusAndOracleTxHash failed", "batch.Hash", batch.Hash, "err", err)
|
||||
return
|
||||
@@ -309,119 +194,93 @@ func (r *Layer2Relayer) ProcessGasPriceOracle() {
|
||||
}
|
||||
}
|
||||
|
||||
// ProcessPendingBatches processes the pending batches by sending commitBatch transactions to layer 1.
|
||||
func (r *Layer2Relayer) ProcessPendingBatches() {
|
||||
// get pending batches from database in ascending order by their index.
|
||||
pendingBatches, err := r.batchOrm.GetPendingBatches(r.ctx, 10)
|
||||
// SendCommitTx sends commitBatches tx to L1.
|
||||
func (r *Layer2Relayer) SendCommitTx(batchData []*bridgeTypes.BatchData) error {
|
||||
if len(batchData) == 0 {
|
||||
log.Error("SendCommitTx receives empty batch")
|
||||
return nil
|
||||
}
|
||||
|
||||
// pack calldata
|
||||
commitBatches := make([]bridgeAbi.IScrollChainBatch, len(batchData))
|
||||
for i, batch := range batchData {
|
||||
commitBatches[i] = batch.Batch
|
||||
}
|
||||
calldata, err := r.l1RollupABI.Pack("commitBatches", commitBatches)
|
||||
if err != nil {
|
||||
log.Error("Failed to fetch pending L2 batches", "err", err)
|
||||
return
|
||||
log.Error("Failed to pack commitBatches",
|
||||
"error", err,
|
||||
"start_batch_index", commitBatches[0].BatchIndex,
|
||||
"end_batch_index", commitBatches[len(commitBatches)-1].BatchIndex)
|
||||
return err
|
||||
}
|
||||
for _, batch := range pendingBatches {
|
||||
// get current header and parent header.
|
||||
currentBatchHeader, err := bridgeTypes.DecodeBatchHeader(batch.BatchHeader)
|
||||
if err != nil {
|
||||
log.Error("Failed to decode batch header", "index", batch.Index, "error", err)
|
||||
return
|
||||
}
|
||||
parentBatch := &orm.Batch{}
|
||||
if batch.Index > 0 {
|
||||
parentBatch, err = r.batchOrm.GetBatchByIndex(r.ctx, batch.Index-1)
|
||||
if err != nil {
|
||||
log.Error("Failed to get parent batch header", "index", batch.Index-1, "error", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// get the chunks for the batch
|
||||
startChunkIndex := batch.StartChunkIndex
|
||||
endChunkIndex := batch.EndChunkIndex
|
||||
dbChunks, err := r.chunkOrm.GetChunksInRange(r.ctx, startChunkIndex, endChunkIndex)
|
||||
if err != nil {
|
||||
log.Error("Failed to fetch chunks",
|
||||
"start index", startChunkIndex,
|
||||
"end index", endChunkIndex, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
encodedChunks := make([][]byte, len(dbChunks))
|
||||
for i, c := range dbChunks {
|
||||
var wrappedBlocks []*bridgeTypes.WrappedBlock
|
||||
wrappedBlocks, err = r.l2BlockOrm.GetL2BlocksInRange(r.ctx, c.StartBlockNumber, c.EndBlockNumber)
|
||||
if err != nil {
|
||||
log.Error("Failed to fetch wrapped blocks",
|
||||
"start number", c.StartBlockNumber,
|
||||
"end number", c.EndBlockNumber, "error", err)
|
||||
return
|
||||
}
|
||||
chunk := &bridgeTypes.Chunk{
|
||||
Blocks: wrappedBlocks,
|
||||
}
|
||||
var chunkBytes []byte
|
||||
chunkBytes, err = chunk.Encode(c.TotalL1MessagesPoppedBefore)
|
||||
if err != nil {
|
||||
log.Error("Failed to encode chunk", "error", err)
|
||||
return
|
||||
}
|
||||
encodedChunks[i] = chunkBytes
|
||||
}
|
||||
|
||||
calldata, err := r.l1RollupABI.Pack("commitBatch", currentBatchHeader.Version(), parentBatch.BatchHeader, encodedChunks, currentBatchHeader.SkippedL1MessageBitmap())
|
||||
if err != nil {
|
||||
log.Error("Failed to pack commitBatch", "index", batch.Index, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
// send transaction
|
||||
txID := batch.Hash + "-commit"
|
||||
txHash, err := r.rollupSender.SendTransaction(txID, &r.cfg.RollupContractAddress, big.NewInt(0), calldata, 0)
|
||||
if err != nil {
|
||||
if !errors.Is(err, sender.ErrNoAvailableAccount) && !errors.Is(err, sender.ErrFullPending) {
|
||||
log.Error("Failed to send commitBatch tx to layer1 ", "err", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
err = r.batchOrm.UpdateCommitTxHashAndRollupStatus(r.ctx, batch.Hash, txHash.String(), types.RollupCommitting)
|
||||
if err != nil {
|
||||
log.Error("UpdateCommitTxHashAndRollupStatus failed", "hash", batch.Hash, "index", batch.Index, "err", err)
|
||||
return
|
||||
}
|
||||
bridgeL2BatchesCommittedTotalCounter.Inc(1)
|
||||
r.processingCommitment.Store(txID, batch.Hash)
|
||||
log.Info("Sent the commitBatch tx to layer1", "batch index", batch.Index, "batch hash", batch.Hash, "tx hash", txHash.Hex())
|
||||
// generate a unique txID and send transaction
|
||||
var bytes []byte
|
||||
for _, batch := range batchData {
|
||||
bytes = append(bytes, batch.Hash().Bytes()...)
|
||||
}
|
||||
txID := crypto.Keccak256Hash(bytes).String()
|
||||
txHash, err := r.rollupSender.SendTransaction(txID, &r.cfg.RollupContractAddress, big.NewInt(0), calldata, 0)
|
||||
if err != nil {
|
||||
if !errors.Is(err, sender.ErrNoAvailableAccount) && !errors.Is(err, sender.ErrFullPending) {
|
||||
log.Error("Failed to send commitBatches tx to layer1 ", "err", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
bridgeL2BatchesCommittedTotalCounter.Inc(int64(len(commitBatches)))
|
||||
log.Info("Sent the commitBatches tx to layer1",
|
||||
"tx_hash", txHash.Hex(),
|
||||
"start_batch_index", commitBatches[0].BatchIndex,
|
||||
"end_batch_index", commitBatches[len(commitBatches)-1].BatchIndex)
|
||||
|
||||
// record and sync with db, @todo handle db error
|
||||
batchHashes := make([]string, len(batchData))
|
||||
for i, batch := range batchData {
|
||||
batchHashes[i] = batch.Hash().Hex()
|
||||
err = r.blockBatchOrm.UpdateCommitTxHashAndRollupStatus(r.ctx, batchHashes[i], txHash.String(), types.RollupCommitting)
|
||||
if err != nil {
|
||||
log.Error("UpdateCommitTxHashAndRollupStatus failed", "hash", batchHashes[i], "index", batch.Batch.BatchIndex, "err", err)
|
||||
}
|
||||
}
|
||||
r.processingBatchesCommitment.Store(txID, batchHashes)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ProcessCommittedBatches submit proof to layer 1 rollup contract
|
||||
func (r *Layer2Relayer) ProcessCommittedBatches() {
|
||||
// set skipped batches in a single db operation
|
||||
if count, err := r.batchOrm.UpdateSkippedBatches(r.ctx); err != nil {
|
||||
if count, err := r.blockBatchOrm.UpdateSkippedBatches(); err != nil {
|
||||
log.Error("UpdateSkippedBatches failed", "err", err)
|
||||
// continue anyway
|
||||
} else if count > 0 {
|
||||
bridgeL2BatchesSkippedTotalCounter.Inc(int64(count))
|
||||
bridgeL2BatchesSkippedTotalCounter.Inc(count)
|
||||
log.Info("Skipping batches", "count", count)
|
||||
}
|
||||
|
||||
// retrieves the earliest batch whose rollup status is 'committed'
|
||||
fields := map[string]interface{}{
|
||||
"rollup_status": types.RollupCommitted,
|
||||
}
|
||||
orderByList := []string{"index ASC"}
|
||||
limit := 1
|
||||
batches, err := r.batchOrm.GetBatches(r.ctx, fields, orderByList, limit)
|
||||
// batches are sorted by batch index in increasing order
|
||||
batchHashes, err := r.blockBatchOrm.GetBlockBatchesHashByRollupStatus(types.RollupCommitted, 1)
|
||||
if err != nil {
|
||||
log.Error("Failed to fetch committed L2 batches", "err", err)
|
||||
return
|
||||
}
|
||||
if len(batches) != 1 {
|
||||
log.Warn("Unexpected result for GetBlockBatches", "number of batches", len(batches))
|
||||
if len(batchHashes) == 0 {
|
||||
return
|
||||
}
|
||||
hash := batchHashes[0]
|
||||
// @todo add support to relay multiple batches
|
||||
|
||||
batches, err := r.blockBatchOrm.GetBlockBatches(map[string]interface{}{"hash": hash}, nil, 1)
|
||||
if err != nil {
|
||||
log.Error("Failed to fetch committed L2 batch", "hash", hash, "err", err)
|
||||
return
|
||||
}
|
||||
if len(batches) == 0 {
|
||||
log.Error("Unexpected result for GetBlockBatches", "hash", hash, "len", 0)
|
||||
return
|
||||
}
|
||||
|
||||
batch := batches[0]
|
||||
hash := batch.Hash
|
||||
status := types.ProvingStatus(batch.ProvingStatus)
|
||||
switch status {
|
||||
case types.ProvingTaskUnassigned, types.ProvingTaskAssigned:
|
||||
@@ -433,54 +292,75 @@ func (r *Layer2Relayer) ProcessCommittedBatches() {
|
||||
return
|
||||
case types.ProvingTaskFailed, types.ProvingTaskSkipped:
|
||||
// note: this is covered by UpdateSkippedBatches, but we keep it for completeness's sake
|
||||
if err = r.batchOrm.UpdateRollupStatus(r.ctx, hash, types.RollupFinalizationSkipped); err != nil {
|
||||
if err = r.blockBatchOrm.UpdateRollupStatus(r.ctx, hash, types.RollupFinalizationSkipped); err != nil {
|
||||
log.Warn("UpdateRollupStatus failed", "hash", hash, "err", err)
|
||||
}
|
||||
case types.ProvingTaskVerified:
|
||||
log.Info("Start to roll up zk proof", "hash", hash)
|
||||
success := false
|
||||
|
||||
var parentBatchStateRoot string
|
||||
if batch.Index > 0 {
|
||||
var parentBatch *orm.Batch
|
||||
parentBatch, err = r.batchOrm.GetBatchByIndex(r.ctx, batch.Index-1)
|
||||
// handle unexpected db error
|
||||
if err != nil {
|
||||
log.Error("Failed to get batch", "index", batch.Index-1, "err", err)
|
||||
return
|
||||
rollupStatues := []types.RollupStatus{
|
||||
types.RollupFinalizing,
|
||||
types.RollupFinalized,
|
||||
}
|
||||
previousBatch, err := r.blockBatchOrm.GetLatestBatchByRollupStatus(rollupStatues)
|
||||
// skip submitting proof
|
||||
if err == nil && uint64(batch.CreatedAt.Sub(previousBatch.CreatedAt).Seconds()) < r.cfg.FinalizeBatchIntervalSec {
|
||||
log.Info(
|
||||
"Not enough time passed, skipping",
|
||||
"hash", hash,
|
||||
"createdAt", batch.CreatedAt,
|
||||
"lastFinalizingHash", previousBatch.Hash,
|
||||
"lastFinalizingStatus", previousBatch.RollupStatus,
|
||||
"lastFinalizingCreatedAt", previousBatch.CreatedAt,
|
||||
)
|
||||
|
||||
if err = r.blockBatchOrm.UpdateRollupStatus(r.ctx, hash, types.RollupFinalizationSkipped); err != nil {
|
||||
log.Warn("UpdateRollupStatus failed", "hash", hash, "err", err)
|
||||
} else {
|
||||
success = true
|
||||
}
|
||||
parentBatchStateRoot = parentBatch.StateRoot
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// handle unexpected db error
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
log.Error("Failed to get latest finalized batch", "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// TODO: need to revisit this and have a more fine-grained error handling
|
||||
if !success {
|
||||
log.Info("Failed to upload the proof, change rollup status to FinalizationSkipped", "hash", hash)
|
||||
if err = r.batchOrm.UpdateRollupStatus(r.ctx, hash, types.RollupFinalizationSkipped); err != nil {
|
||||
if err = r.blockBatchOrm.UpdateRollupStatus(r.ctx, hash, types.RollupFinalizationSkipped); err != nil {
|
||||
log.Warn("UpdateRollupStatus failed", "hash", hash, "err", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
aggProof, err := r.batchOrm.GetVerifiedProofByHash(r.ctx, hash)
|
||||
proofBuffer, icBuffer, err := r.blockBatchOrm.GetVerifiedProofAndInstanceCommitmentsByHash(hash)
|
||||
if err != nil {
|
||||
log.Warn("get verified proof by hash failed", "hash", hash, "err", err)
|
||||
log.Warn("fetch get proof by hash failed", "hash", hash, "err", err)
|
||||
return
|
||||
}
|
||||
if proofBuffer == nil || icBuffer == nil {
|
||||
log.Warn("proof or instance not ready", "hash", hash)
|
||||
return
|
||||
}
|
||||
if len(proofBuffer)%32 != 0 {
|
||||
log.Error("proof buffer has wrong length", "hash", hash, "length", len(proofBuffer))
|
||||
return
|
||||
}
|
||||
if len(icBuffer)%32 != 0 {
|
||||
log.Warn("instance buffer has wrong length", "hash", hash, "length", len(icBuffer))
|
||||
return
|
||||
}
|
||||
|
||||
if err = aggProof.SanityCheck(); err != nil {
|
||||
log.Warn("agg_proof sanity check fails", "hash", hash, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
data, err := r.l1RollupABI.Pack(
|
||||
"finalizeBatchWithProof",
|
||||
batch.BatchHeader,
|
||||
common.HexToHash(parentBatchStateRoot),
|
||||
common.HexToHash(batch.StateRoot),
|
||||
common.HexToHash(batch.WithdrawRoot),
|
||||
aggProof.Proof,
|
||||
)
|
||||
proof := utils.BufferToUint256Le(proofBuffer)
|
||||
instance := utils.BufferToUint256Le(icBuffer)
|
||||
data, err := r.l1RollupABI.Pack("finalizeBatchWithProof", common.HexToHash(hash), proof, instance)
|
||||
if err != nil {
|
||||
log.Error("Pack finalizeBatchWithProof failed", "err", err)
|
||||
return
|
||||
@@ -492,20 +372,17 @@ func (r *Layer2Relayer) ProcessCommittedBatches() {
|
||||
finalizeTxHash := &txHash
|
||||
if err != nil {
|
||||
if !errors.Is(err, sender.ErrNoAvailableAccount) && !errors.Is(err, sender.ErrFullPending) {
|
||||
log.Error("finalizeBatchWithProof in layer1 failed",
|
||||
"index", batch.Index, "hash", batch.Hash, "err", err)
|
||||
log.Error("finalizeBatchWithProof in layer1 failed", "hash", hash, "err", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
bridgeL2BatchesFinalizedTotalCounter.Inc(1)
|
||||
log.Info("finalizeBatchWithProof in layer1", "index", batch.Index, "batch hash", batch.Hash, "tx hash", hash)
|
||||
log.Info("finalizeBatchWithProof in layer1", "batch_hash", hash, "tx_hash", hash)
|
||||
|
||||
// record and sync with db, @todo handle db error
|
||||
err = r.batchOrm.UpdateFinalizeTxHashAndRollupStatus(r.ctx, hash, finalizeTxHash.String(), types.RollupFinalizing)
|
||||
err = r.blockBatchOrm.UpdateFinalizeTxHashAndRollupStatus(r.ctx, hash, finalizeTxHash.String(), types.RollupFinalizing)
|
||||
if err != nil {
|
||||
log.Warn("UpdateFinalizeTxHashAndRollupStatus failed",
|
||||
"index", batch.Index, "batch hash", batch.Hash,
|
||||
"tx hash", finalizeTxHash.String(), "err", err)
|
||||
log.Warn("UpdateFinalizeTxHashAndRollupStatus failed", "batch_hash", hash, "err", err)
|
||||
}
|
||||
success = true
|
||||
r.processingFinalization.Store(txID, hash)
|
||||
@@ -519,9 +396,29 @@ func (r *Layer2Relayer) ProcessCommittedBatches() {
|
||||
|
||||
func (r *Layer2Relayer) handleConfirmation(confirmation *sender.Confirmation) {
|
||||
transactionType := "Unknown"
|
||||
// check whether it is message relay transaction
|
||||
if msgHash, ok := r.processingMessage.Load(confirmation.ID); ok {
|
||||
transactionType = "MessageRelay"
|
||||
var status types.MsgStatus
|
||||
if confirmation.IsSuccessful {
|
||||
status = types.MsgConfirmed
|
||||
} else {
|
||||
status = types.MsgRelayFailed
|
||||
log.Warn("transaction confirmed but failed in layer1", "confirmation", confirmation)
|
||||
}
|
||||
// @todo handle db error
|
||||
err := r.l2MessageOrm.UpdateLayer2StatusAndLayer1Hash(r.ctx, msgHash.(string), status, confirmation.TxHash.String())
|
||||
if err != nil {
|
||||
log.Warn("UpdateLayer2StatusAndLayer1Hash failed", "msgHash", msgHash.(string), "err", err)
|
||||
}
|
||||
bridgeL2MsgsRelayedConfirmedTotalCounter.Inc(1)
|
||||
r.processingMessage.Delete(confirmation.ID)
|
||||
}
|
||||
|
||||
// check whether it is CommitBatches transaction
|
||||
if batchHash, ok := r.processingCommitment.Load(confirmation.ID); ok {
|
||||
if batchBatches, ok := r.processingBatchesCommitment.Load(confirmation.ID); ok {
|
||||
transactionType = "BatchesCommitment"
|
||||
batchHashes := batchBatches.([]string)
|
||||
var status types.RollupStatus
|
||||
if confirmation.IsSuccessful {
|
||||
status = types.RollupCommitted
|
||||
@@ -529,15 +426,15 @@ func (r *Layer2Relayer) handleConfirmation(confirmation *sender.Confirmation) {
|
||||
status = types.RollupCommitFailed
|
||||
log.Warn("transaction confirmed but failed in layer1", "confirmation", confirmation)
|
||||
}
|
||||
// @todo handle db error
|
||||
err := r.batchOrm.UpdateCommitTxHashAndRollupStatus(r.ctx, batchHash.(string), confirmation.TxHash.String(), status)
|
||||
if err != nil {
|
||||
log.Warn("UpdateCommitTxHashAndRollupStatus failed",
|
||||
"batch hash", batchHash.(string),
|
||||
"tx hash", confirmation.TxHash.String(), "err", err)
|
||||
for _, batchHash := range batchHashes {
|
||||
// @todo handle db error
|
||||
err := r.blockBatchOrm.UpdateCommitTxHashAndRollupStatus(r.ctx, batchHash, confirmation.TxHash.String(), status)
|
||||
if err != nil {
|
||||
log.Warn("UpdateCommitTxHashAndRollupStatus failed", "batch_hash", batchHash, "err", err)
|
||||
}
|
||||
}
|
||||
bridgeL2BatchesCommittedConfirmedTotalCounter.Inc(1)
|
||||
r.processingCommitment.Delete(confirmation.ID)
|
||||
bridgeL2BatchesCommittedConfirmedTotalCounter.Inc(int64(len(batchHashes)))
|
||||
r.processingBatchesCommitment.Delete(confirmation.ID)
|
||||
}
|
||||
|
||||
// check whether it is proof finalization transaction
|
||||
@@ -550,13 +447,10 @@ func (r *Layer2Relayer) handleConfirmation(confirmation *sender.Confirmation) {
|
||||
status = types.RollupFinalizeFailed
|
||||
log.Warn("transaction confirmed but failed in layer1", "confirmation", confirmation)
|
||||
}
|
||||
|
||||
// @todo handle db error
|
||||
err := r.batchOrm.UpdateFinalizeTxHashAndRollupStatus(r.ctx, batchHash.(string), confirmation.TxHash.String(), status)
|
||||
err := r.blockBatchOrm.UpdateFinalizeTxHashAndRollupStatus(r.ctx, batchHash.(string), confirmation.TxHash.String(), status)
|
||||
if err != nil {
|
||||
log.Warn("UpdateFinalizeTxHashAndRollupStatus failed",
|
||||
"batch hash", batchHash.(string),
|
||||
"tx hash", confirmation.TxHash.String(), "err", err)
|
||||
log.Warn("UpdateFinalizeTxHashAndRollupStatus failed", "batch_hash", batchHash.(string), "err", err)
|
||||
}
|
||||
bridgeL2BatchesFinalizedConfirmedTotalCounter.Inc(1)
|
||||
r.processingFinalization.Delete(confirmation.ID)
|
||||
@@ -576,14 +470,14 @@ func (r *Layer2Relayer) handleConfirmLoop(ctx context.Context) {
|
||||
case cfm := <-r.gasOracleSender.ConfirmChan():
|
||||
if !cfm.IsSuccessful {
|
||||
// @discuss: maybe make it pending again?
|
||||
err := r.batchOrm.UpdateL2GasOracleStatusAndOracleTxHash(r.ctx, cfm.ID, types.GasOracleFailed, cfm.TxHash.String())
|
||||
err := r.blockBatchOrm.UpdateL2GasOracleStatusAndOracleTxHash(r.ctx, cfm.ID, types.GasOracleFailed, cfm.TxHash.String())
|
||||
if err != nil {
|
||||
log.Warn("UpdateL2GasOracleStatusAndOracleTxHash failed", "err", err)
|
||||
}
|
||||
log.Warn("transaction confirmed but failed in layer1", "confirmation", cfm)
|
||||
} else {
|
||||
// @todo handle db error
|
||||
err := r.batchOrm.UpdateL2GasOracleStatusAndOracleTxHash(r.ctx, cfm.ID, types.GasOracleImported, cfm.TxHash.String())
|
||||
err := r.blockBatchOrm.UpdateL2GasOracleStatusAndOracleTxHash(r.ctx, cfm.ID, types.GasOracleImported, cfm.TxHash.String())
|
||||
if err != nil {
|
||||
log.Warn("UpdateL2GasOracleStatusAndOracleTxHash failed", "err", err)
|
||||
}
|
||||
|
||||
@@ -2,18 +2,22 @@ package relayer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/big"
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/agiledragon/gomonkey/v2"
|
||||
"github.com/scroll-tech/go-ethereum/accounts/abi"
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
gethTypes "github.com/scroll-tech/go-ethereum/core/types"
|
||||
"github.com/smartystreets/goconvey/convey"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"scroll-tech/common/types"
|
||||
"scroll-tech/common/types/message"
|
||||
"scroll-tech/common/utils"
|
||||
|
||||
"scroll-tech/bridge/internal/controller/sender"
|
||||
@@ -23,6 +27,20 @@ import (
|
||||
bridgeUtils "scroll-tech/bridge/internal/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
templateL2Message = []orm.L2Message{
|
||||
{
|
||||
Nonce: 1,
|
||||
Height: 1,
|
||||
Sender: "0x596a746661dbed76a84556111c2872249b070e15",
|
||||
Value: "100",
|
||||
Target: "0x2c73620b223808297ea734d946813f0dd78eb8f7",
|
||||
Calldata: "testdata",
|
||||
Layer2Hash: "hash0",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func setupL2RelayerDB(t *testing.T) *gorm.DB {
|
||||
db, err := bridgeUtils.InitDB(cfg.DBConfig)
|
||||
assert.NoError(t, err)
|
||||
@@ -35,37 +53,74 @@ func setupL2RelayerDB(t *testing.T) *gorm.DB {
|
||||
func testCreateNewRelayer(t *testing.T) {
|
||||
db := setupL2RelayerDB(t)
|
||||
defer bridgeUtils.CloseDB(db)
|
||||
relayer, err := NewLayer2Relayer(context.Background(), l2Cli, db, cfg.L2Config.RelayerConfig, false)
|
||||
relayer, err := NewLayer2Relayer(context.Background(), l2Cli, db, cfg.L2Config.RelayerConfig)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, relayer)
|
||||
}
|
||||
|
||||
func testL2RelayerProcessPendingBatches(t *testing.T) {
|
||||
func testL2RelayerProcessSaveEvents(t *testing.T) {
|
||||
db := setupL2RelayerDB(t)
|
||||
defer bridgeUtils.CloseDB(db)
|
||||
|
||||
l2Cfg := cfg.L2Config
|
||||
relayer, err := NewLayer2Relayer(context.Background(), l2Cli, db, l2Cfg.RelayerConfig, false)
|
||||
relayer, err := NewLayer2Relayer(context.Background(), l2Cli, db, l2Cfg.RelayerConfig)
|
||||
assert.NoError(t, err)
|
||||
|
||||
l2BlockOrm := orm.NewL2Block(db)
|
||||
err = l2BlockOrm.InsertL2Blocks(context.Background(), []*bridgeTypes.WrappedBlock{wrappedBlock1, wrappedBlock2})
|
||||
assert.NoError(t, err)
|
||||
chunkOrm := orm.NewChunk(db)
|
||||
dbChunk1, err := chunkOrm.InsertChunk(context.Background(), chunk1)
|
||||
assert.NoError(t, err)
|
||||
dbChunk2, err := chunkOrm.InsertChunk(context.Background(), chunk2)
|
||||
assert.NoError(t, err)
|
||||
batchOrm := orm.NewBatch(db)
|
||||
batch, err := batchOrm.InsertBatch(context.Background(), 0, 1, dbChunk1.Hash, dbChunk2.Hash, []*bridgeTypes.Chunk{chunk1, chunk2})
|
||||
l2MessageOrm := orm.NewL2Message(db)
|
||||
err = l2MessageOrm.SaveL2Messages(context.Background(), templateL2Message)
|
||||
assert.NoError(t, err)
|
||||
|
||||
relayer.ProcessPendingBatches()
|
||||
traces := []*bridgeTypes.WrappedBlock{
|
||||
{
|
||||
Header: &gethTypes.Header{
|
||||
Number: big.NewInt(int64(templateL2Message[0].Height)),
|
||||
},
|
||||
Transactions: nil,
|
||||
WithdrawTrieRoot: common.Hash{},
|
||||
},
|
||||
{
|
||||
Header: &gethTypes.Header{
|
||||
Number: big.NewInt(int64(templateL2Message[0].Height + 1)),
|
||||
},
|
||||
Transactions: nil,
|
||||
WithdrawTrieRoot: common.Hash{},
|
||||
},
|
||||
}
|
||||
|
||||
statuses, err := batchOrm.GetRollupStatusByHashList(context.Background(), []string{batch.Hash})
|
||||
blockTraceOrm := orm.NewBlockTrace(db)
|
||||
assert.NoError(t, blockTraceOrm.InsertWrappedBlocks(traces))
|
||||
blockBatchOrm := orm.NewBlockBatch(db)
|
||||
parentBatch1 := &bridgeTypes.BatchInfo{
|
||||
Index: 0,
|
||||
Hash: common.Hash{}.Hex(),
|
||||
StateRoot: common.Hash{}.Hex(),
|
||||
}
|
||||
batchData1 := bridgeTypes.NewBatchData(parentBatch1, []*bridgeTypes.WrappedBlock{wrappedBlock1}, nil)
|
||||
batchHash := batchData1.Hash().Hex()
|
||||
err = db.Transaction(func(tx *gorm.DB) error {
|
||||
rowsAffected, dbTxErr := blockBatchOrm.InsertBlockBatchByBatchData(tx, batchData1)
|
||||
if dbTxErr != nil {
|
||||
return dbTxErr
|
||||
}
|
||||
if rowsAffected != 1 {
|
||||
dbTxErr = errors.New("the InsertBlockBatchByBatchData affected row is not 1")
|
||||
return dbTxErr
|
||||
}
|
||||
dbTxErr = blockTraceOrm.UpdateBatchHashForL2Blocks(tx, []uint64{1}, batchHash)
|
||||
if dbTxErr != nil {
|
||||
return dbTxErr
|
||||
}
|
||||
return nil
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(statuses))
|
||||
assert.Equal(t, types.RollupCommitting, statuses[0])
|
||||
|
||||
err = blockBatchOrm.UpdateRollupStatus(context.Background(), batchHash, types.RollupFinalized)
|
||||
assert.NoError(t, err)
|
||||
|
||||
relayer.ProcessSavedEvents()
|
||||
|
||||
msg, err := l2MessageOrm.GetL2MessageByNonce(templateL2Message[0].Nonce)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, types.MsgSubmitted, types.MsgStatus(msg.Status))
|
||||
}
|
||||
|
||||
func testL2RelayerProcessCommittedBatches(t *testing.T) {
|
||||
@@ -73,36 +128,44 @@ func testL2RelayerProcessCommittedBatches(t *testing.T) {
|
||||
defer bridgeUtils.CloseDB(db)
|
||||
|
||||
l2Cfg := cfg.L2Config
|
||||
relayer, err := NewLayer2Relayer(context.Background(), l2Cli, db, l2Cfg.RelayerConfig, false)
|
||||
assert.NoError(t, err)
|
||||
batchOrm := orm.NewBatch(db)
|
||||
batch, err := batchOrm.InsertBatch(context.Background(), 0, 1, chunkHash1.Hex(), chunkHash2.Hex(), []*bridgeTypes.Chunk{chunk1, chunk2})
|
||||
relayer, err := NewLayer2Relayer(context.Background(), l2Cli, db, l2Cfg.RelayerConfig)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = batchOrm.UpdateRollupStatus(context.Background(), batch.Hash, types.RollupCommitted)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = batchOrm.UpdateProvingStatus(context.Background(), batch.Hash, types.ProvingTaskVerified)
|
||||
assert.NoError(t, err)
|
||||
|
||||
relayer.ProcessCommittedBatches()
|
||||
|
||||
statuses, err := batchOrm.GetRollupStatusByHashList(context.Background(), []string{batch.Hash})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(statuses))
|
||||
assert.Equal(t, types.RollupFinalizationSkipped, statuses[0])
|
||||
|
||||
err = batchOrm.UpdateRollupStatus(context.Background(), batch.Hash, types.RollupCommitted)
|
||||
assert.NoError(t, err)
|
||||
proof := &message.AggProof{
|
||||
Proof: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31},
|
||||
FinalPair: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31},
|
||||
parentBatch1 := &bridgeTypes.BatchInfo{
|
||||
Index: 0,
|
||||
Hash: common.Hash{}.Hex(),
|
||||
StateRoot: common.Hash{}.Hex(),
|
||||
}
|
||||
err = batchOrm.UpdateProofByHash(context.Background(), batch.Hash, proof, 100)
|
||||
|
||||
blockBatchOrm := orm.NewBlockBatch(db)
|
||||
batchData1 := bridgeTypes.NewBatchData(parentBatch1, []*bridgeTypes.WrappedBlock{wrappedBlock1}, nil)
|
||||
batchHash := batchData1.Hash().Hex()
|
||||
err = db.Transaction(func(tx *gorm.DB) error {
|
||||
rowsAffected, dbTxErr := blockBatchOrm.InsertBlockBatchByBatchData(tx, batchData1)
|
||||
if dbTxErr != nil {
|
||||
return dbTxErr
|
||||
}
|
||||
if rowsAffected != 1 {
|
||||
dbTxErr = errors.New("the InsertBlockBatchByBatchData affected row is not 1")
|
||||
return dbTxErr
|
||||
}
|
||||
return nil
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = blockBatchOrm.UpdateRollupStatus(context.Background(), batchHash, types.RollupCommitted)
|
||||
assert.NoError(t, err)
|
||||
|
||||
tProof := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}
|
||||
tInstanceCommitments := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}
|
||||
err = blockBatchOrm.UpdateProofByHash(context.Background(), batchHash, tProof, tInstanceCommitments, 100)
|
||||
assert.NoError(t, err)
|
||||
err = blockBatchOrm.UpdateProvingStatus(batchHash, types.ProvingTaskVerified)
|
||||
assert.NoError(t, err)
|
||||
|
||||
relayer.ProcessCommittedBatches()
|
||||
statuses, err = batchOrm.GetRollupStatusByHashList(context.Background(), []string{batch.Hash})
|
||||
|
||||
statuses, err := blockBatchOrm.GetRollupStatusByHashList([]string{batchHash})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(statuses))
|
||||
assert.Equal(t, types.RollupFinalizing, statuses[0])
|
||||
@@ -113,100 +176,172 @@ func testL2RelayerSkipBatches(t *testing.T) {
|
||||
defer bridgeUtils.CloseDB(db)
|
||||
|
||||
l2Cfg := cfg.L2Config
|
||||
relayer, err := NewLayer2Relayer(context.Background(), l2Cli, db, l2Cfg.RelayerConfig, false)
|
||||
relayer, err := NewLayer2Relayer(context.Background(), l2Cli, db, l2Cfg.RelayerConfig)
|
||||
assert.NoError(t, err)
|
||||
|
||||
batchOrm := orm.NewBatch(db)
|
||||
createBatch := func(rollupStatus types.RollupStatus, provingStatus types.ProvingStatus) string {
|
||||
batch, err := batchOrm.InsertBatch(context.Background(), 0, 1, chunkHash1.Hex(), chunkHash2.Hex(), []*bridgeTypes.Chunk{chunk1, chunk2})
|
||||
blockBatchOrm := orm.NewBlockBatch(db)
|
||||
createBatch := func(rollupStatus types.RollupStatus, provingStatus types.ProvingStatus, index uint64) string {
|
||||
batchData := genBatchData(t, index)
|
||||
err = db.Transaction(func(tx *gorm.DB) error {
|
||||
rowsAffected, dbTxErr := blockBatchOrm.InsertBlockBatchByBatchData(tx, batchData)
|
||||
if dbTxErr != nil {
|
||||
return dbTxErr
|
||||
}
|
||||
if rowsAffected != 1 {
|
||||
dbTxErr = errors.New("the InsertBlockBatchByBatchData affected row is not 1")
|
||||
return dbTxErr
|
||||
}
|
||||
return nil
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = batchOrm.UpdateRollupStatus(context.Background(), batch.Hash, rollupStatus)
|
||||
batchHash := batchData.Hash().Hex()
|
||||
err = blockBatchOrm.UpdateRollupStatus(context.Background(), batchHash, rollupStatus)
|
||||
assert.NoError(t, err)
|
||||
|
||||
proof := &message.AggProof{
|
||||
Proof: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31},
|
||||
FinalPair: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31},
|
||||
}
|
||||
err = batchOrm.UpdateProofByHash(context.Background(), batch.Hash, proof, 100)
|
||||
tProof := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}
|
||||
tInstanceCommitments := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}
|
||||
err = blockBatchOrm.UpdateProofByHash(context.Background(), batchHash, tProof, tInstanceCommitments, 100)
|
||||
assert.NoError(t, err)
|
||||
err = batchOrm.UpdateProvingStatus(context.Background(), batch.Hash, provingStatus)
|
||||
err = blockBatchOrm.UpdateProvingStatus(batchHash, provingStatus)
|
||||
assert.NoError(t, err)
|
||||
return batch.Hash
|
||||
return batchHash
|
||||
}
|
||||
|
||||
skipped := []string{
|
||||
createBatch(types.RollupCommitted, types.ProvingTaskSkipped),
|
||||
createBatch(types.RollupCommitted, types.ProvingTaskFailed),
|
||||
createBatch(types.RollupCommitted, types.ProvingTaskSkipped, 1),
|
||||
createBatch(types.RollupCommitted, types.ProvingTaskFailed, 2),
|
||||
}
|
||||
|
||||
notSkipped := []string{
|
||||
createBatch(types.RollupPending, types.ProvingTaskSkipped),
|
||||
createBatch(types.RollupCommitting, types.ProvingTaskSkipped),
|
||||
createBatch(types.RollupFinalizing, types.ProvingTaskSkipped),
|
||||
createBatch(types.RollupFinalized, types.ProvingTaskSkipped),
|
||||
createBatch(types.RollupPending, types.ProvingTaskFailed),
|
||||
createBatch(types.RollupCommitting, types.ProvingTaskFailed),
|
||||
createBatch(types.RollupFinalizing, types.ProvingTaskFailed),
|
||||
createBatch(types.RollupFinalized, types.ProvingTaskFailed),
|
||||
createBatch(types.RollupCommitted, types.ProvingTaskVerified),
|
||||
createBatch(types.RollupPending, types.ProvingTaskSkipped, 3),
|
||||
createBatch(types.RollupCommitting, types.ProvingTaskSkipped, 4),
|
||||
createBatch(types.RollupFinalizing, types.ProvingTaskSkipped, 5),
|
||||
createBatch(types.RollupFinalized, types.ProvingTaskSkipped, 6),
|
||||
createBatch(types.RollupPending, types.ProvingTaskFailed, 7),
|
||||
createBatch(types.RollupCommitting, types.ProvingTaskFailed, 8),
|
||||
createBatch(types.RollupFinalizing, types.ProvingTaskFailed, 9),
|
||||
createBatch(types.RollupFinalized, types.ProvingTaskFailed, 10),
|
||||
createBatch(types.RollupCommitted, types.ProvingTaskVerified, 11),
|
||||
}
|
||||
|
||||
relayer.ProcessCommittedBatches()
|
||||
|
||||
for _, id := range skipped {
|
||||
statuses, err := batchOrm.GetRollupStatusByHashList(context.Background(), []string{id})
|
||||
statuses, err := blockBatchOrm.GetRollupStatusByHashList([]string{id})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(statuses))
|
||||
assert.Equal(t, types.RollupFinalizationSkipped, statuses[0])
|
||||
}
|
||||
|
||||
for _, id := range notSkipped {
|
||||
statuses, err := batchOrm.GetRollupStatusByHashList(context.Background(), []string{id})
|
||||
statuses, err := blockBatchOrm.GetRollupStatusByHashList([]string{id})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(statuses))
|
||||
assert.NotEqual(t, types.RollupFinalizationSkipped, statuses[0])
|
||||
}
|
||||
}
|
||||
|
||||
func testL2RelayerRollupConfirm(t *testing.T) {
|
||||
func testL2RelayerMsgConfirm(t *testing.T) {
|
||||
db := setupL2RelayerDB(t)
|
||||
defer bridgeUtils.CloseDB(db)
|
||||
l2MessageOrm := orm.NewL2Message(db)
|
||||
insertL2Messages := []orm.L2Message{
|
||||
{MsgHash: "msg-1", Nonce: 0},
|
||||
{MsgHash: "msg-2", Nonce: 1},
|
||||
}
|
||||
err := l2MessageOrm.SaveL2Messages(context.Background(), insertL2Messages)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Create and set up the Layer2 Relayer.
|
||||
l2Cfg := cfg.L2Config
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
l2Relayer, err := NewLayer2Relayer(ctx, l2Cli, db, l2Cfg.RelayerConfig, false)
|
||||
l2Relayer, err := NewLayer2Relayer(ctx, l2Cli, db, l2Cfg.RelayerConfig)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Simulate message confirmations.
|
||||
l2Relayer.processingMessage.Store("msg-1", "msg-1")
|
||||
l2Relayer.messageSender.SendConfirmation(&sender.Confirmation{
|
||||
ID: "msg-1",
|
||||
IsSuccessful: true,
|
||||
})
|
||||
l2Relayer.processingMessage.Store("msg-2", "msg-2")
|
||||
l2Relayer.messageSender.SendConfirmation(&sender.Confirmation{
|
||||
ID: "msg-2",
|
||||
IsSuccessful: false,
|
||||
})
|
||||
|
||||
// Check the database for the updated status using TryTimes.
|
||||
assert.True(t, utils.TryTimes(5, func() bool {
|
||||
fields1 := map[string]interface{}{"msg_hash": "msg-1"}
|
||||
msg1, err1 := l2MessageOrm.GetL2Messages(fields1, nil, 0)
|
||||
if len(msg1) != 1 {
|
||||
return false
|
||||
}
|
||||
fields2 := map[string]interface{}{"msg_hash": "msg-2"}
|
||||
msg2, err2 := l2MessageOrm.GetL2Messages(fields2, nil, 0)
|
||||
if len(msg2) != 1 {
|
||||
return false
|
||||
}
|
||||
return err1 == nil && types.MsgStatus(msg1[0].Status) == types.MsgConfirmed &&
|
||||
err2 == nil && types.MsgStatus(msg2[0].Status) == types.MsgRelayFailed
|
||||
}))
|
||||
}
|
||||
|
||||
func testL2RelayerRollupConfirm(t *testing.T) {
|
||||
db := setupL2RelayerDB(t)
|
||||
defer bridgeUtils.CloseDB(db)
|
||||
|
||||
// Insert test data.
|
||||
batches := make([]*bridgeTypes.BatchData, 6)
|
||||
for i := 0; i < 6; i++ {
|
||||
batches[i] = genBatchData(t, uint64(i))
|
||||
}
|
||||
|
||||
blockBatchOrm := orm.NewBlockBatch(db)
|
||||
err := db.Transaction(func(tx *gorm.DB) error {
|
||||
for _, batch := range batches {
|
||||
rowsAffected, dbTxErr := blockBatchOrm.InsertBlockBatchByBatchData(tx, batch)
|
||||
if dbTxErr != nil {
|
||||
return dbTxErr
|
||||
}
|
||||
if rowsAffected != 1 {
|
||||
dbTxErr = errors.New("the InsertBlockBatchByBatchData affected row is not 1")
|
||||
return dbTxErr
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Create and set up the Layer2 Relayer.
|
||||
l2Cfg := cfg.L2Config
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
l2Relayer, err := NewLayer2Relayer(ctx, l2Cli, db, l2Cfg.RelayerConfig)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Simulate message confirmations.
|
||||
processingKeys := []string{"committed-1", "committed-2", "finalized-1", "finalized-2"}
|
||||
isSuccessful := []bool{true, false, true, false}
|
||||
|
||||
batchOrm := orm.NewBatch(db)
|
||||
batchHashes := make([]string, len(processingKeys))
|
||||
for i := range batchHashes {
|
||||
batch, err := batchOrm.InsertBatch(context.Background(), 0, 1, chunkHash1.Hex(), chunkHash2.Hex(), []*bridgeTypes.Chunk{chunk1, chunk2})
|
||||
assert.NoError(t, err)
|
||||
batchHashes[i] = batch.Hash
|
||||
}
|
||||
|
||||
for i, key := range processingKeys[:2] {
|
||||
l2Relayer.processingCommitment.Store(key, batchHashes[i])
|
||||
l2Relayer.rollupSender.SendConfirmation(&sender.Confirmation{
|
||||
batchHashes := []string{batches[i*2].Hash().Hex(), batches[i*2+1].Hash().Hex()}
|
||||
l2Relayer.processingBatchesCommitment.Store(key, batchHashes)
|
||||
l2Relayer.messageSender.SendConfirmation(&sender.Confirmation{
|
||||
ID: key,
|
||||
IsSuccessful: isSuccessful[i],
|
||||
TxHash: common.HexToHash("0x123456789abcdef"),
|
||||
})
|
||||
}
|
||||
|
||||
for i, key := range processingKeys[2:] {
|
||||
l2Relayer.processingFinalization.Store(key, batchHashes[i+2])
|
||||
batchHash := batches[i+4].Hash().Hex()
|
||||
l2Relayer.processingFinalization.Store(key, batchHash)
|
||||
l2Relayer.rollupSender.SendConfirmation(&sender.Confirmation{
|
||||
ID: key,
|
||||
IsSuccessful: isSuccessful[i+2],
|
||||
TxHash: common.HexToHash("0x123456789abcdef"),
|
||||
TxHash: common.HexToHash("0x56789abcdef1234"),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -214,13 +349,15 @@ func testL2RelayerRollupConfirm(t *testing.T) {
|
||||
ok := utils.TryTimes(5, func() bool {
|
||||
expectedStatuses := []types.RollupStatus{
|
||||
types.RollupCommitted,
|
||||
types.RollupCommitted,
|
||||
types.RollupCommitFailed,
|
||||
types.RollupCommitFailed,
|
||||
types.RollupFinalized,
|
||||
types.RollupFinalizeFailed,
|
||||
}
|
||||
|
||||
for i, batchHash := range batchHashes {
|
||||
batchInDB, err := batchOrm.GetBatches(context.Background(), map[string]interface{}{"hash": batchHash}, nil, 0)
|
||||
for i, batch := range batches[:6] {
|
||||
batchInDB, err := blockBatchOrm.GetBlockBatches(map[string]interface{}{"hash": batch.Hash().Hex()}, nil, 0)
|
||||
if err != nil || len(batchInDB) != 1 || types.RollupStatus(batchInDB[0].RollupStatus) != expectedStatuses[i] {
|
||||
return false
|
||||
}
|
||||
@@ -234,42 +371,49 @@ func testL2RelayerGasOracleConfirm(t *testing.T) {
|
||||
db := setupL2RelayerDB(t)
|
||||
defer bridgeUtils.CloseDB(db)
|
||||
|
||||
batchOrm := orm.NewBatch(db)
|
||||
batch1, err := batchOrm.InsertBatch(context.Background(), 0, 0, chunkHash1.Hex(), chunkHash1.Hex(), []*bridgeTypes.Chunk{chunk1})
|
||||
assert.NoError(t, err)
|
||||
// Insert test data.
|
||||
batches := make([]*bridgeTypes.BatchData, 2)
|
||||
for i := 0; i < 2; i++ {
|
||||
batches[i] = genBatchData(t, uint64(i))
|
||||
}
|
||||
|
||||
batch2, err := batchOrm.InsertBatch(context.Background(), 1, 1, chunkHash2.Hex(), chunkHash2.Hex(), []*bridgeTypes.Chunk{chunk2})
|
||||
blockBatchOrm := orm.NewBlockBatch(db)
|
||||
err := db.Transaction(func(tx *gorm.DB) error {
|
||||
for _, batch := range batches {
|
||||
rowsAffected, dbTxErr := blockBatchOrm.InsertBlockBatchByBatchData(tx, batch)
|
||||
if dbTxErr != nil {
|
||||
return dbTxErr
|
||||
}
|
||||
if rowsAffected != 1 {
|
||||
dbTxErr = errors.New("the InsertBlockBatchByBatchData affected row is not 1")
|
||||
return dbTxErr
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Create and set up the Layer2 Relayer.
|
||||
l2Cfg := cfg.L2Config
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
l2Relayer, err := NewLayer2Relayer(ctx, l2Cli, db, l2Cfg.RelayerConfig, false)
|
||||
l2Relayer, err := NewLayer2Relayer(ctx, l2Cli, db, l2Cfg.RelayerConfig)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Simulate message confirmations.
|
||||
type BatchConfirmation struct {
|
||||
batchHash string
|
||||
isSuccessful bool
|
||||
}
|
||||
|
||||
confirmations := []BatchConfirmation{
|
||||
{batchHash: batch1.Hash, isSuccessful: true},
|
||||
{batchHash: batch2.Hash, isSuccessful: false},
|
||||
}
|
||||
|
||||
for _, confirmation := range confirmations {
|
||||
isSuccessful := []bool{true, false}
|
||||
for i, batch := range batches {
|
||||
l2Relayer.gasOracleSender.SendConfirmation(&sender.Confirmation{
|
||||
ID: confirmation.batchHash,
|
||||
IsSuccessful: confirmation.isSuccessful,
|
||||
ID: batch.Hash().Hex(),
|
||||
IsSuccessful: isSuccessful[i],
|
||||
})
|
||||
}
|
||||
|
||||
// Check the database for the updated status using TryTimes.
|
||||
ok := utils.TryTimes(5, func() bool {
|
||||
expectedStatuses := []types.GasOracleStatus{types.GasOracleImported, types.GasOracleFailed}
|
||||
for i, confirmation := range confirmations {
|
||||
gasOracle, err := batchOrm.GetBatches(context.Background(), map[string]interface{}{"hash": confirmation.batchHash}, nil, 0)
|
||||
for i, batch := range batches {
|
||||
gasOracle, err := blockBatchOrm.GetBlockBatches(map[string]interface{}{"hash": batch.Hash().Hex()}, nil, 0)
|
||||
if err != nil || len(gasOracle) != 1 || types.GasOracleStatus(gasOracle[0].OracleStatus) != expectedStatuses[i] {
|
||||
return false
|
||||
}
|
||||
@@ -279,27 +423,42 @@ func testL2RelayerGasOracleConfirm(t *testing.T) {
|
||||
assert.True(t, ok)
|
||||
}
|
||||
|
||||
func genBatchData(t *testing.T, index uint64) *bridgeTypes.BatchData {
|
||||
templateBlockTrace, err := os.ReadFile("../../../testdata/blockTrace_02.json")
|
||||
assert.NoError(t, err)
|
||||
// unmarshal blockTrace
|
||||
wrappedBlock := &bridgeTypes.WrappedBlock{}
|
||||
err = json.Unmarshal(templateBlockTrace, wrappedBlock)
|
||||
assert.NoError(t, err)
|
||||
wrappedBlock.Header.ParentHash = common.HexToHash("0x" + strconv.FormatUint(index+1, 16))
|
||||
parentBatch := &bridgeTypes.BatchInfo{
|
||||
Index: index,
|
||||
Hash: "0x0000000000000000000000000000000000000000",
|
||||
}
|
||||
return bridgeTypes.NewBatchData(parentBatch, []*bridgeTypes.WrappedBlock{wrappedBlock}, nil)
|
||||
}
|
||||
|
||||
func testLayer2RelayerProcessGasPriceOracle(t *testing.T) {
|
||||
db := setupL2RelayerDB(t)
|
||||
defer bridgeUtils.CloseDB(db)
|
||||
|
||||
relayer, err := NewLayer2Relayer(context.Background(), l2Cli, db, cfg.L2Config.RelayerConfig, false)
|
||||
relayer, err := NewLayer2Relayer(context.Background(), l2Cli, db, cfg.L2Config.RelayerConfig)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, relayer)
|
||||
|
||||
var batchOrm *orm.Batch
|
||||
var blockBatchOrm *orm.BlockBatch
|
||||
convey.Convey("Failed to GetLatestBatch", t, func() {
|
||||
targetErr := errors.New("GetLatestBatch error")
|
||||
patchGuard := gomonkey.ApplyMethodFunc(batchOrm, "GetLatestBatch", func(context.Context) (*orm.Batch, error) {
|
||||
patchGuard := gomonkey.ApplyMethodFunc(blockBatchOrm, "GetLatestBatch", func() (*orm.BlockBatch, error) {
|
||||
return nil, targetErr
|
||||
})
|
||||
defer patchGuard.Reset()
|
||||
relayer.ProcessGasPriceOracle()
|
||||
})
|
||||
|
||||
patchGuard := gomonkey.ApplyMethodFunc(batchOrm, "GetLatestBatch", func(context.Context) (*orm.Batch, error) {
|
||||
batch := orm.Batch{
|
||||
OracleStatus: int16(types.GasOraclePending),
|
||||
patchGuard := gomonkey.ApplyMethodFunc(blockBatchOrm, "GetLatestBatch", func() (*orm.BlockBatch, error) {
|
||||
batch := orm.BlockBatch{
|
||||
OracleStatus: int(types.GasOraclePending),
|
||||
Hash: "0x0000000000000000000000000000000000000000",
|
||||
}
|
||||
return &batch, nil
|
||||
@@ -344,14 +503,97 @@ func testLayer2RelayerProcessGasPriceOracle(t *testing.T) {
|
||||
|
||||
convey.Convey("UpdateGasOracleStatusAndOracleTxHash failed", t, func() {
|
||||
targetErr := errors.New("UpdateL2GasOracleStatusAndOracleTxHash error")
|
||||
patchGuard.ApplyMethodFunc(batchOrm, "UpdateL2GasOracleStatusAndOracleTxHash", func(ctx context.Context, hash string, status types.GasOracleStatus, txHash string) error {
|
||||
patchGuard.ApplyMethodFunc(blockBatchOrm, "UpdateL2GasOracleStatusAndOracleTxHash", func(ctx context.Context, hash string, status types.GasOracleStatus, txHash string) error {
|
||||
return targetErr
|
||||
})
|
||||
relayer.ProcessGasPriceOracle()
|
||||
})
|
||||
|
||||
patchGuard.ApplyMethodFunc(batchOrm, "UpdateL2GasOracleStatusAndOracleTxHash", func(ctx context.Context, hash string, status types.GasOracleStatus, txHash string) error {
|
||||
patchGuard.ApplyMethodFunc(blockBatchOrm, "UpdateL2GasOracleStatusAndOracleTxHash", func(ctx context.Context, hash string, status types.GasOracleStatus, txHash string) error {
|
||||
return nil
|
||||
})
|
||||
relayer.ProcessGasPriceOracle()
|
||||
}
|
||||
|
||||
func testLayer2RelayerSendCommitTx(t *testing.T) {
|
||||
db := setupL2RelayerDB(t)
|
||||
defer bridgeUtils.CloseDB(db)
|
||||
|
||||
relayer, err := NewLayer2Relayer(context.Background(), l2Cli, db, cfg.L2Config.RelayerConfig)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, relayer)
|
||||
|
||||
var batchDataList []*bridgeTypes.BatchData
|
||||
convey.Convey("SendCommitTx receives empty batch", t, func() {
|
||||
err = relayer.SendCommitTx(batchDataList)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
parentBatch := &bridgeTypes.BatchInfo{
|
||||
Index: 0,
|
||||
Hash: "0x0000000000000000000000000000000000000000",
|
||||
}
|
||||
|
||||
traces := []*bridgeTypes.WrappedBlock{
|
||||
{
|
||||
Header: &gethTypes.Header{
|
||||
Number: big.NewInt(1000),
|
||||
ParentHash: common.Hash{},
|
||||
Difficulty: big.NewInt(0),
|
||||
BaseFee: big.NewInt(0),
|
||||
},
|
||||
Transactions: nil,
|
||||
WithdrawTrieRoot: common.Hash{},
|
||||
},
|
||||
}
|
||||
|
||||
blocks := []*bridgeTypes.WrappedBlock{traces[0]}
|
||||
tmpBatchData := bridgeTypes.NewBatchData(parentBatch, blocks, cfg.L2Config.BatchProposerConfig.PublicInputConfig)
|
||||
batchDataList = append(batchDataList, tmpBatchData)
|
||||
|
||||
var s abi.ABI
|
||||
convey.Convey("Failed to pack commitBatches", t, func() {
|
||||
targetErr := errors.New("commitBatches error")
|
||||
patchGuard := gomonkey.ApplyMethodFunc(s, "Pack", func(name string, args ...interface{}) ([]byte, error) {
|
||||
return nil, targetErr
|
||||
})
|
||||
defer patchGuard.Reset()
|
||||
|
||||
err = relayer.SendCommitTx(batchDataList)
|
||||
assert.EqualError(t, err, targetErr.Error())
|
||||
})
|
||||
|
||||
patchGuard := gomonkey.ApplyMethodFunc(s, "Pack", func(name string, args ...interface{}) ([]byte, error) {
|
||||
return nil, nil
|
||||
})
|
||||
defer patchGuard.Reset()
|
||||
|
||||
convey.Convey("Failed to send commitBatches tx to layer1", t, func() {
|
||||
targetErr := errors.New("SendTransaction failure")
|
||||
patchGuard.ApplyMethodFunc(relayer.rollupSender, "SendTransaction", func(ID string, target *common.Address, value *big.Int, data []byte, minGasLimit uint64) (hash common.Hash, err error) {
|
||||
return common.Hash{}, targetErr
|
||||
})
|
||||
err = relayer.SendCommitTx(batchDataList)
|
||||
assert.EqualError(t, err, targetErr.Error())
|
||||
})
|
||||
|
||||
patchGuard.ApplyMethodFunc(relayer.rollupSender, "SendTransaction", func(ID string, target *common.Address, value *big.Int, data []byte, minGasLimit uint64) (hash common.Hash, err error) {
|
||||
return common.HexToHash("0x56789abcdef1234"), nil
|
||||
})
|
||||
|
||||
var blockBatchOrm *orm.BlockBatch
|
||||
convey.Convey("UpdateCommitTxHashAndRollupStatus failed", t, func() {
|
||||
targetErr := errors.New("UpdateCommitTxHashAndRollupStatus failure")
|
||||
patchGuard.ApplyMethodFunc(blockBatchOrm, "UpdateCommitTxHashAndRollupStatus", func(ctx context.Context, hash string, commitTxHash string, status types.RollupStatus) error {
|
||||
return targetErr
|
||||
})
|
||||
err = relayer.SendCommitTx(batchDataList)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
patchGuard.ApplyMethodFunc(blockBatchOrm, "UpdateCommitTxHashAndRollupStatus", func(ctx context.Context, hash string, commitTxHash string, status types.RollupStatus) error {
|
||||
return nil
|
||||
})
|
||||
err = relayer.SendCommitTx(batchDataList)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
"github.com/scroll-tech/go-ethereum/ethclient"
|
||||
"github.com/scroll-tech/go-ethereum/log"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"scroll-tech/common/docker"
|
||||
@@ -24,20 +24,17 @@ var (
|
||||
// l2geth client
|
||||
l2Cli *ethclient.Client
|
||||
|
||||
// l2 block
|
||||
// block trace
|
||||
wrappedBlock1 *bridgeTypes.WrappedBlock
|
||||
wrappedBlock2 *bridgeTypes.WrappedBlock
|
||||
|
||||
// chunk
|
||||
chunk1 *bridgeTypes.Chunk
|
||||
chunk2 *bridgeTypes.Chunk
|
||||
chunkHash1 common.Hash
|
||||
chunkHash2 common.Hash
|
||||
// batch data
|
||||
batchData1 *bridgeTypes.BatchData
|
||||
batchData2 *bridgeTypes.BatchData
|
||||
)
|
||||
|
||||
func setupEnv(t *testing.T) {
|
||||
func setupEnv(t *testing.T) (err error) {
|
||||
// Load config.
|
||||
var err error
|
||||
cfg, err = config.NewConfig("../../../conf/config.json")
|
||||
assert.NoError(t, err)
|
||||
|
||||
@@ -57,22 +54,40 @@ func setupEnv(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
templateBlockTrace1, err := os.ReadFile("../../../testdata/blockTrace_02.json")
|
||||
assert.NoError(t, err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// unmarshal blockTrace
|
||||
wrappedBlock1 = &bridgeTypes.WrappedBlock{}
|
||||
err = json.Unmarshal(templateBlockTrace1, wrappedBlock1)
|
||||
assert.NoError(t, err)
|
||||
chunk1 = &bridgeTypes.Chunk{Blocks: []*bridgeTypes.WrappedBlock{wrappedBlock1}}
|
||||
chunkHash1, err = chunk1.Hash(0)
|
||||
assert.NoError(t, err)
|
||||
if err = json.Unmarshal(templateBlockTrace1, wrappedBlock1); err != nil {
|
||||
return err
|
||||
}
|
||||
parentBatch1 := &bridgeTypes.BatchInfo{
|
||||
Index: 0,
|
||||
Hash: "0x0cc6b102c2924402c14b2e3a19baccc316252bfdc44d9ec62e942d34e39ec729",
|
||||
StateRoot: "0x2579122e8f9ec1e862e7d415cef2fb495d7698a8e5f0dddc5651ba4236336e7d",
|
||||
}
|
||||
batchData1 = bridgeTypes.NewBatchData(parentBatch1, []*bridgeTypes.WrappedBlock{wrappedBlock1}, nil)
|
||||
|
||||
templateBlockTrace2, err := os.ReadFile("../../../testdata/blockTrace_03.json")
|
||||
assert.NoError(t, err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// unmarshal blockTrace
|
||||
wrappedBlock2 = &bridgeTypes.WrappedBlock{}
|
||||
err = json.Unmarshal(templateBlockTrace2, wrappedBlock2)
|
||||
assert.NoError(t, err)
|
||||
chunk2 = &bridgeTypes.Chunk{Blocks: []*bridgeTypes.WrappedBlock{wrappedBlock2}}
|
||||
chunkHash2, err = chunk2.Hash(chunk1.NumL1Messages(0))
|
||||
assert.NoError(t, err)
|
||||
if err = json.Unmarshal(templateBlockTrace2, wrappedBlock2); err != nil {
|
||||
return err
|
||||
}
|
||||
parentBatch2 := &bridgeTypes.BatchInfo{
|
||||
Index: batchData1.Batch.BatchIndex,
|
||||
Hash: batchData1.Hash().Hex(),
|
||||
StateRoot: batchData1.Batch.NewStateRoot.String(),
|
||||
}
|
||||
batchData2 = bridgeTypes.NewBatchData(parentBatch2, []*bridgeTypes.WrappedBlock{wrappedBlock2}, nil)
|
||||
|
||||
log.Info("batchHash", "batchhash1", batchData1.Hash().Hex(), "batchhash2", batchData2.Hash().Hex())
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
@@ -84,7 +99,9 @@ func TestMain(m *testing.M) {
|
||||
}
|
||||
|
||||
func TestFunctions(t *testing.T) {
|
||||
setupEnv(t)
|
||||
if err := setupEnv(t); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Run l1 relayer test cases.
|
||||
t.Run("TestCreateNewL1Relayer", testCreateNewL1Relayer)
|
||||
t.Run("TestL1RelayerProcessSaveEvents", testL1RelayerProcessSaveEvents)
|
||||
@@ -94,10 +111,12 @@ func TestFunctions(t *testing.T) {
|
||||
|
||||
// Run l2 relayer test cases.
|
||||
t.Run("TestCreateNewRelayer", testCreateNewRelayer)
|
||||
t.Run("TestL2RelayerProcessPendingBatches", testL2RelayerProcessPendingBatches)
|
||||
t.Run("TestL2RelayerProcessSaveEvents", testL2RelayerProcessSaveEvents)
|
||||
t.Run("TestL2RelayerProcessCommittedBatches", testL2RelayerProcessCommittedBatches)
|
||||
t.Run("TestL2RelayerSkipBatches", testL2RelayerSkipBatches)
|
||||
t.Run("TestL2RelayerMsgConfirm", testL2RelayerMsgConfirm)
|
||||
t.Run("TestL2RelayerRollupConfirm", testL2RelayerRollupConfirm)
|
||||
t.Run("TestL2RelayerGasOracleConfirm", testL2RelayerGasOracleConfirm)
|
||||
t.Run("TestLayer2RelayerProcessGasPriceOracle", testLayer2RelayerProcessGasPriceOracle)
|
||||
t.Run("TestLayer2RelayerSendCommitTx", testLayer2RelayerSendCommitTx)
|
||||
}
|
||||
|
||||
@@ -3,169 +3,391 @@ package watcher
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/log"
|
||||
gethMetrics "github.com/scroll-tech/go-ethereum/metrics"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"scroll-tech/common/metrics"
|
||||
"scroll-tech/common/types"
|
||||
|
||||
bridgeAbi "scroll-tech/bridge/abi"
|
||||
"scroll-tech/bridge/internal/config"
|
||||
"scroll-tech/bridge/internal/controller/relayer"
|
||||
"scroll-tech/bridge/internal/orm"
|
||||
bridgeTypes "scroll-tech/bridge/internal/types"
|
||||
)
|
||||
|
||||
// BatchProposer proposes batches based on available unbatched chunks.
|
||||
var (
|
||||
bridgeL2BatchesGasOverThresholdTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/batches/gas/over/threshold/total", metrics.ScrollRegistry)
|
||||
bridgeL2BatchesTxsOverThresholdTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/batches/txs/over/threshold/total", metrics.ScrollRegistry)
|
||||
bridgeL2BatchesBlocksCreatedTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/batches/blocks/created/total", metrics.ScrollRegistry)
|
||||
bridgeL2BatchesCommitsSentTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/batches/commits/sent/total", metrics.ScrollRegistry)
|
||||
bridgeL2BatchesOversizedTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/batches/oversized/total", metrics.ScrollRegistry)
|
||||
bridgeL2BatchesTxsCreatedPerBatchGauge = gethMetrics.NewRegisteredGauge("bridge/l2/batches/txs/created/per/batch", metrics.ScrollRegistry)
|
||||
bridgeL2BatchesGasCreatedPerBatchGauge = gethMetrics.NewRegisteredGauge("bridge/l2/batches/gas/created/per/batch", metrics.ScrollRegistry)
|
||||
bridgeL2BatchesPayloadSizePerBatchGauge = gethMetrics.NewRegisteredGauge("bridge/l2/batches/payload/size/per/batch", metrics.ScrollRegistry)
|
||||
)
|
||||
|
||||
// BatchProposer sends batches commit transactions to relayer.
|
||||
type BatchProposer struct {
|
||||
ctx context.Context
|
||||
db *gorm.DB
|
||||
mutex sync.Mutex
|
||||
ctx context.Context
|
||||
db *gorm.DB
|
||||
|
||||
batchOrm *orm.Batch
|
||||
chunkOrm *orm.Chunk
|
||||
l2Block *orm.L2Block
|
||||
batchTimeSec uint64
|
||||
batchGasThreshold uint64
|
||||
batchTxNumThreshold uint64
|
||||
batchBlocksLimit uint64
|
||||
batchCommitTimeSec uint64
|
||||
commitCalldataSizeLimit uint64
|
||||
batchDataBufferSizeLimit uint64
|
||||
commitCalldataMinSize uint64
|
||||
|
||||
maxChunkNumPerBatch uint64
|
||||
maxL1CommitGasPerBatch uint64
|
||||
maxL1CommitCalldataSizePerBatch uint64
|
||||
minChunkNumPerBatch uint64
|
||||
batchTimeoutSec uint64
|
||||
proofGenerationFreq uint64
|
||||
batchDataBuffer []*bridgeTypes.BatchData
|
||||
relayer *relayer.Layer2Relayer
|
||||
|
||||
blockBatchOrm *orm.BlockBatch
|
||||
blockTraceOrm *orm.BlockTrace
|
||||
|
||||
piCfg *bridgeTypes.PublicInputHashConfig
|
||||
}
|
||||
|
||||
// NewBatchProposer creates a new BatchProposer instance.
|
||||
func NewBatchProposer(ctx context.Context, cfg *config.BatchProposerConfig, db *gorm.DB) *BatchProposer {
|
||||
return &BatchProposer{
|
||||
ctx: ctx,
|
||||
db: db,
|
||||
batchOrm: orm.NewBatch(db),
|
||||
chunkOrm: orm.NewChunk(db),
|
||||
l2Block: orm.NewL2Block(db),
|
||||
maxChunkNumPerBatch: cfg.MaxChunkNumPerBatch,
|
||||
maxL1CommitGasPerBatch: cfg.MaxL1CommitGasPerBatch,
|
||||
maxL1CommitCalldataSizePerBatch: cfg.MaxL1CommitCalldataSizePerBatch,
|
||||
minChunkNumPerBatch: cfg.MinChunkNumPerBatch,
|
||||
batchTimeoutSec: cfg.BatchTimeoutSec,
|
||||
// NewBatchProposer will return a new instance of BatchProposer.
|
||||
func NewBatchProposer(ctx context.Context, cfg *config.BatchProposerConfig, relayer *relayer.Layer2Relayer, db *gorm.DB) *BatchProposer {
|
||||
p := &BatchProposer{
|
||||
mutex: sync.Mutex{},
|
||||
ctx: ctx,
|
||||
db: db,
|
||||
blockBatchOrm: orm.NewBlockBatch(db),
|
||||
blockTraceOrm: orm.NewBlockTrace(db),
|
||||
batchTimeSec: cfg.BatchTimeSec,
|
||||
batchGasThreshold: cfg.BatchGasThreshold,
|
||||
batchTxNumThreshold: cfg.BatchTxNumThreshold,
|
||||
batchBlocksLimit: cfg.BatchBlocksLimit,
|
||||
batchCommitTimeSec: cfg.BatchCommitTimeSec,
|
||||
commitCalldataSizeLimit: cfg.CommitTxCalldataSizeLimit,
|
||||
commitCalldataMinSize: cfg.CommitTxCalldataMinSize,
|
||||
batchDataBufferSizeLimit: 100*cfg.CommitTxCalldataSizeLimit + 1*1024*1024, // @todo: determine the value.
|
||||
proofGenerationFreq: cfg.ProofGenerationFreq,
|
||||
piCfg: cfg.PublicInputConfig,
|
||||
relayer: relayer,
|
||||
}
|
||||
|
||||
// for graceful restart.
|
||||
p.recoverBatchDataBuffer()
|
||||
|
||||
// try to commit the leftover pending batches
|
||||
p.TryCommitBatches()
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
// TryProposeBatch tries to propose a new batches.
|
||||
func (p *BatchProposer) TryProposeBatch() {
|
||||
dbChunks, err := p.proposeBatchChunks()
|
||||
func (p *BatchProposer) recoverBatchDataBuffer() {
|
||||
// batches are sorted by batch index in increasing order
|
||||
batchHashes, err := p.blockBatchOrm.GetBlockBatchesHashByRollupStatus(types.RollupPending, math.MaxInt32)
|
||||
if err != nil {
|
||||
log.Error("proposeBatchChunks failed", "err", err)
|
||||
log.Crit("Failed to fetch pending L2 batches", "err", err)
|
||||
}
|
||||
if len(batchHashes) == 0 {
|
||||
return
|
||||
}
|
||||
if err := p.updateBatchInfoInDB(dbChunks); err != nil {
|
||||
log.Error("update batch info in db failed", "err", err)
|
||||
log.Info("Load pending batches into batchDataBuffer")
|
||||
|
||||
// helper function to cache and get BlockBatch from DB
|
||||
blockBatchCache := make(map[string]orm.BlockBatch)
|
||||
getBlockBatch := func(batchHash string) (*orm.BlockBatch, error) {
|
||||
if blockBatch, ok := blockBatchCache[batchHash]; ok {
|
||||
return &blockBatch, nil
|
||||
}
|
||||
blockBatches, err := p.blockBatchOrm.GetBlockBatches(map[string]interface{}{"hash": batchHash}, nil, 0)
|
||||
if err != nil || len(blockBatches) == 0 {
|
||||
return nil, err
|
||||
}
|
||||
blockBatchCache[batchHash] = blockBatches[0]
|
||||
return &blockBatches[0], nil
|
||||
}
|
||||
|
||||
// recover the in-memory batchData from DB
|
||||
for _, batchHash := range batchHashes {
|
||||
log.Info("recover batch data from pending batch", "batch_hash", batchHash)
|
||||
blockBatch, err := getBlockBatch(batchHash)
|
||||
if err != nil {
|
||||
log.Error("could not get BlockBatch", "batch_hash", batchHash, "error", err)
|
||||
continue
|
||||
}
|
||||
|
||||
parentBatch, err := getBlockBatch(blockBatch.ParentHash)
|
||||
if err != nil {
|
||||
log.Error("could not get parent BlockBatch", "batch_hash", batchHash, "error", err)
|
||||
continue
|
||||
}
|
||||
|
||||
whereFileds := map[string]interface{}{
|
||||
"batch_hash": batchHash,
|
||||
}
|
||||
orderByList := []string{
|
||||
"number ASC",
|
||||
}
|
||||
|
||||
blockTraces, err := p.blockTraceOrm.GetL2BlockInfos(whereFileds, orderByList, 0)
|
||||
if err != nil {
|
||||
log.Error("could not GetL2BlockInfos", "batch_hash", batchHash, "error", err)
|
||||
continue
|
||||
}
|
||||
if len(blockTraces) != int(blockBatch.EndBlockNumber-blockBatch.StartBlockNumber+1) {
|
||||
log.Error("the number of block info retrieved from DB mistmatches the batch info in the DB",
|
||||
"len(blockInfos)", len(blockTraces),
|
||||
"expected", blockBatch.EndBlockNumber-blockBatch.StartBlockNumber+1)
|
||||
continue
|
||||
}
|
||||
|
||||
batchData, err := p.generateBatchData(parentBatch, blockTraces)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if batchData.Hash().Hex() != batchHash {
|
||||
log.Error("the hash from recovered batch data mismatches the DB entry",
|
||||
"recovered_batch_hash", batchData.Hash().Hex(),
|
||||
"expected", batchHash)
|
||||
continue
|
||||
}
|
||||
|
||||
p.batchDataBuffer = append(p.batchDataBuffer, batchData)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *BatchProposer) updateBatchInfoInDB(dbChunks []*orm.Chunk) error {
|
||||
numChunks := len(dbChunks)
|
||||
if numChunks <= 0 {
|
||||
return nil
|
||||
// TryProposeBatch will try to propose a batch.
|
||||
func (p *BatchProposer) TryProposeBatch() {
|
||||
p.mutex.Lock()
|
||||
defer p.mutex.Unlock()
|
||||
|
||||
for p.getBatchDataBufferSize() < p.batchDataBufferSizeLimit {
|
||||
orderBy := []string{"number ASC"}
|
||||
blockTraces, err := p.blockTraceOrm.GetUnbatchedL2Blocks(map[string]interface{}{}, orderBy, int(p.batchBlocksLimit))
|
||||
if err != nil {
|
||||
log.Error("failed to get unbatched blocks", "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
batchCreated := p.proposeBatch(blockTraces)
|
||||
|
||||
// while size of batchDataBuffer < commitCalldataMinSize,
|
||||
// proposer keeps fetching and porposing batches.
|
||||
if p.getBatchDataBufferSize() >= p.commitCalldataMinSize {
|
||||
return
|
||||
}
|
||||
|
||||
if !batchCreated {
|
||||
// wait for watcher to insert l2 traces.
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
chunks, err := p.dbChunksToBridgeChunks(dbChunks)
|
||||
}
|
||||
|
||||
// TryCommitBatches will try to commit the pending batches.
|
||||
func (p *BatchProposer) TryCommitBatches() {
|
||||
p.mutex.Lock()
|
||||
defer p.mutex.Unlock()
|
||||
|
||||
if len(p.batchDataBuffer) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// estimate the calldata length to determine whether to commit the pending batches
|
||||
index := 0
|
||||
commit := false
|
||||
calldataByteLen := uint64(0)
|
||||
for ; index < len(p.batchDataBuffer); index++ {
|
||||
calldataByteLen += bridgeAbi.GetBatchCalldataLength(&p.batchDataBuffer[index].Batch)
|
||||
if calldataByteLen > p.commitCalldataSizeLimit {
|
||||
commit = true
|
||||
if index == 0 {
|
||||
log.Warn(
|
||||
"The calldata size of one batch is larger than the threshold",
|
||||
"batch_hash", p.batchDataBuffer[0].Hash().Hex(),
|
||||
"calldata_size", calldataByteLen,
|
||||
)
|
||||
index = 1
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if !commit && p.batchDataBuffer[0].Timestamp()+p.batchCommitTimeSec > uint64(time.Now().Unix()) {
|
||||
return
|
||||
}
|
||||
|
||||
// Send commit tx for batchDataBuffer[0:index]
|
||||
log.Info("Commit batches", "start_index", p.batchDataBuffer[0].Batch.BatchIndex,
|
||||
"end_index", p.batchDataBuffer[index-1].Batch.BatchIndex)
|
||||
err := p.relayer.SendCommitTx(p.batchDataBuffer[:index])
|
||||
if err != nil {
|
||||
// leave the retry to the next ticker
|
||||
log.Error("SendCommitTx failed", "error", err)
|
||||
} else {
|
||||
// pop the processed batches from the buffer
|
||||
bridgeL2BatchesCommitsSentTotalCounter.Inc(1)
|
||||
p.batchDataBuffer = p.batchDataBuffer[index:]
|
||||
}
|
||||
}
|
||||
|
||||
func (p *BatchProposer) proposeBatch(blockTraces []orm.BlockTrace) bool {
|
||||
if len(blockTraces) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
approximatePayloadSize := func(hash string) (uint64, error) {
|
||||
traces, err := p.blockTraceOrm.GetL2WrappedBlocks(map[string]interface{}{"hash": hash})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(traces) != 1 {
|
||||
return 0, fmt.Errorf("unexpected traces length, expected = 1, actual = %d", len(traces))
|
||||
}
|
||||
size := 0
|
||||
for _, tx := range traces[0].Transactions {
|
||||
size += len(tx.Data)
|
||||
}
|
||||
return uint64(size), nil
|
||||
}
|
||||
|
||||
firstSize, err := approximatePayloadSize(blockTraces[0].Hash)
|
||||
if err != nil {
|
||||
log.Error("failed to create batch", "number", blockTraces[0].Number, "err", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if firstSize > p.commitCalldataSizeLimit {
|
||||
log.Warn("oversized payload even for only 1 block", "height", blockTraces[0].Number, "size", firstSize)
|
||||
// note: we should probably fail here once we can ensure this will not happen
|
||||
if err := p.createBatchForBlocks(blockTraces[:1]); err != nil {
|
||||
log.Error("failed to create batch", "number", blockTraces[0].Number, "err", err)
|
||||
return false
|
||||
}
|
||||
bridgeL2BatchesTxsCreatedPerBatchGauge.Update(int64(blockTraces[0].TxNum))
|
||||
bridgeL2BatchesGasCreatedPerBatchGauge.Update(int64(blockTraces[0].GasUsed))
|
||||
bridgeL2BatchesPayloadSizePerBatchGauge.Update(int64(firstSize))
|
||||
bridgeL2BatchesBlocksCreatedTotalCounter.Inc(1)
|
||||
bridgeL2BatchesOversizedTotalCounter.Inc(1)
|
||||
return true
|
||||
}
|
||||
|
||||
if blockTraces[0].GasUsed > p.batchGasThreshold {
|
||||
bridgeL2BatchesGasOverThresholdTotalCounter.Inc(1)
|
||||
log.Warn("gas overflow even for only 1 block", "height", blockTraces[0].Number, "gas", blockTraces[0].GasUsed)
|
||||
if err := p.createBatchForBlocks(blockTraces[:1]); err != nil {
|
||||
log.Error("failed to create batch", "number", blockTraces[0].Number, "err", err)
|
||||
} else {
|
||||
bridgeL2BatchesTxsCreatedPerBatchGauge.Update(int64(blockTraces[0].TxNum))
|
||||
bridgeL2BatchesGasCreatedPerBatchGauge.Update(int64(blockTraces[0].GasUsed))
|
||||
bridgeL2BatchesPayloadSizePerBatchGauge.Update(int64(firstSize))
|
||||
bridgeL2BatchesBlocksCreatedTotalCounter.Inc(1)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if blockTraces[0].TxNum > p.batchTxNumThreshold {
|
||||
bridgeL2BatchesTxsOverThresholdTotalCounter.Inc(1)
|
||||
log.Warn("too many txs even for only 1 block", "height", blockTraces[0].Number, "tx_num", blockTraces[0].TxNum)
|
||||
if err := p.createBatchForBlocks(blockTraces[:1]); err != nil {
|
||||
log.Error("failed to create batch", "number", blockTraces[0].Number, "err", err)
|
||||
} else {
|
||||
bridgeL2BatchesTxsCreatedPerBatchGauge.Update(int64(blockTraces[0].TxNum))
|
||||
bridgeL2BatchesGasCreatedPerBatchGauge.Update(int64(blockTraces[0].GasUsed))
|
||||
bridgeL2BatchesPayloadSizePerBatchGauge.Update(int64(firstSize))
|
||||
bridgeL2BatchesBlocksCreatedTotalCounter.Inc(1)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var gasUsed, txNum, payloadSize uint64
|
||||
reachThreshold := false
|
||||
// add blocks into batch until reach batchGasThreshold
|
||||
for i, block := range blockTraces {
|
||||
size, err := approximatePayloadSize(block.Hash)
|
||||
if err != nil {
|
||||
log.Error("failed to create batch", "number", block.Number, "err", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if (gasUsed+block.GasUsed > p.batchGasThreshold) || (txNum+block.TxNum > p.batchTxNumThreshold) || (payloadSize+size > p.commitCalldataSizeLimit) {
|
||||
blockTraces = blockTraces[:i]
|
||||
reachThreshold = true
|
||||
break
|
||||
}
|
||||
gasUsed += block.GasUsed
|
||||
txNum += block.TxNum
|
||||
payloadSize += size
|
||||
}
|
||||
|
||||
// if too few gas gathered, but we don't want to halt, we then check the first block in the batch:
|
||||
// if it's not old enough we will skip proposing the batch,
|
||||
// otherwise we will still propose a batch
|
||||
if !reachThreshold && blockTraces[0].BlockTimestamp+p.batchTimeSec > uint64(time.Now().Unix()) {
|
||||
return false
|
||||
}
|
||||
|
||||
if err := p.createBatchForBlocks(blockTraces); err != nil {
|
||||
log.Error("failed to create batch", "from", blockTraces[0].Number, "to", blockTraces[len(blockTraces)-1].Number, "err", err)
|
||||
} else {
|
||||
bridgeL2BatchesTxsCreatedPerBatchGauge.Update(int64(txNum))
|
||||
bridgeL2BatchesGasCreatedPerBatchGauge.Update(int64(gasUsed))
|
||||
bridgeL2BatchesPayloadSizePerBatchGauge.Update(int64(payloadSize))
|
||||
bridgeL2BatchesBlocksCreatedTotalCounter.Inc(int64(len(blockTraces)))
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *BatchProposer) createBatchForBlocks(blocks []orm.BlockTrace) error {
|
||||
lastBatch, err := p.blockBatchOrm.GetLatestBatch()
|
||||
if err != nil {
|
||||
// We should not receive sql.ErrNoRows error. The DB should have the batch entry that contains the genesis block.
|
||||
return err
|
||||
}
|
||||
|
||||
startChunkIndex := dbChunks[0].Index
|
||||
startChunkHash := dbChunks[0].Hash
|
||||
endChunkIndex := dbChunks[numChunks-1].Index
|
||||
endChunkHash := dbChunks[numChunks-1].Hash
|
||||
err = p.db.Transaction(func(dbTX *gorm.DB) error {
|
||||
batch, dbErr := p.batchOrm.InsertBatch(p.ctx, startChunkIndex, endChunkIndex, startChunkHash, endChunkHash, chunks, dbTX)
|
||||
if dbErr != nil {
|
||||
return dbErr
|
||||
}
|
||||
dbErr = p.chunkOrm.UpdateBatchHashInRange(p.ctx, startChunkIndex, endChunkIndex, batch.Hash, dbTX)
|
||||
if dbErr != nil {
|
||||
return dbErr
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *BatchProposer) proposeBatchChunks() ([]*orm.Chunk, error) {
|
||||
dbChunks, err := p.chunkOrm.GetUnbatchedChunks(p.ctx)
|
||||
batchData, err := p.generateBatchData(lastBatch, blocks)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
log.Error("createBatchData failed", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if len(dbChunks) == 0 {
|
||||
log.Warn("No Unbatched Chunks")
|
||||
return nil, nil
|
||||
if err := orm.AddBatchInfoToDB(p.db, batchData); err != nil {
|
||||
log.Error("addBatchInfoToDB failed", "BatchHash", batchData.Hash(), "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
firstChunk := dbChunks[0]
|
||||
totalL1CommitCalldataSize := firstChunk.TotalL1CommitCalldataSize
|
||||
totalL1CommitGas := firstChunk.TotalL1CommitGas
|
||||
var totalChunks uint64 = 1
|
||||
|
||||
// Check if the first chunk breaks hard limits.
|
||||
// If so, it indicates there are bugs in chunk-proposer, manual fix is needed.
|
||||
if totalL1CommitGas > p.maxL1CommitGasPerBatch {
|
||||
return nil, fmt.Errorf(
|
||||
"the first chunk exceeds l1 commit gas limit; start block number: %v, end block number: %v, commit gas: %v, max commit gas limit: %v",
|
||||
firstChunk.StartBlockNumber,
|
||||
firstChunk.EndBlockNumber,
|
||||
totalL1CommitGas,
|
||||
p.maxL1CommitGasPerBatch,
|
||||
)
|
||||
}
|
||||
|
||||
if totalL1CommitCalldataSize > p.maxL1CommitCalldataSizePerBatch {
|
||||
return nil, fmt.Errorf(
|
||||
"the first chunk exceeds l1 commit calldata size limit; start block number: %v, end block number %v, calldata size: %v, max calldata size limit: %v",
|
||||
firstChunk.StartBlockNumber,
|
||||
firstChunk.EndBlockNumber,
|
||||
totalL1CommitCalldataSize,
|
||||
p.maxL1CommitCalldataSizePerBatch,
|
||||
)
|
||||
}
|
||||
|
||||
for i, chunk := range dbChunks[1:] {
|
||||
totalChunks++
|
||||
totalL1CommitCalldataSize += chunk.TotalL1CommitCalldataSize
|
||||
totalL1CommitGas += chunk.TotalL1CommitGas
|
||||
if totalChunks > p.maxChunkNumPerBatch ||
|
||||
totalL1CommitCalldataSize > p.maxL1CommitCalldataSizePerBatch ||
|
||||
totalL1CommitGas > p.maxL1CommitGasPerBatch {
|
||||
return dbChunks[:i+1], nil
|
||||
}
|
||||
}
|
||||
|
||||
var hasChunkTimeout bool
|
||||
currentTimeSec := uint64(time.Now().Unix())
|
||||
if dbChunks[0].StartBlockTime+p.batchTimeoutSec < currentTimeSec {
|
||||
log.Warn("first block timeout",
|
||||
"start block number", dbChunks[0].StartBlockNumber,
|
||||
"first block timestamp", dbChunks[0].StartBlockTime,
|
||||
"chunk outdated time threshold", currentTimeSec,
|
||||
)
|
||||
hasChunkTimeout = true
|
||||
}
|
||||
|
||||
if !hasChunkTimeout && uint64(len(dbChunks)) < p.minChunkNumPerBatch {
|
||||
log.Warn("The payload size of the batch is less than the minimum limit",
|
||||
"chunk num", len(dbChunks), "minChunkNumPerBatch", p.minChunkNumPerBatch,
|
||||
)
|
||||
return nil, nil
|
||||
}
|
||||
return dbChunks, nil
|
||||
p.batchDataBuffer = append(p.batchDataBuffer, batchData)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *BatchProposer) dbChunksToBridgeChunks(dbChunks []*orm.Chunk) ([]*bridgeTypes.Chunk, error) {
|
||||
chunks := make([]*bridgeTypes.Chunk, len(dbChunks))
|
||||
for i, c := range dbChunks {
|
||||
wrappedBlocks, err := p.l2Block.GetL2BlocksInRange(p.ctx, c.StartBlockNumber, c.EndBlockNumber)
|
||||
if err != nil {
|
||||
log.Error("Failed to fetch wrapped blocks",
|
||||
"start number", c.StartBlockNumber, "end number", c.EndBlockNumber, "error", err)
|
||||
func (p *BatchProposer) generateBatchData(parentBatch *orm.BlockBatch, blocks []orm.BlockTrace) (*bridgeTypes.BatchData, error) {
|
||||
var wrappedBlocks []*bridgeTypes.WrappedBlock
|
||||
for _, block := range blocks {
|
||||
trs, err := p.blockTraceOrm.GetL2WrappedBlocks(map[string]interface{}{"hash": block.Hash})
|
||||
if err != nil || len(trs) != 1 {
|
||||
log.Error("Failed to GetBlockTraces", "hash", block.Hash, "err", err)
|
||||
return nil, err
|
||||
}
|
||||
chunks[i] = &bridgeTypes.Chunk{
|
||||
Blocks: wrappedBlocks,
|
||||
}
|
||||
|
||||
wrappedBlocks = append(wrappedBlocks, trs[0])
|
||||
}
|
||||
return chunks, nil
|
||||
|
||||
parentBatchInfo := bridgeTypes.BatchInfo{
|
||||
Index: parentBatch.Index,
|
||||
Hash: parentBatch.Hash,
|
||||
StateRoot: parentBatch.StateRoot,
|
||||
}
|
||||
return bridgeTypes.NewBatchData(&parentBatchInfo, wrappedBlocks, p.piCfg), nil
|
||||
}
|
||||
|
||||
func (p *BatchProposer) getBatchDataBufferSize() (size uint64) {
|
||||
for _, batchData := range p.batchDataBuffer {
|
||||
size += bridgeAbi.GetBatchCalldataLength(&batchData.Batch)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2,71 +2,205 @@ package watcher
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/agiledragon/gomonkey/v2"
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
gethTtypes "github.com/scroll-tech/go-ethereum/core/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"scroll-tech/common/types"
|
||||
|
||||
"scroll-tech/bridge/internal/config"
|
||||
"scroll-tech/bridge/internal/controller/relayer"
|
||||
"scroll-tech/bridge/internal/orm"
|
||||
bridgeTypes "scroll-tech/bridge/internal/types"
|
||||
"scroll-tech/bridge/internal/utils"
|
||||
bridgeUtils "scroll-tech/bridge/internal/utils"
|
||||
)
|
||||
|
||||
// TODO: Add unit tests that the limits are enforced correctly.
|
||||
func testBatchProposer(t *testing.T) {
|
||||
func testBatchProposerProposeBatch(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer utils.CloseDB(db)
|
||||
defer bridgeUtils.CloseDB(db)
|
||||
|
||||
l2BlockOrm := orm.NewL2Block(db)
|
||||
err := l2BlockOrm.InsertL2Blocks(context.Background(), []*bridgeTypes.WrappedBlock{wrappedBlock1, wrappedBlock2})
|
||||
assert.NoError(t, err)
|
||||
p := &BatchProposer{
|
||||
batchGasThreshold: 1000,
|
||||
batchTxNumThreshold: 10,
|
||||
batchTimeSec: 300,
|
||||
commitCalldataSizeLimit: 500,
|
||||
}
|
||||
|
||||
cp := NewChunkProposer(context.Background(), &config.ChunkProposerConfig{
|
||||
MaxTxGasPerChunk: 1000000000,
|
||||
MaxL2TxNumPerChunk: 10000,
|
||||
MaxL1CommitGasPerChunk: 50000000000,
|
||||
MaxL1CommitCalldataSizePerChunk: 1000000,
|
||||
MinL1CommitCalldataSizePerChunk: 0,
|
||||
ChunkTimeoutSec: 300,
|
||||
}, db)
|
||||
cp.TryProposeChunk()
|
||||
var blockTrace *orm.BlockTrace
|
||||
patchGuard := gomonkey.ApplyMethodFunc(blockTrace, "GetL2WrappedBlocks", func(fields map[string]interface{}) ([]*bridgeTypes.WrappedBlock, error) {
|
||||
hash, _ := fields["hash"].(string)
|
||||
if hash == "blockWithLongData" {
|
||||
longData := strings.Repeat("0", 1000)
|
||||
return []*bridgeTypes.WrappedBlock{{
|
||||
Transactions: []*gethTtypes.TransactionData{{
|
||||
Data: longData,
|
||||
}},
|
||||
}}, nil
|
||||
}
|
||||
return []*bridgeTypes.WrappedBlock{{
|
||||
Transactions: []*gethTtypes.TransactionData{{
|
||||
Data: "short",
|
||||
}},
|
||||
}}, nil
|
||||
})
|
||||
defer patchGuard.Reset()
|
||||
patchGuard.ApplyPrivateMethod(p, "createBatchForBlocks", func(*BatchProposer, []*types.BlockInfo) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
bp := NewBatchProposer(context.Background(), &config.BatchProposerConfig{
|
||||
MaxChunkNumPerBatch: 10,
|
||||
MaxL1CommitGasPerBatch: 50000000000,
|
||||
MaxL1CommitCalldataSizePerBatch: 1000000,
|
||||
MinChunkNumPerBatch: 1,
|
||||
BatchTimeoutSec: 300,
|
||||
}, db)
|
||||
bp.TryProposeBatch()
|
||||
block1 := orm.BlockTrace{Number: 1, GasUsed: 100, TxNum: 1, BlockTimestamp: uint64(time.Now().Unix()) - 200}
|
||||
block2 := orm.BlockTrace{Number: 2, GasUsed: 200, TxNum: 2, BlockTimestamp: uint64(time.Now().Unix())}
|
||||
block3 := orm.BlockTrace{Number: 3, GasUsed: 300, TxNum: 11, BlockTimestamp: uint64(time.Now().Unix())}
|
||||
block4 := orm.BlockTrace{Number: 4, GasUsed: 1001, TxNum: 3, BlockTimestamp: uint64(time.Now().Unix())}
|
||||
blockOutdated := orm.BlockTrace{Number: 1, GasUsed: 100, TxNum: 1, BlockTimestamp: uint64(time.Now().Add(-400 * time.Second).Unix())}
|
||||
blockWithLongData := orm.BlockTrace{Hash: "blockWithLongData", Number: 5, GasUsed: 500, TxNum: 1, BlockTimestamp: uint64(time.Now().Unix())}
|
||||
|
||||
chunkOrm := orm.NewChunk(db)
|
||||
chunks, err := chunkOrm.GetUnbatchedChunks(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, chunks)
|
||||
testCases := []struct {
|
||||
description string
|
||||
blocks []orm.BlockTrace
|
||||
expectedRes bool
|
||||
}{
|
||||
{"Empty block list", []orm.BlockTrace{}, false},
|
||||
{"Single block exceeding gas threshold", []orm.BlockTrace{block4}, true},
|
||||
{"Single block exceeding transaction number threshold", []orm.BlockTrace{block3}, true},
|
||||
{"Multiple blocks meeting thresholds", []orm.BlockTrace{block1, block2, block3}, true},
|
||||
{"Multiple blocks not meeting thresholds", []orm.BlockTrace{block1, block2}, false},
|
||||
{"Outdated and valid block", []orm.BlockTrace{blockOutdated, block2}, true},
|
||||
{"Single block with long data", []orm.BlockTrace{blockWithLongData}, true},
|
||||
}
|
||||
|
||||
batchOrm := orm.NewBatch(db)
|
||||
// get all batches.
|
||||
batches, err := batchOrm.GetBatches(context.Background(), map[string]interface{}{}, []string{}, 0)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, batches, 1)
|
||||
assert.Equal(t, uint64(0), batches[0].StartChunkIndex)
|
||||
assert.Equal(t, uint64(0), batches[0].EndChunkIndex)
|
||||
assert.Equal(t, types.RollupPending, types.RollupStatus(batches[0].RollupStatus))
|
||||
assert.Equal(t, types.ProvingTaskUnassigned, types.ProvingStatus(batches[0].ProvingStatus))
|
||||
|
||||
dbChunks, err := chunkOrm.GetChunksInRange(context.Background(), 0, 0)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, batches, 1)
|
||||
assert.Equal(t, batches[0].Hash, dbChunks[0].BatchHash)
|
||||
assert.Equal(t, types.ProvingTaskUnassigned, types.ProvingStatus(dbChunks[0].ProvingStatus))
|
||||
|
||||
blockOrm := orm.NewL2Block(db)
|
||||
blocks, err := blockOrm.GetL2Blocks(context.Background(), map[string]interface{}{}, []string{}, 0)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, blocks, 2)
|
||||
assert.Equal(t, dbChunks[0].Hash, blocks[0].ChunkHash)
|
||||
assert.Equal(t, dbChunks[0].Hash, blocks[1].ChunkHash)
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
assert.Equal(t, tc.expectedRes, p.proposeBatch(tc.blocks), "Failed on: %s", tc.description)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testBatchProposerBatchGeneration(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
subCtx, cancel := context.WithCancel(context.Background())
|
||||
defer func() {
|
||||
bridgeUtils.CloseDB(db)
|
||||
cancel()
|
||||
}()
|
||||
blockTraceOrm := orm.NewBlockTrace(db)
|
||||
// Insert traces into db.
|
||||
assert.NoError(t, blockTraceOrm.InsertWrappedBlocks([]*bridgeTypes.WrappedBlock{wrappedBlock1}))
|
||||
|
||||
l2cfg := cfg.L2Config
|
||||
wc := NewL2WatcherClient(context.Background(), l2Cli, l2cfg.Confirmations, l2cfg.L2MessengerAddress, l2cfg.L2MessageQueueAddress, l2cfg.WithdrawTrieRootSlot, db)
|
||||
loopToFetchEvent(subCtx, wc)
|
||||
|
||||
blockBatchOrm := orm.NewBlockBatch(db)
|
||||
batch, err := blockBatchOrm.GetLatestBatch()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Create a new batch.
|
||||
batchData := bridgeTypes.NewBatchData(&bridgeTypes.BatchInfo{
|
||||
Index: 0,
|
||||
Hash: batch.Hash,
|
||||
StateRoot: batch.StateRoot,
|
||||
}, []*bridgeTypes.WrappedBlock{wrappedBlock1}, nil)
|
||||
|
||||
relayer, err := relayer.NewLayer2Relayer(context.Background(), l2Cli, db, cfg.L2Config.RelayerConfig)
|
||||
assert.NoError(t, err)
|
||||
|
||||
proposer := NewBatchProposer(context.Background(), &config.BatchProposerConfig{
|
||||
ProofGenerationFreq: 1,
|
||||
BatchGasThreshold: 3000000,
|
||||
BatchTxNumThreshold: 135,
|
||||
BatchTimeSec: 1,
|
||||
BatchBlocksLimit: 100,
|
||||
}, relayer, db)
|
||||
proposer.TryProposeBatch()
|
||||
|
||||
infos, err := blockTraceOrm.GetUnbatchedL2Blocks(map[string]interface{}{}, []string{"number ASC"}, 100)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, len(infos))
|
||||
|
||||
batches, err := blockBatchOrm.GetBlockBatches(map[string]interface{}{"hash": batchData.Hash().Hex()}, nil, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(batches))
|
||||
}
|
||||
|
||||
func testBatchProposerGracefulRestart(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer bridgeUtils.CloseDB(db)
|
||||
|
||||
relayer, err := relayer.NewLayer2Relayer(context.Background(), l2Cli, db, cfg.L2Config.RelayerConfig)
|
||||
assert.NoError(t, err)
|
||||
|
||||
blockTraceOrm := orm.NewBlockTrace(db)
|
||||
// Insert traces into db.
|
||||
assert.NoError(t, blockTraceOrm.InsertWrappedBlocks([]*bridgeTypes.WrappedBlock{wrappedBlock2}))
|
||||
|
||||
// Insert block batch into db.
|
||||
parentBatch1 := &bridgeTypes.BatchInfo{
|
||||
Index: 0,
|
||||
Hash: common.Hash{}.String(),
|
||||
StateRoot: common.Hash{}.String(),
|
||||
}
|
||||
batchData1 := bridgeTypes.NewBatchData(parentBatch1, []*bridgeTypes.WrappedBlock{wrappedBlock1}, nil)
|
||||
|
||||
parentBatch2 := &bridgeTypes.BatchInfo{
|
||||
Index: batchData1.Batch.BatchIndex,
|
||||
Hash: batchData1.Hash().Hex(),
|
||||
StateRoot: batchData1.Batch.NewStateRoot.String(),
|
||||
}
|
||||
batchData2 := bridgeTypes.NewBatchData(parentBatch2, []*bridgeTypes.WrappedBlock{wrappedBlock2}, nil)
|
||||
|
||||
blockBatchOrm := orm.NewBlockBatch(db)
|
||||
err = db.Transaction(func(tx *gorm.DB) error {
|
||||
_, dbTxErr := blockBatchOrm.InsertBlockBatchByBatchData(tx, batchData1)
|
||||
if dbTxErr != nil {
|
||||
return dbTxErr
|
||||
}
|
||||
_, dbTxErr = blockBatchOrm.InsertBlockBatchByBatchData(tx, batchData2)
|
||||
if dbTxErr != nil {
|
||||
return dbTxErr
|
||||
}
|
||||
numbers1 := []uint64{batchData1.Batch.Blocks[0].BlockNumber}
|
||||
hash1 := batchData1.Hash().Hex()
|
||||
dbTxErr = blockTraceOrm.UpdateBatchHashForL2Blocks(tx, numbers1, hash1)
|
||||
if dbTxErr != nil {
|
||||
return dbTxErr
|
||||
}
|
||||
numbers2 := []uint64{batchData2.Batch.Blocks[0].BlockNumber}
|
||||
hash2 := batchData2.Hash().Hex()
|
||||
dbTxErr = blockTraceOrm.UpdateBatchHashForL2Blocks(tx, numbers2, hash2)
|
||||
if dbTxErr != nil {
|
||||
return dbTxErr
|
||||
}
|
||||
return nil
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
err = blockBatchOrm.UpdateRollupStatus(context.Background(), batchData1.Hash().Hex(), types.RollupFinalized)
|
||||
assert.NoError(t, err)
|
||||
batchHashes, err := blockBatchOrm.GetBlockBatchesHashByRollupStatus(types.RollupPending, math.MaxInt32)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(batchHashes))
|
||||
assert.Equal(t, batchData2.Hash().Hex(), batchHashes[0])
|
||||
// test p.recoverBatchDataBuffer().
|
||||
_ = NewBatchProposer(context.Background(), &config.BatchProposerConfig{
|
||||
ProofGenerationFreq: 1,
|
||||
BatchGasThreshold: 3000000,
|
||||
BatchTxNumThreshold: 135,
|
||||
BatchTimeSec: 1,
|
||||
BatchBlocksLimit: 100,
|
||||
}, relayer, db)
|
||||
|
||||
batchHashes, err = blockBatchOrm.GetBlockBatchesHashByRollupStatus(types.RollupPending, math.MaxInt32)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, len(batchHashes))
|
||||
|
||||
batches, err := blockBatchOrm.GetBlockBatches(map[string]interface{}{"hash": batchData2.Hash().Hex()}, nil, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(batches))
|
||||
}
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
package watcher
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/log"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"scroll-tech/bridge/internal/config"
|
||||
"scroll-tech/bridge/internal/orm"
|
||||
bridgeTypes "scroll-tech/bridge/internal/types"
|
||||
)
|
||||
|
||||
// ChunkProposer proposes chunks based on available unchunked blocks.
|
||||
type ChunkProposer struct {
|
||||
ctx context.Context
|
||||
db *gorm.DB
|
||||
|
||||
chunkOrm *orm.Chunk
|
||||
l2BlockOrm *orm.L2Block
|
||||
|
||||
maxTxGasPerChunk uint64
|
||||
maxL2TxNumPerChunk uint64
|
||||
maxL1CommitGasPerChunk uint64
|
||||
maxL1CommitCalldataSizePerChunk uint64
|
||||
minL1CommitCalldataSizePerChunk uint64
|
||||
chunkTimeoutSec uint64
|
||||
}
|
||||
|
||||
// NewChunkProposer creates a new ChunkProposer instance.
|
||||
func NewChunkProposer(ctx context.Context, cfg *config.ChunkProposerConfig, db *gorm.DB) *ChunkProposer {
|
||||
return &ChunkProposer{
|
||||
ctx: ctx,
|
||||
db: db,
|
||||
chunkOrm: orm.NewChunk(db),
|
||||
l2BlockOrm: orm.NewL2Block(db),
|
||||
maxTxGasPerChunk: cfg.MaxTxGasPerChunk,
|
||||
maxL2TxNumPerChunk: cfg.MaxL2TxNumPerChunk,
|
||||
maxL1CommitGasPerChunk: cfg.MaxL1CommitGasPerChunk,
|
||||
maxL1CommitCalldataSizePerChunk: cfg.MaxL1CommitCalldataSizePerChunk,
|
||||
minL1CommitCalldataSizePerChunk: cfg.MinL1CommitCalldataSizePerChunk,
|
||||
chunkTimeoutSec: cfg.ChunkTimeoutSec,
|
||||
}
|
||||
}
|
||||
|
||||
// TryProposeChunk tries to propose a new chunk.
|
||||
func (p *ChunkProposer) TryProposeChunk() {
|
||||
proposedChunk, err := p.proposeChunk()
|
||||
if err != nil {
|
||||
log.Error("propose new chunk failed", "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := p.updateChunkInfoInDB(proposedChunk); err != nil {
|
||||
log.Error("update chunk info in orm failed", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ChunkProposer) updateChunkInfoInDB(chunk *bridgeTypes.Chunk) error {
|
||||
if chunk == nil {
|
||||
log.Warn("proposed chunk is nil, cannot update in DB")
|
||||
return nil
|
||||
}
|
||||
|
||||
err := p.db.Transaction(func(dbTX *gorm.DB) error {
|
||||
dbChunk, err := p.chunkOrm.InsertChunk(p.ctx, chunk, dbTX)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.l2BlockOrm.UpdateChunkHashInRange(p.ctx, dbChunk.StartBlockNumber, dbChunk.EndBlockNumber, dbChunk.Hash, dbTX); err != nil {
|
||||
log.Error("failed to update chunk_hash for l2_blocks", "chunk hash", chunk.Hash, "start block", 0, "end block", 0, "err", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *ChunkProposer) proposeChunk() (*bridgeTypes.Chunk, error) {
|
||||
blocks, err := p.l2BlockOrm.GetUnchunkedBlocks(p.ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(blocks) == 0 {
|
||||
log.Warn("no un-chunked blocks")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
firstBlock := blocks[0]
|
||||
totalTxGasUsed := firstBlock.Header.GasUsed
|
||||
totalL2TxNum := firstBlock.L2TxsNum()
|
||||
totalL1CommitCalldataSize := firstBlock.EstimateL1CommitCalldataSize()
|
||||
totalL1CommitGas := firstBlock.EstimateL1CommitGas()
|
||||
|
||||
// Check if the first block breaks hard limits.
|
||||
// If so, it indicates there are bugs in sequencer, manual fix is needed.
|
||||
if totalL2TxNum > p.maxL2TxNumPerChunk {
|
||||
return nil, fmt.Errorf(
|
||||
"the first block exceeds l2 tx number limit; block number: %v, number of transactions: %v, max transaction number limit: %v",
|
||||
firstBlock.Header.Number,
|
||||
totalL2TxNum,
|
||||
p.maxL2TxNumPerChunk,
|
||||
)
|
||||
}
|
||||
|
||||
if totalL1CommitGas > p.maxL1CommitGasPerChunk {
|
||||
return nil, fmt.Errorf(
|
||||
"the first block exceeds l1 commit gas limit; block number: %v, commit gas: %v, max commit gas limit: %v",
|
||||
firstBlock.Header.Number,
|
||||
totalL1CommitGas,
|
||||
p.maxL1CommitGasPerChunk,
|
||||
)
|
||||
}
|
||||
|
||||
if totalL1CommitCalldataSize > p.maxL1CommitCalldataSizePerChunk {
|
||||
return nil, fmt.Errorf(
|
||||
"the first block exceeds l1 commit calldata size limit; block number: %v, calldata size: %v, max calldata size limit: %v",
|
||||
firstBlock.Header.Number,
|
||||
totalL1CommitCalldataSize,
|
||||
p.maxL1CommitCalldataSizePerChunk,
|
||||
)
|
||||
}
|
||||
|
||||
// Check if the first block breaks any soft limits.
|
||||
if totalTxGasUsed > p.maxTxGasPerChunk {
|
||||
log.Warn(
|
||||
"The first block in chunk exceeds l2 tx gas limit",
|
||||
"block number", firstBlock.Header.Number,
|
||||
"gas used", totalTxGasUsed,
|
||||
"max gas limit", p.maxTxGasPerChunk,
|
||||
)
|
||||
}
|
||||
|
||||
for i, block := range blocks[1:] {
|
||||
totalTxGasUsed += block.Header.GasUsed
|
||||
totalL2TxNum += block.L2TxsNum()
|
||||
totalL1CommitCalldataSize += block.EstimateL1CommitCalldataSize()
|
||||
totalL1CommitGas += block.EstimateL1CommitGas()
|
||||
if totalTxGasUsed > p.maxTxGasPerChunk ||
|
||||
totalL2TxNum > p.maxL2TxNumPerChunk ||
|
||||
totalL1CommitCalldataSize > p.maxL1CommitCalldataSizePerChunk ||
|
||||
totalL1CommitGas > p.maxL1CommitGasPerChunk {
|
||||
blocks = blocks[:i+1]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var hasBlockTimeout bool
|
||||
currentTimeSec := uint64(time.Now().Unix())
|
||||
if blocks[0].Header.Time+p.chunkTimeoutSec < currentTimeSec {
|
||||
log.Warn("first block timeout",
|
||||
"block number", blocks[0].Header.Number,
|
||||
"block timestamp", blocks[0].Header.Time,
|
||||
"block outdated time threshold", currentTimeSec,
|
||||
)
|
||||
hasBlockTimeout = true
|
||||
}
|
||||
|
||||
if !hasBlockTimeout && totalL1CommitCalldataSize < p.minL1CommitCalldataSizePerChunk {
|
||||
log.Warn("The calldata size of the chunk is less than the minimum limit",
|
||||
"totalL1CommitCalldataSize", totalL1CommitCalldataSize,
|
||||
"minL1CommitCalldataSizePerChunk", p.minL1CommitCalldataSizePerChunk,
|
||||
)
|
||||
return nil, nil
|
||||
}
|
||||
return &bridgeTypes.Chunk{Blocks: blocks}, nil
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package watcher
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"scroll-tech/bridge/internal/config"
|
||||
"scroll-tech/bridge/internal/orm"
|
||||
bridgeTypes "scroll-tech/bridge/internal/types"
|
||||
"scroll-tech/bridge/internal/utils"
|
||||
)
|
||||
|
||||
// TODO: Add unit tests that the limits are enforced correctly.
|
||||
func testChunkProposer(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer utils.CloseDB(db)
|
||||
|
||||
l2BlockOrm := orm.NewL2Block(db)
|
||||
err := l2BlockOrm.InsertL2Blocks(context.Background(), []*bridgeTypes.WrappedBlock{wrappedBlock1, wrappedBlock2})
|
||||
assert.NoError(t, err)
|
||||
|
||||
cp := NewChunkProposer(context.Background(), &config.ChunkProposerConfig{
|
||||
MaxTxGasPerChunk: 1000000000,
|
||||
MaxL2TxNumPerChunk: 10000,
|
||||
MaxL1CommitGasPerChunk: 50000000000,
|
||||
MaxL1CommitCalldataSizePerChunk: 1000000,
|
||||
MinL1CommitCalldataSizePerChunk: 0,
|
||||
ChunkTimeoutSec: 300,
|
||||
}, db)
|
||||
cp.TryProposeChunk()
|
||||
|
||||
expectedChunk := &bridgeTypes.Chunk{
|
||||
Blocks: []*bridgeTypes.WrappedBlock{wrappedBlock1, wrappedBlock2},
|
||||
}
|
||||
expectedHash, err := expectedChunk.Hash(0)
|
||||
assert.NoError(t, err)
|
||||
|
||||
chunkOrm := orm.NewChunk(db)
|
||||
chunks, err := chunkOrm.GetUnbatchedChunks(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, chunks, 1)
|
||||
assert.Equal(t, expectedHash.Hex(), chunks[0].Hash)
|
||||
}
|
||||
@@ -24,9 +24,10 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
bridgeL1MsgsSyncHeightGauge = gethMetrics.NewRegisteredGauge("bridge/l1/msgs/sync/height", metrics.ScrollRegistry)
|
||||
bridgeL1MsgsSentEventsTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l1/msgs/sent/events/total", metrics.ScrollRegistry)
|
||||
bridgeL1MsgsRollupEventsTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l1/msgs/rollup/events/total", metrics.ScrollRegistry)
|
||||
bridgeL1MsgsSyncHeightGauge = gethMetrics.NewRegisteredGauge("bridge/l1/msgs/sync/height", metrics.ScrollRegistry)
|
||||
bridgeL1MsgsSentEventsTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l1/msgs/sent/events/total", metrics.ScrollRegistry)
|
||||
bridgeL1MsgsRelayedEventsTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l1/msgs/relayed/events/total", metrics.ScrollRegistry)
|
||||
bridgeL1MsgsRollupEventsTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l1/msgs/rollup/events/total", metrics.ScrollRegistry)
|
||||
)
|
||||
|
||||
type rollupEvent struct {
|
||||
@@ -40,8 +41,9 @@ type L1WatcherClient struct {
|
||||
ctx context.Context
|
||||
client *ethclient.Client
|
||||
l1MessageOrm *orm.L1Message
|
||||
l2MessageOrm *orm.L2Message
|
||||
l1BlockOrm *orm.L1Block
|
||||
batchOrm *orm.Batch
|
||||
l1BatchOrm *orm.BlockBatch
|
||||
|
||||
// The number of new blocks to wait for a block to be confirmed
|
||||
confirmations rpc.BlockNumber
|
||||
@@ -88,7 +90,8 @@ func NewL1WatcherClient(ctx context.Context, client *ethclient.Client, startHeig
|
||||
client: client,
|
||||
l1MessageOrm: l1MessageOrm,
|
||||
l1BlockOrm: l1BlockOrm,
|
||||
batchOrm: orm.NewBatch(db),
|
||||
l1BatchOrm: orm.NewBlockBatch(db),
|
||||
l2MessageOrm: orm.NewL2Message(db),
|
||||
confirmations: confirmations,
|
||||
|
||||
messengerAddress: messengerAddress,
|
||||
@@ -224,23 +227,25 @@ func (w *L1WatcherClient) FetchContractEvent() error {
|
||||
}
|
||||
log.Info("Received new L1 events", "fromBlock", from, "toBlock", to, "cnt", len(logs))
|
||||
|
||||
sentMessageEvents, rollupEvents, err := w.parseBridgeEventLogs(logs)
|
||||
sentMessageEvents, relayedMessageEvents, rollupEvents, err := w.parseBridgeEventLogs(logs)
|
||||
if err != nil {
|
||||
log.Error("Failed to parse emitted events log", "err", err)
|
||||
return err
|
||||
}
|
||||
sentMessageCount := int64(len(sentMessageEvents))
|
||||
relayedMessageCount := int64(len(relayedMessageEvents))
|
||||
rollupEventCount := int64(len(rollupEvents))
|
||||
bridgeL1MsgsSentEventsTotalCounter.Inc(sentMessageCount)
|
||||
bridgeL1MsgsRelayedEventsTotalCounter.Inc(relayedMessageCount)
|
||||
bridgeL1MsgsRollupEventsTotalCounter.Inc(rollupEventCount)
|
||||
log.Info("L1 events types", "SentMessageCount", sentMessageCount, "RollupEventCount", rollupEventCount)
|
||||
log.Info("L1 events types", "SentMessageCount", sentMessageCount, "RelayedMessageCount", relayedMessageCount, "RollupEventCount", rollupEventCount)
|
||||
|
||||
// use rollup event to update rollup results db status
|
||||
var batchHashes []string
|
||||
for _, event := range rollupEvents {
|
||||
batchHashes = append(batchHashes, event.batchHash.String())
|
||||
}
|
||||
statuses, err := w.batchOrm.GetRollupStatusByHashList(w.ctx, batchHashes)
|
||||
statuses, err := w.l1BatchOrm.GetRollupStatusByHashList(batchHashes)
|
||||
if err != nil {
|
||||
log.Error("Failed to GetRollupStatusByHashList", "err", err)
|
||||
return err
|
||||
@@ -256,9 +261,9 @@ func (w *L1WatcherClient) FetchContractEvent() error {
|
||||
// only update when db status is before event status
|
||||
if event.status > status {
|
||||
if event.status == types.RollupFinalized {
|
||||
err = w.batchOrm.UpdateFinalizeTxHashAndRollupStatus(w.ctx, batchHash, event.txHash.String(), event.status)
|
||||
err = w.l1BatchOrm.UpdateFinalizeTxHashAndRollupStatus(w.ctx, batchHash, event.txHash.String(), event.status)
|
||||
} else if event.status == types.RollupCommitted {
|
||||
err = w.batchOrm.UpdateCommitTxHashAndRollupStatus(w.ctx, batchHash, event.txHash.String(), event.status)
|
||||
err = w.l1BatchOrm.UpdateCommitTxHashAndRollupStatus(w.ctx, batchHash, event.txHash.String(), event.status)
|
||||
}
|
||||
if err != nil {
|
||||
log.Error("Failed to update Rollup/Finalize TxHash and Status", "err", err)
|
||||
@@ -267,6 +272,21 @@ func (w *L1WatcherClient) FetchContractEvent() error {
|
||||
}
|
||||
}
|
||||
|
||||
// Update relayed message first to make sure we don't forget to update submitted message.
|
||||
// Since, we always start sync from the latest unprocessed message.
|
||||
for _, msg := range relayedMessageEvents {
|
||||
var msgStatus types.MsgStatus
|
||||
if msg.isSuccessful {
|
||||
msgStatus = types.MsgConfirmed
|
||||
} else {
|
||||
msgStatus = types.MsgFailed
|
||||
}
|
||||
if err = w.l2MessageOrm.UpdateLayer2StatusAndLayer1Hash(w.ctx, msg.msgHash.String(), msgStatus, msg.txHash.String()); err != nil {
|
||||
log.Error("Failed to update layer1 status and layer2 hash", "err", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err = w.l1MessageOrm.SaveL1Messages(w.ctx, sentMessageEvents); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -278,10 +298,11 @@ func (w *L1WatcherClient) FetchContractEvent() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *L1WatcherClient) parseBridgeEventLogs(logs []gethTypes.Log) ([]*orm.L1Message, []rollupEvent, error) {
|
||||
func (w *L1WatcherClient) parseBridgeEventLogs(logs []gethTypes.Log) ([]*orm.L1Message, []relayedMessage, []rollupEvent, error) {
|
||||
// Need use contract abi to parse event Log
|
||||
// Can only be tested after we have our contracts set up
|
||||
var l1Messages []*orm.L1Message
|
||||
var relayedMessages []relayedMessage
|
||||
var rollupEvents []rollupEvent
|
||||
for _, vLog := range logs {
|
||||
switch vLog.Topics[0] {
|
||||
@@ -290,13 +311,13 @@ func (w *L1WatcherClient) parseBridgeEventLogs(logs []gethTypes.Log) ([]*orm.L1M
|
||||
err := utils.UnpackLog(w.messageQueueABI, &event, "QueueTransaction", vLog)
|
||||
if err != nil {
|
||||
log.Warn("Failed to unpack layer1 QueueTransaction event", "err", err)
|
||||
return l1Messages, rollupEvents, err
|
||||
return l1Messages, relayedMessages, rollupEvents, err
|
||||
}
|
||||
|
||||
msgHash := common.BytesToHash(crypto.Keccak256(event.Data))
|
||||
|
||||
l1Messages = append(l1Messages, &orm.L1Message{
|
||||
QueueIndex: event.QueueIndex,
|
||||
QueueIndex: event.QueueIndex.Uint64(),
|
||||
MsgHash: msgHash.String(),
|
||||
Height: vLog.BlockNumber,
|
||||
Sender: event.Sender.String(),
|
||||
@@ -306,12 +327,38 @@ func (w *L1WatcherClient) parseBridgeEventLogs(logs []gethTypes.Log) ([]*orm.L1M
|
||||
GasLimit: event.GasLimit.Uint64(),
|
||||
Layer1Hash: vLog.TxHash.Hex(),
|
||||
})
|
||||
case bridgeAbi.L1RelayedMessageEventSignature:
|
||||
event := bridgeAbi.L1RelayedMessageEvent{}
|
||||
err := utils.UnpackLog(w.messengerABI, &event, "RelayedMessage", vLog)
|
||||
if err != nil {
|
||||
log.Warn("Failed to unpack layer1 RelayedMessage event", "err", err)
|
||||
return l1Messages, relayedMessages, rollupEvents, err
|
||||
}
|
||||
|
||||
relayedMessages = append(relayedMessages, relayedMessage{
|
||||
msgHash: event.MessageHash,
|
||||
txHash: vLog.TxHash,
|
||||
isSuccessful: true,
|
||||
})
|
||||
case bridgeAbi.L1FailedRelayedMessageEventSignature:
|
||||
event := bridgeAbi.L1FailedRelayedMessageEvent{}
|
||||
err := utils.UnpackLog(w.messengerABI, &event, "FailedRelayedMessage", vLog)
|
||||
if err != nil {
|
||||
log.Warn("Failed to unpack layer1 FailedRelayedMessage event", "err", err)
|
||||
return l1Messages, relayedMessages, rollupEvents, err
|
||||
}
|
||||
|
||||
relayedMessages = append(relayedMessages, relayedMessage{
|
||||
msgHash: event.MessageHash,
|
||||
txHash: vLog.TxHash,
|
||||
isSuccessful: false,
|
||||
})
|
||||
case bridgeAbi.L1CommitBatchEventSignature:
|
||||
event := bridgeAbi.L1CommitBatchEvent{}
|
||||
err := utils.UnpackLog(w.scrollChainABI, &event, "CommitBatch", vLog)
|
||||
if err != nil {
|
||||
log.Warn("Failed to unpack layer1 CommitBatch event", "err", err)
|
||||
return l1Messages, rollupEvents, err
|
||||
return l1Messages, relayedMessages, rollupEvents, err
|
||||
}
|
||||
|
||||
rollupEvents = append(rollupEvents, rollupEvent{
|
||||
@@ -324,7 +371,7 @@ func (w *L1WatcherClient) parseBridgeEventLogs(logs []gethTypes.Log) ([]*orm.L1M
|
||||
err := utils.UnpackLog(w.scrollChainABI, &event, "FinalizeBatch", vLog)
|
||||
if err != nil {
|
||||
log.Warn("Failed to unpack layer1 FinalizeBatch event", "err", err)
|
||||
return l1Messages, rollupEvents, err
|
||||
return l1Messages, relayedMessages, rollupEvents, err
|
||||
}
|
||||
|
||||
rollupEvents = append(rollupEvents, rollupEvent{
|
||||
@@ -337,5 +384,5 @@ func (w *L1WatcherClient) parseBridgeEventLogs(logs []gethTypes.Log) ([]*orm.L1M
|
||||
}
|
||||
}
|
||||
|
||||
return l1Messages, rollupEvents, nil
|
||||
return l1Messages, relayedMessages, rollupEvents, nil
|
||||
}
|
||||
|
||||
@@ -159,14 +159,14 @@ func testL1WatcherClientFetchContractEvent(t *testing.T) {
|
||||
|
||||
convey.Convey("parse bridge event logs failure", t, func() {
|
||||
targetErr := errors.New("parse log failure")
|
||||
patchGuard.ApplyPrivateMethod(watcher, "parseBridgeEventLogs", func(*L1WatcherClient, []types.Log) ([]*orm.L1Message, []rollupEvent, error) {
|
||||
return nil, nil, targetErr
|
||||
patchGuard.ApplyPrivateMethod(watcher, "parseBridgeEventLogs", func(*L1WatcherClient, []types.Log) ([]*orm.L1Message, []relayedMessage, []rollupEvent, error) {
|
||||
return nil, nil, nil, targetErr
|
||||
})
|
||||
err := watcher.FetchContractEvent()
|
||||
assert.EqualError(t, err, targetErr.Error())
|
||||
assert.Equal(t, err.Error(), targetErr.Error())
|
||||
})
|
||||
|
||||
patchGuard.ApplyPrivateMethod(watcher, "parseBridgeEventLogs", func(*L1WatcherClient, []types.Log) ([]*orm.L1Message, []rollupEvent, error) {
|
||||
patchGuard.ApplyPrivateMethod(watcher, "parseBridgeEventLogs", func(*L1WatcherClient, []types.Log) ([]*orm.L1Message, []relayedMessage, []rollupEvent, error) {
|
||||
rollupEvents := []rollupEvent{
|
||||
{
|
||||
batchHash: common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"),
|
||||
@@ -179,13 +179,26 @@ func testL1WatcherClientFetchContractEvent(t *testing.T) {
|
||||
status: commonTypes.RollupCommitted,
|
||||
},
|
||||
}
|
||||
return nil, rollupEvents, nil
|
||||
|
||||
relayedMessageEvents := []relayedMessage{
|
||||
{
|
||||
msgHash: common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"),
|
||||
txHash: common.HexToHash("0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5"),
|
||||
isSuccessful: true,
|
||||
},
|
||||
{
|
||||
msgHash: common.HexToHash("0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5"),
|
||||
txHash: common.HexToHash("0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30"),
|
||||
isSuccessful: false,
|
||||
},
|
||||
}
|
||||
return nil, relayedMessageEvents, rollupEvents, nil
|
||||
})
|
||||
|
||||
var batchOrm *orm.Batch
|
||||
var blockBatchOrm *orm.BlockBatch
|
||||
convey.Convey("db get rollup status by hash list failure", t, func() {
|
||||
targetErr := errors.New("get db failure")
|
||||
patchGuard.ApplyMethodFunc(batchOrm, "GetRollupStatusByHashList", func(context.Context, []string) ([]commonTypes.RollupStatus, error) {
|
||||
patchGuard.ApplyMethodFunc(blockBatchOrm, "GetRollupStatusByHashList", func(hashes []string) ([]commonTypes.RollupStatus, error) {
|
||||
return nil, targetErr
|
||||
})
|
||||
err := watcher.FetchContractEvent()
|
||||
@@ -193,7 +206,7 @@ func testL1WatcherClientFetchContractEvent(t *testing.T) {
|
||||
})
|
||||
|
||||
convey.Convey("rollup status mismatch batch hashes length", t, func() {
|
||||
patchGuard.ApplyMethodFunc(batchOrm, "GetRollupStatusByHashList", func(context.Context, []string) ([]commonTypes.RollupStatus, error) {
|
||||
patchGuard.ApplyMethodFunc(blockBatchOrm, "GetRollupStatusByHashList", func(hashes []string) ([]commonTypes.RollupStatus, error) {
|
||||
s := []commonTypes.RollupStatus{
|
||||
commonTypes.RollupFinalized,
|
||||
}
|
||||
@@ -203,7 +216,7 @@ func testL1WatcherClientFetchContractEvent(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
patchGuard.ApplyMethodFunc(batchOrm, "GetRollupStatusByHashList", func(context.Context, []string) ([]commonTypes.RollupStatus, error) {
|
||||
patchGuard.ApplyMethodFunc(blockBatchOrm, "GetRollupStatusByHashList", func(hashes []string) ([]commonTypes.RollupStatus, error) {
|
||||
s := []commonTypes.RollupStatus{
|
||||
commonTypes.RollupPending,
|
||||
commonTypes.RollupCommitting,
|
||||
@@ -213,27 +226,41 @@ func testL1WatcherClientFetchContractEvent(t *testing.T) {
|
||||
|
||||
convey.Convey("db update RollupFinalized status failure", t, func() {
|
||||
targetErr := errors.New("UpdateFinalizeTxHashAndRollupStatus RollupFinalized failure")
|
||||
patchGuard.ApplyMethodFunc(batchOrm, "UpdateFinalizeTxHashAndRollupStatus", func(context.Context, string, string, commonTypes.RollupStatus) error {
|
||||
patchGuard.ApplyMethodFunc(blockBatchOrm, "UpdateFinalizeTxHashAndRollupStatus", func(context.Context, string, string, commonTypes.RollupStatus) error {
|
||||
return targetErr
|
||||
})
|
||||
err := watcher.FetchContractEvent()
|
||||
assert.Equal(t, targetErr.Error(), err.Error())
|
||||
})
|
||||
|
||||
patchGuard.ApplyMethodFunc(batchOrm, "UpdateFinalizeTxHashAndRollupStatus", func(context.Context, string, string, commonTypes.RollupStatus) error {
|
||||
patchGuard.ApplyMethodFunc(blockBatchOrm, "UpdateFinalizeTxHashAndRollupStatus", func(context.Context, string, string, commonTypes.RollupStatus) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
convey.Convey("db update RollupCommitted status failure", t, func() {
|
||||
targetErr := errors.New("UpdateCommitTxHashAndRollupStatus RollupCommitted failure")
|
||||
patchGuard.ApplyMethodFunc(batchOrm, "UpdateCommitTxHashAndRollupStatus", func(context.Context, string, string, commonTypes.RollupStatus) error {
|
||||
patchGuard.ApplyMethodFunc(blockBatchOrm, "UpdateCommitTxHashAndRollupStatus", func(context.Context, string, string, commonTypes.RollupStatus) error {
|
||||
return targetErr
|
||||
})
|
||||
err := watcher.FetchContractEvent()
|
||||
assert.Equal(t, targetErr.Error(), err.Error())
|
||||
})
|
||||
|
||||
patchGuard.ApplyMethodFunc(batchOrm, "UpdateCommitTxHashAndRollupStatus", func(context.Context, string, string, commonTypes.RollupStatus) error {
|
||||
patchGuard.ApplyMethodFunc(blockBatchOrm, "UpdateCommitTxHashAndRollupStatus", func(context.Context, string, string, commonTypes.RollupStatus) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
var l2MessageOrm *orm.L2Message
|
||||
convey.Convey("db update layer2 status and layer1 hash failure", t, func() {
|
||||
targetErr := errors.New("UpdateLayer2StatusAndLayer1Hash failure")
|
||||
patchGuard.ApplyMethodFunc(l2MessageOrm, "UpdateLayer2StatusAndLayer1Hash", func(context.Context, string, commonTypes.MsgStatus, string) error {
|
||||
return targetErr
|
||||
})
|
||||
err := watcher.FetchContractEvent()
|
||||
assert.Equal(t, targetErr.Error(), err.Error())
|
||||
})
|
||||
|
||||
patchGuard.ApplyMethodFunc(l2MessageOrm, "UpdateLayer2StatusAndLayer1Hash", func(context.Context, string, commonTypes.MsgStatus, string) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -276,16 +303,17 @@ func testParseBridgeEventLogsL1QueueTransactionEventSignature(t *testing.T) {
|
||||
})
|
||||
defer patchGuard.Reset()
|
||||
|
||||
l2Messages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
||||
l2Messages, relayedMessages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
||||
assert.EqualError(t, err, targetErr.Error())
|
||||
assert.Empty(t, l2Messages)
|
||||
assert.Empty(t, relayedMessages)
|
||||
assert.Empty(t, rollupEvents)
|
||||
})
|
||||
|
||||
convey.Convey("L1QueueTransactionEventSignature success", t, func() {
|
||||
patchGuard := gomonkey.ApplyFunc(utils.UnpackLog, func(c *abi.ABI, out interface{}, event string, log types.Log) error {
|
||||
tmpOut := out.(*bridgeAbi.L1QueueTransactionEvent)
|
||||
tmpOut.QueueIndex = 100
|
||||
tmpOut.QueueIndex = big.NewInt(100)
|
||||
tmpOut.Data = []byte("test data")
|
||||
tmpOut.Sender = common.HexToAddress("0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30")
|
||||
tmpOut.Value = big.NewInt(1000)
|
||||
@@ -295,14 +323,102 @@ func testParseBridgeEventLogsL1QueueTransactionEventSignature(t *testing.T) {
|
||||
})
|
||||
defer patchGuard.Reset()
|
||||
|
||||
l2Messages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
||||
l2Messages, relayedMessages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, relayedMessages)
|
||||
assert.Empty(t, rollupEvents)
|
||||
assert.Len(t, l2Messages, 1)
|
||||
assert.Equal(t, l2Messages[0].Value, big.NewInt(1000).String())
|
||||
})
|
||||
}
|
||||
|
||||
func testParseBridgeEventLogsL1RelayedMessageEventSignature(t *testing.T) {
|
||||
watcher, db := setupL1Watcher(t)
|
||||
defer utils.CloseDB(db)
|
||||
|
||||
logs := []types.Log{
|
||||
{
|
||||
Topics: []common.Hash{bridgeAbi.L1RelayedMessageEventSignature},
|
||||
BlockNumber: 100,
|
||||
TxHash: common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"),
|
||||
},
|
||||
}
|
||||
|
||||
convey.Convey("unpack RelayedMessage log failure", t, func() {
|
||||
targetErr := errors.New("UnpackLog RelayedMessage failure")
|
||||
patchGuard := gomonkey.ApplyFunc(utils.UnpackLog, func(c *abi.ABI, out interface{}, event string, log types.Log) error {
|
||||
return targetErr
|
||||
})
|
||||
defer patchGuard.Reset()
|
||||
|
||||
l2Messages, relayedMessages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
||||
assert.EqualError(t, err, targetErr.Error())
|
||||
assert.Empty(t, l2Messages)
|
||||
assert.Empty(t, relayedMessages)
|
||||
assert.Empty(t, rollupEvents)
|
||||
})
|
||||
|
||||
convey.Convey("L1RelayedMessageEventSignature success", t, func() {
|
||||
msgHash := common.HexToHash("0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5")
|
||||
patchGuard := gomonkey.ApplyFunc(utils.UnpackLog, func(c *abi.ABI, out interface{}, event string, log types.Log) error {
|
||||
tmpOut := out.(*bridgeAbi.L1RelayedMessageEvent)
|
||||
tmpOut.MessageHash = msgHash
|
||||
return nil
|
||||
})
|
||||
defer patchGuard.Reset()
|
||||
|
||||
l2Messages, relayedMessages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, l2Messages)
|
||||
assert.Empty(t, rollupEvents)
|
||||
assert.Len(t, relayedMessages, 1)
|
||||
assert.Equal(t, relayedMessages[0].msgHash, msgHash)
|
||||
})
|
||||
}
|
||||
|
||||
func testParseBridgeEventLogsL1FailedRelayedMessageEventSignature(t *testing.T) {
|
||||
watcher, db := setupL1Watcher(t)
|
||||
defer utils.CloseDB(db)
|
||||
logs := []types.Log{
|
||||
{
|
||||
Topics: []common.Hash{bridgeAbi.L1FailedRelayedMessageEventSignature},
|
||||
BlockNumber: 100,
|
||||
TxHash: common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"),
|
||||
},
|
||||
}
|
||||
|
||||
convey.Convey("unpack FailedRelayedMessage log failure", t, func() {
|
||||
targetErr := errors.New("UnpackLog FailedRelayedMessage failure")
|
||||
patchGuard := gomonkey.ApplyFunc(utils.UnpackLog, func(c *abi.ABI, out interface{}, event string, log types.Log) error {
|
||||
return targetErr
|
||||
})
|
||||
defer patchGuard.Reset()
|
||||
|
||||
l2Messages, relayedMessages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
||||
assert.EqualError(t, err, targetErr.Error())
|
||||
assert.Empty(t, l2Messages)
|
||||
assert.Empty(t, relayedMessages)
|
||||
assert.Empty(t, rollupEvents)
|
||||
})
|
||||
|
||||
convey.Convey("L1FailedRelayedMessageEventSignature success", t, func() {
|
||||
msgHash := common.HexToHash("0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5")
|
||||
patchGuard := gomonkey.ApplyFunc(utils.UnpackLog, func(c *abi.ABI, out interface{}, event string, log types.Log) error {
|
||||
tmpOut := out.(*bridgeAbi.L1FailedRelayedMessageEvent)
|
||||
tmpOut.MessageHash = msgHash
|
||||
return nil
|
||||
})
|
||||
defer patchGuard.Reset()
|
||||
|
||||
l2Messages, relayedMessages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, l2Messages)
|
||||
assert.Empty(t, rollupEvents)
|
||||
assert.Len(t, relayedMessages, 1)
|
||||
assert.Equal(t, relayedMessages[0].msgHash, msgHash)
|
||||
})
|
||||
}
|
||||
|
||||
func testParseBridgeEventLogsL1CommitBatchEventSignature(t *testing.T) {
|
||||
watcher, db := setupL1Watcher(t)
|
||||
defer utils.CloseDB(db)
|
||||
@@ -321,9 +437,10 @@ func testParseBridgeEventLogsL1CommitBatchEventSignature(t *testing.T) {
|
||||
})
|
||||
defer patchGuard.Reset()
|
||||
|
||||
l2Messages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
||||
l2Messages, relayedMessages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
||||
assert.EqualError(t, err, targetErr.Error())
|
||||
assert.Empty(t, l2Messages)
|
||||
assert.Empty(t, relayedMessages)
|
||||
assert.Empty(t, rollupEvents)
|
||||
})
|
||||
|
||||
@@ -336,9 +453,10 @@ func testParseBridgeEventLogsL1CommitBatchEventSignature(t *testing.T) {
|
||||
})
|
||||
defer patchGuard.Reset()
|
||||
|
||||
l2Messages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
||||
l2Messages, relayedMessages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, l2Messages)
|
||||
assert.Empty(t, relayedMessages)
|
||||
assert.Len(t, rollupEvents, 1)
|
||||
assert.Equal(t, rollupEvents[0].batchHash, msgHash)
|
||||
assert.Equal(t, rollupEvents[0].status, commonTypes.RollupCommitted)
|
||||
@@ -363,9 +481,10 @@ func testParseBridgeEventLogsL1FinalizeBatchEventSignature(t *testing.T) {
|
||||
})
|
||||
defer patchGuard.Reset()
|
||||
|
||||
l2Messages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
||||
l2Messages, relayedMessages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
||||
assert.EqualError(t, err, targetErr.Error())
|
||||
assert.Empty(t, l2Messages)
|
||||
assert.Empty(t, relayedMessages)
|
||||
assert.Empty(t, rollupEvents)
|
||||
})
|
||||
|
||||
@@ -378,9 +497,10 @@ func testParseBridgeEventLogsL1FinalizeBatchEventSignature(t *testing.T) {
|
||||
})
|
||||
defer patchGuard.Reset()
|
||||
|
||||
l2Messages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
||||
l2Messages, relayedMessages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, l2Messages)
|
||||
assert.Empty(t, relayedMessages)
|
||||
assert.Len(t, rollupEvents, 1)
|
||||
assert.Equal(t, rollupEvents[0].batchHash, msgHash)
|
||||
assert.Equal(t, rollupEvents[0].status, commonTypes.RollupFinalized)
|
||||
|
||||
@@ -2,6 +2,7 @@ package watcher
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
@@ -31,6 +32,8 @@ var (
|
||||
bridgeL2MsgsSyncHeightGauge = gethMetrics.NewRegisteredGauge("bridge/l2/msgs/sync/height", metrics.ScrollRegistry)
|
||||
bridgeL2BlocksFetchedHeightGauge = gethMetrics.NewRegisteredGauge("bridge/l2/blocks/fetched/height", metrics.ScrollRegistry)
|
||||
bridgeL2BlocksFetchedGapGauge = gethMetrics.NewRegisteredGauge("bridge/l2/blocks/fetched/gap", metrics.ScrollRegistry)
|
||||
bridgeL2MsgsSentEventsTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/msgs/sent/events/total", metrics.ScrollRegistry)
|
||||
bridgeL2MsgsAppendEventsTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/msgs/append/events/total", metrics.ScrollRegistry)
|
||||
bridgeL2MsgsRelayedEventsTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/msgs/relayed/events/total", metrics.ScrollRegistry)
|
||||
)
|
||||
|
||||
@@ -41,8 +44,11 @@ type L2WatcherClient struct {
|
||||
|
||||
*ethclient.Client
|
||||
|
||||
l2BlockOrm *orm.L2Block
|
||||
l1MessageOrm *orm.L1Message
|
||||
db *gorm.DB
|
||||
blockBatchOrm *orm.BlockBatch
|
||||
blockTraceOrm *orm.BlockTrace
|
||||
l1MessageOrm *orm.L1Message
|
||||
l2MessageOrm *orm.L2Message
|
||||
|
||||
confirmations rpc.BlockNumber
|
||||
|
||||
@@ -61,29 +67,23 @@ type L2WatcherClient struct {
|
||||
|
||||
// NewL2WatcherClient take a l2geth instance to generate a l2watcherclient instance
|
||||
func NewL2WatcherClient(ctx context.Context, client *ethclient.Client, confirmations rpc.BlockNumber, messengerAddress, messageQueueAddress common.Address, withdrawTrieRootSlot common.Hash, db *gorm.DB) *L2WatcherClient {
|
||||
l1MessageOrm := orm.NewL1Message(db)
|
||||
var savedHeight uint64
|
||||
l1msg, err := l1MessageOrm.GetLayer1LatestMessageWithLayer2Hash()
|
||||
if err != nil || l1msg == nil {
|
||||
l2MessageOrm := orm.NewL2Message(db)
|
||||
savedHeight, err := l2MessageOrm.GetLayer2LatestWatchedHeight()
|
||||
if err != nil {
|
||||
log.Warn("fetch height from db failed", "err", err)
|
||||
savedHeight = 0
|
||||
} else {
|
||||
receipt, err := client.TransactionReceipt(ctx, common.HexToHash(l1msg.Layer2Hash))
|
||||
if err != nil || receipt == nil {
|
||||
log.Warn("get tx from l2 failed", "err", err)
|
||||
savedHeight = 0
|
||||
} else {
|
||||
savedHeight = receipt.BlockNumber.Uint64()
|
||||
}
|
||||
}
|
||||
|
||||
w := L2WatcherClient{
|
||||
ctx: ctx,
|
||||
db: db,
|
||||
Client: client,
|
||||
|
||||
l2BlockOrm: orm.NewL2Block(db),
|
||||
blockBatchOrm: orm.NewBlockBatch(db),
|
||||
blockTraceOrm: orm.NewBlockTrace(db),
|
||||
l1MessageOrm: orm.NewL1Message(db),
|
||||
processedMsgHeight: savedHeight,
|
||||
l2MessageOrm: l2MessageOrm,
|
||||
processedMsgHeight: uint64(savedHeight),
|
||||
confirmations: confirmations,
|
||||
|
||||
messengerAddress: messengerAddress,
|
||||
@@ -96,28 +96,84 @@ func NewL2WatcherClient(ctx context.Context, client *ethclient.Client, confirmat
|
||||
stopped: 0,
|
||||
}
|
||||
|
||||
// Initialize genesis before we do anything else
|
||||
if err := w.initializeGenesis(); err != nil {
|
||||
panic(fmt.Sprintf("failed to initialize L2 genesis batch, err: %v", err))
|
||||
}
|
||||
|
||||
return &w
|
||||
}
|
||||
|
||||
func (w *L2WatcherClient) initializeGenesis() error {
|
||||
if count, err := w.blockBatchOrm.GetBatchCount(); err != nil {
|
||||
return fmt.Errorf("failed to get batch count: %v", err)
|
||||
} else if count > 0 {
|
||||
log.Info("genesis already imported")
|
||||
return nil
|
||||
}
|
||||
|
||||
genesis, err := w.HeaderByNumber(w.ctx, big.NewInt(0))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to retrieve L2 genesis header: %v", err)
|
||||
}
|
||||
|
||||
log.Info("retrieved L2 genesis header", "hash", genesis.Hash().String())
|
||||
|
||||
blockTrace := &bridgeTypes.WrappedBlock{
|
||||
Header: genesis,
|
||||
Transactions: nil,
|
||||
WithdrawTrieRoot: common.Hash{},
|
||||
}
|
||||
batchData := bridgeTypes.NewGenesisBatchData(blockTrace)
|
||||
|
||||
if err = orm.AddBatchInfoToDB(w.db, batchData); err != nil {
|
||||
log.Error("failed to add batch info to DB", "BatchHash", batchData.Hash(), "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
batchHash := batchData.Hash().Hex()
|
||||
|
||||
if err = w.blockBatchOrm.UpdateProvingStatus(batchHash, types.ProvingTaskProved); err != nil {
|
||||
return fmt.Errorf("failed to update genesis batch proving status: %v", err)
|
||||
}
|
||||
|
||||
if err = w.blockBatchOrm.UpdateRollupStatus(w.ctx, batchHash, types.RollupFinalized); err != nil {
|
||||
return fmt.Errorf("failed to update genesis batch rollup status: %v", err)
|
||||
}
|
||||
|
||||
log.Info("successfully imported genesis batch")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const blockTracesFetchLimit = uint64(10)
|
||||
|
||||
// TryFetchRunningMissingBlocks attempts to fetch and store block traces for any missing blocks.
|
||||
func (w *L2WatcherClient) TryFetchRunningMissingBlocks(blockHeight uint64) {
|
||||
heightInDB, err := w.l2BlockOrm.GetL2BlocksLatestHeight(w.ctx)
|
||||
// TryFetchRunningMissingBlocks try fetch missing blocks if inconsistent
|
||||
func (w *L2WatcherClient) TryFetchRunningMissingBlocks(ctx context.Context, blockHeight uint64) {
|
||||
// Get newest block in DB. must have blocks at that time.
|
||||
// Don't use "block_trace" table "trace" column's BlockTrace.Number,
|
||||
// because it might be empty if the corresponding rollup_result is finalized/finalization_skipped
|
||||
heightInDB, err := w.blockTraceOrm.GetL2BlocksLatestHeight()
|
||||
if err != nil {
|
||||
log.Error("failed to GetL2BlocksLatestHeight", "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Fetch and store block traces for missing blocks
|
||||
for from := uint64(heightInDB) + 1; from <= blockHeight; from += blockTracesFetchLimit {
|
||||
// Can't get trace from genesis block, so the default start number is 1.
|
||||
var from = uint64(1)
|
||||
if heightInDB > 0 {
|
||||
from = uint64(heightInDB) + 1
|
||||
}
|
||||
|
||||
for ; from <= blockHeight; from += blockTracesFetchLimit {
|
||||
to := from + blockTracesFetchLimit - 1
|
||||
|
||||
if to > blockHeight {
|
||||
to = blockHeight
|
||||
}
|
||||
|
||||
if err = w.getAndStoreBlockTraces(w.ctx, from, to); err != nil {
|
||||
// Get block traces and insert into db.
|
||||
if err = w.getAndStoreBlockTraces(ctx, from, to); err != nil {
|
||||
log.Error("fail to getAndStoreBlockTraces", "from", from, "to", to, "err", err)
|
||||
return
|
||||
}
|
||||
@@ -130,20 +186,10 @@ func txsToTxsData(txs gethTypes.Transactions) []*gethTypes.TransactionData {
|
||||
txsData := make([]*gethTypes.TransactionData, len(txs))
|
||||
for i, tx := range txs {
|
||||
v, r, s := tx.RawSignatureValues()
|
||||
|
||||
nonce := tx.Nonce()
|
||||
|
||||
// We need QueueIndex in `NewBatchHeader`. However, `TransactionData`
|
||||
// does not have this field. Since `L1MessageTx` do not have a nonce,
|
||||
// we reuse this field for storing the queue index.
|
||||
if msg := tx.AsL1MessageTx(); msg != nil {
|
||||
nonce = msg.QueueIndex
|
||||
}
|
||||
|
||||
txsData[i] = &gethTypes.TransactionData{
|
||||
Type: tx.Type(),
|
||||
TxHash: tx.Hash().String(),
|
||||
Nonce: nonce,
|
||||
Nonce: tx.Nonce(),
|
||||
ChainId: (*hexutil.Big)(tx.ChainId()),
|
||||
Gas: tx.Gas(),
|
||||
GasPrice: (*hexutil.Big)(tx.GasPrice()),
|
||||
@@ -183,7 +229,7 @@ func (w *L2WatcherClient) getAndStoreBlockTraces(ctx context.Context, from, to u
|
||||
}
|
||||
|
||||
if len(blocks) > 0 {
|
||||
if err := w.l2BlockOrm.InsertL2Blocks(w.ctx, blocks); err != nil {
|
||||
if err := w.blockTraceOrm.InsertWrappedBlocks(blocks); err != nil {
|
||||
return fmt.Errorf("failed to batch insert BlockTraces: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -241,15 +287,17 @@ func (w *L2WatcherClient) FetchContractEvent() {
|
||||
}
|
||||
log.Info("received new L2 messages", "fromBlock", from, "toBlock", to, "cnt", len(logs))
|
||||
|
||||
relayedMessageEvents, err := w.parseBridgeEventLogs(logs)
|
||||
sentMessageEvents, relayedMessageEvents, err := w.parseBridgeEventLogs(logs)
|
||||
if err != nil {
|
||||
log.Error("failed to parse emitted event log", "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
sentMessageCount := int64(len(sentMessageEvents))
|
||||
relayedMessageCount := int64(len(relayedMessageEvents))
|
||||
bridgeL2MsgsSentEventsTotalCounter.Inc(sentMessageCount)
|
||||
bridgeL2MsgsRelayedEventsTotalCounter.Inc(relayedMessageCount)
|
||||
log.Info("L2 events types", "RelayedMessageCount", relayedMessageCount)
|
||||
log.Info("L2 events types", "SentMessageCount", sentMessageCount, "RelayedMessageCount", relayedMessageCount)
|
||||
|
||||
// Update relayed message first to make sure we don't forget to update submited message.
|
||||
// Since, we always start sync from the latest unprocessed message.
|
||||
@@ -266,24 +314,71 @@ func (w *L2WatcherClient) FetchContractEvent() {
|
||||
}
|
||||
}
|
||||
|
||||
if err = w.l2MessageOrm.SaveL2Messages(w.ctx, sentMessageEvents); err != nil {
|
||||
log.Error("failed to save l2 messages", "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
w.processedMsgHeight = uint64(to)
|
||||
bridgeL2MsgsSyncHeightGauge.Update(to)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *L2WatcherClient) parseBridgeEventLogs(logs []gethTypes.Log) ([]relayedMessage, error) {
|
||||
func (w *L2WatcherClient) parseBridgeEventLogs(logs []gethTypes.Log) ([]orm.L2Message, []relayedMessage, error) {
|
||||
// Need use contract abi to parse event Log
|
||||
// Can only be tested after we have our contracts set up
|
||||
|
||||
var l2Messages []orm.L2Message
|
||||
var relayedMessages []relayedMessage
|
||||
var lastAppendMsgHash common.Hash
|
||||
var lastAppendMsgNonce uint64
|
||||
for _, vLog := range logs {
|
||||
switch vLog.Topics[0] {
|
||||
case bridgeAbi.L2SentMessageEventSignature:
|
||||
event := bridgeAbi.L2SentMessageEvent{}
|
||||
err := utils.UnpackLog(w.messengerABI, &event, "SentMessage", vLog)
|
||||
if err != nil {
|
||||
log.Error("failed to unpack layer2 SentMessage event", "err", err)
|
||||
return l2Messages, relayedMessages, err
|
||||
}
|
||||
|
||||
computedMsgHash := utils.ComputeMessageHash(
|
||||
event.Sender,
|
||||
event.Target,
|
||||
event.Value,
|
||||
event.MessageNonce,
|
||||
event.Message,
|
||||
)
|
||||
|
||||
// `AppendMessage` event is always emitted before `SentMessage` event
|
||||
// So they should always match, just double check
|
||||
if event.MessageNonce.Uint64() != lastAppendMsgNonce {
|
||||
errMsg := fmt.Sprintf("l2 message nonces mismatch: AppendMessage.nonce=%v, SentMessage.nonce=%v, tx_hash=%v",
|
||||
lastAppendMsgNonce, event.MessageNonce.Uint64(), vLog.TxHash.Hex())
|
||||
return l2Messages, relayedMessages, errors.New(errMsg)
|
||||
}
|
||||
if computedMsgHash != lastAppendMsgHash {
|
||||
errMsg := fmt.Sprintf("l2 message hashes mismatch: AppendMessage.msg_hash=%v, SentMessage.msg_hash=%v, tx_hash=%v",
|
||||
lastAppendMsgHash.Hex(), computedMsgHash.Hex(), vLog.TxHash.Hex())
|
||||
return l2Messages, relayedMessages, errors.New(errMsg)
|
||||
}
|
||||
|
||||
l2Messages = append(l2Messages, orm.L2Message{
|
||||
Nonce: event.MessageNonce.Uint64(),
|
||||
MsgHash: computedMsgHash.String(),
|
||||
Height: vLog.BlockNumber,
|
||||
Sender: event.Sender.String(),
|
||||
Value: event.Value.String(),
|
||||
Target: event.Target.String(),
|
||||
Calldata: common.Bytes2Hex(event.Message),
|
||||
Layer2Hash: vLog.TxHash.Hex(),
|
||||
})
|
||||
case bridgeAbi.L2RelayedMessageEventSignature:
|
||||
event := bridgeAbi.L2RelayedMessageEvent{}
|
||||
err := utils.UnpackLog(w.messengerABI, &event, "RelayedMessage", vLog)
|
||||
if err != nil {
|
||||
log.Warn("Failed to unpack layer2 RelayedMessage event", "err", err)
|
||||
return relayedMessages, err
|
||||
return l2Messages, relayedMessages, err
|
||||
}
|
||||
|
||||
relayedMessages = append(relayedMessages, relayedMessage{
|
||||
@@ -296,7 +391,7 @@ func (w *L2WatcherClient) parseBridgeEventLogs(logs []gethTypes.Log) ([]relayedM
|
||||
err := utils.UnpackLog(w.messengerABI, &event, "FailedRelayedMessage", vLog)
|
||||
if err != nil {
|
||||
log.Warn("Failed to unpack layer2 FailedRelayedMessage event", "err", err)
|
||||
return relayedMessages, err
|
||||
return l2Messages, relayedMessages, err
|
||||
}
|
||||
|
||||
relayedMessages = append(relayedMessages, relayedMessage{
|
||||
@@ -304,9 +399,21 @@ func (w *L2WatcherClient) parseBridgeEventLogs(logs []gethTypes.Log) ([]relayedM
|
||||
txHash: vLog.TxHash,
|
||||
isSuccessful: false,
|
||||
})
|
||||
case bridgeAbi.L2AppendMessageEventSignature:
|
||||
event := bridgeAbi.L2AppendMessageEvent{}
|
||||
err := utils.UnpackLog(w.messageQueueABI, &event, "AppendMessage", vLog)
|
||||
if err != nil {
|
||||
log.Warn("Failed to unpack layer2 AppendMessage event", "err", err)
|
||||
return l2Messages, relayedMessages, err
|
||||
}
|
||||
|
||||
lastAppendMsgHash = event.MessageHash
|
||||
lastAppendMsgNonce = event.Index.Uint64()
|
||||
bridgeL2MsgsAppendEventsTotalCounter.Inc(1)
|
||||
default:
|
||||
log.Error("Unknown event", "topic", vLog.Topics[0], "txHash", vLog.TxHash)
|
||||
}
|
||||
}
|
||||
|
||||
return relayedMessages, nil
|
||||
return l2Messages, relayedMessages, nil
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"github.com/smartystreets/goconvey/convey"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"scroll-tech/common/types"
|
||||
cutils "scroll-tech/common/utils"
|
||||
|
||||
bridgeAbi "scroll-tech/bridge/abi"
|
||||
@@ -66,6 +67,137 @@ func testCreateNewWatcherAndStop(t *testing.T) {
|
||||
assert.GreaterOrEqual(t, blockNum, uint64(numTransactions))
|
||||
}
|
||||
|
||||
func testMonitorBridgeContract(t *testing.T) {
|
||||
wc, db := setupL2Watcher(t)
|
||||
subCtx, cancel := context.WithCancel(context.Background())
|
||||
defer func() {
|
||||
cancel()
|
||||
defer utils.CloseDB(db)
|
||||
}()
|
||||
|
||||
loopToFetchEvent(subCtx, wc)
|
||||
|
||||
previousHeight, err := l2Cli.BlockNumber(context.Background())
|
||||
assert.NoError(t, err)
|
||||
|
||||
auth := prepareAuth(t, l2Cli, cfg.L2Config.RelayerConfig.MessageSenderPrivateKeys[0])
|
||||
|
||||
// deploy mock bridge
|
||||
_, tx, instance, err := mock_bridge.DeployMockBridgeL2(auth, l2Cli)
|
||||
assert.NoError(t, err)
|
||||
address, err := bind.WaitDeployed(context.Background(), l2Cli, tx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
rc := prepareWatcherClient(l2Cli, db, address)
|
||||
loopToFetchEvent(subCtx, rc)
|
||||
// Call mock_bridge instance sendMessage to trigger emit events
|
||||
toAddress := common.HexToAddress("0x4592d8f8d7b001e72cb26a73e4fa1806a51ac79d")
|
||||
message := []byte("testbridgecontract")
|
||||
fee := big.NewInt(0)
|
||||
gasLimit := big.NewInt(1)
|
||||
|
||||
tx, err = instance.SendMessage(auth, toAddress, fee, message, gasLimit)
|
||||
assert.NoError(t, err)
|
||||
receipt, err := bind.WaitMined(context.Background(), l2Cli, tx)
|
||||
if receipt.Status != gethTypes.ReceiptStatusSuccessful || err != nil {
|
||||
t.Fatalf("Call failed")
|
||||
}
|
||||
|
||||
// extra block mined
|
||||
toAddress = common.HexToAddress("0x4592d8f8d7b001e72cb26a73e4fa1806a51ac79d")
|
||||
message = []byte("testbridgecontract")
|
||||
tx, err = instance.SendMessage(auth, toAddress, fee, message, gasLimit)
|
||||
assert.NoError(t, err)
|
||||
receipt, err = bind.WaitMined(context.Background(), l2Cli, tx)
|
||||
if receipt.Status != gethTypes.ReceiptStatusSuccessful || err != nil {
|
||||
t.Fatalf("Call failed")
|
||||
}
|
||||
|
||||
l2MessageOrm := orm.NewL2Message(db)
|
||||
// check if we successfully stored events
|
||||
assert.True(t, cutils.TryTimes(10, func() bool {
|
||||
height, err := l2MessageOrm.GetLayer2LatestWatchedHeight()
|
||||
return err == nil && height > int64(previousHeight)
|
||||
}))
|
||||
|
||||
// check l1 messages.
|
||||
assert.True(t, cutils.TryTimes(10, func() bool {
|
||||
msgs, err := l2MessageOrm.GetL2Messages(map[string]interface{}{"status": types.MsgPending}, nil, 0)
|
||||
return err == nil && len(msgs) == 2
|
||||
}))
|
||||
}
|
||||
|
||||
func testFetchMultipleSentMessageInOneBlock(t *testing.T) {
|
||||
_, db := setupL2Watcher(t)
|
||||
subCtx, cancel := context.WithCancel(context.Background())
|
||||
defer func() {
|
||||
cancel()
|
||||
defer utils.CloseDB(db)
|
||||
}()
|
||||
|
||||
previousHeight, err := l2Cli.BlockNumber(context.Background()) // shallow the global previousHeight
|
||||
assert.NoError(t, err)
|
||||
|
||||
auth := prepareAuth(t, l2Cli, cfg.L2Config.RelayerConfig.MessageSenderPrivateKeys[0])
|
||||
|
||||
_, trx, instance, err := mock_bridge.DeployMockBridgeL2(auth, l2Cli)
|
||||
assert.NoError(t, err)
|
||||
address, err := bind.WaitDeployed(context.Background(), l2Cli, trx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
wc := prepareWatcherClient(l2Cli, db, address)
|
||||
loopToFetchEvent(subCtx, wc)
|
||||
|
||||
// Call mock_bridge instance sendMessage to trigger emit events multiple times
|
||||
numTransactions := 4
|
||||
var tx *gethTypes.Transaction
|
||||
for i := 0; i < numTransactions; i++ {
|
||||
addr := common.HexToAddress("0x1c5a77d9fa7ef466951b2f01f724bca3a5820b63")
|
||||
nonce, nounceErr := l2Cli.PendingNonceAt(context.Background(), addr)
|
||||
assert.NoError(t, nounceErr)
|
||||
auth.Nonce = big.NewInt(int64(nonce))
|
||||
toAddress := common.HexToAddress("0x4592d8f8d7b001e72cb26a73e4fa1806a51ac79d")
|
||||
message := []byte("testbridgecontract")
|
||||
fee := big.NewInt(0)
|
||||
gasLimit := big.NewInt(1)
|
||||
tx, err = instance.SendMessage(auth, toAddress, fee, message, gasLimit)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
receipt, err := bind.WaitMined(context.Background(), l2Cli, tx)
|
||||
if receipt.Status != gethTypes.ReceiptStatusSuccessful || err != nil {
|
||||
t.Fatalf("Call failed")
|
||||
}
|
||||
|
||||
// extra block mined
|
||||
addr := common.HexToAddress("0x1c5a77d9fa7ef466951b2f01f724bca3a5820b63")
|
||||
nonce, nounceErr := l2Cli.PendingNonceAt(context.Background(), addr)
|
||||
assert.NoError(t, nounceErr)
|
||||
auth.Nonce = big.NewInt(int64(nonce))
|
||||
toAddress := common.HexToAddress("0x4592d8f8d7b001e72cb26a73e4fa1806a51ac79d")
|
||||
message := []byte("testbridgecontract")
|
||||
fee := big.NewInt(0)
|
||||
gasLimit := big.NewInt(1)
|
||||
tx, err = instance.SendMessage(auth, toAddress, fee, message, gasLimit)
|
||||
assert.NoError(t, err)
|
||||
receipt, err = bind.WaitMined(context.Background(), l2Cli, tx)
|
||||
if receipt.Status != gethTypes.ReceiptStatusSuccessful || err != nil {
|
||||
t.Fatalf("Call failed")
|
||||
}
|
||||
|
||||
l2MessageOrm := orm.NewL2Message(db)
|
||||
// check if we successfully stored events
|
||||
assert.True(t, cutils.TryTimes(10, func() bool {
|
||||
height, err := l2MessageOrm.GetLayer2LatestWatchedHeight()
|
||||
return err == nil && height > int64(previousHeight)
|
||||
}))
|
||||
|
||||
assert.True(t, cutils.TryTimes(10, func() bool {
|
||||
msgs, err := l2MessageOrm.GetL2Messages(map[string]interface{}{"status": types.MsgPending}, nil, 0)
|
||||
return err == nil && len(msgs) == 5
|
||||
}))
|
||||
}
|
||||
|
||||
func testFetchRunningMissingBlocks(t *testing.T) {
|
||||
_, db := setupL2Watcher(t)
|
||||
defer utils.CloseDB(db)
|
||||
@@ -78,15 +210,15 @@ func testFetchRunningMissingBlocks(t *testing.T) {
|
||||
address, err := bind.WaitDeployed(context.Background(), l2Cli, tx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
l2BlockOrm := orm.NewL2Block(db)
|
||||
blockTraceOrm := orm.NewBlockTrace(db)
|
||||
ok := cutils.TryTimes(10, func() bool {
|
||||
latestHeight, err := l2Cli.BlockNumber(context.Background())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
wc := prepareWatcherClient(l2Cli, db, address)
|
||||
wc.TryFetchRunningMissingBlocks(latestHeight)
|
||||
fetchedHeight, err := l2BlockOrm.GetL2BlocksLatestHeight(context.Background())
|
||||
wc.TryFetchRunningMissingBlocks(context.Background(), latestHeight)
|
||||
fetchedHeight, err := blockTraceOrm.GetL2BlocksLatestHeight()
|
||||
return err == nil && uint64(fetchedHeight) == latestHeight
|
||||
})
|
||||
assert.True(t, ok)
|
||||
@@ -104,7 +236,6 @@ func prepareAuth(t *testing.T, l2Cli *ethclient.Client, privateKey *ecdsa.Privat
|
||||
assert.NoError(t, err)
|
||||
auth.GasPrice, err = l2Cli.SuggestGasPrice(context.Background())
|
||||
assert.NoError(t, err)
|
||||
auth.GasLimit = 500000
|
||||
return auth
|
||||
}
|
||||
|
||||
@@ -112,6 +243,57 @@ func loopToFetchEvent(subCtx context.Context, watcher *L2WatcherClient) {
|
||||
go cutils.Loop(subCtx, 2*time.Second, watcher.FetchContractEvent)
|
||||
}
|
||||
|
||||
func testParseBridgeEventLogsL2SentMessageEventSignature(t *testing.T) {
|
||||
watcher, db := setupL2Watcher(t)
|
||||
defer utils.CloseDB(db)
|
||||
|
||||
logs := []gethTypes.Log{
|
||||
{
|
||||
Topics: []common.Hash{
|
||||
bridgeAbi.L2SentMessageEventSignature,
|
||||
},
|
||||
BlockNumber: 100,
|
||||
TxHash: common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"),
|
||||
},
|
||||
}
|
||||
|
||||
convey.Convey("unpack SentMessage log failure", t, func() {
|
||||
targetErr := errors.New("UnpackLog SentMessage failure")
|
||||
patchGuard := gomonkey.ApplyFunc(utils.UnpackLog, func(c *abi.ABI, out interface{}, event string, log gethTypes.Log) error {
|
||||
return targetErr
|
||||
})
|
||||
defer patchGuard.Reset()
|
||||
|
||||
l2Messages, relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
||||
assert.EqualError(t, err, targetErr.Error())
|
||||
assert.Empty(t, l2Messages)
|
||||
assert.Empty(t, relayedMessages)
|
||||
})
|
||||
|
||||
convey.Convey("L2SentMessageEventSignature success", t, func() {
|
||||
tmpSendAddr := common.HexToAddress("0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30")
|
||||
tmpTargetAddr := common.HexToAddress("0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5")
|
||||
tmpValue := big.NewInt(1000)
|
||||
tmpMessageNonce := big.NewInt(100)
|
||||
tmpMessage := []byte("test for L2SentMessageEventSignature")
|
||||
patchGuard := gomonkey.ApplyFunc(utils.UnpackLog, func(c *abi.ABI, out interface{}, event string, log gethTypes.Log) error {
|
||||
tmpOut := out.(*bridgeAbi.L2SentMessageEvent)
|
||||
tmpOut.Sender = tmpSendAddr
|
||||
tmpOut.Value = tmpValue
|
||||
tmpOut.Target = tmpTargetAddr
|
||||
tmpOut.MessageNonce = tmpMessageNonce
|
||||
tmpOut.Message = tmpMessage
|
||||
return nil
|
||||
})
|
||||
defer patchGuard.Reset()
|
||||
|
||||
l2Messages, relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
||||
assert.Error(t, err)
|
||||
assert.Empty(t, relayedMessages)
|
||||
assert.Empty(t, l2Messages)
|
||||
})
|
||||
}
|
||||
|
||||
func testParseBridgeEventLogsL2RelayedMessageEventSignature(t *testing.T) {
|
||||
watcher, db := setupL2Watcher(t)
|
||||
defer utils.CloseDB(db)
|
||||
@@ -131,8 +313,9 @@ func testParseBridgeEventLogsL2RelayedMessageEventSignature(t *testing.T) {
|
||||
})
|
||||
defer patchGuard.Reset()
|
||||
|
||||
relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
||||
l2Messages, relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
||||
assert.EqualError(t, err, targetErr.Error())
|
||||
assert.Empty(t, l2Messages)
|
||||
assert.Empty(t, relayedMessages)
|
||||
})
|
||||
|
||||
@@ -145,8 +328,9 @@ func testParseBridgeEventLogsL2RelayedMessageEventSignature(t *testing.T) {
|
||||
})
|
||||
defer patchGuard.Reset()
|
||||
|
||||
relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
||||
l2Messages, relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, l2Messages)
|
||||
assert.Len(t, relayedMessages, 1)
|
||||
assert.Equal(t, relayedMessages[0].msgHash, msgHash)
|
||||
})
|
||||
@@ -171,8 +355,9 @@ func testParseBridgeEventLogsL2FailedRelayedMessageEventSignature(t *testing.T)
|
||||
})
|
||||
defer patchGuard.Reset()
|
||||
|
||||
relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
||||
l2Messages, relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
||||
assert.EqualError(t, err, targetErr.Error())
|
||||
assert.Empty(t, l2Messages)
|
||||
assert.Empty(t, relayedMessages)
|
||||
})
|
||||
|
||||
@@ -185,9 +370,51 @@ func testParseBridgeEventLogsL2FailedRelayedMessageEventSignature(t *testing.T)
|
||||
})
|
||||
defer patchGuard.Reset()
|
||||
|
||||
relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
||||
l2Messages, relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, l2Messages)
|
||||
assert.Len(t, relayedMessages, 1)
|
||||
assert.Equal(t, relayedMessages[0].msgHash, msgHash)
|
||||
})
|
||||
}
|
||||
|
||||
func testParseBridgeEventLogsL2AppendMessageEventSignature(t *testing.T) {
|
||||
watcher, db := setupL2Watcher(t)
|
||||
defer utils.CloseDB(db)
|
||||
logs := []gethTypes.Log{
|
||||
{
|
||||
Topics: []common.Hash{bridgeAbi.L2AppendMessageEventSignature},
|
||||
BlockNumber: 100,
|
||||
TxHash: common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"),
|
||||
},
|
||||
}
|
||||
|
||||
convey.Convey("unpack AppendMessage log failure", t, func() {
|
||||
targetErr := errors.New("UnpackLog AppendMessage failure")
|
||||
patchGuard := gomonkey.ApplyFunc(utils.UnpackLog, func(c *abi.ABI, out interface{}, event string, log gethTypes.Log) error {
|
||||
return targetErr
|
||||
})
|
||||
defer patchGuard.Reset()
|
||||
|
||||
l2Messages, relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
||||
assert.EqualError(t, err, targetErr.Error())
|
||||
assert.Empty(t, l2Messages)
|
||||
assert.Empty(t, relayedMessages)
|
||||
})
|
||||
|
||||
convey.Convey("L2AppendMessageEventSignature success", t, func() {
|
||||
msgHash := common.HexToHash("0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5")
|
||||
patchGuard := gomonkey.ApplyFunc(utils.UnpackLog, func(c *abi.ABI, out interface{}, event string, log gethTypes.Log) error {
|
||||
tmpOut := out.(*bridgeAbi.L2AppendMessageEvent)
|
||||
tmpOut.MessageHash = msgHash
|
||||
tmpOut.Index = big.NewInt(100)
|
||||
return nil
|
||||
})
|
||||
defer patchGuard.Reset()
|
||||
|
||||
l2Messages, relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, l2Messages)
|
||||
assert.Empty(t, relayedMessages)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -100,18 +100,23 @@ func TestFunction(t *testing.T) {
|
||||
t.Run("TestL1WatcherClientFetchBlockHeader", testL1WatcherClientFetchBlockHeader)
|
||||
t.Run("TestL1WatcherClientFetchContractEvent", testL1WatcherClientFetchContractEvent)
|
||||
t.Run("TestParseBridgeEventLogsL1QueueTransactionEventSignature", testParseBridgeEventLogsL1QueueTransactionEventSignature)
|
||||
t.Run("TestParseBridgeEventLogsL1RelayedMessageEventSignature", testParseBridgeEventLogsL1RelayedMessageEventSignature)
|
||||
t.Run("TestParseBridgeEventLogsL1FailedRelayedMessageEventSignature", testParseBridgeEventLogsL1FailedRelayedMessageEventSignature)
|
||||
t.Run("TestParseBridgeEventLogsL1CommitBatchEventSignature", testParseBridgeEventLogsL1CommitBatchEventSignature)
|
||||
t.Run("TestParseBridgeEventLogsL1FinalizeBatchEventSignature", testParseBridgeEventLogsL1FinalizeBatchEventSignature)
|
||||
|
||||
// Run l2 watcher test cases.
|
||||
t.Run("TestCreateNewWatcherAndStop", testCreateNewWatcherAndStop)
|
||||
t.Run("TestMonitorBridgeContract", testMonitorBridgeContract)
|
||||
t.Run("TestFetchMultipleSentMessageInOneBlock", testFetchMultipleSentMessageInOneBlock)
|
||||
t.Run("TestFetchRunningMissingBlocks", testFetchRunningMissingBlocks)
|
||||
t.Run("TestParseBridgeEventLogsL2SentMessageEventSignature", testParseBridgeEventLogsL2SentMessageEventSignature)
|
||||
t.Run("TestParseBridgeEventLogsL2RelayedMessageEventSignature", testParseBridgeEventLogsL2RelayedMessageEventSignature)
|
||||
t.Run("TestParseBridgeEventLogsL2FailedRelayedMessageEventSignature", testParseBridgeEventLogsL2FailedRelayedMessageEventSignature)
|
||||
t.Run("TestParseBridgeEventLogsL2AppendMessageEventSignature", testParseBridgeEventLogsL2AppendMessageEventSignature)
|
||||
|
||||
// Run chunk-proposer test cases.
|
||||
t.Run("TestChunkProposer", testChunkProposer)
|
||||
|
||||
// Run batch-proposer test cases.
|
||||
t.Run("TestBatchProposer", testBatchProposer)
|
||||
// Run batch proposer test cases.
|
||||
t.Run("TestBatchProposerProposeBatch", testBatchProposerProposeBatch)
|
||||
t.Run("TestBatchProposerBatchGeneration", testBatchProposerBatchGeneration)
|
||||
t.Run("TestBatchProposerGracefulRestart", testBatchProposerGracefulRestart)
|
||||
}
|
||||
|
||||
@@ -1,378 +0,0 @@
|
||||
package orm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"scroll-tech/common/types"
|
||||
"scroll-tech/common/types/message"
|
||||
|
||||
bridgeTypes "scroll-tech/bridge/internal/types"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
"github.com/scroll-tech/go-ethereum/log"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const defaultBatchHeaderVersion = 0
|
||||
|
||||
// Batch represents a batch of chunks.
|
||||
type Batch struct {
|
||||
db *gorm.DB `gorm:"column:-"`
|
||||
|
||||
// batch
|
||||
Index uint64 `json:"index" gorm:"column:index"`
|
||||
Hash string `json:"hash" gorm:"column:hash"`
|
||||
StartChunkIndex uint64 `json:"start_chunk_index" gorm:"column:start_chunk_index"`
|
||||
StartChunkHash string `json:"start_chunk_hash" gorm:"column:start_chunk_hash"`
|
||||
EndChunkIndex uint64 `json:"end_chunk_index" gorm:"column:end_chunk_index"`
|
||||
EndChunkHash string `json:"end_chunk_hash" gorm:"column:end_chunk_hash"`
|
||||
StateRoot string `json:"state_root" gorm:"column:state_root"`
|
||||
WithdrawRoot string `json:"withdraw_root" gorm:"column:withdraw_root"`
|
||||
BatchHeader []byte `json:"batch_header" gorm:"column:batch_header"`
|
||||
|
||||
// proof
|
||||
ProvingStatus int16 `json:"proving_status" gorm:"column:proving_status;default:1"`
|
||||
Proof []byte `json:"proof" gorm:"column:proof;default:NULL"`
|
||||
ProverAssignedAt *time.Time `json:"prover_assigned_at" gorm:"column:prover_assigned_at;default:NULL"`
|
||||
ProvedAt *time.Time `json:"proved_at" gorm:"column:proved_at;default:NULL"`
|
||||
ProofTimeSec int `json:"proof_time_sec" gorm:"column:proof_time_sec;default:NULL"`
|
||||
|
||||
// rollup
|
||||
RollupStatus int16 `json:"rollup_status" gorm:"column:rollup_status;default:1"`
|
||||
CommitTxHash string `json:"commit_tx_hash" gorm:"column:commit_tx_hash;default:NULL"`
|
||||
CommittedAt *time.Time `json:"committed_at" gorm:"column:committed_at;default:NULL"`
|
||||
FinalizeTxHash string `json:"finalize_tx_hash" gorm:"column:finalize_tx_hash;default:NULL"`
|
||||
FinalizedAt *time.Time `json:"finalized_at" gorm:"column:finalized_at;default:NULL"`
|
||||
|
||||
// gas oracle
|
||||
OracleStatus int16 `json:"oracle_status" gorm:"column:oracle_status;default:1"`
|
||||
OracleTxHash string `json:"oracle_tx_hash" gorm:"column:oracle_tx_hash;default:NULL"`
|
||||
|
||||
// metadata
|
||||
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"column:deleted_at;default:NULL"`
|
||||
}
|
||||
|
||||
// NewBatch creates a new Batch database instance.
|
||||
func NewBatch(db *gorm.DB) *Batch {
|
||||
return &Batch{db: db}
|
||||
}
|
||||
|
||||
// TableName returns the table name for the Batch model.
|
||||
func (*Batch) TableName() string {
|
||||
return "batch"
|
||||
}
|
||||
|
||||
// GetBatches retrieves selected batches from the database.
|
||||
// The returned batches are sorted in ascending order by their index.
|
||||
func (o *Batch) GetBatches(ctx context.Context, fields map[string]interface{}, orderByList []string, limit int) ([]*Batch, error) {
|
||||
db := o.db.WithContext(ctx)
|
||||
|
||||
for key, value := range fields {
|
||||
db = db.Where(key, value)
|
||||
}
|
||||
|
||||
for _, orderBy := range orderByList {
|
||||
db = db.Order(orderBy)
|
||||
}
|
||||
|
||||
if limit > 0 {
|
||||
db = db.Limit(limit)
|
||||
}
|
||||
|
||||
db = db.Order("index ASC")
|
||||
|
||||
var batches []*Batch
|
||||
if err := db.Find(&batches).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return batches, nil
|
||||
}
|
||||
|
||||
// GetBatchCount retrieves the total number of batches in the database.
|
||||
func (o *Batch) GetBatchCount(ctx context.Context) (uint64, error) {
|
||||
var count int64
|
||||
err := o.db.WithContext(ctx).Model(&Batch{}).Count(&count).Error
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint64(count), nil
|
||||
}
|
||||
|
||||
// GetVerifiedProofByHash retrieves the verified aggregate proof for a batch with the given hash.
|
||||
func (o *Batch) GetVerifiedProofByHash(ctx context.Context, hash string) (*message.AggProof, error) {
|
||||
var batch Batch
|
||||
err := o.db.WithContext(ctx).Where("hash = ? AND proving_status = ?", hash, types.ProvingTaskVerified).First(&batch).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var proof message.AggProof
|
||||
err = json.Unmarshal(batch.Proof, &proof)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &proof, nil
|
||||
}
|
||||
|
||||
// GetLatestBatch retrieves the latest batch from the database.
|
||||
func (o *Batch) GetLatestBatch(ctx context.Context) (*Batch, error) {
|
||||
var latestBatch Batch
|
||||
err := o.db.WithContext(ctx).Order("index desc").First(&latestBatch).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &latestBatch, nil
|
||||
}
|
||||
|
||||
// GetRollupStatusByHashList retrieves the rollup statuses for a list of batch hashes.
|
||||
func (o *Batch) GetRollupStatusByHashList(ctx context.Context, hashes []string) ([]types.RollupStatus, error) {
|
||||
if len(hashes) == 0 {
|
||||
return []types.RollupStatus{}, nil
|
||||
}
|
||||
|
||||
var batches []Batch
|
||||
err := o.db.WithContext(ctx).Where("hash IN ?", hashes).Find(&batches).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hashToStatusMap := make(map[string]types.RollupStatus)
|
||||
for _, batch := range batches {
|
||||
hashToStatusMap[batch.Hash] = types.RollupStatus(batch.RollupStatus)
|
||||
}
|
||||
|
||||
var statuses []types.RollupStatus
|
||||
for _, hash := range hashes {
|
||||
status, ok := hashToStatusMap[hash]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("hash not found in database: %s", hash)
|
||||
}
|
||||
statuses = append(statuses, status)
|
||||
}
|
||||
|
||||
return statuses, nil
|
||||
}
|
||||
|
||||
// GetPendingBatches retrieves pending batches up to the specified limit.
|
||||
// The returned batches are sorted in ascending order by their index.
|
||||
func (o *Batch) GetPendingBatches(ctx context.Context, limit int) ([]*Batch, error) {
|
||||
if limit <= 0 {
|
||||
return nil, errors.New("limit must be greater than zero")
|
||||
}
|
||||
|
||||
var batches []*Batch
|
||||
db := o.db.WithContext(ctx)
|
||||
|
||||
db = db.Where("rollup_status = ?", types.RollupPending).Order("index ASC").Limit(limit)
|
||||
|
||||
if err := db.Find(&batches).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return batches, nil
|
||||
}
|
||||
|
||||
// GetBatchByIndex retrieves the batch by the given index.
|
||||
func (o *Batch) GetBatchByIndex(ctx context.Context, index uint64) (*Batch, error) {
|
||||
var batch Batch
|
||||
err := o.db.WithContext(ctx).Where("index = ?", index).First(&batch).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &batch, nil
|
||||
}
|
||||
|
||||
// InsertBatch inserts a new batch into the database.
|
||||
func (o *Batch) InsertBatch(ctx context.Context, startChunkIndex, endChunkIndex uint64, startChunkHash, endChunkHash string, chunks []*bridgeTypes.Chunk, dbTX ...*gorm.DB) (*Batch, error) {
|
||||
if len(chunks) == 0 {
|
||||
return nil, errors.New("invalid args")
|
||||
}
|
||||
|
||||
db := o.db
|
||||
if len(dbTX) > 0 && dbTX[0] != nil {
|
||||
db = dbTX[0]
|
||||
}
|
||||
|
||||
parentBatch, err := o.GetLatestBatch(ctx)
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
log.Error("failed to get the latest batch", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var batchIndex uint64
|
||||
var parentBatchHash common.Hash
|
||||
var totalL1MessagePoppedBefore uint64
|
||||
var version uint8 = defaultBatchHeaderVersion
|
||||
|
||||
// if parentBatch==nil then err==gorm.ErrRecordNotFound, which means there's
|
||||
// not batch record in the db, we then use default empty values for the creating batch;
|
||||
// if parentBatch!=nil then err=nil, then we fill the parentBatch-related data into the creating batch
|
||||
if parentBatch != nil {
|
||||
batchIndex = parentBatch.Index + 1
|
||||
parentBatchHash = common.HexToHash(parentBatch.Hash)
|
||||
|
||||
var parentBatchHeader *bridgeTypes.BatchHeader
|
||||
parentBatchHeader, err = bridgeTypes.DecodeBatchHeader(parentBatch.BatchHeader)
|
||||
if err != nil {
|
||||
log.Error("failed to decode parent batch header", "index", parentBatch.Index, "hash", parentBatch.Hash, "err", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
totalL1MessagePoppedBefore = parentBatchHeader.TotalL1MessagePopped()
|
||||
version = parentBatchHeader.Version()
|
||||
}
|
||||
|
||||
batchHeader, err := bridgeTypes.NewBatchHeader(version, batchIndex, totalL1MessagePoppedBefore, parentBatchHash, chunks)
|
||||
if err != nil {
|
||||
log.Error("failed to create batch header",
|
||||
"index", batchIndex, "total l1 message popped before", totalL1MessagePoppedBefore,
|
||||
"parent hash", parentBatchHash, "number of chunks", len(chunks), "err", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
numChunks := len(chunks)
|
||||
lastChunkBlockNum := len(chunks[numChunks-1].Blocks)
|
||||
|
||||
newBatch := Batch{
|
||||
Index: batchIndex,
|
||||
Hash: batchHeader.Hash().Hex(),
|
||||
StartChunkHash: startChunkHash,
|
||||
StartChunkIndex: startChunkIndex,
|
||||
EndChunkHash: endChunkHash,
|
||||
EndChunkIndex: endChunkIndex,
|
||||
StateRoot: chunks[numChunks-1].Blocks[lastChunkBlockNum-1].Header.Root.Hex(),
|
||||
WithdrawRoot: chunks[numChunks-1].Blocks[lastChunkBlockNum-1].WithdrawTrieRoot.Hex(),
|
||||
BatchHeader: batchHeader.Encode(),
|
||||
ProvingStatus: int16(types.ProvingTaskUnassigned),
|
||||
RollupStatus: int16(types.RollupPending),
|
||||
}
|
||||
|
||||
if err := db.WithContext(ctx).Create(&newBatch).Error; err != nil {
|
||||
log.Error("failed to insert batch", "batch", newBatch, "err", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &newBatch, nil
|
||||
}
|
||||
|
||||
// UpdateSkippedBatches updates the skipped batches in the database.
|
||||
func (o *Batch) UpdateSkippedBatches(ctx context.Context) (uint64, error) {
|
||||
provingStatusList := []interface{}{
|
||||
int(types.ProvingTaskSkipped),
|
||||
int(types.ProvingTaskFailed),
|
||||
}
|
||||
result := o.db.Model(&Batch{}).Where("rollup_status", int(types.RollupCommitted)).
|
||||
Where("proving_status IN (?)", provingStatusList).Update("rollup_status", int(types.RollupFinalizationSkipped))
|
||||
if result.Error != nil {
|
||||
return 0, result.Error
|
||||
}
|
||||
return uint64(result.RowsAffected), nil
|
||||
}
|
||||
|
||||
// UpdateL2GasOracleStatusAndOracleTxHash updates the L2 gas oracle status and transaction hash for a batch.
|
||||
func (o *Batch) UpdateL2GasOracleStatusAndOracleTxHash(ctx context.Context, hash string, status types.GasOracleStatus, txHash string) error {
|
||||
updateFields := make(map[string]interface{})
|
||||
updateFields["oracle_status"] = int(status)
|
||||
updateFields["oracle_tx_hash"] = txHash
|
||||
if err := o.db.WithContext(ctx).Model(&Batch{}).Where("hash", hash).Updates(updateFields).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateProvingStatus updates the proving status of a batch.
|
||||
func (o *Batch) UpdateProvingStatus(ctx context.Context, hash string, status types.ProvingStatus, dbTX ...*gorm.DB) error {
|
||||
db := o.db
|
||||
if len(dbTX) > 0 && dbTX[0] != nil {
|
||||
db = dbTX[0]
|
||||
}
|
||||
|
||||
updateFields := make(map[string]interface{})
|
||||
updateFields["proving_status"] = int(status)
|
||||
|
||||
switch status {
|
||||
case types.ProvingTaskAssigned:
|
||||
updateFields["prover_assigned_at"] = time.Now()
|
||||
case types.ProvingTaskUnassigned:
|
||||
updateFields["prover_assigned_at"] = nil
|
||||
case types.ProvingTaskProved, types.ProvingTaskVerified:
|
||||
updateFields["proved_at"] = time.Now()
|
||||
default:
|
||||
}
|
||||
|
||||
if err := db.Model(&Batch{}).Where("hash", hash).Updates(updateFields).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateRollupStatus updates the rollup status of a batch.
|
||||
func (o *Batch) UpdateRollupStatus(ctx context.Context, hash string, status types.RollupStatus, dbTX ...*gorm.DB) error {
|
||||
db := o.db
|
||||
if len(dbTX) > 0 && dbTX[0] != nil {
|
||||
db = dbTX[0]
|
||||
}
|
||||
|
||||
updateFields := make(map[string]interface{})
|
||||
updateFields["rollup_status"] = int(status)
|
||||
|
||||
switch status {
|
||||
case types.RollupCommitted:
|
||||
updateFields["committed_at"] = time.Now()
|
||||
case types.RollupFinalized:
|
||||
updateFields["finalized_at"] = time.Now()
|
||||
}
|
||||
if err := db.Model(&Batch{}).WithContext(ctx).Where("hash", hash).Updates(updateFields).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateCommitTxHashAndRollupStatus updates the commit transaction hash and rollup status for a batch.
|
||||
func (o *Batch) UpdateCommitTxHashAndRollupStatus(ctx context.Context, hash string, commitTxHash string, status types.RollupStatus) error {
|
||||
updateFields := make(map[string]interface{})
|
||||
updateFields["commit_tx_hash"] = commitTxHash
|
||||
updateFields["rollup_status"] = int(status)
|
||||
if status == types.RollupCommitted {
|
||||
updateFields["committed_at"] = time.Now()
|
||||
}
|
||||
if err := o.db.WithContext(ctx).Model(&Batch{}).Where("hash", hash).Updates(updateFields).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateFinalizeTxHashAndRollupStatus updates the finalize transaction hash and rollup status for a batch.
|
||||
func (o *Batch) UpdateFinalizeTxHashAndRollupStatus(ctx context.Context, hash string, finalizeTxHash string, status types.RollupStatus) error {
|
||||
updateFields := make(map[string]interface{})
|
||||
updateFields["finalize_tx_hash"] = finalizeTxHash
|
||||
updateFields["rollup_status"] = int(status)
|
||||
if status == types.RollupFinalized {
|
||||
updateFields["finalized_at"] = time.Now()
|
||||
}
|
||||
if err := o.db.WithContext(ctx).Model(&Batch{}).Where("hash", hash).Updates(updateFields).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateProofByHash updates the block batch proof by hash.
|
||||
// for unit test.
|
||||
func (o *Batch) UpdateProofByHash(ctx context.Context, hash string, proof *message.AggProof, proofTimeSec uint64) error {
|
||||
proofBytes, err := json.Marshal(proof)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
updateFields := make(map[string]interface{})
|
||||
updateFields["proof"] = proofBytes
|
||||
updateFields["proof_time_sec"] = proofTimeSec
|
||||
err = o.db.WithContext(ctx).Model(&Batch{}).Where("hash", hash).Updates(updateFields).Error
|
||||
return err
|
||||
}
|
||||
292
bridge/internal/orm/block_batch.go
Normal file
292
bridge/internal/orm/block_batch.go
Normal file
@@ -0,0 +1,292 @@
|
||||
package orm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/log"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"scroll-tech/common/types"
|
||||
|
||||
bridgeTypes "scroll-tech/bridge/internal/types"
|
||||
)
|
||||
|
||||
// BlockBatch is structure of stored block batch message
|
||||
type BlockBatch struct {
|
||||
db *gorm.DB `gorm:"column:-"`
|
||||
|
||||
Hash string `json:"hash" gorm:"column:hash"`
|
||||
Index uint64 `json:"index" gorm:"column:index"`
|
||||
StartBlockNumber uint64 `json:"start_block_number" gorm:"column:start_block_number"`
|
||||
StartBlockHash string `json:"start_block_hash" gorm:"column:start_block_hash"`
|
||||
EndBlockNumber uint64 `json:"end_block_number" gorm:"column:end_block_number"`
|
||||
EndBlockHash string `json:"end_block_hash" gorm:"column:end_block_hash"`
|
||||
ParentHash string `json:"parent_hash" gorm:"column:parent_hash"`
|
||||
StateRoot string `json:"state_root" gorm:"column:state_root"`
|
||||
TotalTxNum uint64 `json:"total_tx_num" gorm:"column:total_tx_num"`
|
||||
TotalL1TxNum uint64 `json:"total_l1_tx_num" gorm:"column:total_l1_tx_num"`
|
||||
TotalL2Gas uint64 `json:"total_l2_gas" gorm:"column:total_l2_gas"`
|
||||
ProvingStatus int `json:"proving_status" gorm:"column:proving_status;default:1"`
|
||||
Proof []byte `json:"proof" gorm:"column:proof"`
|
||||
InstanceCommitments []byte `json:"instance_commitments" gorm:"column:instance_commitments"`
|
||||
ProofTimeSec uint64 `json:"proof_time_sec" gorm:"column:proof_time_sec;default:0"`
|
||||
RollupStatus int `json:"rollup_status" gorm:"column:rollup_status;default:1"`
|
||||
CommitTxHash string `json:"commit_tx_hash" gorm:"column:commit_tx_hash;default:NULL"`
|
||||
OracleStatus int `json:"oracle_status" gorm:"column:oracle_status;default:1"`
|
||||
OracleTxHash string `json:"oracle_tx_hash" gorm:"column:oracle_tx_hash;default:NULL"`
|
||||
FinalizeTxHash string `json:"finalize_tx_hash" gorm:"column:finalize_tx_hash;default:NULL"`
|
||||
CreatedAt time.Time `json:"created_at" gorm:"column:created_at;default:CURRENT_TIMESTAMP()"`
|
||||
ProverAssignedAt *time.Time `json:"prover_assigned_at" gorm:"column:prover_assigned_at;default:NULL"`
|
||||
ProvedAt *time.Time `json:"proved_at" gorm:"column:proved_at;default:NULL"`
|
||||
CommittedAt *time.Time `json:"committed_at" gorm:"column:committed_at;default:NULL"`
|
||||
FinalizedAt *time.Time `json:"finalized_at" gorm:"column:finalized_at;default:NULL"`
|
||||
}
|
||||
|
||||
// NewBlockBatch create an blockBatchOrm instance
|
||||
func NewBlockBatch(db *gorm.DB) *BlockBatch {
|
||||
return &BlockBatch{db: db}
|
||||
}
|
||||
|
||||
// TableName define the BlockBatch table name
|
||||
func (*BlockBatch) TableName() string {
|
||||
return "block_batch"
|
||||
}
|
||||
|
||||
// GetBatchCount get the batch count
|
||||
func (o *BlockBatch) GetBatchCount() (int64, error) {
|
||||
var count int64
|
||||
if err := o.db.Model(&BlockBatch{}).Count(&count).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// GetBlockBatches get the select block batches
|
||||
func (o *BlockBatch) GetBlockBatches(fields map[string]interface{}, orderByList []string, limit int) ([]BlockBatch, error) {
|
||||
var blockBatches []BlockBatch
|
||||
db := o.db
|
||||
for key, value := range fields {
|
||||
db = db.Where(key, value)
|
||||
}
|
||||
|
||||
for _, orderBy := range orderByList {
|
||||
db = db.Order(orderBy)
|
||||
}
|
||||
|
||||
if limit != 0 {
|
||||
db = db.Limit(limit)
|
||||
}
|
||||
|
||||
if err := db.Find(&blockBatches).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return blockBatches, nil
|
||||
}
|
||||
|
||||
// GetBlockBatchesHashByRollupStatus get the block batches by rollup status
|
||||
func (o *BlockBatch) GetBlockBatchesHashByRollupStatus(status types.RollupStatus, limit int) ([]string, error) {
|
||||
var blockBatches []BlockBatch
|
||||
err := o.db.Select("hash").Where("rollup_status", int(status)).Order("index ASC").Limit(limit).Find(&blockBatches).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var hashes []string
|
||||
for _, v := range blockBatches {
|
||||
hashes = append(hashes, v.Hash)
|
||||
}
|
||||
return hashes, nil
|
||||
}
|
||||
|
||||
// GetVerifiedProofAndInstanceCommitmentsByHash get verified proof and instance comments by hash
|
||||
func (o *BlockBatch) GetVerifiedProofAndInstanceCommitmentsByHash(hash string) ([]byte, []byte, error) {
|
||||
var blockBatch BlockBatch
|
||||
err := o.db.Select("proof, instance_commitments").Where("hash", hash).Where("proving_status", int(types.ProvingTaskVerified)).Find(&blockBatch).Error
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return blockBatch.Proof, blockBatch.InstanceCommitments, nil
|
||||
}
|
||||
|
||||
// GetLatestBatch get the latest batch
|
||||
// because we will `initializeGenesis()` when we start the `L2Watcher`, so a batch must exist.
|
||||
func (o *BlockBatch) GetLatestBatch() (*BlockBatch, error) {
|
||||
var blockBatch BlockBatch
|
||||
err := o.db.Order("index DESC").Limit(1).First(&blockBatch).Error
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
return &blockBatch, nil
|
||||
}
|
||||
|
||||
// GetLatestBatchByRollupStatus get the latest block batch by rollup status
|
||||
func (o *BlockBatch) GetLatestBatchByRollupStatus(rollupStatuses []types.RollupStatus) (*BlockBatch, error) {
|
||||
var tmpRollupStatus []int
|
||||
for _, v := range rollupStatuses {
|
||||
tmpRollupStatus = append(tmpRollupStatus, int(v))
|
||||
}
|
||||
var blockBatch BlockBatch
|
||||
err := o.db.Where("rollup_status IN (?)", tmpRollupStatus).Order("index DESC").Limit(1).First(&blockBatch).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &blockBatch, nil
|
||||
}
|
||||
|
||||
// GetRollupStatusByHashList get rollup status by hash list
|
||||
func (o *BlockBatch) GetRollupStatusByHashList(hashes []string) ([]types.RollupStatus, error) {
|
||||
if len(hashes) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var blockBatches []BlockBatch
|
||||
err := o.db.Select("hash, rollup_status").Where("hash IN (?)", hashes).Find(&blockBatches).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var statuses []types.RollupStatus
|
||||
for _, v := range blockBatches {
|
||||
statuses = append(statuses, types.RollupStatus(v.RollupStatus))
|
||||
}
|
||||
return statuses, nil
|
||||
}
|
||||
|
||||
// InsertBlockBatchByBatchData insert a block batch data by the BatchData
|
||||
func (o *BlockBatch) InsertBlockBatchByBatchData(tx *gorm.DB, batchData *bridgeTypes.BatchData) (int64, error) {
|
||||
var db *gorm.DB
|
||||
if tx != nil {
|
||||
db = tx
|
||||
} else {
|
||||
db = o.db
|
||||
}
|
||||
|
||||
numBlocks := len(batchData.Batch.Blocks)
|
||||
insertBlockBatch := BlockBatch{
|
||||
Hash: batchData.Hash().Hex(),
|
||||
Index: batchData.Batch.BatchIndex,
|
||||
StartBlockNumber: batchData.Batch.Blocks[0].BlockNumber,
|
||||
StartBlockHash: batchData.Batch.Blocks[0].BlockHash.Hex(),
|
||||
EndBlockNumber: batchData.Batch.Blocks[numBlocks-1].BlockNumber,
|
||||
EndBlockHash: batchData.Batch.Blocks[numBlocks-1].BlockHash.Hex(),
|
||||
ParentHash: batchData.Batch.ParentBatchHash.Hex(),
|
||||
StateRoot: batchData.Batch.NewStateRoot.Hex(),
|
||||
TotalTxNum: batchData.TotalTxNum,
|
||||
TotalL1TxNum: batchData.TotalL1TxNum,
|
||||
TotalL2Gas: batchData.TotalL2Gas,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
result := db.Create(&insertBlockBatch)
|
||||
if result.Error != nil {
|
||||
log.Error("failed to insert block batch by batchData", "err", result.Error)
|
||||
return 0, result.Error
|
||||
}
|
||||
return result.RowsAffected, nil
|
||||
}
|
||||
|
||||
// UpdateProvingStatus update the proving status
|
||||
func (o *BlockBatch) UpdateProvingStatus(hash string, status types.ProvingStatus) error {
|
||||
updateFields := make(map[string]interface{})
|
||||
updateFields["proving_status"] = int(status)
|
||||
|
||||
switch status {
|
||||
case types.ProvingTaskAssigned:
|
||||
updateFields["prover_assigned_at"] = time.Now()
|
||||
case types.ProvingTaskUnassigned:
|
||||
updateFields["prover_assigned_at"] = nil
|
||||
case types.ProvingTaskProved, types.ProvingTaskVerified:
|
||||
updateFields["proved_at"] = time.Now()
|
||||
default:
|
||||
}
|
||||
|
||||
if err := o.db.Model(&BlockBatch{}).Where("hash", hash).Updates(updateFields).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateRollupStatus update the rollup status
|
||||
func (o *BlockBatch) UpdateRollupStatus(ctx context.Context, hash string, status types.RollupStatus) error {
|
||||
updateFields := make(map[string]interface{})
|
||||
updateFields["rollup_status"] = int(status)
|
||||
|
||||
switch status {
|
||||
case types.RollupCommitted:
|
||||
updateFields["committed_at"] = time.Now()
|
||||
case types.RollupFinalized:
|
||||
updateFields["finalized_at"] = time.Now()
|
||||
}
|
||||
if err := o.db.Model(&BlockBatch{}).WithContext(ctx).Where("hash", hash).Updates(updateFields).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateSkippedBatches update the skipped batches
|
||||
func (o *BlockBatch) UpdateSkippedBatches() (int64, error) {
|
||||
provingStatusList := []interface{}{
|
||||
int(types.ProvingTaskSkipped),
|
||||
int(types.ProvingTaskFailed),
|
||||
}
|
||||
result := o.db.Model(&BlockBatch{}).Where("rollup_status", int(types.RollupCommitted)).
|
||||
Where("proving_status IN (?)", provingStatusList).Update("rollup_status", int(types.RollupFinalizationSkipped))
|
||||
if result.Error != nil {
|
||||
return 0, result.Error
|
||||
}
|
||||
return result.RowsAffected, nil
|
||||
}
|
||||
|
||||
// UpdateCommitTxHashAndRollupStatus update the commit tx hash and rollup status
|
||||
func (o *BlockBatch) UpdateCommitTxHashAndRollupStatus(ctx context.Context, hash string, commitTxHash string, status types.RollupStatus) error {
|
||||
updateFields := make(map[string]interface{})
|
||||
updateFields["commit_tx_hash"] = commitTxHash
|
||||
updateFields["rollup_status"] = int(status)
|
||||
if status == types.RollupCommitted {
|
||||
updateFields["committed_at"] = time.Now()
|
||||
}
|
||||
if err := o.db.WithContext(ctx).Model(&BlockBatch{}).Where("hash", hash).Updates(updateFields).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateFinalizeTxHashAndRollupStatus update the finalize tx hash and rollup status
|
||||
func (o *BlockBatch) UpdateFinalizeTxHashAndRollupStatus(ctx context.Context, hash string, finalizeTxHash string, status types.RollupStatus) error {
|
||||
updateFields := make(map[string]interface{})
|
||||
updateFields["finalize_tx_hash"] = finalizeTxHash
|
||||
updateFields["rollup_status"] = int(status)
|
||||
if status == types.RollupFinalized {
|
||||
updateFields["finalized_at"] = time.Now()
|
||||
}
|
||||
if err := o.db.WithContext(ctx).Model(&BlockBatch{}).Where("hash", hash).Updates(updateFields).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateL2GasOracleStatusAndOracleTxHash update the l2 gas oracle status and oracle tx hash
|
||||
func (o *BlockBatch) UpdateL2GasOracleStatusAndOracleTxHash(ctx context.Context, hash string, status types.GasOracleStatus, txHash string) error {
|
||||
updateFields := make(map[string]interface{})
|
||||
updateFields["oracle_status"] = int(status)
|
||||
updateFields["oracle_tx_hash"] = txHash
|
||||
if err := o.db.WithContext(ctx).Model(&BlockBatch{}).Where("hash", hash).Updates(updateFields).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateProofByHash update the block batch proof by hash
|
||||
// for unit test
|
||||
func (o *BlockBatch) UpdateProofByHash(ctx context.Context, hash string, proof, instanceCommitments []byte, proofTimeSec uint64) error {
|
||||
updateFields := make(map[string]interface{})
|
||||
updateFields["proof"] = proof
|
||||
updateFields["instance_commitments"] = instanceCommitments
|
||||
updateFields["proof_time_sec"] = proofTimeSec
|
||||
err := o.db.WithContext(ctx).Model(&BlockBatch{}).Where("hash", hash).Updates(updateFields).Error
|
||||
if err != nil {
|
||||
log.Error("failed to update proof", "err", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
155
bridge/internal/orm/block_trace.go
Normal file
155
bridge/internal/orm/block_trace.go
Normal file
@@ -0,0 +1,155 @@
|
||||
package orm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/log"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"scroll-tech/bridge/internal/types"
|
||||
)
|
||||
|
||||
// BlockTrace is structure of stored block trace message
|
||||
type BlockTrace struct {
|
||||
db *gorm.DB `gorm:"column:-"`
|
||||
|
||||
Number uint64 `json:"number" gorm:"number"`
|
||||
Hash string `json:"hash" gorm:"hash"`
|
||||
ParentHash string `json:"parent_hash" gorm:"parent_hash"`
|
||||
Trace string `json:"trace" gorm:"column:trace"`
|
||||
BatchHash string `json:"batch_hash" gorm:"batch_hash;default:NULL"`
|
||||
TxNum uint64 `json:"tx_num" gorm:"tx_num"`
|
||||
GasUsed uint64 `json:"gas_used" gorm:"gas_used"`
|
||||
BlockTimestamp uint64 `json:"block_timestamp" gorm:"block_timestamp"`
|
||||
}
|
||||
|
||||
// NewBlockTrace create an blockTraceOrm instance
|
||||
func NewBlockTrace(db *gorm.DB) *BlockTrace {
|
||||
return &BlockTrace{db: db}
|
||||
}
|
||||
|
||||
// TableName define the BlockTrace table name
|
||||
func (*BlockTrace) TableName() string {
|
||||
return "block_trace"
|
||||
}
|
||||
|
||||
// GetL2BlocksLatestHeight get the l2 blocks latest height
|
||||
func (o *BlockTrace) GetL2BlocksLatestHeight() (int64, error) {
|
||||
result := o.db.Model(&BlockTrace{}).Select("COALESCE(MAX(number), -1)").Row()
|
||||
if result.Err() != nil {
|
||||
return -1, result.Err()
|
||||
}
|
||||
var maxNumber int64
|
||||
if err := result.Scan(&maxNumber); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return maxNumber, nil
|
||||
}
|
||||
|
||||
// GetL2WrappedBlocks get the l2 wrapped blocks
|
||||
func (o *BlockTrace) GetL2WrappedBlocks(fields map[string]interface{}) ([]*types.WrappedBlock, error) {
|
||||
var blockTraces []BlockTrace
|
||||
db := o.db.Select("trace")
|
||||
for key, value := range fields {
|
||||
db = db.Where(key, value)
|
||||
}
|
||||
if err := db.Find(&blockTraces).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var wrappedBlocks []*types.WrappedBlock
|
||||
for _, v := range blockTraces {
|
||||
var wrappedBlock types.WrappedBlock
|
||||
if err := json.Unmarshal([]byte(v.Trace), &wrappedBlock); err != nil {
|
||||
break
|
||||
}
|
||||
wrappedBlocks = append(wrappedBlocks, &wrappedBlock)
|
||||
}
|
||||
return wrappedBlocks, nil
|
||||
}
|
||||
|
||||
// GetL2BlockInfos get l2 block infos
|
||||
func (o *BlockTrace) GetL2BlockInfos(fields map[string]interface{}, orderByList []string, limit int) ([]BlockTrace, error) {
|
||||
var blockTraces []BlockTrace
|
||||
db := o.db.Select("number, hash, parent_hash, batch_hash, tx_num, gas_used, block_timestamp")
|
||||
for key, value := range fields {
|
||||
db = db.Where(key, value)
|
||||
}
|
||||
|
||||
for _, orderBy := range orderByList {
|
||||
db = db.Order(orderBy)
|
||||
}
|
||||
|
||||
if limit != 0 {
|
||||
db = db.Limit(limit)
|
||||
}
|
||||
|
||||
if err := db.Find(&blockTraces).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return blockTraces, nil
|
||||
}
|
||||
|
||||
// GetUnbatchedL2Blocks get unbatched l2 blocks
|
||||
func (o *BlockTrace) GetUnbatchedL2Blocks(fields map[string]interface{}, orderByList []string, limit int) ([]BlockTrace, error) {
|
||||
var unbatchedBlockTraces []BlockTrace
|
||||
db := o.db.Select("number, hash, parent_hash, batch_hash, tx_num, gas_used, block_timestamp").Where("batch_hash is NULL")
|
||||
for key, value := range fields {
|
||||
db = db.Where(key, value)
|
||||
}
|
||||
if err := db.Find(&unbatchedBlockTraces).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return unbatchedBlockTraces, nil
|
||||
}
|
||||
|
||||
// InsertWrappedBlocks insert block to block trace
|
||||
func (o *BlockTrace) InsertWrappedBlocks(blocks []*types.WrappedBlock) error {
|
||||
var blockTraces []BlockTrace
|
||||
for _, block := range blocks {
|
||||
number := block.Header.Number.Uint64()
|
||||
hash := block.Header.Hash().String()
|
||||
txNum := len(block.Transactions)
|
||||
mtime := block.Header.Time
|
||||
gasCost := block.Header.GasUsed
|
||||
|
||||
data, err := json.Marshal(block)
|
||||
if err != nil {
|
||||
log.Error("failed to marshal block", "hash", hash, "err", err)
|
||||
return err
|
||||
}
|
||||
|
||||
tmpBlockTrace := BlockTrace{
|
||||
Number: number,
|
||||
Hash: hash,
|
||||
ParentHash: block.Header.ParentHash.String(),
|
||||
Trace: string(data),
|
||||
TxNum: uint64(txNum),
|
||||
GasUsed: gasCost,
|
||||
BlockTimestamp: mtime,
|
||||
}
|
||||
blockTraces = append(blockTraces, tmpBlockTrace)
|
||||
}
|
||||
|
||||
if err := o.db.Create(&blockTraces).Error; err != nil {
|
||||
log.Error("failed to insert blockTraces", "err", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateBatchHashForL2Blocks update the batch_hash of block trace
|
||||
func (o *BlockTrace) UpdateBatchHashForL2Blocks(tx *gorm.DB, numbers []uint64, batchHash string) error {
|
||||
var db *gorm.DB
|
||||
if tx != nil {
|
||||
db = tx
|
||||
} else {
|
||||
db = o.db
|
||||
}
|
||||
|
||||
err := db.Model(&BlockTrace{}).Where("number IN (?)", numbers).Update("batch_hash", batchHash).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
package orm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"scroll-tech/common/types"
|
||||
|
||||
bridgeTypes "scroll-tech/bridge/internal/types"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/log"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Chunk represents a chunk of blocks in the database.
|
||||
type Chunk struct {
|
||||
db *gorm.DB `gorm:"-"`
|
||||
|
||||
// chunk
|
||||
Index uint64 `json:"index" gorm:"column:index"`
|
||||
Hash string `json:"hash" gorm:"column:hash"`
|
||||
StartBlockNumber uint64 `json:"start_block_number" gorm:"column:start_block_number"`
|
||||
StartBlockHash string `json:"start_block_hash" gorm:"column:start_block_hash"`
|
||||
EndBlockNumber uint64 `json:"end_block_number" gorm:"column:end_block_number"`
|
||||
EndBlockHash string `json:"end_block_hash" gorm:"column:end_block_hash"`
|
||||
StartBlockTime uint64 `json:"start_block_time" gorm:"column:start_block_time"`
|
||||
TotalL1MessagesPoppedBefore uint64 `json:"total_l1_messages_popped_before" gorm:"column:total_l1_messages_popped_before"`
|
||||
TotalL1MessagesPoppedInChunk uint64 `json:"total_l1_messages_popped_in_chunk" gorm:"column:total_l1_messages_popped_in_chunk"`
|
||||
|
||||
// proof
|
||||
ProvingStatus int16 `json:"proving_status" gorm:"column:proving_status;default:1"`
|
||||
Proof []byte `json:"proof" gorm:"column:proof;default:NULL"`
|
||||
ProverAssignedAt *time.Time `json:"prover_assigned_at" gorm:"column:prover_assigned_at;default:NULL"`
|
||||
ProvedAt *time.Time `json:"proved_at" gorm:"column:proved_at;default:NULL"`
|
||||
ProofTimeSec int16 `json:"proof_time_sec" gorm:"column:proof_time_sec;default:NULL"`
|
||||
|
||||
// batch
|
||||
BatchHash string `json:"batch_hash" gorm:"column:batch_hash;default:NULL"`
|
||||
|
||||
// metadata
|
||||
TotalL2TxGas uint64 `json:"total_l2_tx_gas" gorm:"column:total_l2_tx_gas"`
|
||||
TotalL2TxNum uint64 `json:"total_l2_tx_num" gorm:"column:total_l2_tx_num"`
|
||||
TotalL1CommitCalldataSize uint64 `json:"total_l1_commit_calldata_size" gorm:"column:total_l1_commit_calldata_size"`
|
||||
TotalL1CommitGas uint64 `json:"total_l1_commit_gas" gorm:"column:total_l1_commit_gas"`
|
||||
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"column:deleted_at;default:NULL"`
|
||||
}
|
||||
|
||||
// NewChunk creates a new Chunk database instance.
|
||||
func NewChunk(db *gorm.DB) *Chunk {
|
||||
return &Chunk{db: db}
|
||||
}
|
||||
|
||||
// TableName returns the table name for the chunk model.
|
||||
func (*Chunk) TableName() string {
|
||||
return "chunk"
|
||||
}
|
||||
|
||||
// GetChunksInRange retrieves chunks within a given range (inclusive) from the database.
|
||||
// The range is closed, i.e., it includes both start and end indices.
|
||||
// The returned chunks are sorted in ascending order by their index.
|
||||
func (o *Chunk) GetChunksInRange(ctx context.Context, startIndex uint64, endIndex uint64) ([]*Chunk, error) {
|
||||
if startIndex > endIndex {
|
||||
return nil, errors.New("start index should be less than or equal to end index")
|
||||
}
|
||||
|
||||
var chunks []*Chunk
|
||||
db := o.db.WithContext(ctx).Where("index >= ? AND index <= ?", startIndex, endIndex)
|
||||
db = db.Order("index ASC")
|
||||
|
||||
if err := db.Find(&chunks).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if startIndex+uint64(len(chunks)) != endIndex+1 {
|
||||
return nil, errors.New("number of chunks not expected in the specified range")
|
||||
}
|
||||
|
||||
return chunks, nil
|
||||
}
|
||||
|
||||
// GetUnbatchedChunks retrieves unbatched chunks from the database.
|
||||
func (o *Chunk) GetUnbatchedChunks(ctx context.Context) ([]*Chunk, error) {
|
||||
var chunks []*Chunk
|
||||
err := o.db.WithContext(ctx).
|
||||
Where("batch_hash IS NULL").
|
||||
Order("index asc").
|
||||
Find(&chunks).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return chunks, nil
|
||||
}
|
||||
|
||||
// GetLatestChunk retrieves the latest chunk from the database.
|
||||
func (o *Chunk) GetLatestChunk(ctx context.Context) (*Chunk, error) {
|
||||
var latestChunk Chunk
|
||||
err := o.db.WithContext(ctx).
|
||||
Order("index desc").
|
||||
First(&latestChunk).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &latestChunk, nil
|
||||
}
|
||||
|
||||
// InsertChunk inserts a new chunk into the database.
|
||||
func (o *Chunk) InsertChunk(ctx context.Context, chunk *bridgeTypes.Chunk, dbTX ...*gorm.DB) (*Chunk, error) {
|
||||
if chunk == nil || len(chunk.Blocks) == 0 {
|
||||
return nil, errors.New("invalid args")
|
||||
}
|
||||
|
||||
db := o.db
|
||||
if len(dbTX) > 0 && dbTX[0] != nil {
|
||||
db = dbTX[0]
|
||||
}
|
||||
|
||||
var chunkIndex uint64
|
||||
var totalL1MessagePoppedBefore uint64
|
||||
parentChunk, err := o.GetLatestChunk(ctx)
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
log.Error("failed to get latest chunk", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// if parentChunk==nil then err==gorm.ErrRecordNotFound, which means there's
|
||||
// not chunk record in the db, we then use default empty values for the creating chunk;
|
||||
// if parentChunk!=nil then err=nil, then we fill the parentChunk-related data into the creating chunk
|
||||
if parentChunk != nil {
|
||||
chunkIndex = parentChunk.Index + 1
|
||||
totalL1MessagePoppedBefore = parentChunk.TotalL1MessagesPoppedBefore + parentChunk.TotalL1MessagesPoppedInChunk
|
||||
}
|
||||
|
||||
hash, err := chunk.Hash(totalL1MessagePoppedBefore)
|
||||
if err != nil {
|
||||
log.Error("failed to get chunk hash", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var totalL2TxGas uint64
|
||||
var totalL2TxNum uint64
|
||||
var totalL1CommitCalldataSize uint64
|
||||
var totalL1CommitGas uint64
|
||||
for _, block := range chunk.Blocks {
|
||||
totalL2TxGas += block.Header.GasUsed
|
||||
totalL2TxNum += block.L2TxsNum()
|
||||
totalL1CommitCalldataSize += block.EstimateL1CommitCalldataSize()
|
||||
totalL1CommitGas += block.EstimateL1CommitGas()
|
||||
}
|
||||
|
||||
numBlocks := len(chunk.Blocks)
|
||||
newChunk := Chunk{
|
||||
Index: chunkIndex,
|
||||
Hash: hash.Hex(),
|
||||
StartBlockNumber: chunk.Blocks[0].Header.Number.Uint64(),
|
||||
StartBlockHash: chunk.Blocks[0].Header.Hash().Hex(),
|
||||
EndBlockNumber: chunk.Blocks[numBlocks-1].Header.Number.Uint64(),
|
||||
EndBlockHash: chunk.Blocks[numBlocks-1].Header.Hash().Hex(),
|
||||
TotalL2TxGas: totalL2TxGas,
|
||||
TotalL2TxNum: totalL2TxNum,
|
||||
TotalL1CommitCalldataSize: totalL1CommitCalldataSize,
|
||||
TotalL1CommitGas: totalL1CommitGas,
|
||||
StartBlockTime: chunk.Blocks[0].Header.Time,
|
||||
TotalL1MessagesPoppedBefore: totalL1MessagePoppedBefore,
|
||||
TotalL1MessagesPoppedInChunk: chunk.NumL1Messages(totalL1MessagePoppedBefore),
|
||||
ProvingStatus: int16(types.ProvingTaskUnassigned),
|
||||
}
|
||||
|
||||
if err := db.Create(&newChunk).Error; err != nil {
|
||||
log.Error("failed to insert chunk", "hash", hash, "err", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &newChunk, nil
|
||||
}
|
||||
|
||||
// UpdateProvingStatus updates the proving status of a chunk.
|
||||
func (o *Chunk) UpdateProvingStatus(ctx context.Context, hash string, status types.ProvingStatus, dbTX ...*gorm.DB) error {
|
||||
db := o.db
|
||||
if len(dbTX) > 0 && dbTX[0] != nil {
|
||||
db = dbTX[0]
|
||||
}
|
||||
|
||||
updateFields := make(map[string]interface{})
|
||||
updateFields["proving_status"] = int(status)
|
||||
|
||||
switch status {
|
||||
case types.ProvingTaskAssigned:
|
||||
updateFields["prover_assigned_at"] = time.Now()
|
||||
case types.ProvingTaskUnassigned:
|
||||
updateFields["prover_assigned_at"] = nil
|
||||
case types.ProvingTaskProved, types.ProvingTaskVerified:
|
||||
updateFields["proved_at"] = time.Now()
|
||||
default:
|
||||
}
|
||||
|
||||
if err := db.Model(&Chunk{}).Where("hash", hash).Updates(updateFields).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateBatchHashInRange updates the batch_hash for chunks within the specified range (inclusive).
|
||||
// The range is closed, i.e., it includes both start and end indices.
|
||||
func (o *Chunk) UpdateBatchHashInRange(ctx context.Context, startIndex uint64, endIndex uint64, batchHash string, dbTX ...*gorm.DB) error {
|
||||
db := o.db
|
||||
if len(dbTX) > 0 && dbTX[0] != nil {
|
||||
db = dbTX[0]
|
||||
}
|
||||
db = db.Model(&Chunk{}).Where("index >= ? AND index <= ?", startIndex, endIndex)
|
||||
|
||||
if err := db.Update("batch_hash", batchHash).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
38
bridge/internal/orm/common.go
Normal file
38
bridge/internal/orm/common.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package orm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
bridgeTypes "scroll-tech/bridge/internal/types"
|
||||
)
|
||||
|
||||
// AddBatchInfoToDB inserts the batch information to the BlockBatch table and updates the batch_hash
|
||||
// in all blocks included in the batch.
|
||||
func AddBatchInfoToDB(db *gorm.DB, batchData *bridgeTypes.BatchData) error {
|
||||
blockBatch := NewBlockBatch(db)
|
||||
blockTrace := NewBlockTrace(db)
|
||||
err := db.Transaction(func(tx *gorm.DB) error {
|
||||
rowsAffected, dbTxErr := blockBatch.InsertBlockBatchByBatchData(tx, batchData)
|
||||
if dbTxErr != nil {
|
||||
return dbTxErr
|
||||
}
|
||||
if rowsAffected != 1 {
|
||||
dbTxErr = errors.New("the InsertBlockBatchByBatchData affected row is not 1")
|
||||
return dbTxErr
|
||||
}
|
||||
|
||||
var blockIDs = make([]uint64, len(batchData.Batch.Blocks))
|
||||
for i, block := range batchData.Batch.Blocks {
|
||||
blockIDs[i] = block.BlockNumber
|
||||
}
|
||||
|
||||
dbTxErr = blockTrace.UpdateBatchHashForL2Blocks(tx, blockIDs, batchData.Hash().Hex())
|
||||
if dbTxErr != nil {
|
||||
return dbTxErr
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
@@ -52,16 +52,6 @@ func (m *L1Message) GetLayer1LatestWatchedHeight() (int64, error) {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
// GetLayer1LatestMessageWithLayer2Hash returns latest l1 message with layer2 hash
|
||||
func (m *L1Message) GetLayer1LatestMessageWithLayer2Hash() (*L1Message, error) {
|
||||
var msg *L1Message
|
||||
err := m.db.Where("layer2_hash IS NOT NULL").Order("queue_index DESC").First(&msg).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
// GetL1MessagesByStatus fetch list of unprocessed messages given msg status
|
||||
func (m *L1Message) GetL1MessagesByStatus(status types.MsgStatus, limit uint64) ([]L1Message, error) {
|
||||
var msgs []L1Message
|
||||
|
||||
@@ -1,208 +0,0 @@
|
||||
package orm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
gethTypes "github.com/scroll-tech/go-ethereum/core/types"
|
||||
"github.com/scroll-tech/go-ethereum/log"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"scroll-tech/bridge/internal/types"
|
||||
)
|
||||
|
||||
// L2Block represents a l2 block in the database.
|
||||
type L2Block struct {
|
||||
db *gorm.DB `gorm:"column:-"`
|
||||
|
||||
Number uint64 `json:"number" gorm:"number"`
|
||||
Hash string `json:"hash" gorm:"hash"`
|
||||
ParentHash string `json:"parent_hash" gorm:"parent_hash"`
|
||||
Header string `json:"header" gorm:"header"`
|
||||
Transactions string `json:"transactions" gorm:"transactions"`
|
||||
WithdrawTrieRoot string `json:"withdraw_trie_root" gorm:"withdraw_trie_root"`
|
||||
TxNum uint64 `json:"tx_num" gorm:"tx_num"`
|
||||
GasUsed uint64 `json:"gas_used" gorm:"gas_used"`
|
||||
BlockTimestamp uint64 `json:"block_timestamp" gorm:"block_timestamp"`
|
||||
ChunkHash string `json:"chunk_hash" gorm:"chunk_hash;default:NULL"`
|
||||
}
|
||||
|
||||
// NewL2Block creates a new L2Block instance
|
||||
func NewL2Block(db *gorm.DB) *L2Block {
|
||||
return &L2Block{db: db}
|
||||
}
|
||||
|
||||
// TableName returns the name of the "l2_block" table.
|
||||
func (*L2Block) TableName() string {
|
||||
return "l2_block"
|
||||
}
|
||||
|
||||
// GetL2BlocksLatestHeight retrieves the height of the latest L2 block.
|
||||
// If the l2_block table is empty, it returns 0 to represent the genesis block height.
|
||||
// In case of an error, it returns -1 along with the error.
|
||||
func (o *L2Block) GetL2BlocksLatestHeight(ctx context.Context) (int64, error) {
|
||||
var maxNumber int64
|
||||
if err := o.db.WithContext(ctx).Model(&L2Block{}).Select("COALESCE(MAX(number), 0)").Row().Scan(&maxNumber); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
return maxNumber, nil
|
||||
}
|
||||
|
||||
// GetUnchunkedBlocks get the l2 blocks that have not been put into a chunk.
|
||||
// The returned blocks are sorted in ascending order by their block number.
|
||||
func (o *L2Block) GetUnchunkedBlocks(ctx context.Context) ([]*types.WrappedBlock, error) {
|
||||
var l2Blocks []L2Block
|
||||
if err := o.db.WithContext(ctx).Select("header, transactions, withdraw_trie_root").
|
||||
Where("chunk_hash IS NULL").
|
||||
Order("number asc").
|
||||
Find(&l2Blocks).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var wrappedBlocks []*types.WrappedBlock
|
||||
for _, v := range l2Blocks {
|
||||
var wrappedBlock types.WrappedBlock
|
||||
|
||||
if err := json.Unmarshal([]byte(v.Transactions), &wrappedBlock.Transactions); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
wrappedBlock.Header = &gethTypes.Header{}
|
||||
if err := json.Unmarshal([]byte(v.Header), wrappedBlock.Header); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
wrappedBlock.WithdrawTrieRoot = common.HexToHash(v.WithdrawTrieRoot)
|
||||
wrappedBlocks = append(wrappedBlocks, &wrappedBlock)
|
||||
}
|
||||
|
||||
return wrappedBlocks, nil
|
||||
}
|
||||
|
||||
// GetL2Blocks retrieves selected L2Blocks from the database.
|
||||
// The returned L2Blocks are sorted in ascending order by their block number.
|
||||
func (o *L2Block) GetL2Blocks(ctx context.Context, fields map[string]interface{}, orderByList []string, limit int) ([]*L2Block, error) {
|
||||
db := o.db.WithContext(ctx)
|
||||
|
||||
for key, value := range fields {
|
||||
db = db.Where(key, value)
|
||||
}
|
||||
|
||||
for _, orderBy := range orderByList {
|
||||
db = db.Order(orderBy)
|
||||
}
|
||||
|
||||
if limit > 0 {
|
||||
db = db.Limit(limit)
|
||||
}
|
||||
|
||||
db = db.Order("number ASC")
|
||||
|
||||
var l2Blocks []*L2Block
|
||||
if err := db.Find(&l2Blocks).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return l2Blocks, nil
|
||||
}
|
||||
|
||||
// GetL2BlocksInRange retrieves the L2 blocks within the specified range (inclusive).
|
||||
// The range is closed, i.e., it includes both start and end block numbers.
|
||||
// The returned blocks are sorted in ascending order by their block number.
|
||||
func (o *L2Block) GetL2BlocksInRange(ctx context.Context, startBlockNumber uint64, endBlockNumber uint64) ([]*types.WrappedBlock, error) {
|
||||
if startBlockNumber > endBlockNumber {
|
||||
return nil, errors.New("start block number should be less than or equal to end block number")
|
||||
}
|
||||
|
||||
var l2Blocks []L2Block
|
||||
db := o.db.WithContext(ctx)
|
||||
db = db.Where("number >= ? AND number <= ?", startBlockNumber, endBlockNumber)
|
||||
db = db.Order("number ASC")
|
||||
|
||||
if err := db.Find(&l2Blocks).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if uint64(len(l2Blocks)) != endBlockNumber-startBlockNumber+1 {
|
||||
return nil, errors.New("number of blocks not expected in the specified range")
|
||||
}
|
||||
|
||||
var wrappedBlocks []*types.WrappedBlock
|
||||
for _, v := range l2Blocks {
|
||||
var wrappedBlock types.WrappedBlock
|
||||
|
||||
if err := json.Unmarshal([]byte(v.Transactions), &wrappedBlock.Transactions); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
wrappedBlock.Header = &gethTypes.Header{}
|
||||
if err := json.Unmarshal([]byte(v.Header), wrappedBlock.Header); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
wrappedBlock.WithdrawTrieRoot = common.HexToHash(v.WithdrawTrieRoot)
|
||||
wrappedBlocks = append(wrappedBlocks, &wrappedBlock)
|
||||
}
|
||||
|
||||
return wrappedBlocks, nil
|
||||
}
|
||||
|
||||
// InsertL2Blocks inserts l2 blocks into the "l2_block" table.
|
||||
func (o *L2Block) InsertL2Blocks(ctx context.Context, blocks []*types.WrappedBlock) error {
|
||||
var l2Blocks []L2Block
|
||||
for _, block := range blocks {
|
||||
header, err := json.Marshal(block.Header)
|
||||
if err != nil {
|
||||
log.Error("failed to marshal block header", "hash", block.Header.Hash().String(), "err", err)
|
||||
return err
|
||||
}
|
||||
|
||||
txs, err := json.Marshal(block.Transactions)
|
||||
if err != nil {
|
||||
log.Error("failed to marshal transactions", "hash", block.Header.Hash().String(), "err", err)
|
||||
return err
|
||||
}
|
||||
|
||||
l2Block := L2Block{
|
||||
Number: block.Header.Number.Uint64(),
|
||||
Hash: block.Header.Hash().String(),
|
||||
ParentHash: block.Header.ParentHash.String(),
|
||||
Transactions: string(txs),
|
||||
WithdrawTrieRoot: block.WithdrawTrieRoot.Hex(),
|
||||
TxNum: uint64(len(block.Transactions)),
|
||||
GasUsed: block.Header.GasUsed,
|
||||
BlockTimestamp: block.Header.Time,
|
||||
Header: string(header),
|
||||
}
|
||||
l2Blocks = append(l2Blocks, l2Block)
|
||||
}
|
||||
|
||||
if err := o.db.WithContext(ctx).Create(&l2Blocks).Error; err != nil {
|
||||
log.Error("failed to insert l2Blocks", "err", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateChunkHashInRange updates the chunk_hash of block tx within the specified range (inclusive).
|
||||
// The range is closed, i.e., it includes both start and end indices.
|
||||
// This function ensures the number of rows updated must equal to (endIndex - startIndex + 1).
|
||||
// If the rows affected do not match this expectation, an error is returned.
|
||||
func (o *L2Block) UpdateChunkHashInRange(ctx context.Context, startIndex uint64, endIndex uint64, chunkHash string, dbTX ...*gorm.DB) error {
|
||||
db := o.db
|
||||
if len(dbTX) > 0 && dbTX[0] != nil {
|
||||
db = dbTX[0]
|
||||
}
|
||||
|
||||
db = db.WithContext(ctx).Model(&L2Block{}).Where("number >= ? AND number <= ?", startIndex, endIndex)
|
||||
tx := db.Update("chunk_hash", chunkHash)
|
||||
|
||||
if tx.RowsAffected != int64(endIndex-startIndex+1) {
|
||||
return fmt.Errorf("expected %d rows to be updated, got %d", endIndex-startIndex+1, tx.RowsAffected)
|
||||
}
|
||||
|
||||
return tx.Error
|
||||
}
|
||||
127
bridge/internal/orm/l2_message.go
Normal file
127
bridge/internal/orm/l2_message.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package orm
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/log"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"scroll-tech/common/types"
|
||||
)
|
||||
|
||||
// L2Message is structure of stored layer2 bridge message
|
||||
type L2Message struct {
|
||||
db *gorm.DB `gorm:"column:-"`
|
||||
|
||||
Nonce uint64 `json:"nonce" gorm:"column:nonce"`
|
||||
MsgHash string `json:"msg_hash" gorm:"column:msg_hash"`
|
||||
Height uint64 `json:"height" gorm:"column:height"`
|
||||
Sender string `json:"sender" gorm:"column:sender"`
|
||||
Value string `json:"value" gorm:"column:value"`
|
||||
Target string `json:"target" gorm:"column:target"`
|
||||
Calldata string `json:"calldata" gorm:"column:calldata"`
|
||||
Layer2Hash string `json:"layer2_hash" gorm:"column:layer2_hash"`
|
||||
Layer1Hash string `json:"layer1_hash" gorm:"column:layer1_hash;default:NULL"`
|
||||
Proof string `json:"proof" gorm:"column:proof;default:NULL"`
|
||||
Status int `json:"status" gorm:"column:status;default:1"`
|
||||
}
|
||||
|
||||
// NewL2Message create an L2Message instance
|
||||
func NewL2Message(db *gorm.DB) *L2Message {
|
||||
return &L2Message{db: db}
|
||||
}
|
||||
|
||||
// TableName define the L2Message table name
|
||||
func (*L2Message) TableName() string {
|
||||
return "l2_message"
|
||||
}
|
||||
|
||||
// GetL2Messages fetch list of messages given msg status
|
||||
func (m *L2Message) GetL2Messages(fields map[string]interface{}, orderByList []string, limit int) ([]L2Message, error) {
|
||||
var l2MsgList []L2Message
|
||||
db := m.db
|
||||
for key, value := range fields {
|
||||
db = db.Where(key, value)
|
||||
}
|
||||
|
||||
for _, orderBy := range orderByList {
|
||||
db = db.Order(orderBy)
|
||||
}
|
||||
|
||||
if limit != 0 {
|
||||
db = db.Limit(limit)
|
||||
}
|
||||
|
||||
if err := db.Find(&l2MsgList).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return l2MsgList, nil
|
||||
}
|
||||
|
||||
// GetLayer2LatestWatchedHeight returns latest height stored in the table
|
||||
func (m *L2Message) GetLayer2LatestWatchedHeight() (int64, error) {
|
||||
// @note It's not correct, since we may don't have message in some blocks.
|
||||
// But it will only be called at start, some redundancy is acceptable.
|
||||
result := m.db.Model(&L2Message{}).Select("COALESCE(MAX(height), -1)").Row()
|
||||
if result.Err() != nil {
|
||||
return -1, result.Err()
|
||||
}
|
||||
|
||||
var maxNumber int64
|
||||
if err := result.Scan(&maxNumber); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return maxNumber, nil
|
||||
}
|
||||
|
||||
// GetL2MessageByNonce fetch message by nonce
|
||||
// for unit test
|
||||
func (m *L2Message) GetL2MessageByNonce(nonce uint64) (*L2Message, error) {
|
||||
var msg L2Message
|
||||
err := m.db.Where("nonce", nonce).First(&msg).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &msg, nil
|
||||
}
|
||||
|
||||
// SaveL2Messages batch save a list of layer2 messages
|
||||
func (m *L2Message) SaveL2Messages(ctx context.Context, messages []L2Message) error {
|
||||
if len(messages) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := m.db.WithContext(ctx).Create(&messages).Error
|
||||
if err != nil {
|
||||
nonces := make([]uint64, 0, len(messages))
|
||||
heights := make([]uint64, 0, len(messages))
|
||||
for _, msg := range messages {
|
||||
nonces = append(nonces, msg.Nonce)
|
||||
heights = append(heights, msg.Height)
|
||||
}
|
||||
log.Error("failed to insert layer2Messages", "nonces", nonces, "heights", heights, "err", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateLayer2Status updates message stauts, given message hash
|
||||
func (m *L2Message) UpdateLayer2Status(ctx context.Context, msgHash string, status types.MsgStatus) error {
|
||||
err := m.db.Model(&L2Message{}).WithContext(ctx).Where("msg_hash", msgHash).Update("status", int(status)).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateLayer2StatusAndLayer1Hash updates message stauts and layer1 transaction hash, given message hash
|
||||
func (m *L2Message) UpdateLayer2StatusAndLayer1Hash(ctx context.Context, msgHash string, status types.MsgStatus, layer1Hash string) error {
|
||||
updateFields := map[string]interface{}{
|
||||
"status": int(status),
|
||||
"layer1_hash": layer1Hash,
|
||||
}
|
||||
err := m.db.Model(&L2Message{}).WithContext(ctx).Where("msg_hash", msgHash).Updates(updateFields).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
38
bridge/internal/orm/migrate/migrations/00001_block_trace.sql
Normal file
38
bridge/internal/orm/migrate/migrations/00001_block_trace.sql
Normal file
@@ -0,0 +1,38 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
|
||||
-- TODO: use foreign key for batch_id?
|
||||
-- TODO: why tx_num is bigint?
|
||||
create table block_trace
|
||||
(
|
||||
number BIGINT NOT NULL,
|
||||
hash VARCHAR NOT NULL,
|
||||
parent_hash VARCHAR NOT NULL,
|
||||
trace JSON NOT NULL,
|
||||
batch_hash VARCHAR DEFAULT NULL,
|
||||
tx_num INTEGER NOT NULL,
|
||||
gas_used BIGINT NOT NULL,
|
||||
block_timestamp NUMERIC NOT NULL
|
||||
);
|
||||
|
||||
create unique index block_trace_hash_uindex
|
||||
on block_trace (hash);
|
||||
|
||||
create unique index block_trace_number_uindex
|
||||
on block_trace (number);
|
||||
|
||||
create unique index block_trace_parent_uindex
|
||||
on block_trace (number, parent_hash);
|
||||
|
||||
create unique index block_trace_parent_hash_uindex
|
||||
on block_trace (hash, parent_hash);
|
||||
|
||||
create index block_trace_batch_hash_index
|
||||
on block_trace (batch_hash);
|
||||
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
drop table if exists block_trace;
|
||||
-- +goose StatementEnd
|
||||
@@ -13,8 +13,8 @@ create table l1_message
|
||||
layer1_hash VARCHAR NOT NULL,
|
||||
layer2_hash VARCHAR DEFAULT NULL,
|
||||
status INTEGER DEFAULT 1,
|
||||
created_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
created_time TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_time TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
comment
|
||||
@@ -29,6 +29,19 @@ on l1_message (queue_index);
|
||||
create index l1_message_height_index
|
||||
on l1_message (height);
|
||||
|
||||
CREATE OR REPLACE FUNCTION update_timestamp()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_time = CURRENT_TIMESTAMP;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
CREATE TRIGGER update_timestamp BEFORE UPDATE
|
||||
ON l1_message FOR EACH ROW EXECUTE PROCEDURE
|
||||
update_timestamp();
|
||||
|
||||
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
@@ -1,32 +0,0 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
|
||||
create table l2_block
|
||||
(
|
||||
number BIGINT NOT NULL,
|
||||
hash VARCHAR NOT NULL,
|
||||
parent_hash VARCHAR NOT NULL,
|
||||
header TEXT NOT NULL,
|
||||
transactions TEXT NOT NULL,
|
||||
withdraw_trie_root VARCHAR NOT NULL,
|
||||
tx_num INTEGER NOT NULL,
|
||||
gas_used BIGINT NOT NULL,
|
||||
block_timestamp NUMERIC NOT NULL,
|
||||
chunk_hash VARCHAR DEFAULT NULL
|
||||
);
|
||||
|
||||
create unique index l2_block_hash_uindex
|
||||
on l2_block (hash);
|
||||
|
||||
create unique index l2_block_number_uindex
|
||||
on l2_block (number);
|
||||
|
||||
create index l2_block_chunk_hash_index
|
||||
on l2_block (chunk_hash);
|
||||
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
drop table if exists l2_block;
|
||||
-- +goose StatementEnd
|
||||
50
bridge/internal/orm/migrate/migrations/00003_l2_message.sql
Normal file
50
bridge/internal/orm/migrate/migrations/00003_l2_message.sql
Normal file
@@ -0,0 +1,50 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
create table l2_message
|
||||
(
|
||||
nonce BIGINT NOT NULL,
|
||||
msg_hash VARCHAR NOT NULL,
|
||||
height BIGINT NOT NULL,
|
||||
sender VARCHAR NOT NULL,
|
||||
target VARCHAR NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
calldata TEXT NOT NULL,
|
||||
layer2_hash VARCHAR NOT NULL,
|
||||
layer1_hash VARCHAR DEFAULT NULL,
|
||||
proof TEXT DEFAULT NULL,
|
||||
status INTEGER DEFAULT 1,
|
||||
created_time TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_time TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
comment
|
||||
on column l2_message.status is 'undefined, pending, submitted, confirmed, failed, expired, relay_failed';
|
||||
|
||||
create unique index l2_message_hash_uindex
|
||||
on l2_message (msg_hash);
|
||||
|
||||
create unique index l2_message_nonce_uindex
|
||||
on l2_message (nonce);
|
||||
|
||||
create index l2_message_height_index
|
||||
on l2_message (height);
|
||||
|
||||
CREATE OR REPLACE FUNCTION update_timestamp()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_time = CURRENT_TIMESTAMP;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
CREATE TRIGGER update_timestamp BEFORE UPDATE
|
||||
ON l2_message FOR EACH ROW EXECUTE PROCEDURE
|
||||
update_timestamp();
|
||||
|
||||
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
drop table if exists l2_message;
|
||||
-- +goose StatementEnd
|
||||
50
bridge/internal/orm/migrate/migrations/00004_block_batch.sql
Normal file
50
bridge/internal/orm/migrate/migrations/00004_block_batch.sql
Normal file
@@ -0,0 +1,50 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
|
||||
create table block_batch
|
||||
(
|
||||
hash VARCHAR NOT NULL,
|
||||
index BIGINT NOT NULL,
|
||||
start_block_number BIGINT NOT NULL,
|
||||
start_block_hash VARCHAR NOT NULL,
|
||||
end_block_number BIGINT NOT NULL,
|
||||
end_block_hash VARCHAR NOT NULL,
|
||||
parent_hash VARCHAR NOT NULL,
|
||||
state_root VARCHAR NOT NULL,
|
||||
total_tx_num BIGINT NOT NULL,
|
||||
total_l1_tx_num BIGINT NOT NULL,
|
||||
total_l2_gas BIGINT NOT NULL,
|
||||
proving_status INTEGER DEFAULT 1,
|
||||
proof BYTEA DEFAULT NULL,
|
||||
instance_commitments BYTEA DEFAULT NULL,
|
||||
proof_time_sec INTEGER DEFAULT 0,
|
||||
rollup_status INTEGER DEFAULT 1,
|
||||
commit_tx_hash VARCHAR DEFAULT NULL,
|
||||
finalize_tx_hash VARCHAR DEFAULT NULL,
|
||||
oracle_status INTEGER DEFAULT 1,
|
||||
oracle_tx_hash VARCHAR DEFAULT NULL,
|
||||
created_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
prover_assigned_at TIMESTAMP(0) DEFAULT NULL,
|
||||
proved_at TIMESTAMP(0) DEFAULT NULL,
|
||||
committed_at TIMESTAMP(0) DEFAULT NULL,
|
||||
finalized_at TIMESTAMP(0) DEFAULT NULL
|
||||
);
|
||||
|
||||
comment
|
||||
on column block_batch.proving_status is 'undefined, unassigned, skipped, assigned, proved, verified, failed';
|
||||
comment
|
||||
on column block_batch.rollup_status is 'undefined, pending, committing, committed, finalizing, finalized, finalization_skipped, commit_failed, finalize_failed';
|
||||
comment
|
||||
on column block_batch.oracle_status is 'undefined, pending, importing, imported, failed';
|
||||
|
||||
create unique index block_batch_hash_uindex
|
||||
on block_batch (hash);
|
||||
create unique index block_batch_index_uindex
|
||||
on block_batch (index);
|
||||
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
drop table if exists block_batch;
|
||||
-- +goose StatementEnd
|
||||
@@ -1,54 +0,0 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
|
||||
create table chunk
|
||||
(
|
||||
-- chunk
|
||||
index BIGINT NOT NULL,
|
||||
hash VARCHAR NOT NULL,
|
||||
start_block_number BIGINT NOT NULL,
|
||||
start_block_hash VARCHAR NOT NULL,
|
||||
end_block_number BIGINT NOT NULL,
|
||||
end_block_hash VARCHAR NOT NULL,
|
||||
total_l1_messages_popped_before BIGINT NOT NULL,
|
||||
total_l1_messages_popped_in_chunk BIGINT NOT NULL,
|
||||
start_block_time BIGINT NOT NULL,
|
||||
|
||||
-- proof
|
||||
proving_status SMALLINT NOT NULL DEFAULT 1,
|
||||
proof BYTEA DEFAULT NULL,
|
||||
prover_assigned_at TIMESTAMP(0) DEFAULT NULL,
|
||||
proved_at TIMESTAMP(0) DEFAULT NULL,
|
||||
proof_time_sec SMALLINT DEFAULT NULL,
|
||||
|
||||
-- batch
|
||||
batch_hash VARCHAR DEFAULT NULL,
|
||||
|
||||
-- metadata
|
||||
total_l2_tx_gas BIGINT NOT NULL,
|
||||
total_l2_tx_num BIGINT NOT NULL,
|
||||
total_l1_commit_calldata_size BIGINT NOT NULL,
|
||||
total_l1_commit_gas BIGINT NOT NULL,
|
||||
created_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP(0) DEFAULT NULL
|
||||
);
|
||||
|
||||
comment
|
||||
on column chunk.proving_status is 'undefined, unassigned, skipped, assigned, proved, verified, failed';
|
||||
|
||||
create unique index chunk_index_uindex
|
||||
on chunk (index);
|
||||
|
||||
create unique index chunk_hash_uindex
|
||||
on chunk (hash);
|
||||
|
||||
create index batch_hash_index
|
||||
on chunk (batch_hash);
|
||||
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
drop table if exists chunk;
|
||||
-- +goose StatementEnd
|
||||
@@ -1,58 +0,0 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
|
||||
create table batch
|
||||
(
|
||||
-- batch
|
||||
index BIGINT NOT NULL,
|
||||
hash VARCHAR NOT NULL,
|
||||
start_chunk_index BIGINT NOT NULL,
|
||||
start_chunk_hash VARCHAR NOT NULL,
|
||||
end_chunk_index BIGINT NOT NULL,
|
||||
end_chunk_hash VARCHAR NOT NULL,
|
||||
state_root VARCHAR NOT NULL,
|
||||
withdraw_root VARCHAR NOT NULL,
|
||||
batch_header BYTEA NOT NULL,
|
||||
|
||||
-- proof
|
||||
proving_status SMALLINT NOT NULL DEFAULT 1,
|
||||
proof BYTEA DEFAULT NULL,
|
||||
prover_assigned_at TIMESTAMP(0) DEFAULT NULL,
|
||||
proved_at TIMESTAMP(0) DEFAULT NULL,
|
||||
proof_time_sec INTEGER DEFAULT NULL,
|
||||
|
||||
-- rollup
|
||||
rollup_status SMALLINT NOT NULL DEFAULT 1,
|
||||
commit_tx_hash VARCHAR DEFAULT NULL,
|
||||
committed_at TIMESTAMP(0) DEFAULT NULL,
|
||||
finalize_tx_hash VARCHAR DEFAULT NULL,
|
||||
finalized_at TIMESTAMP(0) DEFAULT NULL,
|
||||
|
||||
-- gas oracle
|
||||
oracle_status SMALLINT NOT NULL DEFAULT 1,
|
||||
oracle_tx_hash VARCHAR DEFAULT NULL,
|
||||
|
||||
-- metadata
|
||||
created_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP(0) DEFAULT NULL
|
||||
);
|
||||
|
||||
create unique index batch_index_uindex
|
||||
on batch (index);
|
||||
|
||||
create unique index batch_hash_uindex
|
||||
on batch (hash);
|
||||
|
||||
comment
|
||||
on column batch.proving_status is 'undefined, unassigned, skipped, assigned, proved, verified, failed';
|
||||
|
||||
comment
|
||||
on column batch.rollup_status is 'undefined, pending, committing, committed, finalizing, finalized, finalization_skipped, commit_failed, finalize_failed';
|
||||
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
drop table if exists batch;
|
||||
-- +goose StatementEnd
|
||||
@@ -30,4 +30,4 @@ on l1_block (number);
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
drop table if exists l1_block;
|
||||
-- +goose StatementEnd
|
||||
-- +goose StatementEnd
|
||||
@@ -1,283 +0,0 @@
|
||||
package orm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"scroll-tech/common/docker"
|
||||
"scroll-tech/common/types"
|
||||
|
||||
"scroll-tech/bridge/internal/config"
|
||||
"scroll-tech/bridge/internal/orm/migrate"
|
||||
bridgeTypes "scroll-tech/bridge/internal/types"
|
||||
"scroll-tech/bridge/internal/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
base *docker.App
|
||||
|
||||
db *gorm.DB
|
||||
l2BlockOrm *L2Block
|
||||
chunkOrm *Chunk
|
||||
batchOrm *Batch
|
||||
|
||||
wrappedBlock1 *bridgeTypes.WrappedBlock
|
||||
wrappedBlock2 *bridgeTypes.WrappedBlock
|
||||
chunk1 *bridgeTypes.Chunk
|
||||
chunk2 *bridgeTypes.Chunk
|
||||
chunkHash1 common.Hash
|
||||
chunkHash2 common.Hash
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
t := &testing.T{}
|
||||
setupEnv(t)
|
||||
defer tearDownEnv(t)
|
||||
m.Run()
|
||||
}
|
||||
|
||||
func setupEnv(t *testing.T) {
|
||||
base = docker.NewDockerApp()
|
||||
base.RunDBImage(t)
|
||||
var err error
|
||||
db, err = utils.InitDB(
|
||||
&config.DBConfig{
|
||||
DSN: base.DBConfig.DSN,
|
||||
DriverName: base.DBConfig.DriverName,
|
||||
MaxOpenNum: base.DBConfig.MaxOpenNum,
|
||||
MaxIdleNum: base.DBConfig.MaxIdleNum,
|
||||
},
|
||||
)
|
||||
assert.NoError(t, err)
|
||||
sqlDB, err := db.DB()
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, migrate.ResetDB(sqlDB))
|
||||
|
||||
batchOrm = NewBatch(db)
|
||||
chunkOrm = NewChunk(db)
|
||||
l2BlockOrm = NewL2Block(db)
|
||||
|
||||
templateBlockTrace, err := os.ReadFile("../../../common/testdata/blockTrace_02.json")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read file: %v", err)
|
||||
}
|
||||
wrappedBlock1 = &bridgeTypes.WrappedBlock{}
|
||||
if err = json.Unmarshal(templateBlockTrace, wrappedBlock1); err != nil {
|
||||
t.Fatalf("failed to unmarshal block trace: %v", err)
|
||||
}
|
||||
|
||||
templateBlockTrace, err = os.ReadFile("../../../common/testdata/blockTrace_03.json")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read file: %v", err)
|
||||
}
|
||||
wrappedBlock2 = &bridgeTypes.WrappedBlock{}
|
||||
if err = json.Unmarshal(templateBlockTrace, wrappedBlock2); err != nil {
|
||||
t.Fatalf("failed to unmarshal block trace: %v", err)
|
||||
}
|
||||
|
||||
chunk1 = &bridgeTypes.Chunk{Blocks: []*bridgeTypes.WrappedBlock{wrappedBlock1}}
|
||||
chunkHash1, err = chunk1.Hash(0)
|
||||
assert.NoError(t, err)
|
||||
|
||||
chunk2 = &bridgeTypes.Chunk{Blocks: []*bridgeTypes.WrappedBlock{wrappedBlock2}}
|
||||
chunkHash2, err = chunk2.Hash(chunk1.NumL1Messages(0))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func tearDownEnv(t *testing.T) {
|
||||
sqlDB, err := db.DB()
|
||||
assert.NoError(t, err)
|
||||
sqlDB.Close()
|
||||
base.Free()
|
||||
}
|
||||
|
||||
func TestL2BlockOrm(t *testing.T) {
|
||||
sqlDB, err := db.DB()
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, migrate.ResetDB(sqlDB))
|
||||
|
||||
err = l2BlockOrm.InsertL2Blocks(context.Background(), []*bridgeTypes.WrappedBlock{wrappedBlock1, wrappedBlock2})
|
||||
assert.NoError(t, err)
|
||||
|
||||
height, err := l2BlockOrm.GetL2BlocksLatestHeight(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(3), height)
|
||||
|
||||
blocks, err := l2BlockOrm.GetUnchunkedBlocks(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, blocks, 2)
|
||||
assert.Equal(t, wrappedBlock1, blocks[0])
|
||||
assert.Equal(t, wrappedBlock2, blocks[1])
|
||||
|
||||
blocks, err = l2BlockOrm.GetL2BlocksInRange(context.Background(), 2, 3)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, blocks, 2)
|
||||
assert.Equal(t, wrappedBlock1, blocks[0])
|
||||
assert.Equal(t, wrappedBlock2, blocks[1])
|
||||
|
||||
err = l2BlockOrm.UpdateChunkHashInRange(context.Background(), 2, 2, "test hash")
|
||||
assert.NoError(t, err)
|
||||
|
||||
blocks, err = l2BlockOrm.GetUnchunkedBlocks(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, blocks, 1)
|
||||
assert.Equal(t, wrappedBlock2, blocks[0])
|
||||
}
|
||||
|
||||
func TestChunkOrm(t *testing.T) {
|
||||
sqlDB, err := db.DB()
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, migrate.ResetDB(sqlDB))
|
||||
|
||||
err = l2BlockOrm.InsertL2Blocks(context.Background(), []*bridgeTypes.WrappedBlock{wrappedBlock1, wrappedBlock2})
|
||||
assert.NoError(t, err)
|
||||
|
||||
dbChunk1, err := chunkOrm.InsertChunk(context.Background(), chunk1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, dbChunk1.Hash, chunkHash1.Hex())
|
||||
|
||||
dbChunk2, err := chunkOrm.InsertChunk(context.Background(), chunk2)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, dbChunk2.Hash, chunkHash2.Hex())
|
||||
|
||||
chunks, err := chunkOrm.GetUnbatchedChunks(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, chunks, 2)
|
||||
assert.Equal(t, chunkHash1.Hex(), chunks[0].Hash)
|
||||
assert.Equal(t, chunkHash2.Hex(), chunks[1].Hash)
|
||||
|
||||
err = chunkOrm.UpdateProvingStatus(context.Background(), chunkHash1.Hex(), types.ProvingTaskVerified)
|
||||
assert.NoError(t, err)
|
||||
err = chunkOrm.UpdateProvingStatus(context.Background(), chunkHash2.Hex(), types.ProvingTaskAssigned)
|
||||
assert.NoError(t, err)
|
||||
|
||||
chunks, err = chunkOrm.GetChunksInRange(context.Background(), 0, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, chunks, 2)
|
||||
assert.Equal(t, chunkHash1.Hex(), chunks[0].Hash)
|
||||
assert.Equal(t, chunkHash2.Hex(), chunks[1].Hash)
|
||||
assert.Equal(t, types.ProvingTaskVerified, types.ProvingStatus(chunks[0].ProvingStatus))
|
||||
assert.Equal(t, types.ProvingTaskAssigned, types.ProvingStatus(chunks[1].ProvingStatus))
|
||||
|
||||
err = chunkOrm.UpdateBatchHashInRange(context.Background(), 0, 0, "test hash")
|
||||
assert.NoError(t, err)
|
||||
chunks, err = chunkOrm.GetUnbatchedChunks(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, chunks, 1)
|
||||
}
|
||||
|
||||
func TestBatchOrm(t *testing.T) {
|
||||
sqlDB, err := db.DB()
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, migrate.ResetDB(sqlDB))
|
||||
|
||||
err = l2BlockOrm.InsertL2Blocks(context.Background(), []*bridgeTypes.WrappedBlock{wrappedBlock1, wrappedBlock2})
|
||||
assert.NoError(t, err)
|
||||
|
||||
dbChunk1, err := chunkOrm.InsertChunk(context.Background(), chunk1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, dbChunk1.Hash, chunkHash1.Hex())
|
||||
|
||||
dbChunk2, err := chunkOrm.InsertChunk(context.Background(), chunk2)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, dbChunk2.Hash, chunkHash2.Hex())
|
||||
|
||||
batch1, err := batchOrm.InsertBatch(context.Background(), 0, 0, chunkHash1.Hex(), chunkHash1.Hex(), []*bridgeTypes.Chunk{chunk1})
|
||||
assert.NoError(t, err)
|
||||
hash1 := batch1.Hash
|
||||
|
||||
batch1, err = batchOrm.GetBatchByIndex(context.Background(), 0)
|
||||
assert.NoError(t, err)
|
||||
batchHeader1, err := bridgeTypes.DecodeBatchHeader(batch1.BatchHeader)
|
||||
assert.NoError(t, err)
|
||||
batchHash1 := batchHeader1.Hash().Hex()
|
||||
assert.Equal(t, hash1, batchHash1)
|
||||
|
||||
batch2, err := batchOrm.InsertBatch(context.Background(), 1, 1, chunkHash2.Hex(), chunkHash2.Hex(), []*bridgeTypes.Chunk{chunk2})
|
||||
assert.NoError(t, err)
|
||||
hash2 := batch2.Hash
|
||||
|
||||
batch2, err = batchOrm.GetBatchByIndex(context.Background(), 1)
|
||||
assert.NoError(t, err)
|
||||
batchHeader2, err := bridgeTypes.DecodeBatchHeader(batch2.BatchHeader)
|
||||
assert.NoError(t, err)
|
||||
batchHash2 := batchHeader2.Hash().Hex()
|
||||
assert.Equal(t, hash2, batchHash2)
|
||||
|
||||
count, err := batchOrm.GetBatchCount(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, uint64(2), count)
|
||||
|
||||
pendingBatches, err := batchOrm.GetPendingBatches(context.Background(), 100)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 2, len(pendingBatches))
|
||||
|
||||
rollupStatus, err := batchOrm.GetRollupStatusByHashList(context.Background(), []string{batchHash1, batchHash2})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 2, len(rollupStatus))
|
||||
assert.Equal(t, types.RollupPending, rollupStatus[0])
|
||||
assert.Equal(t, types.RollupPending, rollupStatus[1])
|
||||
|
||||
err = batchOrm.UpdateProvingStatus(context.Background(), batchHash1, types.ProvingTaskSkipped)
|
||||
assert.NoError(t, err)
|
||||
err = batchOrm.UpdateRollupStatus(context.Background(), batchHash1, types.RollupCommitted)
|
||||
assert.NoError(t, err)
|
||||
err = batchOrm.UpdateProvingStatus(context.Background(), batchHash2, types.ProvingTaskFailed)
|
||||
assert.NoError(t, err)
|
||||
err = batchOrm.UpdateRollupStatus(context.Background(), batchHash2, types.RollupCommitted)
|
||||
assert.NoError(t, err)
|
||||
|
||||
count, err = batchOrm.UpdateSkippedBatches(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, uint64(2), count)
|
||||
|
||||
count, err = batchOrm.UpdateSkippedBatches(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, uint64(0), count)
|
||||
|
||||
batch, err := batchOrm.GetBatchByIndex(context.Background(), 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, types.RollupFinalizationSkipped, types.RollupStatus(batch.RollupStatus))
|
||||
|
||||
err = batchOrm.UpdateProvingStatus(context.Background(), batchHash2, types.ProvingTaskVerified)
|
||||
assert.NoError(t, err)
|
||||
|
||||
dbProof, err := batchOrm.GetVerifiedProofByHash(context.Background(), batchHash1)
|
||||
assert.Error(t, err, gorm.ErrRecordNotFound)
|
||||
assert.Nil(t, dbProof)
|
||||
|
||||
err = batchOrm.UpdateProvingStatus(context.Background(), batchHash2, types.ProvingTaskVerified)
|
||||
assert.NoError(t, err)
|
||||
err = batchOrm.UpdateRollupStatus(context.Background(), batchHash2, types.RollupFinalized)
|
||||
assert.NoError(t, err)
|
||||
err = batchOrm.UpdateL2GasOracleStatusAndOracleTxHash(context.Background(), batchHash2, types.GasOracleImported, "oracleTxHash")
|
||||
assert.NoError(t, err)
|
||||
|
||||
updatedBatch, err := batchOrm.GetLatestBatch(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, types.ProvingTaskVerified, types.ProvingStatus(updatedBatch.ProvingStatus))
|
||||
assert.Equal(t, types.RollupFinalized, types.RollupStatus(updatedBatch.RollupStatus))
|
||||
assert.Equal(t, types.GasOracleImported, types.GasOracleStatus(updatedBatch.OracleStatus))
|
||||
assert.Equal(t, "oracleTxHash", updatedBatch.OracleTxHash)
|
||||
|
||||
err = batchOrm.UpdateCommitTxHashAndRollupStatus(context.Background(), batchHash2, "commitTxHash", types.RollupCommitted)
|
||||
assert.NoError(t, err)
|
||||
updatedBatch, err = batchOrm.GetLatestBatch(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "commitTxHash", updatedBatch.CommitTxHash)
|
||||
assert.Equal(t, types.RollupCommitted, types.RollupStatus(updatedBatch.RollupStatus))
|
||||
|
||||
err = batchOrm.UpdateFinalizeTxHashAndRollupStatus(context.Background(), batchHash2, "finalizeTxHash", types.RollupFinalizeFailed)
|
||||
assert.NoError(t, err)
|
||||
|
||||
updatedBatch, err = batchOrm.GetLatestBatch(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "finalizeTxHash", updatedBatch.FinalizeTxHash)
|
||||
assert.Equal(t, types.RollupFinalizeFailed, types.RollupStatus(updatedBatch.RollupStatus))
|
||||
}
|
||||
236
bridge/internal/types/batch.go
Normal file
236
bridge/internal/types/batch.go
Normal file
@@ -0,0 +1,236 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"math/big"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
||||
"github.com/scroll-tech/go-ethereum/core/types"
|
||||
"github.com/scroll-tech/go-ethereum/crypto"
|
||||
|
||||
abi "scroll-tech/bridge/abi"
|
||||
)
|
||||
|
||||
// PublicInputHashConfig is the configuration of how to compute the public input hash.
|
||||
type PublicInputHashConfig struct {
|
||||
MaxTxNum int `json:"max_tx_num"`
|
||||
PaddingTxHash common.Hash `json:"padding_tx_hash"`
|
||||
}
|
||||
|
||||
const defaultMaxTxNum = 44
|
||||
|
||||
var defaultPaddingTxHash = [32]byte{}
|
||||
|
||||
// BatchData contains info of batch to be committed.
|
||||
type BatchData struct {
|
||||
Batch abi.IScrollChainBatch
|
||||
TxHashes []common.Hash
|
||||
TotalTxNum uint64
|
||||
TotalL1TxNum uint64
|
||||
TotalL2Gas uint64
|
||||
|
||||
// cache for the BatchHash
|
||||
hash *common.Hash
|
||||
// The config to compute the public input hash, or the block hash.
|
||||
// If it is nil, the hash calculation will use `defaultMaxTxNum` and `defaultPaddingTxHash`.
|
||||
piCfg *PublicInputHashConfig
|
||||
}
|
||||
|
||||
// Timestamp returns the timestamp of the first block in the BlockData.
|
||||
func (b *BatchData) Timestamp() uint64 {
|
||||
if len(b.Batch.Blocks) == 0 {
|
||||
return 0
|
||||
}
|
||||
return b.Batch.Blocks[0].Timestamp
|
||||
}
|
||||
|
||||
// Hash calculates the hash of this batch.
|
||||
func (b *BatchData) Hash() *common.Hash {
|
||||
if b.hash != nil {
|
||||
return b.hash
|
||||
}
|
||||
|
||||
buf := make([]byte, 8)
|
||||
hasher := crypto.NewKeccakState()
|
||||
|
||||
// 1. hash PrevStateRoot, NewStateRoot, WithdrawTrieRoot
|
||||
// @todo: panic on error here.
|
||||
_, _ = hasher.Write(b.Batch.PrevStateRoot[:])
|
||||
_, _ = hasher.Write(b.Batch.NewStateRoot[:])
|
||||
_, _ = hasher.Write(b.Batch.WithdrawTrieRoot[:])
|
||||
|
||||
// 2. hash all block contexts
|
||||
for _, block := range b.Batch.Blocks {
|
||||
// write BlockHash & ParentHash
|
||||
_, _ = hasher.Write(block.BlockHash[:])
|
||||
_, _ = hasher.Write(block.ParentHash[:])
|
||||
// write BlockNumber
|
||||
binary.BigEndian.PutUint64(buf, block.BlockNumber)
|
||||
_, _ = hasher.Write(buf)
|
||||
// write Timestamp
|
||||
binary.BigEndian.PutUint64(buf, block.Timestamp)
|
||||
_, _ = hasher.Write(buf)
|
||||
// write BaseFee
|
||||
var baseFee [32]byte
|
||||
if block.BaseFee != nil {
|
||||
baseFee = newByte32FromBytes(block.BaseFee.Bytes())
|
||||
}
|
||||
_, _ = hasher.Write(baseFee[:])
|
||||
// write GasLimit
|
||||
binary.BigEndian.PutUint64(buf, block.GasLimit)
|
||||
_, _ = hasher.Write(buf)
|
||||
// write NumTransactions
|
||||
binary.BigEndian.PutUint16(buf[:2], block.NumTransactions)
|
||||
_, _ = hasher.Write(buf[:2])
|
||||
// write NumL1Messages
|
||||
binary.BigEndian.PutUint16(buf[:2], block.NumL1Messages)
|
||||
_, _ = hasher.Write(buf[:2])
|
||||
}
|
||||
|
||||
// 3. add all tx hashes
|
||||
for _, txHash := range b.TxHashes {
|
||||
_, _ = hasher.Write(txHash[:])
|
||||
}
|
||||
|
||||
// 4. append empty tx hash up to MaxTxNum
|
||||
maxTxNum := defaultMaxTxNum
|
||||
paddingTxHash := common.Hash(defaultPaddingTxHash)
|
||||
if b.piCfg != nil {
|
||||
maxTxNum = b.piCfg.MaxTxNum
|
||||
paddingTxHash = b.piCfg.PaddingTxHash
|
||||
}
|
||||
for i := len(b.TxHashes); i < maxTxNum; i++ {
|
||||
_, _ = hasher.Write(paddingTxHash[:])
|
||||
}
|
||||
|
||||
b.hash = new(common.Hash)
|
||||
_, _ = hasher.Read(b.hash[:])
|
||||
|
||||
return b.hash
|
||||
}
|
||||
|
||||
// NewBatchData creates a BatchData given the parent batch information and the traces of the blocks
|
||||
// included in this batch
|
||||
func NewBatchData(parentBatch *BatchInfo, blocks []*WrappedBlock, piCfg *PublicInputHashConfig) *BatchData {
|
||||
batchData := new(BatchData)
|
||||
batch := &batchData.Batch
|
||||
|
||||
// set BatchIndex, ParentBatchHash
|
||||
batch.BatchIndex = parentBatch.Index + 1
|
||||
batch.ParentBatchHash = common.HexToHash(parentBatch.Hash)
|
||||
batch.Blocks = make([]abi.IScrollChainBlockContext, len(blocks))
|
||||
|
||||
var batchTxDataBuf bytes.Buffer
|
||||
batchTxDataWriter := bufio.NewWriter(&batchTxDataBuf)
|
||||
|
||||
for i, block := range blocks {
|
||||
batchData.TotalTxNum += uint64(len(block.Transactions))
|
||||
batchData.TotalL2Gas += block.Header.GasUsed
|
||||
|
||||
// set baseFee to 0 when it's nil in the block header
|
||||
baseFee := block.Header.BaseFee
|
||||
if baseFee == nil {
|
||||
baseFee = big.NewInt(0)
|
||||
}
|
||||
|
||||
batch.Blocks[i] = abi.IScrollChainBlockContext{
|
||||
BlockHash: block.Header.Hash(),
|
||||
ParentHash: block.Header.ParentHash,
|
||||
BlockNumber: block.Header.Number.Uint64(),
|
||||
Timestamp: block.Header.Time,
|
||||
BaseFee: baseFee,
|
||||
GasLimit: block.Header.GasLimit,
|
||||
NumTransactions: uint16(len(block.Transactions)),
|
||||
NumL1Messages: 0, // TODO: currently use 0, will re-enable after we use l2geth to include L1 messages
|
||||
}
|
||||
|
||||
// fill in RLP-encoded transactions
|
||||
for _, txData := range block.Transactions {
|
||||
data, _ := hexutil.Decode(txData.Data)
|
||||
// right now we only support legacy tx
|
||||
tx := types.NewTx(&types.LegacyTx{
|
||||
Nonce: txData.Nonce,
|
||||
To: txData.To,
|
||||
Value: txData.Value.ToInt(),
|
||||
Gas: txData.Gas,
|
||||
GasPrice: txData.GasPrice.ToInt(),
|
||||
Data: data,
|
||||
V: txData.V.ToInt(),
|
||||
R: txData.R.ToInt(),
|
||||
S: txData.S.ToInt(),
|
||||
})
|
||||
rlpTxData, _ := tx.MarshalBinary()
|
||||
var txLen [4]byte
|
||||
binary.BigEndian.PutUint32(txLen[:], uint32(len(rlpTxData)))
|
||||
_, _ = batchTxDataWriter.Write(txLen[:])
|
||||
_, _ = batchTxDataWriter.Write(rlpTxData)
|
||||
batchData.TxHashes = append(batchData.TxHashes, tx.Hash())
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
batch.PrevStateRoot = common.HexToHash(parentBatch.StateRoot)
|
||||
}
|
||||
|
||||
// set NewStateRoot & WithdrawTrieRoot from the last block
|
||||
if i == len(blocks)-1 {
|
||||
batch.NewStateRoot = block.Header.Root
|
||||
batch.WithdrawTrieRoot = block.WithdrawTrieRoot
|
||||
}
|
||||
}
|
||||
|
||||
if err := batchTxDataWriter.Flush(); err != nil {
|
||||
panic("Buffered I/O flush failed")
|
||||
}
|
||||
|
||||
batch.L2Transactions = batchTxDataBuf.Bytes()
|
||||
batchData.piCfg = piCfg
|
||||
|
||||
return batchData
|
||||
}
|
||||
|
||||
// NewGenesisBatchData generates the batch that contains the genesis block.
|
||||
func NewGenesisBatchData(genesisBlockTrace *WrappedBlock) *BatchData {
|
||||
header := genesisBlockTrace.Header
|
||||
if header.Number.Uint64() != 0 {
|
||||
panic("invalid genesis block trace: block number is not 0")
|
||||
}
|
||||
|
||||
batchData := new(BatchData)
|
||||
batch := &batchData.Batch
|
||||
|
||||
// fill in batch information
|
||||
batch.BatchIndex = 0
|
||||
batch.Blocks = make([]abi.IScrollChainBlockContext, 1)
|
||||
batch.NewStateRoot = header.Root
|
||||
// PrevStateRoot, WithdrawTrieRoot, ParentBatchHash should all be 0
|
||||
// L2Transactions should be empty
|
||||
|
||||
// fill in block context
|
||||
batch.Blocks[0] = abi.IScrollChainBlockContext{
|
||||
BlockHash: header.Hash(),
|
||||
ParentHash: header.ParentHash,
|
||||
BlockNumber: header.Number.Uint64(),
|
||||
Timestamp: header.Time,
|
||||
BaseFee: header.BaseFee,
|
||||
GasLimit: header.GasLimit,
|
||||
NumTransactions: 0,
|
||||
NumL1Messages: 0,
|
||||
}
|
||||
|
||||
return batchData
|
||||
}
|
||||
|
||||
// newByte32FromBytes converts the bytes in big-endian encoding to 32 bytes in big-endian encoding
|
||||
func newByte32FromBytes(b []byte) [32]byte {
|
||||
var byte32 [32]byte
|
||||
|
||||
if len(b) > 32 {
|
||||
b = b[len(b)-32:]
|
||||
}
|
||||
|
||||
copy(byte32[32-len(b):], b)
|
||||
return byte32
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
"github.com/scroll-tech/go-ethereum/core/types"
|
||||
"github.com/scroll-tech/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
// BatchHeader contains batch header info to be committed.
|
||||
type BatchHeader struct {
|
||||
// Encoded in BatchHeaderV0Codec
|
||||
version uint8
|
||||
batchIndex uint64
|
||||
l1MessagePopped uint64
|
||||
totalL1MessagePopped uint64
|
||||
dataHash common.Hash
|
||||
parentBatchHash common.Hash
|
||||
skippedL1MessageBitmap []byte
|
||||
}
|
||||
|
||||
// NewBatchHeader creates a new BatchHeader
|
||||
func NewBatchHeader(version uint8, batchIndex, totalL1MessagePoppedBefore uint64, parentBatchHash common.Hash, chunks []*Chunk) (*BatchHeader, error) {
|
||||
// buffer for storing chunk hashes in order to compute the batch data hash
|
||||
var dataBytes []byte
|
||||
|
||||
// skipped L1 message bitmap, an array of 256-bit bitmaps
|
||||
var skippedBitmap []*big.Int
|
||||
|
||||
// the first queue index that belongs to this batch
|
||||
baseIndex := totalL1MessagePoppedBefore
|
||||
|
||||
// the next queue index that we need to process
|
||||
nextIndex := totalL1MessagePoppedBefore
|
||||
|
||||
for chunkID, chunk := range chunks {
|
||||
// build data hash
|
||||
totalL1MessagePoppedBeforeChunk := nextIndex
|
||||
chunkHash, err := chunk.Hash(totalL1MessagePoppedBeforeChunk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dataBytes = append(dataBytes, chunkHash.Bytes()...)
|
||||
|
||||
// build skip bitmap
|
||||
for blockID, block := range chunk.Blocks {
|
||||
for _, tx := range block.Transactions {
|
||||
if tx.Type != types.L1MessageTxType {
|
||||
continue
|
||||
}
|
||||
currentIndex := tx.Nonce
|
||||
|
||||
if currentIndex < nextIndex {
|
||||
return nil, fmt.Errorf("unexpected batch payload, expected queue index: %d, got: %d. Batch index: %d, chunk index in batch: %d, block index in chunk: %d, block hash: %v, transaction hash: %v", nextIndex, currentIndex, batchIndex, chunkID, blockID, block.Header.Hash(), tx.TxHash)
|
||||
}
|
||||
|
||||
// mark skipped messages
|
||||
for skippedIndex := nextIndex; skippedIndex < currentIndex; skippedIndex++ {
|
||||
quo := int((skippedIndex - baseIndex) / 256)
|
||||
rem := int((skippedIndex - baseIndex) % 256)
|
||||
for len(skippedBitmap) <= quo {
|
||||
bitmap := big.NewInt(0)
|
||||
skippedBitmap = append(skippedBitmap, bitmap)
|
||||
}
|
||||
skippedBitmap[quo].SetBit(skippedBitmap[quo], rem, 1)
|
||||
}
|
||||
|
||||
// process included message
|
||||
quo := int((currentIndex - baseIndex) / 256)
|
||||
for len(skippedBitmap) <= quo {
|
||||
bitmap := big.NewInt(0)
|
||||
skippedBitmap = append(skippedBitmap, bitmap)
|
||||
}
|
||||
|
||||
nextIndex = currentIndex + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// compute data hash
|
||||
dataHash := crypto.Keccak256Hash(dataBytes)
|
||||
|
||||
// compute skipped bitmap
|
||||
bitmapBytes := make([]byte, len(skippedBitmap)*32)
|
||||
for ii, num := range skippedBitmap {
|
||||
bytes := num.Bytes()
|
||||
padding := 32 - len(bytes)
|
||||
copy(bitmapBytes[32*ii+padding:], bytes)
|
||||
}
|
||||
|
||||
return &BatchHeader{
|
||||
version: version,
|
||||
batchIndex: batchIndex,
|
||||
l1MessagePopped: nextIndex - totalL1MessagePoppedBefore,
|
||||
totalL1MessagePopped: nextIndex,
|
||||
dataHash: dataHash,
|
||||
parentBatchHash: parentBatchHash,
|
||||
skippedL1MessageBitmap: bitmapBytes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Version returns the version of the BatchHeader.
|
||||
func (b *BatchHeader) Version() uint8 {
|
||||
return b.version
|
||||
}
|
||||
|
||||
// BatchIndex returns the batch index of the BatchHeader.
|
||||
func (b *BatchHeader) BatchIndex() uint64 {
|
||||
return b.batchIndex
|
||||
}
|
||||
|
||||
// TotalL1MessagePopped returns the total number of L1 messages popped in the BatchHeader.
|
||||
func (b *BatchHeader) TotalL1MessagePopped() uint64 {
|
||||
return b.totalL1MessagePopped
|
||||
}
|
||||
|
||||
// SkippedL1MessageBitmap returns the skipped L1 message bitmap in the BatchHeader.
|
||||
func (b *BatchHeader) SkippedL1MessageBitmap() []byte {
|
||||
return b.skippedL1MessageBitmap
|
||||
}
|
||||
|
||||
// Encode encodes the BatchHeader into RollupV2 BatchHeaderV0Codec Encoding.
|
||||
func (b *BatchHeader) Encode() []byte {
|
||||
batchBytes := make([]byte, 89+len(b.skippedL1MessageBitmap))
|
||||
batchBytes[0] = b.version
|
||||
binary.BigEndian.PutUint64(batchBytes[1:], b.batchIndex)
|
||||
binary.BigEndian.PutUint64(batchBytes[9:], b.l1MessagePopped)
|
||||
binary.BigEndian.PutUint64(batchBytes[17:], b.totalL1MessagePopped)
|
||||
copy(batchBytes[25:], b.dataHash[:])
|
||||
copy(batchBytes[57:], b.parentBatchHash[:])
|
||||
copy(batchBytes[89:], b.skippedL1MessageBitmap[:])
|
||||
return batchBytes
|
||||
}
|
||||
|
||||
// Hash calculates the hash of the batch header.
|
||||
func (b *BatchHeader) Hash() common.Hash {
|
||||
return crypto.Keccak256Hash(b.Encode())
|
||||
}
|
||||
|
||||
// DecodeBatchHeader attempts to decode the given byte slice into a BatchHeader.
|
||||
func DecodeBatchHeader(data []byte) (*BatchHeader, error) {
|
||||
if len(data) < 89 {
|
||||
return nil, fmt.Errorf("insufficient data for BatchHeader")
|
||||
}
|
||||
b := &BatchHeader{
|
||||
version: data[0],
|
||||
batchIndex: binary.BigEndian.Uint64(data[1:9]),
|
||||
l1MessagePopped: binary.BigEndian.Uint64(data[9:17]),
|
||||
totalL1MessagePopped: binary.BigEndian.Uint64(data[17:25]),
|
||||
dataHash: common.BytesToHash(data[25:57]),
|
||||
parentBatchHash: common.BytesToHash(data[57:89]),
|
||||
skippedL1MessageBitmap: data[89:],
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
@@ -1,251 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewBatchHeader(t *testing.T) {
|
||||
// Without L1 Msg
|
||||
templateBlockTrace, err := os.ReadFile("../../../common/testdata/blockTrace_02.json")
|
||||
assert.NoError(t, err)
|
||||
|
||||
wrappedBlock := &WrappedBlock{}
|
||||
assert.NoError(t, json.Unmarshal(templateBlockTrace, wrappedBlock))
|
||||
chunk := &Chunk{
|
||||
Blocks: []*WrappedBlock{
|
||||
wrappedBlock,
|
||||
},
|
||||
}
|
||||
parentBatchHeader := &BatchHeader{
|
||||
version: 1,
|
||||
batchIndex: 0,
|
||||
l1MessagePopped: 0,
|
||||
totalL1MessagePopped: 0,
|
||||
dataHash: common.HexToHash("0x0"),
|
||||
parentBatchHash: common.HexToHash("0x0"),
|
||||
skippedL1MessageBitmap: nil,
|
||||
}
|
||||
batchHeader, err := NewBatchHeader(1, 1, 0, parentBatchHeader.Hash(), []*Chunk{chunk})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, batchHeader)
|
||||
assert.Equal(t, 0, len(batchHeader.skippedL1MessageBitmap))
|
||||
|
||||
// 1 L1 Msg in 1 bitmap
|
||||
templateBlockTrace2, err := os.ReadFile("../../../common/testdata/blockTrace_04.json")
|
||||
assert.NoError(t, err)
|
||||
|
||||
wrappedBlock2 := &WrappedBlock{}
|
||||
assert.NoError(t, json.Unmarshal(templateBlockTrace2, wrappedBlock2))
|
||||
chunk = &Chunk{
|
||||
Blocks: []*WrappedBlock{
|
||||
wrappedBlock2,
|
||||
},
|
||||
}
|
||||
batchHeader, err = NewBatchHeader(1, 1, 0, parentBatchHeader.Hash(), []*Chunk{chunk})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, batchHeader)
|
||||
assert.Equal(t, 32, len(batchHeader.skippedL1MessageBitmap))
|
||||
expectedBitmap := "00000000000000000000000000000000000000000000000000000000000003ff" // skip first 10
|
||||
assert.Equal(t, expectedBitmap, common.Bytes2Hex(batchHeader.skippedL1MessageBitmap))
|
||||
|
||||
// many consecutive L1 Msgs in 1 bitmap, no leading skipped msgs
|
||||
templateBlockTrace3, err := os.ReadFile("../../../common/testdata/blockTrace_05.json")
|
||||
assert.NoError(t, err)
|
||||
|
||||
wrappedBlock3 := &WrappedBlock{}
|
||||
assert.NoError(t, json.Unmarshal(templateBlockTrace3, wrappedBlock3))
|
||||
chunk = &Chunk{
|
||||
Blocks: []*WrappedBlock{
|
||||
wrappedBlock3,
|
||||
},
|
||||
}
|
||||
batchHeader, err = NewBatchHeader(1, 1, 37, parentBatchHeader.Hash(), []*Chunk{chunk})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, batchHeader)
|
||||
assert.Equal(t, uint64(5), batchHeader.l1MessagePopped)
|
||||
assert.Equal(t, 32, len(batchHeader.skippedL1MessageBitmap))
|
||||
expectedBitmap = "0000000000000000000000000000000000000000000000000000000000000000" // all bits are included, so none are skipped
|
||||
assert.Equal(t, expectedBitmap, common.Bytes2Hex(batchHeader.skippedL1MessageBitmap))
|
||||
|
||||
// many consecutive L1 Msgs in 1 bitmap, with leading skipped msgs
|
||||
chunk = &Chunk{
|
||||
Blocks: []*WrappedBlock{
|
||||
wrappedBlock3,
|
||||
},
|
||||
}
|
||||
batchHeader, err = NewBatchHeader(1, 1, 0, parentBatchHeader.Hash(), []*Chunk{chunk})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, batchHeader)
|
||||
assert.Equal(t, uint64(42), batchHeader.l1MessagePopped)
|
||||
assert.Equal(t, 32, len(batchHeader.skippedL1MessageBitmap))
|
||||
expectedBitmap = "0000000000000000000000000000000000000000000000000000001fffffffff" // skipped the first 37 messages
|
||||
assert.Equal(t, expectedBitmap, common.Bytes2Hex(batchHeader.skippedL1MessageBitmap))
|
||||
|
||||
// many sparse L1 Msgs in 1 bitmap
|
||||
templateBlockTrace4, err := os.ReadFile("../../../common/testdata/blockTrace_06.json")
|
||||
assert.NoError(t, err)
|
||||
|
||||
wrappedBlock4 := &WrappedBlock{}
|
||||
assert.NoError(t, json.Unmarshal(templateBlockTrace4, wrappedBlock4))
|
||||
chunk = &Chunk{
|
||||
Blocks: []*WrappedBlock{
|
||||
wrappedBlock4,
|
||||
},
|
||||
}
|
||||
batchHeader, err = NewBatchHeader(1, 1, 0, parentBatchHeader.Hash(), []*Chunk{chunk})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, batchHeader)
|
||||
assert.Equal(t, uint64(10), batchHeader.l1MessagePopped)
|
||||
assert.Equal(t, 32, len(batchHeader.skippedL1MessageBitmap))
|
||||
expectedBitmap = "00000000000000000000000000000000000000000000000000000000000001dd" // 0111011101
|
||||
assert.Equal(t, expectedBitmap, common.Bytes2Hex(batchHeader.skippedL1MessageBitmap))
|
||||
|
||||
// many L1 Msgs in each of 2 bitmaps
|
||||
templateBlockTrace5, err := os.ReadFile("../../../common/testdata/blockTrace_07.json")
|
||||
assert.NoError(t, err)
|
||||
|
||||
wrappedBlock5 := &WrappedBlock{}
|
||||
assert.NoError(t, json.Unmarshal(templateBlockTrace5, wrappedBlock5))
|
||||
chunk = &Chunk{
|
||||
Blocks: []*WrappedBlock{
|
||||
wrappedBlock5,
|
||||
},
|
||||
}
|
||||
batchHeader, err = NewBatchHeader(1, 1, 0, parentBatchHeader.Hash(), []*Chunk{chunk})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, batchHeader)
|
||||
assert.Equal(t, uint64(257), batchHeader.l1MessagePopped)
|
||||
assert.Equal(t, 64, len(batchHeader.skippedL1MessageBitmap))
|
||||
expectedBitmap = "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0000000000000000000000000000000000000000000000000000000000000000"
|
||||
assert.Equal(t, expectedBitmap, common.Bytes2Hex(batchHeader.skippedL1MessageBitmap))
|
||||
}
|
||||
|
||||
func TestBatchHeaderEncode(t *testing.T) {
|
||||
// Without L1 Msg
|
||||
templateBlockTrace, err := os.ReadFile("../../../common/testdata/blockTrace_02.json")
|
||||
assert.NoError(t, err)
|
||||
|
||||
wrappedBlock := &WrappedBlock{}
|
||||
assert.NoError(t, json.Unmarshal(templateBlockTrace, wrappedBlock))
|
||||
chunk := &Chunk{
|
||||
Blocks: []*WrappedBlock{
|
||||
wrappedBlock,
|
||||
},
|
||||
}
|
||||
parentBatchHeader := &BatchHeader{
|
||||
version: 1,
|
||||
batchIndex: 0,
|
||||
l1MessagePopped: 0,
|
||||
totalL1MessagePopped: 0,
|
||||
dataHash: common.HexToHash("0x0"),
|
||||
parentBatchHash: common.HexToHash("0x0"),
|
||||
skippedL1MessageBitmap: nil,
|
||||
}
|
||||
batchHeader, err := NewBatchHeader(1, 1, 0, parentBatchHeader.Hash(), []*Chunk{chunk})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, batchHeader)
|
||||
bytes := batchHeader.Encode()
|
||||
assert.Equal(t, 89, len(bytes))
|
||||
assert.Equal(t, "0100000000000000010000000000000000000000000000000010a64c9bd905f8caf5d668fbda622d6558c5a42cdb4b3895709743d159c22e534136709aabc8a23aa17fbcc833da2f7857d3c2884feec9aae73429c135f94985", common.Bytes2Hex(bytes))
|
||||
|
||||
// With L1 Msg
|
||||
templateBlockTrace2, err := os.ReadFile("../../../common/testdata/blockTrace_04.json")
|
||||
assert.NoError(t, err)
|
||||
|
||||
wrappedBlock2 := &WrappedBlock{}
|
||||
assert.NoError(t, json.Unmarshal(templateBlockTrace2, wrappedBlock2))
|
||||
chunk = &Chunk{
|
||||
Blocks: []*WrappedBlock{
|
||||
wrappedBlock2,
|
||||
},
|
||||
}
|
||||
batchHeader, err = NewBatchHeader(1, 1, 0, parentBatchHeader.Hash(), []*Chunk{chunk})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, batchHeader)
|
||||
bytes = batchHeader.Encode()
|
||||
assert.Equal(t, 121, len(bytes))
|
||||
assert.Equal(t, "010000000000000001000000000000000b000000000000000b457a9e90e8e51ba2de2f66c6b589540b88cf594dac7fa7d04b99cdcfecf24e384136709aabc8a23aa17fbcc833da2f7857d3c2884feec9aae73429c135f9498500000000000000000000000000000000000000000000000000000000000003ff", common.Bytes2Hex(bytes))
|
||||
}
|
||||
|
||||
func TestBatchHeaderHash(t *testing.T) {
|
||||
// Without L1 Msg
|
||||
templateBlockTrace, err := os.ReadFile("../../../common/testdata/blockTrace_02.json")
|
||||
assert.NoError(t, err)
|
||||
|
||||
wrappedBlock := &WrappedBlock{}
|
||||
assert.NoError(t, json.Unmarshal(templateBlockTrace, wrappedBlock))
|
||||
chunk := &Chunk{
|
||||
Blocks: []*WrappedBlock{
|
||||
wrappedBlock,
|
||||
},
|
||||
}
|
||||
parentBatchHeader := &BatchHeader{
|
||||
version: 1,
|
||||
batchIndex: 0,
|
||||
l1MessagePopped: 0,
|
||||
totalL1MessagePopped: 0,
|
||||
dataHash: common.HexToHash("0x0"),
|
||||
parentBatchHash: common.HexToHash("0x0"),
|
||||
skippedL1MessageBitmap: nil,
|
||||
}
|
||||
batchHeader, err := NewBatchHeader(1, 1, 0, parentBatchHeader.Hash(), []*Chunk{chunk})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, batchHeader)
|
||||
hash := batchHeader.Hash()
|
||||
assert.Equal(t, "d69da4357da0073f4093c76e49f077e21bb52f48f57ee3e1fbd9c38a2881af81", common.Bytes2Hex(hash.Bytes()))
|
||||
|
||||
templateBlockTrace, err = os.ReadFile("../../../common/testdata/blockTrace_03.json")
|
||||
assert.NoError(t, err)
|
||||
|
||||
wrappedBlock2 := &WrappedBlock{}
|
||||
assert.NoError(t, json.Unmarshal(templateBlockTrace, wrappedBlock2))
|
||||
chunk2 := &Chunk{
|
||||
Blocks: []*WrappedBlock{
|
||||
wrappedBlock2,
|
||||
},
|
||||
}
|
||||
batchHeader2, err := NewBatchHeader(1, 2, 0, batchHeader.Hash(), []*Chunk{chunk2})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, batchHeader2)
|
||||
hash2 := batchHeader2.Hash()
|
||||
assert.Equal(t, "34de600163aa745d4513113137a5b54960d13f0d3f2849e490c4b875028bf930", common.Bytes2Hex(hash2.Bytes()))
|
||||
|
||||
// With L1 Msg
|
||||
templateBlockTrace3, err := os.ReadFile("../../../common/testdata/blockTrace_04.json")
|
||||
assert.NoError(t, err)
|
||||
|
||||
wrappedBlock3 := &WrappedBlock{}
|
||||
assert.NoError(t, json.Unmarshal(templateBlockTrace3, wrappedBlock3))
|
||||
chunk = &Chunk{
|
||||
Blocks: []*WrappedBlock{
|
||||
wrappedBlock3,
|
||||
},
|
||||
}
|
||||
batchHeader, err = NewBatchHeader(1, 1, 0, parentBatchHeader.Hash(), []*Chunk{chunk})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, batchHeader)
|
||||
hash = batchHeader.Hash()
|
||||
assert.Equal(t, "0ec9547c6645d5f0c1254e121f49e93f54525cfda5bfb2236440fb3470f48902", common.Bytes2Hex(hash.Bytes()))
|
||||
}
|
||||
|
||||
func TestBatchHeaderDecode(t *testing.T) {
|
||||
header := &BatchHeader{
|
||||
version: 1,
|
||||
batchIndex: 10,
|
||||
l1MessagePopped: 20,
|
||||
totalL1MessagePopped: 30,
|
||||
dataHash: common.HexToHash("0x01"),
|
||||
parentBatchHash: common.HexToHash("0x02"),
|
||||
skippedL1MessageBitmap: []byte{0x01, 0x02, 0x03},
|
||||
}
|
||||
|
||||
encoded := header.Encode()
|
||||
decoded, err := DecodeBatchHeader(encoded)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, header, decoded)
|
||||
}
|
||||
90
bridge/internal/types/batch_test.go
Normal file
90
bridge/internal/types/batch_test.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
gethTypes "github.com/scroll-tech/go-ethereum/core/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
abi "scroll-tech/bridge/abi"
|
||||
)
|
||||
|
||||
func TestBatchHash(t *testing.T) {
|
||||
txBytes := common.Hex2Bytes("02f8710582fd14808506e38dccc9825208944d496ccc28058b1d74b7a19541663e21154f9c848801561db11e24a43380c080a0d890606d7a35b2ab0f9b866d62c092d5b163f3e6a55537ae1485aac08c3f8ff7a023997be2d32f53e146b160fff0ba81e81dbb4491c865ab174d15c5b3d28c41ae")
|
||||
tx := new(gethTypes.Transaction)
|
||||
if err := tx.UnmarshalBinary(txBytes); err != nil {
|
||||
t.Fatalf("invalid tx hex string: %s", err)
|
||||
}
|
||||
|
||||
batchData := new(BatchData)
|
||||
batchData.TxHashes = append(batchData.TxHashes, tx.Hash())
|
||||
batchData.piCfg = &PublicInputHashConfig{
|
||||
MaxTxNum: 4,
|
||||
PaddingTxHash: common.HexToHash("0xb5baa665b2664c3bfed7eb46e00ebc110ecf2ebd257854a9bf2b9dbc9b2c08f6"),
|
||||
}
|
||||
|
||||
batch := &batchData.Batch
|
||||
batch.PrevStateRoot = common.HexToHash("0x000000000000000000000000000000000000000000000000000000000000cafe")
|
||||
|
||||
block := abi.IScrollChainBlockContext{
|
||||
BlockNumber: 51966,
|
||||
Timestamp: 123456789,
|
||||
BaseFee: new(big.Int).SetUint64(0),
|
||||
GasLimit: 10000000000000000,
|
||||
NumTransactions: 1,
|
||||
NumL1Messages: 0,
|
||||
}
|
||||
batch.Blocks = append(batch.Blocks, block)
|
||||
|
||||
hash := batchData.Hash()
|
||||
assert.Equal(t, *hash, common.HexToHash("0xa9f2ca3175794f91226a410ba1e60fff07a405c957562675c4149b77e659d805"))
|
||||
|
||||
// use a different tx hash
|
||||
txBytes = common.Hex2Bytes("f8628001830f424094000000000000000000000000000000000000bbbb8080820a97a064e07cd8f939e2117724bdcbadc80dda421381cbc2a1f4e0d093d9cc5c5cf68ea03e264227f80852d88743cd9e43998f2746b619180366a87e4531debf9c3fa5dc")
|
||||
tx = new(gethTypes.Transaction)
|
||||
if err := tx.UnmarshalBinary(txBytes); err != nil {
|
||||
t.Fatalf("invalid tx hex string: %s", err)
|
||||
}
|
||||
batchData.TxHashes[0] = tx.Hash()
|
||||
|
||||
batchData.hash = nil // clear the cache
|
||||
assert.Equal(t, *batchData.Hash(), common.HexToHash("0x398cb22bbfa1665c1b342b813267538a4c933d7f92d8bd9184aba0dd1122987b"))
|
||||
}
|
||||
|
||||
func TestNewGenesisBatch(t *testing.T) {
|
||||
genesisBlock := &gethTypes.Header{
|
||||
UncleHash: common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"),
|
||||
Root: common.HexToHash("0x1b186a7a90ec3b41a2417062fe44dce8ce82ae76bfbb09eae786a4f1be1895f5"),
|
||||
TxHash: common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"),
|
||||
ReceiptHash: common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"),
|
||||
Difficulty: big.NewInt(1),
|
||||
Number: big.NewInt(0),
|
||||
GasLimit: 940000000,
|
||||
GasUsed: 0,
|
||||
Time: 1639724192,
|
||||
Extra: common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000214f8d488aa9ebf83e30bad45fb8f9c8ee2509f5511caff794753d07e9dfb218cfc233bb62d2c57022783094e1a7edb6f069f8424bb68496a0926b130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
|
||||
BaseFee: big.NewInt(1000000000),
|
||||
}
|
||||
assert.Equal(
|
||||
t,
|
||||
genesisBlock.Hash().Hex(),
|
||||
"0x92826bd3aad2ef70d8061dc4e25150b305d1233d9cd7579433a77d6eb01dae1c",
|
||||
"wrong genesis block header",
|
||||
)
|
||||
|
||||
blockTrace := &WrappedBlock{genesisBlock, nil, common.Hash{}}
|
||||
batchData := NewGenesisBatchData(blockTrace)
|
||||
t.Log(batchData.Batch.Blocks[0])
|
||||
batchData.piCfg = &PublicInputHashConfig{
|
||||
MaxTxNum: 25,
|
||||
PaddingTxHash: common.HexToHash("0xb5baa665b2664c3bfed7eb46e00ebc110ecf2ebd257854a9bf2b9dbc9b2c08f6"),
|
||||
}
|
||||
assert.Equal(
|
||||
t,
|
||||
batchData.Hash().Hex(),
|
||||
"0x65cf210e30f75cf8fd198df124255f73bc08d6324759e828a784fa938e7ac43d",
|
||||
"wrong genesis batch hash",
|
||||
)
|
||||
}
|
||||
@@ -1,18 +1,10 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
||||
"github.com/scroll-tech/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
const nonZeroByteGas uint64 = 16
|
||||
const zeroByteGas uint64 = 4
|
||||
|
||||
// WrappedBlock contains the block's Header, Transactions and WithdrawTrieRoot hash.
|
||||
type WrappedBlock struct {
|
||||
Header *types.Header `json:"header"`
|
||||
@@ -21,116 +13,9 @@ type WrappedBlock struct {
|
||||
WithdrawTrieRoot common.Hash `json:"withdraw_trie_root,omitempty"`
|
||||
}
|
||||
|
||||
// NumL1Messages returns the number of L1 messages in this block.
|
||||
// This number is the sum of included and skipped L1 messages.
|
||||
func (w *WrappedBlock) NumL1Messages(totalL1MessagePoppedBefore uint64) uint64 {
|
||||
var lastQueueIndex *uint64
|
||||
for _, txData := range w.Transactions {
|
||||
if txData.Type == types.L1MessageTxType {
|
||||
lastQueueIndex = &txData.Nonce
|
||||
}
|
||||
}
|
||||
if lastQueueIndex == nil {
|
||||
return 0
|
||||
}
|
||||
// note: last queue index included before this block is totalL1MessagePoppedBefore - 1
|
||||
// TODO: cache results
|
||||
return *lastQueueIndex - totalL1MessagePoppedBefore + 1
|
||||
}
|
||||
|
||||
// Encode encodes the WrappedBlock into RollupV2 BlockContext Encoding.
|
||||
func (w *WrappedBlock) Encode(totalL1MessagePoppedBefore uint64) ([]byte, error) {
|
||||
bytes := make([]byte, 60)
|
||||
|
||||
if !w.Header.Number.IsUint64() {
|
||||
return nil, errors.New("block number is not uint64")
|
||||
}
|
||||
if len(w.Transactions) > math.MaxUint16 {
|
||||
return nil, errors.New("number of transactions exceeds max uint16")
|
||||
}
|
||||
|
||||
numL1Messages := w.NumL1Messages(totalL1MessagePoppedBefore)
|
||||
if numL1Messages > math.MaxUint16 {
|
||||
return nil, errors.New("number of L1 messages exceeds max uint16")
|
||||
}
|
||||
|
||||
binary.BigEndian.PutUint64(bytes[0:], w.Header.Number.Uint64())
|
||||
binary.BigEndian.PutUint64(bytes[8:], w.Header.Time)
|
||||
// TODO: [16:47] Currently, baseFee is 0, because we disable EIP-1559.
|
||||
binary.BigEndian.PutUint64(bytes[48:], w.Header.GasLimit)
|
||||
binary.BigEndian.PutUint16(bytes[56:], uint16(len(w.Transactions)))
|
||||
binary.BigEndian.PutUint16(bytes[58:], uint16(numL1Messages))
|
||||
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
// EstimateL1CommitCalldataSize calculates the calldata size in l1 commit approximately.
|
||||
// TODO: The calculation could be more accurate by using 58 + len(l2TxDataBytes) (see Chunk).
|
||||
// This needs to be adjusted in the future.
|
||||
func (w *WrappedBlock) EstimateL1CommitCalldataSize() uint64 {
|
||||
var size uint64
|
||||
for _, txData := range w.Transactions {
|
||||
if txData.Type == types.L1MessageTxType {
|
||||
continue
|
||||
}
|
||||
size += uint64(len(txData.Data))
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
// EstimateL1CommitGas calculates the calldata gas in l1 commit approximately.
|
||||
// TODO: This will need to be adjusted.
|
||||
// The part added here is only the calldata cost,
|
||||
// but we have execution cost for verifying blocks / chunks / batches and storing the batch hash.
|
||||
func (w *WrappedBlock) EstimateL1CommitGas() uint64 {
|
||||
var total uint64
|
||||
for _, txData := range w.Transactions {
|
||||
if txData.Type == types.L1MessageTxType {
|
||||
continue
|
||||
}
|
||||
data, _ := hexutil.Decode(txData.Data)
|
||||
tx := types.NewTx(&types.LegacyTx{
|
||||
Nonce: txData.Nonce,
|
||||
To: txData.To,
|
||||
Value: txData.Value.ToInt(),
|
||||
Gas: txData.Gas,
|
||||
GasPrice: txData.GasPrice.ToInt(),
|
||||
Data: data,
|
||||
V: txData.V.ToInt(),
|
||||
R: txData.R.ToInt(),
|
||||
S: txData.S.ToInt(),
|
||||
})
|
||||
rlpTxData, _ := tx.MarshalBinary()
|
||||
|
||||
for _, b := range rlpTxData {
|
||||
if b == 0 {
|
||||
total += zeroByteGas
|
||||
} else {
|
||||
total += nonZeroByteGas
|
||||
}
|
||||
}
|
||||
|
||||
var txLen [4]byte
|
||||
binary.BigEndian.PutUint32(txLen[:], uint32(len(rlpTxData)))
|
||||
|
||||
for _, b := range txLen {
|
||||
if b == 0 {
|
||||
total += zeroByteGas
|
||||
} else {
|
||||
total += nonZeroByteGas
|
||||
}
|
||||
}
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
// L2TxsNum calculates the number of l2 txs.
|
||||
func (w *WrappedBlock) L2TxsNum() uint64 {
|
||||
var count uint64
|
||||
for _, txData := range w.Transactions {
|
||||
if txData.Type != types.L1MessageTxType {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
// BatchInfo contains the BlockBatch's main info
|
||||
type BatchInfo struct {
|
||||
Index uint64 `json:"index"`
|
||||
Hash string `json:"hash"`
|
||||
StateRoot string `json:"state_root"`
|
||||
}
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
||||
"github.com/scroll-tech/go-ethereum/core/types"
|
||||
"github.com/scroll-tech/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
// Chunk contains blocks to be encoded
|
||||
type Chunk struct {
|
||||
Blocks []*WrappedBlock `json:"blocks"`
|
||||
}
|
||||
|
||||
// NumL1Messages returns the number of L1 messages in this chunk.
|
||||
// This number is the sum of included and skipped L1 messages.
|
||||
func (c *Chunk) NumL1Messages(totalL1MessagePoppedBefore uint64) uint64 {
|
||||
var numL1Messages uint64
|
||||
for _, block := range c.Blocks {
|
||||
numL1MessagesInBlock := block.NumL1Messages(totalL1MessagePoppedBefore)
|
||||
numL1Messages += numL1MessagesInBlock
|
||||
totalL1MessagePoppedBefore += numL1MessagesInBlock
|
||||
}
|
||||
// TODO: cache results
|
||||
return numL1Messages
|
||||
}
|
||||
|
||||
// Encode encodes the Chunk into RollupV2 Chunk Encoding.
|
||||
func (c *Chunk) Encode(totalL1MessagePoppedBefore uint64) ([]byte, error) {
|
||||
numBlocks := len(c.Blocks)
|
||||
|
||||
if numBlocks > 255 {
|
||||
return nil, errors.New("number of blocks exceeds 1 byte")
|
||||
}
|
||||
if numBlocks == 0 {
|
||||
return nil, errors.New("number of blocks is 0")
|
||||
}
|
||||
|
||||
var chunkBytes []byte
|
||||
chunkBytes = append(chunkBytes, byte(numBlocks))
|
||||
|
||||
var l2TxDataBytes []byte
|
||||
|
||||
for _, block := range c.Blocks {
|
||||
blockBytes, err := block.Encode(totalL1MessagePoppedBefore)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to encode block: %v", err)
|
||||
}
|
||||
totalL1MessagePoppedBefore += block.NumL1Messages(totalL1MessagePoppedBefore)
|
||||
|
||||
if len(blockBytes) != 60 {
|
||||
return nil, fmt.Errorf("block encoding is not 60 bytes long %x", len(blockBytes))
|
||||
}
|
||||
|
||||
chunkBytes = append(chunkBytes, blockBytes...)
|
||||
|
||||
// Append rlp-encoded l2Txs
|
||||
for _, txData := range block.Transactions {
|
||||
if txData.Type == types.L1MessageTxType {
|
||||
continue
|
||||
}
|
||||
data, err := hexutil.Decode(txData.Data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// right now we only support legacy tx
|
||||
tx := types.NewTx(&types.LegacyTx{
|
||||
Nonce: txData.Nonce,
|
||||
To: txData.To,
|
||||
Value: txData.Value.ToInt(),
|
||||
Gas: txData.Gas,
|
||||
GasPrice: txData.GasPrice.ToInt(),
|
||||
Data: data,
|
||||
V: txData.V.ToInt(),
|
||||
R: txData.R.ToInt(),
|
||||
S: txData.S.ToInt(),
|
||||
})
|
||||
rlpTxData, err := tx.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var txLen [4]byte
|
||||
binary.BigEndian.PutUint32(txLen[:], uint32(len(rlpTxData)))
|
||||
l2TxDataBytes = append(l2TxDataBytes, txLen[:]...)
|
||||
l2TxDataBytes = append(l2TxDataBytes, rlpTxData...)
|
||||
}
|
||||
}
|
||||
|
||||
chunkBytes = append(chunkBytes, l2TxDataBytes...)
|
||||
|
||||
return chunkBytes, nil
|
||||
}
|
||||
|
||||
// Hash hashes the Chunk into RollupV2 Chunk Hash
|
||||
func (c *Chunk) Hash(totalL1MessagePoppedBefore uint64) (common.Hash, error) {
|
||||
chunkBytes, err := c.Encode(totalL1MessagePoppedBefore)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
numBlocks := chunkBytes[0]
|
||||
|
||||
// concatenate block contexts
|
||||
var dataBytes []byte
|
||||
for i := 0; i < int(numBlocks); i++ {
|
||||
// only the first 58 bytes of each BlockContext are needed for the hashing process
|
||||
dataBytes = append(dataBytes, chunkBytes[1+60*i:60*i+59]...)
|
||||
}
|
||||
|
||||
// concatenate l1 and l2 tx hashes
|
||||
for _, block := range c.Blocks {
|
||||
var l1TxHashes []byte
|
||||
var l2TxHashes []byte
|
||||
for _, txData := range block.Transactions {
|
||||
txHash := strings.TrimPrefix(txData.TxHash, "0x")
|
||||
hashBytes, err := hex.DecodeString(txHash)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
if txData.Type == types.L1MessageTxType {
|
||||
l1TxHashes = append(l1TxHashes, hashBytes...)
|
||||
} else {
|
||||
l2TxHashes = append(l2TxHashes, hashBytes...)
|
||||
}
|
||||
}
|
||||
dataBytes = append(dataBytes, l1TxHashes...)
|
||||
dataBytes = append(dataBytes, l2TxHashes...)
|
||||
}
|
||||
|
||||
hash := crypto.Keccak256Hash(dataBytes)
|
||||
return hash, nil
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestChunkEncode(t *testing.T) {
|
||||
// Test case 1: when the chunk contains no blocks.
|
||||
chunk := &Chunk{
|
||||
Blocks: []*WrappedBlock{},
|
||||
}
|
||||
bytes, err := chunk.Encode(0)
|
||||
assert.Nil(t, bytes)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "number of blocks is 0")
|
||||
|
||||
// Test case 2: when the chunk contains more than 255 blocks.
|
||||
chunk = &Chunk{
|
||||
Blocks: []*WrappedBlock{},
|
||||
}
|
||||
for i := 0; i < 256; i++ {
|
||||
chunk.Blocks = append(chunk.Blocks, &WrappedBlock{})
|
||||
}
|
||||
bytes, err = chunk.Encode(0)
|
||||
assert.Nil(t, bytes)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "number of blocks exceeds 1 byte")
|
||||
|
||||
// Test case 3: when the chunk contains one block.
|
||||
templateBlockTrace, err := os.ReadFile("../../../common/testdata/blockTrace_02.json")
|
||||
assert.NoError(t, err)
|
||||
|
||||
wrappedBlock := &WrappedBlock{}
|
||||
assert.NoError(t, json.Unmarshal(templateBlockTrace, wrappedBlock))
|
||||
assert.Equal(t, uint64(0), wrappedBlock.NumL1Messages(0))
|
||||
chunk = &Chunk{
|
||||
Blocks: []*WrappedBlock{
|
||||
wrappedBlock,
|
||||
},
|
||||
}
|
||||
bytes, err = chunk.Encode(0)
|
||||
hexString := hex.EncodeToString(bytes)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 299, len(bytes))
|
||||
assert.Equal(t, "0100000000000000020000000063807b2a0000000000000000000000000000000000000000000000000000000000000000000355418d1e81840002000000000073f87180843b9aec2e8307a12094c0c4c8baea3f6acb49b6e1fb9e2adeceeacb0ca28a152d02c7e14af60000008083019ecea0ab07ae99c67aa78e7ba5cf6781e90cc32b219b1de102513d56548a41e86df514a034cbd19feacd73e8ce64d00c4d1996b9b5243c578fd7f51bfaec288bbaf42a8b00000073f87101843b9aec2e8307a1209401bae6bf68e9a03fb2bc0615b1bf0d69ce9411ed8a152d02c7e14af60000008083019ecea0f039985866d8256f10c1be4f7b2cace28d8f20bde27e2604393eb095b7f77316a05a3e6e81065f2b4604bcec5bd4aba684835996fc3f879380aac1c09c6eed32f1", hexString)
|
||||
|
||||
// Test case 4: when the chunk contains one block with 1 L1MsgTx
|
||||
templateBlockTrace2, err := os.ReadFile("../../../common/testdata/blockTrace_04.json")
|
||||
assert.NoError(t, err)
|
||||
|
||||
wrappedBlock2 := &WrappedBlock{}
|
||||
assert.NoError(t, json.Unmarshal(templateBlockTrace2, wrappedBlock2))
|
||||
assert.Equal(t, uint64(11), wrappedBlock2.NumL1Messages(0)) // 0..=9 skipped, 10 included
|
||||
chunk = &Chunk{
|
||||
Blocks: []*WrappedBlock{
|
||||
wrappedBlock2,
|
||||
},
|
||||
}
|
||||
bytes, err = chunk.Encode(0)
|
||||
hexString = hex.EncodeToString(bytes)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 97, len(bytes))
|
||||
assert.Equal(t, "01000000000000000d00000000646b6e13000000000000000000000000000000000000000000000000000000000000000000000000007a12000002000b00000020df0b80825dc0941a258d17bf244c4df02d40343a7626a9d321e1058080808080", hexString)
|
||||
|
||||
// Test case 5: when the chunk contains two blocks each with 1 L1MsgTx
|
||||
chunk = &Chunk{
|
||||
Blocks: []*WrappedBlock{
|
||||
wrappedBlock2,
|
||||
wrappedBlock2,
|
||||
},
|
||||
}
|
||||
bytes, err = chunk.Encode(0)
|
||||
hexString = hex.EncodeToString(bytes)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 193, len(bytes))
|
||||
assert.Equal(t, "02000000000000000d00000000646b6e13000000000000000000000000000000000000000000000000000000000000000000000000007a12000002000b000000000000000d00000000646b6e13000000000000000000000000000000000000000000000000000000000000000000000000007a12000002000000000020df0b80825dc0941a258d17bf244c4df02d40343a7626a9d321e105808080808000000020df0b80825dc0941a258d17bf244c4df02d40343a7626a9d321e1058080808080", hexString)
|
||||
}
|
||||
|
||||
func TestChunkHash(t *testing.T) {
|
||||
// Test case 1: when the chunk contains no blocks
|
||||
chunk := &Chunk{
|
||||
Blocks: []*WrappedBlock{},
|
||||
}
|
||||
hash, err := chunk.Hash(0)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "0x0000000000000000000000000000000000000000000000000000000000000000", hash.Hex())
|
||||
assert.Contains(t, err.Error(), "number of blocks is 0")
|
||||
|
||||
// Test case 2: successfully hashing a chunk on one block
|
||||
templateBlockTrace, err := os.ReadFile("../../../common/testdata/blockTrace_02.json")
|
||||
assert.NoError(t, err)
|
||||
wrappedBlock := &WrappedBlock{}
|
||||
assert.NoError(t, json.Unmarshal(templateBlockTrace, wrappedBlock))
|
||||
chunk = &Chunk{
|
||||
Blocks: []*WrappedBlock{
|
||||
wrappedBlock,
|
||||
},
|
||||
}
|
||||
hash, err = chunk.Hash(0)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "0x78c839dfc494396c16b40946f32b3f4c3e8c2d4bfd04aefcf235edec474482f8", hash.Hex())
|
||||
|
||||
// Test case 3: successfully hashing a chunk on two blocks
|
||||
templateBlockTrace1, err := os.ReadFile("../../../common/testdata/blockTrace_03.json")
|
||||
assert.NoError(t, err)
|
||||
wrappedBlock1 := &WrappedBlock{}
|
||||
assert.NoError(t, json.Unmarshal(templateBlockTrace1, wrappedBlock1))
|
||||
chunk = &Chunk{
|
||||
Blocks: []*WrappedBlock{
|
||||
wrappedBlock,
|
||||
wrappedBlock1,
|
||||
},
|
||||
}
|
||||
hash, err = chunk.Hash(0)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "0xaa9e494f72bc6965857856f0fae6916f27b2a6591c714a573b2fab46df03b8ae", hash.Hex())
|
||||
|
||||
// Test case 4: successfully hashing a chunk on two blocks each with L1 and L2 txs
|
||||
templateBlockTrace2, err := os.ReadFile("../../../common/testdata/blockTrace_04.json")
|
||||
assert.NoError(t, err)
|
||||
wrappedBlock2 := &WrappedBlock{}
|
||||
assert.NoError(t, json.Unmarshal(templateBlockTrace2, wrappedBlock2))
|
||||
chunk = &Chunk{
|
||||
Blocks: []*WrappedBlock{
|
||||
wrappedBlock2,
|
||||
wrappedBlock2,
|
||||
},
|
||||
}
|
||||
hash, err = chunk.Hash(0)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "0x42967825696a129e7a83f082097aca982747480956dcaa448c9296e795c9a91a", hash.Hex())
|
||||
}
|
||||
@@ -1,10 +1,6 @@
|
||||
// SPDX-License-Identifier: UNLICENSED
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import {BatchHeaderV0Codec} from "../../contracts/src/libraries/codec/BatchHeaderV0Codec.sol";
|
||||
import {ChunkCodec} from "../../contracts/src/libraries/codec/ChunkCodec.sol";
|
||||
import {IL1MessageQueue} from "../../contracts/src/L1/rollup/IL1MessageQueue.sol";
|
||||
|
||||
contract MockBridgeL1 {
|
||||
/******************************
|
||||
* Events from L1MessageQueue *
|
||||
@@ -21,7 +17,7 @@ contract MockBridgeL1 {
|
||||
address indexed sender,
|
||||
address indexed target,
|
||||
uint256 value,
|
||||
uint64 queueIndex,
|
||||
uint256 queueIndex,
|
||||
uint256 gasLimit,
|
||||
bytes data
|
||||
);
|
||||
@@ -50,27 +46,74 @@ contract MockBridgeL1 {
|
||||
/// @param messageHash The hash of the message.
|
||||
event RelayedMessage(bytes32 indexed messageHash);
|
||||
|
||||
/// @dev The maximum number of transaction in on batch.
|
||||
uint256 public immutable maxNumTxInBatch;
|
||||
|
||||
/// @dev The hash used for padding public inputs.
|
||||
bytes32 public immutable paddingTxHash;
|
||||
|
||||
/***************************
|
||||
* Events from ScrollChain *
|
||||
***************************/
|
||||
|
||||
/// @notice Emitted when a new batch is committed.
|
||||
/// @param batchHash The hash of the batch.
|
||||
/// @notice Emitted when a new batch is commited.
|
||||
/// @param batchHash The hash of the batch
|
||||
event CommitBatch(bytes32 indexed batchHash);
|
||||
|
||||
/// @notice Emitted when a batch is reverted.
|
||||
/// @param batchHash The identification of the batch.
|
||||
event RevertBatch(bytes32 indexed batchHash);
|
||||
|
||||
/// @notice Emitted when a batch is finalized.
|
||||
/// @param batchHash The hash of the batch
|
||||
/// @param stateRoot The state root in layer 2 after this batch.
|
||||
/// @param withdrawRoot The merkle root in layer2 after this batch.
|
||||
event FinalizeBatch(bytes32 indexed batchHash, bytes32 stateRoot, bytes32 withdrawRoot);
|
||||
event FinalizeBatch(bytes32 indexed batchHash);
|
||||
|
||||
/***********
|
||||
* Structs *
|
||||
***********/
|
||||
|
||||
struct BlockContext {
|
||||
// The hash of this block.
|
||||
bytes32 blockHash;
|
||||
// The parent hash of this block.
|
||||
bytes32 parentHash;
|
||||
// The height of this block.
|
||||
uint64 blockNumber;
|
||||
// The timestamp of this block.
|
||||
uint64 timestamp;
|
||||
// The base fee of this block.
|
||||
// Currently, it is not used, because we disable EIP-1559.
|
||||
// We keep it for future proof.
|
||||
uint256 baseFee;
|
||||
// The gas limit of this block.
|
||||
uint64 gasLimit;
|
||||
// The number of transactions in this block, both L1 & L2 txs.
|
||||
uint16 numTransactions;
|
||||
// The number of l1 messages in this block.
|
||||
uint16 numL1Messages;
|
||||
}
|
||||
|
||||
struct Batch {
|
||||
// The list of blocks in this batch
|
||||
BlockContext[] blocks; // MAX_NUM_BLOCKS = 100, about 5 min
|
||||
// The state root of previous batch.
|
||||
// The first batch will use 0x0 for prevStateRoot
|
||||
bytes32 prevStateRoot;
|
||||
// The state root of the last block in this batch.
|
||||
bytes32 newStateRoot;
|
||||
// The withdraw trie root of the last block in this batch.
|
||||
bytes32 withdrawTrieRoot;
|
||||
// The index of the batch.
|
||||
uint64 batchIndex;
|
||||
// The parent batch hash.
|
||||
bytes32 parentBatchHash;
|
||||
// Concatenated raw data of RLP encoded L2 txs
|
||||
bytes l2Transactions;
|
||||
}
|
||||
|
||||
struct L2MessageProof {
|
||||
// The index of the batch where the message belongs to.
|
||||
uint256 batchIndex;
|
||||
// The hash of the batch where the message belongs to.
|
||||
bytes32 batchHash;
|
||||
// Concatenation of merkle proof for withdraw merkle trie.
|
||||
bytes merkleProof;
|
||||
}
|
||||
@@ -82,7 +125,14 @@ contract MockBridgeL1 {
|
||||
/// @notice Message nonce, used to avoid relay attack.
|
||||
uint256 public messageNonce;
|
||||
|
||||
mapping(uint256 => bytes32) public committedBatches;
|
||||
/***************
|
||||
* Constructor *
|
||||
***************/
|
||||
|
||||
constructor() {
|
||||
maxNumTxInBatch = 44;
|
||||
paddingTxHash = 0x0000000000000000000000000000000000000000000000000000000000000000;
|
||||
}
|
||||
|
||||
/***********************************
|
||||
* Functions from L2GasPriceOracle *
|
||||
@@ -104,7 +154,7 @@ contract MockBridgeL1 {
|
||||
bytes memory _xDomainCalldata = _encodeXDomainCalldata(msg.sender, target, value, messageNonce, message);
|
||||
{
|
||||
address _sender = applyL1ToL2Alias(address(this));
|
||||
emit QueueTransaction(_sender, target, 0, uint64(messageNonce), gasLimit, _xDomainCalldata);
|
||||
emit QueueTransaction(_sender, target, 0, messageNonce, gasLimit, _xDomainCalldata);
|
||||
}
|
||||
|
||||
emit SentMessage(msg.sender, target, value, messageNonce, gasLimit, message);
|
||||
@@ -128,65 +178,37 @@ contract MockBridgeL1 {
|
||||
* Functions from ScrollChain *
|
||||
******************************/
|
||||
|
||||
function commitBatch(
|
||||
uint8 /*version*/,
|
||||
bytes calldata /*parentBatchHeader*/,
|
||||
bytes[] memory chunks,
|
||||
bytes calldata /*skippedL1MessageBitmap*/
|
||||
) external {
|
||||
// check whether the batch is empty
|
||||
uint256 _chunksLength = chunks.length;
|
||||
require(_chunksLength > 0, "batch is empty");
|
||||
function commitBatch(Batch memory _batch) external {
|
||||
_commitBatch(_batch);
|
||||
}
|
||||
|
||||
uint256 dataPtr;
|
||||
assembly {
|
||||
dataPtr := mload(0x40)
|
||||
mstore(0x40, add(dataPtr, mul(_chunksLength, 32)))
|
||||
function commitBatches(Batch[] memory _batches) external {
|
||||
for (uint256 i = 0; i < _batches.length; i++) {
|
||||
_commitBatch(_batches[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (uint256 i = 0; i < _chunksLength; i++) {
|
||||
_commitChunk(dataPtr, chunks[i]);
|
||||
|
||||
unchecked {
|
||||
dataPtr += 32;
|
||||
}
|
||||
}
|
||||
|
||||
bytes32 _dataHash;
|
||||
assembly {
|
||||
let dataLen := mul(_chunksLength, 0x20)
|
||||
_dataHash := keccak256(sub(dataPtr, dataLen), dataLen)
|
||||
}
|
||||
|
||||
bytes memory paddedData = new bytes(89);
|
||||
assembly {
|
||||
mstore(add(paddedData, 57), _dataHash)
|
||||
}
|
||||
|
||||
uint256 batchPtr;
|
||||
assembly {
|
||||
batchPtr := add(paddedData, 32)
|
||||
}
|
||||
bytes32 _batchHash = BatchHeaderV0Codec.computeBatchHash(batchPtr, 89);
|
||||
committedBatches[0] = _batchHash;
|
||||
emit CommitBatch(_batchHash);
|
||||
function revertBatch(bytes32 _batchHash) external {
|
||||
emit RevertBatch(_batchHash);
|
||||
}
|
||||
|
||||
function finalizeBatchWithProof(
|
||||
bytes calldata /*batchHeader*/,
|
||||
bytes32 /*prevStateRoot*/,
|
||||
bytes32 postStateRoot,
|
||||
bytes32 withdrawRoot,
|
||||
bytes calldata /*aggrProof*/
|
||||
bytes32 _batchHash,
|
||||
uint256[] memory,
|
||||
uint256[] memory
|
||||
) external {
|
||||
bytes32 _batchHash = committedBatches[0];
|
||||
emit FinalizeBatch(_batchHash, postStateRoot, withdrawRoot);
|
||||
emit FinalizeBatch(_batchHash);
|
||||
}
|
||||
|
||||
/**********************
|
||||
* Internal Functions *
|
||||
**********************/
|
||||
|
||||
function _commitBatch(Batch memory _batch) internal {
|
||||
bytes32 _batchHash = _computePublicInputHash(_batch);
|
||||
emit CommitBatch(_batchHash);
|
||||
}
|
||||
|
||||
/// @dev Internal function to generate the correct cross domain calldata for a message.
|
||||
/// @param _sender Message sender address.
|
||||
/// @param _target Target contract address.
|
||||
@@ -212,10 +234,6 @@ contract MockBridgeL1 {
|
||||
);
|
||||
}
|
||||
|
||||
/// @notice Utility function that converts the address in the L1 that submitted a tx to
|
||||
/// the inbox to the msg.sender viewed in the L2
|
||||
/// @param l1Address the address in the L1 that triggered the tx to L2
|
||||
/// @return l2Address L2 address as viewed in msg.sender
|
||||
function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) {
|
||||
uint160 offset = uint160(0x1111000000000000000000000000000000001111);
|
||||
unchecked {
|
||||
@@ -223,67 +241,140 @@ contract MockBridgeL1 {
|
||||
}
|
||||
}
|
||||
|
||||
function _commitChunk(
|
||||
uint256 memPtr,
|
||||
bytes memory _chunk
|
||||
) internal pure {
|
||||
uint256 chunkPtr;
|
||||
uint256 startDataPtr;
|
||||
uint256 dataPtr;
|
||||
uint256 blockPtr;
|
||||
|
||||
assembly {
|
||||
dataPtr := mload(0x40)
|
||||
startDataPtr := dataPtr
|
||||
chunkPtr := add(_chunk, 0x20) // skip chunkLength
|
||||
blockPtr := add(chunkPtr, 1) // skip numBlocks
|
||||
}
|
||||
|
||||
uint256 _numBlocks = ChunkCodec.validateChunkLength(chunkPtr, _chunk.length);
|
||||
|
||||
// concatenate block contexts
|
||||
uint256 _totalTransactionsInChunk;
|
||||
for (uint256 i = 0; i < _numBlocks; i++) {
|
||||
dataPtr = ChunkCodec.copyBlockContext(chunkPtr, dataPtr, i);
|
||||
uint256 _numTransactionsInBlock = ChunkCodec.numTransactions(blockPtr);
|
||||
unchecked {
|
||||
_totalTransactionsInChunk += _numTransactionsInBlock;
|
||||
blockPtr += ChunkCodec.BLOCK_CONTEXT_LENGTH;
|
||||
/// @dev Internal function to compute the public input hash.
|
||||
/// @param batch The batch to compute.
|
||||
function _computePublicInputHash(Batch memory batch)
|
||||
internal
|
||||
view
|
||||
returns (
|
||||
bytes32
|
||||
)
|
||||
{
|
||||
uint256 publicInputsPtr;
|
||||
// 1. append prevStateRoot, newStateRoot and withdrawTrieRoot to public inputs
|
||||
{
|
||||
bytes32 prevStateRoot = batch.prevStateRoot;
|
||||
bytes32 newStateRoot = batch.newStateRoot;
|
||||
bytes32 withdrawTrieRoot = batch.withdrawTrieRoot;
|
||||
// number of bytes in public inputs: 32 * 3 + 124 * blocks + 32 * MAX_NUM_TXS
|
||||
uint256 publicInputsSize = 32 * 3 + batch.blocks.length * 124 + 32 * maxNumTxInBatch;
|
||||
assembly {
|
||||
publicInputsPtr := mload(0x40)
|
||||
mstore(0x40, add(publicInputsPtr, publicInputsSize))
|
||||
mstore(publicInputsPtr, prevStateRoot)
|
||||
publicInputsPtr := add(publicInputsPtr, 0x20)
|
||||
mstore(publicInputsPtr, newStateRoot)
|
||||
publicInputsPtr := add(publicInputsPtr, 0x20)
|
||||
mstore(publicInputsPtr, withdrawTrieRoot)
|
||||
publicInputsPtr := add(publicInputsPtr, 0x20)
|
||||
}
|
||||
}
|
||||
|
||||
assembly {
|
||||
mstore(0x40, add(dataPtr, mul(_totalTransactionsInChunk, 0x20))) // reserve memory for tx hashes
|
||||
blockPtr := add(chunkPtr, 1) // reset block ptr
|
||||
}
|
||||
uint64 numTransactionsInBatch;
|
||||
BlockContext memory _block;
|
||||
// 2. append block information to public inputs.
|
||||
for (uint256 i = 0; i < batch.blocks.length; i++) {
|
||||
// validate blocks, we won't check first block against previous batch.
|
||||
{
|
||||
BlockContext memory _currentBlock = batch.blocks[i];
|
||||
if (i > 0) {
|
||||
require(_block.blockHash == _currentBlock.parentHash, "Parent hash mismatch");
|
||||
require(_block.blockNumber + 1 == _currentBlock.blockNumber, "Block number mismatch");
|
||||
}
|
||||
_block = _currentBlock;
|
||||
}
|
||||
|
||||
// concatenate tx hashes
|
||||
uint256 l2TxPtr = ChunkCodec.l2TxPtr(chunkPtr, _numBlocks);
|
||||
while (_numBlocks > 0) {
|
||||
// concatenate l2 transaction hashes
|
||||
uint256 _numTransactionsInBlock = ChunkCodec.numTransactions(blockPtr);
|
||||
for (uint256 j = 0; j < _numTransactionsInBlock; j++) {
|
||||
bytes32 txHash;
|
||||
(txHash, l2TxPtr) = ChunkCodec.loadL2TxHash(l2TxPtr);
|
||||
// append blockHash and parentHash to public inputs
|
||||
{
|
||||
bytes32 blockHash = _block.blockHash;
|
||||
bytes32 parentHash = _block.parentHash;
|
||||
assembly {
|
||||
mstore(dataPtr, txHash)
|
||||
dataPtr := add(dataPtr, 0x20)
|
||||
mstore(publicInputsPtr, blockHash)
|
||||
publicInputsPtr := add(publicInputsPtr, 0x20)
|
||||
mstore(publicInputsPtr, parentHash)
|
||||
publicInputsPtr := add(publicInputsPtr, 0x20)
|
||||
}
|
||||
}
|
||||
// append blockNumber and blockTimestamp to public inputs
|
||||
{
|
||||
uint256 blockNumber = _block.blockNumber;
|
||||
uint256 blockTimestamp = _block.timestamp;
|
||||
assembly {
|
||||
mstore(publicInputsPtr, shl(192, blockNumber))
|
||||
publicInputsPtr := add(publicInputsPtr, 0x8)
|
||||
mstore(publicInputsPtr, shl(192, blockTimestamp))
|
||||
publicInputsPtr := add(publicInputsPtr, 0x8)
|
||||
}
|
||||
}
|
||||
// append baseFee to public inputs
|
||||
{
|
||||
uint256 baseFee = _block.baseFee;
|
||||
assembly {
|
||||
mstore(publicInputsPtr, baseFee)
|
||||
publicInputsPtr := add(publicInputsPtr, 0x20)
|
||||
}
|
||||
}
|
||||
uint64 numTransactionsInBlock = _block.numTransactions;
|
||||
// gasLimit, numTransactions and numL1Messages to public inputs
|
||||
{
|
||||
uint256 gasLimit = _block.gasLimit;
|
||||
uint256 numL1MessagesInBlock = _block.numL1Messages;
|
||||
assembly {
|
||||
mstore(publicInputsPtr, shl(192, gasLimit))
|
||||
publicInputsPtr := add(publicInputsPtr, 0x8)
|
||||
mstore(publicInputsPtr, shl(240, numTransactionsInBlock))
|
||||
publicInputsPtr := add(publicInputsPtr, 0x2)
|
||||
mstore(publicInputsPtr, shl(240, numL1MessagesInBlock))
|
||||
publicInputsPtr := add(publicInputsPtr, 0x2)
|
||||
}
|
||||
}
|
||||
numTransactionsInBatch += numTransactionsInBlock;
|
||||
}
|
||||
require(numTransactionsInBatch <= maxNumTxInBatch, "Too many transactions in batch");
|
||||
|
||||
unchecked {
|
||||
_numBlocks -= 1;
|
||||
blockPtr += ChunkCodec.BLOCK_CONTEXT_LENGTH;
|
||||
// 3. append transaction hash to public inputs.
|
||||
uint256 _l2TxnPtr;
|
||||
{
|
||||
bytes memory l2Transactions = batch.l2Transactions;
|
||||
assembly {
|
||||
_l2TxnPtr := add(l2Transactions, 0x20)
|
||||
}
|
||||
}
|
||||
for (uint256 i = 0; i < batch.blocks.length; i++) {
|
||||
uint256 numL1MessagesInBlock = batch.blocks[i].numL1Messages;
|
||||
require(numL1MessagesInBlock == 0);
|
||||
uint256 numTransactionsInBlock = batch.blocks[i].numTransactions;
|
||||
for (uint256 j = numL1MessagesInBlock; j < numTransactionsInBlock; ++j) {
|
||||
bytes32 hash;
|
||||
assembly {
|
||||
let txPayloadLength := shr(224, mload(_l2TxnPtr))
|
||||
_l2TxnPtr := add(_l2TxnPtr, 4)
|
||||
_l2TxnPtr := add(_l2TxnPtr, txPayloadLength)
|
||||
hash := keccak256(sub(_l2TxnPtr, txPayloadLength), txPayloadLength)
|
||||
mstore(publicInputsPtr, hash)
|
||||
publicInputsPtr := add(publicInputsPtr, 0x20)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check chunk has correct length
|
||||
require(l2TxPtr - chunkPtr == _chunk.length, "incomplete l2 transaction data");
|
||||
|
||||
// compute data hash and store to memory
|
||||
assembly {
|
||||
let dataHash := keccak256(startDataPtr, sub(dataPtr, startDataPtr))
|
||||
mstore(memPtr, dataHash)
|
||||
// 4. append padding transaction to public inputs.
|
||||
bytes32 txHashPadding = paddingTxHash;
|
||||
for (uint256 i = numTransactionsInBatch; i < maxNumTxInBatch; i++) {
|
||||
assembly {
|
||||
mstore(publicInputsPtr, txHashPadding)
|
||||
publicInputsPtr := add(publicInputsPtr, 0x20)
|
||||
}
|
||||
}
|
||||
|
||||
// 5. compute public input hash
|
||||
bytes32 publicInputHash;
|
||||
{
|
||||
uint256 publicInputsSize = 32 * 3 + batch.blocks.length * 124 + 32 * maxNumTxInBatch;
|
||||
assembly {
|
||||
publicInputHash := keccak256(sub(publicInputsPtr, publicInputsSize), publicInputsSize)
|
||||
}
|
||||
}
|
||||
|
||||
return publicInputHash;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,24 @@ contract MockBridgeL2 {
|
||||
/// @param messageHash The hash of the corresponding message.
|
||||
event AppendMessage(uint256 index, bytes32 messageHash);
|
||||
|
||||
/********************************
|
||||
* Events from L1BlockContainer *
|
||||
********************************/
|
||||
|
||||
/// @notice Emitted when a block is imported.
|
||||
/// @param blockHash The hash of the imported block.
|
||||
/// @param blockHeight The height of the imported block.
|
||||
/// @param blockTimestamp The timestamp of the imported block.
|
||||
/// @param baseFee The base fee of the imported block.
|
||||
/// @param stateRoot The state root of the imported block.
|
||||
event ImportBlock(
|
||||
bytes32 indexed blockHash,
|
||||
uint256 blockHeight,
|
||||
uint256 blockTimestamp,
|
||||
uint256 baseFee,
|
||||
bytes32 stateRoot
|
||||
);
|
||||
|
||||
/*********************************
|
||||
* Events from L2ScrollMessenger *
|
||||
*********************************/
|
||||
|
||||
@@ -63,9 +63,9 @@ func setupDB(t *testing.T) *gorm.DB {
|
||||
func TestMain(m *testing.M) {
|
||||
base = docker.NewDockerApp()
|
||||
bridgeApp = bcmd.NewBridgeApp(base, "../conf/config.json")
|
||||
defer bridgeApp.Free()
|
||||
defer base.Free()
|
||||
m.Run()
|
||||
bridgeApp.Free()
|
||||
base.Free()
|
||||
}
|
||||
|
||||
func setupEnv(t *testing.T) {
|
||||
@@ -129,10 +129,6 @@ func prepareContracts(t *testing.T) {
|
||||
func TestFunction(t *testing.T) {
|
||||
setupEnv(t)
|
||||
|
||||
// process start test
|
||||
t.Run("TestProcessStart", testProcessStart)
|
||||
t.Run("TestProcessStartEnableMetrics", testProcessStartEnableMetrics)
|
||||
|
||||
// l1 rollup and watch rollup events
|
||||
t.Run("TestCommitBatchAndFinalizeBatch", testCommitBatchAndFinalizeBatch)
|
||||
|
||||
@@ -140,7 +136,7 @@ func TestFunction(t *testing.T) {
|
||||
t.Run("TestRelayL1MessageSucceed", testRelayL1MessageSucceed)
|
||||
|
||||
// l2 message
|
||||
// TODO: add a "user relay l2msg Succeed" test
|
||||
t.Run("TestRelayL2MessageSucceed", testRelayL2MessageSucceed)
|
||||
|
||||
// l1/l2 gas oracle
|
||||
t.Run("TestImportL1GasPrice", testImportL1GasPrice)
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
gethTypes "github.com/scroll-tech/go-ethereum/core/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"scroll-tech/common/types"
|
||||
|
||||
@@ -68,40 +69,50 @@ func testImportL2GasPrice(t *testing.T) {
|
||||
prepareContracts(t)
|
||||
|
||||
l2Cfg := bridgeApp.Config.L2Config
|
||||
l2Relayer, err := relayer.NewLayer2Relayer(context.Background(), l2Client, db, l2Cfg.RelayerConfig, false)
|
||||
l2Relayer, err := relayer.NewLayer2Relayer(context.Background(), l2Client, db, l2Cfg.RelayerConfig)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// add fake chunk
|
||||
chunk := &bridgeTypes.Chunk{
|
||||
Blocks: []*bridgeTypes.WrappedBlock{
|
||||
{
|
||||
Header: &gethTypes.Header{
|
||||
Number: big.NewInt(1),
|
||||
ParentHash: common.Hash{},
|
||||
Difficulty: big.NewInt(0),
|
||||
BaseFee: big.NewInt(0),
|
||||
},
|
||||
Transactions: nil,
|
||||
WithdrawTrieRoot: common.Hash{},
|
||||
// add fake blocks
|
||||
traces := []*bridgeTypes.WrappedBlock{
|
||||
{
|
||||
Header: &gethTypes.Header{
|
||||
Number: big.NewInt(1),
|
||||
ParentHash: common.Hash{},
|
||||
Difficulty: big.NewInt(0),
|
||||
BaseFee: big.NewInt(0),
|
||||
},
|
||||
Transactions: nil,
|
||||
WithdrawTrieRoot: common.Hash{},
|
||||
},
|
||||
}
|
||||
chunkHash, err := chunk.Hash(0)
|
||||
assert.NoError(t, err)
|
||||
|
||||
batchOrm := orm.NewBatch(db)
|
||||
_, err = batchOrm.InsertBatch(context.Background(), 0, 0, chunkHash.Hex(), chunkHash.Hex(), []*bridgeTypes.Chunk{chunk})
|
||||
blockTraceOrm := orm.NewBlockTrace(db)
|
||||
assert.NoError(t, blockTraceOrm.InsertWrappedBlocks(traces))
|
||||
|
||||
parentBatch := &bridgeTypes.BatchInfo{
|
||||
Index: 0,
|
||||
Hash: "0x0000000000000000000000000000000000000000",
|
||||
}
|
||||
batchData := bridgeTypes.NewBatchData(parentBatch, []*bridgeTypes.WrappedBlock{traces[0]}, l2Cfg.BatchProposerConfig.PublicInputConfig)
|
||||
blockBatchOrm := orm.NewBlockBatch(db)
|
||||
err = db.Transaction(func(tx *gorm.DB) error {
|
||||
_, dbTxErr := blockBatchOrm.InsertBlockBatchByBatchData(tx, batchData)
|
||||
if dbTxErr != nil {
|
||||
return dbTxErr
|
||||
}
|
||||
return nil
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// check db status
|
||||
batch, err := batchOrm.GetLatestBatch(context.Background())
|
||||
batch, err := blockBatchOrm.GetLatestBatch()
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, batch.OracleTxHash)
|
||||
assert.Equal(t, types.GasOracleStatus(batch.OracleStatus), types.GasOraclePending)
|
||||
|
||||
// relay gas price
|
||||
l2Relayer.ProcessGasPriceOracle()
|
||||
batch, err = batchOrm.GetLatestBatch(context.Background())
|
||||
batch, err = blockBatchOrm.GetLatestBatch()
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, batch.OracleTxHash)
|
||||
assert.Equal(t, types.GasOracleStatus(batch.OracleStatus), types.GasOracleImporting)
|
||||
|
||||
187
bridge/tests/l2_message_relay_test.go
Normal file
187
bridge/tests/l2_message_relay_test.go
Normal file
@@ -0,0 +1,187 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/accounts/abi/bind"
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
gethTypes "github.com/scroll-tech/go-ethereum/core/types"
|
||||
"github.com/scroll-tech/go-ethereum/rpc"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"scroll-tech/common/types"
|
||||
|
||||
"scroll-tech/bridge/internal/controller/relayer"
|
||||
"scroll-tech/bridge/internal/controller/watcher"
|
||||
"scroll-tech/bridge/internal/orm"
|
||||
bridgeTypes "scroll-tech/bridge/internal/types"
|
||||
"scroll-tech/bridge/internal/utils"
|
||||
)
|
||||
|
||||
func testRelayL2MessageSucceed(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer utils.CloseDB(db)
|
||||
|
||||
prepareContracts(t)
|
||||
|
||||
l2Cfg := bridgeApp.Config.L2Config
|
||||
|
||||
// Create L2Watcher
|
||||
confirmations := rpc.LatestBlockNumber
|
||||
l2Watcher := watcher.NewL2WatcherClient(context.Background(), l2Client, confirmations, l2Cfg.L2MessengerAddress, l2Cfg.L2MessageQueueAddress, l2Cfg.WithdrawTrieRootSlot, db)
|
||||
|
||||
// Create L2Relayer
|
||||
l2Relayer, err := relayer.NewLayer2Relayer(context.Background(), l2Client, db, l2Cfg.RelayerConfig)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Create L1Watcher
|
||||
l1Cfg := bridgeApp.Config.L1Config
|
||||
l1Watcher := watcher.NewL1WatcherClient(context.Background(), l1Client, 0, confirmations, l1Cfg.L1MessengerAddress, l1Cfg.L1MessageQueueAddress, l1Cfg.ScrollChainContractAddress, db)
|
||||
|
||||
// send message through l2 messenger contract
|
||||
nonce, err := l2MessengerInstance.MessageNonce(&bind.CallOpts{})
|
||||
assert.NoError(t, err)
|
||||
sendTx, err := l2MessengerInstance.SendMessage(l2Auth, l1Auth.From, big.NewInt(0), common.Hex2Bytes("00112233"), big.NewInt(0))
|
||||
assert.NoError(t, err)
|
||||
sendReceipt, err := bind.WaitMined(context.Background(), l2Client, sendTx)
|
||||
assert.NoError(t, err)
|
||||
if sendReceipt.Status != gethTypes.ReceiptStatusSuccessful || err != nil {
|
||||
t.Fatalf("Call failed")
|
||||
}
|
||||
|
||||
// l2 watch process events
|
||||
l2Watcher.FetchContractEvent()
|
||||
l2MessageOrm := orm.NewL2Message(db)
|
||||
blockTraceOrm := orm.NewBlockTrace(db)
|
||||
blockBatchOrm := orm.NewBlockBatch(db)
|
||||
|
||||
// check db status
|
||||
msg, err := l2MessageOrm.GetL2MessageByNonce(nonce.Uint64())
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, types.MsgStatus(msg.Status), types.MsgPending)
|
||||
assert.Equal(t, msg.Sender, l2Auth.From.String())
|
||||
assert.Equal(t, msg.Target, l1Auth.From.String())
|
||||
|
||||
// add fake blocks
|
||||
traces := []*bridgeTypes.WrappedBlock{
|
||||
{
|
||||
Header: &gethTypes.Header{
|
||||
Number: sendReceipt.BlockNumber,
|
||||
ParentHash: common.Hash{},
|
||||
Difficulty: big.NewInt(0),
|
||||
BaseFee: big.NewInt(0),
|
||||
},
|
||||
Transactions: nil,
|
||||
WithdrawTrieRoot: common.Hash{},
|
||||
},
|
||||
}
|
||||
assert.NoError(t, blockTraceOrm.InsertWrappedBlocks(traces))
|
||||
|
||||
parentBatch := &bridgeTypes.BatchInfo{
|
||||
Index: 0,
|
||||
Hash: "0x0000000000000000000000000000000000000000",
|
||||
}
|
||||
batchData := bridgeTypes.NewBatchData(parentBatch, []*bridgeTypes.WrappedBlock{traces[0]}, l2Cfg.BatchProposerConfig.PublicInputConfig)
|
||||
batchHash := batchData.Hash().String()
|
||||
// add fake batch
|
||||
err = db.Transaction(func(tx *gorm.DB) error {
|
||||
rowsAffected, dbTxErr := blockBatchOrm.InsertBlockBatchByBatchData(tx, batchData)
|
||||
if dbTxErr != nil {
|
||||
return dbTxErr
|
||||
}
|
||||
if rowsAffected != 1 {
|
||||
dbTxErr = errors.New("the InsertBlockBatchByBatchData affected row is not 1")
|
||||
return dbTxErr
|
||||
}
|
||||
var blockIDs = make([]uint64, len(batchData.Batch.Blocks))
|
||||
for i, block := range batchData.Batch.Blocks {
|
||||
blockIDs[i] = block.BlockNumber
|
||||
}
|
||||
dbTxErr = blockTraceOrm.UpdateBatchHashForL2Blocks(tx, blockIDs, batchHash)
|
||||
if dbTxErr != nil {
|
||||
return dbTxErr
|
||||
}
|
||||
return nil
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// add dummy proof
|
||||
tProof := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}
|
||||
tInstanceCommitments := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}
|
||||
err = blockBatchOrm.UpdateProofByHash(context.Background(), batchHash, tProof, tInstanceCommitments, 100)
|
||||
assert.NoError(t, err)
|
||||
err = blockBatchOrm.UpdateProvingStatus(batchHash, types.ProvingTaskVerified)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// process pending batch and check status
|
||||
assert.NoError(t, l2Relayer.SendCommitTx([]*bridgeTypes.BatchData{batchData}))
|
||||
|
||||
blockBatches, err := blockBatchOrm.GetBlockBatches(map[string]interface{}{"hash": batchHash}, nil, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(blockBatches))
|
||||
assert.NotEmpty(t, blockBatches[0].CommitTxHash)
|
||||
assert.Equal(t, types.RollupCommitting, types.RollupStatus(blockBatches[0].RollupStatus))
|
||||
|
||||
commitTx, _, err := l1Client.TransactionByHash(context.Background(), common.HexToHash(blockBatches[0].CommitTxHash))
|
||||
assert.NoError(t, err)
|
||||
commitTxReceipt, err := bind.WaitMined(context.Background(), l1Client, commitTx)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, len(commitTxReceipt.Logs), 1)
|
||||
|
||||
// fetch CommitBatch rollup events
|
||||
err = l1Watcher.FetchContractEvent()
|
||||
assert.NoError(t, err)
|
||||
statuses, err := blockBatchOrm.GetRollupStatusByHashList([]string{batchHash})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(statuses))
|
||||
assert.Equal(t, types.RollupCommitted, statuses[0])
|
||||
|
||||
// process committed batch and check status
|
||||
l2Relayer.ProcessCommittedBatches()
|
||||
|
||||
blockBatchWithFinalizeTxHash, err := blockBatchOrm.GetBlockBatches(map[string]interface{}{"hash": batchHash}, nil, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(blockBatchWithFinalizeTxHash))
|
||||
assert.NotEmpty(t, blockBatchWithFinalizeTxHash[0].FinalizeTxHash)
|
||||
assert.Equal(t, types.RollupFinalizing, types.RollupStatus(blockBatchWithFinalizeTxHash[0].RollupStatus))
|
||||
|
||||
finalizeTx, _, err := l1Client.TransactionByHash(context.Background(), common.HexToHash(blockBatchWithFinalizeTxHash[0].FinalizeTxHash))
|
||||
assert.NoError(t, err)
|
||||
finalizeTxReceipt, err := bind.WaitMined(context.Background(), l1Client, finalizeTx)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, len(finalizeTxReceipt.Logs), 1)
|
||||
|
||||
// fetch FinalizeBatch events
|
||||
err = l1Watcher.FetchContractEvent()
|
||||
assert.NoError(t, err)
|
||||
statuses, err = blockBatchOrm.GetRollupStatusByHashList([]string{batchHash})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(statuses))
|
||||
assert.Equal(t, types.RollupFinalized, statuses[0])
|
||||
|
||||
// process l2 messages
|
||||
l2Relayer.ProcessSavedEvents()
|
||||
|
||||
l2Messages, err := l2MessageOrm.GetL2Messages(map[string]interface{}{"nonce": nonce.Uint64()}, nil, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(l2Messages))
|
||||
assert.NotEmpty(t, l2Messages[0].Layer1Hash)
|
||||
assert.Equal(t, types.MsgStatus(l2Messages[0].Status), types.MsgSubmitted)
|
||||
|
||||
relayTx, _, err := l1Client.TransactionByHash(context.Background(), common.HexToHash(l2Messages[0].Layer1Hash))
|
||||
assert.NoError(t, err)
|
||||
relayTxReceipt, err := bind.WaitMined(context.Background(), l1Client, relayTx)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, len(relayTxReceipt.Logs), 1)
|
||||
|
||||
// fetch message relayed events
|
||||
err = l1Watcher.FetchContractEvent()
|
||||
assert.NoError(t, err)
|
||||
msg, err = l2MessageOrm.GetL2MessageByNonce(nonce.Uint64())
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, types.MsgStatus(msg.Status), types.MsgConfirmed)
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
_ "scroll-tech/bridge/cmd/event_watcher/app"
|
||||
_ "scroll-tech/bridge/cmd/gas_oracle/app"
|
||||
_ "scroll-tech/bridge/cmd/msg_relayer/app"
|
||||
_ "scroll-tech/bridge/cmd/rollup_relayer/app"
|
||||
|
||||
cutils "scroll-tech/common/utils"
|
||||
|
||||
"scroll-tech/bridge/internal/utils"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func testProcessStart(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer utils.CloseDB(db)
|
||||
|
||||
bridgeApp.RunApp(t, cutils.EventWatcherApp)
|
||||
bridgeApp.RunApp(t, cutils.GasOracleApp)
|
||||
bridgeApp.RunApp(t, cutils.MessageRelayerApp)
|
||||
bridgeApp.RunApp(t, cutils.RollupRelayerApp)
|
||||
|
||||
bridgeApp.WaitExit()
|
||||
}
|
||||
|
||||
func testProcessStartEnableMetrics(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer utils.CloseDB(db)
|
||||
|
||||
port, err := rand.Int(rand.Reader, big.NewInt(2000))
|
||||
assert.NoError(t, err)
|
||||
svrPort := strconv.FormatInt(port.Int64()+50000, 10)
|
||||
bridgeApp.RunApp(t, cutils.EventWatcherApp, "--metrics", "--metrics.addr", "localhost", "--metrics.port", svrPort)
|
||||
|
||||
port, err = rand.Int(rand.Reader, big.NewInt(2000))
|
||||
assert.NoError(t, err)
|
||||
svrPort = strconv.FormatInt(port.Int64()+50000, 10)
|
||||
bridgeApp.RunApp(t, cutils.GasOracleApp, "--metrics", "--metrics.addr", "localhost", "--metrics.port", svrPort)
|
||||
|
||||
port, err = rand.Int(rand.Reader, big.NewInt(2000))
|
||||
assert.NoError(t, err)
|
||||
svrPort = strconv.FormatInt(port.Int64()+50000, 10)
|
||||
bridgeApp.RunApp(t, cutils.MessageRelayerApp, "--metrics", "--metrics.addr", "localhost", "--metrics.port", svrPort)
|
||||
|
||||
port, err = rand.Int(rand.Reader, big.NewInt(2000))
|
||||
assert.NoError(t, err)
|
||||
svrPort = strconv.FormatInt(port.Int64()+50000, 10)
|
||||
bridgeApp.RunApp(t, cutils.RollupRelayerApp, "--metrics", "--metrics.addr", "localhost", "--metrics.port", svrPort)
|
||||
|
||||
bridgeApp.WaitExit()
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package tests
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
@@ -9,11 +10,10 @@ import (
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
gethTypes "github.com/scroll-tech/go-ethereum/core/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"scroll-tech/common/types"
|
||||
"scroll-tech/common/types/message"
|
||||
|
||||
"scroll-tech/bridge/internal/config"
|
||||
"scroll-tech/bridge/internal/controller/relayer"
|
||||
"scroll-tech/bridge/internal/controller/watcher"
|
||||
"scroll-tech/bridge/internal/orm"
|
||||
@@ -29,19 +29,22 @@ func testCommitBatchAndFinalizeBatch(t *testing.T) {
|
||||
|
||||
// Create L2Relayer
|
||||
l2Cfg := bridgeApp.Config.L2Config
|
||||
l2Relayer, err := relayer.NewLayer2Relayer(context.Background(), l2Client, db, l2Cfg.RelayerConfig, false)
|
||||
l2Relayer, err := relayer.NewLayer2Relayer(context.Background(), l2Client, db, l2Cfg.RelayerConfig)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Create L1Watcher
|
||||
l1Cfg := bridgeApp.Config.L1Config
|
||||
l1Watcher := watcher.NewL1WatcherClient(context.Background(), l1Client, 0, l1Cfg.Confirmations, l1Cfg.L1MessengerAddress, l1Cfg.L1MessageQueueAddress, l1Cfg.ScrollChainContractAddress, db)
|
||||
|
||||
blockTraceOrm := orm.NewBlockTrace(db)
|
||||
|
||||
// add some blocks to db
|
||||
var wrappedBlocks []*bridgeTypes.WrappedBlock
|
||||
for i := 0; i < 10; i++ {
|
||||
var parentHash common.Hash
|
||||
for i := 1; i <= 10; i++ {
|
||||
header := gethTypes.Header{
|
||||
Number: big.NewInt(int64(i)),
|
||||
ParentHash: common.Hash{},
|
||||
ParentHash: parentHash,
|
||||
Difficulty: big.NewInt(0),
|
||||
BaseFee: big.NewInt(0),
|
||||
}
|
||||
@@ -50,47 +53,56 @@ func testCommitBatchAndFinalizeBatch(t *testing.T) {
|
||||
Transactions: nil,
|
||||
WithdrawTrieRoot: common.Hash{},
|
||||
})
|
||||
parentHash = header.Hash()
|
||||
}
|
||||
assert.NoError(t, blockTraceOrm.InsertWrappedBlocks(wrappedBlocks))
|
||||
|
||||
parentBatch := &bridgeTypes.BatchInfo{
|
||||
Index: 0,
|
||||
Hash: "0x0000000000000000000000000000000000000000",
|
||||
}
|
||||
|
||||
l2BlockOrm := orm.NewL2Block(db)
|
||||
err = l2BlockOrm.InsertL2Blocks(context.Background(), wrappedBlocks)
|
||||
tmpWrapBlocks := []*bridgeTypes.WrappedBlock{
|
||||
wrappedBlocks[0],
|
||||
wrappedBlocks[1],
|
||||
}
|
||||
batchData := bridgeTypes.NewBatchData(parentBatch, tmpWrapBlocks, l2Cfg.BatchProposerConfig.PublicInputConfig)
|
||||
|
||||
batchHash := batchData.Hash().String()
|
||||
|
||||
blockBatchOrm := orm.NewBlockBatch(db)
|
||||
err = db.Transaction(func(tx *gorm.DB) error {
|
||||
rowsAffected, dbTxErr := blockBatchOrm.InsertBlockBatchByBatchData(tx, batchData)
|
||||
if dbTxErr != nil {
|
||||
return dbTxErr
|
||||
}
|
||||
if rowsAffected != 1 {
|
||||
dbTxErr = errors.New("the InsertBlockBatchByBatchData affected row is not 1")
|
||||
return dbTxErr
|
||||
}
|
||||
var blockIDs = make([]uint64, len(batchData.Batch.Blocks))
|
||||
for i, block := range batchData.Batch.Blocks {
|
||||
blockIDs[i] = block.BlockNumber
|
||||
}
|
||||
dbTxErr = blockTraceOrm.UpdateBatchHashForL2Blocks(tx, blockIDs, batchHash)
|
||||
if dbTxErr != nil {
|
||||
return dbTxErr
|
||||
}
|
||||
return nil
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
cp := watcher.NewChunkProposer(context.Background(), &config.ChunkProposerConfig{
|
||||
MaxTxGasPerChunk: 1000000000,
|
||||
MaxL2TxNumPerChunk: 10000,
|
||||
MaxL1CommitGasPerChunk: 50000000000,
|
||||
MaxL1CommitCalldataSizePerChunk: 1000000,
|
||||
MinL1CommitCalldataSizePerChunk: 0,
|
||||
ChunkTimeoutSec: 300,
|
||||
}, db)
|
||||
cp.TryProposeChunk()
|
||||
// process pending batch and check status
|
||||
assert.NoError(t, l2Relayer.SendCommitTx([]*bridgeTypes.BatchData{batchData}))
|
||||
|
||||
chunkOrm := orm.NewChunk(db)
|
||||
chunks, err := chunkOrm.GetUnbatchedChunks(context.Background())
|
||||
blockBatches, err := blockBatchOrm.GetBlockBatches(map[string]interface{}{"hash": batchHash}, nil, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, chunks, 1)
|
||||
assert.Equal(t, 1, len(blockBatches))
|
||||
assert.NotEmpty(t, true, blockBatches[0].CommitTxHash)
|
||||
assert.NotEmpty(t, true, blockBatches[0].RollupStatus)
|
||||
assert.Equal(t, types.RollupStatus(blockBatches[0].RollupStatus), types.RollupCommitting)
|
||||
|
||||
bp := watcher.NewBatchProposer(context.Background(), &config.BatchProposerConfig{
|
||||
MaxChunkNumPerBatch: 10,
|
||||
MaxL1CommitGasPerBatch: 50000000000,
|
||||
MaxL1CommitCalldataSizePerBatch: 1000000,
|
||||
MinChunkNumPerBatch: 1,
|
||||
BatchTimeoutSec: 300,
|
||||
}, db)
|
||||
bp.TryProposeBatch()
|
||||
|
||||
l2Relayer.ProcessPendingBatches()
|
||||
|
||||
batchOrm := orm.NewBatch(db)
|
||||
batch, err := batchOrm.GetLatestBatch(context.Background())
|
||||
assert.NoError(t, err)
|
||||
batchHash := batch.Hash
|
||||
assert.NotEmpty(t, batch.CommitTxHash)
|
||||
assert.Equal(t, types.RollupCommitting, types.RollupStatus(batch.RollupStatus))
|
||||
|
||||
assert.NoError(t, err)
|
||||
commitTx, _, err := l1Client.TransactionByHash(context.Background(), common.HexToHash(batch.CommitTxHash))
|
||||
commitTx, _, err := l1Client.TransactionByHash(context.Background(), common.HexToHash(blockBatches[0].CommitTxHash))
|
||||
assert.NoError(t, err)
|
||||
commitTxReceipt, err := bind.WaitMined(context.Background(), l1Client, commitTx)
|
||||
assert.NoError(t, err)
|
||||
@@ -99,34 +111,33 @@ func testCommitBatchAndFinalizeBatch(t *testing.T) {
|
||||
// fetch rollup events
|
||||
err = l1Watcher.FetchContractEvent()
|
||||
assert.NoError(t, err)
|
||||
statuses, err := batchOrm.GetRollupStatusByHashList(context.Background(), []string{batchHash})
|
||||
statuses, err := blockBatchOrm.GetRollupStatusByHashList([]string{batchHash})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(statuses))
|
||||
assert.Equal(t, types.RollupCommitted, statuses[0])
|
||||
|
||||
// add dummy proof
|
||||
proof := &message.AggProof{
|
||||
Proof: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31},
|
||||
FinalPair: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31},
|
||||
}
|
||||
err = batchOrm.UpdateProofByHash(context.Background(), batchHash, proof, 100)
|
||||
tProof := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}
|
||||
tInstanceCommitments := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}
|
||||
err = blockBatchOrm.UpdateProofByHash(context.Background(), batchHash, tProof, tInstanceCommitments, 100)
|
||||
assert.NoError(t, err)
|
||||
err = batchOrm.UpdateProvingStatus(context.Background(), batchHash, types.ProvingTaskVerified)
|
||||
err = blockBatchOrm.UpdateProvingStatus(batchHash, types.ProvingTaskVerified)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// process committed batch and check status
|
||||
l2Relayer.ProcessCommittedBatches()
|
||||
|
||||
statuses, err = batchOrm.GetRollupStatusByHashList(context.Background(), []string{batchHash})
|
||||
statuses, err = blockBatchOrm.GetRollupStatusByHashList([]string{batchHash})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(statuses))
|
||||
assert.Equal(t, types.RollupFinalizing, statuses[0])
|
||||
|
||||
batch, err = batchOrm.GetLatestBatch(context.Background())
|
||||
blockBatches, err = blockBatchOrm.GetBlockBatches(map[string]interface{}{"hash": batchHash}, nil, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, batch.FinalizeTxHash)
|
||||
assert.Equal(t, 1, len(blockBatches))
|
||||
assert.NotEmpty(t, blockBatches[0].FinalizeTxHash)
|
||||
|
||||
finalizeTx, _, err := l1Client.TransactionByHash(context.Background(), common.HexToHash(batch.FinalizeTxHash))
|
||||
finalizeTx, _, err := l1Client.TransactionByHash(context.Background(), common.HexToHash(blockBatches[0].FinalizeTxHash))
|
||||
assert.NoError(t, err)
|
||||
finalizeTxReceipt, err := bind.WaitMined(context.Background(), l1Client, finalizeTx)
|
||||
assert.NoError(t, err)
|
||||
@@ -135,7 +146,7 @@ func testCommitBatchAndFinalizeBatch(t *testing.T) {
|
||||
// fetch rollup events
|
||||
err = l1Watcher.FetchContractEvent()
|
||||
assert.NoError(t, err)
|
||||
statuses, err = batchOrm.GetRollupStatusByHashList(context.Background(), []string{batchHash})
|
||||
statuses, err = blockBatchOrm.GetRollupStatusByHashList([]string{batchHash})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(statuses))
|
||||
assert.Equal(t, types.RollupFinalized, statuses[0])
|
||||
|
||||
@@ -1,46 +1,45 @@
|
||||
ifeq ($(GO_VERSION),)
|
||||
GO_VERSION=1.19
|
||||
endif
|
||||
ifeq ($(RUST_VERSION),)
|
||||
RUST_VERSION=nightly-2022-12-10
|
||||
endif
|
||||
ifeq ($(PYTHON_VERSION),)
|
||||
PYTHON_VERSION=3.10
|
||||
endif
|
||||
ifeq ($(CUDA_VERSION),)
|
||||
CUDA_VERSION=11.7.1
|
||||
endif
|
||||
GO_VERSION := 1.18
|
||||
PYTHON_VERSION := 3.10
|
||||
RUST_VERSION := nightly-2022-12-10
|
||||
|
||||
.PHONY: all go-alpine-builder rust-builder rust-alpine-builder go-rust-alpine-builder go-rust-builder cuda-go-rust-builder py-runner
|
||||
|
||||
cuda-go-rust-builder:
|
||||
docker build -t scrolltech/cuda-go-rust-builder:cuda-$(CUDA_VERSION)-go-$(GO_VERSION)-rust-$(RUST_VERSION) -f cuda-go-rust-builder.Dockerfile ./ --build-arg CUDA_VERSION=$(CUDA_VERSION) --build-arg GO_VERSION=$(GO_VERSION) --build-arg RUST_VERSION=$(RUST_VERSION)
|
||||
.PHONY: all go-alpine-builder rust-builder rust-alpine-builder go-rust-alpine-builder go-rust-builder py-runner
|
||||
|
||||
go-rust-builder:
|
||||
docker build -t scrolltech/go-rust-builder:go-$(GO_VERSION)-rust-$(RUST_VERSION) -f go-rust-builder.Dockerfile ./ --build-arg GO_VERSION=$(GO_VERSION) --build-arg RUST_VERSION=$(RUST_VERSION)
|
||||
docker build -t scrolltech/go-rust-builder:latest -f go-rust-builder.Dockerfile ./
|
||||
docker image tag scrolltech/go-rust-builder:latest scrolltech/go-rust-builder:go-$(GO_VERSION)-rust-$(RUST_VERSION)
|
||||
|
||||
go-alpine-builder:
|
||||
docker build -t scrolltech/go-alpine-builder:$(GO_VERSION) -f go-alpine-builder.Dockerfile ./ --build-arg GO_VERSION=$(GO_VERSION)
|
||||
docker build -t scrolltech/go-alpine-builder:latest -f go-alpine-builder.Dockerfile ./
|
||||
docker image tag scrolltech/go-alpine-builder:latest scrolltech/go-alpine-builder:$(GO_VERSION)
|
||||
|
||||
rust-builder:
|
||||
docker build -t scrolltech/rust-builder:$(RUST_VERSION) -f rust-builder.Dockerfile ./ --build-arg RUST_VERSION=$(RUST_VERSION)
|
||||
docker build -t scrolltech/rust-builder:latest -f rust-builder.Dockerfile ./
|
||||
docker image tag scrolltech/rust-builder:latest scrolltech/rust-builder:$(RUST_VERSION)
|
||||
|
||||
rust-alpine-builder:
|
||||
docker build -t scrolltech/rust-alpine-builder:$(RUST_VERSION) -f rust-alpine-builder.Dockerfile ./ --build-arg RUST_VERSION=$(RUST_VERSION)
|
||||
docker build -t scrolltech/rust-alpine-builder:latest -f rust-alpine-builder.Dockerfile ./
|
||||
docker image tag scrolltech/rust-alpine-builder:latest scrolltech/rust-alpine-builder:$(RUST_VERSION)
|
||||
|
||||
go-rust-alpine-builder:
|
||||
docker build -t scrolltech/go-rust-alpine-builder:go-$(GO_VERSION)-rust-$(RUST_VERSION) -f go-rust-alpine-builder.Dockerfile ./ --build-arg GO_VERSION=$(GO_VERSION) --build-arg RUST_VERSION=$(RUST_VERSION)
|
||||
docker build -t scrolltech/go-rust-alpine-builder:latest -f go-rust-alpine-builder.Dockerfile ./
|
||||
docker image tag scrolltech/go-rust-alpine-builder:latest scrolltech/go-rust-alpine-builder:go-$(GO_VERSION)-rust-$(RUST_VERSION)
|
||||
|
||||
py-runner:
|
||||
docker build -t scrolltech/py-runner:$(PYTHON_VERSION) -f py-runner.Dockerfile ./ --build-arg PYTHON_VERSION=$(PYTHON_VERSION)
|
||||
docker build -t scrolltech/py-runner:latest -f py-runner.Dockerfile ./
|
||||
docker image tag scrolltech/py-runner:latest scrolltech/py-runner:$(PYTHON_VERSION)
|
||||
|
||||
all: go-alpine-builder rust-builder rust-alpine-builder go-rust-alpine-builder go-rust-builder cuda-go-rust-builder py-runner
|
||||
all: go-alpine-builder rust-builder rust-alpine-builder go-rust-alpine-builder go-rust-builder py-runner
|
||||
|
||||
publish:
|
||||
docker push scrolltech/go-alpine-builder:latest
|
||||
docker push scrolltech/go-alpine-builder:$(GO_VERSION)
|
||||
docker push scrolltech/rust-builder:latest
|
||||
docker push scrolltech/rust-builder:$(RUST_VERSION)
|
||||
docker push scrolltech/rust-alpine-builder:latest
|
||||
docker push scrolltech/rust-alpine-builder:$(RUST_VERSION)
|
||||
docker push scrolltech/go-rust-alpine-builder:latest
|
||||
docker push scrolltech/go-rust-alpine-builder:go-$(GO_VERSION)-rust-$(RUST_VERSION)
|
||||
docker push scrolltech/go-rust-builder:latest
|
||||
docker push scrolltech/go-rust-builder:go-$(GO_VERSION)-rust-$(RUST_VERSION)
|
||||
docker push scrolltech/cuda-go-rust-builder:cuda-$(CUDA_VERSION)-go-$(GO_VERSION)-rust-$(RUST_VERSION)
|
||||
docker push scrolltech/py-runner:latest
|
||||
docker push scrolltech/py-runner:$(PYTHON_VERSION)
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
ARG CUDA_VERSION=11.7.1
|
||||
ARG GO_VERSION=1.19
|
||||
ARG RUST_VERSION=nightly-2022-12-10
|
||||
ARG CARGO_CHEF_TAG=0.1.41
|
||||
|
||||
FROM nvidia/cuda:${CUDA_VERSION}-devel-ubuntu22.04
|
||||
RUN apt-get update
|
||||
# Install basic packages
|
||||
RUN apt-get install build-essential curl wget git pkg-config --no-install-recommends -y
|
||||
# Install dev-packages
|
||||
RUN apt-get install libclang-dev libssl-dev cmake llvm --no-install-recommends -y
|
||||
# Install related libs
|
||||
RUN apt install libprocps-dev libboost-all-dev libmpfr-dev libgmp-dev --no-install-recommends -y
|
||||
# Clean installed cache
|
||||
RUN rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Rust
|
||||
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
ENV PATH="/root/.cargo/bin:${PATH}"
|
||||
ENV CARGO_HOME=/root/.cargo
|
||||
|
||||
# Add Toolchain
|
||||
ARG RUST_VERSION
|
||||
RUN rustup toolchain install ${RUST_VERSION}
|
||||
ARG CARGO_CHEF_TAG
|
||||
RUN cargo install cargo-chef --locked --version ${CARGO_CHEF_TAG} \
|
||||
&& rm -rf $CARGO_HOME/registry/
|
||||
|
||||
# Install Go
|
||||
ARG GO_VERSION
|
||||
RUN rm -rf /usr/local/go
|
||||
RUN wget https://go.dev/dl/go${GO_VERSION}.1.linux-amd64.tar.gz
|
||||
RUN tar -C /usr/local -xzf go${GO_VERSION}.1.linux-amd64.tar.gz
|
||||
RUN rm go${GO_VERSION}.1.linux-amd64.tar.gz
|
||||
ENV PATH="/usr/local/go/bin:${PATH}"
|
||||
@@ -1,6 +1,4 @@
|
||||
ARG GO_VERSION=1.19
|
||||
|
||||
FROM golang:${GO_VERSION}-alpine
|
||||
FROM golang:1.18-alpine
|
||||
|
||||
# ENV GOPROXY https://goproxy.cn,direct
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
ARG GO_VERSION=1.19
|
||||
ARG RUST_VERSION=nightly-2022-12-10
|
||||
FROM golang:1.18-alpine
|
||||
ARG CARGO_CHEF_TAG=0.1.41
|
||||
|
||||
FROM golang:${GO_VERSION}-alpine
|
||||
ARG DEFAULT_RUST_TOOLCHAIN=nightly-2022-12-10
|
||||
|
||||
RUN apk add --no-cache gcc musl-dev linux-headers git ca-certificates openssl-dev
|
||||
|
||||
@@ -26,14 +24,12 @@ RUN set -eux; \
|
||||
wget "$url"; \
|
||||
chmod +x rustup-init;
|
||||
|
||||
ARG RUST_VERSION
|
||||
RUN ./rustup-init -y --no-modify-path --default-toolchain ${RUST_VERSION}; \
|
||||
RUN ./rustup-init -y --no-modify-path --default-toolchain ${DEFAULT_RUST_TOOLCHAIN}; \
|
||||
rm rustup-init; \
|
||||
chmod -R a+w $RUSTUP_HOME $CARGO_HOME; \
|
||||
rustup --version; \
|
||||
cargo --version; \
|
||||
rustc --version;
|
||||
|
||||
ARG CARGO_CHEF_TAG
|
||||
RUN cargo install cargo-chef --locked --version ${CARGO_CHEF_TAG} \
|
||||
&& rm -rf $CARGO_HOME/registry/
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
ARG GO_VERSION=1.19
|
||||
ARG RUST_VERSION=nightly-2022-12-10
|
||||
ARG CARGO_CHEF_TAG=0.1.41
|
||||
|
||||
FROM ubuntu:20.04
|
||||
|
||||
RUN apt-get update && ln -fs /usr/share/zoneinfo/America/New_York /etc/localtime
|
||||
|
||||
# Install basic packages
|
||||
RUN apt-get install build-essential curl wget git pkg-config -y
|
||||
|
||||
# Install dev-packages
|
||||
RUN apt-get install libclang-dev libssl-dev llvm -y
|
||||
|
||||
@@ -15,17 +12,23 @@ RUN apt-get install libclang-dev libssl-dev llvm -y
|
||||
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
ENV PATH="/root/.cargo/bin:${PATH}"
|
||||
ENV CARGO_HOME=/root/.cargo
|
||||
|
||||
# Add Toolchain
|
||||
ARG RUST_VERSION
|
||||
RUN rustup toolchain install ${RUST_VERSION}
|
||||
ARG CARGO_CHEF_TAG
|
||||
RUN rustup toolchain install nightly-2022-12-10
|
||||
|
||||
# TODO: make this ARG
|
||||
ENV CARGO_CHEF_TAG=0.1.41
|
||||
|
||||
RUN cargo install cargo-chef --locked --version ${CARGO_CHEF_TAG} \
|
||||
&& rm -rf $CARGO_HOME/registry/
|
||||
|
||||
# Install Go
|
||||
ARG GO_VERSION
|
||||
RUN rm -rf /usr/local/go
|
||||
RUN wget https://go.dev/dl/go${GO_VERSION}.1.linux-amd64.tar.gz
|
||||
RUN tar -C /usr/local -xzf go${GO_VERSION}.1.linux-amd64.tar.gz
|
||||
RUN rm go${GO_VERSION}.1.linux-amd64.tar.gz
|
||||
# for 1.17
|
||||
# RUN wget https://go.dev/dl/go1.17.13.linux-amd64.tar.gz
|
||||
# RUN tar -C /usr/local -xzf go1.17.13.linux-amd64.tar.gz
|
||||
# for 1.18
|
||||
RUN wget https://go.dev/dl/go1.18.9.linux-amd64.tar.gz
|
||||
RUN tar -C /usr/local -xzf go1.18.9.linux-amd64.tar.gz
|
||||
|
||||
ENV PATH="/usr/local/go/bin:${PATH}"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
ARG PYTHON_VERSION=3.10
|
||||
FROM python:${PYTHON_VERSION}-alpine
|
||||
FROM python:3.10-alpine3.15
|
||||
|
||||
RUN apk add --no-cache gcc g++ make musl-dev
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
ARG ALPINE_VERSION=3.15
|
||||
ARG RUST_VERSION=nightly-2022-12-10
|
||||
ARG CARGO_CHEF_TAG=0.1.41
|
||||
|
||||
FROM alpine:${ALPINE_VERSION}
|
||||
ARG CARGO_CHEF_TAG=0.1.41
|
||||
ARG DEFAULT_RUST_TOOLCHAIN=nightly-2022-12-10
|
||||
|
||||
RUN apk add --no-cache \
|
||||
ca-certificates \
|
||||
@@ -28,14 +27,12 @@ RUN set -eux; \
|
||||
wget "$url"; \
|
||||
chmod +x rustup-init;
|
||||
|
||||
ARG RUST_VERSION
|
||||
RUN ./rustup-init -y --no-modify-path --default-toolchain ${RUST_VERSION}; \
|
||||
RUN ./rustup-init -y --no-modify-path --default-toolchain ${DEFAULT_RUST_TOOLCHAIN}; \
|
||||
rm rustup-init; \
|
||||
chmod -R a+w $RUSTUP_HOME $CARGO_HOME; \
|
||||
rustup --version; \
|
||||
cargo --version; \
|
||||
rustc --version;
|
||||
|
||||
ARG CARGO_CHEF_TAG
|
||||
RUN cargo install cargo-chef --locked --version ${CARGO_CHEF_TAG} \
|
||||
&& rm -rf $CARGO_HOME/registry/
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
ARG RUST_VERSION=nightly-2022-12-10
|
||||
ARG CARGO_CHEF_TAG=0.1.41
|
||||
|
||||
FROM ubuntu:20.04
|
||||
|
||||
RUN apt-get update && ln -fs /usr/share/zoneinfo/America/New_York /etc/localtime
|
||||
@@ -14,10 +11,6 @@ RUN apt-get install libclang-dev libssl-dev llvm -y
|
||||
# Install Rust
|
||||
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
ENV PATH="/root/.cargo/bin:${PATH}"
|
||||
ENV CARGO_HOME=/root/.cargo
|
||||
|
||||
# Add Toolchain
|
||||
ARG RUST_VERSION
|
||||
RUN rustup toolchain install ${RUST_VERSION}
|
||||
ARG CARGO_CHEF_TAG
|
||||
RUN cargo install cargo-chef --locked --version ${CARGO_CHEF_TAG} \
|
||||
&& rm -rf $CARGO_HOME/registry/
|
||||
RUN rustup toolchain install nightly-2022-12-10
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
|
||||
const (
|
||||
// GolangCIVersion to be used for linting.
|
||||
GolangCIVersion = "github.com/golangci/golangci-lint/cmd/golangci-lint@v1.52.2"
|
||||
GolangCIVersion = "github.com/golangci/golangci-lint/cmd/golangci-lint@v1.50.0"
|
||||
)
|
||||
|
||||
// GOBIN environment variable.
|
||||
|
||||
14
build/post-test-report-coverage.sh
Executable file
14
build/post-test-report-coverage.sh
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
set -uex
|
||||
${GOROOT}/bin/bin/gocover-cobertura < coverage.bridge.txt > coverage.bridge.xml
|
||||
${GOROOT}/bin/bin/gocover-cobertura < coverage.db.txt > coverage.db.xml
|
||||
${GOROOT}/bin/bin/gocover-cobertura < coverage.common.txt > coverage.common.xml
|
||||
${GOROOT}/bin/bin/gocover-cobertura < coverage.coordinator.txt > coverage.coordinator.xml
|
||||
#${GOROOT}/bin/bin/gocover-cobertura < coverage.integration.txt > coverage.integration.xml
|
||||
|
||||
npx cobertura-merge -o cobertura.xml \
|
||||
package1=coverage.bridge.xml \
|
||||
package2=coverage.db.xml \
|
||||
package3=coverage.common.xml \
|
||||
package4=coverage.coordinator.xml
|
||||
# package5=coverage.integration.xml
|
||||
85
build/push-docker-tag.Jenkinsfile
Normal file
85
build/push-docker-tag.Jenkinsfile
Normal file
@@ -0,0 +1,85 @@
|
||||
imagePrefix = 'scrolltech'
|
||||
credentialDocker = 'dockerhub'
|
||||
TAGNAME = ''
|
||||
pipeline {
|
||||
agent any
|
||||
options {
|
||||
timeout (20)
|
||||
}
|
||||
tools {
|
||||
go 'go-1.18'
|
||||
nodejs "nodejs"
|
||||
}
|
||||
environment {
|
||||
GO111MODULE = 'on'
|
||||
PATH="/home/ubuntu/.cargo/bin:$PATH"
|
||||
// LOG_DOCKER = 'true'
|
||||
}
|
||||
stages {
|
||||
stage('Tag') {
|
||||
steps {
|
||||
script {
|
||||
TAGNAME = sh(returnStdout: true, script: 'git tag -l --points-at HEAD')
|
||||
sh "echo ${TAGNAME}"
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Build') {
|
||||
environment {
|
||||
// Extract the username and password of our credentials into "DOCKER_CREDENTIALS_USR" and "DOCKER_CREDENTIALS_PSW".
|
||||
// (NOTE 1: DOCKER_CREDENTIALS will be set to "your_username:your_password".)
|
||||
// The new variables will always be YOUR_VARIABLE_NAME + _USR and _PSW.
|
||||
// (NOTE 2: You can't print credentials in the pipeline for security reasons.)
|
||||
DOCKER_CREDENTIALS = credentials('dockerhub')
|
||||
}
|
||||
steps {
|
||||
withCredentials([usernamePassword(credentialsId: "${credentialDocker}", passwordVariable: 'dockerPassword', usernameVariable: 'dockerUser')]) {
|
||||
// Use a scripted pipeline.
|
||||
script {
|
||||
stage('Push image') {
|
||||
if (TAGNAME == ""){
|
||||
return;
|
||||
}
|
||||
sh "docker login --username=$dockerUser --password=$dockerPassword"
|
||||
catchError(buildResult: 'SUCCESS', stageResult: 'SUCCESS') {
|
||||
script {
|
||||
try {
|
||||
sh "docker manifest inspect scrolltech/bridge:$TAGNAME > /dev/null"
|
||||
} catch (e) {
|
||||
// only build if the tag non existed
|
||||
//sh "docker login --username=${dockerUser} --password=${dockerPassword}"
|
||||
sh "make -C bridge docker"
|
||||
sh "docker tag scrolltech/bridge:latest scrolltech/bridge:${TAGNAME}"
|
||||
sh "docker push scrolltech/bridge:${TAGNAME}"
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
catchError(buildResult: 'SUCCESS', stageResult: 'SUCCESS') {
|
||||
script {
|
||||
try {
|
||||
sh "docker manifest inspect scrolltech/coordinator:$TAGNAME > /dev/null"
|
||||
} catch (e) {
|
||||
// only build if the tag non existed
|
||||
//sh "docker login --username=${dockerUser} --password=${dockerPassword}"
|
||||
sh "make -C coordinator docker"
|
||||
sh "docker tag scrolltech/coordinator:latest scrolltech/coordinator:${TAGNAME}"
|
||||
sh "docker push scrolltech/coordinator:${TAGNAME}"
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
cleanWs()
|
||||
slackSend(message: "${JOB_BASE_NAME} ${GIT_COMMIT} #${TAGNAME} Tag build ${currentBuild.result}")
|
||||
}
|
||||
}
|
||||
}
|
||||
38
codecov.yml
38
codecov.yml
@@ -1,38 +0,0 @@
|
||||
coverage:
|
||||
status:
|
||||
project: off
|
||||
patch: off
|
||||
flag_management:
|
||||
default_rules:
|
||||
carryforward: true
|
||||
individual_flags:
|
||||
- name: bridge
|
||||
statuses:
|
||||
- type: project
|
||||
target: auto
|
||||
threshold: 1%
|
||||
- name: bridge-history-api
|
||||
statuses:
|
||||
- type: project
|
||||
target: auto
|
||||
threshold: 1%
|
||||
- name: common
|
||||
statuses:
|
||||
- type: project
|
||||
target: auto
|
||||
threshold: 1%
|
||||
- name: coordinator
|
||||
statuses:
|
||||
- type: project
|
||||
target: auto
|
||||
threshold: 1%
|
||||
- name: database
|
||||
statuses:
|
||||
- type: project
|
||||
target: auto
|
||||
threshold: 1%
|
||||
- name: roller
|
||||
statuses:
|
||||
- type: project
|
||||
target: auto
|
||||
threshold: 1%
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM scrolltech/l2geth:scroll-v4.1.0
|
||||
FROM scrolltech/l2geth:scroll-v3.1.12
|
||||
|
||||
RUN mkdir -p /l2geth/keystore
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module scroll-tech/common
|
||||
|
||||
go 1.19
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/docker/docker v20.10.21+incompatible
|
||||
@@ -10,7 +10,7 @@ require (
|
||||
github.com/mattn/go-isatty v0.0.18
|
||||
github.com/modern-go/reflect2 v1.0.2
|
||||
github.com/orcaman/concurrent-map v1.0.0
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20230613025759-f055f50f9d56
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20230508165858-27a3830afa61
|
||||
github.com/stretchr/testify v1.8.2
|
||||
github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa
|
||||
)
|
||||
@@ -85,12 +85,12 @@ require (
|
||||
github.com/tyler-smith/go-bip39 v1.1.0 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
golang.org/x/crypto v0.10.0 // indirect
|
||||
golang.org/x/crypto v0.9.0 // indirect
|
||||
golang.org/x/mod v0.10.0 // indirect
|
||||
golang.org/x/net v0.10.0 // indirect
|
||||
golang.org/x/sync v0.1.0 // indirect
|
||||
golang.org/x/sys v0.9.0 // indirect
|
||||
golang.org/x/text v0.10.0 // indirect
|
||||
golang.org/x/sys v0.8.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.8.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
|
||||
@@ -361,8 +361,8 @@ github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
|
||||
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20230613025759-f055f50f9d56 h1:Cqj7haxwvzI2O4n9ZZ25helShzFGCy7Z/B+FFSBFHNI=
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20230613025759-f055f50f9d56/go.mod h1:45PZqlQCqV0dU4o4+SE8LoJLEvXkK5j45ligvbih9QY=
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20230508165858-27a3830afa61 h1:4fslVpNOPLeJPYX3tivrVWgqNvChPs7/y9OqWvQSNCw=
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20230508165858-27a3830afa61/go.mod h1:45PZqlQCqV0dU4o4+SE8LoJLEvXkK5j45ligvbih9QY=
|
||||
github.com/scroll-tech/zktrie v0.5.3 h1:jjzQchGU6XPL5s1C5bwwivSadefSRuYASE9OL7UKAdE=
|
||||
github.com/scroll-tech/zktrie v0.5.3/go.mod h1:XvNo7vAk8yxNyTjBDj5WIiFzYW4bx/gJ78+NK6Zn6Uk=
|
||||
github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
|
||||
@@ -432,8 +432,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
|
||||
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
||||
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
|
||||
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -543,8 +543,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
|
||||
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -552,8 +552,8 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
|
||||
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
||||
2
common/testdata/blockTrace_03.json
vendored
2
common/testdata/blockTrace_03.json
vendored
@@ -29,7 +29,7 @@
|
||||
{
|
||||
"type": 2,
|
||||
"nonce": 2,
|
||||
"txHash": "0x6b50040f5f14bad253f202b0775d6742131bcaee6b992f05578386f00e53b7e4",
|
||||
"txHash": "0xaaaeb971adfac989c7db5426737bc2932756091a5730ea6d5324f93e4cff9713",
|
||||
"gas": 1152994,
|
||||
"gasPrice": "0x3b9b0a17",
|
||||
"from": "0x1c5a77d9fa7ef466951b2f01f724bca3a5820b63",
|
||||
|
||||
868
common/testdata/blockTrace_04.json
vendored
868
common/testdata/blockTrace_04.json
vendored
@@ -1,868 +0,0 @@
|
||||
{
|
||||
"coinbase": {
|
||||
"address": "0x5300000000000000000000000000000000000005",
|
||||
"nonce": 0,
|
||||
"balance": "0x2aa86921dcd2c0",
|
||||
"keccakCodeHash": "0x256e306f068f0847c8aab5819879b2ff45c021ce2e2f428be51be663415b1d60",
|
||||
"poseidonCodeHash": "0x2c49d7de76e39008575f2f090bb3e90912bad475ea8102c8565c249a75575df5",
|
||||
"codeSize": 1652
|
||||
},
|
||||
"header": {
|
||||
"parentHash": "0xe761181afb179bc4e6848ecc4e32af82c0eeff4aca77024f985d1dffb0ba0013",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"stateRoot": "0x155c42b3ffa9b88987b02bc8f89fb31f2b555bb8bff971d6fcd92e04a144c248",
|
||||
"transactionsRoot": "0x891f5907147c83867e1e7b200b9d26fb43c3c08f81202d04235c84a2aa79f72f",
|
||||
"receiptsRoot": "0x7ad169feb178baf74f7c0a12a28570bd69bd10e616acad2caea09a55fd1fb541",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"difficulty": "0x2",
|
||||
"number": "0xd",
|
||||
"gasLimit": "0x7a1200",
|
||||
"gasUsed": "0x5dc0",
|
||||
"timestamp": "0x646b6e13",
|
||||
"extraData": "0xd983030201846765746889676f312e31382e3130856c696e7578000000000000f942387d5a3dba7786280b806f022e2afaec53939149ac7b132b4ef1cf5cdf393d688543d984ae15b1896185ea13f9e7ae18b22b65e5ffec9128195d7cde6fa700",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"baseFeePerGas": null,
|
||||
"hash": "0x09f75bc27efe18cd77a82491370442ea5a6066e910b73dc99fe1caff950c357b"
|
||||
},
|
||||
"transactions": [
|
||||
{
|
||||
"type": 126,
|
||||
"nonce": 10,
|
||||
"txHash": "0xed6dff31c5516b3b9d169781865276cf27501aadd45c131bf8c841c5e619e56a",
|
||||
"gas": 24000,
|
||||
"gasPrice": "0x0",
|
||||
"from": "0x478cdd110520a8e733e2acf9e543d2c687ea5239",
|
||||
"to": "0x1a258d17bf244c4df02d40343a7626a9d321e105",
|
||||
"chainId": "0x0",
|
||||
"value": "0x0",
|
||||
"data": "0x8ef1332e000000000000000000000000ea08a65b1829af779261e768d609e59279b510f2000000000000000000000000f2ec6b6206f6208e8f9b394efc1a01c1cbde77750000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000a4232e87480000000000000000000000002b5ad5c4795c026514f8317c7a215e218dccd6cf0000000000000000000000002b5ad5c4795c026514f8317c7a215e218dccd6cf00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"isCreate": false,
|
||||
"v": "0x0",
|
||||
"r": "0x0",
|
||||
"s": "0x0"
|
||||
},
|
||||
{
|
||||
"type": 0,
|
||||
"nonce": 11,
|
||||
"txHash": "0xed6dff31c5516b3b9d169781865276cf27501aadd45c131bf8c841c5e619e56a",
|
||||
"gas": 24000,
|
||||
"gasPrice": "0x0",
|
||||
"from": "0x478cdd110520a8e733e2acf9e543d2c687ea5239",
|
||||
"to": "0x1a258d17bf244c4df02d40343a7626a9d321e105",
|
||||
"chainId": "0x0",
|
||||
"value": "0x0",
|
||||
"data": "0x",
|
||||
"isCreate": false,
|
||||
"v": "0x0",
|
||||
"r": "0x0",
|
||||
"s": "0x0"
|
||||
}
|
||||
],
|
||||
"storageTrace": {
|
||||
"rootBefore": "0x16d403e1c55dee3e020457262414ee7a20596922c08cac631385d8ea6d6c2c2b",
|
||||
"rootAfter": "0x155c42b3ffa9b88987b02bc8f89fb31f2b555bb8bff971d6fcd92e04a144c248",
|
||||
"proofs": {
|
||||
"0x1a258d17bF244C4dF02d40343a7626A9D321e105": [
|
||||
"0x000f2d6436a450dc3daf4f111527f3e187a9641e7c5cbc4f53a386e6e4114bb8202cc33de5af63f5deca2409302103a4523463a3a16529835d526795e8966079db",
|
||||
"0x0029ce00b3e5ddca3bd22d3a923b95239ed11243363803b8e1f5a89fb37ee3c6e52c0d8469864d5ee8e0d62944e8dc1de68f78b094d3ef7cf72a21b372866bab0a",
|
||||
"0x001dcee8089ea21f679f1af199cc93ccb35fdea1257b9ffeac0ae5c89654a0dbce20790d9030fd3f822620f7395f1af3ca53789e7451f811c2364f2b4fa19be9fd",
|
||||
"0x000d62fbf3a623b87d67d8f97132a8f1759360c03c1b78ea3654238eb6c72fd5dd0742c02437cc0294c49133a28968ba1f913963d9c2892254da675958cd4a4b2e",
|
||||
"0x0026875849a967c3af8bbd7ac6efb4ef8250efaee44c8bd85ac026d541c7f509ac18ae138a98367696a39f7abe0a53fd3b32283fa843bdc4a2485d65b3b9651670",
|
||||
"0x0125375fd5ae821cd3e835e2fba4ae79971635b7288d549ba8ba66bea36603686c05080000000000000000000000000000000000000000000000000867000000000000000130644e72e131a029b85045b68181585d2833e84879b9705b0e1847ce1160000030221b0e9cf191ce544dcc5c8927fd08af82cb88be110d9533468ffd2d575aed31f2125c021fb94759cb1993a2f07eae01792311e13f209441ff8969cf1eb8351cafbbe8f01ed4c292d9a27be523919a274441a076b20c7d713d192dbe6485c2201a258d17bf244c4df02d40343a7626a9d321e105000000000000000000000000",
|
||||
"0x5448495320495320534f4d45204d4147494320425954455320464f5220534d54206d3172525867503278704449"
|
||||
],
|
||||
"0x478CDd110520a8e733e2ACF9e543d2c687EA5239": [
|
||||
"0x000f2d6436a450dc3daf4f111527f3e187a9641e7c5cbc4f53a386e6e4114bb8202cc33de5af63f5deca2409302103a4523463a3a16529835d526795e8966079db",
|
||||
"0x0029ce00b3e5ddca3bd22d3a923b95239ed11243363803b8e1f5a89fb37ee3c6e52c0d8469864d5ee8e0d62944e8dc1de68f78b094d3ef7cf72a21b372866bab0a",
|
||||
"0x000bf7d923da6cc335d4074262981bf4615b43a8eb2a4dd6f2eda4fd8e1503d9311c4e63762bb10044749243a2b52db21797da53a89ba6b8ceb5cee1596150ac45",
|
||||
"0x002b29daef215b12b331bf75a98e595b8a10a91928f479cca3562db3859315055a1cb697055013d78d58072071584b3e40e8d846948c8e829cbbe9915e4bcf08f0",
|
||||
"0x00000000000000000000000000000000000000000000000000000000000000000007b1a84d4b19493ba2ca6a59dbc42d0e8559a7f8fb0c066bb8b1d90ceee9ce5c",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000000000e9e173703b7c89f67443e861d959df35575c16617ea238fd235d8612f9020ba",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000000000ea71dd32b28e075772420197e740ad0ed7990e3f6e5be7f5051f0c0709defce",
|
||||
"0x000000000000000000000000000000000000000000000000000000000000000000186f00dca57567f28233cef5140efd49b1624b0ec3aef5b7f7ee42f03c3b6231",
|
||||
"0x0006aac99418e9b09baea374df117e64523910d04427251eec6a9b482b6433bc54186c0fb6b2462a9c851df47ab11054dac43ed5b3f9d8d8a5fcf2fd0f9eb3e147",
|
||||
"0x0109c2edb6138e8d6dc8f0b8b5ae98dd721c7053061887757f6749c484bddf92fa05080000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4702098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b6486420478cdd110520a8e733e2acf9e543d2c687ea5239000000000000000000000000",
|
||||
"0x5448495320495320534f4d45204d4147494320425954455320464f5220534d54206d3172525867503278704449"
|
||||
],
|
||||
"0x5300000000000000000000000000000000000000": [
|
||||
"0x000f2d6436a450dc3daf4f111527f3e187a9641e7c5cbc4f53a386e6e4114bb8202cc33de5af63f5deca2409302103a4523463a3a16529835d526795e8966079db",
|
||||
"0x0029ce00b3e5ddca3bd22d3a923b95239ed11243363803b8e1f5a89fb37ee3c6e52c0d8469864d5ee8e0d62944e8dc1de68f78b094d3ef7cf72a21b372866bab0a",
|
||||
"0x001dcee8089ea21f679f1af199cc93ccb35fdea1257b9ffeac0ae5c89654a0dbce20790d9030fd3f822620f7395f1af3ca53789e7451f811c2364f2b4fa19be9fd",
|
||||
"0x000d62fbf3a623b87d67d8f97132a8f1759360c03c1b78ea3654238eb6c72fd5dd0742c02437cc0294c49133a28968ba1f913963d9c2892254da675958cd4a4b2e",
|
||||
"0x0026875849a967c3af8bbd7ac6efb4ef8250efaee44c8bd85ac026d541c7f509ac18ae138a98367696a39f7abe0a53fd3b32283fa843bdc4a2485d65b3b9651670",
|
||||
"0x000a3197466e4643551413444b60bbf8ab0ced04566326492fdf1993586eec3fe10000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0x002143f0cbad38f9696bb9c0be84281e5b517a06983edef7c75485b7a06473c97921dd9af8de7aade9fba53909b1a98ae938236ceec8ba6346ba3ba75c039194d7",
|
||||
"0x0115d04fcf1fe3d9a4cc7a76b70fafcd7b9304b42108af39d9e500be391563775c0508000000000000000000000000000000000000000000000000064d000000000000000000000000000000000000000000000000000000000000000000000000000000002908ab50d1edc9dac80a344f44731acf807809c545e3388816b97a9882b5d4f974ae902ff6a84825a9cde7cc5f26e8c414e88139716c3423ed908f0a60c996011c70d94e9dc7c85d39f6877b01e59a87c057882957d9fd16c55025dfdcaa4d93205300000000000000000000000000000000000000000000000000000000000000",
|
||||
"0x5448495320495320534f4d45204d4147494320425954455320464f5220534d54206d3172525867503278704449"
|
||||
],
|
||||
"0x5300000000000000000000000000000000000002": [
|
||||
"0x000f2d6436a450dc3daf4f111527f3e187a9641e7c5cbc4f53a386e6e4114bb8202cc33de5af63f5deca2409302103a4523463a3a16529835d526795e8966079db",
|
||||
"0x000150eaa497ee8904a3d2dc8350c03963fb1786ea5253d5cc16f321afcd862cee107e99fa497bffacfb8ab50a44b93c9a74bc7c669323c7fbd0560a657342c55a",
|
||||
"0x000877a6983a09f78254ca94a086eb673296f5583aa33855bfbdbe6d2fadf0ff0107b2e01ad456a3ec4c88478c604ad6a15c6fb572259e49ef4cc781940fe1375e",
|
||||
"0x0013b6a97296cf294d19f634904a7fa973d9714b90cc42e0456ad428b7278f338e0accad868d7f4aaa755b29eae6ad523415a9df210ffced28d7d33fa6d5a319b3",
|
||||
"0x0011de0e672d258d43c785592fc939bc105441bafc9c1455901723358b0a73d5cc29562af63a2293f036058180ce56f5269c6a3d4d18d8e1dc75ef03cb8f51f8b9",
|
||||
"0x01236b0ff4611519fb52869dd99bedcb730ebe17544687c5064da49f42f741831d05080000000000000000000000000000000000000000000000000873000000000000000000000000000000000000000000000000000000000000000000000000000000001bd955d4ef171429eb11fade67006376e84bf94630ddb9b9948c3f385ce0f05aa48c68219d344cebd30fca18d0777f587e55052ae6161c88fa4c16407211ddaa0d39d683afa3720f93c44224e2b95a5871a5a2207b5323f7fbf8f1862120ba90205300000000000000000000000000000000000002000000000000000000000000",
|
||||
"0x5448495320495320534f4d45204d4147494320425954455320464f5220534d54206d3172525867503278704449"
|
||||
],
|
||||
"0x5300000000000000000000000000000000000005": [
|
||||
"0x000f2d6436a450dc3daf4f111527f3e187a9641e7c5cbc4f53a386e6e4114bb8202cc33de5af63f5deca2409302103a4523463a3a16529835d526795e8966079db",
|
||||
"0x0029ce00b3e5ddca3bd22d3a923b95239ed11243363803b8e1f5a89fb37ee3c6e52c0d8469864d5ee8e0d62944e8dc1de68f78b094d3ef7cf72a21b372866bab0a",
|
||||
"0x000bf7d923da6cc335d4074262981bf4615b43a8eb2a4dd6f2eda4fd8e1503d9311c4e63762bb10044749243a2b52db21797da53a89ba6b8ceb5cee1596150ac45",
|
||||
"0x002b29daef215b12b331bf75a98e595b8a10a91928f479cca3562db3859315055a1cb697055013d78d58072071584b3e40e8d846948c8e829cbbe9915e4bcf08f0",
|
||||
"0x011facf302b106912bccc8194dff4cb12139e7f04288d3f5eefb57ccf4d842ba22050800000000000000000000000000000000000000000000000006740000000000000000000000000000000000000000000000000000000000000000002aa86921dcd2c018f4988204e816e17e42d9f9a2a468d8ca70ad453a88d3e371a0d9f743b799a6256e306f068f0847c8aab5819879b2ff45c021ce2e2f428be51be663415b1d602c49d7de76e39008575f2f090bb3e90912bad475ea8102c8565c249a75575df5205300000000000000000000000000000000000005000000000000000000000000",
|
||||
"0x5448495320495320534f4d45204d4147494320425954455320464f5220534d54206d3172525867503278704449"
|
||||
]
|
||||
},
|
||||
"storageProofs": {
|
||||
"0x1a258d17bF244C4dF02d40343a7626A9D321e105": {
|
||||
"0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": [
|
||||
"0x001914b8a8cb4d4339d89ed1d5e6cd54ec609082fdf42fadb2d4101f3214f2a2290a1746dfbdf492c00e2854b46eda6adad88ad1b0583997db4121cb7d8e6de5ca",
|
||||
"0x00084878451370def5a5648862c037adb6ae24f29b9237a1823638ca29d573bdd42446af3926a42a7e8b65f9a5fdd5a00e82e4f2b9684816fdc5d52c238bef604a",
|
||||
"0x00027f6e365685a83e63cde58e13d22b99c130a578178f8198d755171a2ff97bf303e187b8ea9652424a9d9dac9bc16796838b196f141c6db57136643f22b48468",
|
||||
"0x00149dad479c283104bb461dcce598d82aacff80a5844d863d8f64e0d3f3e83b1a0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0x001f232429e01853a7456bc8bb4cbc3a35c132f7783e2b300306bceb64a44ce81e0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0x0027e1c425d61d4468534c93b8aa80c34bbdea9ec2d69df7a730ecacf0089b22640000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0x001f4bdfdda0df475064a0ea35302dddc6401b8c93afad9a7569afb9f2534750560000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0x0001fc65caf9a60abae81bcb17c4854fa114100528e73ab1e649fac03ed9fa764e304459eb829e92aa3009534c4eba916b2900783c694385d2e7f87004e7649215",
|
||||
"0x01249c7b39f739f430be8e1e2cae0f1db06dfe2f8d4cc631d312d5b98efb3e7402010100000000000000000000000000008eebfef33eb00149852cadb631838ad9bfcce84820b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103",
|
||||
"0x5448495320495320534f4d45204d4147494320425954455320464f5220534d54206d3172525867503278704449"
|
||||
]
|
||||
},
|
||||
"0x5300000000000000000000000000000000000000": {
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000000": [
|
||||
"0x0004f706d28ba7344cc73128f383e7f4df4c79f296a56e1bbc24cdfab5bc4cba5c2a970eaf68f6e47243e30bea39087adc0082afa5fd55fc5537baccd03f786953",
|
||||
"0x00296af6438bc81ff661ef6d1bb16d33d6784e88ae39ff28258e56e4e72d5607052bb61b23d947a704c29df01936e7c557bf9ec541243566a336b43f8aeca37eed",
|
||||
"0x001750ff1780c9b253cfcbd6274a4f79f3a95819e0856c31f0a6025e30ac3a5b261b73cc5623d88d2687f0fa6006bc823149c779b9e751477a6f2b83773062ddbe",
|
||||
"0x0004c8c2bf27ee6712f4175555679ff662b9423a1d7205fe31e77999106cfb5a2f0efef64a4ef3d151d1364174e0e72745aeee51bf93fb17f8071e6daf4571a736",
|
||||
"0x001de6dfed408db1b0cf580652da17c9277834302d9ee2c39ab074675ca61fd9e02ea58d0958b74734329987e16d8afa4d83a7acc46417a7f7dbc1fd42e305b394",
|
||||
"0x001dd3e7dce636d92fdb4dd8b65cb4e5b8ffd3d64e54a51d93a527826bb1ec3a480000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0x02",
|
||||
"0x5448495320495320534f4d45204d4147494320425954455320464f5220534d54206d3172525867503278704449"
|
||||
]
|
||||
},
|
||||
"0x5300000000000000000000000000000000000002": {
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000001": [
|
||||
"0x00024a2d3ee220db30dece4b39c0cffc2ba97ddded52a3f2da3aeed1f485d0a7220000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0x001da3cd3096ffd62c95bad392eedc1c578e7ccf248898c49c5ed82abb49a4b31a2b63c0d58a64939cf9026618503b904e267eeb0e465e15812b85485e81fb856c",
|
||||
"0x01232927899d46fea05cc897a4f4671f808aa83c4eaf89396dfab15480fee91e8e010100000000000000000000000000005300000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000004",
|
||||
"0x5448495320495320534f4d45204d4147494320425954455320464f5220534d54206d3172525867503278704449"
|
||||
],
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000002": [
|
||||
"0x00024a2d3ee220db30dece4b39c0cffc2ba97ddded52a3f2da3aeed1f485d0a7220000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0x001da3cd3096ffd62c95bad392eedc1c578e7ccf248898c49c5ed82abb49a4b31a2b63c0d58a64939cf9026618503b904e267eeb0e465e15812b85485e81fb856c",
|
||||
"0x012098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864010100000000000000000000000000006f4c950442e1af093bcff730381e63ae9171b87a200000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0x5448495320495320534f4d45204d4147494320425954455320464f5220534d54206d3172525867503278704449"
|
||||
],
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000003": [
|
||||
"0x00024a2d3ee220db30dece4b39c0cffc2ba97ddded52a3f2da3aeed1f485d0a7220000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0x001da3cd3096ffd62c95bad392eedc1c578e7ccf248898c49c5ed82abb49a4b31a2b63c0d58a64939cf9026618503b904e267eeb0e465e15812b85485e81fb856c",
|
||||
"0x012098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864010100000000000000000000000000006f4c950442e1af093bcff730381e63ae9171b87a200000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0x5448495320495320534f4d45204d4147494320425954455320464f5220534d54206d3172525867503278704449"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"executionResults": [
|
||||
{
|
||||
"gas": 24000,
|
||||
"failed": true,
|
||||
"returnValue": "",
|
||||
"from": {
|
||||
"address": "0x478cdd110520a8e733e2acf9e543d2c687ea5239",
|
||||
"nonce": 10,
|
||||
"balance": "0x0",
|
||||
"keccakCodeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
|
||||
"poseidonCodeHash": "0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864",
|
||||
"codeSize": 0
|
||||
},
|
||||
"to": {
|
||||
"address": "0x1a258d17bf244c4df02d40343a7626a9d321e105",
|
||||
"nonce": 1,
|
||||
"balance": "0x30644e72e131a029b85045b68181585d2833e84879b9705b0e1847ce11600000",
|
||||
"keccakCodeHash": "0x31f2125c021fb94759cb1993a2f07eae01792311e13f209441ff8969cf1eb835",
|
||||
"poseidonCodeHash": "0x1cafbbe8f01ed4c292d9a27be523919a274441a076b20c7d713d192dbe6485c2",
|
||||
"codeSize": 2151
|
||||
},
|
||||
"accountAfter": [
|
||||
{
|
||||
"address": "0x478cdd110520a8e733e2acf9e543d2c687ea5239",
|
||||
"nonce": 11,
|
||||
"balance": "0x0",
|
||||
"keccakCodeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
|
||||
"poseidonCodeHash": "0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864",
|
||||
"codeSize": 0
|
||||
},
|
||||
{
|
||||
"address": "0x1a258d17bf244c4df02d40343a7626a9d321e105",
|
||||
"nonce": 1,
|
||||
"balance": "0x30644e72e131a029b85045b68181585d2833e84879b9705b0e1847ce11600000",
|
||||
"keccakCodeHash": "0x31f2125c021fb94759cb1993a2f07eae01792311e13f209441ff8969cf1eb835",
|
||||
"poseidonCodeHash": "0x1cafbbe8f01ed4c292d9a27be523919a274441a076b20c7d713d192dbe6485c2",
|
||||
"codeSize": 2151
|
||||
},
|
||||
{
|
||||
"address": "0x5300000000000000000000000000000000000005",
|
||||
"nonce": 0,
|
||||
"balance": "0x2aa86921dcd2c0",
|
||||
"keccakCodeHash": "0x256e306f068f0847c8aab5819879b2ff45c021ce2e2f428be51be663415b1d60",
|
||||
"poseidonCodeHash": "0x2c49d7de76e39008575f2f090bb3e90912bad475ea8102c8565c249a75575df5",
|
||||
"codeSize": 1652
|
||||
}
|
||||
],
|
||||
"poseidonCodeHash": "0x1cafbbe8f01ed4c292d9a27be523919a274441a076b20c7d713d192dbe6485c2",
|
||||
"byteCode": "0x60806040526004361061004e5760003560e01c80633659cfe6146100655780634f1ef286146100855780635c60da1b146100985780638f283970146100c9578063f851a440146100e95761005d565b3661005d5761005b6100fe565b005b61005b6100fe565b34801561007157600080fd5b5061005b6100803660046106f1565b610118565b61005b61009336600461070c565b61015f565b3480156100a457600080fd5b506100ad6101d0565b6040516001600160a01b03909116815260200160405180910390f35b3480156100d557600080fd5b5061005b6100e43660046106f1565b61020b565b3480156100f557600080fd5b506100ad610235565b61010661029b565b61011661011161033a565b610344565b565b610120610368565b6001600160a01b0316336001600160a01b03161415610157576101548160405180602001604052806000815250600061039b565b50565b6101546100fe565b610167610368565b6001600160a01b0316336001600160a01b031614156101c8576101c38383838080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506001925061039b915050565b505050565b6101c36100fe565b60006101da610368565b6001600160a01b0316336001600160a01b03161415610200576101fb61033a565b905090565b6102086100fe565b90565b610213610368565b6001600160a01b0316336001600160a01b0316141561015757610154816103c6565b600061023f610368565b6001600160a01b0316336001600160a01b03161415610200576101fb610368565b6060610285838360405180606001604052806027815260200161080b6027913961041a565b9392505050565b6001600160a01b03163b151590565b6102a3610368565b6001600160a01b0316336001600160a01b031614156101165760405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b60006101fb6104f7565b3660008037600080366000845af43d6000803e808015610363573d6000f35b3d6000fd5b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316919050565b6103a48361051f565b6000825111806103b15750805b156101c3576103c08383610260565b50505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6103ef610368565b604080516001600160a01b03928316815291841660208301520160405180910390a16101548161055f565b60606001600160a01b0384163b6104825760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610331565b600080856001600160a01b03168560405161049d91906107bb565b600060405180830381855af49150503d80600081146104d8576040519150601f19603f3d011682016040523d82523d6000602084013e6104dd565b606091505b50915091506104ed828286610608565b9695505050505050565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc61038c565b61052881610641565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6001600160a01b0381166105c45760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b6064820152608401610331565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b80546001600160a01b0319166001600160a01b039290921691909117905550565b60608315610617575081610285565b8251156106275782518084602001fd5b8160405162461bcd60e51b815260040161033191906107d7565b6001600160a01b0381163b6106ae5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401610331565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc6105e7565b80356001600160a01b03811681146106ec57600080fd5b919050565b60006020828403121561070357600080fd5b610285826106d5565b60008060006040848603121561072157600080fd5b61072a846106d5565b9250602084013567ffffffffffffffff8082111561074757600080fd5b818601915086601f83011261075b57600080fd5b81358181111561076a57600080fd5b87602082850101111561077c57600080fd5b6020830194508093505050509250925092565b60005b838110156107aa578181015183820152602001610792565b838111156103c05750506000910152565b600082516107cd81846020870161078f565b9190910192915050565b60208152600082518060208401526107f681604085016020870161078f565b601f01601f1916919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220366737524a7ac8fa76e3b2cd04bb1e0b8aa75e165c32f59b0076ead59d529de564736f6c634300080a0033",
|
||||
"structLogs": [
|
||||
{
|
||||
"pc": 0,
|
||||
"op": "PUSH1",
|
||||
"gas": 320,
|
||||
"gasCost": 3,
|
||||
"depth": 1
|
||||
},
|
||||
{
|
||||
"pc": 2,
|
||||
"op": "PUSH1",
|
||||
"gas": 317,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x80"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 4,
|
||||
"op": "MSTORE",
|
||||
"gas": 314,
|
||||
"gasCost": 12,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x80",
|
||||
"0x40"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 5,
|
||||
"op": "PUSH1",
|
||||
"gas": 302,
|
||||
"gasCost": 3,
|
||||
"depth": 1
|
||||
},
|
||||
{
|
||||
"pc": 7,
|
||||
"op": "CALLDATASIZE",
|
||||
"gas": 299,
|
||||
"gasCost": 2,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x4"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 8,
|
||||
"op": "LT",
|
||||
"gas": 297,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x4",
|
||||
"0x184"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 9,
|
||||
"op": "PUSH2",
|
||||
"gas": 294,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x0"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 12,
|
||||
"op": "JUMPI",
|
||||
"gas": 291,
|
||||
"gasCost": 10,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x0",
|
||||
"0x4e"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 13,
|
||||
"op": "PUSH1",
|
||||
"gas": 281,
|
||||
"gasCost": 3,
|
||||
"depth": 1
|
||||
},
|
||||
{
|
||||
"pc": 15,
|
||||
"op": "CALLDATALOAD",
|
||||
"gas": 278,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x0"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 16,
|
||||
"op": "PUSH1",
|
||||
"gas": 275,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e000000000000000000000000ea08a65b1829af779261e768d609e592"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 18,
|
||||
"op": "SHR",
|
||||
"gas": 272,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e000000000000000000000000ea08a65b1829af779261e768d609e592",
|
||||
"0xe0"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 19,
|
||||
"op": "DUP1",
|
||||
"gas": 269,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 20,
|
||||
"op": "PUSH4",
|
||||
"gas": 266,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x8ef1332e"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 25,
|
||||
"op": "EQ",
|
||||
"gas": 263,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x8ef1332e",
|
||||
"0x3659cfe6"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 26,
|
||||
"op": "PUSH2",
|
||||
"gas": 260,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x0"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 29,
|
||||
"op": "JUMPI",
|
||||
"gas": 257,
|
||||
"gasCost": 10,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x0",
|
||||
"0x65"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 30,
|
||||
"op": "DUP1",
|
||||
"gas": 247,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 31,
|
||||
"op": "PUSH4",
|
||||
"gas": 244,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x8ef1332e"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 36,
|
||||
"op": "EQ",
|
||||
"gas": 241,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x8ef1332e",
|
||||
"0x4f1ef286"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 37,
|
||||
"op": "PUSH2",
|
||||
"gas": 238,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x0"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 40,
|
||||
"op": "JUMPI",
|
||||
"gas": 235,
|
||||
"gasCost": 10,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x0",
|
||||
"0x85"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 41,
|
||||
"op": "DUP1",
|
||||
"gas": 225,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 42,
|
||||
"op": "PUSH4",
|
||||
"gas": 222,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x8ef1332e"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 47,
|
||||
"op": "EQ",
|
||||
"gas": 219,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x8ef1332e",
|
||||
"0x5c60da1b"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 48,
|
||||
"op": "PUSH2",
|
||||
"gas": 216,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x0"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 51,
|
||||
"op": "JUMPI",
|
||||
"gas": 213,
|
||||
"gasCost": 10,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x0",
|
||||
"0x98"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 52,
|
||||
"op": "DUP1",
|
||||
"gas": 203,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 53,
|
||||
"op": "PUSH4",
|
||||
"gas": 200,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x8ef1332e"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 58,
|
||||
"op": "EQ",
|
||||
"gas": 197,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x8ef1332e",
|
||||
"0x8f283970"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 59,
|
||||
"op": "PUSH2",
|
||||
"gas": 194,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x0"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 62,
|
||||
"op": "JUMPI",
|
||||
"gas": 191,
|
||||
"gasCost": 10,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x0",
|
||||
"0xc9"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 63,
|
||||
"op": "DUP1",
|
||||
"gas": 181,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 64,
|
||||
"op": "PUSH4",
|
||||
"gas": 178,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x8ef1332e"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 69,
|
||||
"op": "EQ",
|
||||
"gas": 175,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x8ef1332e",
|
||||
"0xf851a440"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 70,
|
||||
"op": "PUSH2",
|
||||
"gas": 172,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x0"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 73,
|
||||
"op": "JUMPI",
|
||||
"gas": 169,
|
||||
"gasCost": 10,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x0",
|
||||
"0xe9"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 74,
|
||||
"op": "PUSH2",
|
||||
"gas": 159,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 77,
|
||||
"op": "JUMP",
|
||||
"gas": 156,
|
||||
"gasCost": 8,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x5d"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 93,
|
||||
"op": "JUMPDEST",
|
||||
"gas": 148,
|
||||
"gasCost": 1,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 94,
|
||||
"op": "PUSH2",
|
||||
"gas": 147,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 97,
|
||||
"op": "PUSH2",
|
||||
"gas": 144,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x5b"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 100,
|
||||
"op": "JUMP",
|
||||
"gas": 141,
|
||||
"gasCost": 8,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x5b",
|
||||
"0xfe"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 254,
|
||||
"op": "JUMPDEST",
|
||||
"gas": 133,
|
||||
"gasCost": 1,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x5b"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 255,
|
||||
"op": "PUSH2",
|
||||
"gas": 132,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x5b"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 258,
|
||||
"op": "PUSH2",
|
||||
"gas": 129,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x5b",
|
||||
"0x106"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 261,
|
||||
"op": "JUMP",
|
||||
"gas": 126,
|
||||
"gasCost": 8,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x5b",
|
||||
"0x106",
|
||||
"0x29b"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 667,
|
||||
"op": "JUMPDEST",
|
||||
"gas": 118,
|
||||
"gasCost": 1,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x5b",
|
||||
"0x106"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 668,
|
||||
"op": "PUSH2",
|
||||
"gas": 117,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x5b",
|
||||
"0x106"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 671,
|
||||
"op": "PUSH2",
|
||||
"gas": 114,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x5b",
|
||||
"0x106",
|
||||
"0x2a3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 674,
|
||||
"op": "JUMP",
|
||||
"gas": 111,
|
||||
"gasCost": 8,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x5b",
|
||||
"0x106",
|
||||
"0x2a3",
|
||||
"0x368"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 872,
|
||||
"op": "JUMPDEST",
|
||||
"gas": 103,
|
||||
"gasCost": 1,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x5b",
|
||||
"0x106",
|
||||
"0x2a3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 873,
|
||||
"op": "PUSH1",
|
||||
"gas": 102,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x5b",
|
||||
"0x106",
|
||||
"0x2a3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 875,
|
||||
"op": "PUSH32",
|
||||
"gas": 99,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x5b",
|
||||
"0x106",
|
||||
"0x2a3",
|
||||
"0x0"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 908,
|
||||
"op": "JUMPDEST",
|
||||
"gas": 96,
|
||||
"gasCost": 1,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x5b",
|
||||
"0x106",
|
||||
"0x2a3",
|
||||
"0x0",
|
||||
"0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 909,
|
||||
"op": "SLOAD",
|
||||
"gas": 95,
|
||||
"gasCost": 2100,
|
||||
"depth": 1,
|
||||
"error": "out of gas",
|
||||
"stack": [
|
||||
"0x8ef1332e",
|
||||
"0x5b",
|
||||
"0x106",
|
||||
"0x2a3",
|
||||
"0x0",
|
||||
"0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103"
|
||||
],
|
||||
"storage": {
|
||||
"0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x0000000000000000000000008eebfef33eb00149852cadb631838ad9bfcce848"
|
||||
},
|
||||
"extraData": {
|
||||
"proofList": [
|
||||
{
|
||||
"address": "0x1a258d17bf244c4df02d40343a7626a9d321e105",
|
||||
"nonce": 1,
|
||||
"balance": "0x30644e72e131a029b85045b68181585d2833e84879b9705b0e1847ce11600000",
|
||||
"keccakCodeHash": "0x31f2125c021fb94759cb1993a2f07eae01792311e13f209441ff8969cf1eb835",
|
||||
"poseidonCodeHash": "0x1cafbbe8f01ed4c292d9a27be523919a274441a076b20c7d713d192dbe6485c2",
|
||||
"codeSize": 2151,
|
||||
"storage": {
|
||||
"key": "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103",
|
||||
"value": "0x0000000000000000000000008eebfef33eb00149852cadb631838ad9bfcce848"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"withdraw_trie_root": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
3710
common/testdata/blockTrace_05.json
vendored
3710
common/testdata/blockTrace_05.json
vendored
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user