Compare commits

..

65 Commits

Author SHA1 Message Date
HAOYUatHZ
38551c4eeb refactor: rename bridge to rollup (#644)
Co-authored-by: HAOYUatHZ <HAOYUatHZ@users.noreply.github.com>
2023-09-02 15:42:01 +08:00
HAOYUatHZ
82330621ce docs(): update README 2023-09-02 15:08:37 +08:00
maskpp
237d048ce6 feat(prover): update l2geth version and enable use compression when get trace (#915)
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <HAOYUatHZ@users.noreply.github.com>
2023-09-02 14:38:47 +08:00
Haichen Shen
38b440b1fa docs(contract): Update readme (#378)
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
Co-authored-by: Orest Tarasiuk <830847+OrestTa@users.noreply.github.com>
Co-authored-by: Péter Garamvölgyi <peter@scroll.io>
2023-09-01 20:22:09 -07:00
Péter Garamvölgyi
0ed3d68fc3 ci: remove message_relayer Github Action (#914) 2023-09-01 19:44:36 +02:00
Nazarii Denha
2847d02498 refactor(bridge): remove message-relayer and related code (#670)
Co-authored-by: colin <102356659+colinlyguo@users.noreply.github.com>
Co-authored-by: Péter Garamvölgyi <peter@scroll.io>
Co-authored-by: NazariiDenha <NazariiDenha@users.noreply.github.com>
Co-authored-by: colinlyguo <colinlyguo@scroll.io>
2023-09-01 19:04:09 +02:00
Xi Lin
a7d9bf3488 fix(contracts): OZ-N05 Lack of Logs on Sensitive Actions (#908)
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
2023-09-01 22:55:11 +08:00
Xi Lin
255eb5f307 fix(contracts): OZ-L02 Utilizing Deprecated Function From Library (#902)
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
2023-09-01 22:42:20 +08:00
Haichen Shen
0955382aec fix(ci): enable the bump-version bot when PR is updated (#905)
Co-authored-by: icemelon <icemelon@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
2023-09-01 22:41:23 +08:00
Xi Lin
07961f751e fix(contracts): OZ-N01 Variable Cast is Unnecessary (#903)
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
2023-09-01 22:18:17 +08:00
Xi Lin
bbd5a5a9c2 fix(contracts): OZ-N02 Incorrect Function Visibility (#904)
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
2023-09-01 21:58:47 +08:00
Xi Lin
2e1f42fcb6 fix(contracts): OZ-N04 Implicit Type Casting (#907)
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
2023-09-01 19:45:35 +08:00
Xi Lin
bcdbe1f119 fix(contracts): OZ-N07 Naming Issue (#910)
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
2023-09-01 19:29:57 +08:00
Xi Lin
6034c43bb1 fix(contracts): OZ-N06 Unpinned Compiler Version (#909) 2023-09-01 19:20:10 +08:00
colin
f9da81d587 feat(rollup-relayer): use db gas estimation as min gas limit (#901)
Co-authored-by: colinlyguo <colinlyguo@users.noreply.github.com>
Co-authored-by: Xi Lin <zimpha@gmail.com>
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
2023-09-01 14:54:57 +08:00
Xi Lin
38f64e70b7 feat(contracts): bench test for ScrollChain (#721)
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <HAOYUatHZ@users.noreply.github.com>
Co-authored-by: Haichen Shen <shenhaichen@gmail.com>
Co-authored-by: zimpha <zimpha@users.noreply.github.com>
Co-authored-by: icemelon <icemelon@users.noreply.github.com>
2023-08-31 23:52:50 -07:00
Haichen Shen
44b924170a fix(ci): don't trigger CI for version change (#898)
Co-authored-by: icemelon <icemelon@users.noreply.github.com>
2023-08-31 23:06:32 -07:00
georgehao
227f09a2cf feat(coordinator): pretty log (#900)
Co-authored-by: georgehao <georgehao@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <haoyu@protonmail.com>
2023-09-01 12:56:03 +08:00
HAOYUatHZ
112e82a4ef feat(relayer): cache maxFeeData (#897)
Co-authored-by: georgehao <haohongfan@gmail.com>
Co-authored-by: georgehao <georgehao@users.noreply.github.com>
2023-09-01 12:06:02 +08:00
georgehao
82e6d28e82 feat(rollup-relayer): pretty the send transaction log (#896)
Co-authored-by: georgehao <georgehao@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
2023-09-01 11:42:04 +08:00
Xi Lin
8daa5d5496 fix(contracts): check actual number of transactions in each chunk (#887)
Co-authored-by: zimpha <zimpha@users.noreply.github.com>
2023-08-31 20:28:54 -07:00
maskpp
3958e8bd86 feat(rollup_relayer): add chain_monitor client (#885)
Co-authored-by: mask-pp <mask-pp@users.noreply.github.com>
Co-authored-by: colin <102356659+colinlyguo@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
2023-08-31 20:15:26 +08:00
colin
f553a70d20 feat(rollup-relayer): add l1 commit estimation fields in batch schema (#891)
Co-authored-by: colinlyguo <colinlyguo@users.noreply.github.com>
Co-authored-by: Péter Garamvölgyi <peter@scroll.io>
2023-08-30 19:52:23 +08:00
colin
2dc5ceb44c fix(prover): refine error logs (#890)
Co-authored-by: colinlyguo <colinlyguo@users.noreply.github.com>
2023-08-30 16:45:52 +08:00
colin
ff03924d76 feat(prover): add chunk & batch proving circuit error handling (#884)
Co-authored-by: colinlyguo <colinlyguo@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
2023-08-30 12:22:54 +08:00
Xi Lin
f6894bb82f feat(contracts): add usdc gateway (#426)
Co-authored-by: zimpha <zimpha@users.noreply.github.com>
Co-authored-by: Haichen Shen <shenhaichen@gmail.com>
2023-08-29 00:28:29 -07:00
georgehao
e990e02391 feat(database): add index (#881)
Co-authored-by: georgehao <georgehao@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
2023-08-28 17:07:29 +08:00
colin
f8d48a6326 fix(common): chunk test (#886)
Co-authored-by: colinlyguo <colinlyguo@users.noreply.github.com>
2023-08-28 15:46:54 +08:00
colin
dba097e03d fix(rollup-relayer): block l1 commit calldata and gas estimation (#882)
Co-authored-by: colinlyguo <colinlyguo@users.noreply.github.com>
2023-08-28 15:33:40 +08:00
HAOYUatHZ
30ad0bfe78 fix(coordinator): fix TestApis (#883)
Co-authored-by: HAOYUatHZ <HAOYUatHZ@users.noreply.github.com>
2023-08-28 11:21:03 +08:00
georgehao
1dfca3b7c0 feat(coordinator): prover task record unique (#845)
Co-authored-by: georgehao <georgehao@users.noreply.github.com>
Co-authored-by: Péter Garamvölgyi <peter@scroll.io>
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
2023-08-28 10:51:01 +08:00
colin
826e847b5a fix(rollup-relayer): determine first block height and first chunk index (#861)
Co-authored-by: colinlyguo <colinlyguo@users.noreply.github.com>
Co-authored-by: Haichen Shen <shenhaichen@gmail.com>
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <HAOYUatHZ@users.noreply.github.com>
2023-08-26 15:04:52 +08:00
colin
8c71a6d22a fix(chunk-proposer): count l1+l2 txs into chunk (#879)
Co-authored-by: colinlyguo <colinlyguo@users.noreply.github.com>
2023-08-26 14:53:59 +08:00
Péter Garamvölgyi
87f18efba8 fix: limit DB query result count in chunk and batch proposer (#878)
Co-authored-by: Thegaram <Thegaram@users.noreply.github.com>
Co-authored-by: colin <102356659+colinlyguo@users.noreply.github.com>
Co-authored-by: colinlyguo <colinlyguo@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <HAOYUatHZ@users.noreply.github.com>
2023-08-25 12:04:41 +02:00
Xi Lin
fecd129a39 fix(contracts): OZ-L08 Redundancy of Replaying Messages in L2ScrollMessenger (#850)
Co-authored-by: zimpha <zimpha@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
2023-08-25 16:25:52 +08:00
Xi Lin
663156984f fix(contracts): OZ-N08 Inconsistent Order of Event Emissions (#876)
Co-authored-by: zimpha <zimpha@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
2023-08-25 15:51:52 +08:00
Xi Lin
3499c595e7 fix(contracts): OZ-N07 Incorrect Function Visibility (#875)
Co-authored-by: zimpha <zimpha@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
2023-08-25 15:03:15 +08:00
Xi Lin
95d2df46e3 fix(contracts): OZ-N14 Duplicate Imports (#877)
Co-authored-by: zimpha <zimpha@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <HAOYUatHZ@users.noreply.github.com>
2023-08-25 14:21:59 +08:00
Xi Lin
102d29c54d docs(contracts): OZ-N06 Inconsistent Coding Style (#874)
Co-authored-by: zimpha <zimpha@users.noreply.github.com>
2023-08-25 11:59:20 +08:00
Xi Lin
7d50699344 fix(contracts): OZ-L07 Potentially Misleading Verifier Event (#849)
Co-authored-by: zimpha <zimpha@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
2023-08-25 10:45:35 +08:00
Xi Lin
e08b800d1d fix(contracts): OZ-L06 Possible Misleading revert Message When Swapping Non-ERC20Permit Tokens (#847)
Co-authored-by: zimpha <zimpha@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
2023-08-25 10:23:02 +08:00
Xi Lin
6139ca0df0 fix(contracts): OZ-L10 maxReplayTimes is Not Initialized in L1ScrollMessenger (#852)
Co-authored-by: zimpha <zimpha@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <HAOYUatHZ@users.noreply.github.com>
2023-08-25 10:10:31 +08:00
colin
24a0fd08ac fix(rollup-relayer): chunk and batch proposer metrics (#873)
Co-authored-by: colinlyguo <colinlyguo@users.noreply.github.com>
Co-authored-by: Péter Garamvölgyi <peter@scroll.io>
2023-08-24 19:53:15 +02:00
Steven
2840485f38 fix: update scroll-prover to v0.7.5 (#872)
Co-authored-by: silathdiir <silathdiir@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <HAOYUatHZ@users.noreply.github.com>
2023-08-24 23:21:03 +08:00
colin
dab21fc712 refactor(coordinator): omit coordinator's error details to prover & add logs (#863)
Co-authored-by: colinlyguo <colinlyguo@users.noreply.github.com>
2023-08-24 22:59:33 +08:00
HAOYUatHZ
c44b7f7bf4 fix(coordinator): fix TestApis (#870) 2023-08-24 16:05:53 +08:00
HAOYUatHZ
a8c71b5e36 feat(coordinator): bump hardcoded tag check (#869)
Co-authored-by: HAOYUatHZ <HAOYUatHZ@users.noreply.github.com>
2023-08-24 15:55:27 +08:00
georgehao
ae2f62df00 fix(coordinator): fix GetAssignedTaskOfOtherProvers (#868)
Co-authored-by: georgehao <georgehao@users.noreply.github.com>
2023-08-24 15:25:16 +08:00
georgehao
ce5c6e0aa3 feat(coordinator): fix all chunks ready bug (#862)
Co-authored-by: georgehao <georgehao@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
2023-08-24 15:20:03 +08:00
georgehao
e8ddf99184 feat(coordinator):fix recover status bug (#866)
Co-authored-by: georgehao <georgehao@users.noreply.github.com>
2023-08-24 12:21:29 +08:00
Steven
ebf2b429a3 fix: update scroll-prover to v0.7.3 (#865)
Co-authored-by: silathdiir <silathdiir@users.noreply.github.com>
2023-08-24 07:03:42 +08:00
HAOYUatHZ
db46ce408d feat(coordinator): hardcode tag validation (#864)
Co-authored-by: HAOYUatHZ <HAOYUatHZ@users.noreply.github.com>
2023-08-23 21:37:29 +08:00
HAOYUatHZ
3d1a8374d0 feat(coordinator): check vk (#859)
Co-authored-by: HAOYUatHZ <HAOYUatHZ@users.noreply.github.com>
2023-08-23 17:12:39 +08:00
yqrashawn
574aa68491 ci: failed to create commit in bump version action (#860)
Co-authored-by: yqrashawn <yqrashawn@users.noreply.github.com>
2023-08-23 14:49:41 +08:00
Haichen Shen
589388b288 fix(rollup): increase the batch proposer frequency (#858)
Co-authored-by: icemelon <icemelon@users.noreply.github.com>
2023-08-23 12:03:08 +08:00
Steven
af8175800f fix: update libzkp to use scroll-prover v0.7.2 (#807)
Co-authored-by: silathdiir <silathdiir@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <HAOYUatHZ@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <haoyu@protonmail.com>
2023-08-23 11:48:27 +08:00
vyzo
e6793a85f5 test: Increase test coverage in block/chunk encoding (#830)
Co-authored-by: vyzo <vyzo@users.noreply.github.com>
2023-08-22 08:50:17 -07:00
colin
7e9f3b7376 fix(rollup-relayer): estimate l1 commit calldata size (#848)
Co-authored-by: colinlyguo <colinlyguo@users.noreply.github.com>
Co-authored-by: Péter Garamvölgyi <peter@scroll.io>
Co-authored-by: Thegaram <Thegaram@users.noreply.github.com>
2023-08-22 13:09:28 +02:00
Péter Garamvölgyi
8b611b443a fix: only create/drop index if exists in migration (#856)
Co-authored-by: Thegaram <Thegaram@users.noreply.github.com>
2023-08-22 11:27:49 +02:00
Steven
1f62596b0a fix: release libzkp v0.6.6 (#855)
Co-authored-by: silathdiir <silathdiir@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <haoyu@protonmail.com>
2023-08-22 16:38:12 +08:00
HAOYUatHZ
a2b9878733 fix(coordinator): fix taskType for ProverTask orm calls (#854)
Co-authored-by: HAOYUatHZ <HAOYUatHZ@users.noreply.github.com>
2023-08-22 15:19:03 +08:00
HAOYUatHZ
e166dfba48 ci(github): revert gacts/install-geth-tools (#853)
Co-authored-by: HAOYUatHZ <HAOYUatHZ@users.noreply.github.com>
2023-08-22 13:02:38 +08:00
Xi Lin
d7d62eb8e6 docs(contracts): OZ-L09 Misleading and Incorrect Comments (#851)
Co-authored-by: zimpha <zimpha@users.noreply.github.com>
2023-08-22 12:53:42 +08:00
HAOYUatHZ
61eb75fbb6 refactor(types): make DEPRECATED ProvingTaskProved more explict (#837)
Co-authored-by: HAOYUatHZ <HAOYUatHZ@users.noreply.github.com>
2023-08-22 10:47:48 +08:00
georgehao
e9af5912ac Fix(coordinator): fix coordinator recover proving_status error (#829)
Co-authored-by: georgehao <georgehao@users.noreply.github.com>
Co-authored-by: colinlyguo <colinlyguo@scroll.io>
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
2023-08-22 06:42:48 +08:00
225 changed files with 4482 additions and 2285 deletions

View File

@@ -20,7 +20,7 @@ Your PR title must follow [conventional commits](https://www.conventionalcommits
### Deployment tag versioning
Has `tag` in `common/version.go` been updated?
Has `tag` in `common/version.go` been updated or have you added `bump-version` label to this PR?
- [ ] No, this PR doesn't involve a new deployment, git tag, docker image tag
- [ ] Yes

View File

@@ -2,19 +2,23 @@ name: Bump Version
on:
pull_request:
branches: [develop]
branches: [ develop ]
types:
- opened
- reopened
- synchronize
- ready_for_review
- labeled
jobs:
try-to-bump:
if: contains(github.event.pull_request.labels.*.name, 'bump-version')
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
ref: ${{ github.head_ref }}
- name: check diff
id: check_diff
run: |
@@ -49,7 +53,11 @@ jobs:
- name: bump version in common/version/version.go
if: steps.check_diff.outputs.result == 'bump'
run: node .github/scripts/bump_version_dot_go.mjs
# Commits made by this Action do not trigger new Workflow runs
- uses: stefanzweifel/git-auto-commit-action@3ea6ae190baf489ba007f7c92608f33ce20ef04a
if: steps.check_diff.outputs.result == 'bump'
with:
skip_fetch: true # already did fetch in check diff
file_pattern: "common/version/version.go"
commit_message: "chore: auto version bump[bot]"

View File

@@ -9,6 +9,7 @@ on:
- alpha
paths:
- 'common/**'
- '!common/version/version.go'
- '.github/workflows/common.yml'
pull_request:
types:
@@ -18,6 +19,7 @@ on:
- ready_for_review
paths:
- 'common/**'
- '!common/version/version.go'
- '.github/workflows/common.yml'
jobs:
@@ -88,8 +90,6 @@ jobs:
version: '0.8.16'
- name: Install Geth Tools
uses: gacts/install-geth-tools@v1
with:
version: 1.10.19
- name: Build prerequisites
run: |
make dev_docker

View File

@@ -10,6 +10,7 @@ on:
paths:
- 'coordinator/**'
- 'common/**'
- '!common/version/version.go'
- 'database/**'
- '.github/workflows/coordinator.yml'
pull_request:
@@ -21,6 +22,7 @@ on:
paths:
- 'coordinator/**'
- 'common/**'
- '!common/version/version.go'
- 'database/**'
- '.github/workflows/coordinator.yml'
@@ -104,8 +106,6 @@ jobs:
version: '0.8.16'
- name: Install Geth Tools
uses: gacts/install-geth-tools@v1
with:
version: 1.10.19
- name: Build prerequisites
run: |
make dev_docker

View File

@@ -10,6 +10,7 @@ on:
paths:
- 'database/**'
- 'common/**'
- '!common/version/version.go'
- '.github/workflows/database.yml'
pull_request:
types:
@@ -20,6 +21,7 @@ on:
paths:
- 'database/**'
- 'common/**'
- '!common/version/version.go'
- '.github/workflows/database.yml'
jobs:
@@ -81,8 +83,6 @@ jobs:
version: '0.8.16'
- name: Install Geth Tools
uses: gacts/install-geth-tools@v1
with:
version: 1.10.19
- name: Build prerequisites
run: |
make dev_docker

View File

@@ -48,27 +48,6 @@ jobs:
tags: scrolltech/gas-oracle:${{github.ref_name}}
# cache-from: type=gha,scope=${{ github.workflow }}
# cache-to: type=gha,scope=${{ github.workflow }}
msg_relayer:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push msg_relayer docker
uses: docker/build-push-action@v2
with:
context: .
file: ./build/dockerfiles/msg_relayer.Dockerfile
push: true
tags: scrolltech/msg-relayer:${{github.ref_name}}
# cache-from: type=gha,scope=${{ github.workflow }}
# cache-to: type=gha,scope=${{ github.workflow }}
rollup_relayer:
runs-on: ubuntu-latest
steps:

View File

@@ -33,12 +33,10 @@ jobs:
version: '0.8.16'
- name: Install Geth Tools
uses: gacts/install-geth-tools@v1
with:
version: 1.10.19
- name: Build prerequisites
run: |
make dev_docker
make -C bridge mock_abi
make -C rollup mock_abi
make -C common/bytecode all
- name: Run integration tests
run: |

View File

@@ -1,4 +1,4 @@
name: Bridge
name: Rollup
on:
push:
@@ -8,10 +8,11 @@ on:
- develop
- alpha
paths:
- 'bridge/**'
- 'rollup/**'
- 'common/**'
- '!common/version/version.go'
- 'database/**'
- '.github/workflows/bridge.yml'
- '.github/workflows/rollup.yml'
pull_request:
types:
- opened
@@ -19,10 +20,11 @@ on:
- synchronize
- ready_for_review
paths:
- 'bridge/**'
- 'rollup/**'
- 'common/**'
- '!common/version/version.go'
- 'database/**'
- '.github/workflows/bridge.yml'
- '.github/workflows/rollup.yml'
jobs:
check:
@@ -43,10 +45,8 @@ jobs:
version: '0.8.16'
- name: Install Geth Tools
uses: gacts/install-geth-tools@v1
with:
version: 1.10.19
- name: Lint
working-directory: 'bridge'
working-directory: 'rollup'
run: |
rm -rf $HOME/.cache/golangci-lint
make mock_abi
@@ -64,14 +64,14 @@ jobs:
- name: Install goimports
run: go install golang.org/x/tools/cmd/goimports
- name: Run goimports lint
run: goimports -local scroll-tech/bridge/ -w .
working-directory: 'bridge'
run: goimports -local scroll-tech/rollup/ -w .
working-directory: 'rollup'
- name: Run go mod tidy
run: go mod tidy
working-directory: 'bridge'
working-directory: 'rollup'
# If there are any diffs from goimports or go mod tidy, fail.
- name: Verify no changes from goimports and go mod tidy
working-directory: 'bridge'
working-directory: 'rollup'
run: |
if [ -n "$(git status --porcelain)" ]; then
exit 1
@@ -94,18 +94,16 @@ jobs:
version: '0.8.16'
- name: Install Geth Tools
uses: gacts/install-geth-tools@v1
with:
version: 1.10.19
- name: Build prerequisites
run: |
make dev_docker
make -C bridge mock_abi
- name: Build bridge binaries
working-directory: 'bridge'
make -C rollup mock_abi
- name: Build rollup binaries
working-directory: 'rollup'
run: |
make bridge_bins
- name: Test bridge packages
working-directory: 'bridge'
make rollup_bins
- name: Test rollup packages
working-directory: 'rollup'
run: |
go test -v -race -gcflags="-l" -ldflags="-s=false" -coverprofile=coverage.txt -covermode=atomic ./...
- name: Upload coverage reports to Codecov
@@ -113,7 +111,7 @@ jobs:
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
flags: bridge
flags: rollup
# docker-build:
# if: github.event.pull_request.draft == false
# runs-on: ubuntu-latest

View File

@@ -1,6 +1,6 @@
.PHONY: check update dev_docker build_test_docker run_test_docker clean
L2GETH_TAG=scroll-v4.3.34
L2GETH_TAG=scroll-v4.3.55
help: ## Display this help message
@grep -h \
@@ -8,7 +8,7 @@ help: ## Display this help message
awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
lint: ## The code's format and security checks.
make -C bridge lint
make -C rollup lint
make -C common lint
make -C coordinator lint
make -C database lint
@@ -17,7 +17,7 @@ lint: ## The code's format and security checks.
update: ## update dependencies
go work sync
cd $(PWD)/bridge/ && go get -u github.com/scroll-tech/go-ethereum@${L2GETH_TAG} && go mod tidy
cd $(PWD)/rollup/ && go get -u github.com/scroll-tech/go-ethereum@${L2GETH_TAG} && go mod tidy
cd $(PWD)/bridge-history-api/ && go get -u github.com/ethereum/go-ethereum@latest && go mod tidy
cd $(PWD)/common/ && go get -u github.com/scroll-tech/go-ethereum@${L2GETH_TAG} && go mod tidy
cd $(PWD)/coordinator/ && go get -u github.com/scroll-tech/go-ethereum@${L2GETH_TAG} && go mod tidy
@@ -25,7 +25,7 @@ update: ## update dependencies
cd $(PWD)/prover/ && go get -u github.com/scroll-tech/go-ethereum@${L2GETH_TAG} && go mod tidy
cd $(PWD)/prover-stats-api/ && go get -u github.com/scroll-tech/go-ethereum@${L2GETH_TAG} && go mod tidy
cd $(PWD)/tests/integration-test/ && go get -u github.com/scroll-tech/go-ethereum@${L2GETH_TAG} && go mod tidy
goimports -local $(PWD)/bridge/ -w .
goimports -local $(PWD)/rollup/ -w .
goimports -local $(PWD)/bridge-history-api/ -w .
goimports -local $(PWD)/common/ -w .
goimports -local $(PWD)/coordinator/ -w .

View File

@@ -15,14 +15,14 @@ docker pull postgres
make dev_docker
```
## Testing Bridge & Coordinator
## Testing Rollup & Coordinator
### For Non-Apple Silicon (M1/M2) Macs
Run the tests using the following commands:
```bash
go test -v -race -covermode=atomic scroll-tech/bridge/...
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/...
@@ -55,7 +55,7 @@ This command runs a Docker container named `scroll_test_container` from the `scr
Once the Docker container is running, execute the tests using the following commands:
```bash
go test -v -race -covermode=atomic scroll-tech/bridge/...
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/...

View File

@@ -89,9 +89,6 @@ var (
// L2FailedRelayedMessageEventSignature = keccak256("FailedRelayedMessage(bytes32)")
L2FailedRelayedMessageEventSignature common.Hash
// L2ImportBlockEventSignature = keccak256("ImportBlock(bytes32,uint256,uint256,uint256,bytes32)")
L2ImportBlockEventSignature common.Hash
// L2AppendMessageEventSignature = keccak256("AppendMessage(uint256,bytes32)")
L2AppendMessageEventSignature common.Hash
)
@@ -153,8 +150,6 @@ func init() {
L2RelayedMessageEventSignature = L2ScrollMessengerABI.Events["RelayedMessage"].ID
L2FailedRelayedMessageEventSignature = L2ScrollMessengerABI.Events["FailedRelayedMessage"].ID
L2ImportBlockEventSignature = L1BlockContainerABI.Events["ImportBlock"].ID
L2AppendMessageEventSignature = L2MessageQueueABI.Events["AppendMessage"].ID
}

View File

@@ -116,7 +116,7 @@ require (
golang.org/x/arch v0.4.0 // indirect
golang.org/x/crypto v0.12.0 // indirect
golang.org/x/exp v0.0.0-20230810033253-352e893a4cad // indirect
golang.org/x/net v0.12.0 // indirect
golang.org/x/net v0.14.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/text v0.12.0 // indirect

View File

@@ -531,8 +531,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=

View File

@@ -1,36 +0,0 @@
# Bridge
This repo contains the Scroll bridge.
In addition, launching the bridge will launch a separate instance of l2geth, and sets up a communication channel
between the two, over JSON-RPC sockets.
Something we should pay attention is that all private keys inside sender instance cannot be duplicated.
## Dependency
+ install `abigen`
``` bash
go install -v github.com/scroll-tech/go-ethereum/cmd/abigen
```
## Build
```bash
make clean
make bridge
```
## Start
* use default ports and config.json
```bash
./build/bin/bridge --http
```
* use specified ports and config.json
```bash
./build/bin/bridge --config ./config.json --http --http.addr localhost --http.port 8290
```

View File

@@ -1,7 +0,0 @@
package main
import "scroll-tech/bridge/cmd/event_watcher/app"
func main() {
app.Run()
}

View File

@@ -1,7 +0,0 @@
package main
import "scroll-tech/bridge/cmd/gas_oracle/app"
func main() {
app.Run()
}

View File

@@ -1,94 +0,0 @@
package app
import (
"context"
"fmt"
"os"
"os/signal"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/scroll-tech/go-ethereum/log"
"github.com/urfave/cli/v2"
"scroll-tech/common/database"
"scroll-tech/common/metrics"
"scroll-tech/common/utils"
"scroll-tech/common/version"
"scroll-tech/bridge/internal/config"
"scroll-tech/bridge/internal/controller/relayer"
)
var app *cli.App
func init() {
// Set up message-relayer app info.
app = cli.NewApp()
app.Action = action
app.Name = "message-relayer"
app.Usage = "The Scroll Message Relayer"
app.Description = "Message Relayer contains two main service: 1) relay l1 message to l2. 2) relay l2 message to l1."
app.Version = version.Version
app.Flags = append(app.Flags, utils.CommonFlags...)
app.Commands = []*cli.Command{}
app.Before = func(ctx *cli.Context) error {
return utils.LogSetup(ctx)
}
// Register `message-relayer-test` app for integration-test.
utils.RegisterSimulation(app, utils.MessageRelayerApp)
}
func action(ctx *cli.Context) error {
// Load config file.
cfgFile := ctx.String(utils.ConfigFileFlag.Name)
cfg, err := config.NewConfig(cfgFile)
if err != nil {
log.Crit("failed to load config file", "config file", cfgFile, "error", err)
}
subCtx, cancel := context.WithCancel(ctx.Context)
// Init db connection
db, err := database.InitDB(cfg.DBConfig)
if err != nil {
log.Crit("failed to init db connection", "err", err)
}
defer func() {
cancel()
if err = database.CloseDB(db); err != nil {
log.Error("can not close ormFactory", "error", err)
}
}()
registry := prometheus.DefaultRegisterer
metrics.Server(ctx, registry.(*prometheus.Registry))
l1relayer, err := relayer.NewLayer1Relayer(ctx.Context, db, cfg.L1Config.RelayerConfig, registry)
if err != nil {
log.Error("failed to create new l1 relayer", "config file", cfgFile, "error", err)
return err
}
// Start l1relayer process
go utils.Loop(subCtx, 10*time.Second, l1relayer.ProcessSavedEvents)
// Finish start all message relayer functions
log.Info("Start message-relayer successfully")
// Catch CTRL-C to ensure a graceful shutdown.
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
// Wait until the interrupt signal is received from an OS signal.
<-interrupt
return nil
}
// Run message_relayer cmd instance.
func Run() {
if err := app.Run(os.Args); err != nil {
_, _ = fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}

View File

@@ -1,7 +0,0 @@
package main
import "scroll-tech/bridge/cmd/msg_relayer/app"
func main() {
app.Run()
}

View File

@@ -1,7 +0,0 @@
package main
import "scroll-tech/bridge/cmd/rollup_relayer/app"
func main() {
app.Run()
}

View File

@@ -1,54 +0,0 @@
package relayer
import (
"sync"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
type l1RelayerMetrics struct {
bridgeL1RelayedMsgsTotal prometheus.Counter
bridgeL1RelayedMsgsFailureTotal prometheus.Counter
bridgeL1RelayerGasPriceOraclerRunTotal prometheus.Counter
bridgeL1RelayerLastGasPrice prometheus.Gauge
bridgeL1MsgsRelayedConfirmedTotal prometheus.Counter
bridgeL1GasOraclerConfirmedTotal prometheus.Counter
}
var (
initL1RelayerMetricOnce sync.Once
l1RelayerMetric *l1RelayerMetrics
)
func initL1RelayerMetrics(reg prometheus.Registerer) *l1RelayerMetrics {
initL1RelayerMetricOnce.Do(func() {
l1RelayerMetric = &l1RelayerMetrics{
bridgeL1RelayedMsgsTotal: promauto.With(reg).NewCounter(prometheus.CounterOpts{
Name: "bridge_layer1_msg_relayed_total",
Help: "The total number of the l1 relayed message.",
}),
bridgeL1RelayedMsgsFailureTotal: promauto.With(reg).NewCounter(prometheus.CounterOpts{
Name: "bridge_layer1_msg_relayed_failure_total",
Help: "The total number of the l1 relayed failure message.",
}),
bridgeL1MsgsRelayedConfirmedTotal: promauto.With(reg).NewCounter(prometheus.CounterOpts{
Name: "bridge_layer1_relayed_confirmed_total",
Help: "The total number of layer1 relayed confirmed",
}),
bridgeL1RelayerGasPriceOraclerRunTotal: promauto.With(reg).NewCounter(prometheus.CounterOpts{
Name: "bridge_layer1_gas_price_oracler_total",
Help: "The total number of layer1 gas price oracler run total",
}),
bridgeL1RelayerLastGasPrice: promauto.With(reg).NewGauge(prometheus.GaugeOpts{
Name: "bridge_layer1_gas_price_latest_gas_price",
Help: "The latest gas price of bridge relayer l1",
}),
bridgeL1GasOraclerConfirmedTotal: promauto.With(reg).NewCounter(prometheus.CounterOpts{
Name: "bridge_layer1_gas_oracler_confirmed_total",
Help: "The total number of layer1 relayed confirmed",
}),
}
})
return l1RelayerMetric
}

View File

@@ -1,74 +0,0 @@
package relayer
import (
"sync"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
type l2RelayerMetrics struct {
bridgeL2RelayerProcessPendingBatchTotal prometheus.Counter
bridgeL2RelayerProcessPendingBatchSuccessTotal prometheus.Counter
bridgeL2RelayerGasPriceOraclerRunTotal prometheus.Counter
bridgeL2RelayerLastGasPrice prometheus.Gauge
bridgeL2RelayerProcessCommittedBatchesTotal prometheus.Counter
bridgeL2RelayerProcessCommittedBatchesFinalizedTotal prometheus.Counter
bridgeL2RelayerProcessCommittedBatchesFinalizedSuccessTotal prometheus.Counter
bridgeL2BatchesCommittedConfirmedTotal prometheus.Counter
bridgeL2BatchesFinalizedConfirmedTotal prometheus.Counter
bridgeL2BatchesGasOraclerConfirmedTotal prometheus.Counter
}
var (
initL2RelayerMetricOnce sync.Once
l2RelayerMetric *l2RelayerMetrics
)
func initL2RelayerMetrics(reg prometheus.Registerer) *l2RelayerMetrics {
initL2RelayerMetricOnce.Do(func() {
l2RelayerMetric = &l2RelayerMetrics{
bridgeL2RelayerProcessPendingBatchTotal: promauto.With(reg).NewCounter(prometheus.CounterOpts{
Name: "bridge_layer2_process_pending_batch_total",
Help: "The total number of layer2 process pending batch",
}),
bridgeL2RelayerProcessPendingBatchSuccessTotal: promauto.With(reg).NewCounter(prometheus.CounterOpts{
Name: "bridge_layer2_process_pending_batch_success_total",
Help: "The total number of layer2 process pending success batch",
}),
bridgeL2RelayerGasPriceOraclerRunTotal: promauto.With(reg).NewCounter(prometheus.CounterOpts{
Name: "bridge_layer2_gas_price_oracler_total",
Help: "The total number of layer2 gas price oracler run total",
}),
bridgeL2RelayerLastGasPrice: promauto.With(reg).NewGauge(prometheus.GaugeOpts{
Name: "bridge_layer2_gas_price_latest_gas_price",
Help: "The latest gas price of bridge relayer l2",
}),
bridgeL2RelayerProcessCommittedBatchesTotal: promauto.With(reg).NewCounter(prometheus.CounterOpts{
Name: "bridge_layer2_process_committed_batches_total",
Help: "The total number of layer2 process committed batches run total",
}),
bridgeL2RelayerProcessCommittedBatchesFinalizedTotal: promauto.With(reg).NewCounter(prometheus.CounterOpts{
Name: "bridge_layer2_process_committed_batches_finalized_total",
Help: "The total number of layer2 process committed batches finalized total",
}),
bridgeL2RelayerProcessCommittedBatchesFinalizedSuccessTotal: promauto.With(reg).NewCounter(prometheus.CounterOpts{
Name: "bridge_layer2_process_committed_batches_finalized_success_total",
Help: "The total number of layer2 process committed batches finalized success total",
}),
bridgeL2BatchesCommittedConfirmedTotal: promauto.With(reg).NewCounter(prometheus.CounterOpts{
Name: "bridge_layer2_process_committed_batches_confirmed_total",
Help: "The total number of layer2 process committed batches confirmed total",
}),
bridgeL2BatchesFinalizedConfirmedTotal: promauto.With(reg).NewCounter(prometheus.CounterOpts{
Name: "bridge_layer2_process_finalized_batches_confirmed_total",
Help: "The total number of layer2 process finalized batches confirmed total",
}),
bridgeL2BatchesGasOraclerConfirmedTotal: promauto.With(reg).NewCounter(prometheus.CounterOpts{
Name: "bridge_layer2_process_gras_oracler_confirmed_total",
Help: "The total number of layer2 process finalized batches confirmed total",
}),
}
})
return l2RelayerMetric
}

View File

@@ -1,83 +0,0 @@
package tests
import (
"context"
"math/big"
"testing"
"github.com/scroll-tech/go-ethereum/accounts/abi/bind"
"github.com/scroll-tech/go-ethereum/common"
geth_types "github.com/scroll-tech/go-ethereum/core/types"
"github.com/scroll-tech/go-ethereum/rpc"
"github.com/stretchr/testify/assert"
"scroll-tech/common/database"
"scroll-tech/common/types"
"scroll-tech/bridge/internal/controller/relayer"
"scroll-tech/bridge/internal/controller/watcher"
"scroll-tech/bridge/internal/orm"
)
func testRelayL1MessageSucceed(t *testing.T) {
db := setupDB(t)
defer database.CloseDB(db)
prepareContracts(t)
l1Cfg := bridgeApp.Config.L1Config
l2Cfg := bridgeApp.Config.L2Config
// Create L1Relayer
l1Relayer, err := relayer.NewLayer1Relayer(context.Background(), db, l1Cfg.RelayerConfig, nil)
assert.NoError(t, err)
// Create L1Watcher
confirmations := rpc.LatestBlockNumber
l1Watcher := watcher.NewL1WatcherClient(context.Background(), l1Client, 0, confirmations, l1Cfg.L1MessengerAddress,
l1Cfg.L1MessageQueueAddress, l1Cfg.ScrollChainContractAddress, db, nil)
// Create L2Watcher
l2Watcher := watcher.NewL2WatcherClient(context.Background(), l2Client, confirmations, l2Cfg.L2MessengerAddress,
l2Cfg.L2MessageQueueAddress, l2Cfg.WithdrawTrieRootSlot, db, nil)
// send message through l1 messenger contract
nonce, err := l1MessengerInstance.MessageNonce(&bind.CallOpts{})
assert.NoError(t, err)
sendTx, err := l1MessengerInstance.SendMessage(l1Auth, l2Auth.From, big.NewInt(0), common.Hex2Bytes("00112233"), big.NewInt(0))
assert.NoError(t, err)
sendReceipt, err := bind.WaitMined(context.Background(), l1Client, sendTx)
assert.NoError(t, err)
if sendReceipt.Status != geth_types.ReceiptStatusSuccessful || err != nil {
t.Fatalf("Call failed")
}
// l1 watch process events
l1Watcher.FetchContractEvent()
l1MessageOrm := orm.NewL1Message(db)
// check db status
msg, err := l1MessageOrm.GetL1MessageByQueueIndex(nonce.Uint64())
assert.NoError(t, err)
assert.Equal(t, types.MsgStatus(msg.Status), types.MsgPending)
assert.Equal(t, msg.Target, l2Auth.From.String())
// process l1 messages
l1Relayer.ProcessSavedEvents()
l1Message, err := l1MessageOrm.GetL1MessageByQueueIndex(nonce.Uint64())
assert.NoError(t, err)
assert.NotEmpty(t, l1Message.Layer2Hash)
assert.Equal(t, types.MsgStatus(l1Message.Status), types.MsgSubmitted)
relayTx, _, err := l2Client.TransactionByHash(context.Background(), common.HexToHash(l1Message.Layer2Hash))
assert.NoError(t, err)
relayTxReceipt, err := bind.WaitMined(context.Background(), l2Client, relayTx)
assert.NoError(t, err)
assert.Equal(t, len(relayTxReceipt.Logs), 1)
// fetch message relayed events
l2Watcher.FetchContractEvent()
msg, err = l1MessageOrm.GetL1MessageByQueueIndex(nonce.Uint64())
assert.NoError(t, err)
assert.Equal(t, types.MsgStatus(msg.Status), types.MsgConfirmed)
}

View File

@@ -20,7 +20,7 @@ RUN find ./ | grep libzktrie.so | xargs -I{} cp {} /app/target/release/
FROM scrolltech/go-rust-builder:go-1.19-rust-nightly-2022-12-10 as base
WORKDIR /src
COPY go.work* ./
COPY ./bridge/go.* ./bridge/
COPY ./rollup/go.* ./rollup/
COPY ./common/go.* ./common/
COPY ./coordinator/go.* ./coordinator/
COPY ./database/go.* ./database/

View File

@@ -3,7 +3,7 @@ FROM scrolltech/go-alpine-builder:1.19 as base
WORKDIR /src
COPY go.work* ./
COPY ./bridge/go.* ./bridge/
COPY ./rollup/go.* ./rollup/
COPY ./common/go.* ./common/
COPY ./coordinator/go.* ./coordinator/
COPY ./database/go.* ./database/

View File

@@ -3,7 +3,7 @@ FROM scrolltech/go-alpine-builder:1.19 as base
WORKDIR /src
COPY go.work* ./
COPY ./bridge/go.* ./bridge/
COPY ./rollup/go.* ./rollup/
COPY ./common/go.* ./common/
COPY ./coordinator/go.* ./coordinator/
COPY ./database/go.* ./database/
@@ -18,7 +18,7 @@ FROM base as builder
RUN --mount=target=. \
--mount=type=cache,target=/root/.cache/go-build \
cd /src/bridge/cmd/event_watcher/ && go build -v -p 4 -o /bin/event_watcher
cd /src/rollup/cmd/event_watcher/ && go build -v -p 4 -o /bin/event_watcher
# Pull event_watcher into a second stage deploy alpine container
FROM alpine:latest

View File

@@ -3,7 +3,7 @@ FROM scrolltech/go-alpine-builder:1.19 as base
WORKDIR /src
COPY go.work* ./
COPY ./bridge/go.* ./bridge/
COPY ./rollup/go.* ./rollup/
COPY ./common/go.* ./common/
COPY ./coordinator/go.* ./coordinator/
COPY ./database/go.* ./database/
@@ -18,7 +18,7 @@ FROM base as builder
RUN --mount=target=. \
--mount=type=cache,target=/root/.cache/go-build \
cd /src/bridge/cmd/gas_oracle/ && go build -v -p 4 -o /bin/gas_oracle
cd /src/rollup/cmd/gas_oracle/ && go build -v -p 4 -o /bin/gas_oracle
# Pull gas_oracle into a second stage deploy alpine container
FROM alpine:latest

View File

@@ -1,28 +0,0 @@
# Download Go dependencies
FROM scrolltech/go-alpine-builder:1.19 as base
WORKDIR /src
COPY go.work* ./
COPY ./bridge/go.* ./bridge/
COPY ./common/go.* ./common/
COPY ./coordinator/go.* ./coordinator/
COPY ./database/go.* ./database/
COPY ./prover-stats-api/go.* ./prover-stats-api/
COPY ./prover/go.* ./prover/
COPY ./tests/integration-test/go.* ./tests/integration-test/
COPY ./bridge-history-api/go.* ./bridge-history-api/
RUN go mod download -x
# Build msg_relayer
FROM base as builder
RUN --mount=target=. \
--mount=type=cache,target=/root/.cache/go-build \
cd /src/bridge/cmd/msg_relayer/ && go build -v -p 4 -o /bin/msg_relayer
# Pull msg_relayer into a second stage deploy alpine container
FROM alpine:latest
COPY --from=builder /bin/msg_relayer /bin/
ENTRYPOINT ["msg_relayer"]

View File

@@ -1,5 +0,0 @@
assets/
docs/
l2geth/
rpc-gateway/
*target/*

View File

@@ -3,7 +3,7 @@ FROM scrolltech/go-alpine-builder:1.19 as base
WORKDIR /src
COPY go.work* ./
COPY ./bridge/go.* ./bridge/
COPY ./rollup/go.* ./rollup/
COPY ./common/go.* ./common/
COPY ./coordinator/go.* ./coordinator/
COPY ./database/go.* ./database/

View File

@@ -3,7 +3,7 @@ FROM scrolltech/go-alpine-builder:1.19 as base
WORKDIR /src
COPY go.work* ./
COPY ./bridge/go.* ./bridge/
COPY ./rollup/go.* ./rollup/
COPY ./common/go.* ./common/
COPY ./coordinator/go.* ./coordinator/
COPY ./database/go.* ./database/
@@ -18,7 +18,7 @@ FROM base as builder
RUN --mount=target=. \
--mount=type=cache,target=/root/.cache/go-build \
cd /src/bridge/cmd/rollup_relayer/ && go build -v -p 4 -o /bin/rollup_relayer
cd /src/rollup/cmd/rollup_relayer/ && go build -v -p 4 -o /bin/rollup_relayer
# Pull rollup_relayer into a second stage deploy alpine container
FROM alpine:latest

View File

@@ -3,7 +3,7 @@ set -uex
profile_name=$1
exclude_dirs=("scroll-tech/bridge/cmd" "scroll-tech/bridge/tests" "scroll-tech/bridge/mock_bridge" "scroll-tech/coordinator/cmd" "scroll-tech/coordinator/internal/logic/verifier")
exclude_dirs=("scroll-tech/rollup/cmd" "scroll-tech/rollup/tests" "scroll-tech/rollup/mock_bridge" "scroll-tech/coordinator/cmd" "scroll-tech/coordinator/internal/logic/verifier")
all_packages=$(go list ./... | grep -v "^scroll-tech/${profile_name}$")
coverpkg="scroll-tech/${profile_name}"

View File

@@ -6,7 +6,7 @@ flag_management:
default_rules:
carryforward: true
individual_flags:
- name: bridge
- name: rollup
statuses:
- type: project
target: auto

View File

@@ -5,8 +5,9 @@ import (
"os"
"os/exec"
"strings"
"sync"
"sync/atomic"
"github.com/docker/docker/pkg/reexec"
cmap "github.com/orcaman/concurrent-map"
)
@@ -26,8 +27,9 @@ type Cmd struct {
name string
args []string
mu sync.Mutex
cmd *exec.Cmd
isRunning uint64
cmd *exec.Cmd
app *exec.Cmd
checkFuncs cmap.ConcurrentMap //map[string]checkFunc
@@ -38,13 +40,23 @@ type Cmd struct {
}
// NewCmd create Cmd instance.
func NewCmd(name string, args ...string) *Cmd {
return &Cmd{
func NewCmd(name string, params ...string) *Cmd {
cmd := &Cmd{
checkFuncs: cmap.New(),
name: name,
args: args,
args: params,
ErrChan: make(chan error, 10),
cmd: exec.Command(name, params...),
app: &exec.Cmd{
Path: reexec.Self(),
Args: append([]string{name}, params...),
},
}
cmd.cmd.Stdout = cmd
cmd.cmd.Stderr = cmd
cmd.app.Stdout = cmd
cmd.app.Stderr = cmd
return cmd
}
// RegistFunc register check func
@@ -58,15 +70,14 @@ func (c *Cmd) UnRegistFunc(key string) {
}
func (c *Cmd) runCmd() {
cmd := exec.Command(c.args[0], c.args[1:]...) //nolint:gosec
cmd.Stdout = c
cmd.Stderr = c
c.ErrChan <- cmd.Run()
fmt.Println("cmd:", append([]string{c.name}, c.args...))
if atomic.CompareAndSwapUint64(&c.isRunning, 0, 1) {
c.ErrChan <- c.cmd.Run()
}
}
// RunCmd parallel running when parallel is true.
func (c *Cmd) RunCmd(parallel bool) {
fmt.Println("cmd:", c.args)
if parallel {
go c.runCmd()
} else {

View File

@@ -3,41 +3,45 @@ package cmd
import (
"fmt"
"os"
"os/exec"
"strings"
"sync/atomic"
"syscall"
"testing"
"time"
"github.com/docker/docker/pkg/reexec"
"github.com/stretchr/testify/assert"
)
// IsRunning 1 started, 0 not started.
func (c *Cmd) IsRunning() bool {
return atomic.LoadUint64(&c.isRunning) == 1
}
func (c *Cmd) runApp() {
fmt.Println("cmd:", append([]string{c.name}, c.args...))
if atomic.CompareAndSwapUint64(&c.isRunning, 0, 1) {
c.ErrChan <- c.app.Run()
}
}
// RunApp exec's the current binary using name as argv[0] which will trigger the
// reexec init function for that name (e.g. "geth-test" in cmd/geth/run_test.go)
func (c *Cmd) RunApp(waitResult func() bool) {
fmt.Println("cmd: ", append([]string{c.name}, c.args...))
cmd := &exec.Cmd{
Path: reexec.Self(),
Args: append([]string{c.name}, c.args...),
Stderr: c,
Stdout: c,
}
if waitResult != nil {
go func() {
_ = cmd.Run()
c.runApp()
}()
waitResult()
} else {
_ = cmd.Run()
c.runApp()
}
c.mu.Lock()
c.cmd = cmd
c.mu.Unlock()
}
// WaitExit wait util process exit.
func (c *Cmd) WaitExit() {
if atomic.LoadUint64(&c.isRunning) == 0 {
return
}
// Wait all the check functions are finished, interrupt loop when appear error.
var err error
for err == nil && !c.checkFuncs.IsEmpty() {
@@ -52,20 +56,18 @@ func (c *Cmd) WaitExit() {
}
// Send interrupt signal.
c.mu.Lock()
_ = c.cmd.Process.Signal(os.Interrupt)
// should use `_ = c.cmd.Process.Wait()` here, but we have some bugs in coordinator's graceful exit,
_ = c.app.Process.Signal(os.Interrupt)
// should use `_ = c.app.Process.Wait()` here, but we have some bugs in coordinator's graceful exit,
// so we use `Kill` as a temp workaround. And since `WaitExit` is only used in integration tests, so
// it won't really affect our functionalities.
_ = c.cmd.Process.Kill()
c.mu.Unlock()
if err = c.app.Process.Signal(syscall.SIGTERM); err != nil {
_ = c.app.Process.Kill()
}
}
// Interrupt send interrupt signal.
func (c *Cmd) Interrupt() {
c.mu.Lock()
c.ErrChan <- c.cmd.Process.Signal(os.Interrupt)
c.mu.Unlock()
c.ErrChan <- c.app.Process.Signal(os.Interrupt)
}
// WaitResult return true when get the keyword during timeout.

View File

@@ -12,7 +12,7 @@ import (
)
func TestCmd(t *testing.T) {
app := cmd.NewCmd("curTime", "date", "+%Y-%m-%d")
app := cmd.NewCmd("date", "+%Y-%m-%d")
tm := time.Now()
curTime := fmt.Sprintf("%d-%02d-%02d", tm.Year(), tm.Month(), tm.Day())

View File

@@ -27,6 +27,7 @@ var (
// AppAPI app interface.
type AppAPI interface {
IsRunning() bool
WaitResult(t *testing.T, timeout time.Duration, keyword string) bool
RunApp(waitResult func() bool)
WaitExit()

View File

@@ -36,7 +36,7 @@ func NewImgDB(image, password, dbName string, port int) ImgInstance {
dbName: dbName,
port: port,
}
img.cmd = cmd.NewCmd(img.name, img.prepare()...)
img.cmd = cmd.NewCmd("docker", img.prepare()...)
return img
}
@@ -89,7 +89,7 @@ func (i *ImgDB) IsRunning() bool {
}
func (i *ImgDB) prepare() []string {
cmd := []string{"docker", "run", "--rm", "--name", i.name, "-p", fmt.Sprintf("%d:5432", i.port)}
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),

View File

@@ -42,7 +42,7 @@ func NewImgGeth(image, volume, ipc string, hPort, wPort int) GethImgInstance {
httpPort: hPort,
wsPort: wPort,
}
img.cmd = cmd.NewCmd(img.name, img.prepare()...)
img.cmd = cmd.NewCmd("docker", img.params()...)
return img
}
@@ -149,8 +149,8 @@ func (i *ImgGeth) Stop() error {
return cli.ContainerRemove(ctx, i.id, types.ContainerRemoveOptions{})
}
func (i *ImgGeth) prepare() []string {
cmds := []string{"docker", "run", "--rm", "--name", i.name}
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"}...)

View File

@@ -1,4 +1,4 @@
FROM scrolltech/l2geth:scroll-v4.3.34
FROM scrolltech/l2geth:scroll-v4.3.55
RUN mkdir -p /l2geth/keystore

View File

@@ -14,7 +14,7 @@ require (
github.com/modern-go/reflect2 v1.0.2
github.com/orcaman/concurrent-map v1.0.0
github.com/prometheus/client_golang v1.14.0
github.com/scroll-tech/go-ethereum v1.10.14-0.20230812030736-25fe3ba69a28
github.com/scroll-tech/go-ethereum v1.10.14-0.20230829000527-f883dcdc21fc
github.com/stretchr/testify v1.8.3
github.com/urfave/cli/v2 v2.25.7
gorm.io/driver/postgres v1.5.0
@@ -117,7 +117,7 @@ require (
golang.org/x/arch v0.4.0 // indirect
golang.org/x/crypto v0.12.0 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.12.0 // indirect
golang.org/x/net v0.14.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/text v0.12.0 // indirect

View File

@@ -434,8 +434,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.20230812030736-25fe3ba69a28 h1:CECBTWhZ5NGAn8lGFB4ooRAYxZns8PXoX8kTR/14c04=
github.com/scroll-tech/go-ethereum v1.10.14-0.20230812030736-25fe3ba69a28/go.mod h1:DiN3p2inoXOxGffxSswDKqWjQ7bU+Mp0c9v0XQXKmaA=
github.com/scroll-tech/go-ethereum v1.10.14-0.20230829000527-f883dcdc21fc h1:eK3NOpjgm/b2TQ6rYqWx92Zri0kBuxf6gKjjsVxWKr8=
github.com/scroll-tech/go-ethereum v1.10.14-0.20230829000527-f883dcdc21fc/go.mod h1:DiN3p2inoXOxGffxSswDKqWjQ7bU+Mp0c9v0XQXKmaA=
github.com/scroll-tech/zktrie v0.6.0 h1:xLrMAO31Yo2BiPg1jtYKzcjpEFnXy8acbB7iIsyshPs=
github.com/scroll-tech/zktrie v0.6.0/go.mod h1:XvNo7vAk8yxNyTjBDj5WIiFzYW4bx/gJ78+NK6Zn6Uk=
github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
@@ -576,8 +576,8 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=

View File

@@ -32,7 +32,7 @@ dependencies = [
[[package]]
name = "aggregator"
version = "0.1.0"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.6.5#0cafc84c6dce2c0f385b36445fb3c61ca30d28ef"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.7.5#aa9a9aff698a5b253d1f3c29ea3d3006364777bf"
dependencies = [
"ark-std",
"env_logger 0.10.0",
@@ -433,7 +433,7 @@ checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
[[package]]
name = "bus-mapping"
version = "0.1.0"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.6.5#0cafc84c6dce2c0f385b36445fb3c61ca30d28ef"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.7.5#aa9a9aff698a5b253d1f3c29ea3d3006364777bf"
dependencies = [
"eth-types",
"ethers-core",
@@ -1049,7 +1049,7 @@ dependencies = [
[[package]]
name = "eth-types"
version = "0.1.0"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.6.5#0cafc84c6dce2c0f385b36445fb3c61ca30d28ef"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.7.5#aa9a9aff698a5b253d1f3c29ea3d3006364777bf"
dependencies = [
"ethers-core",
"ethers-signers",
@@ -1226,7 +1226,7 @@ dependencies = [
[[package]]
name = "external-tracer"
version = "0.1.0"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.6.5#0cafc84c6dce2c0f385b36445fb3c61ca30d28ef"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.7.5#aa9a9aff698a5b253d1f3c29ea3d3006364777bf"
dependencies = [
"eth-types",
"geth-utils",
@@ -1439,7 +1439,7 @@ dependencies = [
[[package]]
name = "gadgets"
version = "0.1.0"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.6.5#0cafc84c6dce2c0f385b36445fb3c61ca30d28ef"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.7.5#aa9a9aff698a5b253d1f3c29ea3d3006364777bf"
dependencies = [
"digest 0.7.6",
"eth-types",
@@ -1479,7 +1479,7 @@ dependencies = [
[[package]]
name = "geth-utils"
version = "0.1.0"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.6.5#0cafc84c6dce2c0f385b36445fb3c61ca30d28ef"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.7.5#aa9a9aff698a5b253d1f3c29ea3d3006364777bf"
dependencies = [
"env_logger 0.9.3",
"gobuild 0.1.0-alpha.2 (git+https://github.com/scroll-tech/gobuild.git)",
@@ -2077,7 +2077,7 @@ dependencies = [
[[package]]
name = "keccak256"
version = "0.1.0"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.6.5#0cafc84c6dce2c0f385b36445fb3c61ca30d28ef"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.7.5#aa9a9aff698a5b253d1f3c29ea3d3006364777bf"
dependencies = [
"env_logger 0.9.3",
"eth-types",
@@ -2264,7 +2264,7 @@ dependencies = [
[[package]]
name = "mock"
version = "0.1.0"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.6.5#0cafc84c6dce2c0f385b36445fb3c61ca30d28ef"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.7.5#aa9a9aff698a5b253d1f3c29ea3d3006364777bf"
dependencies = [
"eth-types",
"ethers-core",
@@ -2279,7 +2279,7 @@ dependencies = [
[[package]]
name = "mpt-zktrie"
version = "0.1.0"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.6.5#0cafc84c6dce2c0f385b36445fb3c61ca30d28ef"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.7.5#aa9a9aff698a5b253d1f3c29ea3d3006364777bf"
dependencies = [
"bus-mapping",
"eth-types",
@@ -2754,8 +2754,8 @@ dependencies = [
[[package]]
name = "prover"
version = "0.6.4"
source = "git+https://github.com/scroll-tech/scroll-prover?tag=v0.6.5#ccb3cd457080dcfdf8ae59416eb47ae9b6b6ddd6"
version = "0.7.5"
source = "git+https://github.com/scroll-tech/scroll-prover?tag=v0.7.5#9699d40940aed2f14d8e1958167d714bca2c9984"
dependencies = [
"aggregator",
"anyhow",
@@ -3624,7 +3624,7 @@ checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
[[package]]
name = "snark-verifier"
version = "0.1.0"
source = "git+https://github.com/scroll-tech//snark-verifier?tag=v0.1.1#11a09d4a37c31c659b29e2dac0ceb544a776ad7b"
source = "git+https://github.com/scroll-tech/snark-verifier?tag=v0.1.2#4466059ce9a6dfaf26455e4ffb61d72af775cf52"
dependencies = [
"bytes",
"ethereum-types 0.14.1",
@@ -3648,7 +3648,7 @@ dependencies = [
[[package]]
name = "snark-verifier-sdk"
version = "0.0.1"
source = "git+https://github.com/scroll-tech//snark-verifier?tag=v0.1.1#11a09d4a37c31c659b29e2dac0ceb544a776ad7b"
source = "git+https://github.com/scroll-tech/snark-verifier?tag=v0.1.2#4466059ce9a6dfaf26455e4ffb61d72af775cf52"
dependencies = [
"bincode",
"env_logger 0.10.0",
@@ -4039,8 +4039,8 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "types"
version = "0.6.4"
source = "git+https://github.com/scroll-tech/scroll-prover?tag=v0.6.5#ccb3cd457080dcfdf8ae59416eb47ae9b6b6ddd6"
version = "0.7.5"
source = "git+https://github.com/scroll-tech/scroll-prover?tag=v0.7.5#9699d40940aed2f14d8e1958167d714bca2c9984"
dependencies = [
"base64 0.13.1",
"blake2",
@@ -4491,7 +4491,7 @@ checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9"
[[package]]
name = "zkevm-circuits"
version = "0.1.0"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.6.5#0cafc84c6dce2c0f385b36445fb3c61ca30d28ef"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.7.5#aa9a9aff698a5b253d1f3c29ea3d3006364777bf"
dependencies = [
"array-init",
"bus-mapping",
@@ -4536,6 +4536,7 @@ dependencies = [
name = "zkp"
version = "0.1.0"
dependencies = [
"base64 0.13.1",
"env_logger 0.9.3",
"halo2_proofs",
"libc",

View File

@@ -19,23 +19,19 @@ maingate = { git = "https://github.com/scroll-tech/halo2wrong", branch = "halo2-
[patch."https://github.com/privacy-scaling-explorations/halo2curves.git"]
halo2curves = { git = "https://github.com/scroll-tech/halo2curves.git", branch = "0.3.1-derive-serde" }
[patch."https://github.com/scroll-tech/snark-verifier"]
snark-verifier = { git = "https://github.com/scroll-tech//snark-verifier", tag = "v0.1.1" }
snark-verifier-sdk = { git = "https://github.com/scroll-tech//snark-verifier", tag = "v0.1.1" }
[dependencies]
prover = { git = "https://github.com/scroll-tech/scroll-prover", tag = "v0.6.5" }
types = { git = "https://github.com/scroll-tech/scroll-prover", tag = "v0.6.5" }
prover = { git = "https://github.com/scroll-tech/scroll-prover", tag = "v0.7.5" }
types = { git = "https://github.com/scroll-tech/scroll-prover", tag = "v0.7.5" }
halo2_proofs = { git = "https://github.com/scroll-tech/halo2.git", branch = "develop" }
log = "0.4"
base64 = "0.13.0"
env_logger = "0.9.0"
libc = "0.2"
log = "0.4"
once_cell = "1.8.0"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0.66"
libc = "0.2"
once_cell = "1.8.0"
[profile.test]
opt-level = 3

View File

@@ -1,11 +1,14 @@
use crate::utils::{c_char_to_str, c_char_to_vec, vec_to_c_char, OUTPUT_DIR};
use crate::{
types::{CheckChunkProofsResponse, ProofResult},
utils::{c_char_to_str, c_char_to_vec, string_to_c_char, vec_to_c_char, OUTPUT_DIR},
};
use libc::c_char;
use prover::{
aggregator::{Prover, Verifier},
utils::{chunk_trace_to_witness_block, init_env_and_log},
BatchProof, ChunkHash, ChunkProof,
};
use std::{cell::OnceCell, panic, ptr::null};
use std::{cell::OnceCell, env, panic, ptr::null};
use types::eth::BlockTrace;
static mut PROVER: OnceCell<Prover> = OnceCell::new();
@@ -13,11 +16,15 @@ static mut VERIFIER: OnceCell<Verifier> = OnceCell::new();
/// # Safety
#[no_mangle]
pub unsafe extern "C" fn init_batch_prover(params_dir: *const c_char) {
pub unsafe extern "C" fn init_batch_prover(params_dir: *const c_char, assets_dir: *const c_char) {
init_env_and_log("ffi_batch_prove");
let params_dir = c_char_to_str(params_dir);
let prover = Prover::from_params_dir(params_dir);
let assets_dir = c_char_to_str(assets_dir);
// TODO: add a settings in scroll-prover.
env::set_var("SCROLL_PROVER_ASSETS_DIR", assets_dir);
let prover = Prover::from_dirs(params_dir, assets_dir);
PROVER.set(prover).unwrap();
}
@@ -30,39 +37,104 @@ pub unsafe extern "C" fn init_batch_verifier(params_dir: *const c_char, assets_d
let params_dir = c_char_to_str(params_dir);
let assets_dir = c_char_to_str(assets_dir);
// TODO: add a settings in scroll-prover.
env::set_var("SCROLL_PROVER_ASSETS_DIR", assets_dir);
let verifier = Verifier::from_dirs(params_dir, assets_dir);
VERIFIER.set(verifier).unwrap();
}
/// # Safety
#[no_mangle]
pub unsafe extern "C" fn get_batch_vk() -> *const c_char {
let vk_result = panic::catch_unwind(|| PROVER.get_mut().unwrap().get_vk());
vk_result
.ok()
.flatten()
.map_or(null(), |vk| string_to_c_char(base64::encode(vk)))
}
/// # Safety
#[no_mangle]
pub unsafe extern "C" fn check_chunk_proofs(chunk_proofs: *const c_char) -> *const c_char {
let check_result: Result<bool, String> = panic::catch_unwind(|| {
let chunk_proofs = c_char_to_vec(chunk_proofs);
let chunk_proofs = serde_json::from_slice::<Vec<ChunkProof>>(&chunk_proofs)
.map_err(|e| format!("failed to deserialize chunk proofs: {e:?}"))?;
if chunk_proofs.is_empty() {
return Err("provided chunk proofs are empty.".to_string());
}
let prover_ref = PROVER.get().expect("failed to get reference to PROVER.");
let valid = prover_ref.check_chunk_proofs(&chunk_proofs);
Ok(valid)
})
.unwrap_or_else(|e| Err(format!("unwind error: {e:?}")));
let r = match check_result {
Ok(valid) => CheckChunkProofsResponse {
ok: valid,
error: None,
},
Err(err) => CheckChunkProofsResponse {
ok: false,
error: Some(err),
},
};
serde_json::to_vec(&r).map_or(null(), vec_to_c_char)
}
/// # Safety
#[no_mangle]
pub unsafe extern "C" fn gen_batch_proof(
chunk_hashes: *const c_char,
chunk_proofs: *const c_char,
) -> *const c_char {
let chunk_hashes = c_char_to_vec(chunk_hashes);
let chunk_proofs = c_char_to_vec(chunk_proofs);
let proof_result: Result<Vec<u8>, String> = panic::catch_unwind(|| {
let chunk_hashes = c_char_to_vec(chunk_hashes);
let chunk_proofs = c_char_to_vec(chunk_proofs);
let chunk_hashes = serde_json::from_slice::<Vec<ChunkHash>>(&chunk_hashes).unwrap();
let chunk_proofs = serde_json::from_slice::<Vec<ChunkProof>>(&chunk_proofs).unwrap();
assert_eq!(chunk_hashes.len(), chunk_proofs.len());
let chunk_hashes = serde_json::from_slice::<Vec<ChunkHash>>(&chunk_hashes)
.map_err(|e| format!("failed to deserialize chunk hashes: {e:?}"))?;
let chunk_proofs = serde_json::from_slice::<Vec<ChunkProof>>(&chunk_proofs)
.map_err(|e| format!("failed to deserialize chunk proofs: {e:?}"))?;
let chunk_hashes_proofs = chunk_hashes
.into_iter()
.zip(chunk_proofs.into_iter())
.collect();
if chunk_hashes.len() != chunk_proofs.len() {
return Err(format!("chunk hashes and chunk proofs lengths mismatch: chunk_hashes.len() = {}, chunk_proofs.len() = {}",
chunk_hashes.len(), chunk_proofs.len()));
}
let chunk_hashes_proofs = chunk_hashes
.into_iter()
.zip(chunk_proofs.into_iter())
.collect();
let proof_result = panic::catch_unwind(|| {
let proof = PROVER
.get_mut()
.unwrap()
.expect("failed to get mutable reference to PROVER.")
.gen_agg_evm_proof(chunk_hashes_proofs, None, OUTPUT_DIR.as_deref())
.unwrap();
.map_err(|e| format!("failed to generate proof: {e:?}"))?;
serde_json::to_vec(&proof).unwrap()
});
proof_result.map_or(null(), vec_to_c_char)
serde_json::to_vec(&proof).map_err(|e| format!("failed to serialize the proof: {e:?}"))
})
.unwrap_or_else(|e| Err(format!("unwind error: {e:?}")));
let r = match proof_result {
Ok(proof_bytes) => ProofResult {
message: Some(proof_bytes),
error: None,
},
Err(err) => ProofResult {
message: None,
error: Some(err),
},
};
serde_json::to_vec(&r).map_or(null(), vec_to_c_char)
}
/// # Safety

View File

@@ -1,11 +1,14 @@
use crate::utils::{c_char_to_str, c_char_to_vec, vec_to_c_char, OUTPUT_DIR};
use crate::{
types::ProofResult,
utils::{c_char_to_str, c_char_to_vec, string_to_c_char, vec_to_c_char, OUTPUT_DIR},
};
use libc::c_char;
use prover::{
utils::init_env_and_log,
zkevm::{Prover, Verifier},
ChunkProof,
};
use std::{cell::OnceCell, panic, ptr::null};
use std::{cell::OnceCell, env, panic, ptr::null};
use types::eth::BlockTrace;
static mut PROVER: OnceCell<Prover> = OnceCell::new();
@@ -13,10 +16,14 @@ static mut VERIFIER: OnceCell<Verifier> = OnceCell::new();
/// # Safety
#[no_mangle]
pub unsafe extern "C" fn init_chunk_prover(params_dir: *const c_char) {
pub unsafe extern "C" fn init_chunk_prover(params_dir: *const c_char, assets_dir: *const c_char) {
init_env_and_log("ffi_chunk_prove");
let params_dir = c_char_to_str(params_dir);
let assets_dir = c_char_to_str(assets_dir);
// TODO: add a settings in scroll-prover.
env::set_var("SCROLL_PROVER_ASSETS_DIR", assets_dir);
let prover = Prover::from_params_dir(params_dir);
PROVER.set(prover).unwrap();
@@ -30,6 +37,8 @@ pub unsafe extern "C" fn init_chunk_verifier(params_dir: *const c_char, assets_d
let params_dir = c_char_to_str(params_dir);
let assets_dir = c_char_to_str(assets_dir);
// TODO: add a settings in scroll-prover.
env::set_var("SCROLL_PROVER_ASSETS_DIR", assets_dir);
let verifier = Verifier::from_dirs(params_dir, assets_dir);
VERIFIER.set(verifier).unwrap();
@@ -37,21 +46,45 @@ pub unsafe extern "C" fn init_chunk_verifier(params_dir: *const c_char, assets_d
/// # Safety
#[no_mangle]
pub unsafe extern "C" fn gen_chunk_proof(block_traces: *const c_char) -> *const c_char {
let block_traces = c_char_to_vec(block_traces);
let block_traces = serde_json::from_slice::<Vec<BlockTrace>>(&block_traces).unwrap();
pub unsafe extern "C" fn get_chunk_vk() -> *const c_char {
let vk_result = panic::catch_unwind(|| PROVER.get_mut().unwrap().get_vk());
vk_result
.ok()
.flatten()
.map_or(null(), |vk| string_to_c_char(base64::encode(vk)))
}
/// # Safety
#[no_mangle]
pub unsafe extern "C" fn gen_chunk_proof(block_traces: *const c_char) -> *const c_char {
let proof_result: Result<Vec<u8>, String> = panic::catch_unwind(|| {
let block_traces = c_char_to_vec(block_traces);
let block_traces = serde_json::from_slice::<Vec<BlockTrace>>(&block_traces)
.map_err(|e| format!("failed to deserialize block traces: {e:?}"))?;
let proof_result = panic::catch_unwind(|| {
let proof = PROVER
.get_mut()
.unwrap()
.expect("failed to get mutable reference to PROVER.")
.gen_chunk_proof(block_traces, None, OUTPUT_DIR.as_deref())
.unwrap();
.map_err(|e| format!("failed to generate proof: {e:?}"))?;
serde_json::to_vec(&proof).unwrap()
});
serde_json::to_vec(&proof).map_err(|e| format!("failed to serialize the proof: {e:?}"))
})
.unwrap_or_else(|e| Err(format!("unwind error: {e:?}")));
proof_result.map_or(null(), vec_to_c_char)
let r = match proof_result {
Ok(proof_bytes) => ProofResult {
message: Some(proof_bytes),
error: None,
},
Err(err) => ProofResult {
message: None,
error: Some(err),
},
};
serde_json::to_vec(&r).map_or(null(), vec_to_c_char)
}
/// # Safety

View File

@@ -2,4 +2,5 @@
mod batch;
mod chunk;
mod types;
mod utils;

View File

@@ -0,0 +1,22 @@
use serde::{Deserialize, Serialize};
// Represents the result of a chunk proof checking operation.
// `ok` indicates whether the proof checking was successful.
// `error` provides additional details in case the check failed.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct CheckChunkProofsResponse {
pub ok: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
// Encapsulates the result from generating a proof.
// `message` holds the generated proof in byte slice format.
// `error` provides additional details in case the proof generation failed.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ProofResult {
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<Vec<u8>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}

View File

@@ -19,6 +19,10 @@ pub(crate) fn c_char_to_vec(c: *const c_char) -> Vec<u8> {
cstr.to_bytes().to_vec()
}
pub(crate) fn string_to_c_char(string: String) -> *const c_char {
CString::new(string).unwrap().into_raw()
}
pub(crate) fn vec_to_c_char(bytes: Vec<u8>) -> *const c_char {
CString::new(bytes).unwrap().into_raw()
}

View File

@@ -1,10 +1,13 @@
void init_batch_prover(char* params_dir);
void init_batch_prover(char* params_dir, char* assets_dir);
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);
void init_chunk_prover(char* params_dir);
void init_chunk_prover(char* params_dir, char* assets_dir);
void init_chunk_verifier(char* params_dir, char* assets_dir);
char* get_chunk_vk();
char* gen_chunk_proof(char* block_traces);
char verify_chunk_proof(char* proof);

View File

@@ -10,6 +10,16 @@ import (
"github.com/scroll-tech/go-ethereum/crypto"
)
// BatchMeta contains metadata of a batch.
type BatchMeta struct {
StartChunkIndex uint64
StartChunkHash string
EndChunkIndex uint64
EndChunkHash string
TotalL1CommitGas uint64
TotalL1CommitCalldataSize uint32
}
// BatchHeader contains batch header info to be committed.
type BatchHeader struct {
// Encoded in BatchHeaderV0Codec

View File

@@ -3,11 +3,13 @@ package types
import (
"encoding/binary"
"errors"
"fmt"
"math"
"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"
)
// CalldataNonZeroByteGas is the gas consumption per non zero byte in calldata.
@@ -22,9 +24,10 @@ func GetKeccak256Gas(size uint64) uint64 {
type WrappedBlock struct {
Header *types.Header `json:"header"`
// Transactions is only used for recover types.Transactions, the from of types.TransactionData field is missing.
Transactions []*types.TransactionData `json:"transactions"`
WithdrawRoot common.Hash `json:"withdraw_trie_root,omitempty"`
RowConsumption *types.RowConsumption `json:"row_consumption"`
Transactions []*types.TransactionData `json:"transactions"`
WithdrawRoot common.Hash `json:"withdraw_trie_root,omitempty"`
RowConsumption *types.RowConsumption `json:"row_consumption"`
txPayloadLengthCache map[string]uint64
}
// NumL1Messages returns the number of L1 messages in this block.
@@ -95,8 +98,10 @@ func (w *WrappedBlock) EstimateL1CommitCalldataSize() uint64 {
if txData.Type == types.L1MessageTxType {
continue
}
size += uint64(len(txData.Data))
size += 4 // 4 bytes payload length
size += w.getTxPayloadLength(txData)
}
size += 60 // 60 bytes BlockContext
return size
}
@@ -110,25 +115,15 @@ func (w *WrappedBlock) EstimateL1CommitGas() uint64 {
continue
}
data, _ := hexutil.Decode(txData.Data)
tx := types.NewTx(&types.LegacyTx{
Nonce: txData.Nonce,
To: txData.To,
Value: txData.Value.ToInt(),
Gas: txData.Gas,
GasPrice: txData.GasPrice.ToInt(),
Data: data,
V: txData.V.ToInt(),
R: txData.R.ToInt(),
S: txData.S.ToInt(),
})
rlpTxData, _ := tx.MarshalBinary()
txPayloadLength := uint64(len(rlpTxData))
txPayloadLength := w.getTxPayloadLength(txData)
total += CalldataNonZeroByteGas * txPayloadLength // an over-estimate: treat each byte as non-zero
total += CalldataNonZeroByteGas * 4 // size of a uint32 field
total += CalldataNonZeroByteGas * 4 // 4 bytes payload length
total += GetKeccak256Gas(txPayloadLength) // l2 tx hash
}
// 60 bytes BlockContext calldata
total += CalldataNonZeroByteGas * 60
// sload
total += 2100 * numL1Messages // numL1Messages times cold sload in L1MessageQueue
@@ -139,13 +134,47 @@ func (w *WrappedBlock) EstimateL1CommitGas() uint64 {
return total
}
// L2TxsNum calculates the number of l2 txs.
func (w *WrappedBlock) L2TxsNum() uint64 {
var count uint64
for _, txData := range w.Transactions {
if txData.Type != types.L1MessageTxType {
count++
}
func (w *WrappedBlock) getTxPayloadLength(txData *types.TransactionData) uint64 {
if w.txPayloadLengthCache == nil {
w.txPayloadLengthCache = make(map[string]uint64)
}
return count
if length, exists := w.txPayloadLengthCache[txData.TxHash]; exists {
return length
}
rlpTxData, err := convertTxDataToRLPEncoding(txData)
if err != nil {
log.Crit("convertTxDataToRLPEncoding failed, which should not happen", "hash", txData.TxHash, "err", err)
return 0
}
txPayloadLength := uint64(len(rlpTxData))
w.txPayloadLengthCache[txData.TxHash] = txPayloadLength
return txPayloadLength
}
func convertTxDataToRLPEncoding(txData *types.TransactionData) ([]byte, error) {
data, err := hexutil.Decode(txData.Data)
if err != nil {
return nil, fmt.Errorf("failed to decode txData.Data: %s, err: %w", txData.Data, err)
}
tx := types.NewTx(&types.LegacyTx{
Nonce: txData.Nonce,
To: txData.To,
Value: txData.Value.ToInt(),
Gas: txData.Gas,
GasPrice: txData.GasPrice.ToInt(),
Data: data,
V: txData.V.ToInt(),
R: txData.R.ToInt(),
S: txData.S.ToInt(),
})
rlpTxData, err := tx.MarshalBinary()
if err != nil {
return nil, fmt.Errorf("failed to marshal binary of the tx: %+v, err: %w", tx, err)
}
return rlpTxData, nil
}

View File

@@ -8,7 +8,6 @@ import (
"strings"
"github.com/scroll-tech/go-ethereum/common"
"github.com/scroll-tech/go-ethereum/common/hexutil"
"github.com/scroll-tech/go-ethereum/core/types"
"github.com/scroll-tech/go-ethereum/crypto"
)
@@ -65,23 +64,7 @@ func (c *Chunk) Encode(totalL1MessagePoppedBefore uint64) ([]byte, error) {
if txData.Type == types.L1MessageTxType {
continue
}
data, err := hexutil.Decode(txData.Data)
if err != nil {
return nil, err
}
// right now we only support legacy tx
tx := types.NewTx(&types.LegacyTx{
Nonce: txData.Nonce,
To: txData.To,
Value: txData.Value.ToInt(),
Gas: txData.Gas,
GasPrice: txData.GasPrice.ToInt(),
Data: data,
V: txData.V.ToInt(),
R: txData.R.ToInt(),
S: txData.S.ToInt(),
})
rlpTxData, err := tx.MarshalBinary()
rlpTxData, err := convertTxDataToRLPEncoding(txData)
if err != nil {
return nil, err
}

View File

@@ -38,11 +38,15 @@ func TestChunkEncode(t *testing.T) {
wrappedBlock := &WrappedBlock{}
assert.NoError(t, json.Unmarshal(templateBlockTrace, wrappedBlock))
assert.Equal(t, uint64(0), wrappedBlock.NumL1Messages(0))
assert.Equal(t, uint64(298), wrappedBlock.EstimateL1CommitCalldataSize())
assert.Equal(t, uint64(2), wrappedBlock.NumL2Transactions())
chunk = &Chunk{
Blocks: []*WrappedBlock{
wrappedBlock,
},
}
assert.Equal(t, uint64(0), chunk.NumL1Messages(0))
assert.Equal(t, uint64(6006), chunk.EstimateL1CommitGas())
bytes, err = chunk.Encode(0)
hexString := hex.EncodeToString(bytes)
assert.NoError(t, err)
@@ -56,11 +60,15 @@ func TestChunkEncode(t *testing.T) {
wrappedBlock2 := &WrappedBlock{}
assert.NoError(t, json.Unmarshal(templateBlockTrace2, wrappedBlock2))
assert.Equal(t, uint64(11), wrappedBlock2.NumL1Messages(0)) // 0..=9 skipped, 10 included
assert.Equal(t, uint64(96), wrappedBlock2.EstimateL1CommitCalldataSize())
assert.Equal(t, uint64(1), wrappedBlock2.NumL2Transactions())
chunk = &Chunk{
Blocks: []*WrappedBlock{
wrappedBlock2,
},
}
assert.Equal(t, uint64(11), chunk.NumL1Messages(0))
assert.Equal(t, uint64(5002), chunk.EstimateL1CommitGas())
bytes, err = chunk.Encode(0)
hexString = hex.EncodeToString(bytes)
assert.NoError(t, err)
@@ -75,6 +83,8 @@ func TestChunkEncode(t *testing.T) {
wrappedBlock2,
},
}
assert.Equal(t, uint64(11), chunk.NumL1Messages(0))
assert.Equal(t, uint64(9958), chunk.EstimateL1CommitGas())
bytes, err = chunk.Encode(0)
hexString = hex.EncodeToString(bytes)
assert.NoError(t, err)
@@ -136,3 +146,81 @@ func TestChunkHash(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, "0x2eb7dd63bf8fc29a0f8c10d16c2ae6f9da446907c79d50f5c164d30dc8526b60", hash.Hex())
}
func TestErrorPaths(t *testing.T) {
// test 1: Header.Number is not a uint64
templateBlockTrace, err := os.ReadFile("../testdata/blockTrace_02.json")
assert.NoError(t, err)
wrappedBlock := &WrappedBlock{}
assert.NoError(t, json.Unmarshal(templateBlockTrace, wrappedBlock))
wrappedBlock.Header.Number = wrappedBlock.Header.Number.Lsh(wrappedBlock.Header.Number, 64)
bytes, err := wrappedBlock.Encode(0)
assert.Nil(t, bytes)
assert.Error(t, err)
assert.Contains(t, err.Error(), "block number is not uint64")
assert.NoError(t, json.Unmarshal(templateBlockTrace, wrappedBlock))
for i := 0; i < 65537; i++ {
wrappedBlock.Transactions = append(wrappedBlock.Transactions, wrappedBlock.Transactions[0])
}
bytes, err = wrappedBlock.Encode(0)
assert.Nil(t, bytes)
assert.Error(t, err)
assert.Contains(t, err.Error(), "number of transactions exceeds max uint16")
chunk := &Chunk{
Blocks: []*WrappedBlock{
wrappedBlock,
},
}
bytes, err = chunk.Encode(0)
assert.Nil(t, bytes)
assert.Error(t, err)
assert.Contains(t, err.Error(), "number of transactions exceeds max uint16")
wrappedBlock.Transactions = wrappedBlock.Transactions[:1]
wrappedBlock.Transactions[0].Data = "not-a-hex"
bytes, err = chunk.Encode(0)
assert.Nil(t, bytes)
assert.Error(t, err)
assert.Contains(t, err.Error(), "hex string without 0x prefix")
assert.NoError(t, json.Unmarshal(templateBlockTrace, wrappedBlock))
wrappedBlock.Transactions[0].TxHash = "not-a-hex"
_, err = chunk.Hash(0)
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid byte")
templateBlockTrace2, err := os.ReadFile("../testdata/blockTrace_04.json")
assert.NoError(t, err)
wrappedBlock2 := &WrappedBlock{}
assert.NoError(t, json.Unmarshal(templateBlockTrace2, wrappedBlock2))
for i := 0; i < 65535; i++ {
tx := &wrappedBlock2.Transactions[i]
txCopy := *tx
txCopy.Nonce = uint64(i + 1)
wrappedBlock2.Transactions = append(wrappedBlock2.Transactions, txCopy)
}
bytes, err = wrappedBlock2.Encode(0)
assert.Nil(t, bytes)
assert.Error(t, err)
assert.Contains(t, err.Error(), "number of L1 messages exceeds max uint16")
chunk = &Chunk{
Blocks: []*WrappedBlock{
wrappedBlock2,
},
}
bytes, err = chunk.Encode(0)
assert.Nil(t, bytes)
assert.Error(t, err)
assert.Contains(t, err.Error(), "number of L1 messages exceeds max uint16")
}

View File

@@ -126,8 +126,8 @@ const (
ProvingTaskUnassigned
// ProvingTaskAssigned : proving_task is assigned to be proved
ProvingTaskAssigned
// ProvingTaskProved DEPRECATED: proof has been returned by prover
ProvingTaskProved
// ProvingTaskProvedDEPRECATED DEPRECATED: proof has been returned by prover
ProvingTaskProvedDEPRECATED
// ProvingTaskVerified : proof is valid
ProvingTaskVerified
// ProvingTaskFailed : fail to generate proof
@@ -140,7 +140,7 @@ func (ps ProvingStatus) String() string {
return "unassigned"
case ProvingTaskAssigned:
return "assigned"
case ProvingTaskProved:
case ProvingTaskProvedDEPRECATED:
return "proved"
case ProvingTaskVerified:
return "verified"

View File

@@ -58,8 +58,8 @@ func TestProvingStatus(t *testing.T) {
"assigned",
},
{
"ProvingTaskProved",
ProvingTaskProved,
"ProvingTaskProvedDEPRECATED",
ProvingTaskProvedDEPRECATED,
"proved",
},
{

View File

@@ -211,6 +211,7 @@ func (a *ProofMsg) PublicKey() (string, error) {
// TaskMsg is a wrapper type around db ProveTask type.
type TaskMsg struct {
UUID string `json:"uuid"`
ID string `json:"id"`
Type ProofType `json:"type,omitempty"`
BatchTaskDetail *BatchTaskDetail `json:"batch_task_detail,omitempty"`

27
common/utils/http.go Normal file
View File

@@ -0,0 +1,27 @@
package utils
import (
"net/http"
"time"
)
// StartHTTPServer a public http server to be used.
func StartHTTPServer(address string, handler http.Handler) (*http.Server, error) {
srv := &http.Server{
Handler: handler,
Addr: address,
ReadTimeout: time.Second * 3,
WriteTimeout: time.Second * 3,
IdleTimeout: time.Second * 12,
}
errCh := make(chan error, 1)
go func() {
errCh <- srv.ListenAndServe()
}()
select {
case err := <-errCh:
return nil, err
case <-time.After(time.Second):
}
return srv, nil
}

View File

@@ -2,6 +2,9 @@ package utils
import (
"context"
"crypto/rand"
"fmt"
"math/big"
"time"
"github.com/modern-go/reflect2"
@@ -50,3 +53,9 @@ func Loop(ctx context.Context, period time.Duration, f func()) {
func IsNil(i interface{}) bool {
return i == nil || reflect2.IsNil(i)
}
// RandomURL return a random port endpoint.
func RandomURL() string {
id, _ := rand.Int(rand.Reader, big.NewInt(5000-1))
return fmt.Sprintf("localhost:%d", 10000+2000+id.Int64())
}

View File

@@ -0,0 +1,55 @@
package version
import (
"strconv"
"strings"
)
// CheckScrollProverVersion check the "scroll-prover" version, if it's different from the local one, return false
func CheckScrollProverVersion(proverVersion string) bool {
// 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 {
return false
}
local := strings.Split(Version, "-")
if len(local) != 4 {
return false
}
// compare the `scroll_prover` version
return remote[2] == local[2]
}
// CheckScrollProverVersionTag check the "scroll-prover" version's tag, if it's too old, return false
func CheckScrollProverVersionTag(proverVersion string) bool {
// 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 {
return false
}
remoteTagNums := strings.Split(strings.TrimPrefix(remote[0], "v"), ".")
if len(remoteTagNums) != 3 {
return false
}
remoteTagMajor, err := strconv.Atoi(remoteTagNums[0])
if err != nil {
return false
}
remoteTagMinor, err := strconv.Atoi(remoteTagNums[1])
if err != nil {
return false
}
remoteTagPatch, err := strconv.Atoi(remoteTagNums[2])
if err != nil {
return false
}
if remoteTagMajor < 4 {
return false
}
if remoteTagMinor == 1 && remoteTagPatch < 98 {
return false
}
return true
}

View File

@@ -3,10 +3,9 @@ package version
import (
"fmt"
"runtime/debug"
"strings"
)
var tag = "v4.1.83"
var tag = "v4.2.17"
var commit = func() string {
if info, ok := debug.ReadBuildInfo(); ok {
@@ -30,19 +29,3 @@ var ZkVersion = "000000-000000"
// Version denote the version of scroll protocol, including the l2geth, relayer, coordinator, prover, contracts and etc.
var Version = fmt.Sprintf("%s-%s-%s", tag, commit, ZkVersion)
// CheckScrollProverVersion check the "scroll-prover" version, if it's different from the local one, return false
func CheckScrollProverVersion(proverVersion string) bool {
// 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 {
return false
}
local := strings.Split(Version, "-")
if len(local) != 4 {
return false
}
// compare the `scroll_prover` version
return remote[2] == local[2] || remote[2] == "8c439b1"
}

View File

@@ -1,78 +1,78 @@
# Scroll Contracts
Note: For more comprehensive documentation, see [`./docs/`](./docs).
This directory contains the solidity code for Scroll L1 bridge and rollup contracts and L2 bridge and pre-deployed contracts. The [`specs`](../specs/) folder describes the overall Scroll protocol including the cross-domain messaging and rollup process. You can also find contract APIs and more details in the [`docs`](./docs) folder.
## Directory Structure
```
integration-test
|- xxx.test.ts - "Hardhat integration tests"
lib
|- forge-std - "foundry dependency"
scripts
|- deploy_xxx.ts - "hardhat deploy script"
|- foundry - "foundry deploy scripts"
src
|- test
| `- xxx.t.sol - "Unit testi in solidity"
`- xxx.sol - "solidity contract"
.gitmodules - "foundry dependecy modules"
foundry.toml - "configure foundry"
hardhat.config.ts - "configure hardhat"
remappings.txt - "foundry dependency mappings"
<pre>
├── <a href="./docs/">docs</a>: Documentation for the contracts
├── <a href="./integration-test/">integration-test</a>: Hardhat integration tests
├── <a href="./lib/">lib</a>: External libraries and testing tools
├── <a href="./scripts">scripts</a>: Deployment scripts
├── <a href="./src">src</a>
│ ├── <a href="./src/gas-swap/">gas-swap</a>: Utility contract that allows gas payment in other tokens
│ ├── <a href="./src/interfaces/">interfaces</a>: Common contract interfaces
│ ├── <a href="./src/L1/">L1</a>: Contracts deployed on the L1 (Ethereum)
│ │ ├── <a href="./src/L1/gateways/">gateways</a>: Gateway router and token gateway contracts
│ │ ├── <a href="./src/L1/rollup/">rollup</a>: Rollup contracts for data availability and finalization
│ │ ├── <a href="./src/L1/IL1ScrollMessenger.sol">IL1ScrollMessenger.sol</a>: L1 Scroll messenger interface
│ │ └── <a href="./src/L1/L1ScrollMessenger.sol">L1ScrollMessenger.sol</a>: L1 Scroll messenger contract
│ ├── <a href="./src/L2/">L2</a>: Contracts deployed on the L2 (Scroll)
│ │ ├── <a href="./src/L2/gateways/">gateways</a>: Gateway router and token gateway contracts
│ │ ├── <a href="./src/L2/predeploys/">predeploys</a>: Pre-deployed contracts on L2
│ │ ├── <a href="./src/L2/IL2ScrollMessenger.sol">IL2ScrollMessenger.sol</a>: L2 Scroll messenger interface
│ │ └── <a href="./src/L2/L2ScrollMessenger.sol">L2ScrollMessenger.sol</a>: L2 Scroll messenger contract
│ ├── <a href="./src/libraries/">libraries</a>: Shared contract libraries
│ ├── <a href="./src/misc/">misc</a>: Miscellaneous contracts
│ ├── <a href="./src/mocks/">mocks</a>: Mock contracts used in the testing
│ ├── <a href="./src/rate-limiter/">rate-limiter</a>: Rater limiter contract
│ └── <a href="./src/test/">test</a>: Unit tests in solidity
├── <a href="./foundry.toml">foundry.toml</a>: Foundry configuration
├── <a href="./hardhat.config.ts">hardhat.config.ts</a>: Hardhat configuration
├── <a href="./remappings.txt">remappings.txt</a>: Foundry dependency mappings
...
```
</pre>
## Dependencies
### Node.js
First install [`Node.js`](https://nodejs.org/en) and [`npm`](https://www.npmjs.com/).
Run the following command to install [`yarn`](https://classic.yarnpkg.com/en/):
```bash
npm install --global yarn
```
### Foundry
First run the command below to get foundryup, the Foundry toolchain installer:
Install `foundryup`, the Foundry toolchain installer:
```bash
curl -L https://foundry.paradigm.xyz | bash
```
If you do not want to use the redirect, feel free to manually download the foundryup installation script from [here](https://raw.githubusercontent.com/foundry-rs/foundry/master/foundryup/foundryup).
If you do not want to use the redirect, feel free to manually download the `foundryup` installation script from [here](https://raw.githubusercontent.com/foundry-rs/foundry/master/foundryup/foundryup).
Then, run `foundryup` in a new terminal session or after reloading your `PATH`.
Then, run `foundryup` in a new terminal session or after reloading `PATH`.
Other ways to install Foundry can be found [here](https://github.com/foundry-rs/foundry#installation).
### Hardhat
Run the following command to install [Hardhat](https://hardhat.org/) and other dependencies.
```
yarn install
```
## Build
- Run `git submodule update --init --recursive` to initialise git submodules.
- Run `git submodule update --init --recursive` to initialize git submodules.
- Run `yarn prettier:solidity` to run linting in fix mode, will auto-format all solidity codes.
- Run `yarn prettier` to run linting in fix mode, will auto-format all typescript codes.
- Run `yarn prepare` to install the precommit linting hook
- Run `yarn prepare` to install the precommit linting hook.
- Run `forge build` to compile contracts with foundry.
- Run `npx hardhat compile` to compile with hardhat.
- Run `forge test -vvv` to run foundry units tests. It will compile all contracts before running the unit tests.
- Run `npx hardhat test` to run integration tests. It may not compile all contracts before running, it's better to run `npx hardhat compile` first.
## TODO
- [ ] unit tests
- [ ] L1 Messenger
- [x] L1 Gateways
- [x] L1 Gateway Router
- [ ] L2 Messenger
- [x] L2 Gateways
- [x] L2 Gateway Router
- [x] ScrollStandardERC20Factory
- [x] Whitelist
- [ ] SimpleGasOracle
- [ ] integration tests
- [x] ERC20Gateway
- [x] GatewayRouter
- [ ] ZKRollup contracts
- [x] Gas Oracle contracts for cross chain message call
- [ ] ERC721/ERC115 interface design
- [ ] add proof verification codes
- [ ] security analysis

View File

@@ -1,33 +0,0 @@
# Cross Domain Messaging
Like other layer 2 protocol, Scroll allow dapps to communicate between layer 1 and layer 2. More specifically, dapps on layer 1 can trigger contract functions in layer 2, and vice versa.
## Message Between L1 and L2
The Scroll protocol implements two core contracts `L1ScrollMessenger` and `L2ScrollMessenger` to enable cross domain messaging. The only entry to send cross domain message is to call the following function:
```solidity
function sendMessage(
address _to,
bytes memory _message,
uint256 _gasLimit
) external payable
```
The function is attached in both messenger in layer 1 and layer 2. After that, the Sequencer will handle the rest part for you. We will explain the detailed workflow in the following docs.
### Send Message from L1 to L2
As described above, the first step is to call `L1ScrollMessenger.sendMessage` in layer 1. The `L1ScrollMessenger` contract will emit a `SentMessage` event, which will be notified by the Sequencer. The Sequencer will for the confirmation of the function call in layer 1. Normally, the Sequencer will wait for 10-20 blocks. After that, the Sequencer will initiate a transaction in layer 2, calling function `L2ScrollMessenger.relayMessage` and finally, the message is executed in layer 2.
The execution in layer 2 may be failed due to out of gas problem. In such case, one can call `L1ScrollMessenger.replayMessage` to replace the message with a larger gas limit. And the Sequencer will follow the steps and execute the message again in layer 2.
### Send Message from L2 to L1
Similar to sending message from L1 to L2, you should call `L2ScrollMessenger.sendMessage` first in layer 2. The `L2ScrollMessenger` contract will emit a `SentMessage` event, which will be notified by the Sequencer. Unlike above, the Sequencer will first batch submit layer 2 transactions (or block) to `ZKRollup` contract in layer 1. Then the Sequencer will wait the proof generated by prover and submit the proof to `ZKRollup` contract in layer 1 again. Finally, anyone can call `L1ScrollMessenger.relayMessageWithProof` with correct proof to execute the message in layer 1.
Currently, for the safety reason, we only allow privileged contracts to send cross domain messages. And only privileged accounts can call `L2ScrollMessenger.relayMessage`.
## Fee For Sending Message
to be discussed.

View File

@@ -1,22 +0,0 @@
# Overview
![](./assets/overview.png)
The above picture is the overview of the contract design. There are several components both in layer 1 and layer 2: L1/L2 Scroll Messenger, various L1/L2 Gateways and L1/L2 Gateway Router. Besides these, there is a Rollup component only in layer 1.
The followings are the detailed docs for each component (docs are generated automatically by `@primitivefi/hardhat-dodoc` plugin):
- [L1 Scroll Messenger](./apis/L1ScrollMessenger.md) and [L2 Scroll Messenger](./apis/L2ScrollMessenger.md): Main entry for sending and relaying cross domain message.
- [Rollup](./apis/ZKRollup.md)
- [L1 Gateway Router](./apis/L1GatewayRouter.md) and [L2 Gateway Router](./apis/L2GatewayRouter.md): Router contract for depositing/withdrawing Ethers and ERC20 tokens.
- L1/L2 Gateways:
- [L1 Standard ERC20 Gateway](./apis/L1StandardERC20Gateway.md) and [L2 Standard ERC20 Gateway](./apis/L2StandardERC20Gateway.md)
- [L1 WETH Gateway](./apis/L1WETHGateway.md) and [L2 WETH Gateway](./apis/L2WETHGateway.md)
- [L1 ERC721 Gateway](./apis/L1ERC721Gateway.md) and [L2 ERC721 Gateway](./apis/L2ERC721Gateway.md)
- [L1 ERC1155 Gateway](./apis/L1ERC1155Gateway.md) and [L2 ERC1155 Gateway](./apis/L2ERC1155Gateway.md)
- [ScrollStandardERC20Factory](./apis/ScrollStandardERC20Factory.md): The `ScrollStandardERC20` token factory used by `L2StandardERC20Gateway`.
There are two main applications: Token Bridge and Cross Domain Messaging. You can find the documentations in the links below:
- [Token Bridge](./TokenBridge.md): moving token from layer 1 to layer 2, or from layer 2 to layer 1.
- [Cross Domain Messaging](./CrossDomainMessaging.md): sending data to layer 2 from layer 1, or sending data to layer 2 from layer 1. Basically, it will help to trigger function call cross layer. The token bridge also use cross domain messaging to achieve its functionality.

View File

@@ -1,134 +0,0 @@
# Bridge Token Between Layer 1 and Layer 2
The Token Bridge of Scroll Protocol offers a way to move assets from layer 1 to layer 2 and back, including Ether, ERC20 token, ERC-721 token, ERC-1155 token, etc. The asset should be deposited and locked in layer 1 and then in exchange of the same amount of an equivalent token on layer 2. For example, if you deposit 1000 Ether in layer 1, you will get 1000 Ether in layer 2 for return. And if you withdraw 1000 Ether in layer 2, you will get 1000 Ether in layer 1 for return.
The Ether and ERC20 tokens can be deposited or withdrawn using one single contract `GatewayRouter` (`L1GatewayRouter` in layer 1 and `L2GatewayRouter` in layer 2). The ERC-721 tokens and ERC-1155 tokens can be deposited or withdrawn using the corresponding `ERC1155Gateway` and `ERC721Gateway` in layer 1 or layer 2 (They may be integrated into `GatewayRouter` in the future).
## Bridge Ether
To bridge Ether from layer 1 to layer 2, one can use `L1GatewayRouter.depositETH`. This will transfer ethers to the `L1ScrollMessenger` contract on the layer 1 and credits the same amount of ether to you in layer 2 at the specified address.
```solidity
function depositETH(uint256 _gasLimit) external payable;
function depositETH(address _to, uint256 _gasLimit) external payable;
```
In the layer 1, all deposited Ether will be locked in `L1ScrollMessenger` contract. It means your deposited Ether will firstly be transfered to `L1GatewayRouter` contract and then to `L1ScrollMessenger` contract.
To withdraw Ether from layer 2 to layer 1, one can use `L2GatewayRouter.withdrawETH`.
```solidity
function withdrawETH(uint256 _gasLimit) external payable;
function withdrawETH(address _to, uint256 _gasLimit) external payable;
```
In layer 2, the `L2ScrollMessenger` holds infinite amount of Ether at the beginning. All your withdrawn Ether will be transfered back to `L2ScrollMessenger`, just like the process in layer 1.
In addition, you can actually call `sendMessage` from the `L1ScrollMessenger` or `L2ScrollMessenger` contract to deposit or withdraw Ether. The `L1GatewayRouter.depositETH` and `L2GatewayRouter.withdrawETH` are just alias for `L1ScrollMessenger/L2ScrollMessenger.sendMessage`.
## Bridge ERC20 Tokens
We use the similar design as [Arbitrum protocol](https://developer.offchainlabs.com/docs/bridging_assets#bridging-erc20-tokens) do. Several gateway contracts are used to bridge different kinds of ERC20 tokens, such as Wrapped Ether, standard ERC20 tokens, etc.
We implement a `StandardERC20Gateway` to deposit and withdraw standard ERC20 tokens. The standard procedure to deposit ERC20 tokens is to call `L1GatewayRouter.depositERC20` in layer 1. The token will be locked in `L1StandardERC20Gateway` contract in layer 1. The the standard procedure to withdraw ERC20 tokens is to call `L2GatewayRouter.withdrawRC20` in layer 2 and the token will be burned in layer 2.
For many other non-standard ERC20 tokens, we provide a custom ERC20 gateway. Anyone can implement such gateway as long as it implements all required interfaces. We implement the Wrapped Ether gateway as an example. To deposit or withdraw Wrapped Ether, one should first unwrap it to Ether, then transfer the Ether to `ScrollMessenger` just like Ether bridging.
### Passing data when depositing ERC20 tokens
The Scroll protocol offer a way to call another contract after depositing the token in layer 2 by calling `L1GatewayRouter.depositERC20AndCall` in layer 1. The ERC20 token in layer 2 implements the [ERC 677 Standard](https://github.com/ethereum/EIPs/issues/677). By using `transferAndCall` function, we can transfer the token to corresponding recipient in layer 2 and then call the recipient with passed data.
```solidity
function depositERC20AndCall(
address _token,
address _to,
uint256 _amount,
bytes memory _data,
uint256 _gasLimit
) external;
```
Like Bridging Ether, all above functionality can be achieved by calling corresponding function in ERC20Gateway contract.
## Bridge ERC-721/ERC-1155 Tokens
The depositing/withdrawing ERC-721 or ERC-1155 tokens works very similar to ERC20 tokens. One can use the following function to deposit ERC-721/ERC-1155 tokens in layer 1.
```solidity
function depositERC1155(
address _token,
uint256 _tokenId,
uint256 _amount,
uint256 _gasLimit
) external;
function depositERC1155(
address _token,
address _to,
uint256 _tokenId,
uint256 _amount,
uint256 _gasLimit
) external;
function depositERC721(
address _token,
uint256 _tokenId,
uint256 _gasLimit
) external;
function depositERC721(
address _token,
address _to,
uint256 _tokenId,
uint256 _gasLimit
) external;
```
One can use the following function to withdraw ERC-721/ERC-1155 tokens in layer 2.
```solidity
function withdrawERC1155(
address _token,
uint256 _tokenId,
uint256 _amount,
uint256 _gasLimit
) external;
function withdrawERC1155(
address _token,
address _to,
uint256 _tokenId,
uint256 _amount,
uint256 _gasLimit
) external;
function withdrawERC721(
address _token,
uint256 _tokenId,
uint256 _gasLimit
) external;
function withdrawERC721(
address _token,
address _to,
uint256 _tokenId,
uint256 _gasLimit
) external;
```
To save the gas usage, we also provide a batch deposit/withdraw function, such as `batchDepositERC1155` and `batchDepositERC721`, by passing a list of token ids to the function.
## Drop Depositing/Withdrawing
Coming soon...
## Force Exit
Coming soon...

View File

@@ -104,45 +104,6 @@ Mapping from L2 message hash to sent status.
|---|---|---|
| _0 | bool | undefined |
### l1MessageFailedTimes
```solidity
function l1MessageFailedTimes(bytes32) external view returns (uint256)
```
Mapping from L1 message hash to the number of failure times.
#### Parameters
| Name | Type | Description |
|---|---|---|
| _0 | bytes32 | undefined |
#### Returns
| Name | Type | Description |
|---|---|---|
| _0 | uint256 | undefined |
### maxFailedExecutionTimes
```solidity
function maxFailedExecutionTimes() external view returns (uint256)
```
The maximum number of times each L1 message can fail on L2.
#### Returns
| Name | Type | Description |
|---|---|---|
| _0 | uint256 | undefined |
### messageQueue
```solidity
@@ -329,22 +290,6 @@ Update fee vault contract.
|---|---|---|
| _newFeeVault | address | The address of new fee vault contract. |
### updateMaxFailedExecutionTimes
```solidity
function updateMaxFailedExecutionTimes(uint256 _newMaxFailedExecutionTimes) external nonpayable
```
Update max failed execution times.
*This function can only called by contract owner.*
#### Parameters
| Name | Type | Description |
|---|---|---|
| _newMaxFailedExecutionTimes | uint256 | The new max failed execution times. |
### updateRateLimiter
```solidity

View File

@@ -0,0 +1,571 @@
# ScrollChain
> ScrollChain
This contract maintains data for the Scroll rollup.
## Methods
### commitBatch
```solidity
function commitBatch(uint8 _version, bytes _parentBatchHeader, bytes[] _chunks, bytes _skippedL1MessageBitmap) external nonpayable
```
Commit a batch of transactions on layer 1.
#### Parameters
| Name | Type | Description |
|---|---|---|
| _version | uint8 | undefined |
| _parentBatchHeader | bytes | undefined |
| _chunks | bytes[] | undefined |
| _skippedL1MessageBitmap | bytes | undefined |
### committedBatches
```solidity
function committedBatches(uint256) external view returns (bytes32)
```
Return the batch hash of a committed batch.
#### Parameters
| Name | Type | Description |
|---|---|---|
| _0 | uint256 | undefined |
#### Returns
| Name | Type | Description |
|---|---|---|
| _0 | bytes32 | undefined |
### finalizeBatchWithProof
```solidity
function finalizeBatchWithProof(bytes _batchHeader, bytes32 _prevStateRoot, bytes32 _postStateRoot, bytes32 _withdrawRoot, bytes _aggrProof) external nonpayable
```
Finalize a committed batch on layer 1.
#### Parameters
| Name | Type | Description |
|---|---|---|
| _batchHeader | bytes | undefined |
| _prevStateRoot | bytes32 | undefined |
| _postStateRoot | bytes32 | undefined |
| _withdrawRoot | bytes32 | undefined |
| _aggrProof | bytes | undefined |
### finalizedStateRoots
```solidity
function finalizedStateRoots(uint256) external view returns (bytes32)
```
Return the state root of a committed batch.
#### Parameters
| Name | Type | Description |
|---|---|---|
| _0 | uint256 | undefined |
#### Returns
| Name | Type | Description |
|---|---|---|
| _0 | bytes32 | undefined |
### importGenesisBatch
```solidity
function importGenesisBatch(bytes _batchHeader, bytes32 _stateRoot) external nonpayable
```
Import layer 2 genesis block
#### Parameters
| Name | Type | Description |
|---|---|---|
| _batchHeader | bytes | undefined |
| _stateRoot | bytes32 | undefined |
### initialize
```solidity
function initialize(address _messageQueue, address _verifier, uint256 _maxNumL2TxInChunk) external nonpayable
```
#### Parameters
| Name | Type | Description |
|---|---|---|
| _messageQueue | address | undefined |
| _verifier | address | undefined |
| _maxNumL2TxInChunk | uint256 | undefined |
### isBatchFinalized
```solidity
function isBatchFinalized(uint256 _batchIndex) external view returns (bool)
```
Return whether the batch is finalized by batch index.
#### Parameters
| Name | Type | Description |
|---|---|---|
| _batchIndex | uint256 | undefined |
#### Returns
| Name | Type | Description |
|---|---|---|
| _0 | bool | undefined |
### isProver
```solidity
function isProver(address) external view returns (bool)
```
Whether an account is a prover.
#### Parameters
| Name | Type | Description |
|---|---|---|
| _0 | address | undefined |
#### Returns
| Name | Type | Description |
|---|---|---|
| _0 | bool | undefined |
### isSequencer
```solidity
function isSequencer(address) external view returns (bool)
```
Whether an account is a sequencer.
#### Parameters
| Name | Type | Description |
|---|---|---|
| _0 | address | undefined |
#### Returns
| Name | Type | Description |
|---|---|---|
| _0 | bool | undefined |
### lastFinalizedBatchIndex
```solidity
function lastFinalizedBatchIndex() external view returns (uint256)
```
The latest finalized batch index.
#### Returns
| Name | Type | Description |
|---|---|---|
| _0 | uint256 | undefined |
### layer2ChainId
```solidity
function layer2ChainId() external view returns (uint64)
```
The chain id of the corresponding layer 2 chain.
#### Returns
| Name | Type | Description |
|---|---|---|
| _0 | uint64 | undefined |
### maxNumL2TxInChunk
```solidity
function maxNumL2TxInChunk() external view returns (uint256)
```
The maximum number of transactions allowed in each chunk.
#### Returns
| Name | Type | Description |
|---|---|---|
| _0 | uint256 | undefined |
### messageQueue
```solidity
function messageQueue() external view returns (address)
```
The address of L1MessageQueue.
#### Returns
| Name | Type | Description |
|---|---|---|
| _0 | address | undefined |
### owner
```solidity
function owner() external view returns (address)
```
*Returns the address of the current owner.*
#### Returns
| Name | Type | Description |
|---|---|---|
| _0 | address | undefined |
### renounceOwnership
```solidity
function renounceOwnership() external nonpayable
```
*Leaves the contract without owner. It will not be possible to call `onlyOwner` functions anymore. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby removing any functionality that is only available to the owner.*
### revertBatch
```solidity
function revertBatch(bytes _batchHeader, uint256 _count) external nonpayable
```
Revert a pending batch.
*one can only revert unfinalized batches.*
#### Parameters
| Name | Type | Description |
|---|---|---|
| _batchHeader | bytes | undefined |
| _count | uint256 | undefined |
### transferOwnership
```solidity
function transferOwnership(address newOwner) external nonpayable
```
*Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner.*
#### Parameters
| Name | Type | Description |
|---|---|---|
| newOwner | address | undefined |
### updateMaxNumL2TxInChunk
```solidity
function updateMaxNumL2TxInChunk(uint256 _maxNumL2TxInChunk) external nonpayable
```
Update the value of `maxNumL2TxInChunk`.
#### Parameters
| Name | Type | Description |
|---|---|---|
| _maxNumL2TxInChunk | uint256 | The new value of `maxNumL2TxInChunk`. |
### updateProver
```solidity
function updateProver(address _account, bool _status) external nonpayable
```
Update the status of prover.
*This function can only called by contract owner.*
#### Parameters
| Name | Type | Description |
|---|---|---|
| _account | address | The address of account to update. |
| _status | bool | The status of the account to update. |
### updateSequencer
```solidity
function updateSequencer(address _account, bool _status) external nonpayable
```
Update the status of sequencer.
*This function can only called by contract owner.*
#### Parameters
| Name | Type | Description |
|---|---|---|
| _account | address | The address of account to update. |
| _status | bool | The status of the account to update. |
### updateVerifier
```solidity
function updateVerifier(address _newVerifier) external nonpayable
```
Update the address verifier contract.
#### Parameters
| Name | Type | Description |
|---|---|---|
| _newVerifier | address | The address of new verifier contract. |
### verifier
```solidity
function verifier() external view returns (address)
```
The address of RollupVerifier.
#### Returns
| Name | Type | Description |
|---|---|---|
| _0 | address | undefined |
### withdrawRoots
```solidity
function withdrawRoots(uint256) external view returns (bytes32)
```
Return the message root of a committed batch.
#### Parameters
| Name | Type | Description |
|---|---|---|
| _0 | uint256 | undefined |
#### Returns
| Name | Type | Description |
|---|---|---|
| _0 | bytes32 | undefined |
## Events
### CommitBatch
```solidity
event CommitBatch(bytes32 indexed batchHash)
```
Emitted when a new batch is committed.
#### Parameters
| Name | Type | Description |
|---|---|---|
| batchHash `indexed` | bytes32 | undefined |
### FinalizeBatch
```solidity
event FinalizeBatch(bytes32 indexed batchHash, bytes32 stateRoot, bytes32 withdrawRoot)
```
Emitted when a batch is finalized.
#### Parameters
| Name | Type | Description |
|---|---|---|
| batchHash `indexed` | bytes32 | undefined |
| stateRoot | bytes32 | undefined |
| withdrawRoot | bytes32 | undefined |
### OwnershipTransferred
```solidity
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)
```
#### Parameters
| Name | Type | Description |
|---|---|---|
| previousOwner `indexed` | address | undefined |
| newOwner `indexed` | address | undefined |
### RevertBatch
```solidity
event RevertBatch(bytes32 indexed batchHash)
```
revert a pending batch.
#### Parameters
| Name | Type | Description |
|---|---|---|
| batchHash `indexed` | bytes32 | undefined |
### UpdateMaxNumL2TxInChunk
```solidity
event UpdateMaxNumL2TxInChunk(uint256 oldMaxNumL2TxInChunk, uint256 newMaxNumL2TxInChunk)
```
Emitted when the value of `maxNumL2TxInChunk` is updated.
#### Parameters
| Name | Type | Description |
|---|---|---|
| oldMaxNumL2TxInChunk | uint256 | The old value of `maxNumL2TxInChunk`. |
| newMaxNumL2TxInChunk | uint256 | The new value of `maxNumL2TxInChunk`. |
### UpdateProver
```solidity
event UpdateProver(address indexed account, bool status)
```
Emitted when owner updates the status of prover.
#### Parameters
| Name | Type | Description |
|---|---|---|
| account `indexed` | address | The address of account updated. |
| status | bool | The status of the account updated. |
### UpdateSequencer
```solidity
event UpdateSequencer(address indexed account, bool status)
```
Emitted when owner updates the status of sequencer.
#### Parameters
| Name | Type | Description |
|---|---|---|
| account `indexed` | address | The address of account updated. |
| status | bool | The status of the account updated. |
### UpdateVerifier
```solidity
event UpdateVerifier(address oldVerifier, address newVerifier)
```
Emitted when the address of rollup verifier is updated.
#### Parameters
| Name | Type | Description |
|---|---|---|
| oldVerifier | address | The address of old rollup verifier. |
| newVerifier | address | The address of new rollup verifier. |

Binary file not shown.

Before

Width:  |  Height:  |  Size: 684 KiB

View File

@@ -84,11 +84,14 @@ const config: HardhatUserConfig = {
etherscan: {
apiKey: process.env.ETHERSCAN_API_KEY,
},
mocha: {
timeout: 10000000,
},
dodoc: {
runOnCompile: true,
keepFileStructure: false,
include: [
"ZKRollup",
"ScrollChain",
"L1ScrollMessenger",
"L2ScrollMessenger",
"L1GatewayRouter",
@@ -115,8 +118,9 @@ const config: HardhatUserConfig = {
"IL1ERC1155Gateway",
"IL2ERC1155Gateway",
"IScrollStandardERC20Factory",
"IZKRollup",
"WrappedEther",
"IScrollChain",
"ScrollChainCommitmentVerifier",
"WETH9",
],
},
};

View File

@@ -1,5 +1,6 @@
/* eslint-disable node/no-unpublished-import */
/* eslint-disable node/no-missing-import */
import { concat } from "ethers/lib/utils";
import { constants } from "ethers";
import { ethers } from "hardhat";
import { ScrollChain, L1MessageQueue } from "../typechain";
@@ -11,22 +12,27 @@ describe("ScrollChain", async () => {
beforeEach(async () => {
const [deployer] = await ethers.getSigners();
const ProxyAdmin = await ethers.getContractFactory("ProxyAdmin", deployer);
const admin = await ProxyAdmin.deploy();
await admin.deployed();
const TransparentUpgradeableProxy = await ethers.getContractFactory("TransparentUpgradeableProxy", deployer);
const L1MessageQueue = await ethers.getContractFactory("L1MessageQueue", deployer);
queue = await L1MessageQueue.deploy();
await queue.deployed();
const queueImpl = await L1MessageQueue.deploy();
await queueImpl.deployed();
const queueProxy = await TransparentUpgradeableProxy.deploy(queueImpl.address, admin.address, "0x");
await queueProxy.deployed();
queue = await ethers.getContractAt("L1MessageQueue", queueProxy.address, deployer);
const RollupVerifier = await ethers.getContractFactory("RollupVerifier", deployer);
const verifier = await RollupVerifier.deploy();
await verifier.deployed();
const ScrollChain = await ethers.getContractFactory("ScrollChain", deployer);
const chainImpl = await ScrollChain.deploy(0);
await chainImpl.deployed();
const chainProxy = await TransparentUpgradeableProxy.deploy(chainImpl.address, admin.address, "0x");
await chainProxy.deployed();
chain = await ethers.getContractAt("ScrollChain", chainProxy.address, deployer);
const ScrollChain = await ethers.getContractFactory("ScrollChain", {
signer: deployer,
libraries: { RollupVerifier: verifier.address },
});
chain = await ScrollChain.deploy(0);
await chain.deployed();
await chain.initialize(queue.address, constants.AddressZero, 44);
await chain.initialize(queue.address, constants.AddressZero, 100);
await chain.addSequencer(deployer.address);
await queue.initialize(
constants.AddressZero,
@@ -38,79 +44,54 @@ describe("ScrollChain", async () => {
});
// @note skip this benchmark tests
/*
it("should succeed", async () => {
await chain.importGenesisBatch({
blocks: [
{
blockHash: "0x92826bd3aad2ef70d8061dc4e25150b305d1233d9cd7579433a77d6eb01dae1c",
parentHash: constants.HashZero,
blockNumber: 0,
timestamp: 1639724192,
baseFee: 1000000000,
gasLimit: 940000000,
numTransactions: 0,
numL1Messages: 0,
},
],
prevStateRoot: constants.HashZero,
newStateRoot: "0x1b186a7a90ec3b41a2417062fe44dce8ce82ae76bfbb09eae786a4f1be1895f5",
withdrawTrieRoot: constants.HashZero,
batchIndex: 0,
parentBatchHash: constants.HashZero,
l2Transactions: [],
});
const parentBatchHash = await chain.lastFinalizedBatchHash();
it.skip("should succeed", async () => {
const batchHeader0 = new Uint8Array(89);
batchHeader0[25] = 1;
await chain.importGenesisBatch(batchHeader0, "0x0000000000000000000000000000000000000000000000000000000000000001");
const parentBatchHash = await chain.committedBatches(0);
console.log("genesis batch hash:", parentBatchHash);
console.log(`ChunkPerBatch`, `BlockPerChunk`, `TxPerBlock`, `BytesPerTx`, `TotalBytes`, `EstimateGas`);
for (let numChunks = 3; numChunks <= 6; ++numChunks) {
for (let numBlocks = 1; numBlocks <= 5; ++numBlocks) {
for (let numTx = 20; numTx <= Math.min(30, 100 / numBlocks); ++numTx) {
for (let txLength = 800; txLength <= 1000; txLength += 100) {
const txs: Array<Uint8Array> = [];
for (let i = 0; i < numTx; i++) {
const tx = new Uint8Array(4 + txLength);
let offset = 3;
for (let x = txLength; x > 0; x = Math.floor(x / 256)) {
tx[offset] = x % 256;
offset -= 1;
}
tx.fill(1, 4);
txs.push(tx);
}
const chunk = new Uint8Array(1 + 60 * numBlocks);
chunk[0] = numBlocks;
for (let i = 0; i < numBlocks; i++) {
chunk[1 + i * 60 + 57] = numTx;
}
const chunks: Array<Uint8Array> = [];
for (let i = 0; i < numChunks; i++) {
const txsInChunk: Array<Uint8Array> = [];
for (let j = 0; j < numBlocks; j++) {
txsInChunk.push(concat(txs));
}
chunks.push(concat([chunk, concat(txsInChunk)]));
}
for (let numTx = 1; numTx <= 25; ++numTx) {
for (let txLength = 100; txLength <= 1000; txLength += 100) {
const txs: Array<Uint8Array> = [];
for (let i = 0; i < numTx; i++) {
const tx = new Uint8Array(4 + txLength);
let offset = 3;
for (let x = txLength; x > 0; x = Math.floor(x / 256)) {
tx[offset] = x % 256;
offset -= 1;
const estimateGas = await chain.estimateGas.commitBatch(0, batchHeader0, chunks, "0x");
console.log(
`${numChunks}`,
`${numBlocks}`,
`${numTx}`,
`${txLength}`,
`${numChunks * numBlocks * numTx * (txLength + 1)}`,
`${estimateGas.toString()}`
);
}
tx.fill(1, 4);
txs.push(tx);
}
const batch = {
blocks: [
{
blockHash: "0xb5baa665b2664c3bfed7eb46e00ebc110ecf2ebd257854a9bf2b9dbc9b2c08f6",
parentHash: "0x92826bd3aad2ef70d8061dc4e25150b305d1233d9cd7579433a77d6eb01dae1c",
blockNumber: 1,
timestamp: numTx * 100000 + txLength,
baseFee: 0,
gasLimit: 0,
numTransactions: 0,
numL1Messages: 0,
},
],
prevStateRoot: "0x1b186a7a90ec3b41a2417062fe44dce8ce82ae76bfbb09eae786a4f1be1895f5",
newStateRoot: "0xb5baa665b2664c3bfed7eb46e00ebc110ecf2ebd257854a9bf2b9dbc9b2c08f6",
withdrawTrieRoot: "0xb5baa665b2664c3bfed7eb46e00ebc110ecf2ebd257854a9bf2b9dbc9b2c08f6",
batchIndex: 1,
parentBatchHash: parentBatchHash,
l2Transactions: concat(txs),
};
const estimateGas = await chain.estimateGas.commitBatch(batch);
const tx = await chain.commitBatch(batch, { gasLimit: estimateGas.mul(12).div(10) });
const receipt = await tx.wait();
console.log(
"Commit batch with l2TransactionsBytes:",
numTx * (txLength + 4),
"gasLimit:",
tx.gasLimit.toString(),
"estimateGas:",
estimateGas.toString(),
"gasUsed:",
receipt.gasUsed.toString()
);
}
}
});
*/
});

View File

@@ -12,6 +12,7 @@ import {L1ScrollMessenger} from "../../src/L1/L1ScrollMessenger.sol";
import {L1StandardERC20Gateway} from "../../src/L1/gateways/L1StandardERC20Gateway.sol";
import {L1WETHGateway} from "../../src/L1/gateways/L1WETHGateway.sol";
import {L1DAIGateway} from "../../src/L1/gateways/L1DAIGateway.sol";
import {MultipleVersionRollupVerifier} from "../../src/L1/rollup/MultipleVersionRollupVerifier.sol";
import {ScrollChain} from "../../src/L1/rollup/ScrollChain.sol";
import {L1MessageQueue} from "../../src/L1/rollup/L1MessageQueue.sol";
import {L2GasPriceOracle} from "../../src/L1/rollup/L2GasPriceOracle.sol";
@@ -71,6 +72,9 @@ contract InitializeL1BridgeContracts is Script {
ScrollChain(L1_SCROLL_CHAIN_PROXY_ADDR).addSequencer(L1_COMMIT_SENDER_ADDRESS);
ScrollChain(L1_SCROLL_CHAIN_PROXY_ADDR).addProver(L1_FINALIZE_SENDER_ADDRESS);
// initialize MultipleVersionRollupVerifier
MultipleVersionRollupVerifier(L1_MULTIPLE_VERSION_ROLLUP_VERIFIER_ADDR).initialize(L1_SCROLL_CHAIN_PROXY_ADDR);
// initialize L2GasPriceOracle
L2GasPriceOracle(L2_GAS_PRICE_ORACLE_PROXY_ADDR).initialize(
21000, // _txGas

View File

@@ -25,18 +25,18 @@ async function main() {
const L2StandardERC20Impl = process.env.L2_SCROLL_STANDARD_ERC20_ADDR!;
const L2StandardERC20FactoryAddress = process.env.L2_SCROLL_STANDARD_ERC20_FACTORY_ADDR!;
// if ((await L1StandardERC20Gateway.counterpart()) === constants.AddressZero) {
const tx = await L1StandardERC20Gateway.initialize(
L2StandardERC20GatewayAddress,
L1GatewayRouterAddress,
L1ScrollMessengerAddress,
L2StandardERC20Impl,
L2StandardERC20FactoryAddress
);
console.log("initialize L1StandardERC20Gateway, hash:", tx.hash);
const receipt = await tx.wait();
console.log(`✅ Done, gas used: ${receipt.gasUsed}`);
// }
if ((await L1StandardERC20Gateway.counterpart()) === constants.AddressZero) {
const tx = await L1StandardERC20Gateway.initialize(
L2StandardERC20GatewayAddress,
L1GatewayRouterAddress,
L1ScrollMessengerAddress,
L2StandardERC20Impl,
L2StandardERC20FactoryAddress
);
console.log("initialize L1StandardERC20Gateway, hash:", tx.hash);
const receipt = await tx.wait();
console.log(`✅ Done, gas used: ${receipt.gasUsed}`);
}
}
// We recommend this pattern to be able to use async/await everywhere

View File

@@ -23,16 +23,16 @@ async function main() {
const L1ScrollMessengerAddress = addressFile.get("L1ScrollMessenger.proxy");
const L2GatewayRouterAddress = process.env.L2_GATEWAY_ROUTER_PROXY_ADDR!;
// if ((await L1GatewayRouter.counterpart()) === constants.AddressZero) {
const tx = await L1GatewayRouter.initialize(
L1StandardERC20GatewayAddress,
L2GatewayRouterAddress,
L1ScrollMessengerAddress
);
console.log("initialize L1StandardERC20Gateway, hash:", tx.hash);
const receipt = await tx.wait();
console.log(`✅ Done, gas used: ${receipt.gasUsed}`);
// }
if ((await L1GatewayRouter.counterpart()) === constants.AddressZero) {
const tx = await L1GatewayRouter.initialize(
L1StandardERC20GatewayAddress,
L2GatewayRouterAddress,
L1ScrollMessengerAddress
);
console.log("initialize L1StandardERC20Gateway, hash:", tx.hash);
const receipt = await tx.wait();
console.log(`✅ Done, gas used: ${receipt.gasUsed}`);
}
}
// We recommend this pattern to be able to use async/await everywhere

View File

@@ -21,12 +21,12 @@ async function main() {
const ZKRollupAddress = addressFile.get("ZKRollup.proxy");
// if ((await L1ScrollMessenger.rollup()) === constants.AddressZero) {
const tx = await L1ScrollMessenger.initialize(ZKRollupAddress);
console.log("initialize L1StandardERC20Gateway, hash:", tx.hash);
const receipt = await tx.wait();
console.log(`✅ Done, gas used: ${receipt.gasUsed}`);
// }
if ((await L1ScrollMessenger.rollup()) === constants.AddressZero) {
const tx = await L1ScrollMessenger.initialize(ZKRollupAddress);
console.log("initialize L1StandardERC20Gateway, hash:", tx.hash);
const receipt = await tx.wait();
console.log(`✅ Done, gas used: ${receipt.gasUsed}`);
}
}
// We recommend this pattern to be able to use async/await everywhere

View File

@@ -24,17 +24,17 @@ async function main() {
const L2StandardERC20FactoryAddress = addressFile.get("ScrollStandardERC20Factory");
const L1StandardERC20GatewayAddress = process.env.L1_STANDARD_ERC20_GATEWAY_PROXY_ADDR!;
// if ((await L2StandardERC20Gateway.counterpart()) === constants.AddressZero) {
const tx = await L2StandardERC20Gateway.initialize(
L1StandardERC20GatewayAddress,
L2GatewayRouterAddress,
L2ScrollMessengerAddress,
L2StandardERC20FactoryAddress
);
console.log("initialize L2StandardERC20Gateway, hash:", tx.hash);
const receipt = await tx.wait();
console.log(`✅ Done, gas used: ${receipt.gasUsed}`);
// }
if ((await L2StandardERC20Gateway.counterpart()) === constants.AddressZero) {
const tx = await L2StandardERC20Gateway.initialize(
L1StandardERC20GatewayAddress,
L2GatewayRouterAddress,
L2ScrollMessengerAddress,
L2StandardERC20FactoryAddress
);
console.log("initialize L2StandardERC20Gateway, hash:", tx.hash);
const receipt = await tx.wait();
console.log(`✅ Done, gas used: ${receipt.gasUsed}`);
}
}
// We recommend this pattern to be able to use async/await everywhere

View File

@@ -23,16 +23,16 @@ async function main() {
const L2ScrollMessengerAddress = addressFile.get("L2ScrollMessenger");
const L1GatewayRouterAddress = process.env.L1_GATEWAY_ROUTER_PROXY_ADDR!;
// if ((await L2GatewayRouter.counterpart()) === constants.AddressZero) {
const tx = await L2GatewayRouter.initialize(
L2StandardERC20GatewayAddress,
L1GatewayRouterAddress,
L2ScrollMessengerAddress
);
console.log("initialize L1StandardERC20Gateway, hash:", tx.hash);
const receipt = await tx.wait();
console.log(`✅ Done, gas used: ${receipt.gasUsed}`);
// }
if ((await L2GatewayRouter.counterpart()) === constants.AddressZero) {
const tx = await L2GatewayRouter.initialize(
L2StandardERC20GatewayAddress,
L1GatewayRouterAddress,
L2ScrollMessengerAddress
);
console.log("initialize L1StandardERC20Gateway, hash:", tx.hash);
const receipt = await tx.wait();
console.log(`✅ Done, gas used: ${receipt.gasUsed}`);
}
}
// We recommend this pattern to be able to use async/await everywhere

View File

@@ -98,6 +98,9 @@ contract L1ScrollMessenger is ScrollMessengerBase, IL1ScrollMessenger {
rollup = _rollup;
messageQueue = _messageQueue;
maxReplayTimes = 3;
emit UpdateMaxReplayTimes(0, 3);
}
/*****************************

View File

@@ -14,8 +14,6 @@ import {ScrollConstants} from "../../libraries/constants/ScrollConstants.sol";
import {ScrollGatewayBase} from "../../libraries/gateway/ScrollGatewayBase.sol";
import {IMessageDropCallback} from "../../libraries/callbacks/IMessageDropCallback.sol";
// solhint-disable no-empty-blocks
abstract contract L1ERC20Gateway is IL1ERC20Gateway, IMessageDropCallback, ScrollGatewayBase {
using SafeERC20Upgradeable for IERC20Upgradeable;
@@ -68,7 +66,7 @@ abstract contract L1ERC20Gateway is IL1ERC20Gateway, IMessageDropCallback, Scrol
address _to,
uint256 _amount,
bytes calldata _data
) external payable override onlyCallByCounterpart nonReentrant {
) external payable virtual override onlyCallByCounterpart nonReentrant {
_beforeFinalizeWithdrawERC20(_l1Token, _l2Token, _from, _to, _amount, _data);
// @note can possible trigger reentrant call to this contract or messenger,

View File

@@ -52,7 +52,7 @@ contract L1ETHGateway is ScrollGatewayBase, IL1ETHGateway, IMessageDropCallback
address _to,
uint256 _amount,
uint256 _gasLimit
) public payable override {
) external payable override {
_deposit(_to, _amount, new bytes(0), _gasLimit);
}

View File

@@ -2,10 +2,176 @@
pragma solidity =0.8.16;
import {L1CustomERC20Gateway} from "../L1CustomERC20Gateway.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
// solhint-disable no-empty-blocks
import {IFiatToken} from "../../../interfaces/IFiatToken.sol";
import {IUSDCBurnableSourceBridge} from "../../../interfaces/IUSDCBurnableSourceBridge.sol";
import {IL2ERC20Gateway} from "../../../L2/gateways/IL2ERC20Gateway.sol";
import {IL1ScrollMessenger} from "../../IL1ScrollMessenger.sol";
import {IL1ERC20Gateway} from "../IL1ERC20Gateway.sol";
contract L1USDCGateway is L1CustomERC20Gateway {
import {ScrollGatewayBase} from "../../../libraries/gateway/ScrollGatewayBase.sol";
import {L1ERC20Gateway} from "../L1ERC20Gateway.sol";
/// @title L1USDCGateway
/// @notice The `L1USDCGateway` contract is used to deposit `USDC` token in layer 1 and
/// finalize withdraw `USDC` from layer 2, before USDC become native in layer 2.
contract L1USDCGateway is L1ERC20Gateway, IUSDCBurnableSourceBridge {
/*************
* Constants *
*************/
/// @notice The address of L1 USDC address.
// solhint-disable-next-line var-name-mixedcase
address public immutable l1USDC;
/// @notice The address of L2 USDC address.
address public immutable l2USDC;
/*************
* Variables *
*************/
/// @notice The address of caller from Circle.
address public circleCaller;
/// @notice The flag indicates whether USDC deposit is paused.
bool public depositPaused;
/// @notice The flag indicates whether USDC withdrawal is paused.
/// @dev This is not necessary to be set `true` since we will set `L2USDCGateway.withdrawPaused` first.
/// This is kept just in case and will be set after all pending messages are relayed.
bool public withdrawPaused;
/// @notice The total amount of bridged USDC in this contract.
/// @dev Only deposited USDC will count. Accidentally transferred USDC will be ignored.
uint256 public totalBridgedUSDC;
/***************
* Constructor *
***************/
constructor(address _l1USDC, address _l2USDC) {
_disableInitializers();
l1USDC = _l1USDC;
l2USDC = _l2USDC;
}
/// @notice Initialize the storage of L1WETHGateway.
/// @param _counterpart The address of L2ETHGateway in L2.
/// @param _router The address of L1GatewayRouter.
/// @param _messenger The address of L1ScrollMessenger.
function initialize(
address _counterpart,
address _router,
address _messenger
) external initializer {
require(_router != address(0), "zero router address");
ScrollGatewayBase._initialize(_counterpart, _router, _messenger);
}
/*************************
* Public View Functions *
*************************/
/// @inheritdoc IL1ERC20Gateway
function getL2ERC20Address(address) public view override returns (address) {
return l2USDC;
}
/*******************************
* Public Restricted Functions *
*******************************/
/// @inheritdoc IUSDCBurnableSourceBridge
function burnAllLockedUSDC() external override {
require(msg.sender == circleCaller, "only circle caller");
// @note Only bridged USDC will be burned. We may refund the rest if possible.
uint256 _balance = totalBridgedUSDC;
totalBridgedUSDC = 0;
IFiatToken(l1USDC).burn(_balance);
}
/// @notice Update the Circle EOA address.
/// @param _caller The address to update.
function updateCircleCaller(address _caller) external onlyOwner {
circleCaller = _caller;
}
/// @notice Change the deposit pause status of this contract.
/// @param _paused The new status, `true` means paused and `false` means not paused.
function pauseDeposit(bool _paused) external onlyOwner {
depositPaused = _paused;
}
/// @notice Change the withdraw pause status of this contract.
/// @param _paused The new status, `true` means paused and `false` means not paused.
function pauseWithdraw(bool _paused) external onlyOwner {
withdrawPaused = _paused;
}
/**********************
* Internal Functions *
**********************/
/// @inheritdoc L1ERC20Gateway
function _beforeFinalizeWithdrawERC20(
address _l1Token,
address _l2Token,
address,
address,
uint256 _amount,
bytes calldata
) internal virtual override {
require(msg.value == 0, "nonzero msg.value");
require(_l1Token == l1USDC, "l1 token not USDC");
require(_l2Token == l2USDC, "l2 token not USDC");
require(!withdrawPaused, "withdraw paused");
totalBridgedUSDC -= _amount;
}
/// @inheritdoc L1ERC20Gateway
function _beforeDropMessage(
address,
address,
uint256 _amount
) internal virtual override {
require(msg.value == 0, "nonzero msg.value");
totalBridgedUSDC -= _amount;
}
/// @inheritdoc L1ERC20Gateway
function _deposit(
address _token,
address _to,
uint256 _amount,
bytes memory _data,
uint256 _gasLimit
) internal virtual override nonReentrant {
require(_amount > 0, "deposit zero amount");
require(_token == l1USDC, "only USDC is allowed");
require(!depositPaused, "deposit paused");
// 1. Transfer token into this contract.
address _from;
(_from, _amount, _data) = _transferERC20In(_token, _amount, _data);
require(_data.length == 0, "call is not allowed");
totalBridgedUSDC += _amount;
// 2. Generate message passed to L2USDCGateway.
bytes memory _message = abi.encodeCall(
IL2ERC20Gateway.finalizeDepositERC20,
(_token, l2USDC, _from, _to, _amount, _data)
);
// 3. Send message to L1ScrollMessenger.
IL1ScrollMessenger(messenger).sendMessage{value: msg.value}(counterpart, 0, _message, _gasLimit, _from);
emit DepositERC20(_token, l2USDC, _from, _to, _amount, _data);
}
}

View File

@@ -0,0 +1,180 @@
// SPDX-License-Identifier: MIT
pragma solidity =0.8.16;
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {ITokenMessenger} from "../../../interfaces/ITokenMessenger.sol";
import {IL2ERC20Gateway} from "../../../L2/gateways/IL2ERC20Gateway.sol";
import {IL1ScrollMessenger} from "../../IL1ScrollMessenger.sol";
import {IL1ERC20Gateway} from "../IL1ERC20Gateway.sol";
import {CCTPGatewayBase} from "../../../libraries/gateway/CCTPGatewayBase.sol";
import {ScrollGatewayBase} from "../../../libraries/gateway/ScrollGatewayBase.sol";
import {L1ERC20Gateway} from "../L1ERC20Gateway.sol";
/// @title L1USDCGatewayCCTP
/// @notice The `L1USDCGateway` contract is used to deposit `USDC` token in layer 1 and
/// finalize withdraw `USDC` from layer 2, after USDC become native in layer 2.
contract L1USDCGatewayCCTP is CCTPGatewayBase, L1ERC20Gateway {
/***************
* Constructor *
***************/
constructor(
address _l1USDC,
address _l2USDC,
uint32 _destinationDomain
) CCTPGatewayBase(_l1USDC, _l2USDC, _destinationDomain) {
_disableInitializers();
}
/// @notice Initialize the storage of L1USDCGatewayCCTP.
/// @param _counterpart The address of L2USDCGatewayCCTP in L2.
/// @param _router The address of L1GatewayRouter.
/// @param _messenger The address of L1ScrollMessenger.
/// @param _cctpMessenger The address of TokenMessenger in local domain.
/// @param _cctpTransmitter The address of MessageTransmitter in local domain.
function initialize(
address _counterpart,
address _router,
address _messenger,
address _cctpMessenger,
address _cctpTransmitter
) external initializer {
require(_router != address(0), "zero router address");
ScrollGatewayBase._initialize(_counterpart, _router, _messenger);
CCTPGatewayBase._initialize(_cctpMessenger, _cctpTransmitter);
}
/*************************
* Public View Functions *
*************************/
/// @inheritdoc IL1ERC20Gateway
function getL2ERC20Address(address) public view override returns (address) {
return l2USDC;
}
/*****************************
* Public Mutating Functions *
*****************************/
/// @notice Relay cross chain message and claim USDC that has been cross chained.
/// @dev The `_scrollMessage` is actually encoded calldata for `L1ScrollMessenger.relayMessageWithProof`.
///
/// @dev This helper function is aimed to claim USDC in single transaction.
/// Normally, an user should call `L1ScrollMessenger.relayMessageWithProof` first,
/// then `L1USDCGatewayCCTP.claimUSDC`.
///
/// @param _nonce The nonce of the message from CCTP.
/// @param _cctpMessage The message passed to MessageTransmitter contract in CCTP.
/// @param _cctpSignature The message passed to MessageTransmitter contract in CCTP.
/// @param _scrollMessage The message passed to L1ScrollMessenger contract.
function relayAndClaimUSDC(
uint256 _nonce,
bytes calldata _cctpMessage,
bytes calldata _cctpSignature,
bytes calldata _scrollMessage
) external {
require(status[_nonce] == CCTPMessageStatus.None, "message relayed");
// call messenger to set `status[_nonce]` to `CCTPMessageStatus.Pending`.
(bool _success, ) = messenger.call(_scrollMessage);
require(_success, "call messenger failed");
claimUSDC(_nonce, _cctpMessage, _cctpSignature);
}
/// @inheritdoc IL1ERC20Gateway
/// @dev The function will not mint the USDC, users need to call `claimUSDC` after this function is done.
function finalizeWithdrawERC20(
address _l1Token,
address _l2Token,
address _from,
address _to,
uint256 _amount,
bytes memory _data
) external payable override onlyCallByCounterpart {
require(msg.value == 0, "nonzero msg.value");
require(_l1Token == l1USDC, "l1 token not USDC");
require(_l2Token == l2USDC, "l2 token not USDC");
uint256 _nonce;
(_nonce, _data) = abi.decode(_data, (uint256, bytes));
require(status[_nonce] == CCTPMessageStatus.None, "message relayed");
status[_nonce] = CCTPMessageStatus.Pending;
emit FinalizeWithdrawERC20(_l1Token, _l2Token, _from, _to, _amount, _data);
}
/*******************************
* Public Restricted Functions *
*******************************/
/// @notice Update the CCTP contract addresses.
/// @param _messenger The address of TokenMessenger in local domain.
/// @param _transmitter The address of MessageTransmitter in local domain.
function updateCCTPContracts(address _messenger, address _transmitter) external onlyOwner {
cctpMessenger = _messenger;
cctpTransmitter = _transmitter;
}
/**********************
* Internal Functions *
**********************/
/// @inheritdoc L1ERC20Gateway
function _beforeFinalizeWithdrawERC20(
address,
address,
address,
address,
uint256,
bytes calldata
) internal virtual override {}
/// @inheritdoc L1ERC20Gateway
function _beforeDropMessage(
address,
address,
uint256
) internal virtual override {
require(msg.value == 0, "nonzero msg.value");
}
/// @inheritdoc L1ERC20Gateway
function _deposit(
address _token,
address _to,
uint256 _amount,
bytes memory _data,
uint256 _gasLimit
) internal virtual override nonReentrant {
require(_amount > 0, "deposit zero amount");
require(_token == l1USDC, "only USDC is allowed");
// 1. Extract real sender if this call is from L1GatewayRouter.
address _from;
(_from, _amount, _data) = _transferERC20In(_token, _amount, _data);
// 2. Burn token through CCTP TokenMessenger
uint256 _nonce = ITokenMessenger(cctpMessenger).depositForBurnWithCaller(
_amount,
destinationDomain,
bytes32(uint256(uint160(_to))),
address(this),
bytes32(uint256(uint160(counterpart)))
);
// 3. Generate message passed to L2USDCGatewayCCTP.
bytes memory _message = abi.encodeCall(
IL2ERC20Gateway.finalizeDepositERC20,
(_token, l2USDC, _from, _to, _amount, abi.encode(_nonce, _data))
);
// 4. Send message to L1ScrollMessenger.
IL1ScrollMessenger(messenger).sendMessage{value: msg.value}(counterpart, 0, _message, _gasLimit);
emit DepositERC20(_token, l2USDC, _from, _to, _amount, _data);
}
}

View File

@@ -28,6 +28,9 @@ interface IScrollChain {
* Public View Functions *
*************************/
/// @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.
function committedBatches(uint256 batchIndex) external view returns (bytes32);

View File

@@ -4,6 +4,7 @@ pragma solidity =0.8.16;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IScrollChain} from "./IScrollChain.sol";
import {IRollupVerifier} from "../../libraries/verifier/IRollupVerifier.sol";
import {IZkEvmVerifier} from "../../libraries/verifier/IZkEvmVerifier.sol";
@@ -38,6 +39,9 @@ contract MultipleVersionRollupVerifier is IRollupVerifier, Ownable {
/// @notice The lastest used zkevm verifier.
Verifier public latestVerifier;
/// @notice The address of ScrollChain contract.
address public scrollChain;
/***************
* Constructor *
***************/
@@ -48,6 +52,12 @@ contract MultipleVersionRollupVerifier is IRollupVerifier, Ownable {
latestVerifier.verifier = _verifier;
}
function initialize(address _scrollChain) external onlyOwner {
require(scrollChain == address(0), "initialized");
scrollChain = _scrollChain;
}
/*************************
* Public View Functions *
*************************/
@@ -101,6 +111,8 @@ contract MultipleVersionRollupVerifier is IRollupVerifier, Ownable {
/// @param _startBatchIndex The start batch index when the verifier will be used.
/// @param _verifier The address of new verifier.
function updateVerifier(uint64 _startBatchIndex, address _verifier) external onlyOwner {
require(_startBatchIndex > IScrollChain(scrollChain).lastFinalizedBatchIndex(), "start batch index finalized");
Verifier memory _latestVerifier = latestVerifier;
require(_startBatchIndex >= _latestVerifier.startBatchIndex, "start batch index too small");
require(_verifier != address(0), "zero verifier address");

View File

@@ -36,10 +36,10 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain {
/// @param newVerifier The address of new rollup verifier.
event UpdateVerifier(address indexed oldVerifier, address indexed newVerifier);
/// @notice Emitted when the value of `maxNumL2TxInChunk` is updated.
/// @param oldMaxNumL2TxInChunk The old value of `maxNumL2TxInChunk`.
/// @param newMaxNumL2TxInChunk The new value of `maxNumL2TxInChunk`.
event UpdateMaxNumL2TxInChunk(uint256 oldMaxNumL2TxInChunk, uint256 newMaxNumL2TxInChunk);
/// @notice Emitted when the value of `maxNumTxInChunk` is updated.
/// @param oldMaxNumTxInChunk The old value of `maxNumTxInChunk`.
/// @param newMaxNumTxInChunk The new value of `maxNumTxInChunk`.
event UpdateMaxNumTxInChunk(uint256 oldMaxNumTxInChunk, uint256 newMaxNumTxInChunk);
/*************
* Constants *
@@ -53,7 +53,7 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain {
*************/
/// @notice The maximum number of transactions allowed in each chunk.
uint256 public maxNumL2TxInChunk;
uint256 public maxNumTxInChunk;
/// @notice The address of L1MessageQueue.
address public messageQueue;
@@ -67,8 +67,8 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain {
/// @notice Whether an account is a prover.
mapping(address => bool) public isProver;
/// @notice The latest finalized batch index.
uint256 public lastFinalizedBatchIndex;
/// @inheritdoc IScrollChain
uint256 public override lastFinalizedBatchIndex;
/// @inheritdoc IScrollChain
mapping(uint256 => bytes32) public override committedBatches;
@@ -107,16 +107,16 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain {
function initialize(
address _messageQueue,
address _verifier,
uint256 _maxNumL2TxInChunk
uint256 _maxNumTxInChunk
) public initializer {
OwnableUpgradeable.__Ownable_init();
messageQueue = _messageQueue;
verifier = _verifier;
maxNumL2TxInChunk = _maxNumL2TxInChunk;
maxNumTxInChunk = _maxNumTxInChunk;
emit UpdateVerifier(address(0), _verifier);
emit UpdateMaxNumL2TxInChunk(0, _maxNumL2TxInChunk);
emit UpdateMaxNumTxInChunk(0, _maxNumTxInChunk);
}
/*************************
@@ -272,9 +272,10 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain {
require(_batchIndex > lastFinalizedBatchIndex, "can only revert unfinalized batch");
while (_count > 0) {
committedBatches[_batchIndex] = bytes32(0);
emit RevertBatch(_batchIndex, _batchHash);
committedBatches[_batchIndex] = bytes32(0);
unchecked {
_batchIndex += 1;
_count -= 1;
@@ -397,13 +398,13 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain {
emit UpdateVerifier(_oldVerifier, _newVerifier);
}
/// @notice Update the value of `maxNumL2TxInChunk`.
/// @param _maxNumL2TxInChunk The new value of `maxNumL2TxInChunk`.
function updateMaxNumL2TxInChunk(uint256 _maxNumL2TxInChunk) external onlyOwner {
uint256 _oldMaxNumL2TxInChunk = maxNumL2TxInChunk;
maxNumL2TxInChunk = _maxNumL2TxInChunk;
/// @notice Update the value of `maxNumTxInChunk`.
/// @param _maxNumTxInChunk The new value of `maxNumTxInChunk`.
function updateMaxNumTxInChunk(uint256 _maxNumTxInChunk) external onlyOwner {
uint256 _oldMaxNumTxInChunk = maxNumTxInChunk;
maxNumTxInChunk = _maxNumTxInChunk;
emit UpdateMaxNumL2TxInChunk(_oldMaxNumL2TxInChunk, _maxNumL2TxInChunk);
emit UpdateMaxNumTxInChunk(_oldMaxNumTxInChunk, _maxNumTxInChunk);
}
/// @notice Pause the contract
@@ -461,19 +462,26 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain {
uint256 _numBlocks = ChunkCodec.validateChunkLength(chunkPtr, _chunk.length);
// concatenate block contexts
uint256 _totalTransactionsInChunk;
for (uint256 i = 0; i < _numBlocks; i++) {
dataPtr = ChunkCodec.copyBlockContext(chunkPtr, dataPtr, i);
uint256 _numTransactionsInBlock = ChunkCodec.numTransactions(blockPtr);
unchecked {
_totalTransactionsInChunk += _numTransactionsInBlock;
blockPtr += ChunkCodec.BLOCK_CONTEXT_LENGTH;
// concatenate block contexts, use scope to avoid stack too deep
{
uint256 _totalTransactionsInChunk;
for (uint256 i = 0; i < _numBlocks; i++) {
dataPtr = ChunkCodec.copyBlockContext(chunkPtr, dataPtr, i);
uint256 _numTransactionsInBlock = ChunkCodec.numTransactions(blockPtr);
unchecked {
_totalTransactionsInChunk += _numTransactionsInBlock;
blockPtr += ChunkCodec.BLOCK_CONTEXT_LENGTH;
}
}
assembly {
mstore(0x40, add(dataPtr, mul(_totalTransactionsInChunk, 0x20))) // reserve memory for tx hashes
}
}
// It is used to compute the actual number of transactions in chunk.
uint256 txHashStartDataPtr;
assembly {
mstore(0x40, add(dataPtr, mul(_totalTransactionsInChunk, 0x20))) // reserve memory for tx hashes
txHashStartDataPtr := dataPtr
blockPtr := add(chunkPtr, 1) // reset block ptr
}
@@ -512,11 +520,8 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain {
}
}
// check the number of L2 transactions in the chunk
require(
_totalTransactionsInChunk - _totalNumL1MessagesInChunk <= maxNumL2TxInChunk,
"too many L2 txs in one chunk"
);
// check the actual number of transactions in the chunk
require((dataPtr - txHashStartDataPtr) / 32 <= maxNumTxInChunk, "too many txs in one chunk");
// check chunk has correct length
require(l2TxPtr - chunkPtr == _chunk.length, "incomplete l2 transaction data");
@@ -549,9 +554,10 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain {
unchecked {
uint256 _bitmap;
uint256 rem;
for (uint256 i = 0; i < _numL1Messages; i++) {
uint256 quo = _totalL1MessagesPoppedInBatch >> 8;
uint256 rem = _totalL1MessagesPoppedInBatch & 0xff;
rem = _totalL1MessagesPoppedInBatch & 0xff;
// load bitmap every 256 bits
if (i == 0 || rem == 0) {
@@ -573,7 +579,7 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain {
}
// check last L1 message is not skipped, _totalL1MessagesPoppedInBatch must > 0
uint256 rem = (_totalL1MessagesPoppedInBatch - 1) & 0xff;
rem = (_totalL1MessagesPoppedInBatch - 1) & 0xff;
require(((_bitmap >> rem) & 1) == 0, "cannot skip last L1 message");
}

View File

@@ -40,12 +40,6 @@ contract L2ScrollMessenger is ScrollMessengerBase, IL2ScrollMessenger {
/// @notice Mapping from L1 message hash to a boolean value indicating if the message has been successfully executed.
mapping(bytes32 => bool) public isL1MessageExecuted;
/// @notice Mapping from L1 message hash to the number of failure times.
mapping(bytes32 => uint256) public l1MessageFailedTimes;
/// @notice The maximum number of times each L1 message can fail on L2.
uint256 public maxFailedExecutionTimes;
/***************
* Constructor *
***************/
@@ -58,8 +52,6 @@ contract L2ScrollMessenger is ScrollMessengerBase, IL2ScrollMessenger {
function initialize(address _counterpart) external initializer {
ScrollMessengerBase.__ScrollMessengerBase_init(_counterpart, address(0));
maxFailedExecutionTimes = 3;
}
/*****************************
@@ -105,22 +97,6 @@ contract L2ScrollMessenger is ScrollMessengerBase, IL2ScrollMessenger {
_executeMessage(_from, _to, _value, _message, _xDomainCalldataHash);
}
/************************
* Restricted Functions *
************************/
/// @notice Update max failed execution times.
/// @dev This function can only called by contract owner.
/// @param _newMaxFailedExecutionTimes The new max failed execution times.
function updateMaxFailedExecutionTimes(uint256 _newMaxFailedExecutionTimes) external onlyOwner {
require(_newMaxFailedExecutionTimes > 0, "maxFailedExecutionTimes cannot be zero");
uint256 _oldMaxFailedExecutionTimes = maxFailedExecutionTimes;
maxFailedExecutionTimes = _newMaxFailedExecutionTimes;
emit UpdateMaxFailedExecutionTimes(_oldMaxFailedExecutionTimes, _newMaxFailedExecutionTimes);
}
/**********************
* Internal Functions *
**********************/
@@ -181,11 +157,6 @@ contract L2ScrollMessenger is ScrollMessengerBase, IL2ScrollMessenger {
isL1MessageExecuted[_xDomainCalldataHash] = true;
emit RelayedMessage(_xDomainCalldataHash);
} else {
unchecked {
uint256 _failedTimes = l1MessageFailedTimes[_xDomainCalldataHash] + 1;
require(_failedTimes <= maxFailedExecutionTimes, "Exceed maximum failure times");
l1MessageFailedTimes[_xDomainCalldataHash] = _failedTimes;
}
emit FailedRelayedMessage(_xDomainCalldataHash);
}
}

View File

@@ -6,8 +6,6 @@ import {IL2ERC20Gateway} from "./IL2ERC20Gateway.sol";
import {ScrollGatewayBase} from "../../libraries/gateway/ScrollGatewayBase.sol";
// solhint-disable no-empty-blocks
abstract contract L2ERC20Gateway is ScrollGatewayBase, IL2ERC20Gateway {
/*************
* Variables *

View File

@@ -2,10 +2,12 @@
pragma solidity =0.8.16;
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import {SafeERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import {IFiatToken} from "../../../interfaces/IFiatToken.sol";
import {IUSDCDestinationBridge} from "../../../interfaces/IUSDCDestinationBridge.sol";
import {IL1ERC20Gateway} from "../../../L1/gateways/IL1ERC20Gateway.sol";
import {IL2ScrollMessenger} from "../../IL2ScrollMessenger.sol";
import {IL2ERC20Gateway} from "../IL2ERC20Gateway.sol";
@@ -16,7 +18,7 @@ import {L2ERC20Gateway} from "../L2ERC20Gateway.sol";
/// @title L2USDCGateway
/// @notice The `L2USDCGateway` contract is used to withdraw `USDC` token on layer 2 and
/// finalize deposit `USDC` from layer 1.
contract L2USDCGateway is L2ERC20Gateway {
contract L2USDCGateway is L2ERC20Gateway, IUSDCDestinationBridge {
using SafeERC20Upgradeable for IERC20Upgradeable;
/*************
@@ -33,8 +35,15 @@ contract L2USDCGateway is L2ERC20Gateway {
* Variables *
*************/
/// @notice The address of caller from Circle.
address public circleCaller;
/// @notice The flag indicates whether USDC deposit is paused.
/// @dev This is not necessary to be set `true` since we will set `L1USDCGateway.depositPaused` first.
/// This is kept just in case and will be set after all pending messages are relayed.
bool public depositPaused;
/// @notice The flag indicates whether USDC withdrawal is paused.
bool public withdrawPaused;
/***************
@@ -91,7 +100,8 @@ contract L2USDCGateway is L2ERC20Gateway {
require(IFiatToken(_l2Token).mint(_to, _amount), "mint USDC failed");
_doCallback(_to, _data);
// disable call for USDC
// _doCallback(_to, _data);
emit FinalizeDepositERC20(_l1Token, _l2Token, _from, _to, _amount, _data);
}
@@ -100,6 +110,19 @@ contract L2USDCGateway is L2ERC20Gateway {
* Public Restricted Functions *
*******************************/
/// @inheritdoc IUSDCDestinationBridge
function transferUSDCRoles(address _owner) external {
require(msg.sender == circleCaller, "only circle caller");
OwnableUpgradeable(l2USDC).transferOwnership(_owner);
}
/// @notice Update the Circle EOA address.
/// @param _caller The address to update.
function updateCircleCaller(address _caller) external onlyOwner {
circleCaller = _caller;
}
/// @notice Change the deposit pause status of this contract.
/// @param _paused The new status, `true` means paused and `false` means not paused.
function pauseDeposit(bool _paused) external onlyOwner {
@@ -128,15 +151,16 @@ contract L2USDCGateway is L2ERC20Gateway {
require(_token == l2USDC, "only USDC is allowed");
require(!withdrawPaused, "withdraw paused");
// 1. Extract real sender if this call is from L1GatewayRouter.
// 1. Extract real sender if this call is from L2GatewayRouter.
address _from = msg.sender;
if (router == msg.sender) {
(_from, _data) = abi.decode(_data, (address, bytes));
}
require(_data.length == 0, "call is not allowed");
// 2. Transfer token into this contract.
IERC20Upgradeable(_token).safeTransferFrom(_from, address(this), _amount);
require(IFiatToken(_token).burn(_amount), "burn USDC failed");
IFiatToken(_token).burn(_amount);
// 3. Generate message passed to L1USDCGateway.
address _l1USDC = l1USDC;

View File

@@ -0,0 +1,156 @@
// SPDX-License-Identifier: MIT
pragma solidity =0.8.16;
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import {SafeERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import {ITokenMessenger} from "../../../interfaces/ITokenMessenger.sol";
import {IL1ERC20Gateway} from "../../../L1/gateways/IL1ERC20Gateway.sol";
import {IL2ScrollMessenger} from "../../IL2ScrollMessenger.sol";
import {IL2ERC20Gateway} from "../IL2ERC20Gateway.sol";
import {CCTPGatewayBase} from "../../../libraries/gateway/CCTPGatewayBase.sol";
import {ScrollGatewayBase} from "../../../libraries/gateway/ScrollGatewayBase.sol";
import {L2ERC20Gateway} from "../L2ERC20Gateway.sol";
/// @title L2USDCGatewayCCTP
/// @notice The `L2USDCGatewayCCTP` contract is used to withdraw `USDC` token in layer 2 and
/// finalize deposit `USDC` from layer 1.
contract L2USDCGatewayCCTP is CCTPGatewayBase, L2ERC20Gateway {
using SafeERC20Upgradeable for IERC20Upgradeable;
/***************
* Constructor *
***************/
constructor(
address _l1USDC,
address _l2USDC,
uint32 _destinationDomain
) CCTPGatewayBase(_l1USDC, _l2USDC, _destinationDomain) {
_disableInitializers();
}
/// @notice Initialize the storage of L2USDCGatewayCCTP.
/// @param _counterpart The address of L1USDCGatewayCCTP in L1.
/// @param _router The address of L2GatewayRouter.
/// @param _messenger The address of L2ScrollMessenger.
/// @param _cctpMessenger The address of TokenMessenger in local domain.
/// @param _cctpTransmitter The address of MessageTransmitter in local domain.
function initialize(
address _counterpart,
address _router,
address _messenger,
address _cctpMessenger,
address _cctpTransmitter
) external initializer {
require(_router != address(0), "zero router address");
ScrollGatewayBase._initialize(_counterpart, _router, _messenger);
CCTPGatewayBase._initialize(_cctpMessenger, _cctpTransmitter);
}
/*************************
* Public View Functions *
*************************/
/// @inheritdoc IL2ERC20Gateway
function getL1ERC20Address(address) external view override returns (address) {
return l1USDC;
}
/// @inheritdoc IL2ERC20Gateway
function getL2ERC20Address(address) public view override returns (address) {
return l2USDC;
}
/*****************************
* Public Mutating Functions *
*****************************/
/// @inheritdoc IL2ERC20Gateway
/// @dev The function will not mint the USDC, users need to call `claimUSDC` after this function is done.
function finalizeDepositERC20(
address _l1Token,
address _l2Token,
address _from,
address _to,
uint256 _amount,
bytes memory _data
) external payable override onlyCallByCounterpart {
require(msg.value == 0, "nonzero msg.value");
require(_l1Token == l1USDC, "l1 token not USDC");
require(_l2Token == l2USDC, "l2 token not USDC");
uint256 _nonce;
(_nonce, _data) = abi.decode(_data, (uint256, bytes));
require(status[_nonce] == CCTPMessageStatus.None, "message relayed");
status[_nonce] = CCTPMessageStatus.Pending;
emit FinalizeDepositERC20(_l1Token, _l2Token, _from, _to, _amount, _data);
}
/*******************************
* Public Restricted Functions *
*******************************/
/// @notice Update the CCTP contract addresses.
/// @param _messenger The address of TokenMessenger in local domain.
/// @param _transmitter The address of MessageTransmitter in local domain.
function updateCCTPContracts(address _messenger, address _transmitter) external onlyOwner {
cctpMessenger = _messenger;
cctpTransmitter = _transmitter;
}
/**********************
* Internal Functions *
**********************/
/// @inheritdoc L2ERC20Gateway
function _withdraw(
address _token,
address _to,
uint256 _amount,
bytes memory _data,
uint256 _gasLimit
) internal virtual override {
require(_amount > 0, "withdraw zero amount");
require(_token == l2USDC, "only USDC is allowed");
// 1. Extract real sender if this call is from L1GatewayRouter.
address _from = msg.sender;
if (router == msg.sender) {
(_from, _data) = abi.decode(_data, (address, bytes));
}
// 2. Transfer token into this contract.
IERC20Upgradeable(_token).safeTransferFrom(_from, address(this), _amount);
// 3. Burn token through CCTP TokenMessenger
uint256 _nonce = ITokenMessenger(cctpMessenger).depositForBurnWithCaller(
_amount,
destinationDomain,
bytes32(uint256(uint160(_to))),
address(this),
bytes32(uint256(uint160(counterpart)))
);
// 4. Generate message passed to L1USDCGateway.
address _l1USDC = l1USDC;
bytes memory _message = abi.encodeWithSelector(
IL1ERC20Gateway.finalizeWithdrawERC20.selector,
_l1USDC,
_token,
_from,
_to,
_amount,
abi.encode(_nonce, _data)
);
// 4. Send message to L1ScrollMessenger.
IL2ScrollMessenger(messenger).sendMessage{value: msg.value}(counterpart, 0, _message, _gasLimit);
emit WithdrawERC20(_l1USDC, _token, _from, _to, _amount, _data);
}
}

View File

@@ -1,6 +1,6 @@
A library for interacting with Scroll contracts.
This library includes contracts and interfaces needed to interact with the Scroll Smart Contracts deployed on both Layer 1 and Layer 2. This includes deposting and withdrawing ETH, ERC20 tokens and NFTs or sending arbitrary messages.
This library includes contracts and interfaces needed to interact with the Scroll Smart Contracts deployed on both Layer 1 and Layer 2. This includes deposting and withdrawing ETH, ERC20 tokens and NFTs or sending arbitrary messages.
# Overview
@@ -21,10 +21,11 @@ pragma solidity 0.8.20;
import "@scroll-tech/contracts/L1/gateways/IL1ETHGateway.sol";
contract MyContract {
function bridgeETH(address scrollBridge, uint gasLimit) public payable {
IL1ETHGateway(scrollBridge).depositETH(msg.sender, msg.value, gasLimit);
}
function bridgeETH(address scrollBridge, uint256 gasLimit) public payable {
IL1ETHGateway(scrollBridge).depositETH(msg.sender, msg.value, gasLimit);
}
}
```
Visit the Bridge Documentation for API reference, architecture overview and guides with code examples.

View File

@@ -6,7 +6,6 @@ import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol"
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol";
import {OwnableBase} from "../libraries/common/OwnableBase.sol";
@@ -15,6 +14,7 @@ import {OwnableBase} from "../libraries/common/OwnableBase.sol";
contract GasSwap is ERC2771Context, ReentrancyGuard, OwnableBase {
using SafeERC20 for IERC20;
using SafeERC20 for IERC20Permit;
/**********
* Events *
@@ -94,7 +94,7 @@ contract GasSwap is ERC2771Context, ReentrancyGuard, OwnableBase {
address _sender = _msgSender();
// do permit
IERC20Permit(_permit.token).permit(
IERC20Permit(_permit.token).safePermit(
_sender,
address(this),
_permit.value,

View File

@@ -18,5 +18,5 @@ interface IFiatToken {
* amount is less than or equal to the minter's account balance
* @param _amount uint256 the amount of tokens to be burned
*/
function burn(uint256 _amount) external returns (bool);
function burn(uint256 _amount) external;
}

View File

@@ -0,0 +1,16 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;
interface IMessageTransmitter {
function usedNonces(bytes32 _sourceAndNonce) external view returns (uint256);
/**
* @notice Receives an incoming message, validating the header and passing
* the body to application-specific handler.
* @param message The message raw bytes
* @param signature The message signature
* @return success bool, true if successful
*/
function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success);
}

View File

@@ -0,0 +1,63 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;
interface ITokenMessenger {
/**
* @notice Deposits and burns tokens from sender to be minted on destination domain. The mint
* on the destination domain must be called by `destinationCaller`.
* WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible
* to broadcast the message on the destination domain. This is an advanced feature, and the standard
* depositForBurn() should be preferred for use cases where a specific destination caller is not required.
* Emits a `DepositForBurn` event.
* @dev reverts if:
* - given destinationCaller is zero address
* - given burnToken is not supported
* - given destinationDomain has no TokenMessenger registered
* - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance
* to this contract is less than `amount`.
* - burn() reverts. For example, if `amount` is 0.
* - MessageTransmitter returns false or reverts.
* @param amount amount of tokens to burn
* @param destinationDomain destination domain
* @param mintRecipient address of mint recipient on destination domain
* @param burnToken address of contract to burn deposited tokens, on local domain
* @param destinationCaller caller on the destination domain, as bytes32
* @return nonce unique nonce reserved by message
*/
function depositForBurnWithCaller(
uint256 amount,
uint32 destinationDomain,
bytes32 mintRecipient,
address burnToken,
bytes32 destinationCaller
) external returns (uint64 nonce);
/**
* @notice Replace a BurnMessage to change the mint recipient and/or
* destination caller. Allows the sender of a previous BurnMessage
* (created by depositForBurn or depositForBurnWithCaller)
* to send a new BurnMessage to replace the original.
* The new BurnMessage will reuse the amount and burn token of the original,
* without requiring a new deposit.
* @dev The new message will reuse the original message's nonce. For a
* given nonce, all replacement message(s) and the original message are
* valid to broadcast on the destination domain, until the first message
* at the nonce confirms, at which point all others are invalidated.
* Note: The msg.sender of the replaced message must be the same as the
* msg.sender of the original message.
* @param originalMessage original message bytes (to replace)
* @param originalAttestation original attestation bytes
* @param newDestinationCaller the new destination caller, which may be the
* same as the original destination caller, a new destination caller, or an empty
* destination caller (bytes32(0), indicating that any destination caller is valid.)
* @param newMintRecipient the new mint recipient, which may be the same as the
* original mint recipient, or different.
*/
function replaceDepositForBurn(
bytes calldata originalMessage,
bytes calldata originalAttestation,
bytes32 newDestinationCaller,
bytes32 newMintRecipient
) external;
}

View File

@@ -0,0 +1,12 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;
// Implement this on the source chain (Ethereum).
interface IUSDCBurnableSourceBridge {
/**
* @notice Called by Circle, this executes a burn on the source
* chain.
*/
function burnAllLockedUSDC() external;
}

View File

@@ -0,0 +1,11 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;
// Implement this on the destination chain (Scroll).
interface IUSDCDestinationBridge {
/**
* @notice Called by Circle, this transfers FiatToken roles to the designated owner.
*/
function transferUSDCRoles(address owner) external;
}

View File

@@ -0,0 +1,95 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IMessageTransmitter} from "../../interfaces/IMessageTransmitter.sol";
import {ScrollGatewayBase} from "./ScrollGatewayBase.sol";
abstract contract CCTPGatewayBase is ScrollGatewayBase {
/*********
* Enums *
*********/
enum CCTPMessageStatus {
None,
Pending,
Relayed
}
/*************
* Constants *
*************/
/// @notice The address of L1 USDC address.
address public immutable l1USDC;
/// @notice The address of L2 USDC address.
address public immutable l2USDC;
/// @notice The destination domain for layer2.
uint32 public immutable destinationDomain;
/*************
* Variables *
*************/
/// @notice The address of TokenMessenger in local domain.
address public cctpMessenger;
/// @notice The address of MessageTransmitter in local domain.
address public cctpTransmitter;
/// @notice Mapping from destination domain CCTP nonce to status.
mapping(uint256 => CCTPMessageStatus) public status;
/***************
* Constructor *
***************/
constructor(
address _l1USDC,
address _l2USDC,
uint32 _destinationDomain
) {
l1USDC = _l1USDC;
l2USDC = _l2USDC;
destinationDomain = _destinationDomain;
}
function _initialize(address _cctpMessenger, address _cctpTransmitter) internal {
cctpMessenger = _cctpMessenger;
cctpTransmitter = _cctpTransmitter;
}
/*****************************
* Public Mutating Functions *
*****************************/
/// @notice Claim USDC that has been cross chained.
/// @param _nonce The nonce of the message from CCTP.
/// @param _cctpMessage The message passed to MessageTransmitter contract in CCTP.
/// @param _cctpSignature The message passed to MessageTransmitter contract in CCTP.
function claimUSDC(
uint256 _nonce,
bytes calldata _cctpMessage,
bytes calldata _cctpSignature
) public {
// Check `_nonce` match with `_cctpMessage`.
// According to the encoding of `_cctpMessage`, the nonce is in bytes 12 to 16.
// See here: https://github.com/circlefin/evm-cctp-contracts/blob/master/src/messages/Message.sol#L29
uint256 _expectedMessageNonce;
assembly {
_expectedMessageNonce := and(shr(96, calldataload(_cctpMessage.offset)), 0xffffffffffffffff)
}
require(_expectedMessageNonce == _nonce, "nonce mismatch");
require(status[_nonce] == CCTPMessageStatus.Pending, "message not relayed");
// call transmitter to mint USDC
bool _success = IMessageTransmitter(cctpTransmitter).receiveMessage(_cctpMessage, _cctpSignature);
require(_success, "call transmitter failed");
status[_nonce] = CCTPMessageStatus.Relayed;
}
}

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma solidity =0.8.16;
import {AccessControlEnumerable} from "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
@@ -10,6 +10,22 @@ import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet
contract ScrollOwner is AccessControlEnumerable {
using EnumerableSet for EnumerableSet.Bytes32Set;
/**********
* Events *
**********/
/// @notice Emitted when the access to target contract is granted.
/// @param role The role to grant access.
/// @param target The address of target contract.
/// @param selectors The list of function selectors to grant access.
event GrantAccess(bytes32 indexed role, address indexed target, bytes4[] selectors);
/// @notice Emitted when the access to target contract is revoked.
/// @param role The role to revoke access.
/// @param target The address of target contract.
/// @param selectors The list of function selectors to revoke access.
event RevokeAccess(bytes32 indexed role, address indexed target, bytes4[] selectors);
/*************
* Variables *
*************/
@@ -69,7 +85,7 @@ contract ScrollOwner is AccessControlEnumerable {
uint256 _value,
bytes calldata _data,
bytes32 _role
) public payable onlyRole(_role) hasAccess(_target, bytes4(_data[0:4]), _role) {
) external payable onlyRole(_role) hasAccess(_target, bytes4(_data[0:4]), _role) {
_execute(_target, _value, _data);
}
@@ -95,10 +111,14 @@ contract ScrollOwner is AccessControlEnumerable {
for (uint256 i = 0; i < _selectors.length; i++) {
targetAccess[_target][_selectors[i]].add(_role);
}
emit GrantAccess(_role, _target, _selectors);
} else {
for (uint256 i = 0; i < _selectors.length; i++) {
targetAccess[_target][_selectors[i]].remove(_role);
}
emit RevokeAccess(_role, _target, _selectors);
}
}
@@ -114,9 +134,9 @@ contract ScrollOwner is AccessControlEnumerable {
address _target,
uint256 _value,
bytes calldata _data
) internal {
) private {
// solhint-disable-next-line avoid-low-level-calls
(bool success, ) = address(_target).call{value: _value}(_data);
(bool success, ) = _target.call{value: _value}(_data);
if (!success) {
// solhint-disable-next-line no-inline-assembly
assembly {

View File

@@ -15,7 +15,7 @@ contract ETHRateLimiter is Ownable, IETHRateLimiter {
* Structs *
***********/
struct TokenAmount {
struct ETHAmount {
// The timestamp when the amount is updated.
uint48 lastUpdateTs;
// The ETH limit in wei.
@@ -39,8 +39,8 @@ contract ETHRateLimiter is Ownable, IETHRateLimiter {
* Variables *
*************/
/// @notice The token amount used in current period.
TokenAmount public currentPeriod;
/// @notice The ETH amount used in current period.
ETHAmount public currentPeriod;
/***************
* Constructor *
@@ -55,14 +55,10 @@ contract ETHRateLimiter is Ownable, IETHRateLimiter {
revert PeriodIsZero();
}
if (_totalLimit == 0) {
revert TotalLimitIsZero();
}
periodDuration = _periodDuration;
spender = _spender;
currentPeriod.limit = _totalLimit;
_updateTotalLimit(_totalLimit);
}
/*****************************
@@ -80,9 +76,9 @@ contract ETHRateLimiter is Ownable, IETHRateLimiter {
// check total limit
uint256 _currentTotalAmount;
TokenAmount memory _currentPeriod = currentPeriod;
ETHAmount memory _currentPeriod = currentPeriod;
if (_currentPeriod.lastUpdateTs < _currentPeriodStart) {
if (uint256(_currentPeriod.lastUpdateTs) < _currentPeriodStart) {
_currentTotalAmount = _amount;
} else {
_currentTotalAmount = _currentPeriod.amount + _amount;
@@ -101,9 +97,19 @@ contract ETHRateLimiter is Ownable, IETHRateLimiter {
* Restricted Functions *
************************/
/// @notice Update the total token amount limit.
/// @notice Update the total ETH amount limit.
/// @param _newTotalLimit The new total limit.
function updateTotalLimit(uint104 _newTotalLimit) external onlyOwner {
_updateTotalLimit(_newTotalLimit);
}
/**********************
* Internal Functions *
**********************/
/// @dev Internal function to update the total token amount limit.
/// @param _newTotalLimit The new total limit.
function _updateTotalLimit(uint104 _newTotalLimit) private {
if (_newTotalLimit == 0) {
revert TotalLimitIsZero();
}

View File

@@ -54,7 +54,7 @@ contract TokenRateLimiter is AccessControlEnumerable, ITokenRateLimiter {
revert PeriodIsZero();
}
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
periodDuration = _periodDuration;
}
@@ -72,7 +72,7 @@ contract TokenRateLimiter is AccessControlEnumerable, ITokenRateLimiter {
// check total limit, `0` means no limit at all.
uint256 _currentTotalAmount;
TokenAmount memory _currentPeriod = currentPeriod[_token];
if (_currentPeriod.lastUpdateTs < _currentPeriodStart) {
if (uint256(_currentPeriod.lastUpdateTs) < _currentPeriodStart) {
_currentTotalAmount = _amount;
} else {
_currentTotalAmount = _currentPeriod.amount + _amount;

View File

@@ -106,6 +106,8 @@ contract L1ScrollMessengerTest is L1GatewayTestBase {
exceedValue = bound(exceedValue, 1, address(this).balance / 2);
l1Messenger.updateMaxReplayTimes(0);
// append a message
l1Messenger.sendMessage{value: 100}(address(0), 100, new bytes(0), 0, refundAddress);
@@ -179,9 +181,9 @@ contract L1ScrollMessengerTest is L1GatewayTestBase {
hevm.stopPrank();
hevm.expectEmit(false, false, false, true);
emit UpdateMaxReplayTimes(0, _maxReplayTimes);
emit UpdateMaxReplayTimes(3, _maxReplayTimes);
assertEq(l1Messenger.maxReplayTimes(), 0);
assertEq(l1Messenger.maxReplayTimes(), 3);
l1Messenger.updateMaxReplayTimes(_maxReplayTimes);
assertEq(l1Messenger.maxReplayTimes(), _maxReplayTimes);
}

View File

@@ -0,0 +1,528 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol";
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import {L1GatewayRouter} from "../L1/gateways/L1GatewayRouter.sol";
import {IL1ERC20Gateway, L1USDCGateway} from "../L1/gateways/usdc/L1USDCGateway.sol";
import {IL1ScrollMessenger} from "../L1/IL1ScrollMessenger.sol";
import {IL2ERC20Gateway, L2USDCGateway} from "../L2/gateways/usdc/L2USDCGateway.sol";
import {AddressAliasHelper} from "../libraries/common/AddressAliasHelper.sol";
import {L1GatewayTestBase} from "./L1GatewayTestBase.t.sol";
import {MockScrollMessenger} from "./mocks/MockScrollMessenger.sol";
import {MockGatewayRecipient} from "./mocks/MockGatewayRecipient.sol";
contract L1USDCGatewayTest is L1GatewayTestBase {
// from L1USDCGateway
event FinalizeWithdrawERC20(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
event DepositERC20(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
MockERC20 private l1USDC;
MockERC20 private l2USDC;
L1USDCGateway private gateway;
L1GatewayRouter private router;
L2USDCGateway private counterpartGateway;
function setUp() public {
setUpBase();
// Deploy tokens
l1USDC = new MockERC20("USDC", "USDC", 6);
l2USDC = new MockERC20("USDC", "USDC", 6);
// Deploy L1 contracts
gateway = _deployGateway();
router = L1GatewayRouter(address(new ERC1967Proxy(address(new L1GatewayRouter()), new bytes(0))));
// Deploy L2 contracts
counterpartGateway = new L2USDCGateway(address(l1USDC), address(l2USDC));
// Initialize L1 contracts
gateway.initialize(address(counterpartGateway), address(router), address(l1Messenger));
router.initialize(address(0), address(gateway));
// Prepare token balances
l1USDC.mint(address(this), type(uint128).max);
l1USDC.approve(address(gateway), type(uint256).max);
l1USDC.approve(address(router), type(uint256).max);
}
function testInitialized() public {
assertEq(address(counterpartGateway), gateway.counterpart());
assertEq(address(router), gateway.router());
assertEq(address(l1Messenger), gateway.messenger());
assertEq(address(l1USDC), gateway.l1USDC());
assertEq(address(l2USDC), gateway.l2USDC());
assertEq(address(l2USDC), gateway.getL2ERC20Address(address(l1USDC)));
assertEq(0, gateway.totalBridgedUSDC());
hevm.expectRevert("Initializable: contract is already initialized");
gateway.initialize(address(counterpartGateway), address(router), address(l1Messenger));
}
function testDepositPaused() public {
// non-owner call pause, should revert
hevm.startPrank(address(1));
hevm.expectRevert("Ownable: caller is not the owner");
gateway.pauseDeposit(false);
hevm.expectRevert("Ownable: caller is not the owner");
gateway.pauseDeposit(true);
hevm.stopPrank();
// pause deposit
gateway.pauseDeposit(true);
// deposit paused, should revert
hevm.expectRevert("deposit paused");
gateway.depositERC20(address(l1USDC), 1, 0);
hevm.expectRevert("deposit paused");
gateway.depositERC20(address(l1USDC), address(this), 1, 0);
hevm.expectRevert("deposit paused");
gateway.depositERC20AndCall(address(l1USDC), address(this), 1, new bytes(0), 0);
}
function testPauseWithdraw() public {
// non-owner call pause, should revert
hevm.startPrank(address(1));
hevm.expectRevert("Ownable: caller is not the owner");
gateway.pauseWithdraw(false);
hevm.expectRevert("Ownable: caller is not the owner");
gateway.pauseWithdraw(true);
hevm.stopPrank();
}
function testDepositERC20(
uint256 amount,
uint256 gasLimit,
uint256 feePerGas
) public {
_depositERC20(false, amount, gasLimit, feePerGas);
}
function testDepositERC20WithRecipient(
uint256 amount,
address recipient,
uint256 gasLimit,
uint256 feePerGas
) public {
_depositERC20WithRecipient(false, amount, recipient, gasLimit, feePerGas);
}
function testRouterDepositERC20(
uint256 amount,
uint256 gasLimit,
uint256 feePerGas
) public {
_depositERC20(true, amount, gasLimit, feePerGas);
}
function testRouterDepositERC20WithRecipient(
uint256 amount,
address recipient,
uint256 gasLimit,
uint256 feePerGas
) public {
_depositERC20WithRecipient(true, amount, recipient, gasLimit, feePerGas);
}
function testFinalizeWithdrawERC20FailedMocking(
address sender,
address recipient,
uint256 amount,
bytes memory dataToCall
) public {
amount = bound(amount, 1, 100000);
// revert when caller is not messenger
hevm.expectRevert("only messenger can call");
gateway.finalizeWithdrawERC20(address(l1USDC), address(l2USDC), sender, recipient, amount, dataToCall);
MockScrollMessenger mockMessenger = new MockScrollMessenger();
gateway = _deployGateway();
gateway.initialize(address(counterpartGateway), address(router), address(mockMessenger));
// only call by conterpart
hevm.expectRevert("only call by counterpart");
mockMessenger.callTarget(
address(gateway),
abi.encodeWithSelector(
gateway.finalizeWithdrawERC20.selector,
address(l1USDC),
address(l2USDC),
sender,
recipient,
amount,
dataToCall
)
);
mockMessenger.setXDomainMessageSender(address(counterpartGateway));
// nonzero msg.value
hevm.expectRevert("nonzero msg.value");
mockMessenger.callTarget{value: 1}(
address(gateway),
abi.encodeWithSelector(
gateway.finalizeWithdrawERC20.selector,
address(l1USDC),
address(l2USDC),
sender,
recipient,
amount,
dataToCall
)
);
// l1 token not USDC
hevm.expectRevert("l1 token not USDC");
mockMessenger.callTarget(
address(gateway),
abi.encodeWithSelector(
gateway.finalizeWithdrawERC20.selector,
address(l2USDC),
address(l2USDC),
sender,
recipient,
amount,
dataToCall
)
);
// l2 token not USDC
hevm.expectRevert("l2 token not USDC");
mockMessenger.callTarget(
address(gateway),
abi.encodeWithSelector(
gateway.finalizeWithdrawERC20.selector,
address(l1USDC),
address(l1USDC),
sender,
recipient,
amount,
dataToCall
)
);
// withdraw paused
gateway.pauseWithdraw(true);
hevm.expectRevert("withdraw paused");
mockMessenger.callTarget(
address(gateway),
abi.encodeWithSelector(
gateway.finalizeWithdrawERC20.selector,
address(l1USDC),
address(l2USDC),
sender,
recipient,
amount,
dataToCall
)
);
}
function testFinalizeWithdrawERC20Failed(
address sender,
address recipient,
uint256 amount,
bytes memory dataToCall
) public {
// blacklist some addresses
hevm.assume(recipient != address(0));
hevm.assume(recipient != address(gateway));
amount = bound(amount, 1, l1USDC.balanceOf(address(this)));
// deposit some USDC to L1ScrollMessenger
gateway.depositERC20(address(l1USDC), amount, 0);
// do finalize withdraw usdc
bytes memory message = abi.encodeWithSelector(
IL1ERC20Gateway.finalizeWithdrawERC20.selector,
address(l1USDC),
address(l2USDC),
sender,
recipient,
amount,
dataToCall
);
bytes memory xDomainCalldata = abi.encodeWithSignature(
"relayMessage(address,address,uint256,uint256,bytes)",
address(uint160(address(counterpartGateway)) + 1),
address(gateway),
0,
0,
message
);
prepareL2MessageRoot(keccak256(xDomainCalldata));
IL1ScrollMessenger.L2MessageProof memory proof;
proof.batchIndex = rollup.lastFinalizedBatchIndex();
// conterpart is not L2USDCGateway
// emit FailedRelayedMessage from L1ScrollMessenger
hevm.expectEmit(true, false, false, true);
emit FailedRelayedMessage(keccak256(xDomainCalldata));
uint256 gatewayBalance = l1USDC.balanceOf(address(gateway));
uint256 recipientBalance = l1USDC.balanceOf(recipient);
assertBoolEq(false, l1Messenger.isL2MessageExecuted(keccak256(xDomainCalldata)));
l1Messenger.relayMessageWithProof(
address(uint160(address(counterpartGateway)) + 1),
address(gateway),
0,
0,
message,
proof
);
assertEq(gatewayBalance, l1USDC.balanceOf(address(gateway)));
assertEq(recipientBalance, l1USDC.balanceOf(recipient));
assertBoolEq(false, l1Messenger.isL2MessageExecuted(keccak256(xDomainCalldata)));
}
function testFinalizeWithdrawERC20(
address sender,
uint256 amount,
bytes memory dataToCall
) public {
MockGatewayRecipient recipient = new MockGatewayRecipient();
amount = bound(amount, 1, l1USDC.balanceOf(address(this)));
// deposit some USDC to gateway
gateway.depositERC20(address(l1USDC), amount, 0);
// do finalize withdraw usdc
bytes memory message = abi.encodeWithSelector(
IL1ERC20Gateway.finalizeWithdrawERC20.selector,
address(l1USDC),
address(l2USDC),
sender,
address(recipient),
amount,
dataToCall
);
bytes memory xDomainCalldata = abi.encodeWithSignature(
"relayMessage(address,address,uint256,uint256,bytes)",
address(counterpartGateway),
address(gateway),
0,
0,
message
);
prepareL2MessageRoot(keccak256(xDomainCalldata));
IL1ScrollMessenger.L2MessageProof memory proof;
proof.batchIndex = rollup.lastFinalizedBatchIndex();
// emit FinalizeWithdrawERC20 from L1USDCGateway
{
hevm.expectEmit(true, true, true, true);
emit FinalizeWithdrawERC20(
address(l1USDC),
address(l2USDC),
sender,
address(recipient),
amount,
dataToCall
);
}
// emit RelayedMessage from L1ScrollMessenger
{
hevm.expectEmit(true, false, false, true);
emit RelayedMessage(keccak256(xDomainCalldata));
}
uint256 gatewayBalance = l1USDC.balanceOf(address(gateway));
uint256 totalBridgedUSDCBefore = gateway.totalBridgedUSDC();
uint256 recipientBalance = l1USDC.balanceOf(address(recipient));
assertBoolEq(false, l1Messenger.isL2MessageExecuted(keccak256(xDomainCalldata)));
l1Messenger.relayMessageWithProof(address(counterpartGateway), address(gateway), 0, 0, message, proof);
assertEq(gatewayBalance - amount, l1USDC.balanceOf(address(gateway)));
assertEq(totalBridgedUSDCBefore - amount, gateway.totalBridgedUSDC());
assertEq(recipientBalance + amount, l1USDC.balanceOf(address(recipient)));
assertBoolEq(true, l1Messenger.isL2MessageExecuted(keccak256(xDomainCalldata)));
}
function _depositERC20(
bool useRouter,
uint256 amount,
uint256 gasLimit,
uint256 feePerGas
) private {
amount = bound(amount, 0, l1USDC.balanceOf(address(this)));
gasLimit = bound(gasLimit, 0, 1000000);
feePerGas = bound(feePerGas, 0, 1000);
gasOracle.setL2BaseFee(feePerGas);
uint256 feeToPay = feePerGas * gasLimit;
bytes memory message = abi.encodeWithSelector(
IL2ERC20Gateway.finalizeDepositERC20.selector,
address(l1USDC),
address(l2USDC),
address(this),
address(this),
amount,
new bytes(0)
);
bytes memory xDomainCalldata = abi.encodeWithSignature(
"relayMessage(address,address,uint256,uint256,bytes)",
address(gateway),
address(counterpartGateway),
0,
0,
message
);
if (amount == 0) {
hevm.expectRevert("deposit zero amount");
if (useRouter) {
router.depositERC20{value: feeToPay + extraValue}(address(l1USDC), amount, gasLimit);
} else {
gateway.depositERC20{value: feeToPay + extraValue}(address(l1USDC), amount, gasLimit);
}
} else {
// token is not l1USDC
hevm.expectRevert("only USDC is allowed");
gateway.depositERC20(address(l2USDC), amount, gasLimit);
// emit QueueTransaction from L1MessageQueue
{
hevm.expectEmit(true, true, false, true);
address sender = AddressAliasHelper.applyL1ToL2Alias(address(l1Messenger));
emit QueueTransaction(sender, address(l2Messenger), 0, 0, gasLimit, xDomainCalldata);
}
// emit SentMessage from L1ScrollMessenger
{
hevm.expectEmit(true, true, false, true);
emit SentMessage(address(gateway), address(counterpartGateway), 0, 0, gasLimit, message);
}
// emit DepositERC20 from L1USDCGateway
hevm.expectEmit(true, true, true, true);
emit DepositERC20(address(l1USDC), address(l2USDC), address(this), address(this), amount, new bytes(0));
uint256 gatewayBalance = l1USDC.balanceOf(address(gateway));
uint256 totalBridgedUSDCBefore = gateway.totalBridgedUSDC();
uint256 feeVaultBalance = address(feeVault).balance;
assertBoolEq(false, l1Messenger.isL1MessageSent(keccak256(xDomainCalldata)));
if (useRouter) {
router.depositERC20{value: feeToPay + extraValue}(address(l1USDC), amount, gasLimit);
} else {
gateway.depositERC20{value: feeToPay + extraValue}(address(l1USDC), amount, gasLimit);
}
assertEq(amount + gatewayBalance, l1USDC.balanceOf(address(gateway)));
assertEq(amount + totalBridgedUSDCBefore, gateway.totalBridgedUSDC());
assertEq(feeToPay + feeVaultBalance, address(feeVault).balance);
assertBoolEq(true, l1Messenger.isL1MessageSent(keccak256(xDomainCalldata)));
}
}
function _depositERC20WithRecipient(
bool useRouter,
uint256 amount,
address recipient,
uint256 gasLimit,
uint256 feePerGas
) private {
amount = bound(amount, 0, l1USDC.balanceOf(address(this)));
gasLimit = bound(gasLimit, 0, 1000000);
feePerGas = bound(feePerGas, 0, 1000);
gasOracle.setL2BaseFee(feePerGas);
uint256 feeToPay = feePerGas * gasLimit;
bytes memory message = abi.encodeWithSelector(
IL2ERC20Gateway.finalizeDepositERC20.selector,
address(l1USDC),
address(l2USDC),
address(this),
recipient,
amount,
new bytes(0)
);
bytes memory xDomainCalldata = abi.encodeWithSignature(
"relayMessage(address,address,uint256,uint256,bytes)",
address(gateway),
address(counterpartGateway),
0,
0,
message
);
if (amount == 0) {
hevm.expectRevert("deposit zero amount");
if (useRouter) {
router.depositERC20{value: feeToPay + extraValue}(address(l1USDC), recipient, amount, gasLimit);
} else {
gateway.depositERC20{value: feeToPay + extraValue}(address(l1USDC), recipient, amount, gasLimit);
}
} else {
// token is not l1USDC
hevm.expectRevert("only USDC is allowed");
gateway.depositERC20(address(l2USDC), recipient, amount, gasLimit);
// emit QueueTransaction from L1MessageQueue
{
hevm.expectEmit(true, true, false, true);
address sender = AddressAliasHelper.applyL1ToL2Alias(address(l1Messenger));
emit QueueTransaction(sender, address(l2Messenger), 0, 0, gasLimit, xDomainCalldata);
}
// emit SentMessage from L1ScrollMessenger
{
hevm.expectEmit(true, true, false, true);
emit SentMessage(address(gateway), address(counterpartGateway), 0, 0, gasLimit, message);
}
// emit DepositERC20 from L1USDCGateway
hevm.expectEmit(true, true, true, true);
emit DepositERC20(address(l1USDC), address(l2USDC), address(this), recipient, amount, new bytes(0));
uint256 gatewayBalance = l1USDC.balanceOf(address(gateway));
uint256 totalBridgedUSDCBefore = gateway.totalBridgedUSDC();
uint256 feeVaultBalance = address(feeVault).balance;
assertBoolEq(false, l1Messenger.isL1MessageSent(keccak256(xDomainCalldata)));
if (useRouter) {
router.depositERC20{value: feeToPay + extraValue}(address(l1USDC), recipient, amount, gasLimit);
} else {
gateway.depositERC20{value: feeToPay + extraValue}(address(l1USDC), recipient, amount, gasLimit);
}
assertEq(amount + gatewayBalance, l1USDC.balanceOf(address(gateway)));
assertEq(amount + totalBridgedUSDCBefore, gateway.totalBridgedUSDC());
assertEq(feeToPay + feeVaultBalance, address(feeVault).balance);
assertBoolEq(true, l1Messenger.isL1MessageSent(keccak256(xDomainCalldata)));
}
}
function _deployGateway() internal returns (L1USDCGateway) {
return
L1USDCGateway(
payable(new ERC1967Proxy(address(new L1USDCGateway(address(l1USDC), address(l2USDC))), new bytes(0)))
);
}
}

Some files were not shown because too many files have changed in this diff Show More