mirror of
https://github.com/scroll-tech/scroll.git
synced 2026-01-11 23:18:07 -05:00
Compare commits
15 Commits
v4.4.4
...
4844-local
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6c32ee636 | ||
|
|
c1afa61da8 | ||
|
|
cccec61b7e | ||
|
|
c3e8a3ffa6 | ||
|
|
722b77c269 | ||
|
|
800f3c2674 | ||
|
|
055b70ebc9 | ||
|
|
7764f3ae7a | ||
|
|
25f32b5967 | ||
|
|
3bb7c63621 | ||
|
|
febec99f79 | ||
|
|
cdb061aa0c | ||
|
|
45de8e491c | ||
|
|
6490b70893 | ||
|
|
b876b5649d |
2
.github/workflows/common.yml
vendored
2
.github/workflows/common.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly-2023-12-03
|
||||
toolchain: nightly-2022-12-10
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
- name: Install Go
|
||||
|
||||
8
.github/workflows/coordinator.yml
vendored
8
.github/workflows/coordinator.yml
vendored
@@ -33,13 +33,13 @@ jobs:
|
||||
steps:
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly-2023-12-03
|
||||
toolchain: nightly-2022-12-10
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.21.x
|
||||
go-version: 1.20.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Lint
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.21.x
|
||||
go-version: 1.20.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install goimports
|
||||
@@ -95,7 +95,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.21.x
|
||||
go-version: 1.20.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install Solc
|
||||
|
||||
52
.github/workflows/docker.yml
vendored
52
.github/workflows/docker.yml
vendored
@@ -46,7 +46,6 @@ jobs:
|
||||
with:
|
||||
context: .
|
||||
file: ./build/dockerfiles/event_watcher.Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.REPOSITORY }}:${{ env.IMAGE_TAG }}
|
||||
@@ -91,7 +90,6 @@ jobs:
|
||||
with:
|
||||
context: .
|
||||
file: ./build/dockerfiles/gas_oracle.Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.REPOSITORY }}:${{ env.IMAGE_TAG }}
|
||||
@@ -136,52 +134,6 @@ jobs:
|
||||
with:
|
||||
context: .
|
||||
file: ./build/dockerfiles/rollup_relayer.Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.REPOSITORY }}:${{ env.IMAGE_TAG }}
|
||||
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.REPOSITORY }}:latest
|
||||
${{ env.ECR_REGISTRY }}/${{ env.REPOSITORY }}:${{ env.IMAGE_TAG }}
|
||||
${{ env.ECR_REGISTRY }}/${{ env.REPOSITORY }}:latest
|
||||
|
||||
rollup-db-cli:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@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: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ env.AWS_REGION }}
|
||||
- name: Login to Amazon ECR
|
||||
id: login-ecr
|
||||
uses: aws-actions/amazon-ecr-login@v2
|
||||
- name: check repo and create it if not exist
|
||||
env:
|
||||
REPOSITORY: rollup-db-cli
|
||||
run: |
|
||||
aws --region ${{ env.AWS_REGION }} ecr describe-repositories --repository-names ${{ env.REPOSITORY }} && : || aws --region ${{ env.AWS_REGION }} ecr create-repository --repository-name ${{ env.REPOSITORY }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v3
|
||||
env:
|
||||
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
|
||||
REPOSITORY: rollup-db-cli
|
||||
IMAGE_TAG: ${{ github.ref_name }}
|
||||
with:
|
||||
context: .
|
||||
file: ./build/dockerfiles/db_cli.Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.REPOSITORY }}:${{ env.IMAGE_TAG }}
|
||||
@@ -226,7 +178,6 @@ jobs:
|
||||
with:
|
||||
context: .
|
||||
file: ./build/dockerfiles/bridgehistoryapi-fetcher.Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.REPOSITORY }}:${{ env.IMAGE_TAG }}
|
||||
@@ -271,7 +222,6 @@ jobs:
|
||||
with:
|
||||
context: .
|
||||
file: ./build/dockerfiles/bridgehistoryapi-api.Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.REPOSITORY }}:${{ env.IMAGE_TAG }}
|
||||
@@ -316,7 +266,6 @@ jobs:
|
||||
with:
|
||||
context: .
|
||||
file: ./build/dockerfiles/coordinator-api.Dockerfile
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
tags: |
|
||||
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.REPOSITORY }}:${{ env.IMAGE_TAG }}
|
||||
@@ -361,7 +310,6 @@ jobs:
|
||||
with:
|
||||
context: .
|
||||
file: ./build/dockerfiles/coordinator-cron.Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.REPOSITORY }}:${{ env.IMAGE_TAG }}
|
||||
|
||||
216
.github/workflows/intermediate-docker.yml
vendored
216
.github/workflows/intermediate-docker.yml
vendored
@@ -7,12 +7,12 @@ on:
|
||||
description: 'Go version'
|
||||
required: true
|
||||
type: string
|
||||
default: '1.21'
|
||||
default: '1.20'
|
||||
RUST_VERSION:
|
||||
description: 'Rust toolchain version'
|
||||
required: true
|
||||
type: string
|
||||
default: 'nightly-2023-12-03'
|
||||
default: 'nightly-2022-12-10'
|
||||
PYTHON_VERSION:
|
||||
description: 'Python version'
|
||||
required: false
|
||||
@@ -29,191 +29,31 @@ defaults:
|
||||
working-directory: 'build/dockerfiles/intermediate'
|
||||
|
||||
jobs:
|
||||
build-and-publish-cuda-go-rust-builder:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@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 image
|
||||
id: build
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
file: build/dockerfiles/intermediate/cuda-go-rust-builder.Dockerfile
|
||||
tags: scrolltech/cuda-go-rust-builder:cuda-${{ github.event.inputs.CUDA_VERSION }}-go-${{ github.event.inputs.GO_VERSION }}-rust-${{ github.event.inputs.RUST_VERSION }}
|
||||
build-args: |
|
||||
CUDA_VERSION: ${{ github.event.inputs.CUDA_VERSION }}
|
||||
GO_VERSION: ${{ github.event.inputs.GO_VERSION }}
|
||||
RUST_VERSION: ${{ github.event.inputs.RUST_VERSION }}
|
||||
|
||||
build-and-publish-go-rust-builder:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@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 image
|
||||
id: build
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
file: build/dockerfiles/intermediate/go-rust-builder.Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: scrolltech/go-rust-builder:go-${{ github.event.inputs.GO_VERSION }}-rust-${{ github.event.inputs.RUST_VERSION }}
|
||||
build-args: |
|
||||
GO_VERSION: ${{ github.event.inputs.GO_VERSION }}
|
||||
RUST_VERSION: ${{ github.event.inputs.RUST_VERSION }}
|
||||
|
||||
build-and-publish-go-alpine-builder:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@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 image
|
||||
id: build
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
file: build/dockerfiles/intermediate/go-alpine-builder.Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: scrolltech/go-alpine-builder:${{ github.event.inputs.GO_VERSION }}
|
||||
build-args: |
|
||||
GO_VERSION: ${{ github.event.inputs.GO_VERSION }}
|
||||
RUST_VERSION: ${{ github.event.inputs.RUST_VERSION }}
|
||||
|
||||
build-and-publish-rust-builder:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@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 image
|
||||
id: build
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
file: build/dockerfiles/intermediate/rust-builder.Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: scrolltech/rust-builder:${{ github.event.inputs.RUST_VERSION }}
|
||||
build-args: |
|
||||
RUST_VERSION: ${{ github.event.inputs.RUST_VERSION }}
|
||||
|
||||
build-and-publish-rust-alpine-builder:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@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 image
|
||||
id: build
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
file: build/dockerfiles/intermediate/rust-alpine-builder.Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: scrolltech/rust-alpine-builder:${{ github.event.inputs.RUST_VERSION }}
|
||||
build-args: |
|
||||
RUST_VERSION: ${{ github.event.inputs.RUST_VERSION }}
|
||||
|
||||
build-and-publish-go-rust-alpine-builder:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@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 image
|
||||
id: build
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
file: build/dockerfiles/intermediate/go-rust-alpine-builder.Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: scrolltech/go-rust-alpine-builder:go-${{ github.event.inputs.GO_VERSION }}-rust-${{ github.event.inputs.RUST_VERSION }}
|
||||
build-args: |
|
||||
GO_VERSION: ${{ github.event.inputs.GO_VERSION }}
|
||||
RUST_VERSION: ${{ github.event.inputs.RUST_VERSION }}
|
||||
|
||||
build-and-publish-py-runner:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@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 image
|
||||
id: build
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
file: build/dockerfiles/intermediate/py-runner.Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: scrolltech/py-runner:${{ github.event.inputs.PYTHON_VERSION }}
|
||||
build-args: |
|
||||
CUDA_VERSION: ${{ github.event.inputs.CUDA_VERSION }}
|
||||
GO_VERSION: ${{ github.event.inputs.GO_VERSION }}
|
||||
RUST_VERSION: ${{ github.event.inputs.RUST_VERSION }}
|
||||
|
||||
- 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 }}
|
||||
|
||||
2
.github/workflows/prover.yml
vendored
2
.github/workflows/prover.yml
vendored
@@ -50,7 +50,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly-2023-12-03
|
||||
toolchain: nightly-2022-12-10
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
- name: Install Go
|
||||
|
||||
8
Makefile
8
Makefile
@@ -1,6 +1,6 @@
|
||||
.PHONY: fmt dev_docker build_test_docker run_test_docker clean update
|
||||
|
||||
L2GETH_TAG=scroll-v5.3.0
|
||||
L2GETH_TAG=scroll-v5.1.6
|
||||
|
||||
help: ## Display this help message
|
||||
@grep -h \
|
||||
@@ -47,5 +47,11 @@ dev_docker: ## build docker images for development/testing usages
|
||||
docker build -t scroll_l1geth ./common/docker/l1geth/
|
||||
docker build -t scroll_l2geth ./common/docker/l2geth/
|
||||
|
||||
build_test_docker: ## build Docker image for local testing on M1/M2 Silicon Mac
|
||||
docker build -t scroll_test_image -f ./build/dockerfiles/local_testing.Dockerfile $$(mktemp -d)
|
||||
|
||||
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 -e HOST_PATH=$(PWD) scroll_test_image
|
||||
|
||||
clean: ## Empty out the bin folder
|
||||
@rm -rf build/bin
|
||||
|
||||
35
README.md
35
README.md
@@ -43,6 +43,8 @@ make dev_docker
|
||||
|
||||
## Testing Rollup & Coordinator
|
||||
|
||||
### For Non-Apple Silicon (M1/M2) Macs
|
||||
|
||||
Run the tests using the following commands:
|
||||
|
||||
```bash
|
||||
@@ -52,6 +54,39 @@ go test -v -race -covermode=atomic scroll-tech/database/...
|
||||
go test -v -race -covermode=atomic scroll-tech/common/...
|
||||
```
|
||||
|
||||
### For Apple Silicon (M1/M2) Macs
|
||||
|
||||
To run tests on Apple Silicon Macs, build and execute the Docker image as outlined below:
|
||||
|
||||
#### Build a Docker Image for Testing
|
||||
|
||||
Use the following command to build a Docker image:
|
||||
|
||||
```bash
|
||||
make build_test_docker
|
||||
```
|
||||
|
||||
This command builds a Docker image named `scroll_test_image` using the Dockerfile found at `./build/dockerfiles/local_test.Dockerfile`.
|
||||
|
||||
#### Run Docker Image
|
||||
|
||||
After the image is built, run a Docker container from it:
|
||||
|
||||
```bash
|
||||
make run_test_docker
|
||||
```
|
||||
|
||||
This command runs a Docker container named `scroll_test_container` from the `scroll_test_image` image. The container uses the host network and has access to the Docker socket and the current directory.
|
||||
|
||||
Once the Docker container is running, execute the tests using the following commands:
|
||||
|
||||
```bash
|
||||
go test -v -race -covermode=atomic scroll-tech/rollup/...
|
||||
go test -tags="mock_verifier" -v -race -covermode=atomic scroll-tech/coordinator/...
|
||||
go test -v -race -covermode=atomic scroll-tech/database/...
|
||||
go test -v -race -covermode=atomic scroll-tech/common/...
|
||||
```
|
||||
|
||||
## Testing Contracts
|
||||
|
||||
You can find the unit tests in [`contracts/src/test/`](/contracts/src/test/), and integration tests in [`contracts/integration-test/`](/contracts/integration-test/).
|
||||
|
||||
@@ -79,50 +79,3 @@ provides REST APIs. Please refer to the API details below.
|
||||
// @Success 200
|
||||
// @Router /api/txsbyhashes [post]
|
||||
```
|
||||
|
||||
## Running bridge-history-api locally
|
||||
|
||||
1. Pull the latest Redis image:
|
||||
```
|
||||
docker pull redis:latest
|
||||
```
|
||||
|
||||
2. Run the Redis container:
|
||||
```
|
||||
docker run --name bridgehistoryapi-redis -d -p 6379:6379 redis:latest
|
||||
```
|
||||
|
||||
3. Pull the latest PostgreSQL image:
|
||||
```
|
||||
docker pull postgres:latest
|
||||
```
|
||||
|
||||
4. Run the PostgreSQL container:
|
||||
```
|
||||
docker run --name bridgehistoryapi-history-db -p 5444:5432 -e POSTGRES_PASSWORD=123456 -e POSTGRES_DB=test -d postgres
|
||||
```
|
||||
|
||||
5. Run database migrations to initialize the tables:
|
||||
```
|
||||
make bridgehistoryapi-db-cli
|
||||
./build/bin/bridgehistoryapi-db-cli migrate
|
||||
```
|
||||
|
||||
6. Run bridgehistoryapi-fetcher:
|
||||
```
|
||||
make bridgehistoryapi-fetcher
|
||||
./build/bin/bridgehistoryapi-fetcher
|
||||
```
|
||||
|
||||
7. Run bridgehistoryapi-api:
|
||||
```
|
||||
make bridgehistoryapi-api
|
||||
./build/bin/bridgehistoryapi-api
|
||||
```
|
||||
|
||||
The endpoints provided in [./conf/config.json](./conf/config.json) are all public endpoints and have rate limits.
|
||||
|
||||
For production usage:
|
||||
|
||||
- For L1 endpoints, utilizing a service provider's free tier should suffice.
|
||||
- For L2 endpoints, consider [running a Scroll L2geth node](https://docs.scroll.xyz/en/developers/guides/running-a-scroll-node) and using the exposed HTTP port.
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
"USDCGatewayAddr": "0xf1AF3b23DE0A5Ca3CAb7261cb0061C0D779A5c7B",
|
||||
"LIDOGatewayAddr": "0x6625C6332c9F91F2D27c304E729B86db87A3f504",
|
||||
"DAIGatewayAddr": "0x67260A8B73C5B77B55c1805218A42A7A6F98F515",
|
||||
"PufferGatewayAddr": "0xA033Ff09f2da45f0e9ae495f525363722Df42b2a",
|
||||
"ScrollChainAddr": "0xa13BAF47339d63B743e7Da8741db5456DAc1E556",
|
||||
"GatewayRouterAddr": "0xF8B1378579659D8F7EE5f3C929c2f3E332E41Fd6",
|
||||
"MessageQueueAddr": "0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B"
|
||||
@@ -35,9 +34,7 @@
|
||||
"USDCGatewayAddr": "0x33B60d5Dd260d453cAC3782b0bDC01ce84672142",
|
||||
"LIDOGatewayAddr": "0x8aE8f22226B9d789A36AC81474e633f8bE2856c9",
|
||||
"DAIGatewayAddr": "0xaC78dff3A87b5b534e366A93E785a0ce8fA6Cc62",
|
||||
"PufferGatewayAddr": "0x9eBf2f33526CD571f8b2ad312492cb650870CFd6",
|
||||
"GatewayRouterAddr": "0x4C0926FF5252A435FD19e10ED15e5a249Ba19d79",
|
||||
"MessageQueueAddr": "0x5300000000000000000000000000000000000000"
|
||||
"GatewayRouterAddr": "0x4C0926FF5252A435FD19e10ED15e5a249Ba19d79"
|
||||
},
|
||||
"db": {
|
||||
"dsn": "postgres://postgres:123456@localhost:5444/test?sslmode=disable",
|
||||
|
||||
@@ -8,7 +8,7 @@ require (
|
||||
github.com/go-redis/redis/v8 v8.11.5
|
||||
github.com/pressly/goose/v3 v3.16.0
|
||||
github.com/prometheus/client_golang v1.16.0
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20240326144132-0f0cd99f7a2e
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20240314095130-4553f5f26935
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/urfave/cli/v2 v2.25.7
|
||||
golang.org/x/sync v0.6.0
|
||||
@@ -60,7 +60,6 @@ require (
|
||||
github.com/holiman/uint256 v1.2.4 // indirect
|
||||
github.com/huin/goupnp v1.3.0 // indirect
|
||||
github.com/iden3/go-iden3-crypto v0.0.15 // indirect
|
||||
github.com/jackc/pgx/v5 v5.5.4 // indirect
|
||||
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
|
||||
@@ -184,8 +184,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.5.4 h1:Xp2aQS8uXButQdnCMWNmvx6UysWQQC+u1EoizjguY+8=
|
||||
github.com/jackc/pgx/v5 v5.5.4/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
||||
github.com/jackc/pgx/v5 v5.5.0 h1:NxstgwndsTRy7eq9/kqYc/BZh5w2hHJV86wjvO+1xPw=
|
||||
github.com/jackc/pgx/v5 v5.5.0/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA=
|
||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
|
||||
@@ -311,8 +311,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.20240326144132-0f0cd99f7a2e h1:FcoK0rykAWI+5E7cQM6ALRLd5CmjBTHRvJztRBH2xeM=
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20240326144132-0f0cd99f7a2e/go.mod h1:7Rz2bh9pn42rGuxjh51CG7HL9SKMG3ZugJkL3emdZx8=
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20240314095130-4553f5f26935 h1:bHBt6sillaT4o/9RjxkVX8pWwvEmu37uWBw4XbCjfzY=
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20240314095130-4553f5f26935/go.mod h1:7Rz2bh9pn42rGuxjh51CG7HL9SKMG3ZugJkL3emdZx8=
|
||||
github.com/scroll-tech/zktrie v0.7.1 h1:NrmZNjuBzsbrKePqdHDG+t2cXnimbtezPAFS0+L9ElE=
|
||||
github.com/scroll-tech/zktrie v0.7.1/go.mod h1:XvNo7vAk8yxNyTjBDj5WIiFzYW4bx/gJ78+NK6Zn6Uk=
|
||||
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
|
||||
|
||||
@@ -23,7 +23,6 @@ type FetcherConfig struct {
|
||||
DAIGatewayAddr string `json:"DAIGatewayAddr"`
|
||||
USDCGatewayAddr string `json:"USDCGatewayAddr"`
|
||||
LIDOGatewayAddr string `json:"LIDOGatewayAddr"`
|
||||
PufferGatewayAddr string `json:"PufferGatewayAddr"`
|
||||
ERC721GatewayAddr string `json:"ERC721GatewayAddr"`
|
||||
ERC1155GatewayAddr string `json:"ERC1155GatewayAddr"`
|
||||
ScrollChainAddr string `json:"ScrollChainAddr"`
|
||||
|
||||
@@ -93,11 +93,6 @@ func NewL1FetcherLogic(cfg *config.FetcherConfig, db *gorm.DB, client *ethclient
|
||||
gatewayList = append(gatewayList, common.HexToAddress(cfg.LIDOGatewayAddr))
|
||||
}
|
||||
|
||||
if common.HexToAddress(cfg.PufferGatewayAddr) != (common.Address{}) {
|
||||
addressList = append(addressList, common.HexToAddress(cfg.PufferGatewayAddr))
|
||||
gatewayList = append(gatewayList, common.HexToAddress(cfg.PufferGatewayAddr))
|
||||
}
|
||||
|
||||
log.Info("L1 Fetcher configured with the following address list", "addresses", addressList, "gateways", gatewayList)
|
||||
|
||||
f := &L1FetcherLogic{
|
||||
|
||||
@@ -85,12 +85,7 @@ func NewL2FetcherLogic(cfg *config.FetcherConfig, db *gorm.DB, client *ethclient
|
||||
|
||||
if common.HexToAddress(cfg.LIDOGatewayAddr) != (common.Address{}) {
|
||||
addressList = append(addressList, common.HexToAddress(cfg.LIDOGatewayAddr))
|
||||
gatewayList = append(gatewayList, common.HexToAddress(cfg.LIDOGatewayAddr))
|
||||
}
|
||||
|
||||
if common.HexToAddress(cfg.PufferGatewayAddr) != (common.Address{}) {
|
||||
addressList = append(addressList, common.HexToAddress(cfg.PufferGatewayAddr))
|
||||
gatewayList = append(gatewayList, common.HexToAddress(cfg.PufferGatewayAddr))
|
||||
gatewayList = append(gatewayList, common.HexToAddress(cfg.USDCGatewayAddr))
|
||||
}
|
||||
|
||||
log.Info("L2 Fetcher configured with the following address list", "addresses", addressList, "gateways", gatewayList)
|
||||
|
||||
@@ -104,12 +104,10 @@ linters-settings:
|
||||
# minimal occurrences count to trigger, 3 by default
|
||||
min-occurrences: 3
|
||||
depguard:
|
||||
rules:
|
||||
main:
|
||||
files:
|
||||
- $all
|
||||
deny:
|
||||
- pkg: "github.com/davecgh/go-spew/spew"
|
||||
list-type: blacklist
|
||||
include-go-root: false
|
||||
packages:
|
||||
- github.com/davecgh/go-spew/spew
|
||||
misspell:
|
||||
# Correct spellings using locale preferences for US or UK.
|
||||
# Default is to use a neutral variety of English.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Download Go dependencies
|
||||
FROM golang:1.21-alpine3.19 as base
|
||||
FROM golang:1.20-alpine3.16 as base
|
||||
|
||||
WORKDIR /src
|
||||
COPY go.mod* ./
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Download Go dependencies
|
||||
FROM golang:1.21-alpine3.19 as base
|
||||
FROM golang:1.20-alpine3.16 as base
|
||||
|
||||
WORKDIR /src
|
||||
COPY ./bridge-history-api/go.* ./
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Download Go dependencies
|
||||
FROM golang:1.21-alpine3.19 as base
|
||||
FROM golang:1.20-alpine3.16 as base
|
||||
|
||||
WORKDIR /src
|
||||
COPY go.mod* ./
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Build libzkp dependency
|
||||
FROM scrolltech/go-rust-builder:go-1.21-rust-nightly-2023-12-03 as chef
|
||||
FROM scrolltech/go-rust-builder:go-1.20-rust-nightly-2022-12-10 as chef
|
||||
WORKDIR app
|
||||
|
||||
FROM chef as planner
|
||||
@@ -17,7 +17,7 @@ RUN find ./ | grep libzktrie.so | xargs -I{} cp {} /app/target/release/
|
||||
|
||||
|
||||
# Download Go dependencies
|
||||
FROM scrolltech/go-rust-builder:go-1.21-rust-nightly-2023-12-03 as base
|
||||
FROM scrolltech/go-rust-builder:go-1.20-rust-nightly-2022-12-10 as base
|
||||
WORKDIR /src
|
||||
COPY go.work* ./
|
||||
COPY ./rollup/go.* ./rollup/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Download Go dependencies
|
||||
FROM scrolltech/go-alpine-builder:1.21 as base
|
||||
FROM scrolltech/go-alpine-builder:1.20 as base
|
||||
|
||||
WORKDIR /src
|
||||
COPY go.work* ./
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Download Go dependencies
|
||||
FROM scrolltech/go-alpine-builder:1.21 as base
|
||||
FROM scrolltech/go-alpine-builder:1.20 as base
|
||||
|
||||
WORKDIR /src
|
||||
COPY go.work* ./
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Download Go dependencies
|
||||
FROM scrolltech/go-alpine-builder:1.21 as base
|
||||
FROM scrolltech/go-alpine-builder:1.20 as base
|
||||
|
||||
WORKDIR /src
|
||||
COPY go.work* ./
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Download Go dependencies
|
||||
FROM scrolltech/go-alpine-builder:1.21 as base
|
||||
FROM scrolltech/go-alpine-builder:1.20 as base
|
||||
|
||||
WORKDIR /src
|
||||
COPY go.work* ./
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
ifeq ($(GO_VERSION),)
|
||||
GO_VERSION=1.21
|
||||
GO_VERSION=1.20
|
||||
endif
|
||||
ifeq ($(RUST_VERSION),)
|
||||
RUST_VERSION=nightly-2023-12-03
|
||||
RUST_VERSION=nightly-2022-12-10
|
||||
endif
|
||||
ifeq ($(PYTHON_VERSION),)
|
||||
PYTHON_VERSION=3.10
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
ARG CUDA_VERSION=11.7.1
|
||||
ARG GO_VERSION=1.21
|
||||
ARG RUST_VERSION=nightly-2023-12-03
|
||||
ARG GO_VERSION=1.20
|
||||
ARG RUST_VERSION=nightly-2022-12-10
|
||||
ARG CARGO_CHEF_TAG=0.1.41
|
||||
|
||||
FROM nvidia/cuda:${CUDA_VERSION}-devel-ubuntu22.04
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
ARG GO_VERSION=1.21
|
||||
ARG GO_VERSION=1.20
|
||||
|
||||
FROM golang:${GO_VERSION}-alpine
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
ARG GO_VERSION=1.21
|
||||
ARG RUST_VERSION=nightly-2023-12-03
|
||||
ARG GO_VERSION=1.20
|
||||
ARG RUST_VERSION=nightly-2022-12-10
|
||||
ARG CARGO_CHEF_TAG=0.1.41
|
||||
|
||||
FROM golang:${GO_VERSION}-alpine
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
ARG GO_VERSION=1.21
|
||||
ARG RUST_VERSION=nightly-2023-12-03
|
||||
ARG GO_VERSION=1.20
|
||||
ARG RUST_VERSION=nightly-2022-12-10
|
||||
ARG CARGO_CHEF_TAG=0.1.41
|
||||
|
||||
FROM ubuntu:20.04
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
ARG ALPINE_VERSION=3.15
|
||||
ARG RUST_VERSION=nightly-2023-12-03
|
||||
ARG RUST_VERSION=nightly-2022-12-10
|
||||
ARG CARGO_CHEF_TAG=0.1.41
|
||||
|
||||
FROM alpine:${ALPINE_VERSION}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
ARG RUST_VERSION=nightly-2023-12-03
|
||||
ARG RUST_VERSION=nightly-2022-12-10
|
||||
ARG CARGO_CHEF_TAG=0.1.41
|
||||
|
||||
FROM ubuntu:20.04
|
||||
|
||||
11
build/dockerfiles/local_testing.Dockerfile
Normal file
11
build/dockerfiles/local_testing.Dockerfile
Normal file
@@ -0,0 +1,11 @@
|
||||
# Start from the latest golang base image
|
||||
FROM golang:1.21
|
||||
|
||||
# Install Docker
|
||||
RUN apt-get update && apt-get install -y docker.io docker-compose
|
||||
|
||||
# Set the working directory
|
||||
WORKDIR /go/src/app
|
||||
|
||||
# This container will be executable
|
||||
ENTRYPOINT [ "/bin/bash" ]
|
||||
@@ -1,5 +1,5 @@
|
||||
# Download Go dependencies
|
||||
FROM scrolltech/go-alpine-builder:1.21 as base
|
||||
FROM scrolltech/go-alpine-builder:1.20 as base
|
||||
|
||||
WORKDIR /src
|
||||
COPY go.work* ./
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
|
||||
const (
|
||||
// GolangCIVersion to be used for linting.
|
||||
GolangCIVersion = "github.com/golangci/golangci-lint/cmd/golangci-lint@v1.57.2"
|
||||
GolangCIVersion = "github.com/golangci/golangci-lint/cmd/golangci-lint@v1.52.2"
|
||||
)
|
||||
|
||||
// GOBIN environment variable.
|
||||
@@ -51,7 +51,7 @@ func lint() {
|
||||
}
|
||||
|
||||
cmd = exec.Command(filepath.Join(goBin(), "golangci-lint"))
|
||||
cmd.Args = append(cmd.Args, "run", "--config", "../build/.golangci.yml", "--timeout", "10m")
|
||||
cmd.Args = append(cmd.Args, "run", "--config", "../build/.golangci.yml")
|
||||
|
||||
if *v {
|
||||
cmd.Args = append(cmd.Args, "-v")
|
||||
|
||||
@@ -1,27 +1,62 @@
|
||||
package database_test
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mattn/go-colorable"
|
||||
"github.com/mattn/go-isatty"
|
||||
"github.com/scroll-tech/go-ethereum/log"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"scroll-tech/common/database"
|
||||
"scroll-tech/common/testcontainers"
|
||||
"scroll-tech/common/docker"
|
||||
"scroll-tech/common/version"
|
||||
)
|
||||
|
||||
func TestGormLogger(t *testing.T) {
|
||||
output := io.Writer(os.Stderr)
|
||||
usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb"
|
||||
if usecolor {
|
||||
output = colorable.NewColorableStderr()
|
||||
}
|
||||
ostream := log.StreamHandler(output, log.TerminalFormat(usecolor))
|
||||
glogger := log.NewGlogHandler(ostream)
|
||||
// Set log level
|
||||
glogger.Verbosity(log.LvlTrace)
|
||||
log.Root().SetHandler(glogger)
|
||||
|
||||
var gl gormLogger
|
||||
gl.gethLogger = log.Root()
|
||||
|
||||
gl.Error(context.Background(), "test %s error:%v", "testError", errors.New("test error"))
|
||||
gl.Warn(context.Background(), "test %s warn:%v", "testWarn", errors.New("test warn"))
|
||||
gl.Info(context.Background(), "test %s warn:%v", "testInfo", errors.New("test info"))
|
||||
gl.Trace(context.Background(), time.Now(), func() (string, int64) { return "test trace", 1 }, nil)
|
||||
}
|
||||
|
||||
func TestDB(t *testing.T) {
|
||||
version.Version = "v4.1.98-aaa-bbb-ccc"
|
||||
base := docker.NewDockerApp()
|
||||
base.RunDBImage(t)
|
||||
|
||||
testApps := testcontainers.NewTestcontainerApps()
|
||||
assert.NoError(t, testApps.StartPostgresContainer())
|
||||
dbCfg := &Config{
|
||||
DSN: base.DBConfig.DSN,
|
||||
DriverName: base.DBConfig.DriverName,
|
||||
MaxOpenNum: base.DBConfig.MaxOpenNum,
|
||||
MaxIdleNum: base.DBConfig.MaxIdleNum,
|
||||
}
|
||||
|
||||
db, err := testApps.GetGormDBClient()
|
||||
var err error
|
||||
db, err := InitDB(dbCfg)
|
||||
assert.NoError(t, err)
|
||||
|
||||
sqlDB, err := database.Ping(db)
|
||||
sqlDB, err := Ping(db)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, sqlDB)
|
||||
|
||||
assert.NoError(t, database.CloseDB(db))
|
||||
assert.NoError(t, CloseDB(db))
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mattn/go-colorable"
|
||||
"github.com/mattn/go-isatty"
|
||||
"github.com/scroll-tech/go-ethereum/log"
|
||||
)
|
||||
|
||||
func TestGormLogger(t *testing.T) {
|
||||
output := io.Writer(os.Stderr)
|
||||
usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb"
|
||||
if usecolor {
|
||||
output = colorable.NewColorableStderr()
|
||||
}
|
||||
ostream := log.StreamHandler(output, log.TerminalFormat(usecolor))
|
||||
glogger := log.NewGlogHandler(ostream)
|
||||
// Set log level
|
||||
glogger.Verbosity(log.LvlTrace)
|
||||
log.Root().SetHandler(glogger)
|
||||
|
||||
var gl gormLogger
|
||||
gl.gethLogger = log.Root()
|
||||
|
||||
gl.Error(context.Background(), "test %s error:%v", "testError", errors.New("test error"))
|
||||
gl.Warn(context.Background(), "test %s warn:%v", "testWarn", errors.New("test warn"))
|
||||
gl.Info(context.Background(), "test %s warn:%v", "testInfo", errors.New("test info"))
|
||||
gl.Trace(context.Background(), time.Now(), func() (string, int64) { return "test trace", 1 }, nil)
|
||||
}
|
||||
@@ -19,7 +19,7 @@ CAPELLA_FORK_VERSION: 0x20000092
|
||||
MAX_WITHDRAWALS_PER_PAYLOAD: 16
|
||||
|
||||
# Deneb
|
||||
DENEB_FORK_EPOCH: 0
|
||||
DENEB_FORK_EPOCH: 1
|
||||
DENEB_FORK_VERSION: 0x20000093
|
||||
|
||||
# Time parameters
|
||||
127
common/docker-compose/l1/docker-compose.go
Normal file
127
common/docker-compose/l1/docker-compose.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package dockercompose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/cloudflare/cfssl/log"
|
||||
"github.com/scroll-tech/go-ethereum/ethclient"
|
||||
tc "github.com/testcontainers/testcontainers-go/modules/compose"
|
||||
"github.com/testcontainers/testcontainers-go/wait"
|
||||
)
|
||||
|
||||
// PoSL1TestEnv represents the config needed to test in PoS Layer 1.
|
||||
type PoSL1TestEnv struct {
|
||||
dockerComposeFile string
|
||||
compose tc.ComposeStack
|
||||
gethHTTPPort int
|
||||
hostPath string
|
||||
}
|
||||
|
||||
// NewPoSL1TestEnv creates and initializes a new instance of PoSL1TestEnv with a random HTTP port.
|
||||
func NewPoSL1TestEnv() (*PoSL1TestEnv, error) {
|
||||
rootDir, err := findProjectRootDir()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find project root directory: %v", err)
|
||||
}
|
||||
|
||||
hostPath, found := os.LookupEnv("HOST_PATH")
|
||||
if !found {
|
||||
hostPath = ""
|
||||
}
|
||||
|
||||
rnd, err := rand.Int(rand.Reader, big.NewInt(65536-1024))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate a random: %v", err)
|
||||
}
|
||||
gethHTTPPort := int(rnd.Int64()) + 1024
|
||||
|
||||
if err := os.Setenv("GETH_HTTP_PORT", fmt.Sprintf("%d", gethHTTPPort)); err != nil {
|
||||
return nil, fmt.Errorf("failed to set GETH_HTTP_PORT: %v", err)
|
||||
}
|
||||
|
||||
return &PoSL1TestEnv{
|
||||
dockerComposeFile: filepath.Join(rootDir, "common", "docker-compose", "l1", "docker-compose.yml"),
|
||||
gethHTTPPort: gethHTTPPort,
|
||||
hostPath: hostPath,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Start starts the PoS L1 test environment by running the associated Docker Compose configuration.
|
||||
func (e *PoSL1TestEnv) Start() error {
|
||||
var err error
|
||||
e.compose, err = tc.NewDockerCompose([]string{e.dockerComposeFile}...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create docker compose: %w", err)
|
||||
}
|
||||
|
||||
env := map[string]string{
|
||||
"GETH_HTTP_PORT": fmt.Sprintf("%d", e.gethHTTPPort),
|
||||
}
|
||||
|
||||
if e.hostPath != "" {
|
||||
env["HOST_PATH"] = e.hostPath
|
||||
}
|
||||
|
||||
if err = e.compose.WaitForService("geth", wait.NewHTTPStrategy("/").WithPort("8545/tcp").WithStartupTimeout(15*time.Second)).WithEnv(env).Up(context.Background()); err != nil {
|
||||
if errStop := e.Stop(); errStop != nil {
|
||||
log.Error("failed to stop PoS L1 test environment", "err", errStop)
|
||||
}
|
||||
return fmt.Errorf("failed to start PoS L1 test environment: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop stops the PoS L1 test environment by stopping and removing the associated Docker Compose services.
|
||||
func (e *PoSL1TestEnv) Stop() error {
|
||||
if e.compose != nil {
|
||||
if err := e.compose.Down(context.Background(), tc.RemoveOrphans(true), tc.RemoveVolumes(true), tc.RemoveImagesLocal); err != nil {
|
||||
return fmt.Errorf("failed to stop PoS L1 test environment: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Endpoint returns the HTTP endpoint for the PoS L1 test environment.
|
||||
func (e *PoSL1TestEnv) Endpoint() string {
|
||||
return fmt.Sprintf("http://127.0.0.1:%d", e.gethHTTPPort)
|
||||
}
|
||||
|
||||
// L1Client returns an ethclient by dialing the running PoS L1 test environment
|
||||
func (e *PoSL1TestEnv) L1Client() (*ethclient.Client, error) {
|
||||
if e == nil {
|
||||
return nil, fmt.Errorf("PoS L1 test environment is not initialized")
|
||||
}
|
||||
|
||||
client, err := ethclient.Dial(e.Endpoint())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to dial PoS L1 test environment: %w", err)
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func findProjectRootDir() (string, error) {
|
||||
currentDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get working directory: %w", err)
|
||||
}
|
||||
|
||||
for {
|
||||
_, err := os.Stat(filepath.Join(currentDir, "go.work"))
|
||||
if err == nil {
|
||||
return currentDir, nil
|
||||
}
|
||||
|
||||
parentDir := filepath.Dir(currentDir)
|
||||
if parentDir == currentDir {
|
||||
return "", fmt.Errorf("go.work file not found in any parent directory")
|
||||
}
|
||||
|
||||
currentDir = parentDir
|
||||
}
|
||||
}
|
||||
@@ -8,18 +8,18 @@ services:
|
||||
mkdir -p /data/execution &&
|
||||
cp -a /execution/* /data/execution/"
|
||||
volumes:
|
||||
- ../../common/testcontainers/consensus:/consensus
|
||||
- ../../common/testcontainers/execution:/execution
|
||||
- ${HOST_PATH:-../../..}/common/docker-compose/l1/consensus:/consensus
|
||||
- ${HOST_PATH:-../../..}/common/docker-compose/l1/execution:/execution
|
||||
- data:/data
|
||||
|
||||
# Creates a genesis state for the beacon chain using a YAML configuration file and
|
||||
# a deterministic set of 64 validators.
|
||||
create-beacon-chain-genesis:
|
||||
image: "gcr.io/prysmaticlabs/prysm/cmd/prysmctl:HEAD-263557"
|
||||
image: "gcr.io/prysmaticlabs/prysm/cmd/prysmctl:latest"
|
||||
command:
|
||||
- testnet
|
||||
- generate-genesis
|
||||
- --fork=deneb
|
||||
- --fork=capella
|
||||
- --num-validators=64
|
||||
- --genesis-time-delay=3
|
||||
- --output-ssz=/data/consensus/genesis.ssz
|
||||
@@ -96,7 +96,7 @@ services:
|
||||
- --nodiscover
|
||||
- --syncmode=full
|
||||
ports:
|
||||
- 8545
|
||||
- ${GETH_HTTP_PORT:-8545}:8545
|
||||
depends_on:
|
||||
geth-genesis:
|
||||
condition: service_completed_successfully
|
||||
196
common/docker/docker_app.go
Normal file
196
common/docker/docker_app.go
Normal file
@@ -0,0 +1,196 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/scroll-tech/go-ethereum/ethclient"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"scroll-tech/database"
|
||||
|
||||
"scroll-tech/common/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
l1StartPort = 10000
|
||||
l2StartPort = 20000
|
||||
dbStartPort = 30000
|
||||
)
|
||||
|
||||
// AppAPI app interface.
|
||||
type AppAPI interface {
|
||||
IsRunning() bool
|
||||
WaitResult(t *testing.T, timeout time.Duration, keyword string) bool
|
||||
RunApp(waitResult func() bool)
|
||||
WaitExit()
|
||||
ExpectWithTimeout(t *testing.T, parallel bool, timeout time.Duration, keyword string)
|
||||
}
|
||||
|
||||
// App is collection struct of runtime docker images
|
||||
type App struct {
|
||||
L1gethImg GethImgInstance
|
||||
L2gethImg GethImgInstance
|
||||
DBImg ImgInstance
|
||||
|
||||
dbClient *sql.DB
|
||||
DBConfig *database.DBConfig
|
||||
DBConfigFile string
|
||||
|
||||
// common time stamp.
|
||||
Timestamp int
|
||||
}
|
||||
|
||||
// NewDockerApp returns new instance of dockerApp struct
|
||||
func NewDockerApp() *App {
|
||||
timestamp := time.Now().Nanosecond()
|
||||
app := &App{
|
||||
Timestamp: timestamp,
|
||||
L1gethImg: newTestL1Docker(),
|
||||
L2gethImg: newTestL2Docker(),
|
||||
DBImg: newTestDBDocker("postgres"),
|
||||
DBConfigFile: fmt.Sprintf("/tmp/%d_db-config.json", timestamp),
|
||||
}
|
||||
if err := app.mockDBConfig(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return app
|
||||
}
|
||||
|
||||
// RunImages runs all images togather
|
||||
func (b *App) RunImages(t *testing.T) {
|
||||
b.RunDBImage(t)
|
||||
b.RunL1Geth(t)
|
||||
b.RunL2Geth(t)
|
||||
}
|
||||
|
||||
// RunDBImage starts postgres docker container.
|
||||
func (b *App) RunDBImage(t *testing.T) {
|
||||
if b.DBImg.IsRunning() {
|
||||
return
|
||||
}
|
||||
assert.NoError(t, b.DBImg.Start())
|
||||
|
||||
// try 5 times until the db is ready.
|
||||
ok := utils.TryTimes(10, func() bool {
|
||||
db, err := sqlx.Open("postgres", b.DBImg.Endpoint())
|
||||
return err == nil && db != nil && db.Ping() == nil
|
||||
})
|
||||
assert.True(t, ok)
|
||||
}
|
||||
|
||||
// Free clear all running images, double check and recycle docker container.
|
||||
func (b *App) Free() {
|
||||
if b.L1gethImg.IsRunning() {
|
||||
_ = b.L1gethImg.Stop()
|
||||
}
|
||||
if b.L2gethImg.IsRunning() {
|
||||
_ = b.L2gethImg.Stop()
|
||||
}
|
||||
if b.DBImg.IsRunning() {
|
||||
_ = b.DBImg.Stop()
|
||||
_ = os.Remove(b.DBConfigFile)
|
||||
if !utils.IsNil(b.dbClient) {
|
||||
_ = b.dbClient.Close()
|
||||
b.dbClient = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RunL1Geth starts l1geth docker container.
|
||||
func (b *App) RunL1Geth(t *testing.T) {
|
||||
if b.L1gethImg.IsRunning() {
|
||||
return
|
||||
}
|
||||
assert.NoError(t, b.L1gethImg.Start())
|
||||
}
|
||||
|
||||
// L1Client returns a ethclient by dialing running l1geth
|
||||
func (b *App) L1Client() (*ethclient.Client, error) {
|
||||
if utils.IsNil(b.L1gethImg) {
|
||||
return nil, fmt.Errorf("l1 geth is not running")
|
||||
}
|
||||
client, err := ethclient.Dial(b.L1gethImg.Endpoint())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// RunL2Geth starts l2geth docker container.
|
||||
func (b *App) RunL2Geth(t *testing.T) {
|
||||
if b.L2gethImg.IsRunning() {
|
||||
return
|
||||
}
|
||||
assert.NoError(t, b.L2gethImg.Start())
|
||||
}
|
||||
|
||||
// L2Client returns a ethclient by dialing running l2geth
|
||||
func (b *App) L2Client() (*ethclient.Client, error) {
|
||||
if utils.IsNil(b.L2gethImg) {
|
||||
return nil, fmt.Errorf("l2 geth is not running")
|
||||
}
|
||||
client, err := ethclient.Dial(b.L2gethImg.Endpoint())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// DBClient create and return *sql.DB instance.
|
||||
func (b *App) DBClient(t *testing.T) *sql.DB {
|
||||
if !utils.IsNil(b.dbClient) {
|
||||
return b.dbClient
|
||||
}
|
||||
var (
|
||||
cfg = b.DBConfig
|
||||
err error
|
||||
)
|
||||
b.dbClient, err = sql.Open(cfg.DriverName, cfg.DSN)
|
||||
assert.NoError(t, err)
|
||||
b.dbClient.SetMaxOpenConns(cfg.MaxOpenNum)
|
||||
b.dbClient.SetMaxIdleConns(cfg.MaxIdleNum)
|
||||
assert.NoError(t, b.dbClient.Ping())
|
||||
return b.dbClient
|
||||
}
|
||||
|
||||
func (b *App) mockDBConfig() error {
|
||||
b.DBConfig = &database.DBConfig{
|
||||
DSN: "",
|
||||
DriverName: "postgres",
|
||||
MaxOpenNum: 200,
|
||||
MaxIdleNum: 20,
|
||||
}
|
||||
|
||||
if b.DBImg != nil {
|
||||
b.DBConfig.DSN = b.DBImg.Endpoint()
|
||||
}
|
||||
data, err := json.Marshal(b.DBConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.WriteFile(b.DBConfigFile, data, 0644) //nolint:gosec
|
||||
}
|
||||
|
||||
func newTestL1Docker() GethImgInstance {
|
||||
id, _ := rand.Int(rand.Reader, big.NewInt(2000))
|
||||
return NewImgGeth("scroll_l1geth", "", "", 0, l1StartPort+int(id.Int64()))
|
||||
}
|
||||
|
||||
func newTestL2Docker() GethImgInstance {
|
||||
id, _ := rand.Int(rand.Reader, big.NewInt(2000))
|
||||
return NewImgGeth("scroll_l2geth", "", "", 0, l2StartPort+int(id.Int64()))
|
||||
}
|
||||
|
||||
func newTestDBDocker(driverName string) ImgInstance {
|
||||
id, _ := rand.Int(rand.Reader, big.NewInt(2000))
|
||||
return NewImgDB(driverName, "123456", "test_db", dbStartPort+int(id.Int64()))
|
||||
}
|
||||
131
common/docker/docker_db.go
Normal file
131
common/docker/docker_db.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
|
||||
"scroll-tech/common/cmd"
|
||||
"scroll-tech/common/utils"
|
||||
)
|
||||
|
||||
// ImgDB the postgres image manager.
|
||||
type ImgDB struct {
|
||||
image string
|
||||
name string
|
||||
id string
|
||||
|
||||
dbName string
|
||||
port int
|
||||
password string
|
||||
|
||||
running bool
|
||||
cmd *cmd.Cmd
|
||||
}
|
||||
|
||||
// NewImgDB return postgres db img instance.
|
||||
func NewImgDB(image, password, dbName string, port int) ImgInstance {
|
||||
img := &ImgDB{
|
||||
image: image,
|
||||
name: fmt.Sprintf("%s-%s_%d", image, dbName, port),
|
||||
password: password,
|
||||
dbName: dbName,
|
||||
port: port,
|
||||
}
|
||||
img.cmd = cmd.NewCmd("docker", img.prepare()...)
|
||||
return img
|
||||
}
|
||||
|
||||
// Start postgres db container.
|
||||
func (i *ImgDB) Start() error {
|
||||
id := GetContainerID(i.name)
|
||||
if id != "" {
|
||||
return fmt.Errorf("container already exist, name: %s", i.name)
|
||||
}
|
||||
i.running = i.isOk()
|
||||
if !i.running {
|
||||
_ = i.Stop()
|
||||
return fmt.Errorf("failed to start image: %s", i.image)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop the container.
|
||||
func (i *ImgDB) Stop() error {
|
||||
if !i.running {
|
||||
return nil
|
||||
}
|
||||
i.running = false
|
||||
|
||||
ctx := context.Background()
|
||||
// stop the running container.
|
||||
if i.id == "" {
|
||||
i.id = GetContainerID(i.name)
|
||||
}
|
||||
|
||||
timeoutSec := 3
|
||||
timeout := container.StopOptions{
|
||||
Timeout: &timeoutSec,
|
||||
}
|
||||
if err := cli.ContainerStop(ctx, i.id, timeout); err != nil {
|
||||
return err
|
||||
}
|
||||
// remove the stopped container.
|
||||
return cli.ContainerRemove(ctx, i.id, container.RemoveOptions{})
|
||||
}
|
||||
|
||||
// Endpoint return the dsn.
|
||||
func (i *ImgDB) Endpoint() string {
|
||||
return fmt.Sprintf("postgres://postgres:%s@localhost:%d/%s?sslmode=disable", i.password, i.port, i.dbName)
|
||||
}
|
||||
|
||||
// IsRunning returns docker container's running status.
|
||||
func (i *ImgDB) IsRunning() bool {
|
||||
return i.running
|
||||
}
|
||||
|
||||
func (i *ImgDB) prepare() []string {
|
||||
cmd := []string{"run", "--rm", "--name", i.name, "-p", fmt.Sprintf("%d:5432", i.port)}
|
||||
envs := []string{
|
||||
"-e", "POSTGRES_PASSWORD=" + i.password,
|
||||
"-e", fmt.Sprintf("POSTGRES_DB=%s", i.dbName),
|
||||
}
|
||||
|
||||
cmd = append(cmd, envs...)
|
||||
return append(cmd, i.image)
|
||||
}
|
||||
|
||||
func (i *ImgDB) isOk() bool {
|
||||
keyword := "database system is ready to accept connections"
|
||||
okCh := make(chan struct{}, 1)
|
||||
i.cmd.RegistFunc(keyword, func(buf string) {
|
||||
if strings.Contains(buf, keyword) {
|
||||
select {
|
||||
case okCh <- struct{}{}:
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
defer i.cmd.UnRegistFunc(keyword)
|
||||
// Start cmd in parallel.
|
||||
i.cmd.RunCmd(true)
|
||||
|
||||
select {
|
||||
case <-okCh:
|
||||
utils.TryTimes(20, func() bool {
|
||||
i.id = GetContainerID(i.name)
|
||||
return i.id != ""
|
||||
})
|
||||
case err := <-i.cmd.ErrChan:
|
||||
if err != nil {
|
||||
fmt.Printf("failed to start %s, err: %v\n", i.name, err)
|
||||
}
|
||||
case <-time.After(time.Second * 20):
|
||||
return false
|
||||
}
|
||||
return i.id != ""
|
||||
}
|
||||
174
common/docker/docker_geth.go
Normal file
174
common/docker/docker_geth.go
Normal file
@@ -0,0 +1,174 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/scroll-tech/go-ethereum/ethclient"
|
||||
|
||||
"scroll-tech/common/cmd"
|
||||
"scroll-tech/common/utils"
|
||||
)
|
||||
|
||||
// ImgGeth the geth image manager include l1geth and l2geth.
|
||||
type ImgGeth struct {
|
||||
image string
|
||||
name string
|
||||
id string
|
||||
|
||||
volume string
|
||||
ipcPath string
|
||||
httpPort int
|
||||
wsPort int
|
||||
chainID *big.Int
|
||||
|
||||
running bool
|
||||
cmd *cmd.Cmd
|
||||
}
|
||||
|
||||
// NewImgGeth return geth img instance.
|
||||
func NewImgGeth(image, volume, ipc string, hPort, wPort int) GethImgInstance {
|
||||
img := &ImgGeth{
|
||||
image: image,
|
||||
name: fmt.Sprintf("%s-%d", image, time.Now().Nanosecond()),
|
||||
volume: volume,
|
||||
ipcPath: ipc,
|
||||
httpPort: hPort,
|
||||
wsPort: wPort,
|
||||
}
|
||||
img.cmd = cmd.NewCmd("docker", img.params()...)
|
||||
return img
|
||||
}
|
||||
|
||||
// Start run image and check if it is running healthily.
|
||||
func (i *ImgGeth) Start() error {
|
||||
id := GetContainerID(i.name)
|
||||
if id != "" {
|
||||
return fmt.Errorf("container already exist, name: %s", i.name)
|
||||
}
|
||||
i.running = i.isOk()
|
||||
if !i.running {
|
||||
_ = i.Stop()
|
||||
return fmt.Errorf("failed to start image: %s", i.image)
|
||||
}
|
||||
|
||||
// try 10 times to get chainID until is ok.
|
||||
utils.TryTimes(10, func() bool {
|
||||
client, err := ethclient.Dial(i.Endpoint())
|
||||
if err == nil && client != nil {
|
||||
i.chainID, err = client.ChainID(context.Background())
|
||||
return err == nil && i.chainID != nil
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsRunning returns docker container's running status.
|
||||
func (i *ImgGeth) IsRunning() bool {
|
||||
return i.running
|
||||
}
|
||||
|
||||
// Endpoint return the connection endpoint.
|
||||
func (i *ImgGeth) Endpoint() string {
|
||||
switch true {
|
||||
case i.httpPort != 0:
|
||||
return fmt.Sprintf("http://127.0.0.1:%d", i.httpPort)
|
||||
case i.wsPort != 0:
|
||||
return fmt.Sprintf("ws://127.0.0.1:%d", i.wsPort)
|
||||
default:
|
||||
return i.ipcPath
|
||||
}
|
||||
}
|
||||
|
||||
// ChainID return chainID.
|
||||
func (i *ImgGeth) ChainID() *big.Int {
|
||||
return i.chainID
|
||||
}
|
||||
|
||||
func (i *ImgGeth) isOk() bool {
|
||||
keyword := "WebSocket enabled"
|
||||
okCh := make(chan struct{}, 1)
|
||||
i.cmd.RegistFunc(keyword, func(buf string) {
|
||||
if strings.Contains(buf, keyword) {
|
||||
select {
|
||||
case okCh <- struct{}{}:
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
defer i.cmd.UnRegistFunc(keyword)
|
||||
// Start cmd in parallel.
|
||||
i.cmd.RunCmd(true)
|
||||
|
||||
select {
|
||||
case <-okCh:
|
||||
utils.TryTimes(20, func() bool {
|
||||
i.id = GetContainerID(i.name)
|
||||
return i.id != ""
|
||||
})
|
||||
case err := <-i.cmd.ErrChan:
|
||||
if err != nil {
|
||||
fmt.Printf("failed to start %s, err: %v\n", i.name, err)
|
||||
}
|
||||
case <-time.After(time.Second * 10):
|
||||
return false
|
||||
}
|
||||
return i.id != ""
|
||||
}
|
||||
|
||||
// Stop the docker container.
|
||||
func (i *ImgGeth) Stop() error {
|
||||
if !i.running {
|
||||
return nil
|
||||
}
|
||||
i.running = false
|
||||
|
||||
ctx := context.Background()
|
||||
// check if container is running, stop the running container.
|
||||
id := GetContainerID(i.name)
|
||||
if id != "" {
|
||||
timeoutSec := 3
|
||||
timeout := container.StopOptions{
|
||||
Timeout: &timeoutSec,
|
||||
}
|
||||
if err := cli.ContainerStop(ctx, id, timeout); err != nil {
|
||||
return err
|
||||
}
|
||||
i.id = id
|
||||
}
|
||||
// remove the stopped container.
|
||||
return cli.ContainerRemove(ctx, i.id, container.RemoveOptions{})
|
||||
}
|
||||
|
||||
func (i *ImgGeth) params() []string {
|
||||
cmds := []string{"run", "--rm", "--name", i.name}
|
||||
var ports []string
|
||||
if i.httpPort != 0 {
|
||||
ports = append(ports, []string{"-p", strconv.Itoa(i.httpPort) + ":8545"}...)
|
||||
}
|
||||
if i.wsPort != 0 {
|
||||
ports = append(ports, []string{"-p", strconv.Itoa(i.wsPort) + ":8546"}...)
|
||||
}
|
||||
|
||||
var envs []string
|
||||
if i.ipcPath != "" {
|
||||
envs = append(envs, []string{"-e", fmt.Sprintf("IPC_PATH=%s", i.ipcPath)}...)
|
||||
}
|
||||
|
||||
if i.volume != "" {
|
||||
cmds = append(cmds, []string{"-v", fmt.Sprintf("%s:%s", i.volume, i.volume)}...)
|
||||
}
|
||||
|
||||
cmds = append(cmds, ports...)
|
||||
cmds = append(cmds, envs...)
|
||||
|
||||
return append(cmds, i.image)
|
||||
}
|
||||
54
common/docker/docker_test.go
Normal file
54
common/docker/docker_test.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package docker_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
_ "github.com/lib/pq" //nolint:golint
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"scroll-tech/common/docker"
|
||||
)
|
||||
|
||||
var (
|
||||
base *docker.App
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
base = docker.NewDockerApp()
|
||||
|
||||
m.Run()
|
||||
|
||||
base.Free()
|
||||
}
|
||||
|
||||
func TestDB(t *testing.T) {
|
||||
base.RunDBImage(t)
|
||||
|
||||
db, err := sqlx.Open("postgres", base.DBImg.Endpoint())
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, db.Ping())
|
||||
}
|
||||
|
||||
func TestL1Geth(t *testing.T) {
|
||||
base.RunL1Geth(t)
|
||||
|
||||
client, err := base.L1Client()
|
||||
assert.NoError(t, err)
|
||||
|
||||
chainID, err := client.ChainID(context.Background())
|
||||
assert.NoError(t, err)
|
||||
t.Logf("chainId: %s", chainID.String())
|
||||
}
|
||||
|
||||
func TestL2Geth(t *testing.T) {
|
||||
base.RunL2Geth(t)
|
||||
|
||||
client, err := base.L2Client()
|
||||
assert.NoError(t, err)
|
||||
|
||||
chainID, err := client.ChainID(context.Background())
|
||||
assert.NoError(t, err)
|
||||
t.Logf("chainId: %s", chainID.String())
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
"archimedesBlock": 0,
|
||||
"shanghaiBlock": 0,
|
||||
"clique": {
|
||||
"period": 1,
|
||||
"period": 3,
|
||||
"epoch": 30000
|
||||
},
|
||||
"scroll": {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package forks
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/big"
|
||||
"sort"
|
||||
|
||||
@@ -9,48 +8,30 @@ import (
|
||||
)
|
||||
|
||||
// CollectSortedForkHeights returns a sorted set of block numbers that one or more forks are activated on
|
||||
func CollectSortedForkHeights(config *params.ChainConfig) ([]uint64, map[uint64]bool, map[string]uint64) {
|
||||
type nameFork struct {
|
||||
name string
|
||||
block *big.Int
|
||||
}
|
||||
|
||||
forkHeightNameMap := make(map[uint64]string)
|
||||
|
||||
for _, fork := range []nameFork{
|
||||
{name: "homestead", block: config.HomesteadBlock},
|
||||
{name: "daoFork", block: config.DAOForkBlock},
|
||||
{name: "eip150", block: config.EIP150Block},
|
||||
{name: "eip155", block: config.EIP155Block},
|
||||
{name: "eip158", block: config.EIP158Block},
|
||||
{name: "byzantium", block: config.ByzantiumBlock},
|
||||
{name: "constantinople", block: config.ConstantinopleBlock},
|
||||
{name: "petersburg", block: config.PetersburgBlock},
|
||||
{name: "istanbul", block: config.IstanbulBlock},
|
||||
{name: "muirGlacier", block: config.MuirGlacierBlock},
|
||||
{name: "berlin", block: config.BerlinBlock},
|
||||
{name: "london", block: config.LondonBlock},
|
||||
{name: "arrowGlacier", block: config.ArrowGlacierBlock},
|
||||
{name: "archimedes", block: config.ArchimedesBlock},
|
||||
{name: "shanghai", block: config.ShanghaiBlock},
|
||||
{name: "bernoulli", block: config.BernoulliBlock},
|
||||
{name: "curie", block: config.CurieBlock},
|
||||
} {
|
||||
if fork.block == nil {
|
||||
continue
|
||||
}
|
||||
height := fork.block.Uint64()
|
||||
|
||||
// only keep latest fork for at each height, discard the rest
|
||||
forkHeightNameMap[height] = fork.name
|
||||
}
|
||||
|
||||
func CollectSortedForkHeights(config *params.ChainConfig) ([]uint64, map[uint64]bool) {
|
||||
forkHeightsMap := make(map[uint64]bool)
|
||||
forkNameHeightMap := make(map[string]uint64)
|
||||
|
||||
for height, name := range forkHeightNameMap {
|
||||
forkHeightsMap[height] = true
|
||||
forkNameHeightMap[name] = height
|
||||
for _, fork := range []*big.Int{
|
||||
config.HomesteadBlock,
|
||||
config.DAOForkBlock,
|
||||
config.EIP150Block,
|
||||
config.EIP155Block,
|
||||
config.EIP158Block,
|
||||
config.ByzantiumBlock,
|
||||
config.ConstantinopleBlock,
|
||||
config.PetersburgBlock,
|
||||
config.IstanbulBlock,
|
||||
config.MuirGlacierBlock,
|
||||
config.BerlinBlock,
|
||||
config.LondonBlock,
|
||||
config.ArrowGlacierBlock,
|
||||
config.ArchimedesBlock,
|
||||
config.ShanghaiBlock,
|
||||
} {
|
||||
if fork == nil {
|
||||
continue
|
||||
} else if height := fork.Uint64(); height != 0 {
|
||||
forkHeightsMap[height] = true
|
||||
}
|
||||
}
|
||||
|
||||
var forkHeights []uint64
|
||||
@@ -60,7 +41,7 @@ func CollectSortedForkHeights(config *params.ChainConfig) ([]uint64, map[uint64]
|
||||
sort.Slice(forkHeights, func(i, j int) bool {
|
||||
return forkHeights[i] < forkHeights[j]
|
||||
})
|
||||
return forkHeights, forkHeightsMap, forkNameHeightMap
|
||||
return forkHeights, forkHeightsMap
|
||||
}
|
||||
|
||||
// BlocksUntilFork returns the number of blocks until the next fork
|
||||
@@ -73,17 +54,3 @@ func BlocksUntilFork(blockHeight uint64, forkHeights []uint64) uint64 {
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// BlockRange returns the block range of the hard fork
|
||||
// Need ensure the forkHeights is incremental
|
||||
func BlockRange(currentForkHeight uint64, forkHeights []uint64) (from, to uint64) {
|
||||
to = math.MaxInt64
|
||||
for _, height := range forkHeights {
|
||||
if currentForkHeight < height {
|
||||
to = height
|
||||
return
|
||||
}
|
||||
from = height
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package forks
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
@@ -10,27 +9,20 @@ import (
|
||||
)
|
||||
|
||||
func TestCollectSortedForkBlocks(t *testing.T) {
|
||||
l, m, n := CollectSortedForkHeights(¶ms.ChainConfig{
|
||||
ArchimedesBlock: big.NewInt(0),
|
||||
ShanghaiBlock: big.NewInt(3),
|
||||
BernoulliBlock: big.NewInt(3),
|
||||
CurieBlock: big.NewInt(4),
|
||||
l, m := CollectSortedForkHeights(¶ms.ChainConfig{
|
||||
EIP155Block: big.NewInt(4),
|
||||
EIP158Block: big.NewInt(3),
|
||||
ByzantiumBlock: big.NewInt(3),
|
||||
ConstantinopleBlock: big.NewInt(0),
|
||||
})
|
||||
require.Equal(t, l, []uint64{
|
||||
0,
|
||||
3,
|
||||
4,
|
||||
})
|
||||
require.Equal(t, map[uint64]bool{
|
||||
3: true,
|
||||
4: true,
|
||||
0: true,
|
||||
}, m)
|
||||
require.Equal(t, map[string]uint64{
|
||||
"archimedes": 0,
|
||||
"bernoulli": 3,
|
||||
"curie": 4,
|
||||
}, n)
|
||||
}
|
||||
|
||||
func TestBlocksUntilFork(t *testing.T) {
|
||||
@@ -72,71 +64,3 @@ func TestBlocksUntilFork(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockRange(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
forkHeight uint64
|
||||
forkHeights []uint64
|
||||
expectedFrom uint64
|
||||
expectedTo uint64
|
||||
}{
|
||||
{
|
||||
name: "ToInfinite",
|
||||
forkHeight: 300,
|
||||
forkHeights: []uint64{100, 200, 300},
|
||||
expectedFrom: 300,
|
||||
expectedTo: math.MaxInt64,
|
||||
},
|
||||
{
|
||||
name: "To300",
|
||||
forkHeight: 200,
|
||||
forkHeights: []uint64{100, 200, 300},
|
||||
expectedFrom: 200,
|
||||
expectedTo: 300,
|
||||
},
|
||||
{
|
||||
name: "To200",
|
||||
forkHeight: 100,
|
||||
forkHeights: []uint64{100, 200, 300},
|
||||
expectedFrom: 100,
|
||||
expectedTo: 200,
|
||||
},
|
||||
{
|
||||
name: "To100",
|
||||
forkHeight: 0,
|
||||
forkHeights: []uint64{100, 200, 300},
|
||||
expectedFrom: 0,
|
||||
expectedTo: 100,
|
||||
},
|
||||
{
|
||||
name: "To200-1",
|
||||
forkHeight: 100,
|
||||
forkHeights: []uint64{100, 200},
|
||||
expectedFrom: 100,
|
||||
expectedTo: 200,
|
||||
},
|
||||
{
|
||||
name: "To2",
|
||||
forkHeight: 1,
|
||||
forkHeights: []uint64{1, 2},
|
||||
expectedFrom: 1,
|
||||
expectedTo: 2,
|
||||
},
|
||||
{
|
||||
name: "ToInfinite-1",
|
||||
forkHeight: 0,
|
||||
forkHeights: []uint64{0},
|
||||
expectedFrom: 0,
|
||||
expectedTo: math.MaxInt64,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
from, to := BlockRange(test.forkHeight, test.forkHeights)
|
||||
require.Equal(t, test.expectedFrom, from)
|
||||
require.Equal(t, test.expectedTo, to)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,19 +5,21 @@ go 1.21
|
||||
require (
|
||||
github.com/Masterminds/semver/v3 v3.2.1
|
||||
github.com/bits-and-blooms/bitset v1.12.0
|
||||
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004
|
||||
github.com/docker/docker v25.0.3+incompatible
|
||||
github.com/gin-contrib/pprof v1.4.0
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
github.com/jmoiron/sqlx v1.3.5
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/mattn/go-colorable v0.1.13
|
||||
github.com/mattn/go-isatty v0.0.20
|
||||
github.com/modern-go/reflect2 v1.0.2
|
||||
github.com/orcaman/concurrent-map v1.0.0
|
||||
github.com/prometheus/client_golang v1.16.0
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20240426041101-a860446ebaea
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20240314095130-4553f5f26935
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/testcontainers/testcontainers-go v0.28.0
|
||||
github.com/testcontainers/testcontainers-go/modules/compose v0.28.0
|
||||
github.com/testcontainers/testcontainers-go/modules/postgres v0.28.0
|
||||
github.com/testcontainers/testcontainers-go v0.29.1
|
||||
github.com/testcontainers/testcontainers-go/modules/compose v0.29.1
|
||||
github.com/urfave/cli/v2 v2.25.7
|
||||
gorm.io/driver/postgres v1.5.0
|
||||
gorm.io/gorm v1.25.5
|
||||
@@ -61,7 +63,7 @@ require (
|
||||
github.com/containerd/typeurl/v2 v2.1.1 // indirect
|
||||
github.com/cpuguy83/dockercfg v0.3.1 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
|
||||
github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect
|
||||
github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/deckarep/golang-set v1.8.0 // indirect
|
||||
github.com/distribution/reference v0.5.0 // indirect
|
||||
@@ -125,7 +127,7 @@ require (
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jackc/pgx/v5 v5.5.4 // indirect
|
||||
github.com/jackc/pgx/v5 v5.5.0 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
@@ -142,6 +144,7 @@ require (
|
||||
github.com/mailru/easyjson v0.7.6 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/mattn/go-shellwords v1.0.12 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.16 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||
@@ -182,7 +185,7 @@ require (
|
||||
github.com/rjeczalik/notify v0.9.1 // indirect
|
||||
github.com/rs/cors v1.7.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/scroll-tech/zktrie v0.8.2 // indirect
|
||||
github.com/scroll-tech/zktrie v0.7.1 // indirect
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.4.0 // indirect
|
||||
github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002 // indirect
|
||||
github.com/shibumi/go-pathspec v1.3.0 // indirect
|
||||
|
||||
@@ -148,8 +148,8 @@ github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoY
|
||||
github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI=
|
||||
github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc=
|
||||
github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA=
|
||||
github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||
@@ -268,6 +268,7 @@ github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXS
|
||||
github.com/go-playground/validator/v10 v10.15.5 h1:LEBecTWb/1j5TNY1YYG2RcOUN3R7NLylN+x8TTueE24=
|
||||
github.com/go-playground/validator/v10 v10.15.5/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
@@ -381,8 +382,8 @@ github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5ey
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.3.0/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8=
|
||||
github.com/jackc/pgx/v5 v5.5.4 h1:Xp2aQS8uXButQdnCMWNmvx6UysWQQC+u1EoizjguY+8=
|
||||
github.com/jackc/pgx/v5 v5.5.4/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
||||
github.com/jackc/pgx/v5 v5.5.0 h1:NxstgwndsTRy7eq9/kqYc/BZh5w2hHJV86wjvO+1xPw=
|
||||
github.com/jackc/pgx/v5 v5.5.0/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA=
|
||||
github.com/jackc/puddle/v2 v2.2.0/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
@@ -399,6 +400,8 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
|
||||
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
|
||||
github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4=
|
||||
github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
@@ -440,6 +443,7 @@ github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ic
|
||||
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||
github.com/lib/pq v0.0.0-20150723085316-0dad96c0b94f/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
@@ -465,6 +469,9 @@ github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh
|
||||
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
|
||||
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||
github.com/mattn/go-sqlite3 v1.6.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
@@ -607,10 +614,10 @@ 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.20240426041101-a860446ebaea h1:CH1WXWrpEpLaP3N+bFs2a1xdE0+lRm1AuJQb5YvE6Ls=
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20240426041101-a860446ebaea/go.mod h1:i4VBgWoaW/y0D8MmQb7hSOulyw1dKhuiSFAbznwivCA=
|
||||
github.com/scroll-tech/zktrie v0.8.2 h1:UMuIfA+jdgWMLmTgTL64Emo+zzMOdcnH0+eYdDcshxQ=
|
||||
github.com/scroll-tech/zktrie v0.8.2/go.mod h1:XvNo7vAk8yxNyTjBDj5WIiFzYW4bx/gJ78+NK6Zn6Uk=
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20240314095130-4553f5f26935 h1:bHBt6sillaT4o/9RjxkVX8pWwvEmu37uWBw4XbCjfzY=
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20240314095130-4553f5f26935/go.mod h1:7Rz2bh9pn42rGuxjh51CG7HL9SKMG3ZugJkL3emdZx8=
|
||||
github.com/scroll-tech/zktrie v0.7.1 h1:NrmZNjuBzsbrKePqdHDG+t2cXnimbtezPAFS0+L9ElE=
|
||||
github.com/scroll-tech/zktrie v0.7.1/go.mod h1:XvNo7vAk8yxNyTjBDj5WIiFzYW4bx/gJ78+NK6Zn6Uk=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.4.0 h1:b23VGrQhTA8cN2CbBw7/FulN9fTtqYUdS5+Oxzt+DUE=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.4.0/go.mod h1:FGBZgq2tXWICsxWQW1msNf49F0Pf2Op5Htayx335Qbs=
|
||||
github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002 h1:ka9QPuQg2u4LGipiZGsgkg3rJCo4iIUCy75FddM0GRQ=
|
||||
@@ -671,12 +678,10 @@ github.com/supranational/blst v0.3.11-0.20230124161941-ca03e11a3ff2 h1:wh1wzwAhZ
|
||||
github.com/supranational/blst v0.3.11-0.20230124161941-ca03e11a3ff2/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
|
||||
github.com/testcontainers/testcontainers-go v0.28.0 h1:1HLm9qm+J5VikzFDYhOd+Zw12NtOl+8drH2E8nTY1r8=
|
||||
github.com/testcontainers/testcontainers-go v0.28.0/go.mod h1:COlDpUXbwW3owtpMkEB1zo9gwb1CoKVKlyrVPejF4AU=
|
||||
github.com/testcontainers/testcontainers-go/modules/compose v0.28.0 h1:QOCeTYZIYixg796Ik60MOaeMgpAKPbQd5pJOdTrftyg=
|
||||
github.com/testcontainers/testcontainers-go/modules/compose v0.28.0/go.mod h1:lShXm8oldlLck3ltA5u+ShSvUnZ+wiNxwpp8wAQGZ1Y=
|
||||
github.com/testcontainers/testcontainers-go/modules/postgres v0.28.0 h1:ff0s4JdYIdNAVSi/SrpN2Pdt1f+IjIw3AKjbHau8Un4=
|
||||
github.com/testcontainers/testcontainers-go/modules/postgres v0.28.0/go.mod h1:fXgcYpbyrduNdiz2qRZuYkmvqLnEqsjbQiBNYH1ystI=
|
||||
github.com/testcontainers/testcontainers-go v0.29.1 h1:z8kxdFlovA2y97RWx98v/TQ+tR+SXZm6p35M+xB92zk=
|
||||
github.com/testcontainers/testcontainers-go v0.29.1/go.mod h1:SnKnKQav8UcgtKqjp/AD8bE1MqZm+3TDb/B8crE3XnI=
|
||||
github.com/testcontainers/testcontainers-go/modules/compose v0.29.1 h1:47ipPM+s+ltCDOP3Sa1j95AkNb+z+WGiHLDbLU8ixuc=
|
||||
github.com/testcontainers/testcontainers-go/modules/compose v0.29.1/go.mod h1:Sqh+Ef2ESdbJQjTJl57UOkEHkOc7gXvQLg1b5xh6f1Y=
|
||||
github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c=
|
||||
github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw=
|
||||
github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375 h1:QB54BJwA6x8QU9nHY3xJSZR2kX9bgpZekRKGkLTmEXA=
|
||||
|
||||
1050
common/libzkp/impl/Cargo.lock
generated
1050
common/libzkp/impl/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -8,30 +8,26 @@ edition = "2021"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[patch.crates-io]
|
||||
gobuild = { git = "https://github.com/scroll-tech/gobuild.git" }
|
||||
halo2curves = { git = "https://github.com/scroll-tech/halo2curves", branch = "v0.1.0" }
|
||||
ethers-core = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7" }
|
||||
ethers-providers = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7" }
|
||||
ethers-signers = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7" }
|
||||
#ethers-etherscan = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7" }
|
||||
#ethers = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7" }
|
||||
[patch."https://github.com/privacy-scaling-explorations/halo2.git"]
|
||||
halo2_proofs = { git = "https://github.com/scroll-tech/halo2.git", branch = "v1.1" }
|
||||
halo2_proofs = { git = "https://github.com/scroll-tech/halo2.git", branch = "develop" }
|
||||
[patch."https://github.com/privacy-scaling-explorations/poseidon.git"]
|
||||
poseidon = { git = "https://github.com/scroll-tech/poseidon.git", branch = "main" }
|
||||
[patch."https://github.com/privacy-scaling-explorations/bls12_381"]
|
||||
bls12_381 = { git = "https://github.com/scroll-tech/bls12_381", branch = "feat/impl_scalar_field" }
|
||||
poseidon = { git = "https://github.com/scroll-tech/poseidon.git", branch = "scroll-dev-0220" }
|
||||
[patch."https://github.com/privacy-scaling-explorations/halo2wrong.git"]
|
||||
halo2wrong = { git = "https://github.com/scroll-tech/halo2wrong.git", branch = "halo2-ecc-snark-verifier-0323" }
|
||||
maingate = { git = "https://github.com/scroll-tech/halo2wrong", branch = "halo2-ecc-snark-verifier-0323" }
|
||||
[patch."https://github.com/privacy-scaling-explorations/halo2curves.git"]
|
||||
halo2curves = { git = "https://github.com/scroll-tech/halo2curves.git", branch = "0.3.1-derive-serde" }
|
||||
|
||||
[dependencies]
|
||||
halo2_proofs = { git = "https://github.com/scroll-tech/halo2.git", branch = "v1.1" }
|
||||
snark-verifier-sdk = { git = "https://github.com/scroll-tech/snark-verifier", branch = "develop", default-features = false, features = ["loader_halo2", "loader_evm", "halo2-pse"] }
|
||||
prover = { git = "https://github.com/scroll-tech/zkevm-circuits.git", tag = "v0.10.3", default-features = false, features = ["parallel_syn", "scroll", "shanghai"] }
|
||||
halo2_proofs = { git = "https://github.com/scroll-tech/halo2.git", branch = "develop" }
|
||||
prover = { git = "https://github.com/scroll-tech/zkevm-circuits.git", tag = "v0.9.9", default-features = false, features = ["parallel_syn", "scroll", "shanghai", "strict-ccc"] }
|
||||
|
||||
base64 = "0.13.0"
|
||||
env_logger = "0.9.0"
|
||||
libc = "0.2"
|
||||
log = "0.4"
|
||||
once_cell = "1.19"
|
||||
once_cell = "1.8.0"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0.66"
|
||||
|
||||
@@ -1 +1 @@
|
||||
nightly-2023-12-03
|
||||
nightly-2022-12-10
|
||||
|
||||
@@ -12,7 +12,6 @@ use prover::{
|
||||
utils::{chunk_trace_to_witness_block, init_env_and_log},
|
||||
BatchProof, BlockTrace, ChunkHash, ChunkProof,
|
||||
};
|
||||
use snark_verifier_sdk::verify_evm_calldata;
|
||||
use std::{cell::OnceCell, env, ptr::null};
|
||||
|
||||
static mut PROVER: OnceCell<Prover> = OnceCell::new();
|
||||
@@ -120,7 +119,7 @@ pub unsafe extern "C" fn gen_batch_proof(
|
||||
|
||||
let chunk_hashes_proofs = chunk_hashes
|
||||
.into_iter()
|
||||
.zip(chunk_proofs)
|
||||
.zip(chunk_proofs.into_iter())
|
||||
.collect();
|
||||
|
||||
let proof = PROVER
|
||||
@@ -149,33 +148,11 @@ pub unsafe extern "C" fn gen_batch_proof(
|
||||
|
||||
/// # Safety
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn verify_batch_proof(
|
||||
proof: *const c_char,
|
||||
fork_name: *const c_char,
|
||||
) -> c_char {
|
||||
pub unsafe extern "C" fn verify_batch_proof(proof: *const c_char) -> c_char {
|
||||
let proof = c_char_to_vec(proof);
|
||||
let proof = serde_json::from_slice::<BatchProof>(proof.as_slice()).unwrap();
|
||||
let fork_name_str = c_char_to_str(fork_name);
|
||||
let fork_id = match fork_name_str {
|
||||
"" => 0,
|
||||
"shanghai" => 0,
|
||||
"bernoulli" => 1,
|
||||
_ => {
|
||||
log::warn!("unexpected fork_name {fork_name_str}, treated as bernoulli");
|
||||
1
|
||||
}
|
||||
};
|
||||
let verified = panic_catch(|| {
|
||||
if fork_id == 0 {
|
||||
// before upgrade#2(EIP4844)
|
||||
verify_evm_calldata(
|
||||
include_bytes!("evm_verifier_fork_1.bin").to_vec(),
|
||||
proof.calldata(),
|
||||
)
|
||||
} else {
|
||||
VERIFIER.get().unwrap().verify_agg_evm_proof(proof)
|
||||
}
|
||||
});
|
||||
|
||||
let verified = panic_catch(|| VERIFIER.get().unwrap().verify_agg_evm_proof(proof));
|
||||
verified.unwrap_or(false) as c_char
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
@@ -1,3 +1,5 @@
|
||||
#![feature(once_cell)]
|
||||
|
||||
mod batch;
|
||||
mod chunk;
|
||||
mod types;
|
||||
|
||||
@@ -3,7 +3,7 @@ void init_batch_verifier(char* params_dir, char* assets_dir);
|
||||
char* get_batch_vk();
|
||||
char* check_chunk_proofs(char* chunk_proofs);
|
||||
char* gen_batch_proof(char* chunk_hashes, char* chunk_proofs);
|
||||
char verify_batch_proof(char* proof, char* fork_name);
|
||||
char verify_batch_proof(char* proof);
|
||||
|
||||
void init_chunk_prover(char* params_dir, char* assets_dir);
|
||||
void init_chunk_verifier(char* params_dir, char* assets_dir);
|
||||
|
||||
@@ -1,229 +0,0 @@
|
||||
package testcontainers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/ethclient"
|
||||
"github.com/testcontainers/testcontainers-go"
|
||||
"github.com/testcontainers/testcontainers-go/modules/compose"
|
||||
"github.com/testcontainers/testcontainers-go/modules/postgres"
|
||||
"github.com/testcontainers/testcontainers-go/wait"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"scroll-tech/common/database"
|
||||
)
|
||||
|
||||
// TestcontainerApps testcontainers struct
|
||||
type TestcontainerApps struct {
|
||||
postgresContainer *postgres.PostgresContainer
|
||||
l2GethContainer *testcontainers.DockerContainer
|
||||
poSL1Container compose.ComposeStack
|
||||
|
||||
// common time stamp in nanoseconds.
|
||||
Timestamp int
|
||||
}
|
||||
|
||||
// NewTestcontainerApps returns new instance of TestcontainerApps struct
|
||||
func NewTestcontainerApps() *TestcontainerApps {
|
||||
timestamp := time.Now().Nanosecond()
|
||||
// In order to solve the problem of "creating reaper failed: failed to create container"
|
||||
// refer to https://github.com/testcontainers/testcontainers-go/issues/2172
|
||||
if err := os.Setenv("TESTCONTAINERS_RYUK_DISABLED", "true"); err != nil {
|
||||
panic("set env failed: " + err.Error())
|
||||
}
|
||||
return &TestcontainerApps{
|
||||
Timestamp: timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
// StartPostgresContainer starts a postgres container
|
||||
func (t *TestcontainerApps) StartPostgresContainer() error {
|
||||
if t.postgresContainer != nil && t.postgresContainer.IsRunning() {
|
||||
return nil
|
||||
}
|
||||
postgresContainer, err := postgres.RunContainer(context.Background(),
|
||||
testcontainers.WithImage("postgres"),
|
||||
postgres.WithDatabase("test_db"),
|
||||
postgres.WithPassword("123456"),
|
||||
testcontainers.WithWaitStrategy(
|
||||
wait.ForLog("database system is ready to accept connections").WithOccurrence(2).WithStartupTimeout(5*time.Second)),
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("failed to start postgres container: %s", err)
|
||||
return err
|
||||
}
|
||||
t.postgresContainer = postgresContainer
|
||||
return nil
|
||||
}
|
||||
|
||||
// StartL2GethContainer starts a L2Geth container
|
||||
func (t *TestcontainerApps) StartL2GethContainer() error {
|
||||
if t.l2GethContainer != nil && t.l2GethContainer.IsRunning() {
|
||||
return nil
|
||||
}
|
||||
req := testcontainers.ContainerRequest{
|
||||
Image: "scroll_l2geth",
|
||||
ExposedPorts: []string{"8546/tcp", "8545/tcp"},
|
||||
WaitingFor: wait.ForAll(
|
||||
wait.ForListeningPort("8546").WithStartupTimeout(100*time.Second),
|
||||
wait.ForListeningPort("8545").WithStartupTimeout(100*time.Second),
|
||||
),
|
||||
}
|
||||
genericContainerReq := testcontainers.GenericContainerRequest{
|
||||
ContainerRequest: req,
|
||||
Started: true,
|
||||
}
|
||||
container, err := testcontainers.GenericContainer(context.Background(), genericContainerReq)
|
||||
if err != nil {
|
||||
log.Printf("failed to start scroll_l2geth container: %s", err)
|
||||
return err
|
||||
}
|
||||
t.l2GethContainer, _ = container.(*testcontainers.DockerContainer)
|
||||
return nil
|
||||
}
|
||||
|
||||
// StartPoSL1Container starts the PoS L1 container by running the associated Docker Compose configuration
|
||||
func (t *TestcontainerApps) StartPoSL1Container() error {
|
||||
var (
|
||||
err error
|
||||
rootDir string
|
||||
dockerComposeFile string
|
||||
)
|
||||
|
||||
if rootDir, err = findProjectRootDir(); err != nil {
|
||||
return fmt.Errorf("failed to find project root directory: %v", err)
|
||||
}
|
||||
|
||||
dockerComposeFile = filepath.Join(rootDir, "common", "testcontainers", "docker-compose.yml")
|
||||
|
||||
if t.poSL1Container, err = compose.NewDockerCompose([]string{dockerComposeFile}...); err != nil {
|
||||
return err
|
||||
}
|
||||
err = t.poSL1Container.WaitForService("geth", wait.NewHTTPStrategy("/").
|
||||
WithPort("8545/tcp").
|
||||
WithStartupTimeout(15*time.Second)).
|
||||
Up(context.Background())
|
||||
if err != nil {
|
||||
t.poSL1Container = nil
|
||||
return fmt.Errorf("failed to start PoS L1 container: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPoSL1EndPoint returns the endpoint of the running PoS L1 endpoint
|
||||
func (t *TestcontainerApps) GetPoSL1EndPoint() (string, error) {
|
||||
if t.poSL1Container == nil {
|
||||
return "", fmt.Errorf("PoS L1 container is not running")
|
||||
}
|
||||
contrainer, err := t.poSL1Container.ServiceContainer(context.Background(), "geth")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return contrainer.PortEndpoint(context.Background(), "8545/tcp", "http")
|
||||
}
|
||||
|
||||
// GetPoSL1Client returns a ethclient by dialing running PoS L1 client
|
||||
func (t *TestcontainerApps) GetPoSL1Client() (*ethclient.Client, error) {
|
||||
endpoint, err := t.GetPoSL1EndPoint()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ethclient.Dial(endpoint)
|
||||
}
|
||||
|
||||
// GetDBEndPoint returns the endpoint of the running postgres container
|
||||
func (t *TestcontainerApps) GetDBEndPoint() (string, error) {
|
||||
if t.postgresContainer == nil || !t.postgresContainer.IsRunning() {
|
||||
return "", fmt.Errorf("postgres is not running")
|
||||
}
|
||||
return t.postgresContainer.ConnectionString(context.Background(), "sslmode=disable")
|
||||
}
|
||||
|
||||
// GetL2GethEndPoint returns the endpoint of the running L2Geth container
|
||||
func (t *TestcontainerApps) GetL2GethEndPoint() (string, error) {
|
||||
if t.l2GethContainer == nil || !t.l2GethContainer.IsRunning() {
|
||||
return "", fmt.Errorf("l2 geth is not running")
|
||||
}
|
||||
endpoint, err := t.l2GethContainer.PortEndpoint(context.Background(), "8546/tcp", "ws")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return endpoint, nil
|
||||
}
|
||||
|
||||
// GetGormDBClient returns a gorm.DB by connecting to the running postgres container
|
||||
func (t *TestcontainerApps) GetGormDBClient() (*gorm.DB, error) {
|
||||
endpoint, err := t.GetDBEndPoint()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dbCfg := &database.Config{
|
||||
DSN: endpoint,
|
||||
DriverName: "postgres",
|
||||
MaxOpenNum: 200,
|
||||
MaxIdleNum: 20,
|
||||
}
|
||||
return database.InitDB(dbCfg)
|
||||
}
|
||||
|
||||
// GetL2GethClient returns a ethclient by dialing running L2Geth
|
||||
func (t *TestcontainerApps) GetL2GethClient() (*ethclient.Client, error) {
|
||||
endpoint, err := t.GetL2GethEndPoint()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client, err := ethclient.Dial(endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// Free stops all running containers
|
||||
func (t *TestcontainerApps) Free() {
|
||||
ctx := context.Background()
|
||||
if t.postgresContainer != nil && t.postgresContainer.IsRunning() {
|
||||
if err := t.postgresContainer.Terminate(ctx); err != nil {
|
||||
log.Printf("failed to stop postgres container: %s", err)
|
||||
}
|
||||
}
|
||||
if t.l2GethContainer != nil && t.l2GethContainer.IsRunning() {
|
||||
if err := t.l2GethContainer.Terminate(ctx); err != nil {
|
||||
log.Printf("failed to stop scroll_l2geth container: %s", err)
|
||||
}
|
||||
}
|
||||
if t.poSL1Container != nil {
|
||||
if err := t.poSL1Container.Down(context.Background(), compose.RemoveOrphans(true), compose.RemoveVolumes(true), compose.RemoveImagesLocal); err != nil {
|
||||
log.Printf("failed to stop PoS L1 container: %s", err)
|
||||
} else {
|
||||
t.poSL1Container = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// findProjectRootDir find project root directory
|
||||
func findProjectRootDir() (string, error) {
|
||||
currentDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get working directory: %w", err)
|
||||
}
|
||||
|
||||
for {
|
||||
_, err := os.Stat(filepath.Join(currentDir, "go.work"))
|
||||
if err == nil {
|
||||
return currentDir, nil
|
||||
}
|
||||
|
||||
parentDir := filepath.Dir(currentDir)
|
||||
if parentDir == currentDir {
|
||||
return "", fmt.Errorf("go.work file not found in any parent directory")
|
||||
}
|
||||
|
||||
currentDir = parentDir
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package testcontainers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/ethclient"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// TestNewTestcontainerApps tests NewTestcontainerApps
|
||||
func TestNewTestcontainerApps(t *testing.T) {
|
||||
var (
|
||||
err error
|
||||
endpoint string
|
||||
gormDBclient *gorm.DB
|
||||
ethclient *ethclient.Client
|
||||
)
|
||||
|
||||
testApps := NewTestcontainerApps()
|
||||
|
||||
// test start testcontainers
|
||||
assert.NoError(t, testApps.StartPostgresContainer())
|
||||
endpoint, err = testApps.GetDBEndPoint()
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, endpoint)
|
||||
gormDBclient, err = testApps.GetGormDBClient()
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, gormDBclient)
|
||||
|
||||
assert.NoError(t, testApps.StartL2GethContainer())
|
||||
endpoint, err = testApps.GetL2GethEndPoint()
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, endpoint)
|
||||
ethclient, err = testApps.GetL2GethClient()
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, ethclient)
|
||||
|
||||
assert.NoError(t, testApps.StartPoSL1Container())
|
||||
endpoint, err = testApps.GetPoSL1EndPoint()
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, endpoint)
|
||||
ethclient, err = testApps.GetPoSL1Client()
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, ethclient)
|
||||
|
||||
// test free testcontainers
|
||||
testApps.Free()
|
||||
endpoint, err = testApps.GetDBEndPoint()
|
||||
assert.EqualError(t, err, "postgres is not running")
|
||||
assert.Empty(t, endpoint)
|
||||
|
||||
endpoint, err = testApps.GetL2GethEndPoint()
|
||||
assert.EqualError(t, err, "l2 geth is not running")
|
||||
assert.Empty(t, endpoint)
|
||||
|
||||
endpoint, err = testApps.GetPoSL1EndPoint()
|
||||
assert.EqualError(t, err, "PoS L1 container is not running")
|
||||
assert.Empty(t, endpoint)
|
||||
}
|
||||
@@ -302,6 +302,8 @@ func GetMemoryExpansionCost(memoryByteSize uint64) uint64 {
|
||||
}
|
||||
|
||||
// EstimateBlockL1CommitCalldataSize calculates the calldata size in l1 commit for this block approximately.
|
||||
// TODO: The calculation could be more accurate by using 58 + len(l2TxDataBytes) (see Chunk).
|
||||
// This needs to be adjusted in the future.
|
||||
func EstimateBlockL1CommitCalldataSize(b *encoding.Block) (uint64, error) {
|
||||
var size uint64
|
||||
for _, txData := range b.Transactions {
|
||||
@@ -440,9 +442,9 @@ func EstimateBatchL1CommitGas(b *encoding.Batch) (uint64, error) {
|
||||
}
|
||||
|
||||
// EstimateBatchL1CommitCalldataSize calculates the calldata size in l1 commit for this batch approximately.
|
||||
func EstimateBatchL1CommitCalldataSize(b *encoding.Batch) (uint64, error) {
|
||||
func EstimateBatchL1CommitCalldataSize(c *encoding.Batch) (uint64, error) {
|
||||
var totalL1CommitCalldataSize uint64
|
||||
for _, chunk := range b.Chunks {
|
||||
for _, chunk := range c.Chunks {
|
||||
chunkL1CommitCalldataSize, err := EstimateChunkL1CommitCalldataSize(chunk)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
||||
@@ -210,10 +210,6 @@ func NewDABatch(batch *encoding.Batch) (*DABatch, error) {
|
||||
return nil, fmt.Errorf("too many chunks in batch")
|
||||
}
|
||||
|
||||
if len(batch.Chunks) == 0 {
|
||||
return nil, fmt.Errorf("too few chunks in batch")
|
||||
}
|
||||
|
||||
// batch data hash
|
||||
dataHash, err := computeBatchDataHash(batch.Chunks, batch.TotalL1MessagePoppedBefore)
|
||||
if err != nil {
|
||||
@@ -227,11 +223,18 @@ func NewDABatch(batch *encoding.Batch) (*DABatch, error) {
|
||||
}
|
||||
|
||||
// blob payload
|
||||
blob, blobVersionedHash, z, err := constructBlobPayload(batch.Chunks)
|
||||
blob, z, err := constructBlobPayload(batch.Chunks)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// blob versioned hash
|
||||
c, err := kzg4844.BlobToCommitment(*blob)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create blob commitment")
|
||||
}
|
||||
blobVersionedHash := kzg4844.CalcBlobHashV1(sha256.New(), &c)
|
||||
|
||||
daBatch := DABatch{
|
||||
Version: CodecV1Version,
|
||||
BatchIndex: batch.Index,
|
||||
@@ -274,58 +277,59 @@ func computeBatchDataHash(chunks []*encoding.Chunk, totalL1MessagePoppedBefore u
|
||||
}
|
||||
|
||||
// constructBlobPayload constructs the 4844 blob payload.
|
||||
func constructBlobPayload(chunks []*encoding.Chunk) (*kzg4844.Blob, common.Hash, *kzg4844.Point, error) {
|
||||
func constructBlobPayload(chunks []*encoding.Chunk) (*kzg4844.Blob, *kzg4844.Point, error) {
|
||||
// metadata consists of num_chunks (2 bytes) and chunki_size (4 bytes per chunk)
|
||||
metadataLength := 2 + MaxNumChunks*4
|
||||
|
||||
// the raw (un-padded) blob payload
|
||||
blobBytes := make([]byte, metadataLength)
|
||||
|
||||
// the number of chunks that contain at least one L2 transaction
|
||||
numNonEmptyChunks := 0
|
||||
|
||||
// challenge digest preimage
|
||||
// 1 hash for metadata, 1 hash for each chunk, 1 hash for blob versioned hash
|
||||
challengePreimage := make([]byte, (1+MaxNumChunks+1)*32)
|
||||
// 1 hash for metadata and 1 for each chunk
|
||||
challengePreimage := make([]byte, (1+MaxNumChunks)*32)
|
||||
|
||||
// the chunk data hash used for calculating the challenge preimage
|
||||
var chunkDataHash common.Hash
|
||||
|
||||
// blob metadata: num_chunks
|
||||
binary.BigEndian.PutUint16(blobBytes[0:], uint16(len(chunks)))
|
||||
// the challenge point z
|
||||
var z kzg4844.Point
|
||||
|
||||
// encode blob metadata and L2 transactions,
|
||||
// and simultaneously also build challenge preimage
|
||||
for chunkID, chunk := range chunks {
|
||||
currentChunkStartIndex := len(blobBytes)
|
||||
hasL2Tx := false
|
||||
|
||||
for _, block := range chunk.Blocks {
|
||||
for _, tx := range block.Transactions {
|
||||
if tx.Type != types.L1MessageTxType {
|
||||
hasL2Tx = true
|
||||
// encode L2 txs into blob payload
|
||||
rlpTxData, err := encoding.ConvertTxDataToRLPEncoding(tx)
|
||||
if err != nil {
|
||||
return nil, common.Hash{}, nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
blobBytes = append(blobBytes, rlpTxData...)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// blob metadata: chunki_size
|
||||
if chunkSize := len(blobBytes) - currentChunkStartIndex; chunkSize != 0 {
|
||||
binary.BigEndian.PutUint32(blobBytes[2+4*chunkID:], uint32(chunkSize))
|
||||
chunkSize := len(blobBytes) - currentChunkStartIndex
|
||||
binary.BigEndian.PutUint32(blobBytes[2+4*chunkID:], uint32(chunkSize))
|
||||
|
||||
if hasL2Tx {
|
||||
numNonEmptyChunks++
|
||||
}
|
||||
|
||||
// challenge: compute chunk data hash
|
||||
chunkDataHash = crypto.Keccak256Hash(blobBytes[currentChunkStartIndex:])
|
||||
copy(challengePreimage[32+chunkID*32:], chunkDataHash[:])
|
||||
hash := crypto.Keccak256Hash(blobBytes[currentChunkStartIndex:])
|
||||
copy(challengePreimage[32+chunkID*32:], hash[:])
|
||||
}
|
||||
|
||||
// if we have fewer than MaxNumChunks chunks, the rest
|
||||
// of the blob metadata is correctly initialized to 0,
|
||||
// but we need to add padding to the challenge preimage
|
||||
for chunkID := len(chunks); chunkID < MaxNumChunks; chunkID++ {
|
||||
// use the last chunk's data hash as padding
|
||||
copy(challengePreimage[32+chunkID*32:], chunkDataHash[:])
|
||||
}
|
||||
// blob metadata: num_chunks
|
||||
binary.BigEndian.PutUint16(blobBytes[0:], uint16(numNonEmptyChunks))
|
||||
|
||||
// challenge: compute metadata hash
|
||||
hash := crypto.Keccak256Hash(blobBytes[0:metadataLength])
|
||||
@@ -334,37 +338,22 @@ func constructBlobPayload(chunks []*encoding.Chunk) (*kzg4844.Blob, common.Hash,
|
||||
// convert raw data to BLSFieldElements
|
||||
blob, err := makeBlobCanonical(blobBytes)
|
||||
if err != nil {
|
||||
return nil, common.Hash{}, nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// compute blob versioned hash
|
||||
c, err := kzg4844.BlobToCommitment(blob)
|
||||
if err != nil {
|
||||
return nil, common.Hash{}, nil, fmt.Errorf("failed to create blob commitment")
|
||||
}
|
||||
blobVersionedHash := kzg4844.CalcBlobHashV1(sha256.New(), &c)
|
||||
|
||||
// challenge: append blob versioned hash
|
||||
copy(challengePreimage[(1+MaxNumChunks)*32:], blobVersionedHash[:])
|
||||
|
||||
// compute z = challenge_digest % BLS_MODULUS
|
||||
challengeDigest := crypto.Keccak256Hash(challengePreimage)
|
||||
pointBigInt := new(big.Int).Mod(new(big.Int).SetBytes(challengeDigest[:]), BLSModulus)
|
||||
pointBytes := pointBigInt.Bytes()
|
||||
challengeDigest := crypto.Keccak256Hash(challengePreimage[:])
|
||||
point := new(big.Int).Mod(new(big.Int).SetBytes(challengeDigest[:]), BLSModulus)
|
||||
copy(z[:], point.Bytes()[0:32])
|
||||
|
||||
// the challenge point z
|
||||
var z kzg4844.Point
|
||||
start := 32 - len(pointBytes)
|
||||
copy(z[start:], pointBytes)
|
||||
|
||||
return blob, blobVersionedHash, &z, nil
|
||||
return blob, &z, nil
|
||||
}
|
||||
|
||||
// makeBlobCanonical converts the raw blob data into the canonical blob representation of 4096 BLSFieldElements.
|
||||
func makeBlobCanonical(blobBytes []byte) (*kzg4844.Blob, error) {
|
||||
// blob contains 131072 bytes but we can only utilize 31/32 of these
|
||||
if len(blobBytes) > 126976 {
|
||||
return nil, fmt.Errorf("oversized batch payload, blob bytes length: %v, max length: %v", len(blobBytes), 126976)
|
||||
return nil, fmt.Errorf("oversized batch payload")
|
||||
}
|
||||
|
||||
// the canonical (padded) blob payload
|
||||
@@ -435,12 +424,12 @@ func (b *DABatch) BlobDataProof() ([]byte, error) {
|
||||
return nil, errors.New("called BlobDataProof with empty z")
|
||||
}
|
||||
|
||||
commitment, err := kzg4844.BlobToCommitment(b.blob)
|
||||
commitment, err := kzg4844.BlobToCommitment(*b.blob)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create blob commitment")
|
||||
}
|
||||
|
||||
proof, y, err := kzg4844.ComputeProof(b.blob, *b.z)
|
||||
proof, y, err := kzg4844.ComputeProof(*b.blob, *b.z)
|
||||
if err != nil {
|
||||
log.Crit("failed to create KZG proof at point", "err", err, "z", hex.EncodeToString(b.z[:]))
|
||||
}
|
||||
@@ -454,184 +443,8 @@ func (b *DABatch) BlobDataProof() ([]byte, error) {
|
||||
return BlobDataProofArgs.Pack(values...)
|
||||
}
|
||||
|
||||
// Blob returns the blob of the batch.
|
||||
func (b *DABatch) Blob() *kzg4844.Blob {
|
||||
return b.blob
|
||||
}
|
||||
|
||||
// DecodeFromCalldata attempts to decode a DABatch and an array of DAChunks from the provided calldata byte slice.
|
||||
func DecodeFromCalldata(data []byte) (*DABatch, []*DAChunk, error) {
|
||||
// TODO: implement this function.
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
// EstimateChunkL1CommitBlobSize estimates the size of the L1 commit blob for a single chunk.
|
||||
func EstimateChunkL1CommitBlobSize(c *encoding.Chunk) (uint64, error) {
|
||||
metadataSize := uint64(2 + 4*MaxNumChunks) // over-estimate: adding metadata length
|
||||
chunkDataSize, err := chunkL1CommitBlobDataSize(c)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return calculatePaddedBlobSize(metadataSize + chunkDataSize), nil
|
||||
}
|
||||
|
||||
// EstimateBatchL1CommitBlobSize estimates the total size of the L1 commit blob for a batch.
|
||||
func EstimateBatchL1CommitBlobSize(b *encoding.Batch) (uint64, error) {
|
||||
metadataSize := uint64(2 + 4*MaxNumChunks)
|
||||
var batchDataSize uint64
|
||||
for _, c := range b.Chunks {
|
||||
chunkDataSize, err := chunkL1CommitBlobDataSize(c)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
batchDataSize += chunkDataSize
|
||||
}
|
||||
return calculatePaddedBlobSize(metadataSize + batchDataSize), nil
|
||||
}
|
||||
|
||||
func chunkL1CommitBlobDataSize(c *encoding.Chunk) (uint64, error) {
|
||||
var dataSize uint64
|
||||
for _, block := range c.Blocks {
|
||||
for _, tx := range block.Transactions {
|
||||
if tx.Type != types.L1MessageTxType {
|
||||
rlpTxData, err := encoding.ConvertTxDataToRLPEncoding(tx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
dataSize += uint64(len(rlpTxData))
|
||||
}
|
||||
}
|
||||
}
|
||||
return dataSize, nil
|
||||
}
|
||||
|
||||
// CalldataNonZeroByteGas is the gas consumption per non zero byte in calldata.
|
||||
const CalldataNonZeroByteGas = 16
|
||||
|
||||
// GetKeccak256Gas calculates the gas cost for computing the keccak256 hash of a given size.
|
||||
func GetKeccak256Gas(size uint64) uint64 {
|
||||
return GetMemoryExpansionCost(size) + 30 + 6*((size+31)/32)
|
||||
}
|
||||
|
||||
// GetMemoryExpansionCost calculates the cost of memory expansion for a given memoryByteSize.
|
||||
func GetMemoryExpansionCost(memoryByteSize uint64) uint64 {
|
||||
memorySizeWord := (memoryByteSize + 31) / 32
|
||||
memoryCost := (memorySizeWord*memorySizeWord)/512 + (3 * memorySizeWord)
|
||||
return memoryCost
|
||||
}
|
||||
|
||||
// EstimateBlockL1CommitGas calculates the total L1 commit gas for this block approximately.
|
||||
func EstimateBlockL1CommitGas(b *encoding.Block) uint64 {
|
||||
var total uint64
|
||||
var numL1Messages uint64
|
||||
for _, txData := range b.Transactions {
|
||||
if txData.Type == types.L1MessageTxType {
|
||||
numL1Messages++
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// 60 bytes BlockContext calldata
|
||||
total += CalldataNonZeroByteGas * 60
|
||||
|
||||
// sload
|
||||
total += 2100 * numL1Messages // numL1Messages times cold sload in L1MessageQueue
|
||||
|
||||
// staticcall
|
||||
total += 100 * numL1Messages // numL1Messages times call to L1MessageQueue
|
||||
total += 100 * numL1Messages // numL1Messages times warm address access to L1MessageQueue
|
||||
|
||||
total += GetMemoryExpansionCost(36) * numL1Messages // staticcall to proxy
|
||||
total += 100 * numL1Messages // read admin in proxy
|
||||
total += 100 * numL1Messages // read impl in proxy
|
||||
total += 100 * numL1Messages // access impl
|
||||
total += GetMemoryExpansionCost(36) * numL1Messages // delegatecall to impl
|
||||
|
||||
return total
|
||||
}
|
||||
|
||||
// EstimateChunkL1CommitCalldataSize calculates the calldata size needed for committing a chunk to L1 approximately.
|
||||
func EstimateChunkL1CommitCalldataSize(c *encoding.Chunk) uint64 {
|
||||
return uint64(60 * len(c.Blocks))
|
||||
}
|
||||
|
||||
// EstimateChunkL1CommitGas calculates the total L1 commit gas for this chunk approximately.
|
||||
func EstimateChunkL1CommitGas(c *encoding.Chunk) uint64 {
|
||||
var totalNonSkippedL1Messages uint64
|
||||
var totalL1CommitGas uint64
|
||||
for _, block := range c.Blocks {
|
||||
totalNonSkippedL1Messages += uint64(len(block.Transactions)) - block.NumL2Transactions()
|
||||
blockL1CommitGas := EstimateBlockL1CommitGas(block)
|
||||
totalL1CommitGas += blockL1CommitGas
|
||||
}
|
||||
|
||||
numBlocks := uint64(len(c.Blocks))
|
||||
totalL1CommitGas += 100 * numBlocks // numBlocks times warm sload
|
||||
totalL1CommitGas += CalldataNonZeroByteGas // numBlocks field of chunk encoding in calldata
|
||||
totalL1CommitGas += CalldataNonZeroByteGas * numBlocks * 60 // numBlocks of BlockContext in chunk
|
||||
|
||||
totalL1CommitGas += GetKeccak256Gas(58*numBlocks + 32*totalNonSkippedL1Messages) // chunk hash
|
||||
return totalL1CommitGas
|
||||
}
|
||||
|
||||
// EstimateBatchL1CommitGas calculates the total L1 commit gas for this batch approximately.
|
||||
func EstimateBatchL1CommitGas(b *encoding.Batch) uint64 {
|
||||
var totalL1CommitGas uint64
|
||||
|
||||
// Add extra gas costs
|
||||
totalL1CommitGas += 100000 // constant to account for ops like _getAdmin, _implementation, _requireNotPaused, etc
|
||||
totalL1CommitGas += 4 * 2100 // 4 one-time cold sload for commitBatch
|
||||
totalL1CommitGas += 20000 // 1 time sstore
|
||||
totalL1CommitGas += 21000 // base fee for tx
|
||||
totalL1CommitGas += CalldataNonZeroByteGas // version in calldata
|
||||
|
||||
// adjusting gas:
|
||||
// add 1 time cold sload (2100 gas) for L1MessageQueue
|
||||
// add 1 time cold address access (2600 gas) for L1MessageQueue
|
||||
// minus 1 time warm sload (100 gas) & 1 time warm address access (100 gas)
|
||||
totalL1CommitGas += (2100 + 2600 - 100 - 100)
|
||||
totalL1CommitGas += GetKeccak256Gas(89 + 32) // parent batch header hash, length is estimated as 89 (constant part)+ 32 (1 skippedL1MessageBitmap)
|
||||
totalL1CommitGas += CalldataNonZeroByteGas * (89 + 32) // parent batch header in calldata
|
||||
|
||||
// adjust batch data hash gas cost
|
||||
totalL1CommitGas += GetKeccak256Gas(uint64(32 * len(b.Chunks)))
|
||||
|
||||
totalL1MessagePoppedBefore := b.TotalL1MessagePoppedBefore
|
||||
|
||||
for _, chunk := range b.Chunks {
|
||||
chunkL1CommitGas := EstimateChunkL1CommitGas(chunk)
|
||||
totalL1CommitGas += chunkL1CommitGas
|
||||
|
||||
totalL1MessagePoppedInChunk := chunk.NumL1Messages(totalL1MessagePoppedBefore)
|
||||
totalL1MessagePoppedBefore += totalL1MessagePoppedInChunk
|
||||
|
||||
totalL1CommitGas += CalldataNonZeroByteGas * (32 * (totalL1MessagePoppedInChunk + 255) / 256)
|
||||
totalL1CommitGas += GetKeccak256Gas(89 + 32*(totalL1MessagePoppedInChunk+255)/256)
|
||||
|
||||
totalL1CommitCalldataSize := EstimateChunkL1CommitCalldataSize(chunk)
|
||||
totalL1CommitGas += GetMemoryExpansionCost(totalL1CommitCalldataSize)
|
||||
}
|
||||
|
||||
return totalL1CommitGas
|
||||
}
|
||||
|
||||
// EstimateBatchL1CommitCalldataSize calculates the calldata size in l1 commit for this batch approximately.
|
||||
func EstimateBatchL1CommitCalldataSize(b *encoding.Batch) uint64 {
|
||||
var totalL1CommitCalldataSize uint64
|
||||
for _, chunk := range b.Chunks {
|
||||
totalL1CommitCalldataSize += EstimateChunkL1CommitCalldataSize(chunk)
|
||||
}
|
||||
return totalL1CommitCalldataSize
|
||||
}
|
||||
|
||||
// calculatePaddedBlobSize calculates the required size on blob storage
|
||||
// where every 32 bytes can store only 31 bytes of actual data, with the first byte being zero.
|
||||
func calculatePaddedBlobSize(dataSize uint64) uint64 {
|
||||
paddedSize := (dataSize / 31) * 32
|
||||
|
||||
if dataSize%31 != 0 {
|
||||
paddedSize += 1 + dataSize%31 // Add 1 byte for the first empty byte plus the remainder bytes
|
||||
}
|
||||
|
||||
return paddedSize
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -6,30 +6,8 @@ import (
|
||||
"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/log"
|
||||
)
|
||||
|
||||
// CodecVersion defines the version of encoder and decoder.
|
||||
type CodecVersion int
|
||||
|
||||
const (
|
||||
// CodecV0 represents the version 0 of the encoder and decoder.
|
||||
CodecV0 CodecVersion = iota
|
||||
|
||||
// CodecV1 represents the version 1 of the encoder and decoder.
|
||||
CodecV1
|
||||
|
||||
// txTypeTest is a special transaction type used in unit tests.
|
||||
txTypeTest = 0xff
|
||||
)
|
||||
|
||||
func init() {
|
||||
// make sure txTypeTest will not interfere with other transaction types
|
||||
if txTypeTest == types.LegacyTxType || txTypeTest == types.AccessListTxType || txTypeTest == types.DynamicFeeTxType || txTypeTest == types.BlobTxType || txTypeTest == types.L1MessageTxType {
|
||||
log.Crit("txTypeTest is overlapping with existing transaction types")
|
||||
}
|
||||
}
|
||||
|
||||
// Block represents an L2 block.
|
||||
type Block struct {
|
||||
Header *types.Header
|
||||
@@ -145,10 +123,6 @@ func ConvertTxDataToRLPEncoding(txData *types.TransactionData) ([]byte, error) {
|
||||
S: txData.S.ToInt(),
|
||||
})
|
||||
|
||||
case txTypeTest:
|
||||
// in the tests, we simply use `data` as the RLP-encoded transaction
|
||||
return data, nil
|
||||
|
||||
case types.L1MessageTxType: // L1MessageTxType is not supported
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported tx type: %d", txData.Type)
|
||||
@@ -235,3 +209,8 @@ func (b *Batch) WithdrawRoot() common.Hash {
|
||||
lastChunkBlockNum := len(b.Chunks[numChunks-1].Blocks)
|
||||
return b.Chunks[len(b.Chunks)-1].Blocks[lastChunkBlockNum-1].WithdrawRoot
|
||||
}
|
||||
|
||||
// NumChunks gets the number of chunks of the batch.
|
||||
func (b *Batch) NumChunks() uint64 {
|
||||
return uint64(len(b.Chunks))
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ func TestUtilFunctions(t *testing.T) {
|
||||
assert.Equal(t, uint64(240000), chunk3.L2GasUsed())
|
||||
|
||||
// Test Batch methods
|
||||
assert.Equal(t, uint64(3), batch.NumChunks())
|
||||
assert.Equal(t, block6.Header.Root, batch.StateRoot())
|
||||
assert.Equal(t, block6.WithdrawRoot, batch.WithdrawRoot())
|
||||
}
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
||||
"github.com/scroll-tech/go-ethereum/crypto"
|
||||
"github.com/scroll-tech/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
// AuthMsg is the first message exchanged from the Prover to the Sequencer.
|
||||
// It effectively acts as a registration, and makes the Prover identification
|
||||
// known to the Sequencer.
|
||||
type AuthMsg struct {
|
||||
// Message fields
|
||||
Identity *Identity `json:"message"`
|
||||
// Prover signature
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
|
||||
// Identity contains all the fields to be signed by the prover.
|
||||
type Identity struct {
|
||||
// ProverName the prover name
|
||||
ProverName string `json:"prover_name"`
|
||||
// ProverVersion the prover version
|
||||
ProverVersion string `json:"prover_version"`
|
||||
// Challenge unique challenge generated by manager
|
||||
Challenge string `json:"challenge"`
|
||||
// HardForkName the hard fork name
|
||||
HardForkName string `json:"hard_fork_name"`
|
||||
}
|
||||
|
||||
// SignWithKey auth message with private key and set public key in auth message's Identity
|
||||
func (a *AuthMsg) SignWithKey(priv *ecdsa.PrivateKey) error {
|
||||
// Hash identity content
|
||||
hash, err := a.Identity.Hash()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Sign register message
|
||||
sig, err := crypto.Sign(hash, priv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.Signature = hexutil.Encode(sig)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Verify verifies the message of auth.
|
||||
func (a *AuthMsg) Verify() (bool, error) {
|
||||
hash, err := a.Identity.Hash()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
sig := common.FromHex(a.Signature)
|
||||
|
||||
pk, err := crypto.SigToPub(hash, sig)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return crypto.VerifySignature(crypto.CompressPubkey(pk), hash, sig[:len(sig)-1]), nil
|
||||
}
|
||||
|
||||
// PublicKey return public key from signature
|
||||
func (a *AuthMsg) PublicKey() (string, error) {
|
||||
hash, err := a.Identity.Hash()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
sig := common.FromHex(a.Signature)
|
||||
// recover public key
|
||||
pk, err := crypto.SigToPub(hash, sig)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return common.Bytes2Hex(crypto.CompressPubkey(pk)), nil
|
||||
}
|
||||
|
||||
// Hash returns the hash of the auth message, which should be the message used
|
||||
// to construct the Signature.
|
||||
func (i *Identity) Hash() ([]byte, error) {
|
||||
byt, err := rlp.EncodeToBytes(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hash := crypto.Keccak256Hash(byt)
|
||||
return hash[:], nil
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
||||
"github.com/scroll-tech/go-ethereum/crypto"
|
||||
"github.com/scroll-tech/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
// LegacyAuthMsg is the old auth message exchanged from the Prover to the Sequencer.
|
||||
// It effectively acts as a registration, and makes the Prover identification
|
||||
// known to the Sequencer.
|
||||
type LegacyAuthMsg struct {
|
||||
// Message fields
|
||||
Identity *LegacyIdentity `json:"message"`
|
||||
// Prover signature
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
|
||||
// LegacyIdentity contains all the fields to be signed by the prover.
|
||||
type LegacyIdentity struct {
|
||||
// ProverName the prover name
|
||||
ProverName string `json:"prover_name"`
|
||||
// ProverVersion the prover version
|
||||
ProverVersion string `json:"prover_version"`
|
||||
// Challenge unique challenge generated by manager
|
||||
Challenge string `json:"challenge"`
|
||||
}
|
||||
|
||||
// SignWithKey auth message with private key and set public key in auth message's Identity
|
||||
func (a *LegacyAuthMsg) SignWithKey(priv *ecdsa.PrivateKey) error {
|
||||
// Hash identity content
|
||||
hash, err := a.Identity.Hash()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Sign register message
|
||||
sig, err := crypto.Sign(hash, priv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.Signature = hexutil.Encode(sig)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Verify verifies the message of auth.
|
||||
func (a *LegacyAuthMsg) Verify() (bool, error) {
|
||||
hash, err := a.Identity.Hash()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
sig := common.FromHex(a.Signature)
|
||||
|
||||
pk, err := crypto.SigToPub(hash, sig)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return crypto.VerifySignature(crypto.CompressPubkey(pk), hash, sig[:len(sig)-1]), nil
|
||||
}
|
||||
|
||||
// PublicKey return public key from signature
|
||||
func (a *LegacyAuthMsg) PublicKey() (string, error) {
|
||||
hash, err := a.Identity.Hash()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
sig := common.FromHex(a.Signature)
|
||||
// recover public key
|
||||
pk, err := crypto.SigToPub(hash, sig)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return common.Bytes2Hex(crypto.CompressPubkey(pk)), nil
|
||||
}
|
||||
|
||||
// Hash returns the hash of the auth message, which should be the message used
|
||||
// to construct the Signature.
|
||||
func (i *LegacyIdentity) Hash() ([]byte, error) {
|
||||
byt, err := rlp.EncodeToBytes(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hash := crypto.Keccak256Hash(byt)
|
||||
return hash[:], nil
|
||||
}
|
||||
@@ -58,6 +58,26 @@ const (
|
||||
ProofTypeBatch
|
||||
)
|
||||
|
||||
// AuthMsg is the first message exchanged from the Prover to the Sequencer.
|
||||
// It effectively acts as a registration, and makes the Prover identification
|
||||
// known to the Sequencer.
|
||||
type AuthMsg struct {
|
||||
// Message fields
|
||||
Identity *Identity `json:"message"`
|
||||
// Prover signature
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
|
||||
// Identity contains all the fields to be signed by the prover.
|
||||
type Identity struct {
|
||||
// ProverName the prover name
|
||||
ProverName string `json:"prover_name"`
|
||||
// ProverVersion the prover version
|
||||
ProverVersion string `json:"prover_version"`
|
||||
// Challenge unique challenge generated by manager
|
||||
Challenge string `json:"challenge"`
|
||||
}
|
||||
|
||||
// GenerateToken generates token
|
||||
func GenerateToken() (string, error) {
|
||||
b := make([]byte, 16)
|
||||
@@ -67,6 +87,65 @@ func GenerateToken() (string, error) {
|
||||
return hex.EncodeToString(b), nil
|
||||
}
|
||||
|
||||
// SignWithKey auth message with private key and set public key in auth message's Identity
|
||||
func (a *AuthMsg) SignWithKey(priv *ecdsa.PrivateKey) error {
|
||||
// Hash identity content
|
||||
hash, err := a.Identity.Hash()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Sign register message
|
||||
sig, err := crypto.Sign(hash, priv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.Signature = hexutil.Encode(sig)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Verify verifies the message of auth.
|
||||
func (a *AuthMsg) Verify() (bool, error) {
|
||||
hash, err := a.Identity.Hash()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
sig := common.FromHex(a.Signature)
|
||||
|
||||
pk, err := crypto.SigToPub(hash, sig)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return crypto.VerifySignature(crypto.CompressPubkey(pk), hash, sig[:len(sig)-1]), nil
|
||||
}
|
||||
|
||||
// PublicKey return public key from signature
|
||||
func (a *AuthMsg) PublicKey() (string, error) {
|
||||
hash, err := a.Identity.Hash()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
sig := common.FromHex(a.Signature)
|
||||
// recover public key
|
||||
pk, err := crypto.SigToPub(hash, sig)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return common.Bytes2Hex(crypto.CompressPubkey(pk)), nil
|
||||
}
|
||||
|
||||
// Hash returns the hash of the auth message, which should be the message used
|
||||
// to construct the Signature.
|
||||
func (i *Identity) Hash() ([]byte, error) {
|
||||
byt, err := rlp.EncodeToBytes(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hash := crypto.Keccak256Hash(byt)
|
||||
return hash[:], nil
|
||||
}
|
||||
|
||||
// ProofMsg is the data structure sent to the coordinator.
|
||||
type ProofMsg struct {
|
||||
*ProofDetail `json:"zkProof"`
|
||||
@@ -180,7 +259,6 @@ type ChunkInfo struct {
|
||||
WithdrawRoot common.Hash `json:"withdraw_root"`
|
||||
DataHash common.Hash `json:"data_hash"`
|
||||
IsPadding bool `json:"is_padding"`
|
||||
TxBytes []byte `json:"tx_bytes"`
|
||||
}
|
||||
|
||||
// ChunkProof includes the proof info that are required for chunk verification and rollup.
|
||||
|
||||
@@ -54,7 +54,7 @@ func TestIdentityHash(t *testing.T) {
|
||||
hash, err := identity.Hash()
|
||||
assert.NoError(t, err)
|
||||
|
||||
expectedHash := "9b8b00f5655411ec1d68ba1666261281c5414aedbda932e5b6a9f7f1b114fdf2"
|
||||
expectedHash := "83f5e0ad023e9c1de639ab07b9b4cb972ec9dbbd2524794c533a420a5b137721"
|
||||
assert.Equal(t, expectedHash, hex.EncodeToString(hash))
|
||||
}
|
||||
|
||||
|
||||
@@ -3,16 +3,11 @@ package utils
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/modern-go/reflect2"
|
||||
"github.com/scroll-tech/go-ethereum/core"
|
||||
)
|
||||
|
||||
// TryTimes try run several times until the function return true.
|
||||
@@ -64,17 +59,3 @@ func RandomURL() string {
|
||||
id, _ := rand.Int(rand.Reader, big.NewInt(5000-1))
|
||||
return fmt.Sprintf("localhost:%d", 10000+2000+id.Int64())
|
||||
}
|
||||
|
||||
// ReadGenesis parses and returns the genesis file at the given path
|
||||
func ReadGenesis(genesisPath string) (*core.Genesis, error) {
|
||||
file, err := os.Open(filepath.Clean(genesisPath))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
genesis := new(core.Genesis)
|
||||
if err := json.NewDecoder(file).Decode(genesis); err != nil {
|
||||
return nil, errors.Join(err, file.Close())
|
||||
}
|
||||
return genesis, file.Close()
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
// CheckScrollProverVersion check the "scroll-prover" version, if it's different from the local one, return false
|
||||
func CheckScrollProverVersion(proverVersion string) bool {
|
||||
// note the version is in fact in the format of "tag-commit-scroll_prover-halo2",
|
||||
// note the the version is in fact in the format of "tag-commit-scroll_prover-halo2",
|
||||
// so split-by-'-' length should be 4
|
||||
remote := strings.Split(proverVersion, "-")
|
||||
if len(remote) != 4 {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"runtime/debug"
|
||||
)
|
||||
|
||||
var tag = "v4.4.4"
|
||||
var tag = "v4.3.74"
|
||||
|
||||
var commit = func() string {
|
||||
if info, ok := debug.ReadBuildInfo(); ok {
|
||||
|
||||
@@ -67,7 +67,7 @@ Commit a batch of transactions on layer 1.
|
||||
function committedBatches(uint256) external view returns (bytes32)
|
||||
```
|
||||
|
||||
|
||||
Return the batch hash of a committed batch.
|
||||
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ function committedBatches(uint256) external view returns (bytes32)
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _0 | bytes32 | The batch hash of a committed batch. |
|
||||
| _0 | bytes32 | undefined |
|
||||
|
||||
### finalizeBatchWithProof
|
||||
|
||||
@@ -130,7 +130,7 @@ Finalize a committed batch (with blob) on layer 1.
|
||||
function finalizedStateRoots(uint256) external view returns (bytes32)
|
||||
```
|
||||
|
||||
|
||||
Return the state root of a committed batch.
|
||||
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ function finalizedStateRoots(uint256) external view returns (bytes32)
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _0 | bytes32 | The state root of a committed batch. |
|
||||
| _0 | bytes32 | undefined |
|
||||
|
||||
### importGenesisBatch
|
||||
|
||||
@@ -160,8 +160,8 @@ Import layer 2 genesis block
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _batchHeader | bytes | The header of the genesis batch. |
|
||||
| _stateRoot | bytes32 | The state root of the genesis block. |
|
||||
| _batchHeader | bytes | undefined |
|
||||
| _stateRoot | bytes32 | undefined |
|
||||
|
||||
### initialize
|
||||
|
||||
@@ -187,7 +187,7 @@ Initialize the storage of ScrollChain.
|
||||
function isBatchFinalized(uint256 _batchIndex) external view returns (bool)
|
||||
```
|
||||
|
||||
|
||||
Return whether the batch is finalized by batch index.
|
||||
|
||||
|
||||
|
||||
@@ -201,7 +201,7 @@ function isBatchFinalized(uint256 _batchIndex) external view returns (bool)
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _0 | bool | Whether the batch is finalized by batch index. |
|
||||
| _0 | bool | undefined |
|
||||
|
||||
### isProver
|
||||
|
||||
@@ -253,7 +253,7 @@ Whether an account is a sequencer.
|
||||
function lastFinalizedBatchIndex() external view returns (uint256)
|
||||
```
|
||||
|
||||
|
||||
The latest finalized batch index.
|
||||
|
||||
|
||||
|
||||
@@ -262,7 +262,7 @@ function lastFinalizedBatchIndex() external view returns (uint256)
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _0 | uint256 | The latest finalized batch index. |
|
||||
| _0 | uint256 | undefined |
|
||||
|
||||
### layer2ChainId
|
||||
|
||||
@@ -480,7 +480,7 @@ The address of RollupVerifier.
|
||||
function withdrawRoots(uint256) external view returns (bytes32)
|
||||
```
|
||||
|
||||
|
||||
Return the message root of a committed batch.
|
||||
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@ function withdrawRoots(uint256) external view returns (bytes32)
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _0 | bytes32 | The message root of a committed batch. |
|
||||
| _0 | bytes32 | undefined |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"test:hardhat": "npx hardhat test",
|
||||
"test:forge": "forge test -vvv --evm-version cancun",
|
||||
"test:forge": "forge test -vvv",
|
||||
"test": "yarn test:hardhat && yarn test:forge",
|
||||
"solhint": "./node_modules/.bin/solhint -f table 'src/**/*.sol'",
|
||||
"lint:sol": "./node_modules/.bin/prettier --write 'src/**/*.sol'",
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
/// @title IScrollChain
|
||||
/// @notice The interface for ScrollChain.
|
||||
interface IScrollChain {
|
||||
/**********
|
||||
* Events *
|
||||
@@ -45,23 +43,23 @@ interface IScrollChain {
|
||||
* Public View Functions *
|
||||
*************************/
|
||||
|
||||
/// @return The latest finalized batch index.
|
||||
/// @notice The latest finalized batch index.
|
||||
function lastFinalizedBatchIndex() external view returns (uint256);
|
||||
|
||||
/// @notice Return the batch hash of a committed batch.
|
||||
/// @param batchIndex The index of the batch.
|
||||
/// @return The batch hash of a committed batch.
|
||||
function committedBatches(uint256 batchIndex) external view returns (bytes32);
|
||||
|
||||
/// @notice Return the state root of a committed batch.
|
||||
/// @param batchIndex The index of the batch.
|
||||
/// @return The state root of a committed batch.
|
||||
function finalizedStateRoots(uint256 batchIndex) external view returns (bytes32);
|
||||
|
||||
/// @notice Return the message root of a committed batch.
|
||||
/// @param batchIndex The index of the batch.
|
||||
/// @return The message root of a committed batch.
|
||||
function withdrawRoots(uint256 batchIndex) external view returns (bytes32);
|
||||
|
||||
/// @notice Return whether the batch is finalized by batch index.
|
||||
/// @param batchIndex The index of the batch.
|
||||
/// @return Whether the batch is finalized by batch index.
|
||||
function isBatchFinalized(uint256 batchIndex) external view returns (bool);
|
||||
|
||||
/*****************************
|
||||
@@ -101,6 +99,18 @@ interface IScrollChain {
|
||||
bytes calldata aggrProof
|
||||
) external;
|
||||
|
||||
/// @notice Finalize a committed batch on layer 1 without providing proof.
|
||||
/// @param batchHeader The header of current batch, see the encoding in comments of `commitBatch.
|
||||
/// @param prevStateRoot The state root of parent batch.
|
||||
/// @param postStateRoot The state root of current batch.
|
||||
/// @param withdrawRoot The withdraw trie root of current batch.
|
||||
function finalizeBatch(
|
||||
bytes calldata batchHeader,
|
||||
bytes32 prevStateRoot,
|
||||
bytes32 postStateRoot,
|
||||
bytes32 withdrawRoot
|
||||
) external;
|
||||
|
||||
/// @notice Finalize a committed batch (with blob) on layer 1.
|
||||
///
|
||||
/// @dev Memory layout of `blobDataProof`:
|
||||
@@ -122,4 +132,18 @@ interface IScrollChain {
|
||||
bytes calldata blobDataProof,
|
||||
bytes calldata aggrProof
|
||||
) external;
|
||||
|
||||
/// @notice Finalize a committed batch (with blob) on layer 1 without providing proof.
|
||||
/// @param batchHeader The header of current batch, see the encoding in comments of `commitBatch`.
|
||||
/// @param prevStateRoot The state root of parent batch.
|
||||
/// @param postStateRoot The state root of current batch.
|
||||
/// @param withdrawRoot The withdraw trie root of current batch.
|
||||
/// @param blobDataProof The proof for blob data.
|
||||
function finalizeBatch4844(
|
||||
bytes calldata batchHeader,
|
||||
bytes32 prevStateRoot,
|
||||
bytes32 postStateRoot,
|
||||
bytes32 withdrawRoot,
|
||||
bytes calldata blobDataProof
|
||||
) external;
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@ import {IScrollChain} from "./IScrollChain.sol";
|
||||
import {IRollupVerifier} from "../../libraries/verifier/IRollupVerifier.sol";
|
||||
import {IZkEvmVerifier} from "../../libraries/verifier/IZkEvmVerifier.sol";
|
||||
|
||||
/// @title MultipleVersionRollupVerifier
|
||||
/// @notice Verifies aggregate zk proofs using the appropriate verifier.
|
||||
contract MultipleVersionRollupVerifier is IRollupVerifier, Ownable {
|
||||
/**********
|
||||
* Events *
|
||||
@@ -39,7 +37,7 @@ contract MultipleVersionRollupVerifier is IRollupVerifier, Ownable {
|
||||
*************/
|
||||
|
||||
/// @notice The address of ScrollChain contract.
|
||||
address public immutable scrollChain;
|
||||
address immutable scrollChain;
|
||||
|
||||
/***********
|
||||
* Structs *
|
||||
@@ -60,7 +58,7 @@ contract MultipleVersionRollupVerifier is IRollupVerifier, Ownable {
|
||||
/// The verifiers are sorted by batchIndex in increasing order.
|
||||
mapping(uint256 => Verifier[]) public legacyVerifiers;
|
||||
|
||||
/// @notice Mapping from verifier version to the latest used zkevm verifier.
|
||||
/// @notice Mapping from verifier version to the lastest used zkevm verifier.
|
||||
mapping(uint256 => Verifier) public latestVerifier;
|
||||
|
||||
/***************
|
||||
@@ -88,8 +86,6 @@ contract MultipleVersionRollupVerifier is IRollupVerifier, Ownable {
|
||||
*************************/
|
||||
|
||||
/// @notice Return the number of legacy verifiers.
|
||||
/// @param _version The version of legacy verifiers.
|
||||
/// @return The number of legacy verifiers.
|
||||
function legacyVerifiersLength(uint256 _version) external view returns (uint256) {
|
||||
return legacyVerifiers[_version].length;
|
||||
}
|
||||
@@ -97,7 +93,6 @@ contract MultipleVersionRollupVerifier is IRollupVerifier, Ownable {
|
||||
/// @notice Compute the verifier should be used for specific batch.
|
||||
/// @param _version The version of verifier to query.
|
||||
/// @param _batchIndex The batch index to query.
|
||||
/// @return The address of verifier.
|
||||
function getVerifier(uint256 _version, uint256 _batchIndex) public view returns (address) {
|
||||
// Normally, we will use the latest verifier.
|
||||
Verifier memory _verifier = latestVerifier[_version];
|
||||
@@ -149,7 +144,6 @@ contract MultipleVersionRollupVerifier is IRollupVerifier, Ownable {
|
||||
************************/
|
||||
|
||||
/// @notice Update the address of zkevm verifier.
|
||||
/// @param _version The version of the verifier.
|
||||
/// @param _startBatchIndex The start batch index when the verifier will be used.
|
||||
/// @param _verifier The address of new verifier.
|
||||
function updateVerifier(
|
||||
|
||||
@@ -115,11 +115,11 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain {
|
||||
*************/
|
||||
|
||||
/// @dev Address of the point evaluation precompile used for EIP-4844 blob verification.
|
||||
address private constant POINT_EVALUATION_PRECOMPILE_ADDR = address(0x0A);
|
||||
address constant POINT_EVALUATION_PRECOMPILE_ADDR = address(0x0A);
|
||||
|
||||
/// @dev BLS Modulus value defined in EIP-4844 and the magic value returned from a successful call to the
|
||||
/// point evaluation precompile
|
||||
uint256 private constant BLS_MODULUS = 52435875175126190479447740508185965837690552500527637822603658699938581184513;
|
||||
uint256 constant BLS_MODULUS = 52435875175126190479447740508185965837690552500527637822603658699938581184513;
|
||||
|
||||
/// @notice The chain id of the corresponding layer 2 chain.
|
||||
uint64 public immutable layer2ChainId;
|
||||
@@ -236,8 +236,6 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain {
|
||||
*****************************/
|
||||
|
||||
/// @notice Import layer 2 genesis block
|
||||
/// @param _batchHeader The header of the genesis batch.
|
||||
/// @param _stateRoot The state root of the genesis block.
|
||||
function importGenesisBatch(bytes calldata _batchHeader, bytes32 _stateRoot) external {
|
||||
// check genesis batch header length
|
||||
if (_stateRoot == bytes32(0)) revert ErrorStateRootIsZero();
|
||||
@@ -428,6 +426,45 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain {
|
||||
emit FinalizeBatch(_batchIndex, _batchHash, _postStateRoot, _withdrawRoot);
|
||||
}
|
||||
|
||||
/// @inheritdoc IScrollChain
|
||||
function finalizeBatch(
|
||||
bytes calldata _batchHeader,
|
||||
bytes32 _prevStateRoot,
|
||||
bytes32 _postStateRoot,
|
||||
bytes32 _withdrawRoot
|
||||
) external override OnlyProver whenNotPaused {
|
||||
if (_prevStateRoot == bytes32(0)) revert ErrorPreviousStateRootIsZero();
|
||||
if (_postStateRoot == bytes32(0)) revert ErrorStateRootIsZero();
|
||||
|
||||
// compute batch hash and verify
|
||||
(uint256 memPtr, bytes32 _batchHash, uint256 _batchIndex, ) = _loadBatchHeader(_batchHeader);
|
||||
|
||||
// verify previous state root.
|
||||
if (finalizedStateRoots[_batchIndex - 1] != _prevStateRoot) revert ErrorIncorrectPreviousStateRoot();
|
||||
|
||||
// avoid duplicated verification
|
||||
if (finalizedStateRoots[_batchIndex] != bytes32(0)) revert ErrorBatchIsAlreadyVerified();
|
||||
|
||||
// check and update lastFinalizedBatchIndex
|
||||
unchecked {
|
||||
if (lastFinalizedBatchIndex + 1 != _batchIndex) revert ErrorIncorrectBatchIndex();
|
||||
lastFinalizedBatchIndex = _batchIndex;
|
||||
}
|
||||
|
||||
// record state root and withdraw root
|
||||
finalizedStateRoots[_batchIndex] = _postStateRoot;
|
||||
withdrawRoots[_batchIndex] = _withdrawRoot;
|
||||
|
||||
// Pop finalized and non-skipped message from L1MessageQueue.
|
||||
_popL1Messages(
|
||||
BatchHeaderV0Codec.getSkippedBitmapPtr(memPtr),
|
||||
BatchHeaderV0Codec.getTotalL1MessagePopped(memPtr),
|
||||
BatchHeaderV0Codec.getL1MessagePopped(memPtr)
|
||||
);
|
||||
|
||||
emit FinalizeBatch(_batchIndex, _batchHash, _postStateRoot, _withdrawRoot);
|
||||
}
|
||||
|
||||
/// @inheritdoc IScrollChain
|
||||
/// @dev Memory layout of `_blobDataProof`:
|
||||
/// ```text
|
||||
@@ -477,8 +514,7 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain {
|
||||
_postStateRoot,
|
||||
_withdrawRoot,
|
||||
_dataHash,
|
||||
_blobDataProof[0:64],
|
||||
_blobVersionedHash
|
||||
_blobDataProof[0:64]
|
||||
)
|
||||
);
|
||||
|
||||
@@ -510,6 +546,59 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain {
|
||||
emit FinalizeBatch(_batchIndex, _batchHash, _postStateRoot, _withdrawRoot);
|
||||
}
|
||||
|
||||
/// @inheritdoc IScrollChain
|
||||
function finalizeBatch4844(
|
||||
bytes calldata _batchHeader,
|
||||
bytes32 _prevStateRoot,
|
||||
bytes32 _postStateRoot,
|
||||
bytes32 _withdrawRoot,
|
||||
bytes calldata _blobDataProof
|
||||
) external override OnlyProver whenNotPaused {
|
||||
if (_prevStateRoot == bytes32(0)) revert ErrorPreviousStateRootIsZero();
|
||||
if (_postStateRoot == bytes32(0)) revert ErrorStateRootIsZero();
|
||||
|
||||
// compute batch hash and verify
|
||||
(uint256 memPtr, bytes32 _batchHash, uint256 _batchIndex, ) = _loadBatchHeader(_batchHeader);
|
||||
bytes32 _blobVersionedHash = BatchHeaderV1Codec.getBlobVersionedHash(memPtr);
|
||||
|
||||
// Calls the point evaluation precompile and verifies the output
|
||||
{
|
||||
(bool success, bytes memory data) = POINT_EVALUATION_PRECOMPILE_ADDR.staticcall(
|
||||
abi.encodePacked(_blobVersionedHash, _blobDataProof)
|
||||
);
|
||||
// We verify that the point evaluation precompile call was successful by testing the latter 32 bytes of the
|
||||
// response is equal to BLS_MODULUS as defined in https://eips.ethereum.org/EIPS/eip-4844#point-evaluation-precompile
|
||||
if (!success) revert ErrorCallPointEvaluationPrecompileFailed();
|
||||
(, uint256 result) = abi.decode(data, (uint256, uint256));
|
||||
if (result != BLS_MODULUS) revert ErrorUnexpectedPointEvaluationPrecompileOutput();
|
||||
}
|
||||
|
||||
// verify previous state root.
|
||||
if (finalizedStateRoots[_batchIndex - 1] != _prevStateRoot) revert ErrorIncorrectPreviousStateRoot();
|
||||
|
||||
// avoid duplicated verification
|
||||
if (finalizedStateRoots[_batchIndex] != bytes32(0)) revert ErrorBatchIsAlreadyVerified();
|
||||
|
||||
// check and update lastFinalizedBatchIndex
|
||||
unchecked {
|
||||
if (lastFinalizedBatchIndex + 1 != _batchIndex) revert ErrorIncorrectBatchIndex();
|
||||
lastFinalizedBatchIndex = _batchIndex;
|
||||
}
|
||||
|
||||
// record state root and withdraw root
|
||||
finalizedStateRoots[_batchIndex] = _postStateRoot;
|
||||
withdrawRoots[_batchIndex] = _withdrawRoot;
|
||||
|
||||
// Pop finalized and non-skipped message from L1MessageQueue.
|
||||
_popL1Messages(
|
||||
BatchHeaderV1Codec.getSkippedBitmapPtr(memPtr),
|
||||
BatchHeaderV1Codec.getTotalL1MessagePopped(memPtr),
|
||||
BatchHeaderV1Codec.getL1MessagePopped(memPtr)
|
||||
);
|
||||
|
||||
emit FinalizeBatch(_batchIndex, _batchHash, _postStateRoot, _withdrawRoot);
|
||||
}
|
||||
|
||||
/************************
|
||||
* Restricted Functions *
|
||||
************************/
|
||||
|
||||
@@ -11,7 +11,7 @@ import {IScrollERC20Upgradeable} from "../../libraries/token/IScrollERC20Upgrade
|
||||
/// @title L2ERC20Gateway
|
||||
/// @notice The `L2ERC20Gateway` is used to withdraw custom ERC20 compatible tokens on layer 2 and
|
||||
/// finalize deposit the tokens from layer 1.
|
||||
/// @dev The withdrawn tokens will be burned directly. On finalizing deposit, the corresponding
|
||||
/// @dev The withdrawn tokens tokens will be burned directly. On finalizing deposit, the corresponding
|
||||
/// tokens will be minted and transferred to the recipient.
|
||||
contract L2CustomERC20Gateway is L2ERC20Gateway {
|
||||
/**********
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity =0.8.24;
|
||||
|
||||
library BatchBridgeCodec {
|
||||
/// @dev Encode the `token` and `batchIndex` to single `bytes32`.
|
||||
function encodeInitialNode(address token, uint64 batchIndex) internal pure returns (bytes32 node) {
|
||||
assembly {
|
||||
node := add(shl(96, token), batchIndex)
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Encode the `sender` and `amount` to single `bytes32`.
|
||||
function encodeNode(address sender, uint96 amount) internal pure returns (bytes32 node) {
|
||||
assembly {
|
||||
node := add(shl(96, sender), amount)
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Decode `bytes32` `node` to `receiver` and `amount`.
|
||||
function decodeNode(bytes32 node) internal pure returns (address receiver, uint256 amount) {
|
||||
receiver = address(uint160(uint256(node) >> 96));
|
||||
amount = uint256(node) & 0xffffffffffffffffffffffff;
|
||||
}
|
||||
|
||||
/// @dev Compute `keccak256(concat(a, b))`.
|
||||
function hash(bytes32 a, bytes32 b) internal pure returns (bytes32 value) {
|
||||
// solhint-disable-next-line no-inline-assembly
|
||||
assembly {
|
||||
mstore(0x00, a)
|
||||
mstore(0x20, b)
|
||||
value := keccak256(0x00, 0x40)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,425 +0,0 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity =0.8.24;
|
||||
|
||||
import {AccessControlEnumerableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol";
|
||||
import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
|
||||
import {SafeERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
|
||||
import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
|
||||
import {AddressUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
|
||||
|
||||
import {IL1ERC20Gateway} from "../L1/gateways/IL1ERC20Gateway.sol";
|
||||
import {IL1GatewayRouter} from "../L1/gateways/IL1GatewayRouter.sol";
|
||||
import {IL1MessageQueue} from "../L1/rollup/IL1MessageQueue.sol";
|
||||
import {IL1ScrollMessenger} from "../L1/IL1ScrollMessenger.sol";
|
||||
|
||||
import {BatchBridgeCodec} from "./BatchBridgeCodec.sol";
|
||||
import {L2BatchBridgeGateway} from "./L2BatchBridgeGateway.sol";
|
||||
|
||||
/// @title L1BatchBridgeGateway
|
||||
contract L1BatchBridgeGateway is AccessControlEnumerableUpgradeable, ReentrancyGuardUpgradeable {
|
||||
using SafeERC20Upgradeable for IERC20Upgradeable;
|
||||
|
||||
/**********
|
||||
* Events *
|
||||
**********/
|
||||
|
||||
/// @notice Emitted when some user deposited token to this contract.
|
||||
/// @param sender The address of token sender.
|
||||
/// @param token The address of deposited token.
|
||||
/// @param batchIndex The batch index of current deposit.
|
||||
/// @param amount The amount of token deposited (including fee).
|
||||
/// @param fee The amount of fee charged.
|
||||
event Deposit(
|
||||
address indexed sender,
|
||||
address indexed token,
|
||||
uint256 indexed batchIndex,
|
||||
uint256 amount,
|
||||
uint256 fee
|
||||
);
|
||||
|
||||
/// @notice Emitted when a batch deposit is initiated.
|
||||
/// @param caller The address of caller who initiate the deposit.
|
||||
/// @param l1Token The address of the token in L1 to deposit.
|
||||
/// @param batchIndex The index of current batch deposit.
|
||||
/// @param l2Token The address of the corresponding token in L2.
|
||||
event BatchDeposit(address indexed caller, address indexed l1Token, uint256 indexed batchIndex, address l2Token);
|
||||
|
||||
/**********
|
||||
* Errors *
|
||||
**********/
|
||||
|
||||
/// @dev Thrown when caller is not `messenger`.
|
||||
error ErrorCallerNotMessenger();
|
||||
|
||||
/// @dev Thrown when the deposited amount is smaller than `minAmountPerTx`.
|
||||
error ErrorDepositAmountTooSmall();
|
||||
|
||||
/// @dev Thrown when users try to deposit ETH with `depositERC20` method.
|
||||
error ErrorIncorrectMethodForETHDeposit();
|
||||
|
||||
/// @dev Thrown when the `msg.value` is not enough for batch deposit fee.
|
||||
error ErrorInsufficientMsgValueForBatchDepositFee();
|
||||
|
||||
/// @dev Thrown when the given new batch config is invalid.
|
||||
error ErrorInvalidBatchConfig();
|
||||
|
||||
/// @dev Thrown when no pending batch exists.
|
||||
error ErrorNoPendingBatch();
|
||||
|
||||
/// @dev Thrown when user deposits unsupported tokens.
|
||||
error ErrorTokenNotSupported();
|
||||
|
||||
/// @dev Thrown when ETH transfer failed.
|
||||
error ErrorTransferETHFailed();
|
||||
|
||||
/*************
|
||||
* Constants *
|
||||
*************/
|
||||
|
||||
/// @notice The role for batch deposit keeper.
|
||||
bytes32 public constant KEEPER_ROLE = keccak256("KEEPER_ROLE");
|
||||
|
||||
/// @notice The safe gas limit for batch bridge.
|
||||
uint256 private constant SAFE_BATCH_BRIDGE_GAS_LIMIT = 200000;
|
||||
|
||||
/// @notice The address of corresponding `L2BatchDepositGateway` contract.
|
||||
address public immutable counterpart;
|
||||
|
||||
/// @notice The address of `L1GatewayRouter` contract.
|
||||
address public immutable router;
|
||||
|
||||
/// @notice The address of `L1ScrollMessenger` contract.
|
||||
address public immutable messenger;
|
||||
|
||||
/// @notice The address of `L1MessageQueue` contract.
|
||||
address public immutable queue;
|
||||
|
||||
/***********
|
||||
* Structs *
|
||||
***********/
|
||||
|
||||
/// @notice The config for batch token bridge.
|
||||
/// @dev Compiler will pack this into a single `bytes32`.
|
||||
/// @param feeAmountPerTx The amount of fee charged for each deposit.
|
||||
/// @param minAmountPerTx The minimum amount of token for each deposit.
|
||||
/// @param maxTxsPerBatch The maximum number of deposit in each batch.
|
||||
/// @param maxDelayPerBatch The maximum number of seconds to wait in each batch.
|
||||
/// @param safeBridgeGasLimit The safe bridge gas limit for bridging token from L1 to L2.
|
||||
struct BatchConfig {
|
||||
uint96 feeAmountPerTx;
|
||||
uint96 minAmountPerTx;
|
||||
uint16 maxTxsPerBatch;
|
||||
uint24 maxDelayPerBatch;
|
||||
uint24 safeBridgeGasLimit;
|
||||
}
|
||||
|
||||
/// @dev Compiler will pack this into two `bytes32`.
|
||||
/// @param amount The total amount of token to deposit in current batch.
|
||||
/// @param startTime The timestamp of the first deposit.
|
||||
/// @param numDeposits The total number of deposits in current batch.
|
||||
/// @param hash The hash of current batch.
|
||||
/// Suppose there are `n` deposits in current batch with `senders` and `amounts`. The hash is computed as
|
||||
/// ```text
|
||||
/// hash[0] = concat(token, batch_index)
|
||||
/// hash[i] = keccak(hash[i-1], concat(senders[i], amounts[i]))
|
||||
/// ```
|
||||
/// The type of `token` and `senders` is `address`, while The type of `batch_index` and `amounts[i]` is `uint96`.
|
||||
/// In current way, the hash of each batch among all tokens should be different.
|
||||
struct BatchState {
|
||||
uint128 amount;
|
||||
uint64 startTime;
|
||||
uint64 numDeposits;
|
||||
bytes32 hash;
|
||||
}
|
||||
|
||||
/// @dev Compiler will pack this into a single `bytes32`.
|
||||
/// @param pending The total amount of token pending to bridge.
|
||||
/// @param currentBatchIndex The index of current batch.
|
||||
/// @param pendingBatchIndex The index of pending batch (next batch to bridge).
|
||||
struct TokenState {
|
||||
uint128 pending;
|
||||
uint64 currentBatchIndex;
|
||||
uint64 pendingBatchIndex;
|
||||
}
|
||||
|
||||
/*************
|
||||
* Variables *
|
||||
*************/
|
||||
|
||||
/// @notice Mapping from token address to batch bridge config.
|
||||
/// @dev The `address(0)` is used for ETH.
|
||||
mapping(address => BatchConfig) public configs;
|
||||
|
||||
/// @notice Mapping from token address to batch index to batch state.
|
||||
/// @dev The `address(0)` is used for ETH.
|
||||
mapping(address => mapping(uint256 => BatchState)) public batches;
|
||||
|
||||
/// @notice Mapping from token address to token state.
|
||||
/// @dev The `address(0)` is used for ETH.
|
||||
mapping(address => TokenState) public tokens;
|
||||
|
||||
/// @notice The address of fee vault.
|
||||
address public feeVault;
|
||||
|
||||
/***************
|
||||
* Constructor *
|
||||
***************/
|
||||
|
||||
/// @param _counterpart The address of `L2BatchDepositGateway` contract in L2.
|
||||
/// @param _router The address of `L1GatewayRouter` contract in L1.
|
||||
/// @param _messenger The address of `L1ScrollMessenger` contract in L1.
|
||||
/// @param _queue The address of `L1MessageQueue` contract in L1.
|
||||
constructor(
|
||||
address _counterpart,
|
||||
address _router,
|
||||
address _messenger,
|
||||
address _queue
|
||||
) {
|
||||
_disableInitializers();
|
||||
|
||||
counterpart = _counterpart;
|
||||
router = _router;
|
||||
messenger = _messenger;
|
||||
queue = _queue;
|
||||
}
|
||||
|
||||
/// @notice Initialize the storage of `L1BatchDepositGateway`.
|
||||
/// @param _feeVault The address of fee vault contract.
|
||||
function initialize(address _feeVault) external initializer {
|
||||
__Context_init(); // from ContextUpgradeable
|
||||
__ERC165_init(); // from ERC165Upgradeable
|
||||
__AccessControl_init(); // from AccessControlUpgradeable
|
||||
__AccessControlEnumerable_init(); // from AccessControlEnumerableUpgradeable
|
||||
__ReentrancyGuard_init(); // from ReentrancyGuardUpgradeable
|
||||
|
||||
feeVault = _feeVault;
|
||||
_grantRole(DEFAULT_ADMIN_ROLE, _msgSender());
|
||||
}
|
||||
|
||||
/*****************************
|
||||
* Public Mutating Functions *
|
||||
*****************************/
|
||||
|
||||
/// @notice Receive refunded ETH from `L1ScrollMessenger`.
|
||||
receive() external payable {
|
||||
if (_msgSender() != messenger) {
|
||||
revert ErrorCallerNotMessenger();
|
||||
}
|
||||
}
|
||||
|
||||
/// @notice Deposit ETH.
|
||||
function depositETH() external payable {
|
||||
// no safe cast check here, since no one has so much ETH yet.
|
||||
_deposit(address(0), _msgSender(), uint96(msg.value));
|
||||
}
|
||||
|
||||
/// @notice Deposit ERC20 token.
|
||||
///
|
||||
/// @param token The address of token.
|
||||
/// @param amount The amount of token to deposit. We use type `uint96`, since it is enough for most of the major tokens.
|
||||
function depositERC20(address token, uint96 amount) external {
|
||||
if (token == address(0)) revert ErrorIncorrectMethodForETHDeposit();
|
||||
|
||||
// common practice to handle fee on transfer token.
|
||||
uint256 beforeBalance = IERC20Upgradeable(token).balanceOf(address(this));
|
||||
IERC20Upgradeable(token).safeTransferFrom(_msgSender(), address(this), amount);
|
||||
amount = uint96(IERC20Upgradeable(token).balanceOf(address(this)) - beforeBalance);
|
||||
|
||||
_deposit(token, _msgSender(), amount);
|
||||
}
|
||||
|
||||
/************************
|
||||
* Restricted Functions *
|
||||
************************/
|
||||
|
||||
/// @notice Add or update the batch bridge config for the given token.
|
||||
///
|
||||
/// @dev The caller should make sure `safeBridgeGasLimit` is enough for batch bridging.
|
||||
///
|
||||
/// @param token The address of token to update.
|
||||
/// @param newConfig The new config.
|
||||
function setBatchConfig(address token, BatchConfig memory newConfig) external onlyRole(DEFAULT_ADMIN_ROLE) {
|
||||
if (
|
||||
newConfig.maxTxsPerBatch == 0 ||
|
||||
newConfig.maxDelayPerBatch == 0 ||
|
||||
newConfig.feeAmountPerTx > newConfig.minAmountPerTx
|
||||
) {
|
||||
revert ErrorInvalidBatchConfig();
|
||||
}
|
||||
configs[token] = newConfig;
|
||||
}
|
||||
|
||||
/// @notice Initiate the batch bridge of current pending batch.
|
||||
/// @param token The address of the token.
|
||||
function executeBatchDeposit(address token) external payable onlyRole(KEEPER_ROLE) {
|
||||
BatchConfig memory cachedBatchConfig = configs[token];
|
||||
TokenState memory cachedTokenState = tokens[token];
|
||||
_tryFinalizeCurrentBatch(token, cachedBatchConfig, cachedTokenState);
|
||||
|
||||
// no batch to bridge
|
||||
if (cachedTokenState.currentBatchIndex == cachedTokenState.pendingBatchIndex) {
|
||||
revert ErrorNoPendingBatch();
|
||||
}
|
||||
|
||||
// check bridge fee
|
||||
uint256 depositFee = IL1MessageQueue(queue).estimateCrossDomainMessageFee(cachedBatchConfig.safeBridgeGasLimit);
|
||||
uint256 batchBridgeFee = IL1MessageQueue(queue).estimateCrossDomainMessageFee(SAFE_BATCH_BRIDGE_GAS_LIMIT);
|
||||
if (msg.value < depositFee + batchBridgeFee) {
|
||||
revert ErrorInsufficientMsgValueForBatchDepositFee();
|
||||
}
|
||||
|
||||
// take accumulated fee to fee vault
|
||||
uint256 accumulatedFee;
|
||||
if (token == address(0)) {
|
||||
// no uncheck here just in case
|
||||
accumulatedFee = address(this).balance - msg.value - cachedTokenState.pending;
|
||||
} else {
|
||||
// no uncheck here just in case
|
||||
accumulatedFee = IERC20Upgradeable(token).balanceOf(address(this)) - cachedTokenState.pending;
|
||||
}
|
||||
if (accumulatedFee > 0) {
|
||||
_transferToken(token, feeVault, accumulatedFee);
|
||||
}
|
||||
|
||||
// deposit token to L2
|
||||
BatchState memory cachedBatchState = batches[token][cachedTokenState.pendingBatchIndex];
|
||||
address l2Token;
|
||||
if (token == address(0)) {
|
||||
IL1ScrollMessenger(messenger).sendMessage{value: cachedBatchState.amount + depositFee}(
|
||||
counterpart,
|
||||
cachedBatchState.amount,
|
||||
new bytes(0),
|
||||
cachedBatchConfig.safeBridgeGasLimit
|
||||
);
|
||||
} else {
|
||||
address gateway = IL1GatewayRouter(router).getERC20Gateway(token);
|
||||
l2Token = IL1ERC20Gateway(gateway).getL2ERC20Address(token);
|
||||
IERC20Upgradeable(token).safeApprove(gateway, 0);
|
||||
IERC20Upgradeable(token).safeApprove(gateway, cachedBatchState.amount);
|
||||
IL1ERC20Gateway(gateway).depositERC20{value: depositFee}(
|
||||
token,
|
||||
counterpart,
|
||||
cachedBatchState.amount,
|
||||
cachedBatchConfig.safeBridgeGasLimit
|
||||
);
|
||||
}
|
||||
|
||||
// notify `L2BatchBridgeGateway`
|
||||
IL1ScrollMessenger(messenger).sendMessage{value: batchBridgeFee}(
|
||||
counterpart,
|
||||
0,
|
||||
abi.encodeCall(
|
||||
L2BatchBridgeGateway.finalizeBatchDeposit,
|
||||
(token, l2Token, cachedTokenState.pendingBatchIndex, cachedBatchState.hash)
|
||||
),
|
||||
SAFE_BATCH_BRIDGE_GAS_LIMIT
|
||||
);
|
||||
|
||||
emit BatchDeposit(_msgSender(), token, cachedTokenState.pendingBatchIndex, l2Token);
|
||||
|
||||
// update token state
|
||||
unchecked {
|
||||
cachedTokenState.pending -= uint128(cachedBatchState.amount);
|
||||
cachedTokenState.pendingBatchIndex += 1;
|
||||
}
|
||||
tokens[token] = cachedTokenState;
|
||||
|
||||
// refund keeper fee
|
||||
unchecked {
|
||||
if (msg.value > depositFee + batchBridgeFee) {
|
||||
_transferToken(address(0), _msgSender(), msg.value - depositFee - batchBridgeFee);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**********************
|
||||
* Internal Functions *
|
||||
**********************/
|
||||
|
||||
/// @dev Internal function to deposit token.
|
||||
/// @param token The address of token to deposit.
|
||||
/// @param sender The address of token sender.
|
||||
/// @param amount The amount of token to deposit.
|
||||
function _deposit(
|
||||
address token,
|
||||
address sender,
|
||||
uint96 amount
|
||||
) internal nonReentrant {
|
||||
BatchConfig memory cachedBatchConfig = configs[token];
|
||||
TokenState memory cachedTokenState = tokens[token];
|
||||
_tryFinalizeCurrentBatch(token, cachedBatchConfig, cachedTokenState);
|
||||
|
||||
BatchState memory cachedBatchState = batches[token][cachedTokenState.currentBatchIndex];
|
||||
if (amount < cachedBatchConfig.minAmountPerTx) {
|
||||
revert ErrorDepositAmountTooSmall();
|
||||
}
|
||||
|
||||
emit Deposit(sender, token, cachedTokenState.currentBatchIndex, amount, cachedBatchConfig.feeAmountPerTx);
|
||||
|
||||
// deduct fee and update cached state
|
||||
unchecked {
|
||||
amount -= cachedBatchConfig.feeAmountPerTx;
|
||||
cachedTokenState.pending += amount;
|
||||
cachedBatchState.amount += amount;
|
||||
cachedBatchState.numDeposits += 1;
|
||||
}
|
||||
|
||||
// compute the hash chain
|
||||
bytes32 node = BatchBridgeCodec.encodeNode(sender, amount);
|
||||
if (cachedBatchState.hash == bytes32(0)) {
|
||||
bytes32 initialNode = BatchBridgeCodec.encodeInitialNode(token, cachedTokenState.currentBatchIndex);
|
||||
// this is first tx in this batch
|
||||
cachedBatchState.hash = BatchBridgeCodec.hash(initialNode, node);
|
||||
cachedBatchState.startTime = uint64(block.timestamp);
|
||||
} else {
|
||||
cachedBatchState.hash = BatchBridgeCodec.hash(cachedBatchState.hash, node);
|
||||
}
|
||||
|
||||
batches[token][cachedTokenState.currentBatchIndex] = cachedBatchState;
|
||||
tokens[token] = cachedTokenState;
|
||||
}
|
||||
|
||||
/// @dev Internal function to finalize current batch.
|
||||
/// This function may change the value of `cachedTokenState`, which can be used in later operation.
|
||||
/// @param token The address of token to finalize.
|
||||
/// @param cachedBatchConfig The cached batch config in memory.
|
||||
/// @param cachedTokenState The cached token state in memory.
|
||||
function _tryFinalizeCurrentBatch(
|
||||
address token,
|
||||
BatchConfig memory cachedBatchConfig,
|
||||
TokenState memory cachedTokenState
|
||||
) internal view {
|
||||
if (cachedBatchConfig.maxTxsPerBatch == 0) {
|
||||
revert ErrorTokenNotSupported();
|
||||
}
|
||||
BatchState memory cachedBatchState = batches[token][cachedTokenState.currentBatchIndex];
|
||||
// return if it is the very first deposit in the current batch
|
||||
if (cachedBatchState.numDeposits == 0) return;
|
||||
|
||||
// finalize current batchIndex when `maxTxsPerBatch` or `maxDelayPerBatch` reached.
|
||||
if (
|
||||
cachedBatchState.numDeposits == cachedBatchConfig.maxTxsPerBatch ||
|
||||
block.timestamp - cachedBatchState.startTime > cachedBatchConfig.maxDelayPerBatch
|
||||
) {
|
||||
cachedTokenState.currentBatchIndex += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Internal function to transfer token, including ETH.
|
||||
/// @param token The address of token.
|
||||
/// @param receiver The address of token receiver.
|
||||
/// @param amount The amount of token to transfer.
|
||||
function _transferToken(
|
||||
address token,
|
||||
address receiver,
|
||||
uint256 amount
|
||||
) private {
|
||||
if (token == address(0)) {
|
||||
(bool success, ) = receiver.call{value: amount}("");
|
||||
if (!success) revert ErrorTransferETHFailed();
|
||||
} else {
|
||||
IERC20Upgradeable(token).safeTransfer(receiver, amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,246 +0,0 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity =0.8.24;
|
||||
|
||||
import {AccessControlEnumerableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol";
|
||||
import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
|
||||
|
||||
import {IL2ScrollMessenger} from "../L2/IL2ScrollMessenger.sol";
|
||||
import {BatchBridgeCodec} from "./BatchBridgeCodec.sol";
|
||||
|
||||
/// @title L2BatchBridgeGateway
|
||||
contract L2BatchBridgeGateway is AccessControlEnumerableUpgradeable {
|
||||
/**********
|
||||
* Events *
|
||||
**********/
|
||||
|
||||
/// @notice Emitted when token mapping for ERC20 token is updated.
|
||||
/// @param l2Token The address of corresponding ERC20 token in layer 2.
|
||||
/// @param oldL1Token The address of the old corresponding ERC20 token in layer 1.
|
||||
/// @param newL1Token The address of the new corresponding ERC20 token in layer 1.
|
||||
event UpdateTokenMapping(address indexed l2Token, address indexed oldL1Token, address indexed newL1Token);
|
||||
|
||||
/// @notice Emitted when batch bridge is finalized.
|
||||
/// @param l1Token The address of token in L1.
|
||||
/// @param l2Token The address of token in L2.
|
||||
/// @param batchIndex The index of batch finalized.
|
||||
event FinalizeBatchDeposit(address indexed l1Token, address indexed l2Token, uint256 indexed batchIndex);
|
||||
|
||||
/// @notice Emitted when batch distribution finished.
|
||||
/// @param l1Token The address of token in L1.
|
||||
/// @param l2Token The address of token in L2.
|
||||
/// @param batchIndex The index of batch distributed.
|
||||
event BatchDistribute(address indexed l1Token, address indexed l2Token, uint256 indexed batchIndex);
|
||||
|
||||
/// @notice Emitted when token distribute failed.
|
||||
/// @param l2Token The address of token in L2.
|
||||
/// @param batchIndex The index of the batch.
|
||||
/// @param receiver The address of token receiver.
|
||||
/// @param amount The amount of token to distribute.
|
||||
event DistributeFailed(address indexed l2Token, uint256 indexed batchIndex, address receiver, uint256 amount);
|
||||
|
||||
/**********
|
||||
* Errors *
|
||||
**********/
|
||||
|
||||
/// @dev Thrown when caller is not `messenger`.
|
||||
error ErrorCallerNotMessenger();
|
||||
|
||||
/// @dev Thrown when the L1 token mapping mismatch with `finalizeBatchBridge`.
|
||||
error ErrorL1TokenMismatched();
|
||||
|
||||
/// @dev Thrown when message sender is not `counterpart`.
|
||||
error ErrorMessageSenderNotCounterpart();
|
||||
|
||||
/// @dev Thrown no failed distribution exists.
|
||||
error ErrorNoFailedDistribution();
|
||||
|
||||
/// @dev Thrown when the batch hash mismatch.
|
||||
error ErrorBatchHashMismatch();
|
||||
|
||||
/// @dev Thrown when distributing the same batch.
|
||||
error ErrorBatchDistributed();
|
||||
|
||||
/*************
|
||||
* Constants *
|
||||
*************/
|
||||
|
||||
/// @notice The role for batch deposit keeper.
|
||||
bytes32 public constant KEEPER_ROLE = keccak256("KEEPER_ROLE");
|
||||
|
||||
/// @notice The safe gas limit for ETH transfer
|
||||
uint256 private constant SAFE_ETH_TRANSFER_GAS_LIMIT = 50000;
|
||||
|
||||
/// @notice The address of corresponding `L1BatchBridgeGateway` contract.
|
||||
address public immutable counterpart;
|
||||
|
||||
/// @notice The address of corresponding `L2ScrollMessenger` contract.
|
||||
address public immutable messenger;
|
||||
|
||||
/*************
|
||||
* Variables *
|
||||
*************/
|
||||
|
||||
/// @notice Mapping from l2 token address to l1 token address.
|
||||
mapping(address => address) public tokenMapping;
|
||||
|
||||
/// @notice Mapping from L2 token address to batch index to batch hash.
|
||||
mapping(address => mapping(uint256 => bytes32)) public batchHashes;
|
||||
|
||||
/// @notice Mapping from token address to the amount of failed distribution.
|
||||
mapping(address => uint256) public failedAmount;
|
||||
|
||||
/// @notice Mapping from batch hash to the distribute status.
|
||||
mapping(bytes32 => bool) public isDistributed;
|
||||
|
||||
/*************
|
||||
* Modifiers *
|
||||
*************/
|
||||
|
||||
modifier onlyMessenger() {
|
||||
if (_msgSender() != messenger) {
|
||||
revert ErrorCallerNotMessenger();
|
||||
}
|
||||
_;
|
||||
}
|
||||
|
||||
/***************
|
||||
* Constructor *
|
||||
***************/
|
||||
|
||||
/// @param _counterpart The address of `L1BatchBridgeGateway` contract in L1.
|
||||
/// @param _messenger The address of `L2ScrollMessenger` contract in L2.
|
||||
constructor(address _counterpart, address _messenger) {
|
||||
_disableInitializers();
|
||||
|
||||
counterpart = _counterpart;
|
||||
messenger = _messenger;
|
||||
}
|
||||
|
||||
/// @notice Initialize the storage of `L2BatchBridgeGateway`.
|
||||
function initialize() external initializer {
|
||||
__Context_init(); // from ContextUpgradeable
|
||||
__ERC165_init(); // from ERC165Upgradeable
|
||||
__AccessControl_init(); // from AccessControlUpgradeable
|
||||
__AccessControlEnumerable_init(); // from AccessControlEnumerableUpgradeable
|
||||
|
||||
_grantRole(DEFAULT_ADMIN_ROLE, _msgSender());
|
||||
}
|
||||
|
||||
/*****************************
|
||||
* Public Mutating Functions *
|
||||
*****************************/
|
||||
|
||||
/// @notice Receive batch bridged ETH from `L2ScrollMessenger`.
|
||||
receive() external payable onlyMessenger {
|
||||
// empty
|
||||
}
|
||||
|
||||
/// @notice Finalize L1 initiated batch token deposit.
|
||||
/// @param l1Token The address of the token in L1.
|
||||
/// @param l2Token The address of the token in L2.
|
||||
/// @param batchIndex The index of this batch bridge.
|
||||
/// @param hash The hash of this batch.
|
||||
function finalizeBatchDeposit(
|
||||
address l1Token,
|
||||
address l2Token,
|
||||
uint256 batchIndex,
|
||||
bytes32 hash
|
||||
) external onlyMessenger {
|
||||
if (counterpart != IL2ScrollMessenger(messenger).xDomainMessageSender()) {
|
||||
revert ErrorMessageSenderNotCounterpart();
|
||||
}
|
||||
|
||||
// trust the messenger and update `tokenMapping` in first call
|
||||
// another assumption is this function should never fail due to out of gas
|
||||
address storedL1Token = tokenMapping[l2Token];
|
||||
if (storedL1Token == address(0) && l1Token != address(0)) {
|
||||
tokenMapping[l2Token] = l1Token;
|
||||
} else if (storedL1Token != l1Token) {
|
||||
// this usually won't happen, check just in case.
|
||||
revert ErrorL1TokenMismatched();
|
||||
}
|
||||
|
||||
batchHashes[l2Token][batchIndex] = hash;
|
||||
|
||||
emit FinalizeBatchDeposit(l1Token, l2Token, batchIndex);
|
||||
}
|
||||
|
||||
/************************
|
||||
* Restricted Functions *
|
||||
************************/
|
||||
|
||||
/// @notice Withdraw distribution failed tokens.
|
||||
/// @param token The address of token to withdraw.
|
||||
/// @param receiver The address of token receiver.
|
||||
function withdrawFailedAmount(address token, address receiver) external onlyRole(DEFAULT_ADMIN_ROLE) {
|
||||
uint256 amount = failedAmount[token];
|
||||
if (amount == 0) revert ErrorNoFailedDistribution();
|
||||
failedAmount[token] = 0;
|
||||
|
||||
_transferToken(token, receiver, amount);
|
||||
}
|
||||
|
||||
/// @notice Distribute deposited token to corresponding receivers.
|
||||
/// @param l2Token The address of L2 token.
|
||||
/// @param batchIndex The index of batch to distribute.
|
||||
/// @param nodes The list of encoded L1 deposits.
|
||||
function distribute(
|
||||
address l2Token,
|
||||
uint64 batchIndex,
|
||||
bytes32[] memory nodes
|
||||
) external onlyRole(KEEPER_ROLE) {
|
||||
address l1Token = tokenMapping[l2Token];
|
||||
bytes32 hash = BatchBridgeCodec.encodeInitialNode(l1Token, batchIndex);
|
||||
for (uint256 i = 0; i < nodes.length; i++) {
|
||||
hash = BatchBridgeCodec.hash(hash, nodes[i]);
|
||||
}
|
||||
if (batchHashes[l2Token][batchIndex] != hash) {
|
||||
revert ErrorBatchHashMismatch();
|
||||
}
|
||||
if (isDistributed[hash]) {
|
||||
revert ErrorBatchDistributed();
|
||||
}
|
||||
isDistributed[hash] = true;
|
||||
|
||||
// do transfer and allow failure to avoid DDOS attack
|
||||
for (uint256 i = 0; i < nodes.length; i++) {
|
||||
(address receiver, uint256 amount) = BatchBridgeCodec.decodeNode(nodes[i]);
|
||||
if (!_transferToken(l2Token, receiver, amount)) {
|
||||
failedAmount[l2Token] += amount;
|
||||
|
||||
emit DistributeFailed(l2Token, batchIndex, receiver, amount);
|
||||
}
|
||||
}
|
||||
|
||||
emit BatchDistribute(l1Token, l2Token, batchIndex);
|
||||
}
|
||||
|
||||
/**********************
|
||||
* Internal Functions *
|
||||
**********************/
|
||||
|
||||
/// @dev Internal function to transfer token, including ETH.
|
||||
/// @param token The address of token.
|
||||
/// @param receiver The address of token receiver.
|
||||
/// @param amount The amount of token to transfer.
|
||||
/// @return success Whether the transfer is successful.
|
||||
function _transferToken(
|
||||
address token,
|
||||
address receiver,
|
||||
uint256 amount
|
||||
) private returns (bool success) {
|
||||
if (token == address(0)) {
|
||||
// We add gas limit here to avoid DDOS from malicious receiver.
|
||||
(success, ) = receiver.call{value: amount, gas: SAFE_ETH_TRANSFER_GAS_LIMIT}("");
|
||||
} else {
|
||||
// We perform a low level call here, to bypass Solidity's return data size checking mechanism.
|
||||
// Normally, the token is selected that the call would not revert unless out of gas.
|
||||
bytes memory returnData;
|
||||
(success, returnData) = token.call(abi.encodeCall(IERC20Upgradeable.transfer, (receiver, amount)));
|
||||
if (success && returnData.length > 0) {
|
||||
success = abi.decode(returnData, (bool));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -79,7 +79,7 @@ library BatchHeaderV0Codec {
|
||||
|
||||
/// @notice Get the number of L1 messages popped before this batch.
|
||||
/// @param batchPtr The start memory offset of the batch header in memory.
|
||||
/// @return _totalL1MessagePopped The number of L1 messages popped before this batch.
|
||||
/// @return _totalL1MessagePopped The the number of L1 messages popped before this batch.
|
||||
function getTotalL1MessagePopped(uint256 batchPtr) internal pure returns (uint256 _totalL1MessagePopped) {
|
||||
assembly {
|
||||
_totalL1MessagePopped := shr(192, mload(add(batchPtr, 17)))
|
||||
|
||||
@@ -79,7 +79,7 @@ library BatchHeaderV1Codec {
|
||||
|
||||
/// @notice Get the number of L1 messages popped before this batch.
|
||||
/// @param batchPtr The start memory offset of the batch header in memory.
|
||||
/// @return _totalL1MessagePopped The number of L1 messages popped before this batch.
|
||||
/// @return _totalL1MessagePopped The the number of L1 messages popped before this batch.
|
||||
function getTotalL1MessagePopped(uint256 batchPtr) internal pure returns (uint256 _totalL1MessagePopped) {
|
||||
assembly {
|
||||
_totalL1MessagePopped := shr(192, mload(add(batchPtr, 17)))
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
/// @title IRollupVerifier
|
||||
/// @notice The interface for rollup verifier.
|
||||
interface IRollupVerifier {
|
||||
/// @notice Verify aggregate zk proof.
|
||||
/// @param batchIndex The batch index to verify.
|
||||
|
||||
@@ -199,7 +199,7 @@ library PatriciaMerkleTrieVerifier {
|
||||
}
|
||||
|
||||
// decodes all RLP encoded data and stores their DATA items
|
||||
// [length - 128 bits | calldata offset - 128 bits] in a continuous memory region.
|
||||
// [length - 128 bits | calldata offset - 128 bits] in a continous memory region.
|
||||
// Expects that the RLP starts with a list that defines the length
|
||||
// of the whole RLP region.
|
||||
function decodeFlat(_ptr) -> ptr, memStart, nItems, hash {
|
||||
@@ -505,7 +505,7 @@ library PatriciaMerkleTrieVerifier {
|
||||
}
|
||||
|
||||
// the one and only boundary check
|
||||
// in case an attacker crafted a malicious payload
|
||||
// in case an attacker crafted a malicous payload
|
||||
// and succeeds in the prior verification steps
|
||||
// then this should catch any bogus accesses
|
||||
if iszero(eq(ptr, add(proof.offset, proof.length))) {
|
||||
|
||||
@@ -98,7 +98,7 @@ According to the Scroll documentation, `L1ScrollMessenger`:
|
||||
|
||||
This contract is central in the L2-to-L1 communication process since all messages from L2 that verified by the zkevm proof are executed on behalf of this contract.
|
||||
|
||||
In case of a vulnerability in the `L1ScrollMessenger`, which allows the attacker to send arbitrary messages bypassing the zkevm proof, an attacker can immediately drain tokens from the L1 bridge.
|
||||
In case of a vulnerability in the `L1ScrollMessenger`, which allows the attacker to send arbitrary messages bypassing the the zkevm proof, an attacker can immediately drain tokens from the L1 bridge.
|
||||
|
||||
Additional risk creates the upgradeability of the `L1ScrollMessenger`. Exist a risk of an attack with the replacement of the implementation with some malicious functionality. Such an attack might be reduced to the above vulnerability and steal all locked tokens on the L1 bridge.
|
||||
|
||||
|
||||
@@ -83,8 +83,6 @@ contract L2USDCGatewayTest is L2GatewayTestBase {
|
||||
}
|
||||
|
||||
function testTransferUSDCRoles(address owner) external {
|
||||
hevm.assume(owner != address(0));
|
||||
|
||||
// non-whitelisted caller call, should revert
|
||||
hevm.expectRevert("only circle caller");
|
||||
gateway.transferUSDCRoles(owner);
|
||||
|
||||
@@ -1,634 +0,0 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity =0.8.24;
|
||||
|
||||
import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol";
|
||||
|
||||
import {ITransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
|
||||
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
|
||||
|
||||
import {L1BatchBridgeGateway} from "../../batch-bridge/L1BatchBridgeGateway.sol";
|
||||
import {L2BatchBridgeGateway} from "../../batch-bridge/L2BatchBridgeGateway.sol";
|
||||
import {BatchBridgeCodec} from "../../batch-bridge/BatchBridgeCodec.sol";
|
||||
import {IL1ERC20Gateway, L1CustomERC20Gateway} from "../../L1/gateways/L1CustomERC20Gateway.sol";
|
||||
import {L1GatewayRouter} from "../../L1/gateways/L1GatewayRouter.sol";
|
||||
import {IL2ERC20Gateway, L2CustomERC20Gateway} from "../../L2/gateways/L2CustomERC20Gateway.sol";
|
||||
import {AddressAliasHelper} from "../../libraries/common/AddressAliasHelper.sol";
|
||||
import {ScrollConstants} from "../../libraries/constants/ScrollConstants.sol";
|
||||
|
||||
import {L1GatewayTestBase} from "../L1GatewayTestBase.t.sol";
|
||||
|
||||
contract L1BatchBridgeGatewayTest is L1GatewayTestBase {
|
||||
event Deposit(
|
||||
address indexed sender,
|
||||
address indexed token,
|
||||
uint256 indexed batchIndex,
|
||||
uint256 amount,
|
||||
uint256 fee
|
||||
);
|
||||
event BatchDeposit(address indexed caller, address indexed l1Token, uint256 indexed batchIndex, address l2Token);
|
||||
event DepositERC20(
|
||||
address indexed _l1Token,
|
||||
address indexed _l2Token,
|
||||
address indexed _from,
|
||||
address _to,
|
||||
uint256 _amount,
|
||||
bytes _data
|
||||
);
|
||||
|
||||
uint24 private constant SAFE_BATCH_BRIDGE_GAS_LIMIT = 200000;
|
||||
uint24 ETH_DEPOSIT_SAFE_GAS_LIMIT = 300000;
|
||||
uint24 ERC20_DEPOSIT_SAFE_GAS_LIMIT = 200000;
|
||||
|
||||
uint256 private constant L2_GAS_PRICE = 10;
|
||||
|
||||
L1BatchBridgeGateway private batch;
|
||||
L1CustomERC20Gateway private gateway;
|
||||
L1GatewayRouter private router;
|
||||
|
||||
L2CustomERC20Gateway private counterpartGateway;
|
||||
L2BatchBridgeGateway private counterpartBatch;
|
||||
|
||||
MockERC20 private l1Token;
|
||||
MockERC20 private l2Token;
|
||||
|
||||
address private batchFeeVault;
|
||||
|
||||
function setUp() public {
|
||||
__L1GatewayTestBase_setUp();
|
||||
|
||||
batchFeeVault = address(uint160(address(this)) - 2);
|
||||
|
||||
// Deploy tokens
|
||||
l1Token = new MockERC20("Mock L1", "ML1", 18);
|
||||
l2Token = new MockERC20("Mock L2", "ML2", 18);
|
||||
|
||||
// Deploy L2 contracts
|
||||
counterpartGateway = new L2CustomERC20Gateway(address(1), address(1), address(1));
|
||||
counterpartBatch = new L2BatchBridgeGateway(address(1), address(1));
|
||||
|
||||
// Deploy L1 contracts
|
||||
router = L1GatewayRouter(_deployProxy(address(new L1GatewayRouter())));
|
||||
gateway = L1CustomERC20Gateway(_deployProxy(address(0)));
|
||||
batch = L1BatchBridgeGateway(payable(_deployProxy(address(0))));
|
||||
|
||||
// Initialize L1 contracts
|
||||
admin.upgrade(
|
||||
ITransparentUpgradeableProxy(address(gateway)),
|
||||
address(new L1CustomERC20Gateway(address(counterpartGateway), address(router), address(l1Messenger)))
|
||||
);
|
||||
gateway.initialize(address(counterpartGateway), address(router), address(l1Messenger));
|
||||
admin.upgrade(
|
||||
ITransparentUpgradeableProxy(address(batch)),
|
||||
address(
|
||||
new L1BatchBridgeGateway(
|
||||
address(counterpartBatch),
|
||||
address(router),
|
||||
address(l1Messenger),
|
||||
address(messageQueue)
|
||||
)
|
||||
)
|
||||
);
|
||||
batch.initialize(batchFeeVault);
|
||||
router.initialize(address(0), address(gateway));
|
||||
messageQueue.setL2BaseFee(L2_GAS_PRICE);
|
||||
|
||||
// Prepare token balances
|
||||
l1Token.mint(address(this), type(uint128).max);
|
||||
gateway.updateTokenMapping(address(l1Token), address(l2Token));
|
||||
hevm.warp(1000000);
|
||||
}
|
||||
|
||||
function testInitialized() external {
|
||||
assertBoolEq(true, batch.hasRole(bytes32(0), address(this)));
|
||||
assertEq(address(counterpartBatch), batch.counterpart());
|
||||
assertEq(address(router), batch.router());
|
||||
assertEq(address(l1Messenger), batch.messenger());
|
||||
assertEq(address(messageQueue), batch.queue());
|
||||
|
||||
hevm.expectRevert("Initializable: contract is already initialized");
|
||||
batch.initialize(address(0));
|
||||
}
|
||||
|
||||
function testSetTokenSetting() external {
|
||||
// revert not admin
|
||||
hevm.startPrank(address(1));
|
||||
hevm.expectRevert(
|
||||
"AccessControl: account 0x0000000000000000000000000000000000000001 is missing role 0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
);
|
||||
batch.setBatchConfig(address(0), L1BatchBridgeGateway.BatchConfig(0, 0, 0, 0, 0));
|
||||
hevm.stopPrank();
|
||||
|
||||
// revert maxTxsPerBatch = 0
|
||||
hevm.expectRevert(L1BatchBridgeGateway.ErrorInvalidBatchConfig.selector);
|
||||
batch.setBatchConfig(address(0), L1BatchBridgeGateway.BatchConfig(0, 0, 0, 0, 0));
|
||||
|
||||
// revert maxDelayPerBatch = 0
|
||||
hevm.expectRevert(L1BatchBridgeGateway.ErrorInvalidBatchConfig.selector);
|
||||
batch.setBatchConfig(address(0), L1BatchBridgeGateway.BatchConfig(0, 0, 1, 0, 0));
|
||||
|
||||
// revert feeAmountPerTx > minAmountPerTx
|
||||
hevm.expectRevert(L1BatchBridgeGateway.ErrorInvalidBatchConfig.selector);
|
||||
batch.setBatchConfig(address(0), L1BatchBridgeGateway.BatchConfig(1, 0, 1, 1, 0));
|
||||
|
||||
// succeed
|
||||
batch.setBatchConfig(address(0), L1BatchBridgeGateway.BatchConfig(1, 2, 3, 4, 5));
|
||||
(
|
||||
uint96 feeAmountPerTx,
|
||||
uint96 minAmountPerTx,
|
||||
uint16 maxTxsPerBatch,
|
||||
uint24 maxDelayPerBatch,
|
||||
uint24 safeBridgeGasLimit
|
||||
) = batch.configs(address(0));
|
||||
assertEq(feeAmountPerTx, 1);
|
||||
assertEq(minAmountPerTx, 2);
|
||||
assertEq(maxTxsPerBatch, 3);
|
||||
assertEq(maxDelayPerBatch, 4);
|
||||
assertEq(safeBridgeGasLimit, 5);
|
||||
}
|
||||
|
||||
function testSetTokenSettingFuzzing(address token, L1BatchBridgeGateway.BatchConfig memory config) external {
|
||||
hevm.assume(config.maxTxsPerBatch > 0);
|
||||
hevm.assume(config.maxDelayPerBatch > 0);
|
||||
hevm.assume(config.feeAmountPerTx <= config.minAmountPerTx);
|
||||
|
||||
(
|
||||
uint96 feeAmountPerTx,
|
||||
uint96 minAmountPerTx,
|
||||
uint16 maxTxsPerBatch,
|
||||
uint24 maxDelayPerBatch,
|
||||
uint24 safeBridgeGasLimit
|
||||
) = batch.configs(token);
|
||||
assertEq(feeAmountPerTx, 0);
|
||||
assertEq(minAmountPerTx, 0);
|
||||
assertEq(maxTxsPerBatch, 0);
|
||||
assertEq(maxDelayPerBatch, 0);
|
||||
assertEq(safeBridgeGasLimit, 0);
|
||||
batch.setBatchConfig(token, config);
|
||||
(feeAmountPerTx, minAmountPerTx, maxTxsPerBatch, maxDelayPerBatch, safeBridgeGasLimit) = batch.configs(token);
|
||||
assertEq(feeAmountPerTx, config.feeAmountPerTx);
|
||||
assertEq(minAmountPerTx, config.minAmountPerTx);
|
||||
assertEq(maxTxsPerBatch, config.maxTxsPerBatch);
|
||||
assertEq(maxDelayPerBatch, config.maxDelayPerBatch);
|
||||
assertEq(safeBridgeGasLimit, config.safeBridgeGasLimit);
|
||||
}
|
||||
|
||||
function checkBatchState(
|
||||
address token,
|
||||
uint256 phase,
|
||||
L1BatchBridgeGateway.BatchState memory expected
|
||||
) private {
|
||||
(uint128 amount, uint64 startTime, uint64 numDeposits, bytes32 hash) = batch.batches(token, phase);
|
||||
assertEq(amount, expected.amount);
|
||||
assertEq(startTime, expected.startTime);
|
||||
assertEq(numDeposits, expected.numDeposits);
|
||||
// assertEq(hash, expected.hash);
|
||||
}
|
||||
|
||||
function checkTokenState(address token, L1BatchBridgeGateway.TokenState memory expected) private {
|
||||
(uint128 pending, uint64 currentBatchIndex, uint64 pendingBatchIndex) = batch.tokens(token);
|
||||
assertEq(pending, expected.pending);
|
||||
assertEq(currentBatchIndex, expected.currentBatchIndex);
|
||||
assertEq(pendingBatchIndex, expected.pendingBatchIndex);
|
||||
}
|
||||
|
||||
function testDepositETH() external {
|
||||
// revert token not supported
|
||||
hevm.expectRevert(L1BatchBridgeGateway.ErrorTokenNotSupported.selector);
|
||||
batch.depositETH();
|
||||
|
||||
// revert deposit amount too small
|
||||
batch.setBatchConfig(address(0), L1BatchBridgeGateway.BatchConfig(0, 100, 2, 100, ETH_DEPOSIT_SAFE_GAS_LIMIT));
|
||||
hevm.expectRevert(L1BatchBridgeGateway.ErrorDepositAmountTooSmall.selector);
|
||||
batch.depositETH{value: 10}();
|
||||
|
||||
// no fee
|
||||
batch.setBatchConfig(address(0), L1BatchBridgeGateway.BatchConfig(0, 0, 2, 100, ETH_DEPOSIT_SAFE_GAS_LIMIT));
|
||||
assertEq(0, address(batch).balance);
|
||||
checkBatchState(address(0), 0, L1BatchBridgeGateway.BatchState(0, 0, 0, bytes32(0)));
|
||||
checkTokenState(address(0), L1BatchBridgeGateway.TokenState(0, 0, 0));
|
||||
|
||||
hevm.warp(1000001);
|
||||
hevm.expectEmit(true, true, true, true);
|
||||
emit Deposit(address(this), address(0), 0, 1000, 0);
|
||||
batch.depositETH{value: 1000}();
|
||||
assertEq(1000, address(batch).balance);
|
||||
checkBatchState(address(0), 0, L1BatchBridgeGateway.BatchState(1000, 1000001, 1, bytes32(0)));
|
||||
checkTokenState(address(0), L1BatchBridgeGateway.TokenState(1000, 0, 0));
|
||||
|
||||
hevm.warp(1000002);
|
||||
hevm.expectEmit(true, true, true, true);
|
||||
emit Deposit(address(this), address(0), 0, 2000, 0);
|
||||
batch.depositETH{value: 2000}();
|
||||
assertEq(3000, address(batch).balance);
|
||||
checkBatchState(address(0), 0, L1BatchBridgeGateway.BatchState(3000, 1000001, 2, bytes32(0)));
|
||||
checkTokenState(address(0), L1BatchBridgeGateway.TokenState(3000, 0, 0));
|
||||
|
||||
hevm.warp(1000003);
|
||||
hevm.expectEmit(true, true, true, true);
|
||||
emit Deposit(address(this), address(0), 1, 3000, 0);
|
||||
batch.depositETH{value: 3000}();
|
||||
assertEq(6000, address(batch).balance);
|
||||
checkBatchState(address(0), 1, L1BatchBridgeGateway.BatchState(3000, 1000003, 1, bytes32(0)));
|
||||
checkTokenState(address(0), L1BatchBridgeGateway.TokenState(6000, 1, 0));
|
||||
|
||||
// with fee
|
||||
batch.setBatchConfig(
|
||||
address(0),
|
||||
L1BatchBridgeGateway.BatchConfig(100, 1000, 2, 100, ETH_DEPOSIT_SAFE_GAS_LIMIT)
|
||||
);
|
||||
|
||||
hevm.warp(1000004);
|
||||
hevm.expectEmit(true, true, true, true);
|
||||
emit Deposit(address(this), address(0), 1, 1000, 100);
|
||||
batch.depositETH{value: 1000}();
|
||||
assertEq(7000, address(batch).balance);
|
||||
checkBatchState(address(0), 1, L1BatchBridgeGateway.BatchState(3900, 1000003, 2, bytes32(0)));
|
||||
checkTokenState(address(0), L1BatchBridgeGateway.TokenState(6900, 1, 0));
|
||||
|
||||
hevm.warp(1000005);
|
||||
hevm.expectEmit(true, true, true, true);
|
||||
emit Deposit(address(this), address(0), 2, 2000, 100);
|
||||
batch.depositETH{value: 2000}();
|
||||
assertEq(9000, address(batch).balance);
|
||||
checkBatchState(address(0), 2, L1BatchBridgeGateway.BatchState(1900, 1000005, 1, bytes32(0)));
|
||||
checkTokenState(address(0), L1BatchBridgeGateway.TokenState(8800, 2, 0));
|
||||
|
||||
hevm.warp(1000006);
|
||||
hevm.expectEmit(true, true, true, true);
|
||||
emit Deposit(address(this), address(0), 2, 3000, 100);
|
||||
batch.depositETH{value: 3000}();
|
||||
assertEq(12000, address(batch).balance);
|
||||
checkBatchState(address(0), 2, L1BatchBridgeGateway.BatchState(4800, 1000005, 2, bytes32(0)));
|
||||
checkTokenState(address(0), L1BatchBridgeGateway.TokenState(11700, 2, 0));
|
||||
|
||||
// switch phase by timestamp
|
||||
batch.setBatchConfig(address(0), L1BatchBridgeGateway.BatchConfig(0, 0, 100, 100, ETH_DEPOSIT_SAFE_GAS_LIMIT));
|
||||
|
||||
hevm.expectEmit(true, true, true, true);
|
||||
emit Deposit(address(this), address(0), 2, 1000, 0);
|
||||
batch.depositETH{value: 1000}();
|
||||
assertEq(13000, address(batch).balance);
|
||||
checkBatchState(address(0), 2, L1BatchBridgeGateway.BatchState(5800, 1000005, 3, bytes32(0)));
|
||||
checkTokenState(address(0), L1BatchBridgeGateway.TokenState(12700, 2, 0));
|
||||
|
||||
hevm.warp(1000005 + 100 + 1);
|
||||
hevm.expectEmit(true, true, true, true);
|
||||
emit Deposit(address(this), address(0), 3, 1000, 0);
|
||||
batch.depositETH{value: 1000}();
|
||||
assertEq(14000, address(batch).balance);
|
||||
checkBatchState(address(0), 3, L1BatchBridgeGateway.BatchState(1000, 1000005 + 100 + 1, 1, bytes32(0)));
|
||||
checkTokenState(address(0), L1BatchBridgeGateway.TokenState(13700, 3, 0));
|
||||
}
|
||||
|
||||
function testDepositERC20() external {
|
||||
// revert token is zero
|
||||
hevm.expectRevert(L1BatchBridgeGateway.ErrorIncorrectMethodForETHDeposit.selector);
|
||||
batch.depositERC20(address(0), 0);
|
||||
|
||||
// revert token not supported
|
||||
hevm.expectRevert(L1BatchBridgeGateway.ErrorTokenNotSupported.selector);
|
||||
batch.depositERC20(address(l1Token), 0);
|
||||
|
||||
// revert deposit amount too small
|
||||
batch.setBatchConfig(
|
||||
address(l1Token),
|
||||
L1BatchBridgeGateway.BatchConfig(0, 100, 2, 100, ERC20_DEPOSIT_SAFE_GAS_LIMIT)
|
||||
);
|
||||
l1Token.approve(address(batch), 10);
|
||||
hevm.expectRevert(L1BatchBridgeGateway.ErrorDepositAmountTooSmall.selector);
|
||||
batch.depositERC20(address(l1Token), 10);
|
||||
|
||||
// no fee
|
||||
batch.setBatchConfig(
|
||||
address(l1Token),
|
||||
L1BatchBridgeGateway.BatchConfig(0, 0, 2, 100, ERC20_DEPOSIT_SAFE_GAS_LIMIT)
|
||||
);
|
||||
assertEq(0, l1Token.balanceOf(address(batch)));
|
||||
checkBatchState(address(l1Token), 0, L1BatchBridgeGateway.BatchState(0, 0, 0, bytes32(0)));
|
||||
checkTokenState(address(l1Token), L1BatchBridgeGateway.TokenState(0, 0, 0));
|
||||
|
||||
hevm.warp(1000001);
|
||||
l1Token.approve(address(batch), 1000);
|
||||
hevm.expectEmit(true, true, true, true);
|
||||
emit Deposit(address(this), address(l1Token), 0, 1000, 0);
|
||||
batch.depositERC20(address(l1Token), 1000);
|
||||
assertEq(1000, l1Token.balanceOf(address(batch)));
|
||||
checkBatchState(address(l1Token), 0, L1BatchBridgeGateway.BatchState(1000, 1000001, 1, bytes32(0)));
|
||||
checkTokenState(address(l1Token), L1BatchBridgeGateway.TokenState(1000, 0, 0));
|
||||
|
||||
hevm.warp(1000002);
|
||||
l1Token.approve(address(batch), 2000);
|
||||
hevm.expectEmit(true, true, true, true);
|
||||
emit Deposit(address(this), address(l1Token), 0, 2000, 0);
|
||||
batch.depositERC20(address(l1Token), 2000);
|
||||
assertEq(3000, l1Token.balanceOf(address(batch)));
|
||||
checkBatchState(address(l1Token), 0, L1BatchBridgeGateway.BatchState(3000, 1000001, 2, bytes32(0)));
|
||||
checkTokenState(address(l1Token), L1BatchBridgeGateway.TokenState(3000, 0, 0));
|
||||
|
||||
hevm.warp(1000003);
|
||||
l1Token.approve(address(batch), 3000);
|
||||
hevm.expectEmit(true, true, true, true);
|
||||
emit Deposit(address(this), address(l1Token), 1, 3000, 0);
|
||||
batch.depositERC20(address(l1Token), 3000);
|
||||
assertEq(6000, l1Token.balanceOf(address(batch)));
|
||||
checkBatchState(address(l1Token), 1, L1BatchBridgeGateway.BatchState(3000, 1000003, 1, bytes32(0)));
|
||||
checkTokenState(address(l1Token), L1BatchBridgeGateway.TokenState(6000, 1, 0));
|
||||
|
||||
// with fee
|
||||
batch.setBatchConfig(
|
||||
address(l1Token),
|
||||
L1BatchBridgeGateway.BatchConfig(100, 1000, 2, 100, ERC20_DEPOSIT_SAFE_GAS_LIMIT)
|
||||
);
|
||||
|
||||
hevm.warp(1000004);
|
||||
l1Token.approve(address(batch), 1000);
|
||||
hevm.expectEmit(true, true, true, true);
|
||||
emit Deposit(address(this), address(l1Token), 1, 1000, 100);
|
||||
batch.depositERC20(address(l1Token), 1000);
|
||||
assertEq(7000, l1Token.balanceOf(address(batch)));
|
||||
checkBatchState(address(l1Token), 1, L1BatchBridgeGateway.BatchState(3900, 1000003, 2, bytes32(0)));
|
||||
checkTokenState(address(l1Token), L1BatchBridgeGateway.TokenState(6900, 1, 0));
|
||||
|
||||
hevm.warp(1000005);
|
||||
l1Token.approve(address(batch), 2000);
|
||||
hevm.expectEmit(true, true, true, true);
|
||||
emit Deposit(address(this), address(l1Token), 2, 2000, 100);
|
||||
batch.depositERC20(address(l1Token), 2000);
|
||||
assertEq(9000, l1Token.balanceOf(address(batch)));
|
||||
checkBatchState(address(l1Token), 2, L1BatchBridgeGateway.BatchState(1900, 1000005, 1, bytes32(0)));
|
||||
checkTokenState(address(l1Token), L1BatchBridgeGateway.TokenState(8800, 2, 0));
|
||||
|
||||
hevm.warp(1000006);
|
||||
l1Token.approve(address(batch), 3000);
|
||||
hevm.expectEmit(true, true, true, true);
|
||||
emit Deposit(address(this), address(l1Token), 2, 3000, 100);
|
||||
batch.depositERC20(address(l1Token), 3000);
|
||||
assertEq(12000, l1Token.balanceOf(address(batch)));
|
||||
checkBatchState(address(l1Token), 2, L1BatchBridgeGateway.BatchState(4800, 1000005, 2, bytes32(0)));
|
||||
checkTokenState(address(l1Token), L1BatchBridgeGateway.TokenState(11700, 2, 0));
|
||||
|
||||
// switch phase by timestamp
|
||||
batch.setBatchConfig(
|
||||
address(l1Token),
|
||||
L1BatchBridgeGateway.BatchConfig(0, 0, 100, 100, ERC20_DEPOSIT_SAFE_GAS_LIMIT)
|
||||
);
|
||||
|
||||
l1Token.approve(address(batch), 1000);
|
||||
hevm.expectEmit(true, true, true, true);
|
||||
emit Deposit(address(this), address(l1Token), 2, 1000, 0);
|
||||
batch.depositERC20(address(l1Token), 1000);
|
||||
assertEq(13000, l1Token.balanceOf(address(batch)));
|
||||
checkBatchState(address(l1Token), 2, L1BatchBridgeGateway.BatchState(5800, 1000005, 3, bytes32(0)));
|
||||
checkTokenState(address(l1Token), L1BatchBridgeGateway.TokenState(12700, 2, 0));
|
||||
|
||||
hevm.warp(1000005 + 100 + 1);
|
||||
l1Token.approve(address(batch), 1000);
|
||||
hevm.expectEmit(true, true, true, true);
|
||||
emit Deposit(address(this), address(l1Token), 3, 1000, 0);
|
||||
batch.depositERC20(address(l1Token), 1000);
|
||||
assertEq(14000, l1Token.balanceOf(address(batch)));
|
||||
checkBatchState(address(l1Token), 3, L1BatchBridgeGateway.BatchState(1000, 1000005 + 100 + 1, 1, bytes32(0)));
|
||||
checkTokenState(address(l1Token), L1BatchBridgeGateway.TokenState(13700, 3, 0));
|
||||
}
|
||||
|
||||
function testBatchBridgeFailure() external {
|
||||
// revert not keeper
|
||||
hevm.startPrank(address(1));
|
||||
hevm.expectRevert(
|
||||
"AccessControl: account 0x0000000000000000000000000000000000000001 is missing role 0xfc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab"
|
||||
);
|
||||
batch.executeBatchDeposit(address(0));
|
||||
hevm.stopPrank();
|
||||
|
||||
batch.grantRole(batch.KEEPER_ROLE(), address(this));
|
||||
|
||||
// revert token not supported
|
||||
hevm.expectRevert(L1BatchBridgeGateway.ErrorTokenNotSupported.selector);
|
||||
batch.executeBatchDeposit(address(0));
|
||||
|
||||
batch.setBatchConfig(address(0), L1BatchBridgeGateway.BatchConfig(0, 0, 1, 1, ETH_DEPOSIT_SAFE_GAS_LIMIT));
|
||||
|
||||
// revert no pending
|
||||
hevm.expectRevert(L1BatchBridgeGateway.ErrorNoPendingBatch.selector);
|
||||
batch.executeBatchDeposit(address(0));
|
||||
|
||||
// revert insufficient msg.value
|
||||
batch.depositETH{value: 1000}();
|
||||
hevm.expectRevert(L1BatchBridgeGateway.ErrorInsufficientMsgValueForBatchDepositFee.selector);
|
||||
batch.executeBatchDeposit(address(0));
|
||||
|
||||
hevm.expectRevert(L1BatchBridgeGateway.ErrorInsufficientMsgValueForBatchDepositFee.selector);
|
||||
batch.executeBatchDeposit{value: L2_GAS_PRICE * ETH_DEPOSIT_SAFE_GAS_LIMIT}(address(0));
|
||||
|
||||
hevm.expectRevert(L1BatchBridgeGateway.ErrorInsufficientMsgValueForBatchDepositFee.selector);
|
||||
batch.executeBatchDeposit{value: L2_GAS_PRICE * (SAFE_BATCH_BRIDGE_GAS_LIMIT + ETH_DEPOSIT_SAFE_GAS_LIMIT) - 1}(
|
||||
address(0)
|
||||
);
|
||||
|
||||
// succeed
|
||||
batch.executeBatchDeposit{value: L2_GAS_PRICE * (SAFE_BATCH_BRIDGE_GAS_LIMIT + ETH_DEPOSIT_SAFE_GAS_LIMIT)}(
|
||||
address(0)
|
||||
);
|
||||
}
|
||||
|
||||
function testBatchBridgeETH() external {
|
||||
batch.grantRole(batch.KEEPER_ROLE(), address(this));
|
||||
|
||||
// no deposit fee
|
||||
batch.setBatchConfig(address(0), L1BatchBridgeGateway.BatchConfig(0, 0, 1, 1, ETH_DEPOSIT_SAFE_GAS_LIMIT));
|
||||
batch.depositETH{value: 1000}();
|
||||
checkTokenState(address(0), L1BatchBridgeGateway.TokenState(1000, 0, 0));
|
||||
|
||||
// emit SentMessage by deposit ETH
|
||||
hevm.expectEmit(true, true, false, true);
|
||||
emit SentMessage(address(batch), address(counterpartBatch), 1000, 0, ETH_DEPOSIT_SAFE_GAS_LIMIT, "");
|
||||
|
||||
// emit SentMessage by batchBridge
|
||||
hevm.expectEmit(true, true, false, true);
|
||||
emit SentMessage(
|
||||
address(batch),
|
||||
address(counterpartBatch),
|
||||
0,
|
||||
1,
|
||||
SAFE_BATCH_BRIDGE_GAS_LIMIT,
|
||||
abi.encodeCall(
|
||||
L2BatchBridgeGateway.finalizeBatchDeposit,
|
||||
(
|
||||
address(0),
|
||||
address(0),
|
||||
0,
|
||||
BatchBridgeCodec.hash(
|
||||
BatchBridgeCodec.encodeInitialNode(address(0), 0),
|
||||
BatchBridgeCodec.encodeNode(address(this), 1000)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// emit BatchBridge
|
||||
hevm.expectEmit(true, true, true, true);
|
||||
emit BatchDeposit(address(this), address(0), 0, address(0));
|
||||
|
||||
uint256 batchFeeVaultBefore = batchFeeVault.balance;
|
||||
uint256 messengerBefore = address(l1Messenger).balance;
|
||||
batch.executeBatchDeposit{value: 1 ether}(address(0));
|
||||
checkTokenState(address(0), L1BatchBridgeGateway.TokenState(0, 1, 1));
|
||||
assertEq(batchFeeVaultBefore, batchFeeVault.balance);
|
||||
assertEq(messengerBefore + 1000, address(l1Messenger).balance);
|
||||
|
||||
// has deposit fee = 100
|
||||
batch.setBatchConfig(address(0), L1BatchBridgeGateway.BatchConfig(100, 1000, 1, 1, ETH_DEPOSIT_SAFE_GAS_LIMIT));
|
||||
|
||||
batch.depositETH{value: 1000}();
|
||||
checkTokenState(address(0), L1BatchBridgeGateway.TokenState(900, 1, 1));
|
||||
|
||||
// emit SentMessage by deposit ETH
|
||||
hevm.expectEmit(true, true, false, true);
|
||||
emit SentMessage(address(batch), address(counterpartBatch), 900, 2, ETH_DEPOSIT_SAFE_GAS_LIMIT, "");
|
||||
|
||||
// emit SentMessage by batchBridge
|
||||
hevm.expectEmit(true, true, false, true);
|
||||
emit SentMessage(
|
||||
address(batch),
|
||||
address(counterpartBatch),
|
||||
0,
|
||||
3,
|
||||
SAFE_BATCH_BRIDGE_GAS_LIMIT,
|
||||
abi.encodeCall(
|
||||
L2BatchBridgeGateway.finalizeBatchDeposit,
|
||||
(
|
||||
address(0),
|
||||
address(0),
|
||||
1,
|
||||
BatchBridgeCodec.hash(
|
||||
BatchBridgeCodec.encodeInitialNode(address(0), 1),
|
||||
BatchBridgeCodec.encodeNode(address(this), 900)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// emit BatchBridge
|
||||
hevm.expectEmit(true, true, true, true);
|
||||
emit BatchDeposit(address(this), address(0), 1, address(0));
|
||||
|
||||
batchFeeVaultBefore = batchFeeVault.balance;
|
||||
messengerBefore = address(l1Messenger).balance;
|
||||
batch.executeBatchDeposit{value: 1 ether}(address(0));
|
||||
checkTokenState(address(0), L1BatchBridgeGateway.TokenState(0, 2, 2));
|
||||
assertEq(batchFeeVaultBefore + 100, batchFeeVault.balance);
|
||||
assertEq(messengerBefore + 900, address(l1Messenger).balance);
|
||||
}
|
||||
|
||||
function testBatchBridgeERC20() external {
|
||||
batch.grantRole(batch.KEEPER_ROLE(), address(this));
|
||||
|
||||
// no deposit fee
|
||||
batch.setBatchConfig(
|
||||
address(l1Token),
|
||||
L1BatchBridgeGateway.BatchConfig(0, 0, 1, 1, ERC20_DEPOSIT_SAFE_GAS_LIMIT)
|
||||
);
|
||||
l1Token.approve(address(batch), 1000);
|
||||
batch.depositERC20(address(l1Token), 1000);
|
||||
checkTokenState(address(l1Token), L1BatchBridgeGateway.TokenState(1000, 0, 0));
|
||||
|
||||
bytes memory message = abi.encodeWithSelector(
|
||||
IL2ERC20Gateway.finalizeDepositERC20.selector,
|
||||
address(l1Token),
|
||||
address(l2Token),
|
||||
address(batch),
|
||||
address(counterpartBatch),
|
||||
1000,
|
||||
new bytes(0)
|
||||
);
|
||||
// emit SentMessage by deposit ERC20
|
||||
hevm.expectEmit(true, true, false, true);
|
||||
emit SentMessage(address(gateway), address(counterpartGateway), 0, 0, ERC20_DEPOSIT_SAFE_GAS_LIMIT, message);
|
||||
// emit SentMessage by batchBridge
|
||||
hevm.expectEmit(true, true, false, true);
|
||||
emit SentMessage(
|
||||
address(batch),
|
||||
address(counterpartBatch),
|
||||
0,
|
||||
1,
|
||||
SAFE_BATCH_BRIDGE_GAS_LIMIT,
|
||||
abi.encodeCall(
|
||||
L2BatchBridgeGateway.finalizeBatchDeposit,
|
||||
(
|
||||
address(l1Token),
|
||||
address(l2Token),
|
||||
0,
|
||||
BatchBridgeCodec.hash(
|
||||
BatchBridgeCodec.encodeInitialNode(address(l1Token), 0),
|
||||
BatchBridgeCodec.encodeNode(address(this), 1000)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
// emit BatchBridge
|
||||
hevm.expectEmit(true, true, true, true);
|
||||
emit BatchDeposit(address(this), address(l1Token), 0, address(l2Token));
|
||||
|
||||
uint256 batchFeeVaultBefore = l1Token.balanceOf(batchFeeVault);
|
||||
uint256 gatewayBefore = l1Token.balanceOf(address(gateway));
|
||||
batch.executeBatchDeposit{value: 1 ether}(address(l1Token));
|
||||
checkTokenState(address(l1Token), L1BatchBridgeGateway.TokenState(0, 1, 1));
|
||||
assertEq(batchFeeVaultBefore, l1Token.balanceOf(batchFeeVault));
|
||||
assertEq(gatewayBefore + 1000, l1Token.balanceOf(address(gateway)));
|
||||
|
||||
// has deposit fee = 100
|
||||
batch.setBatchConfig(
|
||||
address(l1Token),
|
||||
L1BatchBridgeGateway.BatchConfig(100, 1000, 1, 1, ERC20_DEPOSIT_SAFE_GAS_LIMIT)
|
||||
);
|
||||
|
||||
l1Token.approve(address(batch), 1000);
|
||||
batch.depositERC20(address(l1Token), 1000);
|
||||
checkTokenState(address(l1Token), L1BatchBridgeGateway.TokenState(900, 1, 1));
|
||||
|
||||
message = abi.encodeWithSelector(
|
||||
IL2ERC20Gateway.finalizeDepositERC20.selector,
|
||||
address(l1Token),
|
||||
address(l2Token),
|
||||
address(batch),
|
||||
address(counterpartBatch),
|
||||
900,
|
||||
new bytes(0)
|
||||
);
|
||||
// emit SentMessage by deposit ERC20
|
||||
hevm.expectEmit(true, true, false, true);
|
||||
emit SentMessage(address(gateway), address(counterpartGateway), 0, 2, ERC20_DEPOSIT_SAFE_GAS_LIMIT, message);
|
||||
// emit SentMessage by batchBridge
|
||||
hevm.expectEmit(true, true, false, true);
|
||||
emit SentMessage(
|
||||
address(batch),
|
||||
address(counterpartBatch),
|
||||
0,
|
||||
3,
|
||||
SAFE_BATCH_BRIDGE_GAS_LIMIT,
|
||||
abi.encodeCall(
|
||||
L2BatchBridgeGateway.finalizeBatchDeposit,
|
||||
(
|
||||
address(l1Token),
|
||||
address(l2Token),
|
||||
1,
|
||||
BatchBridgeCodec.hash(
|
||||
BatchBridgeCodec.encodeInitialNode(address(l1Token), 1),
|
||||
BatchBridgeCodec.encodeNode(address(this), 900)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
// emit BatchBridge
|
||||
hevm.expectEmit(true, true, true, true);
|
||||
emit BatchDeposit(address(this), address(l1Token), 1, address(l2Token));
|
||||
|
||||
batchFeeVaultBefore = l1Token.balanceOf(batchFeeVault);
|
||||
gatewayBefore = l1Token.balanceOf(address(gateway));
|
||||
batch.executeBatchDeposit{value: 1 ether}(address(l1Token));
|
||||
checkTokenState(address(l1Token), L1BatchBridgeGateway.TokenState(0, 2, 2));
|
||||
assertEq(batchFeeVaultBefore + 100, l1Token.balanceOf(batchFeeVault));
|
||||
assertEq(gatewayBefore + 900, l1Token.balanceOf(address(gateway)));
|
||||
}
|
||||
}
|
||||
@@ -1,454 +0,0 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity =0.8.24;
|
||||
|
||||
import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol";
|
||||
|
||||
import {ITransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
|
||||
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
|
||||
|
||||
import {L1BatchBridgeGateway} from "../../batch-bridge/L1BatchBridgeGateway.sol";
|
||||
import {L2BatchBridgeGateway} from "../../batch-bridge/L2BatchBridgeGateway.sol";
|
||||
import {BatchBridgeCodec} from "../../batch-bridge/BatchBridgeCodec.sol";
|
||||
|
||||
import {RevertOnTransferToken} from "../mocks/tokens/RevertOnTransferToken.sol";
|
||||
import {MockScrollMessenger} from "../mocks/MockScrollMessenger.sol";
|
||||
import {ScrollTestBase} from "../ScrollTestBase.t.sol";
|
||||
|
||||
contract L2BatchBridgeGatewayTest is ScrollTestBase {
|
||||
event UpdateTokenMapping(address indexed l2Token, address indexed oldL1Token, address indexed newL1Token);
|
||||
event FinalizeBatchDeposit(address indexed l1Token, address indexed l2Token, uint256 indexed batchIndex);
|
||||
event BatchDistribute(address indexed l1Token, address indexed l2Token, uint256 indexed batchIndex);
|
||||
event DistributeFailed(address indexed l2Token, uint256 indexed batchIndex, address receiver, uint256 amount);
|
||||
|
||||
L1BatchBridgeGateway private counterpartBatch;
|
||||
L2BatchBridgeGateway private batch;
|
||||
|
||||
MockScrollMessenger messenger;
|
||||
|
||||
MockERC20 private l1Token;
|
||||
MockERC20 private l2Token;
|
||||
RevertOnTransferToken private maliciousL2Token;
|
||||
|
||||
bool revertOnReceive;
|
||||
bool loopOnReceive;
|
||||
|
||||
// two safe EOAs to receive ETH
|
||||
address private recipient1;
|
||||
address private recipient2;
|
||||
|
||||
receive() external payable {
|
||||
if (revertOnReceive) revert();
|
||||
if (loopOnReceive) {
|
||||
for (uint256 i = 0; i < 1000000000; i++) {
|
||||
recipient1 = address(uint160(address(this)) - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setUp() public {
|
||||
__ScrollTestBase_setUp();
|
||||
|
||||
recipient1 = address(uint160(address(this)) - 1);
|
||||
recipient2 = address(uint160(address(this)) - 2);
|
||||
|
||||
// Deploy tokens
|
||||
l1Token = new MockERC20("Mock L1", "ML1", 18);
|
||||
l2Token = new MockERC20("Mock L2", "ML2", 18);
|
||||
maliciousL2Token = new RevertOnTransferToken("X", "Y", 18);
|
||||
|
||||
messenger = new MockScrollMessenger();
|
||||
counterpartBatch = new L1BatchBridgeGateway(address(1), address(1), address(1), address(1));
|
||||
batch = L2BatchBridgeGateway(payable(_deployProxy(address(0))));
|
||||
|
||||
// Initialize L2 contracts
|
||||
admin.upgrade(
|
||||
ITransparentUpgradeableProxy(address(batch)),
|
||||
address(new L2BatchBridgeGateway(address(counterpartBatch), address(messenger)))
|
||||
);
|
||||
batch.initialize();
|
||||
}
|
||||
|
||||
function testInitialized() external {
|
||||
assertBoolEq(true, batch.hasRole(bytes32(0), address(this)));
|
||||
assertEq(address(counterpartBatch), batch.counterpart());
|
||||
assertEq(address(messenger), batch.messenger());
|
||||
|
||||
hevm.expectRevert("Initializable: contract is already initialized");
|
||||
batch.initialize();
|
||||
}
|
||||
|
||||
function testFinalizeBatchDeposit() external {
|
||||
// revert caller not messenger
|
||||
hevm.expectRevert(L2BatchBridgeGateway.ErrorCallerNotMessenger.selector);
|
||||
batch.finalizeBatchDeposit(address(0), address(0), 0, bytes32(0));
|
||||
|
||||
// revert xDomainMessageSender not counterpart
|
||||
hevm.expectRevert(L2BatchBridgeGateway.ErrorMessageSenderNotCounterpart.selector);
|
||||
messenger.callTarget(
|
||||
address(batch),
|
||||
abi.encodeCall(
|
||||
L2BatchBridgeGateway.finalizeBatchDeposit,
|
||||
(address(l1Token), address(l2Token), 0, bytes32(0))
|
||||
)
|
||||
);
|
||||
|
||||
messenger.setXDomainMessageSender(address(counterpartBatch));
|
||||
|
||||
// emit FinalizeBatchDeposit
|
||||
assertEq(address(0), batch.tokenMapping(address(l2Token)));
|
||||
hevm.expectEmit(true, true, true, true);
|
||||
emit FinalizeBatchDeposit(address(l1Token), address(l2Token), 1);
|
||||
messenger.callTarget(
|
||||
address(batch),
|
||||
abi.encodeCall(
|
||||
L2BatchBridgeGateway.finalizeBatchDeposit,
|
||||
(address(l1Token), address(l2Token), 1, bytes32(uint256(1)))
|
||||
)
|
||||
);
|
||||
assertEq(address(l1Token), batch.tokenMapping(address(l2Token)));
|
||||
assertEq(batch.batchHashes(address(l2Token), 1), bytes32(uint256(1)));
|
||||
|
||||
// revert token not match
|
||||
hevm.expectRevert(L2BatchBridgeGateway.ErrorL1TokenMismatched.selector);
|
||||
messenger.callTarget(
|
||||
address(batch),
|
||||
abi.encodeCall(L2BatchBridgeGateway.finalizeBatchDeposit, (address(0), address(l2Token), 0, bytes32(0)))
|
||||
);
|
||||
}
|
||||
|
||||
function testFinalizeBatchDepositFuzzing(
|
||||
address token1,
|
||||
address token2,
|
||||
uint256 batchIndex,
|
||||
bytes32 hash
|
||||
) external {
|
||||
messenger.setXDomainMessageSender(address(counterpartBatch));
|
||||
|
||||
assertEq(address(0), batch.tokenMapping(token2));
|
||||
hevm.expectEmit(true, true, true, true);
|
||||
emit FinalizeBatchDeposit(token1, token2, batchIndex);
|
||||
messenger.callTarget(
|
||||
address(batch),
|
||||
abi.encodeCall(L2BatchBridgeGateway.finalizeBatchDeposit, (token1, token2, batchIndex, hash))
|
||||
);
|
||||
|
||||
assertEq(token1, batch.tokenMapping(token2));
|
||||
assertEq(batch.batchHashes(token2, batchIndex), hash);
|
||||
}
|
||||
|
||||
function testDistributeETH() external {
|
||||
// revert not keeper
|
||||
hevm.startPrank(address(1));
|
||||
hevm.expectRevert(
|
||||
"AccessControl: account 0x0000000000000000000000000000000000000001 is missing role 0xfc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab"
|
||||
);
|
||||
batch.distribute(address(0), 0, new bytes32[](0));
|
||||
hevm.stopPrank();
|
||||
|
||||
batch.grantRole(batch.KEEPER_ROLE(), address(this));
|
||||
|
||||
// revert ErrorBatchHashMismatch
|
||||
hevm.expectRevert(L2BatchBridgeGateway.ErrorBatchHashMismatch.selector);
|
||||
batch.distribute(address(0), 1, new bytes32[](0));
|
||||
|
||||
// send some ETH to `L2BatchBridgeGateway`.
|
||||
messenger.setXDomainMessageSender(address(counterpartBatch));
|
||||
messenger.callTarget{value: 1 ether}(address(batch), "");
|
||||
|
||||
address[] memory receivers = new address[](2);
|
||||
uint256[] memory amounts = new uint256[](2);
|
||||
receivers[0] = recipient1;
|
||||
receivers[1] = recipient2;
|
||||
amounts[0] = 100;
|
||||
amounts[1] = 200;
|
||||
|
||||
// all success
|
||||
(bytes32[] memory nodes, bytes32 batchHash) = _encodeNodes(address(0), 0, receivers, amounts);
|
||||
messenger.callTarget(
|
||||
address(batch),
|
||||
abi.encodeCall(L2BatchBridgeGateway.finalizeBatchDeposit, (address(0), address(0), 0, batchHash))
|
||||
);
|
||||
assertEq(0, recipient1.balance);
|
||||
assertEq(0, recipient2.balance);
|
||||
uint256 batchBalanceBefore = address(batch).balance;
|
||||
hevm.expectEmit(true, true, true, true);
|
||||
emit BatchDistribute(address(0), address(0), 0);
|
||||
batch.distribute(address(0), 0, nodes);
|
||||
assertEq(100, recipient1.balance);
|
||||
assertEq(200, recipient2.balance);
|
||||
assertEq(batchBalanceBefore - 300, address(batch).balance);
|
||||
assertBoolEq(true, batch.isDistributed(batchHash));
|
||||
|
||||
// revert ErrorBatchDistributed
|
||||
hevm.expectRevert(L2BatchBridgeGateway.ErrorBatchDistributed.selector);
|
||||
batch.distribute(address(0), 0, nodes);
|
||||
|
||||
// all failed due to revert
|
||||
revertOnReceive = true;
|
||||
loopOnReceive = false;
|
||||
receivers[0] = address(this);
|
||||
receivers[1] = address(this);
|
||||
(nodes, batchHash) = _encodeNodes(address(0), 1, receivers, amounts);
|
||||
messenger.callTarget(
|
||||
address(batch),
|
||||
abi.encodeCall(L2BatchBridgeGateway.finalizeBatchDeposit, (address(0), address(0), 1, batchHash))
|
||||
);
|
||||
uint256 thisBalanceBefore = address(this).balance;
|
||||
batchBalanceBefore = address(batch).balance;
|
||||
hevm.expectEmit(true, true, false, true);
|
||||
emit DistributeFailed(address(0), 1, address(this), 100);
|
||||
hevm.expectEmit(true, true, false, true);
|
||||
emit DistributeFailed(address(0), 1, address(this), 200);
|
||||
hevm.expectEmit(true, true, true, true);
|
||||
emit BatchDistribute(address(0), address(0), 1);
|
||||
batch.distribute(address(0), 1, nodes);
|
||||
assertEq(batchBalanceBefore, address(batch).balance);
|
||||
assertEq(thisBalanceBefore, address(this).balance);
|
||||
assertBoolEq(true, batch.isDistributed(batchHash));
|
||||
assertEq(300, batch.failedAmount(address(0)));
|
||||
|
||||
// all failed due to out of gas
|
||||
revertOnReceive = false;
|
||||
loopOnReceive = true;
|
||||
(nodes, batchHash) = _encodeNodes(address(0), 2, receivers, amounts);
|
||||
messenger.callTarget(
|
||||
address(batch),
|
||||
abi.encodeCall(L2BatchBridgeGateway.finalizeBatchDeposit, (address(0), address(0), 2, batchHash))
|
||||
);
|
||||
thisBalanceBefore = address(this).balance;
|
||||
batchBalanceBefore = address(batch).balance;
|
||||
hevm.expectEmit(true, true, false, true);
|
||||
emit DistributeFailed(address(0), 2, address(this), 100);
|
||||
hevm.expectEmit(true, true, false, true);
|
||||
emit DistributeFailed(address(0), 2, address(this), 200);
|
||||
hevm.expectEmit(true, true, true, true);
|
||||
emit BatchDistribute(address(0), address(0), 2);
|
||||
batch.distribute(address(0), 2, nodes);
|
||||
assertEq(batchBalanceBefore, address(batch).balance);
|
||||
assertEq(thisBalanceBefore, address(this).balance);
|
||||
assertBoolEq(true, batch.isDistributed(batchHash));
|
||||
assertEq(600, batch.failedAmount(address(0)));
|
||||
}
|
||||
|
||||
function testDistributeERC20() external {
|
||||
// revert not keeper
|
||||
hevm.startPrank(address(1));
|
||||
hevm.expectRevert(
|
||||
"AccessControl: account 0x0000000000000000000000000000000000000001 is missing role 0xfc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab"
|
||||
);
|
||||
batch.distribute(address(l2Token), 0, new bytes32[](0));
|
||||
hevm.stopPrank();
|
||||
|
||||
batch.grantRole(batch.KEEPER_ROLE(), address(this));
|
||||
|
||||
// revert ErrorBatchHashMismatch
|
||||
hevm.expectRevert(L2BatchBridgeGateway.ErrorBatchHashMismatch.selector);
|
||||
batch.distribute(address(l2Token), 1, new bytes32[](0));
|
||||
|
||||
// mint some ERC20 to `L2BatchBridgeGateway`.
|
||||
messenger.setXDomainMessageSender(address(counterpartBatch));
|
||||
l2Token.mint(address(batch), 1 ether);
|
||||
|
||||
address[] memory receivers = new address[](2);
|
||||
uint256[] memory amounts = new uint256[](2);
|
||||
receivers[0] = recipient1;
|
||||
receivers[1] = recipient2;
|
||||
amounts[0] = 100;
|
||||
amounts[1] = 200;
|
||||
|
||||
// all success
|
||||
(bytes32[] memory nodes, bytes32 batchHash) = _encodeNodes(address(l1Token), 0, receivers, amounts);
|
||||
messenger.callTarget(
|
||||
address(batch),
|
||||
abi.encodeCall(
|
||||
L2BatchBridgeGateway.finalizeBatchDeposit,
|
||||
(address(l1Token), address(l2Token), 0, batchHash)
|
||||
)
|
||||
);
|
||||
assertEq(0, recipient1.balance);
|
||||
assertEq(0, recipient2.balance);
|
||||
uint256 batchBalanceBefore = l2Token.balanceOf(address(batch));
|
||||
hevm.expectEmit(true, true, true, true);
|
||||
emit BatchDistribute(address(l1Token), address(l2Token), 0);
|
||||
batch.distribute(address(l2Token), 0, nodes);
|
||||
assertEq(100, l2Token.balanceOf(recipient1));
|
||||
assertEq(200, l2Token.balanceOf(recipient2));
|
||||
assertEq(batchBalanceBefore - 300, l2Token.balanceOf(address(batch)));
|
||||
assertBoolEq(true, batch.isDistributed(batchHash));
|
||||
|
||||
// revert ErrorBatchDistributed
|
||||
hevm.expectRevert(L2BatchBridgeGateway.ErrorBatchDistributed.selector);
|
||||
batch.distribute(address(l2Token), 0, nodes);
|
||||
|
||||
maliciousL2Token.mint(address(batch), 1 ether);
|
||||
|
||||
// all failed due to revert
|
||||
maliciousL2Token.setRevertOnTransfer(true);
|
||||
receivers[0] = address(this);
|
||||
receivers[1] = address(this);
|
||||
(nodes, batchHash) = _encodeNodes(address(l1Token), 1, receivers, amounts);
|
||||
messenger.callTarget(
|
||||
address(batch),
|
||||
abi.encodeCall(
|
||||
L2BatchBridgeGateway.finalizeBatchDeposit,
|
||||
(address(l1Token), address(maliciousL2Token), 1, batchHash)
|
||||
)
|
||||
);
|
||||
uint256 thisBalanceBefore = maliciousL2Token.balanceOf(address(this));
|
||||
batchBalanceBefore = maliciousL2Token.balanceOf(address(batch));
|
||||
hevm.expectEmit(true, true, false, true);
|
||||
emit DistributeFailed(address(maliciousL2Token), 1, address(this), 100);
|
||||
hevm.expectEmit(true, true, false, true);
|
||||
emit DistributeFailed(address(maliciousL2Token), 1, address(this), 200);
|
||||
hevm.expectEmit(true, true, true, true);
|
||||
emit BatchDistribute(address(l1Token), address(maliciousL2Token), 1);
|
||||
batch.distribute(address(maliciousL2Token), 1, nodes);
|
||||
assertEq(batchBalanceBefore, maliciousL2Token.balanceOf(address(batch)));
|
||||
assertEq(thisBalanceBefore, maliciousL2Token.balanceOf(address(this)));
|
||||
assertBoolEq(true, batch.isDistributed(batchHash));
|
||||
assertEq(300, batch.failedAmount(address(maliciousL2Token)));
|
||||
|
||||
// all failed due to transfer return false
|
||||
maliciousL2Token.setRevertOnTransfer(false);
|
||||
maliciousL2Token.setTransferReturn(false);
|
||||
(nodes, batchHash) = _encodeNodes(address(l1Token), 2, receivers, amounts);
|
||||
messenger.callTarget(
|
||||
address(batch),
|
||||
abi.encodeCall(
|
||||
L2BatchBridgeGateway.finalizeBatchDeposit,
|
||||
(address(l1Token), address(maliciousL2Token), 2, batchHash)
|
||||
)
|
||||
);
|
||||
thisBalanceBefore = maliciousL2Token.balanceOf(address(this));
|
||||
batchBalanceBefore = maliciousL2Token.balanceOf(address(batch));
|
||||
hevm.expectEmit(true, true, false, true);
|
||||
emit DistributeFailed(address(maliciousL2Token), 2, address(this), 100);
|
||||
hevm.expectEmit(true, true, false, true);
|
||||
emit DistributeFailed(address(maliciousL2Token), 2, address(this), 200);
|
||||
hevm.expectEmit(true, true, true, true);
|
||||
emit BatchDistribute(address(l1Token), address(maliciousL2Token), 2);
|
||||
batch.distribute(address(maliciousL2Token), 2, nodes);
|
||||
assertEq(batchBalanceBefore, maliciousL2Token.balanceOf(address(batch)));
|
||||
assertEq(thisBalanceBefore, maliciousL2Token.balanceOf(address(this)));
|
||||
assertBoolEq(true, batch.isDistributed(batchHash));
|
||||
assertEq(600, batch.failedAmount(address(maliciousL2Token)));
|
||||
}
|
||||
|
||||
function testWithdrawFailedAmountETH() external {
|
||||
batch.grantRole(batch.KEEPER_ROLE(), address(this));
|
||||
|
||||
// revert not admin
|
||||
hevm.startPrank(address(1));
|
||||
hevm.expectRevert(
|
||||
"AccessControl: account 0x0000000000000000000000000000000000000001 is missing role 0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
);
|
||||
batch.withdrawFailedAmount(address(0), address(this));
|
||||
hevm.stopPrank();
|
||||
|
||||
// revert no failed
|
||||
hevm.expectRevert(L2BatchBridgeGateway.ErrorNoFailedDistribution.selector);
|
||||
batch.withdrawFailedAmount(address(0), address(this));
|
||||
|
||||
// send some ETH to `L2BatchBridgeGateway`.
|
||||
messenger.setXDomainMessageSender(address(counterpartBatch));
|
||||
messenger.callTarget{value: 1 ether}(address(batch), "");
|
||||
|
||||
// make a failed distribution
|
||||
address[] memory receivers = new address[](2);
|
||||
uint256[] memory amounts = new uint256[](2);
|
||||
receivers[0] = address(this);
|
||||
receivers[1] = address(this);
|
||||
amounts[0] = 100;
|
||||
amounts[1] = 200;
|
||||
revertOnReceive = true;
|
||||
(bytes32[] memory nodes, bytes32 batchHash) = _encodeNodes(address(0), 1, receivers, amounts);
|
||||
messenger.callTarget(
|
||||
address(batch),
|
||||
abi.encodeCall(L2BatchBridgeGateway.finalizeBatchDeposit, (address(0), address(0), 1, batchHash))
|
||||
);
|
||||
assertEq(0, batch.failedAmount(address(0)));
|
||||
batch.distribute(address(0), 1, nodes);
|
||||
assertEq(300, batch.failedAmount(address(0)));
|
||||
|
||||
// withdraw failed
|
||||
uint256 thisBalance = recipient1.balance;
|
||||
uint256 batchBalance = address(batch).balance;
|
||||
batch.withdrawFailedAmount(address(0), recipient1);
|
||||
assertEq(0, batch.failedAmount(address(0)));
|
||||
assertEq(thisBalance + 300, recipient1.balance);
|
||||
assertEq(batchBalance - 300, address(batch).balance);
|
||||
|
||||
// revert no failed
|
||||
hevm.expectRevert(L2BatchBridgeGateway.ErrorNoFailedDistribution.selector);
|
||||
batch.withdrawFailedAmount(address(0), recipient1);
|
||||
}
|
||||
|
||||
function testWithdrawFailedAmountERC20() external {
|
||||
batch.grantRole(batch.KEEPER_ROLE(), address(this));
|
||||
|
||||
// revert not admin
|
||||
hevm.startPrank(address(1));
|
||||
hevm.expectRevert(
|
||||
"AccessControl: account 0x0000000000000000000000000000000000000001 is missing role 0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
);
|
||||
batch.withdrawFailedAmount(address(0), address(this));
|
||||
hevm.stopPrank();
|
||||
|
||||
// revert no failed
|
||||
hevm.expectRevert(L2BatchBridgeGateway.ErrorNoFailedDistribution.selector);
|
||||
batch.withdrawFailedAmount(address(0), address(this));
|
||||
|
||||
// send some ETH to `L2BatchBridgeGateway`.
|
||||
messenger.setXDomainMessageSender(address(counterpartBatch));
|
||||
maliciousL2Token.mint(address(batch), 1 ether);
|
||||
|
||||
// make a failed distribution
|
||||
address[] memory receivers = new address[](2);
|
||||
uint256[] memory amounts = new uint256[](2);
|
||||
receivers[0] = address(this);
|
||||
receivers[1] = address(this);
|
||||
amounts[0] = 100;
|
||||
amounts[1] = 200;
|
||||
maliciousL2Token.setRevertOnTransfer(true);
|
||||
(bytes32[] memory nodes, bytes32 batchHash) = _encodeNodes(address(l1Token), 1, receivers, amounts);
|
||||
messenger.callTarget(
|
||||
address(batch),
|
||||
abi.encodeCall(
|
||||
L2BatchBridgeGateway.finalizeBatchDeposit,
|
||||
(address(l1Token), address(maliciousL2Token), 1, batchHash)
|
||||
)
|
||||
);
|
||||
assertEq(0, batch.failedAmount(address(maliciousL2Token)));
|
||||
batch.distribute(address(maliciousL2Token), 1, nodes);
|
||||
assertEq(300, batch.failedAmount(address(maliciousL2Token)));
|
||||
|
||||
// withdraw failed
|
||||
maliciousL2Token.setRevertOnTransfer(false);
|
||||
maliciousL2Token.setTransferReturn(true);
|
||||
uint256 thisBalance = maliciousL2Token.balanceOf(recipient1);
|
||||
uint256 batchBalance = maliciousL2Token.balanceOf(address(batch));
|
||||
batch.withdrawFailedAmount(address(maliciousL2Token), recipient1);
|
||||
assertEq(0, batch.failedAmount(address(maliciousL2Token)));
|
||||
assertEq(thisBalance + 300, maliciousL2Token.balanceOf(recipient1));
|
||||
assertEq(batchBalance - 300, maliciousL2Token.balanceOf(address(batch)));
|
||||
|
||||
// revert no failed
|
||||
hevm.expectRevert(L2BatchBridgeGateway.ErrorNoFailedDistribution.selector);
|
||||
batch.withdrawFailedAmount(address(maliciousL2Token), recipient1);
|
||||
}
|
||||
|
||||
function _encodeNodes(
|
||||
address token,
|
||||
uint256 batchIndex,
|
||||
address[] memory receivers,
|
||||
uint256[] memory amounts
|
||||
) private returns (bytes32[] memory nodes, bytes32 hash) {
|
||||
nodes = new bytes32[](receivers.length);
|
||||
hash = BatchBridgeCodec.encodeInitialNode(token, uint64(batchIndex));
|
||||
for (uint256 i = 0; i < receivers.length; i++) {
|
||||
nodes[i] = BatchBridgeCodec.encodeNode(receivers[i], uint96(amounts[i]));
|
||||
hash = BatchBridgeCodec.hash(hash, nodes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity =0.8.24;
|
||||
|
||||
import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol";
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
|
||||
contract RevertOnTransferToken is MockERC20 {
|
||||
bool private revertOnTransfer;
|
||||
bool private transferReturn;
|
||||
|
||||
constructor(
|
||||
string memory _name,
|
||||
string memory _symbol,
|
||||
uint8 _decimals
|
||||
) MockERC20(_name, _symbol, _decimals) {
|
||||
transferReturn = true;
|
||||
}
|
||||
|
||||
function setRevertOnTransfer(bool _revertOnTransfer) external payable {
|
||||
revertOnTransfer = _revertOnTransfer;
|
||||
}
|
||||
|
||||
function setTransferReturn(bool _transferReturn) external payable {
|
||||
transferReturn = _transferReturn;
|
||||
}
|
||||
|
||||
function transfer(address to, uint256 amount) public virtual override returns (bool) {
|
||||
if (revertOnTransfer) revert();
|
||||
if (!transferReturn) return false;
|
||||
|
||||
balanceOf[msg.sender] -= amount;
|
||||
|
||||
// Cannot overflow because the sum of all user
|
||||
// balances can't exceed the max uint256 value.
|
||||
unchecked {
|
||||
balanceOf[to] += amount;
|
||||
}
|
||||
|
||||
emit Transfer(msg.sender, to, amount);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ make lint
|
||||
|
||||
## Configure
|
||||
|
||||
The coordinator behavior can be configured using [`conf/config.json`](conf/config.json). Check the code comments under `ProverManager` in [`internal/config/config.go`](internal/config/config.go) for more details.
|
||||
The coordinator behavior can be configured using [`config.json`](config.json). Check the code comments under `ProverManager` in [`config/config.go`](config/config.go) for more details.
|
||||
|
||||
|
||||
## Start
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/scroll-tech/go-ethereum/log"
|
||||
"github.com/scroll-tech/go-ethereum/params"
|
||||
"github.com/urfave/cli/v2"
|
||||
"gorm.io/gorm"
|
||||
|
||||
@@ -50,6 +49,7 @@ func action(ctx *cli.Context) error {
|
||||
if err != nil {
|
||||
log.Crit("failed to load config file", "config file", cfgFile, "error", err)
|
||||
}
|
||||
|
||||
db, err := database.InitDB(cfg.DB)
|
||||
if err != nil {
|
||||
log.Crit("failed to init db connection", "err", err)
|
||||
@@ -60,16 +60,10 @@ func action(ctx *cli.Context) error {
|
||||
}
|
||||
}()
|
||||
|
||||
genesisPath := ctx.String(utils.Genesis.Name)
|
||||
genesis, err := utils.ReadGenesis(genesisPath)
|
||||
if err != nil {
|
||||
log.Crit("failed to read genesis", "genesis file", genesisPath, "error", err)
|
||||
}
|
||||
|
||||
registry := prometheus.DefaultRegisterer
|
||||
observability.Server(ctx, db)
|
||||
|
||||
apiSrv := apiServer(ctx, cfg, genesis.Config, db, registry)
|
||||
apiSrv := apiServer(ctx, cfg, db, registry)
|
||||
|
||||
log.Info(
|
||||
"Start coordinator api successfully.",
|
||||
@@ -96,9 +90,9 @@ func action(ctx *cli.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func apiServer(ctx *cli.Context, cfg *config.Config, chainCfg *params.ChainConfig, db *gorm.DB, reg prometheus.Registerer) *http.Server {
|
||||
func apiServer(ctx *cli.Context, cfg *config.Config, db *gorm.DB, reg prometheus.Registerer) *http.Server {
|
||||
router := gin.New()
|
||||
api.InitController(cfg, chainCfg, db, reg)
|
||||
api.InitController(cfg, db, reg)
|
||||
route.Route(router, cfg, reg)
|
||||
port := ctx.String(httpPortFlag.Name)
|
||||
srv := &http.Server{
|
||||
|
||||
@@ -10,13 +10,11 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/params"
|
||||
coordinatorConfig "scroll-tech/coordinator/internal/config"
|
||||
|
||||
"scroll-tech/common/cmd"
|
||||
"scroll-tech/common/testcontainers"
|
||||
"scroll-tech/common/docker"
|
||||
"scroll-tech/common/utils"
|
||||
|
||||
coordinatorConfig "scroll-tech/coordinator/internal/config"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -25,35 +23,29 @@ var (
|
||||
|
||||
// CoordinatorApp coordinator-test client manager.
|
||||
type CoordinatorApp struct {
|
||||
Config *coordinatorConfig.Config
|
||||
ChainConfig *params.ChainConfig
|
||||
Config *coordinatorConfig.Config
|
||||
|
||||
testApps *testcontainers.TestcontainerApps
|
||||
base *docker.App
|
||||
|
||||
configOriginFile string
|
||||
chainConfigOriginFile string
|
||||
coordinatorFile string
|
||||
genesisFile string
|
||||
HTTPPort int64
|
||||
originFile string
|
||||
coordinatorFile string
|
||||
HTTPPort int64
|
||||
|
||||
args []string
|
||||
*cmd.Cmd
|
||||
docker.AppAPI
|
||||
}
|
||||
|
||||
// NewCoordinatorApp return a new coordinatorApp manager.
|
||||
func NewCoordinatorApp(testApps *testcontainers.TestcontainerApps, configFile string, chainConfigFile string) *CoordinatorApp {
|
||||
coordinatorFile := fmt.Sprintf("/tmp/%d_coordinator-config.json", testApps.Timestamp)
|
||||
genesisFile := fmt.Sprintf("/tmp/%d_genesis.json", testApps.Timestamp)
|
||||
func NewCoordinatorApp(base *docker.App, file string) *CoordinatorApp {
|
||||
coordinatorFile := fmt.Sprintf("/tmp/%d_coordinator-config.json", base.Timestamp)
|
||||
port, _ := rand.Int(rand.Reader, big.NewInt(2000))
|
||||
httpPort := port.Int64() + httpStartPort
|
||||
coordinatorApp := &CoordinatorApp{
|
||||
testApps: testApps,
|
||||
configOriginFile: configFile,
|
||||
chainConfigOriginFile: chainConfigFile,
|
||||
coordinatorFile: coordinatorFile,
|
||||
genesisFile: genesisFile,
|
||||
HTTPPort: httpPort,
|
||||
args: []string{"--log.debug", "--config", coordinatorFile, "--genesis", genesisFile, "--http", "--http.port", strconv.Itoa(int(httpPort))},
|
||||
base: base,
|
||||
originFile: file,
|
||||
coordinatorFile: coordinatorFile,
|
||||
HTTPPort: httpPort,
|
||||
args: []string{"--log.debug", "--config", coordinatorFile, "--http", "--http.port", strconv.Itoa(int(httpPort))},
|
||||
}
|
||||
if err := coordinatorApp.MockConfig(true); err != nil {
|
||||
panic(err)
|
||||
@@ -63,14 +55,14 @@ func NewCoordinatorApp(testApps *testcontainers.TestcontainerApps, configFile st
|
||||
|
||||
// RunApp run coordinator-test child process by multi parameters.
|
||||
func (c *CoordinatorApp) RunApp(t *testing.T, args ...string) {
|
||||
c.Cmd = cmd.NewCmd(string(utils.CoordinatorAPIApp), append(c.args, args...)...)
|
||||
c.Cmd.RunApp(func() bool { return c.Cmd.WaitResult(t, time.Second*20, "Start coordinator api successfully") })
|
||||
c.AppAPI = cmd.NewCmd(string(utils.CoordinatorAPIApp), append(c.args, args...)...)
|
||||
c.AppAPI.RunApp(func() bool { return c.AppAPI.WaitResult(t, time.Second*20, "Start coordinator api successfully") })
|
||||
}
|
||||
|
||||
// Free stop and release coordinator-test.
|
||||
func (c *CoordinatorApp) Free() {
|
||||
if !utils.IsNil(c.Cmd) {
|
||||
c.Cmd.WaitExit()
|
||||
if !utils.IsNil(c.AppAPI) {
|
||||
c.AppAPI.WaitExit()
|
||||
}
|
||||
_ = os.Remove(c.coordinatorFile)
|
||||
}
|
||||
@@ -82,7 +74,8 @@ func (c *CoordinatorApp) HTTPEndpoint() string {
|
||||
|
||||
// MockConfig creates a new coordinator config.
|
||||
func (c *CoordinatorApp) MockConfig(store bool) error {
|
||||
cfg, err := coordinatorConfig.NewConfig(c.configOriginFile)
|
||||
base := c.base
|
||||
cfg, err := coordinatorConfig.NewConfig(c.originFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -96,40 +89,20 @@ func (c *CoordinatorApp) MockConfig(store bool) error {
|
||||
MaxVerifierWorkers: 4,
|
||||
MinProverVersion: "v1.0.0",
|
||||
}
|
||||
endpoint, err := c.testApps.GetDBEndPoint()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg.DB.DSN = endpoint
|
||||
cfg.DB.DSN = base.DBImg.Endpoint()
|
||||
cfg.L2.ChainID = 111
|
||||
cfg.Auth.ChallengeExpireDurationSec = 1
|
||||
cfg.Auth.LoginExpireDurationSec = 1
|
||||
c.Config = cfg
|
||||
|
||||
genesis, err := utils.ReadGenesis(c.chainConfigOriginFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.ChainConfig = genesis.Config
|
||||
|
||||
if !store {
|
||||
return nil
|
||||
}
|
||||
|
||||
coordinatorConfigData, err := json.Marshal(c.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
genesisConfigData, err := json.Marshal(genesis)
|
||||
data, err := json.Marshal(c.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if writeErr := os.WriteFile(c.coordinatorFile, coordinatorConfigData, 0600); writeErr != nil {
|
||||
return writeErr
|
||||
}
|
||||
if writeErr := os.WriteFile(c.genesisFile, genesisConfigData, 0600); writeErr != nil {
|
||||
return writeErr
|
||||
}
|
||||
return nil
|
||||
return os.WriteFile(c.coordinatorFile, data, 0600)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
"batch_collection_time_sec": 180,
|
||||
"chunk_collection_time_sec": 180,
|
||||
"verifier": {
|
||||
"fork_name": "bernoulli",
|
||||
"mock_mode": true,
|
||||
"params_path": "",
|
||||
"assets_path": ""
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
module scroll-tech/coordinator
|
||||
|
||||
go 1.21
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/appleboy/gin-jwt/v2 v2.9.1
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
github.com/go-resty/resty/v2 v2.7.0
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20240326144132-0f0cd99f7a2e
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20240314095130-4553f5f26935
|
||||
github.com/shopspring/decimal v1.3.1
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/urfave/cli/v2 v2.25.7
|
||||
|
||||
@@ -59,7 +59,6 @@ github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
@@ -96,7 +95,6 @@ github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao=
|
||||
github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA=
|
||||
github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU=
|
||||
github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
@@ -119,13 +117,11 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=
|
||||
github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||
@@ -148,7 +144,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
@@ -158,7 +153,6 @@ github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6
|
||||
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
|
||||
@@ -170,7 +164,6 @@ github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr
|
||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||
github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
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=
|
||||
@@ -180,8 +173,8 @@ github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjR
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
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.20240326144132-0f0cd99f7a2e h1:FcoK0rykAWI+5E7cQM6ALRLd5CmjBTHRvJztRBH2xeM=
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20240326144132-0f0cd99f7a2e/go.mod h1:7Rz2bh9pn42rGuxjh51CG7HL9SKMG3ZugJkL3emdZx8=
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20240314095130-4553f5f26935 h1:bHBt6sillaT4o/9RjxkVX8pWwvEmu37uWBw4XbCjfzY=
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20240314095130-4553f5f26935/go.mod h1:7Rz2bh9pn42rGuxjh51CG7HL9SKMG3ZugJkL3emdZx8=
|
||||
github.com/scroll-tech/zktrie v0.7.1 h1:NrmZNjuBzsbrKePqdHDG+t2cXnimbtezPAFS0+L9ElE=
|
||||
github.com/scroll-tech/zktrie v0.7.1/go.mod h1:XvNo7vAk8yxNyTjBDj5WIiFzYW4bx/gJ78+NK6Zn6Uk=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
|
||||
@@ -204,7 +197,6 @@ github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8
|
||||
github.com/supranational/blst v0.3.11-0.20230124161941-ca03e11a3ff2 h1:wh1wzwAhZBNiZO37uWS/nDaKiIwHz4mDo4pnA+fqTO0=
|
||||
github.com/supranational/blst v0.3.11-0.20230124161941-ca03e11a3ff2/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
|
||||
github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw=
|
||||
github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
|
||||
@@ -50,7 +50,6 @@ type Config struct {
|
||||
|
||||
// VerifierConfig load zk verifier config.
|
||||
type VerifierConfig struct {
|
||||
ForkName string `json:"fork_name"`
|
||||
MockMode bool `json:"mock_mode"`
|
||||
ParamsPath string `json:"params_path"`
|
||||
AssetsPath string `json:"assets_path"`
|
||||
|
||||
@@ -53,44 +53,25 @@ func (a *AuthController) PayloadFunc(data interface{}) jwt.MapClaims {
|
||||
return jwt.MapClaims{}
|
||||
}
|
||||
|
||||
var publicKey string
|
||||
var err error
|
||||
if v.Message.HardForkName != "" {
|
||||
authMsg := message.AuthMsg{
|
||||
Identity: &message.Identity{
|
||||
Challenge: v.Message.Challenge,
|
||||
ProverName: v.Message.ProverName,
|
||||
ProverVersion: v.Message.ProverVersion,
|
||||
HardForkName: v.Message.HardForkName,
|
||||
},
|
||||
Signature: v.Signature,
|
||||
}
|
||||
publicKey, err = authMsg.PublicKey()
|
||||
} else {
|
||||
authMsg := message.LegacyAuthMsg{
|
||||
Identity: &message.LegacyIdentity{
|
||||
Challenge: v.Message.Challenge,
|
||||
ProverName: v.Message.ProverName,
|
||||
ProverVersion: v.Message.ProverVersion,
|
||||
},
|
||||
Signature: v.Signature,
|
||||
}
|
||||
publicKey, err = authMsg.PublicKey()
|
||||
// recover the public key
|
||||
authMsg := message.AuthMsg{
|
||||
Identity: &message.Identity{
|
||||
Challenge: v.Message.Challenge,
|
||||
ProverName: v.Message.ProverName,
|
||||
ProverVersion: v.Message.ProverVersion,
|
||||
},
|
||||
Signature: v.Signature,
|
||||
}
|
||||
|
||||
publicKey, err := authMsg.PublicKey()
|
||||
if err != nil {
|
||||
return jwt.MapClaims{}
|
||||
}
|
||||
|
||||
if v.Message.HardForkName == "" {
|
||||
v.Message.HardForkName = "shanghai"
|
||||
}
|
||||
|
||||
return jwt.MapClaims{
|
||||
types.PublicKey: publicKey,
|
||||
types.ProverName: v.Message.ProverName,
|
||||
types.ProverVersion: v.Message.ProverVersion,
|
||||
types.HardForkName: v.Message.HardForkName,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,9 +89,5 @@ func (a *AuthController) IdentityHandler(c *gin.Context) interface{} {
|
||||
if proverVersion, ok := claims[types.ProverVersion]; ok {
|
||||
c.Set(types.ProverVersion, proverVersion)
|
||||
}
|
||||
|
||||
if hardForkName, ok := claims[types.HardForkName]; ok {
|
||||
c.Set(types.HardForkName, hardForkName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user