mirror of
https://github.com/scroll-tech/scroll.git
synced 2026-01-14 16:37:56 -05:00
Compare commits
84 Commits
manager_ap
...
v4.0.32
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20e13445f8 | ||
|
|
e780994146 | ||
|
|
f9a0de0f16 | ||
|
|
3eb62880fe | ||
|
|
e2612a3d88 | ||
|
|
530db9e2e1 | ||
|
|
c12b1fd8f2 | ||
|
|
bab1982c30 | ||
|
|
b0ee9fa519 | ||
|
|
d8cc69501e | ||
|
|
2eb458cf42 | ||
|
|
3832422bc9 | ||
|
|
4d96c12e7b | ||
|
|
f56997bf15 | ||
|
|
2bd9694348 | ||
|
|
8b6c237d74 | ||
|
|
0fc6d2a5e5 | ||
|
|
0ce3b182a8 | ||
|
|
5c4f7c33fd | ||
|
|
e8c66e4597 | ||
|
|
de1d9b98ec | ||
|
|
58e07a7481 | ||
|
|
3880cdc1af | ||
|
|
94c557bfed | ||
|
|
523cc2cb7e | ||
|
|
af6d81255c | ||
|
|
b5bbb72756 | ||
|
|
92c52817e6 | ||
|
|
076cfc1eae | ||
|
|
25e4b6c19d | ||
|
|
bae38de0d3 | ||
|
|
c9f623b12b | ||
|
|
3491f0ebff | ||
|
|
ff4a9e1dd2 | ||
|
|
f8ec59f7e1 | ||
|
|
571a577231 | ||
|
|
e3b451c641 | ||
|
|
2d6a195d52 | ||
|
|
577cc90a34 | ||
|
|
ecd3a61a86 | ||
|
|
b4cb30e2a1 | ||
|
|
d9b8891803 | ||
|
|
6db2c0a8cb | ||
|
|
0caf0d4052 | ||
|
|
95f2f7da0f | ||
|
|
d2a1459768 | ||
|
|
f38dda8e02 | ||
|
|
189ef09938 | ||
|
|
b79832566c | ||
|
|
6841ef264c | ||
|
|
425f74e763 | ||
|
|
d59b2b4c41 | ||
|
|
2323dd0daa | ||
|
|
207d13c453 | ||
|
|
357173848a | ||
|
|
535ec91141 | ||
|
|
8a0b526391 | ||
|
|
1b62c064ad | ||
|
|
81ae4d3e7b | ||
|
|
a83e035845 | ||
|
|
96452ee32b | ||
|
|
811db8bcb9 | ||
|
|
fbd50f3d82 | ||
|
|
faec817d34 | ||
|
|
72ef2cc80e | ||
|
|
8f0690be41 | ||
|
|
672c2dd49c | ||
|
|
3d9fce26b6 | ||
|
|
95124ce70e | ||
|
|
f8d4855f26 | ||
|
|
e303fafefc | ||
|
|
f00c400993 | ||
|
|
bb6428848f | ||
|
|
df97200a41 | ||
|
|
c8146ebb1a | ||
|
|
5390ec93b4 | ||
|
|
3ce671098a | ||
|
|
7e9fb0c667 | ||
|
|
d7d09c2ac7 | ||
|
|
86175a04c3 | ||
|
|
dbb26f91b4 | ||
|
|
65620464fb | ||
|
|
bd9fdf885f | ||
|
|
7a1992ba91 |
29
.github/workflows/bridge_history_api.yml
vendored
29
.github/workflows/bridge_history_api.yml
vendored
@@ -25,20 +25,20 @@ defaults:
|
|||||||
working-directory: 'bridge-history-api'
|
working-directory: 'bridge-history-api'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# check:
|
check:
|
||||||
# if: github.event.pull_request.draft == false
|
if: github.event.pull_request.draft == false
|
||||||
# runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
# steps:
|
steps:
|
||||||
# - name: Install Go
|
- name: Install Go
|
||||||
# uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
# with:
|
with:
|
||||||
# go-version: 1.19.x
|
go-version: 1.19.x
|
||||||
# - name: Checkout code
|
- name: Checkout code
|
||||||
# uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
# - name: Lint
|
- name: Lint
|
||||||
# run: |
|
run: |
|
||||||
# rm -rf $HOME/.cache/golangci-lint
|
rm -rf $HOME/.cache/golangci-lint
|
||||||
# make lint
|
make lint
|
||||||
test:
|
test:
|
||||||
if: github.event.pull_request.draft == false
|
if: github.event.pull_request.draft == false
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -51,7 +51,6 @@ jobs:
|
|||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Test
|
- name: Test
|
||||||
run: |
|
run: |
|
||||||
go get ./...
|
|
||||||
make test
|
make test
|
||||||
- name: Upload coverage reports to Codecov
|
- name: Upload coverage reports to Codecov
|
||||||
uses: codecov/codecov-action@v3
|
uses: codecov/codecov-action@v3
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
name: Roller
|
name: Prover
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -8,8 +8,8 @@ on:
|
|||||||
- develop
|
- develop
|
||||||
- alpha
|
- alpha
|
||||||
paths:
|
paths:
|
||||||
- 'roller/**'
|
- 'prover/**'
|
||||||
- '.github/workflows/roller.yml'
|
- '.github/workflows/prover.yml'
|
||||||
pull_request:
|
pull_request:
|
||||||
types:
|
types:
|
||||||
- opened
|
- opened
|
||||||
@@ -17,18 +17,37 @@ on:
|
|||||||
- synchronize
|
- synchronize
|
||||||
- ready_for_review
|
- ready_for_review
|
||||||
paths:
|
paths:
|
||||||
- 'roller/**'
|
- 'prover/**'
|
||||||
- '.github/workflows/roller.yml'
|
- '.github/workflows/prover.yml'
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: 'roller'
|
working-directory: 'prover'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
if: github.event.pull_request.draft == false
|
if: github.event.pull_request.draft == false
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: 1.19.x
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Test
|
||||||
|
run: |
|
||||||
|
go test -tags="mock_prover" -v -coverprofile=coverage.txt ./...
|
||||||
|
- name: Upload coverage reports to Codecov
|
||||||
|
uses: codecov/codecov-action@v3
|
||||||
|
env:
|
||||||
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
with:
|
||||||
|
flags: prover
|
||||||
|
compile:
|
||||||
|
if: github.event_name == 'push' # will only be triggered when pushing to main & staging & develop & alpha
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: nightly-2022-12-10
|
toolchain: nightly-2022-12-10
|
||||||
@@ -46,14 +65,7 @@ jobs:
|
|||||||
workspaces: "common/libzkp/impl -> target"
|
workspaces: "common/libzkp/impl -> target"
|
||||||
- name: Test
|
- name: Test
|
||||||
run: |
|
run: |
|
||||||
make roller
|
make prover
|
||||||
go test -tags="mock_prover" -v -coverprofile=coverage.txt ./...
|
|
||||||
- name: Upload coverage reports to Codecov
|
|
||||||
uses: codecov/codecov-action@v3
|
|
||||||
env:
|
|
||||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
|
||||||
with:
|
|
||||||
flags: roller
|
|
||||||
check:
|
check:
|
||||||
if: github.event.pull_request.draft == false
|
if: github.event.pull_request.draft == false
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -80,7 +92,7 @@ jobs:
|
|||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Install goimports
|
- name: Install goimports
|
||||||
run: go install golang.org/x/tools/cmd/goimports
|
run: go install golang.org/x/tools/cmd/goimports
|
||||||
- run: goimports -local scroll-tech/roller/ -w .
|
- run: goimports -local scroll-tech/prover/ -w .
|
||||||
- run: go mod tidy
|
- run: go mod tidy
|
||||||
# If there are any diffs from goimports or go mod tidy, fail.
|
# If there are any diffs from goimports or go mod tidy, fail.
|
||||||
- name: Verify no changes from goimports and go mod tidy
|
- name: Verify no changes from goimports and go mod tidy
|
||||||
80
.github/workflows/prover_stats_api.yml
vendored
Normal file
80
.github/workflows/prover_stats_api.yml
vendored
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
name: ProverStatsAPI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- staging
|
||||||
|
- develop
|
||||||
|
- alpha
|
||||||
|
paths:
|
||||||
|
- 'prover-stats-api/**'
|
||||||
|
- '.github/workflows/prover_stats_api.yml'
|
||||||
|
pull_request:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- reopened
|
||||||
|
- synchronize
|
||||||
|
- ready_for_review
|
||||||
|
paths:
|
||||||
|
- 'prover-stats-api/**'
|
||||||
|
- '.github/workflows/prover_stats_api.yml'
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: 'prover-stats-api'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
if: github.event.pull_request.draft == false
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: 1.19.x
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Lint
|
||||||
|
run: |
|
||||||
|
rm -rf $HOME/.cache/golangci-lint
|
||||||
|
make lint
|
||||||
|
test:
|
||||||
|
if: github.event.pull_request.draft == false
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: 1.19.x
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Test
|
||||||
|
run: |
|
||||||
|
make test
|
||||||
|
- name: Upload coverage reports to Codecov
|
||||||
|
uses: codecov/codecov-action@v3
|
||||||
|
env:
|
||||||
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
with:
|
||||||
|
flags: prover-stats-api
|
||||||
|
goimports-lint:
|
||||||
|
if: github.event.pull_request.draft == false
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: 1.19.x
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Install goimports
|
||||||
|
run: go install golang.org/x/tools/cmd/goimports
|
||||||
|
- run: goimports -local scroll-tech/prover-stats-api/ -w .
|
||||||
|
- run: go mod tidy
|
||||||
|
# If there are any diffs from goimports or go mod tidy, fail.
|
||||||
|
- name: Verify no changes from goimports and go mod tidy
|
||||||
|
run: |
|
||||||
|
if [ -n "$(git status --porcelain)" ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
27
Makefile
27
Makefile
@@ -1,7 +1,5 @@
|
|||||||
.PHONY: check update dev_docker build_test_docker run_test_docker clean
|
.PHONY: check update dev_docker build_test_docker run_test_docker clean
|
||||||
|
|
||||||
ZKP_VERSION=release-1220
|
|
||||||
|
|
||||||
help: ## Display this help message
|
help: ## Display this help message
|
||||||
@grep -h \
|
@grep -h \
|
||||||
-E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
|
-E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
|
||||||
@@ -12,23 +10,23 @@ lint: ## The code's format and security checks.
|
|||||||
make -C common lint
|
make -C common lint
|
||||||
make -C coordinator lint
|
make -C coordinator lint
|
||||||
make -C database lint
|
make -C database lint
|
||||||
make -C roller lint
|
make -C prover lint
|
||||||
make -C bridge-history-api lint
|
make -C bridge-history-api lint
|
||||||
|
|
||||||
update: ## update dependencies
|
update: ## update dependencies
|
||||||
go work sync
|
go work sync
|
||||||
cd $(PWD)/bridge/ && go get -u github.com/scroll-tech/go-ethereum@scroll-v3.1.12 && go mod tidy
|
cd $(PWD)/bridge/ && go get -u github.com/scroll-tech/go-ethereum@scroll-v4.1.0 && go mod tidy
|
||||||
cd $(PWD)/bridge-history-api/ && go get -u github.com/ethereum/go-ethereum@latest && go mod tidy
|
cd $(PWD)/bridge-history-api/ && go get -u github.com/ethereum/go-ethereum@latest && go mod tidy
|
||||||
cd $(PWD)/common/ && go get -u github.com/scroll-tech/go-ethereum@scroll-v3.1.12 && go mod tidy
|
cd $(PWD)/common/ && go get -u github.com/scroll-tech/go-ethereum@scroll-v4.1.0 && go mod tidy
|
||||||
cd $(PWD)/coordinator/ && go get -u github.com/scroll-tech/go-ethereum@scroll-v3.1.12 && go mod tidy
|
cd $(PWD)/coordinator/ && go get -u github.com/scroll-tech/go-ethereum@scroll-v4.1.0 && go mod tidy
|
||||||
cd $(PWD)/database/ && go get -u github.com/scroll-tech/go-ethereum@scroll-v3.1.12 && go mod tidy
|
cd $(PWD)/database/ && go get -u github.com/scroll-tech/go-ethereum@scroll-v4.1.0 && go mod tidy
|
||||||
cd $(PWD)/roller/ && go get -u github.com/scroll-tech/go-ethereum@scroll-v3.1.12 && go mod tidy
|
cd $(PWD)/prover/ && go get -u github.com/scroll-tech/go-ethereum@scroll-v4.1.0 && go mod tidy
|
||||||
goimports -local $(PWD)/bridge/ -w .
|
goimports -local $(PWD)/bridge/ -w .
|
||||||
goimports -local $(PWD)/bridge-history-api/ -w .
|
goimports -local $(PWD)/bridge-history-api/ -w .
|
||||||
goimports -local $(PWD)/common/ -w .
|
goimports -local $(PWD)/common/ -w .
|
||||||
goimports -local $(PWD)/coordinator/ -w .
|
goimports -local $(PWD)/coordinator/ -w .
|
||||||
goimports -local $(PWD)/database/ -w .
|
goimports -local $(PWD)/database/ -w .
|
||||||
goimports -local $(PWD)/roller/ -w .
|
goimports -local $(PWD)/prover/ -w .
|
||||||
|
|
||||||
dev_docker: ## build docker images for development/testing usages
|
dev_docker: ## build docker images for development/testing usages
|
||||||
docker build -t scroll_l1geth ./common/docker/l1geth/
|
docker build -t scroll_l1geth ./common/docker/l1geth/
|
||||||
@@ -40,16 +38,5 @@ build_test_docker: ## build Docker image for local testing on M1/M2 Silicon Mac
|
|||||||
run_test_docker: ## run Docker image for local testing on M1/M2 Silicon Mac
|
run_test_docker: ## run Docker image for local testing on M1/M2 Silicon Mac
|
||||||
docker run -it --rm --name scroll_test_container --network=host -v /var/run/docker.sock:/var/run/docker.sock -v $(PWD):/go/src/app scroll_test_image
|
docker run -it --rm --name scroll_test_container --network=host -v /var/run/docker.sock:/var/run/docker.sock -v $(PWD):/go/src/app scroll_test_image
|
||||||
|
|
||||||
|
|
||||||
test_zkp: ## Test zkp prove and verify, roller/prover generates the proof and coordinator/verifier verifies it
|
|
||||||
mkdir -p test_params
|
|
||||||
wget https://circuit-release.s3.us-west-2.amazonaws.com/circuit-release/${ZKP_VERSION}/test_params/params19 -O ./test_params/params19
|
|
||||||
wget https://circuit-release.s3.us-west-2.amazonaws.com/circuit-release/${ZKP_VERSION}/test_params/params26 -O ./test_params/params26
|
|
||||||
wget https://circuit-release.s3.us-west-2.amazonaws.com/circuit-release/${ZKP_VERSION}/test_seed -O test_seed
|
|
||||||
rm -rf ./roller/assets/test_params && mv test_params ./roller/assets/ && mv test_seed ./roller/assets/
|
|
||||||
cd ./roller && make test-gpu-prover
|
|
||||||
rm -rf ./coordinator/assets/test_params && mv ./roller/assets/test_params ./coordinator/assets/ && mv ./roller/assets/agg_proof ./coordinator/assets/
|
|
||||||
cd ./coordinator && make test-gpu-verifier
|
|
||||||
|
|
||||||
clean: ## Empty out the bin folder
|
clean: ## Empty out the bin folder
|
||||||
@rm -rf build/bin
|
@rm -rf build/bin
|
||||||
|
|||||||
@@ -39,10 +39,18 @@ var (
|
|||||||
L2WithdrawERC721Sig common.Hash
|
L2WithdrawERC721Sig common.Hash
|
||||||
L2WithdrawERC1155Sig common.Hash
|
L2WithdrawERC1155Sig common.Hash
|
||||||
|
|
||||||
|
// batch nft sigs
|
||||||
|
L1BatchDepositERC721Sig common.Hash
|
||||||
|
L1BatchDepositERC1155Sig common.Hash
|
||||||
|
L2BatchWithdrawERC721Sig common.Hash
|
||||||
|
L2BatchWithdrawERC1155Sig common.Hash
|
||||||
|
|
||||||
// scroll mono repo
|
// scroll mono repo
|
||||||
|
|
||||||
// ScrollChainABI holds information about ScrollChain's context and available invokable methods.
|
// ScrollChainABI holds information about ScrollChain's context and available invokable methods.
|
||||||
ScrollChainABI *abi.ABI
|
ScrollChainABI *abi.ABI
|
||||||
|
// ScrollChainV2ABI holds information about ScrollChainV2's context and available invokable methods.
|
||||||
|
ScrollChainV2ABI *abi.ABI
|
||||||
// L1ScrollMessengerABI holds information about L1ScrollMessenger's context and available invokable methods.
|
// L1ScrollMessengerABI holds information about L1ScrollMessenger's context and available invokable methods.
|
||||||
L1ScrollMessengerABI *abi.ABI
|
L1ScrollMessengerABI *abi.ABI
|
||||||
// L1MessageQueueABI holds information about L1MessageQueue contract's context and available invokable methods.
|
// L1MessageQueueABI holds information about L1MessageQueue contract's context and available invokable methods.
|
||||||
@@ -114,8 +122,15 @@ func init() {
|
|||||||
L2ERC1155GatewayABI, _ = L2ERC1155GatewayMetaData.GetAbi()
|
L2ERC1155GatewayABI, _ = L2ERC1155GatewayMetaData.GetAbi()
|
||||||
L2WithdrawERC1155Sig = L2ERC1155GatewayABI.Events["WithdrawERC1155"].ID
|
L2WithdrawERC1155Sig = L2ERC1155GatewayABI.Events["WithdrawERC1155"].ID
|
||||||
|
|
||||||
|
// batch nft events
|
||||||
|
L1BatchDepositERC721Sig = L1ERC721GatewayABI.Events["BatchDepositERC721"].ID
|
||||||
|
L1BatchDepositERC1155Sig = L1ERC1155GatewayABI.Events["BatchDepositERC1155"].ID
|
||||||
|
L2BatchWithdrawERC721Sig = L2ERC721GatewayABI.Events["BatchWithdrawERC721"].ID
|
||||||
|
L2BatchWithdrawERC1155Sig = L2ERC1155GatewayABI.Events["BatchWithdrawERC1155"].ID
|
||||||
|
|
||||||
// scroll monorepo
|
// scroll monorepo
|
||||||
ScrollChainABI, _ = ScrollChainMetaData.GetAbi()
|
ScrollChainABI, _ = ScrollChainMetaData.GetAbi()
|
||||||
|
ScrollChainV2ABI, _ = ScrollChainV2MetaData.GetAbi()
|
||||||
L1ScrollMessengerABI, _ = L1ScrollMessengerMetaData.GetAbi()
|
L1ScrollMessengerABI, _ = L1ScrollMessengerMetaData.GetAbi()
|
||||||
L1MessageQueueABI, _ = L1MessageQueueMetaData.GetAbi()
|
L1MessageQueueABI, _ = L1MessageQueueMetaData.GetAbi()
|
||||||
L2GasPriceOracleABI, _ = L2GasPriceOracleMetaData.GetAbi()
|
L2GasPriceOracleABI, _ = L2GasPriceOracleMetaData.GetAbi()
|
||||||
@@ -198,6 +213,11 @@ var ScrollChainMetaData = &bind.MetaData{
|
|||||||
ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"batchHash\",\"type\":\"bytes32\"}],\"name\":\"CommitBatch\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"batchHash\",\"type\":\"bytes32\"}],\"name\":\"FinalizeBatch\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"batchHash\",\"type\":\"bytes32\"}],\"name\":\"RevertBatch\",\"type\":\"event\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"baseFee\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"gasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"numTransactions\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"numL1Messages\",\"type\":\"uint16\"}],\"internalType\":\"structIScrollChain.BlockContext[]\",\"name\":\"blocks\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes32\",\"name\":\"prevStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"newStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"withdrawTrieRoot\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"batchIndex\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"parentBatchHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"l2Transactions\",\"type\":\"bytes\"}],\"internalType\":\"structIScrollChain.Batch\",\"name\":\"batch\",\"type\":\"tuple\"}],\"name\":\"commitBatch\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"baseFee\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"gasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"numTransactions\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"numL1Messages\",\"type\":\"uint16\"}],\"internalType\":\"structIScrollChain.BlockContext[]\",\"name\":\"blocks\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes32\",\"name\":\"prevStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"newStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"withdrawTrieRoot\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"batchIndex\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"parentBatchHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"l2Transactions\",\"type\":\"bytes\"}],\"internalType\":\"structIScrollChain.Batch[]\",\"name\":\"batches\",\"type\":\"tuple[]\"}],\"name\":\"commitBatches\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"batchId\",\"type\":\"bytes32\"},{\"internalType\":\"uint256[]\",\"name\":\"proof\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256[]\",\"name\":\"instances\",\"type\":\"uint256[]\"}],\"name\":\"finalizeBatchWithProof\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"batchHash\",\"type\":\"bytes32\"}],\"name\":\"getL2MessageRoot\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"batchHash\",\"type\":\"bytes32\"}],\"name\":\"isBatchFinalized\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"batchId\",\"type\":\"bytes32\"}],\"name\":\"revertBatch\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
|
ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"batchHash\",\"type\":\"bytes32\"}],\"name\":\"CommitBatch\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"batchHash\",\"type\":\"bytes32\"}],\"name\":\"FinalizeBatch\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"batchHash\",\"type\":\"bytes32\"}],\"name\":\"RevertBatch\",\"type\":\"event\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"baseFee\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"gasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"numTransactions\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"numL1Messages\",\"type\":\"uint16\"}],\"internalType\":\"structIScrollChain.BlockContext[]\",\"name\":\"blocks\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes32\",\"name\":\"prevStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"newStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"withdrawTrieRoot\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"batchIndex\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"parentBatchHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"l2Transactions\",\"type\":\"bytes\"}],\"internalType\":\"structIScrollChain.Batch\",\"name\":\"batch\",\"type\":\"tuple\"}],\"name\":\"commitBatch\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"baseFee\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"gasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"numTransactions\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"numL1Messages\",\"type\":\"uint16\"}],\"internalType\":\"structIScrollChain.BlockContext[]\",\"name\":\"blocks\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes32\",\"name\":\"prevStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"newStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"withdrawTrieRoot\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"batchIndex\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"parentBatchHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"l2Transactions\",\"type\":\"bytes\"}],\"internalType\":\"structIScrollChain.Batch[]\",\"name\":\"batches\",\"type\":\"tuple[]\"}],\"name\":\"commitBatches\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"batchId\",\"type\":\"bytes32\"},{\"internalType\":\"uint256[]\",\"name\":\"proof\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256[]\",\"name\":\"instances\",\"type\":\"uint256[]\"}],\"name\":\"finalizeBatchWithProof\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"batchHash\",\"type\":\"bytes32\"}],\"name\":\"getL2MessageRoot\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"batchHash\",\"type\":\"bytes32\"}],\"name\":\"isBatchFinalized\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"batchId\",\"type\":\"bytes32\"}],\"name\":\"revertBatch\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ScrollChainV2MetaData contains all meta data concerning the ScrollChain contract.
|
||||||
|
var ScrollChainV2MetaData = &bind.MetaData{
|
||||||
|
ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"batchHash\",\"type\":\"bytes32\"}],\"name\":\"CommitBatch\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"batchHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"withdrawRoot\",\"type\":\"bytes32\"}],\"name\":\"FinalizeBatch\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"batchHash\",\"type\":\"bytes32\"}],\"name\":\"RevertBatch\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"parentBatchHeader\",\"type\":\"bytes\"},{\"internalType\":\"bytes[]\",\"name\":\"chunks\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes\",\"name\":\"skippedL1MessageBitmap\",\"type\":\"bytes\"}],\"name\":\"commitBatch\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"committedBatches\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"batchHeader\",\"type\":\"bytes\"},{\"internalType\":\"bytes32\",\"name\":\"prevStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"postStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"withdrawRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"aggrProof\",\"type\":\"bytes\"}],\"name\":\"finalizeBatchWithProof\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"finalizedStateRoots\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"isBatchFinalized\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"batchHeader\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"revertBatch\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"withdrawRoots\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]",
|
||||||
|
}
|
||||||
|
|
||||||
// L1ScrollMessengerMetaData contains all meta data concerning the L1ScrollMessenger contract.
|
// L1ScrollMessengerMetaData contains all meta data concerning the L1ScrollMessenger contract.
|
||||||
var L1ScrollMessengerMetaData = &bind.MetaData{
|
var L1ScrollMessengerMetaData = &bind.MetaData{
|
||||||
ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"messageHash\",\"type\":\"bytes32\"}],\"name\":\"FailedRelayedMessage\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"messageHash\",\"type\":\"bytes32\"}],\"name\":\"RelayedMessage\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"messageNonce\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"}],\"name\":\"SentMessage\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"batchHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"merkleProof\",\"type\":\"bytes\"}],\"internalType\":\"structIL1ScrollMessenger.L2MessageProof\",\"name\":\"proof\",\"type\":\"tuple\"}],\"name\":\"relayMessageWithProof\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"queueIndex\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"},{\"internalType\":\"uint32\",\"name\":\"oldGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"newGasLimit\",\"type\":\"uint32\"}],\"name\":\"replayMessage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"}],\"name\":\"sendMessage\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"xDomainMessageSender\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]",
|
ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"messageHash\",\"type\":\"bytes32\"}],\"name\":\"FailedRelayedMessage\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"messageHash\",\"type\":\"bytes32\"}],\"name\":\"RelayedMessage\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"messageNonce\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"}],\"name\":\"SentMessage\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"batchHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"merkleProof\",\"type\":\"bytes\"}],\"internalType\":\"structIL1ScrollMessenger.L2MessageProof\",\"name\":\"proof\",\"type\":\"tuple\"}],\"name\":\"relayMessageWithProof\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"queueIndex\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"},{\"internalType\":\"uint32\",\"name\":\"oldGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"newGasLimit\",\"type\":\"uint32\"}],\"name\":\"replayMessage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"}],\"name\":\"sendMessage\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"xDomainMessageSender\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]",
|
||||||
@@ -269,6 +289,23 @@ type ERC1155MessageEvent struct {
|
|||||||
Amount *big.Int
|
Amount *big.Int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BatchERC721MessageEvent struct {
|
||||||
|
L1Token common.Address
|
||||||
|
L2Token common.Address
|
||||||
|
From common.Address
|
||||||
|
To common.Address
|
||||||
|
TokenIDs []*big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
type BatchERC1155MessageEvent struct {
|
||||||
|
L1Token common.Address
|
||||||
|
L2Token common.Address
|
||||||
|
From common.Address
|
||||||
|
To common.Address
|
||||||
|
TokenIDs []*big.Int
|
||||||
|
TokenAmounts []*big.Int
|
||||||
|
}
|
||||||
|
|
||||||
// scroll monorepo
|
// scroll monorepo
|
||||||
|
|
||||||
// L1SentMessageEvent represents a SentMessage event raised by the L1ScrollMessenger contract.
|
// L1SentMessageEvent represents a SentMessage event raised by the L1ScrollMessenger contract.
|
||||||
@@ -337,6 +374,11 @@ type IScrollChainBatch struct {
|
|||||||
L2Transactions []byte
|
L2Transactions []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// L1CommitBatchEvent represents a CommitBatch event raised by the ScrollChain contract.
|
||||||
|
type L1CommitBatchEvent struct {
|
||||||
|
BatchHash common.Hash
|
||||||
|
}
|
||||||
|
|
||||||
// IScrollChainBlockContext is an auto generated low-level Go binding around an user-defined struct.
|
// IScrollChainBlockContext is an auto generated low-level Go binding around an user-defined struct.
|
||||||
type IScrollChainBlockContext struct {
|
type IScrollChainBlockContext struct {
|
||||||
BlockHash common.Hash
|
BlockHash common.Hash
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
@@ -9,41 +10,60 @@ import (
|
|||||||
"github.com/kataras/iris/v12"
|
"github.com/kataras/iris/v12"
|
||||||
"github.com/kataras/iris/v12/mvc"
|
"github.com/kataras/iris/v12/mvc"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"bridge-history-api/config"
|
"bridge-history-api/config"
|
||||||
"bridge-history-api/controller"
|
"bridge-history-api/controller"
|
||||||
"bridge-history-api/db"
|
|
||||||
"bridge-history-api/service"
|
"bridge-history-api/service"
|
||||||
cutils "bridge-history-api/utils"
|
cutils "bridge-history-api/utils"
|
||||||
|
|
||||||
|
"scroll-tech/common/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
app *cli.App
|
app *cli.App
|
||||||
|
db *gorm.DB
|
||||||
|
subCtx context.Context
|
||||||
)
|
)
|
||||||
|
|
||||||
var database db.OrmFactory
|
|
||||||
|
|
||||||
func pong(ctx iris.Context) {
|
func pong(ctx iris.Context) {
|
||||||
ctx.WriteString("pong")
|
_, err := ctx.WriteString("pong")
|
||||||
|
if err != nil {
|
||||||
|
log.Error("failed to write pong", "err", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupQueryByAddressHandler(backend_app *mvc.Application) {
|
func setupQueryByAddressHandler(backendApp *mvc.Application) {
|
||||||
// Register Dependencies.
|
// Register Dependencies.
|
||||||
backend_app.Register(
|
backendApp.Register(
|
||||||
database,
|
subCtx,
|
||||||
|
db,
|
||||||
service.NewHistoryService,
|
service.NewHistoryService,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Register Controllers.
|
// Register Controllers.
|
||||||
backend_app.Handle(new(controller.QueryAddressController))
|
backendApp.Handle(new(controller.QueryAddressController))
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupQueryByHashHandler(backend_app *mvc.Application) {
|
func setupQueryClaimableHandler(backendApp *mvc.Application) {
|
||||||
backend_app.Register(
|
// Register Dependencies.
|
||||||
database,
|
backendApp.Register(
|
||||||
|
subCtx,
|
||||||
|
db,
|
||||||
service.NewHistoryService,
|
service.NewHistoryService,
|
||||||
)
|
)
|
||||||
backend_app.Handle(new(controller.QueryHashController))
|
|
||||||
|
// Register Controllers.
|
||||||
|
backendApp.Handle(new(controller.QueryClaimableController))
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupQueryByHashHandler(backendApp *mvc.Application) {
|
||||||
|
backendApp.Register(
|
||||||
|
subCtx,
|
||||||
|
db,
|
||||||
|
service.NewHistoryService,
|
||||||
|
)
|
||||||
|
backendApp.Handle(new(controller.QueryHashController))
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -72,17 +92,29 @@ func action(ctx *cli.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Crit("failed to load config file", "config file", cfgFile, "error", err)
|
log.Crit("failed to load config file", "config file", cfgFile, "error", err)
|
||||||
}
|
}
|
||||||
database, err = db.NewOrmFactory(cfg)
|
dbCfg := &database.Config{
|
||||||
if err != nil {
|
DriverName: cfg.DB.DriverName,
|
||||||
log.Crit("can not connect to database", "err", err)
|
DSN: cfg.DB.DSN,
|
||||||
|
MaxOpenNum: cfg.DB.MaxOpenNum,
|
||||||
|
MaxIdleNum: cfg.DB.MaxIdleNum,
|
||||||
}
|
}
|
||||||
defer database.Close()
|
db, err = database.InitDB(dbCfg)
|
||||||
|
if err != nil {
|
||||||
|
log.Crit("failed to init db", "err", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if deferErr := database.CloseDB(db); deferErr != nil {
|
||||||
|
log.Error("failed to close db", "err", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
subCtx = ctx.Context
|
||||||
bridgeApp := iris.New()
|
bridgeApp := iris.New()
|
||||||
bridgeApp.UseRouter(corsOptions)
|
bridgeApp.UseRouter(corsOptions)
|
||||||
bridgeApp.Get("/ping", pong).Describe("healthcheck")
|
bridgeApp.Get("/ping", pong).Describe("healthcheck")
|
||||||
|
|
||||||
mvc.Configure(bridgeApp.Party("/api/txs"), setupQueryByAddressHandler)
|
mvc.Configure(bridgeApp.Party("/api/txs"), setupQueryByAddressHandler)
|
||||||
mvc.Configure(bridgeApp.Party("/api/txsbyhashes"), setupQueryByHashHandler)
|
mvc.Configure(bridgeApp.Party("/api/txsbyhashes"), setupQueryByHashHandler)
|
||||||
|
mvc.Configure(bridgeApp.Party("/api/claimable"), setupQueryClaimableHandler)
|
||||||
|
|
||||||
// TODO: make debug mode configurable
|
// TODO: make debug mode configurable
|
||||||
err = bridgeApp.Listen(cfg.Server.HostPort, iris.WithLogLevel("debug"))
|
err = bridgeApp.Listen(cfg.Server.HostPort, iris.WithLogLevel("debug"))
|
||||||
|
|||||||
@@ -12,9 +12,12 @@ import (
|
|||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
"bridge-history-api/config"
|
"bridge-history-api/config"
|
||||||
"bridge-history-api/cross_msg"
|
"bridge-history-api/crossmsg"
|
||||||
"bridge-history-api/db"
|
"bridge-history-api/crossmsg/messageproof"
|
||||||
|
"bridge-history-api/orm"
|
||||||
cutils "bridge-history-api/utils"
|
cutils "bridge-history-api/utils"
|
||||||
|
|
||||||
|
"scroll-tech/common/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -53,14 +56,29 @@ func action(ctx *cli.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Crit("failed to connect l2 geth", "config file", cfgFile, "error", err)
|
log.Crit("failed to connect l2 geth", "config file", cfgFile, "error", err)
|
||||||
}
|
}
|
||||||
db, err := db.NewOrmFactory(cfg)
|
|
||||||
|
dbCfg := &database.Config{
|
||||||
|
DriverName: cfg.DB.DriverName,
|
||||||
|
DSN: cfg.DB.DSN,
|
||||||
|
MaxOpenNum: cfg.DB.MaxOpenNum,
|
||||||
|
MaxIdleNum: cfg.DB.MaxIdleNum,
|
||||||
|
}
|
||||||
|
db, err := database.InitDB(dbCfg)
|
||||||
|
if err != nil {
|
||||||
|
log.Crit("failed to init db", "err", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if deferErr := database.CloseDB(db); deferErr != nil {
|
||||||
|
log.Error("failed to close db", "err", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Crit("failed to connect to db", "config file", cfgFile, "error", err)
|
log.Crit("failed to connect to db", "config file", cfgFile, "error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
l1worker := &cross_msg.FetchEventWorker{F: cross_msg.L1FetchAndSaveEvents, G: cross_msg.GetLatestL1ProcessedHeight, Name: "L1 events fetch Worker"}
|
l1worker := &crossmsg.FetchEventWorker{F: crossmsg.L1FetchAndSaveEvents, G: crossmsg.GetLatestL1ProcessedHeight, Name: "L1 events fetch Worker"}
|
||||||
|
|
||||||
l2worker := &cross_msg.FetchEventWorker{F: cross_msg.L2FetchAndSaveEvents, G: cross_msg.GetLatestL2ProcessedHeight, Name: "L2 events fetch Worker"}
|
l2worker := &crossmsg.FetchEventWorker{F: crossmsg.L2FetchAndSaveEvents, G: crossmsg.GetLatestL2ProcessedHeight, Name: "L2 events fetch Worker"}
|
||||||
|
|
||||||
l1AddressList := []common.Address{
|
l1AddressList := []common.Address{
|
||||||
common.HexToAddress(cfg.L1.CustomERC20GatewayAddr),
|
common.HexToAddress(cfg.L1.CustomERC20GatewayAddr),
|
||||||
@@ -82,7 +100,7 @@ func action(ctx *cli.Context) error {
|
|||||||
common.HexToAddress(cfg.L2.WETHGatewayAddr),
|
common.HexToAddress(cfg.L2.WETHGatewayAddr),
|
||||||
}
|
}
|
||||||
|
|
||||||
l1crossMsgFetcher, err := cross_msg.NewCrossMsgFetcher(subCtx, cfg.L1, db, l1client, l1worker, l1AddressList, cross_msg.L1ReorgHandling)
|
l1crossMsgFetcher, err := crossmsg.NewMsgFetcher(subCtx, cfg.L1, db, l1client, l1worker, l1AddressList, crossmsg.L1ReorgHandling)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Crit("failed to create l1 cross message fetcher", "error", err)
|
log.Crit("failed to create l1 cross message fetcher", "error", err)
|
||||||
}
|
}
|
||||||
@@ -90,7 +108,7 @@ func action(ctx *cli.Context) error {
|
|||||||
go l1crossMsgFetcher.Start()
|
go l1crossMsgFetcher.Start()
|
||||||
defer l1crossMsgFetcher.Stop()
|
defer l1crossMsgFetcher.Stop()
|
||||||
|
|
||||||
l2crossMsgFetcher, err := cross_msg.NewCrossMsgFetcher(subCtx, cfg.L2, db, l2client, l2worker, l2AddressList, cross_msg.L2ReorgHandling)
|
l2crossMsgFetcher, err := crossmsg.NewMsgFetcher(subCtx, cfg.L2, db, l2client, l2worker, l2AddressList, crossmsg.L2ReorgHandling)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Crit("failed to create l2 cross message fetcher", "error", err)
|
log.Crit("failed to create l2 cross message fetcher", "error", err)
|
||||||
}
|
}
|
||||||
@@ -98,13 +116,22 @@ func action(ctx *cli.Context) error {
|
|||||||
go l2crossMsgFetcher.Start()
|
go l2crossMsgFetcher.Start()
|
||||||
defer l2crossMsgFetcher.Stop()
|
defer l2crossMsgFetcher.Stop()
|
||||||
|
|
||||||
l1BlocktimeFetcher := cross_msg.NewBlocktimestampFetcher(subCtx, uint(cfg.L1.Confirmation), int(cfg.L1.BlockTime), l1client, db.UpdateL1Blocktimestamp, db.GetL1EarliestNoBlocktimestampHeight)
|
CrossMsgOrm := orm.NewCrossMsg(db)
|
||||||
go l1BlocktimeFetcher.Start()
|
|
||||||
defer l1BlocktimeFetcher.Stop()
|
|
||||||
|
|
||||||
l2BlocktimeFetcher := cross_msg.NewBlocktimestampFetcher(subCtx, uint(cfg.L2.Confirmation), int(cfg.L2.BlockTime), l2client, db.UpdateL2Blocktimestamp, db.GetL2EarliestNoBlocktimestampHeight)
|
// BlockTimestamp fetcher for l1 and l2
|
||||||
go l2BlocktimeFetcher.Start()
|
l1BlockTimeFetcher := crossmsg.NewBlockTimestampFetcher(subCtx, cfg.L1.Confirmation, int(cfg.L1.BlockTime), l1client, CrossMsgOrm.UpdateL1BlockTimestamp, CrossMsgOrm.GetL1EarliestNoBlockTimestampHeight)
|
||||||
defer l2BlocktimeFetcher.Stop()
|
go l1BlockTimeFetcher.Start()
|
||||||
|
defer l1BlockTimeFetcher.Stop()
|
||||||
|
|
||||||
|
l2BlockTimeFetcher := crossmsg.NewBlockTimestampFetcher(subCtx, cfg.L2.Confirmation, int(cfg.L2.BlockTime), l2client, CrossMsgOrm.UpdateL2BlockTimestamp, CrossMsgOrm.GetL2EarliestNoBlockTimestampHeight)
|
||||||
|
go l2BlockTimeFetcher.Start()
|
||||||
|
defer l2BlockTimeFetcher.Stop()
|
||||||
|
|
||||||
|
// Proof updater and batch fetcher
|
||||||
|
l2msgProofUpdater := messageproof.NewMsgProofUpdater(subCtx, cfg.L1.Confirmation, cfg.BatchInfoFetcher.BatchIndexStartBlock, db)
|
||||||
|
batchFetcher := crossmsg.NewBatchInfoFetcher(subCtx, common.HexToAddress(cfg.BatchInfoFetcher.ScrollChainAddr), cfg.BatchInfoFetcher.BatchIndexStartBlock, cfg.L1.Confirmation, int(cfg.L1.BlockTime), l1client, db, l2msgProofUpdater)
|
||||||
|
go batchFetcher.Start()
|
||||||
|
defer batchFetcher.Stop()
|
||||||
|
|
||||||
// Catch CTRL-C to ensure a graceful shutdown.
|
// Catch CTRL-C to ensure a graceful shutdown.
|
||||||
interrupt := make(chan os.Signal, 1)
|
interrupt := make(chan os.Signal, 1)
|
||||||
|
|||||||
@@ -2,13 +2,14 @@ package app
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"bridge-history-api/config"
|
"bridge-history-api/config"
|
||||||
"bridge-history-api/db"
|
"bridge-history-api/orm/migrate"
|
||||||
"bridge-history-api/db/migrate"
|
|
||||||
"bridge-history-api/utils"
|
"bridge-history-api/utils"
|
||||||
|
|
||||||
|
"scroll-tech/common/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getConfig(ctx *cli.Context) (*config.Config, error) {
|
func getConfig(ctx *cli.Context) (*config.Config, error) {
|
||||||
@@ -20,14 +21,14 @@ func getConfig(ctx *cli.Context) (*config.Config, error) {
|
|||||||
return dbCfg, nil
|
return dbCfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func initDB(dbCfg *config.Config) (*sqlx.DB, error) {
|
func initDB(dbCfg *config.DBConfig) (*gorm.DB, error) {
|
||||||
factory, err := db.NewOrmFactory(dbCfg)
|
cfg := &database.Config{
|
||||||
if err != nil {
|
DriverName: dbCfg.DriverName,
|
||||||
return nil, err
|
DSN: dbCfg.DSN,
|
||||||
|
MaxOpenNum: dbCfg.MaxOpenNum,
|
||||||
|
MaxIdleNum: dbCfg.MaxIdleNum,
|
||||||
}
|
}
|
||||||
log.Debug("Got db config from env", "driver name", dbCfg.DB.DriverName, "dsn", dbCfg.DB.DSN)
|
return database.InitDB(cfg)
|
||||||
|
|
||||||
return factory.GetDB(), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// resetDB clean or reset database.
|
// resetDB clean or reset database.
|
||||||
@@ -36,11 +37,15 @@ func resetDB(ctx *cli.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
db, err := initDB(cfg)
|
gormDB, err := initDB(cfg.DB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = migrate.ResetDB(db.DB)
|
db, err := gormDB.DB()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = migrate.ResetDB(db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -54,12 +59,15 @@ func checkDBStatus(ctx *cli.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
db, err := initDB(cfg)
|
gormDB, err := initDB(cfg.DB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
db, err := gormDB.DB()
|
||||||
return migrate.Status(db.DB)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return migrate.Status(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
// dbVersion return the latest version
|
// dbVersion return the latest version
|
||||||
@@ -68,12 +76,15 @@ func dbVersion(ctx *cli.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
db, err := initDB(cfg)
|
gormDB, err := initDB(cfg.DB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
db, err := gormDB.DB()
|
||||||
version, err := migrate.Current(db.DB)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
version, err := migrate.Current(db)
|
||||||
log.Info("show database version", "db version", version)
|
log.Info("show database version", "db version", version)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
@@ -85,12 +96,15 @@ func migrateDB(ctx *cli.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
db, err := initDB(cfg)
|
gormDB, err := initDB(cfg.DB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
db, err := gormDB.DB()
|
||||||
return migrate.Migrate(db.DB)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return migrate.Migrate(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
// rollbackDB rollback db by version
|
// rollbackDB rollback db by version
|
||||||
@@ -99,10 +113,14 @@ func rollbackDB(ctx *cli.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
db, err := initDB(cfg)
|
gormDB, err := initDB(cfg.DB)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
db, err := gormDB.DB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
version := ctx.Int64("version")
|
version := ctx.Int64("version")
|
||||||
return migrate.Rollback(db.DB, &version)
|
return migrate.Rollback(db, &version)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
{
|
{
|
||||||
|
"batchInfoFetcher": {
|
||||||
|
"batchIndexStartBlock": 9091265,
|
||||||
|
"ScrollChainAddr": "0xcD00DB804C819175B381b2B44Aa16A391c8a01D6"
|
||||||
|
},
|
||||||
"l1": {
|
"l1": {
|
||||||
"confirmation": 64,
|
"confirmation": 64,
|
||||||
"endpoint": "https://rpc.ankr.com/eth_goerli",
|
"endpoint": "https://rpc.ankr.com/eth_goerli",
|
||||||
|
|||||||
@@ -6,6 +6,12 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// BatchInfoFetcherConfig is the configuration of BatchInfoFetcher
|
||||||
|
type BatchInfoFetcherConfig struct {
|
||||||
|
BatchIndexStartBlock uint64 `json:"batchIndexStartBlock"`
|
||||||
|
ScrollChainAddr string `json:"ScrollChainAddr"`
|
||||||
|
}
|
||||||
|
|
||||||
// DBConfig db config
|
// DBConfig db config
|
||||||
type DBConfig struct {
|
type DBConfig struct {
|
||||||
// data source name
|
// data source name
|
||||||
@@ -16,6 +22,7 @@ type DBConfig struct {
|
|||||||
MaxIdleNum int `json:"maxIdleNum"`
|
MaxIdleNum int `json:"maxIdleNum"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LayerConfig is the configuration of Layer1/Layer2
|
||||||
type LayerConfig struct {
|
type LayerConfig struct {
|
||||||
Confirmation uint64 `json:"confirmation"`
|
Confirmation uint64 `json:"confirmation"`
|
||||||
Endpoint string `json:"endpoint"`
|
Endpoint string `json:"endpoint"`
|
||||||
@@ -30,6 +37,7 @@ type LayerConfig struct {
|
|||||||
CustomERC20GatewayAddr string `json:"CustomERC20GatewayAddr"`
|
CustomERC20GatewayAddr string `json:"CustomERC20GatewayAddr"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ServerConfig is the configuration of the bridge history backend server port
|
||||||
type ServerConfig struct {
|
type ServerConfig struct {
|
||||||
HostPort string `json:"hostPort"`
|
HostPort string `json:"hostPort"`
|
||||||
}
|
}
|
||||||
@@ -41,8 +49,9 @@ type Config struct {
|
|||||||
L2 *LayerConfig `json:"l2"`
|
L2 *LayerConfig `json:"l2"`
|
||||||
|
|
||||||
// data source name
|
// data source name
|
||||||
DB *DBConfig `json:"db"`
|
DB *DBConfig `json:"db"`
|
||||||
Server *ServerConfig `json:"server"`
|
Server *ServerConfig `json:"server"`
|
||||||
|
BatchInfoFetcher *BatchInfoFetcherConfig `json:"batchInfoFetcher"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConfig returns a new instance of Config.
|
// NewConfig returns a new instance of Config.
|
||||||
|
|||||||
@@ -7,16 +7,38 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// QueryAddressController contains the query by address service
|
||||||
type QueryAddressController struct {
|
type QueryAddressController struct {
|
||||||
Service service.HistoryService
|
Service service.HistoryService
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QueryHashController contains the query by hash service
|
||||||
type QueryHashController struct {
|
type QueryHashController struct {
|
||||||
Service service.HistoryService
|
Service service.HistoryService
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QueryClaimableController contains the query claimable txs service
|
||||||
|
type QueryClaimableController struct {
|
||||||
|
Service service.HistoryService
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get defines the http get method behavior for QueryClaimableController
|
||||||
|
func (c *QueryClaimableController) Get(req model.QueryByAddressRequest) (*model.QueryByAddressResponse, error) {
|
||||||
|
txs, total, err := c.Service.GetClaimableTxsByAddress(common.HexToAddress(req.Address), req.Offset, req.Limit)
|
||||||
|
if err != nil {
|
||||||
|
return &model.QueryByAddressResponse{Message: "500", Data: &model.Data{}}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &model.QueryByAddressResponse{Message: "ok",
|
||||||
|
Data: &model.Data{
|
||||||
|
Result: txs,
|
||||||
|
Total: total,
|
||||||
|
}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get defines the http get method behavior for QueryAddressController
|
||||||
func (c *QueryAddressController) Get(req model.QueryByAddressRequest) (*model.QueryByAddressResponse, error) {
|
func (c *QueryAddressController) Get(req model.QueryByAddressRequest) (*model.QueryByAddressResponse, error) {
|
||||||
message, total, err := c.Service.GetTxsByAddress(common.HexToAddress(req.Address), int64(req.Offset), int64(req.Limit))
|
message, total, err := c.Service.GetTxsByAddress(common.HexToAddress(req.Address), req.Offset, req.Limit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &model.QueryByAddressResponse{Message: "500", Data: &model.Data{}}, err
|
return &model.QueryByAddressResponse{Message: "500", Data: &model.Data{}}, err
|
||||||
}
|
}
|
||||||
@@ -28,6 +50,7 @@ func (c *QueryAddressController) Get(req model.QueryByAddressRequest) (*model.Qu
|
|||||||
}}, nil
|
}}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Post defines the http post method behavior for QueryHashController
|
||||||
func (c *QueryHashController) Post(req model.QueryByHashRequest) (*model.QueryByHashResponse, error) {
|
func (c *QueryHashController) Post(req model.QueryByHashRequest) (*model.QueryByHashResponse, error) {
|
||||||
result, err := c.Service.GetTxsByHashes(req.Txs)
|
result, err := c.Service.GetTxsByHashes(req.Txs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
package cross_msg
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"math/big"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/ethclient"
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
type GetEarliestNoBlocktimestampHeightFunc func() (uint64, error)
|
|
||||||
type UpdateBlocktimestampFunc func(height uint64, timestamp time.Time) error
|
|
||||||
|
|
||||||
type BlocktimestampFetcher struct {
|
|
||||||
ctx context.Context
|
|
||||||
confirmation uint
|
|
||||||
blockTimeInSec int
|
|
||||||
client *ethclient.Client
|
|
||||||
updateBlocktimestampFunc UpdateBlocktimestampFunc
|
|
||||||
getEarliestNoBlocktimestampHeightFunc GetEarliestNoBlocktimestampHeightFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewBlocktimestampFetcher(ctx context.Context, confirmation uint, blockTimeInSec int, client *ethclient.Client, updateBlocktimestampFunc UpdateBlocktimestampFunc, getEarliestNoBlocktimestampHeightFunc GetEarliestNoBlocktimestampHeightFunc) *BlocktimestampFetcher {
|
|
||||||
return &BlocktimestampFetcher{
|
|
||||||
ctx: ctx,
|
|
||||||
confirmation: confirmation,
|
|
||||||
blockTimeInSec: blockTimeInSec,
|
|
||||||
client: client,
|
|
||||||
getEarliestNoBlocktimestampHeightFunc: getEarliestNoBlocktimestampHeightFunc,
|
|
||||||
updateBlocktimestampFunc: updateBlocktimestampFunc,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BlocktimestampFetcher) Start() {
|
|
||||||
go func() {
|
|
||||||
tick := time.NewTicker(time.Duration(b.blockTimeInSec) * time.Second)
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-b.ctx.Done():
|
|
||||||
tick.Stop()
|
|
||||||
return
|
|
||||||
case <-tick.C:
|
|
||||||
number, err := b.client.BlockNumber(b.ctx)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Can not get latest block number", "err", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
startHeight, err := b.getEarliestNoBlocktimestampHeightFunc()
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Can not get latest record without block timestamp", "err", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for height := startHeight; number >= height+uint64(b.confirmation) && height > 0; {
|
|
||||||
block, err := b.client.HeaderByNumber(b.ctx, new(big.Int).SetUint64(height))
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Can not get block by number", "err", err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
err = b.updateBlocktimestampFunc(height, time.Unix(int64(block.Time), 0))
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Can not update blocktimstamp into DB ", "err", err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
height, err = b.getEarliestNoBlocktimestampHeightFunc()
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Can not get latest record without block timestamp", "err", err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BlocktimestampFetcher) Stop() {
|
|
||||||
log.Info("BlocktimestampFetcher Stop")
|
|
||||||
b.ctx.Done()
|
|
||||||
}
|
|
||||||
@@ -1,429 +0,0 @@
|
|||||||
package cross_msg
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
geth "github.com/ethereum/go-ethereum"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
"github.com/ethereum/go-ethereum/ethclient"
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
|
|
||||||
backendabi "bridge-history-api/abi"
|
|
||||||
"bridge-history-api/db"
|
|
||||||
"bridge-history-api/db/orm"
|
|
||||||
"bridge-history-api/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Todo : read from config
|
|
||||||
var (
|
|
||||||
// the number of blocks fetch per round
|
|
||||||
FETCH_LIMIT = int64(3000)
|
|
||||||
)
|
|
||||||
|
|
||||||
// FetchAndSave is a function type that fetches events from blockchain and saves them to database
|
|
||||||
type FetchAndSave func(ctx context.Context, client *ethclient.Client, database db.OrmFactory, from int64, to int64, addressList []common.Address) error
|
|
||||||
|
|
||||||
// GetLatestProcessed is a function type that gets the latest processed block height from database
|
|
||||||
type GetLatestProcessed func(db db.OrmFactory) (int64, error)
|
|
||||||
type UpdateXHash func(ctx context.Context)
|
|
||||||
|
|
||||||
type FetchEventWorker struct {
|
|
||||||
F FetchAndSave
|
|
||||||
G GetLatestProcessed
|
|
||||||
Name string
|
|
||||||
}
|
|
||||||
|
|
||||||
type msgHashWrapper struct {
|
|
||||||
msgHash common.Hash
|
|
||||||
txHash common.Hash
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetLatestL1ProcessedHeight(db db.OrmFactory) (int64, error) {
|
|
||||||
crossHeight, err := db.GetLatestL1ProcessedHeight()
|
|
||||||
if err != nil {
|
|
||||||
log.Error("failed to get L1 cross message processed height: ", "err", err)
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
relayedHeight, err := db.GetLatestRelayedHeightOnL1()
|
|
||||||
if err != nil {
|
|
||||||
log.Error("failed to get L1 relayed message processed height: ", "err", err)
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
if crossHeight > relayedHeight {
|
|
||||||
return crossHeight, nil
|
|
||||||
} else {
|
|
||||||
return relayedHeight, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetLatestL2ProcessedHeight(db db.OrmFactory) (int64, error) {
|
|
||||||
crossHeight, err := db.GetLatestL2ProcessedHeight()
|
|
||||||
if err != nil {
|
|
||||||
log.Error("failed to get L2 cross message processed height", "err", err)
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
relayedHeight, err := db.GetLatestRelayedHeightOnL2()
|
|
||||||
if err != nil {
|
|
||||||
log.Error("failed to get L2 relayed message processed height", "err", err)
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
if crossHeight > relayedHeight {
|
|
||||||
return crossHeight, nil
|
|
||||||
} else {
|
|
||||||
return relayedHeight, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func L1FetchAndSaveEvents(ctx context.Context, client *ethclient.Client, database db.OrmFactory, from int64, to int64, addrList []common.Address) error {
|
|
||||||
|
|
||||||
query := geth.FilterQuery{
|
|
||||||
FromBlock: big.NewInt(from), // inclusive
|
|
||||||
ToBlock: big.NewInt(to), // inclusive
|
|
||||||
Addresses: addrList,
|
|
||||||
Topics: make([][]common.Hash, 1),
|
|
||||||
}
|
|
||||||
query.Topics[0] = make([]common.Hash, 7)
|
|
||||||
query.Topics[0][0] = backendabi.L1DepositETHSig
|
|
||||||
query.Topics[0][1] = backendabi.L1DepositERC20Sig
|
|
||||||
query.Topics[0][2] = backendabi.L1RelayedMessageEventSignature
|
|
||||||
query.Topics[0][3] = backendabi.L1SentMessageEventSignature
|
|
||||||
query.Topics[0][4] = backendabi.L1DepositERC721Sig
|
|
||||||
query.Topics[0][5] = backendabi.L1DepositERC1155Sig
|
|
||||||
query.Topics[0][6] = backendabi.L1DepositWETHSig
|
|
||||||
|
|
||||||
logs, err := client.FilterLogs(ctx, query)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Failed to get l1 event logs", "err", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
depositL1CrossMsgs, msgHashes, relayedMsg, err := parseBackendL1EventLogs(logs)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("l1FetchAndSaveEvents: Failed to parse cross msg event logs", "err", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
dbTx, err := database.Beginx()
|
|
||||||
if err != nil {
|
|
||||||
log.Error("l2FetchAndSaveEvents: Failed to begin db transaction", "err", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = database.BatchInsertL1CrossMsgDBTx(dbTx, depositL1CrossMsgs)
|
|
||||||
if err != nil {
|
|
||||||
dbTx.Rollback()
|
|
||||||
log.Crit("l1FetchAndSaveEvents: Failed to insert cross msg event logs", "err", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = database.BatchInsertRelayedMsgDBTx(dbTx, relayedMsg)
|
|
||||||
if err != nil {
|
|
||||||
dbTx.Rollback()
|
|
||||||
log.Crit("l1FetchAndSaveEvents: Failed to insert relayed message event logs", "err", err)
|
|
||||||
}
|
|
||||||
err = updateL1CrossMsgMsgHash(ctx, dbTx, database, msgHashes)
|
|
||||||
if err != nil {
|
|
||||||
dbTx.Rollback()
|
|
||||||
log.Crit("l1FetchAndSaveEvents: Failed to update msgHash in L1 cross msg", "err", err)
|
|
||||||
}
|
|
||||||
err = dbTx.Commit()
|
|
||||||
if err != nil {
|
|
||||||
// if we can not insert into DB, there must something wrong, need a on-call member handle the dababase manually
|
|
||||||
dbTx.Rollback()
|
|
||||||
log.Error("l1FetchAndSaveEvents: Failed to commit db transaction", "err", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func L2FetchAndSaveEvents(ctx context.Context, client *ethclient.Client, database db.OrmFactory, from int64, to int64, addrList []common.Address) error {
|
|
||||||
query := geth.FilterQuery{
|
|
||||||
FromBlock: big.NewInt(from), // inclusive
|
|
||||||
ToBlock: big.NewInt(to), // inclusive
|
|
||||||
Addresses: addrList,
|
|
||||||
Topics: make([][]common.Hash, 1),
|
|
||||||
}
|
|
||||||
query.Topics[0] = make([]common.Hash, 7)
|
|
||||||
query.Topics[0][0] = backendabi.L2WithdrawETHSig
|
|
||||||
query.Topics[0][1] = backendabi.L2WithdrawERC20Sig
|
|
||||||
query.Topics[0][2] = backendabi.L2RelayedMessageEventSignature
|
|
||||||
query.Topics[0][3] = backendabi.L2SentMessageEventSignature
|
|
||||||
query.Topics[0][4] = backendabi.L2WithdrawERC721Sig
|
|
||||||
query.Topics[0][5] = backendabi.L2WithdrawERC1155Sig
|
|
||||||
query.Topics[0][6] = backendabi.L2WithdrawWETHSig
|
|
||||||
|
|
||||||
logs, err := client.FilterLogs(ctx, query)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Failed to get l2 event logs", "err", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
depositL2CrossMsgs, msgHashes, relayedMsg, err := parseBackendL2EventLogs(logs)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("l2FetchAndSaveEvents: Failed to parse cross msg event logs", "err", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
dbTx, err := database.Beginx()
|
|
||||||
if err != nil {
|
|
||||||
log.Error("l2FetchAndSaveEvents: Failed to begin db transaction", "err", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = database.BatchInsertL2CrossMsgDBTx(dbTx, depositL2CrossMsgs)
|
|
||||||
if err != nil {
|
|
||||||
dbTx.Rollback()
|
|
||||||
log.Crit("l2FetchAndSaveEvents: Failed to insert cross msg event logs", "err", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = database.BatchInsertRelayedMsgDBTx(dbTx, relayedMsg)
|
|
||||||
if err != nil {
|
|
||||||
dbTx.Rollback()
|
|
||||||
log.Crit("l2FetchAndSaveEvents: Failed to insert relayed message event logs", "err", err)
|
|
||||||
}
|
|
||||||
err = updateL2CrossMsgMsgHash(ctx, dbTx, database, msgHashes)
|
|
||||||
if err != nil {
|
|
||||||
dbTx.Rollback()
|
|
||||||
log.Crit("l2FetchAndSaveEvents: Failed to update msgHash in L2 cross msg", "err", err)
|
|
||||||
}
|
|
||||||
err = dbTx.Commit()
|
|
||||||
if err != nil {
|
|
||||||
// if we can not insert into DB, there must something wrong, need a on-call member handle the dababase manually
|
|
||||||
dbTx.Rollback()
|
|
||||||
log.Error("l2FetchAndSaveEvents: Failed to commit db transaction", "err", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseBackendL1EventLogs(logs []types.Log) ([]*orm.CrossMsg, []msgHashWrapper, []*orm.RelayedMsg, error) {
|
|
||||||
// Need use contract abi to parse event Log
|
|
||||||
// Can only be tested after we have our contracts set up
|
|
||||||
|
|
||||||
var l1CrossMsg []*orm.CrossMsg
|
|
||||||
var relayedMsgs []*orm.RelayedMsg
|
|
||||||
var msgHashes []msgHashWrapper
|
|
||||||
for _, vlog := range logs {
|
|
||||||
switch vlog.Topics[0] {
|
|
||||||
case backendabi.L1DepositETHSig:
|
|
||||||
event := backendabi.DepositETH{}
|
|
||||||
err := utils.UnpackLog(backendabi.L1ETHGatewayABI, &event, "DepositETH", vlog)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Failed to unpack DepositETH event", "err", err)
|
|
||||||
return l1CrossMsg, msgHashes, relayedMsgs, err
|
|
||||||
}
|
|
||||||
l1CrossMsg = append(l1CrossMsg, &orm.CrossMsg{
|
|
||||||
Height: vlog.BlockNumber,
|
|
||||||
Sender: event.From.String(),
|
|
||||||
Target: event.To.String(),
|
|
||||||
Amount: event.Amount.String(),
|
|
||||||
Asset: int(orm.ETH),
|
|
||||||
Layer1Hash: vlog.TxHash.Hex(),
|
|
||||||
})
|
|
||||||
case backendabi.L1DepositERC20Sig:
|
|
||||||
event := backendabi.ERC20MessageEvent{}
|
|
||||||
err := utils.UnpackLog(backendabi.L1StandardERC20GatewayABI, &event, "DepositERC20", vlog)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Failed to unpack DepositERC20 event", "err", err)
|
|
||||||
return l1CrossMsg, msgHashes, relayedMsgs, err
|
|
||||||
}
|
|
||||||
l1CrossMsg = append(l1CrossMsg, &orm.CrossMsg{
|
|
||||||
Height: vlog.BlockNumber,
|
|
||||||
Sender: event.From.String(),
|
|
||||||
Target: event.To.String(),
|
|
||||||
Amount: event.Amount.String(),
|
|
||||||
Asset: int(orm.ERC20),
|
|
||||||
Layer1Hash: vlog.TxHash.Hex(),
|
|
||||||
Layer1Token: event.L1Token.Hex(),
|
|
||||||
Layer2Token: event.L2Token.Hex(),
|
|
||||||
})
|
|
||||||
case backendabi.L1DepositERC721Sig:
|
|
||||||
event := backendabi.ERC721MessageEvent{}
|
|
||||||
err := utils.UnpackLog(backendabi.L1ERC721GatewayABI, &event, "DepositERC721", vlog)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Failed to unpack DepositERC721 event", "err", err)
|
|
||||||
return l1CrossMsg, msgHashes, relayedMsgs, err
|
|
||||||
}
|
|
||||||
l1CrossMsg = append(l1CrossMsg, &orm.CrossMsg{
|
|
||||||
Height: vlog.BlockNumber,
|
|
||||||
Sender: event.From.String(),
|
|
||||||
Target: event.To.String(),
|
|
||||||
Asset: int(orm.ERC721),
|
|
||||||
Layer1Hash: vlog.TxHash.Hex(),
|
|
||||||
Layer1Token: event.L1Token.Hex(),
|
|
||||||
Layer2Token: event.L2Token.Hex(),
|
|
||||||
TokenID: event.TokenID.Uint64(),
|
|
||||||
})
|
|
||||||
case backendabi.L1DepositERC1155Sig:
|
|
||||||
event := backendabi.ERC1155MessageEvent{}
|
|
||||||
err := utils.UnpackLog(backendabi.L1ERC1155GatewayABI, &event, "DepositERC1155", vlog)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Failed to unpack DepositERC1155 event", "err", err)
|
|
||||||
return l1CrossMsg, msgHashes, relayedMsgs, err
|
|
||||||
}
|
|
||||||
l1CrossMsg = append(l1CrossMsg, &orm.CrossMsg{
|
|
||||||
Height: vlog.BlockNumber,
|
|
||||||
Sender: event.From.String(),
|
|
||||||
Target: event.To.String(),
|
|
||||||
Asset: int(orm.ERC1155),
|
|
||||||
Layer1Hash: vlog.TxHash.Hex(),
|
|
||||||
Layer1Token: event.L1Token.Hex(),
|
|
||||||
Layer2Token: event.L2Token.Hex(),
|
|
||||||
TokenID: event.TokenID.Uint64(),
|
|
||||||
Amount: event.Amount.String(),
|
|
||||||
})
|
|
||||||
case backendabi.L1SentMessageEventSignature:
|
|
||||||
event := backendabi.L1SentMessageEvent{}
|
|
||||||
err := utils.UnpackLog(backendabi.L1ScrollMessengerABI, &event, "SentMessage", vlog)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Failed to unpack SentMessage event", "err", err)
|
|
||||||
return l1CrossMsg, msgHashes, relayedMsgs, err
|
|
||||||
}
|
|
||||||
msgHash := utils.ComputeMessageHash(event.Sender, event.Target, event.Value, event.MessageNonce, event.Message)
|
|
||||||
msgHashes = append(msgHashes, msgHashWrapper{
|
|
||||||
msgHash: msgHash,
|
|
||||||
txHash: vlog.TxHash})
|
|
||||||
case backendabi.L1RelayedMessageEventSignature:
|
|
||||||
event := backendabi.L1RelayedMessageEvent{}
|
|
||||||
err := utils.UnpackLog(backendabi.L1ScrollMessengerABI, &event, "RelayedMessage", vlog)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Failed to unpack RelayedMessage event", "err", err)
|
|
||||||
return l1CrossMsg, msgHashes, relayedMsgs, err
|
|
||||||
}
|
|
||||||
relayedMsgs = append(relayedMsgs, &orm.RelayedMsg{
|
|
||||||
MsgHash: event.MessageHash.String(),
|
|
||||||
Height: vlog.BlockNumber,
|
|
||||||
Layer1Hash: vlog.TxHash.Hex(),
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return l1CrossMsg, msgHashes, relayedMsgs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseBackendL2EventLogs(logs []types.Log) ([]*orm.CrossMsg, []msgHashWrapper, []*orm.RelayedMsg, error) {
|
|
||||||
// Need use contract abi to parse event Log
|
|
||||||
// Can only be tested after we have our contracts set up
|
|
||||||
|
|
||||||
var l2CrossMsg []*orm.CrossMsg
|
|
||||||
var relayedMsgs []*orm.RelayedMsg
|
|
||||||
var msgHashes []msgHashWrapper
|
|
||||||
for _, vlog := range logs {
|
|
||||||
switch vlog.Topics[0] {
|
|
||||||
case backendabi.L2WithdrawETHSig:
|
|
||||||
event := backendabi.DepositETH{}
|
|
||||||
err := utils.UnpackLog(backendabi.L2ETHGatewayABI, &event, "WithdrawETH", vlog)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Failed to unpack WithdrawETH event", "err", err)
|
|
||||||
return l2CrossMsg, msgHashes, relayedMsgs, err
|
|
||||||
}
|
|
||||||
l2CrossMsg = append(l2CrossMsg, &orm.CrossMsg{
|
|
||||||
Height: vlog.BlockNumber,
|
|
||||||
Sender: event.From.String(),
|
|
||||||
Target: event.To.String(),
|
|
||||||
Amount: event.Amount.String(),
|
|
||||||
Asset: int(orm.ETH),
|
|
||||||
Layer2Hash: vlog.TxHash.Hex(),
|
|
||||||
})
|
|
||||||
case backendabi.L2WithdrawERC20Sig:
|
|
||||||
event := backendabi.ERC20MessageEvent{}
|
|
||||||
err := utils.UnpackLog(backendabi.L2StandardERC20GatewayABI, &event, "WithdrawERC20", vlog)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Failed to unpack WithdrawERC20 event", "err", err)
|
|
||||||
return l2CrossMsg, msgHashes, relayedMsgs, err
|
|
||||||
}
|
|
||||||
l2CrossMsg = append(l2CrossMsg, &orm.CrossMsg{
|
|
||||||
Height: vlog.BlockNumber,
|
|
||||||
Sender: event.From.String(),
|
|
||||||
Target: event.To.String(),
|
|
||||||
Amount: event.Amount.String(),
|
|
||||||
Asset: int(orm.ERC20),
|
|
||||||
Layer2Hash: vlog.TxHash.Hex(),
|
|
||||||
Layer1Token: event.L1Token.Hex(),
|
|
||||||
Layer2Token: event.L2Token.Hex(),
|
|
||||||
})
|
|
||||||
case backendabi.L2WithdrawERC721Sig:
|
|
||||||
event := backendabi.ERC721MessageEvent{}
|
|
||||||
err := utils.UnpackLog(backendabi.L2ERC721GatewayABI, &event, "WithdrawERC721", vlog)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Failed to unpack WithdrawERC721 event", "err", err)
|
|
||||||
return l2CrossMsg, msgHashes, relayedMsgs, err
|
|
||||||
}
|
|
||||||
l2CrossMsg = append(l2CrossMsg, &orm.CrossMsg{
|
|
||||||
Height: vlog.BlockNumber,
|
|
||||||
Sender: event.From.String(),
|
|
||||||
Target: event.To.String(),
|
|
||||||
Asset: int(orm.ERC721),
|
|
||||||
Layer2Hash: vlog.TxHash.Hex(),
|
|
||||||
Layer1Token: event.L1Token.Hex(),
|
|
||||||
Layer2Token: event.L2Token.Hex(),
|
|
||||||
TokenID: event.TokenID.Uint64(),
|
|
||||||
})
|
|
||||||
case backendabi.L2WithdrawERC1155Sig:
|
|
||||||
event := backendabi.ERC1155MessageEvent{}
|
|
||||||
err := utils.UnpackLog(backendabi.L2ERC1155GatewayABI, &event, "WithdrawERC1155", vlog)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Failed to unpack WithdrawERC1155 event", "err", err)
|
|
||||||
return l2CrossMsg, msgHashes, relayedMsgs, err
|
|
||||||
}
|
|
||||||
l2CrossMsg = append(l2CrossMsg, &orm.CrossMsg{
|
|
||||||
Height: vlog.BlockNumber,
|
|
||||||
Sender: event.From.String(),
|
|
||||||
Target: event.To.String(),
|
|
||||||
Asset: int(orm.ERC1155),
|
|
||||||
Layer2Hash: vlog.TxHash.Hex(),
|
|
||||||
Layer1Token: event.L1Token.Hex(),
|
|
||||||
Layer2Token: event.L2Token.Hex(),
|
|
||||||
TokenID: event.TokenID.Uint64(),
|
|
||||||
Amount: event.Amount.String(),
|
|
||||||
})
|
|
||||||
case backendabi.L2SentMessageEventSignature:
|
|
||||||
event := backendabi.L2SentMessageEvent{}
|
|
||||||
err := utils.UnpackLog(backendabi.L2ScrollMessengerABI, &event, "SentMessage", vlog)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Failed to unpack SentMessage event", "err", err)
|
|
||||||
return l2CrossMsg, msgHashes, relayedMsgs, err
|
|
||||||
}
|
|
||||||
msgHash := utils.ComputeMessageHash(event.Sender, event.Target, event.Value, event.MessageNonce, event.Message)
|
|
||||||
msgHashes = append(msgHashes, msgHashWrapper{
|
|
||||||
msgHash: msgHash,
|
|
||||||
txHash: vlog.TxHash})
|
|
||||||
case backendabi.L2RelayedMessageEventSignature:
|
|
||||||
event := backendabi.L2RelayedMessageEvent{}
|
|
||||||
err := utils.UnpackLog(backendabi.L2ScrollMessengerABI, &event, "RelayedMessage", vlog)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Failed to unpack RelayedMessage event", "err", err)
|
|
||||||
return l2CrossMsg, msgHashes, relayedMsgs, err
|
|
||||||
}
|
|
||||||
relayedMsgs = append(relayedMsgs, &orm.RelayedMsg{
|
|
||||||
MsgHash: event.MessageHash.String(),
|
|
||||||
Height: vlog.BlockNumber,
|
|
||||||
Layer2Hash: vlog.TxHash.Hex(),
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return l2CrossMsg, msgHashes, relayedMsgs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateL1CrossMsgMsgHash(ctx context.Context, dbTx *sqlx.Tx, database db.OrmFactory, msgHashes []msgHashWrapper) error {
|
|
||||||
for _, msgHash := range msgHashes {
|
|
||||||
err := database.UpdateL1CrossMsgHashDBTx(ctx, dbTx, msgHash.txHash, msgHash.msgHash)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("updateL1CrossMsgMsgHash: can not update layer1 cross msg MsgHash", "layer1 hash", msgHash.txHash, "err", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateL2CrossMsgMsgHash(ctx context.Context, dbTx *sqlx.Tx, database db.OrmFactory, msgHashes []msgHashWrapper) error {
|
|
||||||
for _, msgHash := range msgHashes {
|
|
||||||
err := database.UpdateL2CrossMsgHashDBTx(ctx, dbTx, msgHash.txHash, msgHash.msgHash)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("updateL2CrossMsgMsgHash: can not update layer2 cross msg MsgHash", "layer2 hash", msgHash.txHash, "err", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
package cross_msg
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
"github.com/ethereum/go-ethereum/ethclient"
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
|
|
||||||
"bridge-history-api/db"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ReorgHandling func(ctx context.Context, reorgHeight int64, db db.OrmFactory) error
|
|
||||||
|
|
||||||
func reverseArray(arr []*types.Header) []*types.Header {
|
|
||||||
for i := 0; i < len(arr)/2; i++ {
|
|
||||||
j := len(arr) - i - 1
|
|
||||||
arr[i], arr[j] = arr[j], arr[i]
|
|
||||||
}
|
|
||||||
return arr
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsParentAndChild(parentHeader *types.Header, header *types.Header) bool {
|
|
||||||
return header.ParentHash == parentHeader.Hash()
|
|
||||||
}
|
|
||||||
|
|
||||||
func MergeAddIntoHeaderList(baseArr, extraArr []*types.Header, maxLength int) []*types.Header {
|
|
||||||
mergedArr := append(baseArr, extraArr...)
|
|
||||||
if len(mergedArr) <= maxLength {
|
|
||||||
return mergedArr
|
|
||||||
}
|
|
||||||
|
|
||||||
startIndex := len(mergedArr) - maxLength
|
|
||||||
return mergedArr[startIndex:]
|
|
||||||
}
|
|
||||||
|
|
||||||
func BackwardFindReorgBlock(ctx context.Context, headers []*types.Header, client *ethclient.Client, header *types.Header) (int, bool, []*types.Header) {
|
|
||||||
maxStep := len(headers)
|
|
||||||
backwardHeaderList := []*types.Header{header}
|
|
||||||
for iterRound := 0; iterRound < maxStep; iterRound++ {
|
|
||||||
header, err := client.HeaderByHash(ctx, header.ParentHash)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("BackwardFindReorgBlock failed", "error", err)
|
|
||||||
return -1, false, nil
|
|
||||||
}
|
|
||||||
backwardHeaderList = append(backwardHeaderList, header)
|
|
||||||
for j := len(headers) - 1; j >= 0; j-- {
|
|
||||||
if IsParentAndChild(headers[j], header) {
|
|
||||||
backwardHeaderList = reverseArray(backwardHeaderList)
|
|
||||||
return j, true, backwardHeaderList
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1, false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func L1ReorgHandling(ctx context.Context, reorgHeight int64, db db.OrmFactory) error {
|
|
||||||
dbTx, err := db.Beginx()
|
|
||||||
if err != nil {
|
|
||||||
log.Crit("begin db tx failed", "err", err)
|
|
||||||
}
|
|
||||||
err = db.DeleteL1CrossMsgAfterHeightDBTx(dbTx, reorgHeight)
|
|
||||||
if err != nil {
|
|
||||||
dbTx.Rollback()
|
|
||||||
log.Crit("delete l1 cross msg from height", "height", reorgHeight, "err", err)
|
|
||||||
}
|
|
||||||
err = db.DeleteL1RelayedHashAfterHeightDBTx(dbTx, reorgHeight)
|
|
||||||
if err != nil {
|
|
||||||
dbTx.Rollback()
|
|
||||||
log.Crit("delete l1 relayed hash from height", "height", reorgHeight, "err", err)
|
|
||||||
}
|
|
||||||
err = dbTx.Commit()
|
|
||||||
if err != nil {
|
|
||||||
dbTx.Rollback()
|
|
||||||
log.Error("commit tx failed", "err", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func L2ReorgHandling(ctx context.Context, reorgHeight int64, db db.OrmFactory) error {
|
|
||||||
dbTx, err := db.Beginx()
|
|
||||||
if err != nil {
|
|
||||||
dbTx.Rollback()
|
|
||||||
log.Crit("begin db tx failed", "err", err)
|
|
||||||
}
|
|
||||||
err = db.DeleteL2CrossMsgFromHeightDBTx(dbTx, reorgHeight)
|
|
||||||
if err != nil {
|
|
||||||
dbTx.Rollback()
|
|
||||||
log.Crit("delete l2 cross msg from height", "height", reorgHeight, "err", err)
|
|
||||||
}
|
|
||||||
err = db.DeleteL2RelayedHashAfterHeightDBTx(dbTx, reorgHeight)
|
|
||||||
if err != nil {
|
|
||||||
dbTx.Rollback()
|
|
||||||
log.Crit("delete l2 relayed hash from height", "height", reorgHeight, "err", err)
|
|
||||||
}
|
|
||||||
err = dbTx.Commit()
|
|
||||||
if err != nil {
|
|
||||||
dbTx.Rollback()
|
|
||||||
log.Error("commit tx failed", "err", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
113
bridge-history-api/crossmsg/batch_info_fetcher.go
Normal file
113
bridge-history-api/crossmsg/batch_info_fetcher.go
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package crossmsg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
|
||||||
|
"bridge-history-api/crossmsg/messageproof"
|
||||||
|
"bridge-history-api/orm"
|
||||||
|
"bridge-history-api/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BatchInfoFetcher fetches batch info from l1 chain and update db
|
||||||
|
type BatchInfoFetcher struct {
|
||||||
|
ctx context.Context
|
||||||
|
scrollChainAddr common.Address
|
||||||
|
batchInfoStartNumber uint64
|
||||||
|
confirmation uint64
|
||||||
|
blockTimeInSec int
|
||||||
|
client *ethclient.Client
|
||||||
|
db *gorm.DB
|
||||||
|
rollupOrm *orm.RollupBatch
|
||||||
|
msgProofUpdater *messageproof.MsgProofUpdater
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBatchInfoFetcher creates a new BatchInfoFetcher instance
|
||||||
|
func NewBatchInfoFetcher(ctx context.Context, scrollChainAddr common.Address, batchInfoStartNumber uint64, confirmation uint64, blockTimeInSec int, client *ethclient.Client, db *gorm.DB, msgProofUpdater *messageproof.MsgProofUpdater) *BatchInfoFetcher {
|
||||||
|
return &BatchInfoFetcher{
|
||||||
|
ctx: ctx,
|
||||||
|
scrollChainAddr: scrollChainAddr,
|
||||||
|
batchInfoStartNumber: batchInfoStartNumber,
|
||||||
|
confirmation: confirmation,
|
||||||
|
blockTimeInSec: blockTimeInSec,
|
||||||
|
client: client,
|
||||||
|
db: db,
|
||||||
|
rollupOrm: orm.NewRollupBatch(db),
|
||||||
|
msgProofUpdater: msgProofUpdater,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the BatchInfoFetcher
|
||||||
|
func (b *BatchInfoFetcher) Start() {
|
||||||
|
log.Info("BatchInfoFetcher Start")
|
||||||
|
// Fetch batch info at beginning
|
||||||
|
// Then start msg proof updater after db have some bridge batch
|
||||||
|
err := b.fetchBatchInfo()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("fetch batch info at beginning failed: ", "err", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
go b.msgProofUpdater.Start()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
tick := time.NewTicker(time.Duration(b.blockTimeInSec) * time.Second)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-b.ctx.Done():
|
||||||
|
tick.Stop()
|
||||||
|
return
|
||||||
|
case <-tick.C:
|
||||||
|
err := b.fetchBatchInfo()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("fetch batch info failed: ", "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the BatchInfoFetcher and call msg proof updater to stop
|
||||||
|
func (b *BatchInfoFetcher) Stop() {
|
||||||
|
log.Info("BatchInfoFetcher Stop")
|
||||||
|
b.msgProofUpdater.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BatchInfoFetcher) fetchBatchInfo() error {
|
||||||
|
number, err := utils.GetSafeBlockNumber(b.ctx, b.client, b.confirmation)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Can not get latest block number: ", "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
latestBatchHeight, err := b.rollupOrm.GetLatestRollupBatchProcessedHeight(b.ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Can not get latest BatchInfo: ", "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var startHeight uint64
|
||||||
|
if latestBatchHeight == 0 {
|
||||||
|
log.Info("no batch record in database, start from batchInfoStartNumber", "batchInfoStartNumber", b.batchInfoStartNumber)
|
||||||
|
startHeight = b.batchInfoStartNumber
|
||||||
|
} else {
|
||||||
|
startHeight = latestBatchHeight + 1
|
||||||
|
}
|
||||||
|
for from := startHeight; number >= from; from += fetchLimit {
|
||||||
|
to := from + fetchLimit - 1
|
||||||
|
// number - confirmation can never less than 0 since the for loop condition
|
||||||
|
// but watch out the overflow
|
||||||
|
if to > number {
|
||||||
|
to = number
|
||||||
|
}
|
||||||
|
// filter logs to fetch batches
|
||||||
|
err = FetchAndSaveBatchIndex(b.ctx, b.client, b.db, int64(from), int64(to), b.scrollChainAddr)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Can not fetch and save from chain: ", "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
85
bridge-history-api/crossmsg/block_timestamp_fetcher.go
Normal file
85
bridge-history-api/crossmsg/block_timestamp_fetcher.go
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
package crossmsg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"math/big"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetEarliestNoBlockTimestampHeightFunc is a function type that gets the earliest record without block timestamp from database
|
||||||
|
type GetEarliestNoBlockTimestampHeightFunc func(ctx context.Context) (uint64, error)
|
||||||
|
|
||||||
|
// UpdateBlockTimestampFunc is a function type that updates block timestamp into database
|
||||||
|
type UpdateBlockTimestampFunc func(ctx context.Context, height uint64, timestamp time.Time) error
|
||||||
|
|
||||||
|
// BlockTimestampFetcher fetches block timestamp from blockchain and saves them to database
|
||||||
|
type BlockTimestampFetcher struct {
|
||||||
|
ctx context.Context
|
||||||
|
confirmation uint64
|
||||||
|
blockTimeInSec int
|
||||||
|
client *ethclient.Client
|
||||||
|
updateBlockTimestampFunc UpdateBlockTimestampFunc
|
||||||
|
getEarliestNoBlockTimestampHeightFunc GetEarliestNoBlockTimestampHeightFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBlockTimestampFetcher creates a new BlockTimestampFetcher instance
|
||||||
|
func NewBlockTimestampFetcher(ctx context.Context, confirmation uint64, blockTimeInSec int, client *ethclient.Client, updateBlockTimestampFunc UpdateBlockTimestampFunc, getEarliestNoBlockTimestampHeightFunc GetEarliestNoBlockTimestampHeightFunc) *BlockTimestampFetcher {
|
||||||
|
return &BlockTimestampFetcher{
|
||||||
|
ctx: ctx,
|
||||||
|
confirmation: confirmation,
|
||||||
|
blockTimeInSec: blockTimeInSec,
|
||||||
|
client: client,
|
||||||
|
getEarliestNoBlockTimestampHeightFunc: getEarliestNoBlockTimestampHeightFunc,
|
||||||
|
updateBlockTimestampFunc: updateBlockTimestampFunc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the BlockTimestampFetcher
|
||||||
|
func (b *BlockTimestampFetcher) Start() {
|
||||||
|
go func() {
|
||||||
|
tick := time.NewTicker(time.Duration(b.blockTimeInSec) * time.Second)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-b.ctx.Done():
|
||||||
|
tick.Stop()
|
||||||
|
return
|
||||||
|
case <-tick.C:
|
||||||
|
number, err := b.client.BlockNumber(b.ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Can not get latest block number", "err", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
startHeight, err := b.getEarliestNoBlockTimestampHeightFunc(b.ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Can not get latest record without block timestamp", "err", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for height := startHeight; number >= height+b.confirmation && height > 0; {
|
||||||
|
block, err := b.client.HeaderByNumber(b.ctx, new(big.Int).SetUint64(height))
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Can not get block by number", "err", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
err = b.updateBlockTimestampFunc(b.ctx, height, time.Unix(int64(block.Time), 0))
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Can not update blockTimestamp into DB ", "err", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
height, err = b.getEarliestNoBlockTimestampHeightFunc(b.ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Can not get latest record without block timestamp", "err", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the BlockTimestampFetcher and log the info
|
||||||
|
func (b *BlockTimestampFetcher) Stop() {
|
||||||
|
log.Info("BlockTimestampFetcher Stop")
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package cross_msg
|
package crossmsg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -12,46 +12,47 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/ethclient"
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/modern-go/reflect2"
|
"github.com/modern-go/reflect2"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"bridge-history-api/config"
|
"bridge-history-api/config"
|
||||||
"bridge-history-api/db"
|
|
||||||
"bridge-history-api/utils"
|
"bridge-history-api/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CrossMsgFetcher struct {
|
// MsgFetcher fetches cross message events from blockchain and saves them to database
|
||||||
|
type MsgFetcher struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
config *config.LayerConfig
|
config *config.LayerConfig
|
||||||
db db.OrmFactory
|
db *gorm.DB
|
||||||
client *ethclient.Client
|
client *ethclient.Client
|
||||||
worker *FetchEventWorker
|
worker *FetchEventWorker
|
||||||
reorgHandling ReorgHandling
|
reorgHandling ReorgHandling
|
||||||
addressList []common.Address
|
addressList []common.Address
|
||||||
cachedHeaders []*types.Header
|
cachedHeaders []*types.Header
|
||||||
mu *sync.Mutex
|
mu sync.Mutex
|
||||||
reorgStartCh chan struct{}
|
reorgStartCh chan struct{}
|
||||||
reorgEndCh chan struct{}
|
reorgEndCh chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCrossMsgFetcher(ctx context.Context, config *config.LayerConfig, db db.OrmFactory, client *ethclient.Client, worker *FetchEventWorker, addressList []common.Address, reorg ReorgHandling) (*CrossMsgFetcher, error) {
|
// NewMsgFetcher creates a new MsgFetcher instance
|
||||||
newMU := &sync.Mutex{}
|
func NewMsgFetcher(ctx context.Context, config *config.LayerConfig, db *gorm.DB, client *ethclient.Client, worker *FetchEventWorker, addressList []common.Address, reorg ReorgHandling) (*MsgFetcher, error) {
|
||||||
crossMsgFetcher := &CrossMsgFetcher{
|
msgFetcher := &MsgFetcher{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
config: config,
|
config: config,
|
||||||
db: db,
|
db: db,
|
||||||
client: client,
|
client: client,
|
||||||
worker: worker,
|
worker: worker,
|
||||||
reorgHandling: reorg,
|
reorgHandling: reorg,
|
||||||
mu: newMU,
|
|
||||||
addressList: addressList,
|
addressList: addressList,
|
||||||
cachedHeaders: make([]*types.Header, 0),
|
cachedHeaders: make([]*types.Header, 0),
|
||||||
reorgStartCh: make(chan struct{}),
|
reorgStartCh: make(chan struct{}),
|
||||||
reorgEndCh: make(chan struct{}),
|
reorgEndCh: make(chan struct{}),
|
||||||
}
|
}
|
||||||
return crossMsgFetcher, nil
|
return msgFetcher, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CrossMsgFetcher) Start() {
|
// Start the MsgFetcher
|
||||||
log.Info("CrossMsgFetcher Start")
|
func (c *MsgFetcher) Start() {
|
||||||
|
log.Info("MsgFetcher Start")
|
||||||
// fetch missing events from finalized blocks, we don't handle reorgs here
|
// fetch missing events from finalized blocks, we don't handle reorgs here
|
||||||
c.forwardFetchAndSaveMissingEvents(c.config.Confirmation)
|
c.forwardFetchAndSaveMissingEvents(c.config.Confirmation)
|
||||||
|
|
||||||
@@ -77,7 +78,7 @@ func (c *CrossMsgFetcher) Start() {
|
|||||||
return
|
return
|
||||||
case <-tick.C:
|
case <-tick.C:
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
c.forwardFetchAndSaveMissingEvents(0)
|
c.forwardFetchAndSaveMissingEvents(1)
|
||||||
c.mu.Unlock()
|
c.mu.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,17 +97,17 @@ func (c *CrossMsgFetcher) Start() {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CrossMsgFetcher) Stop() {
|
// Stop the MsgFetcher and log the info
|
||||||
c.db.Close()
|
func (c *MsgFetcher) Stop() {
|
||||||
log.Info("CrossMsgFetcher Stop")
|
log.Info("MsgFetcher Stop")
|
||||||
}
|
}
|
||||||
|
|
||||||
// forwardFetchAndSaveMissingEvents will fetch all events from the latest processed height to the latest block number.
|
// forwardFetchAndSaveMissingEvents will fetch all events from the latest processed height to the latest block number.
|
||||||
func (c *CrossMsgFetcher) forwardFetchAndSaveMissingEvents(confirmation uint64) {
|
func (c *MsgFetcher) forwardFetchAndSaveMissingEvents(confirmation uint64) {
|
||||||
// if we fetch to the latest block, shall not exceed cachedHeaders
|
// if we fetch to the latest block, shall not exceed cachedHeaders
|
||||||
var number uint64
|
var number uint64
|
||||||
var err error
|
var err error
|
||||||
if len(c.cachedHeaders) != 0 && confirmation <= 0 {
|
if len(c.cachedHeaders) != 0 && confirmation == 0 {
|
||||||
number = c.cachedHeaders[len(c.cachedHeaders)-1].Number.Uint64() - 1
|
number = c.cachedHeaders[len(c.cachedHeaders)-1].Number.Uint64() - 1
|
||||||
} else {
|
} else {
|
||||||
number, err = utils.GetSafeBlockNumber(c.ctx, c.client, confirmation)
|
number, err = utils.GetSafeBlockNumber(c.ctx, c.client, confirmation)
|
||||||
@@ -119,22 +120,23 @@ func (c *CrossMsgFetcher) forwardFetchAndSaveMissingEvents(confirmation uint64)
|
|||||||
log.Error(fmt.Sprintf("%s: invalid get/fetch function", c.worker.Name))
|
log.Error(fmt.Sprintf("%s: invalid get/fetch function", c.worker.Name))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
processed_height, err := c.worker.G(c.db)
|
processedHeight, err := c.worker.G(c.ctx, c.db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(fmt.Sprintf("%s: can not get latest processed block height", c.worker.Name))
|
log.Error(fmt.Sprintf("%s: can not get latest processed block height", c.worker.Name))
|
||||||
}
|
}
|
||||||
log.Info(fmt.Sprintf("%s: ", c.worker.Name), "height", processed_height)
|
log.Info(fmt.Sprintf("%s: ", c.worker.Name), "height", processedHeight)
|
||||||
if processed_height <= 0 || processed_height < int64(c.config.StartHeight) {
|
if processedHeight <= 0 || processedHeight < c.config.StartHeight {
|
||||||
processed_height = int64(c.config.StartHeight)
|
processedHeight = c.config.StartHeight
|
||||||
} else {
|
} else {
|
||||||
processed_height += 1
|
processedHeight++
|
||||||
}
|
}
|
||||||
for n := processed_height; n <= int64(number); n += FETCH_LIMIT {
|
for from := processedHeight; from <= number; from += fetchLimit {
|
||||||
iter_end := n + FETCH_LIMIT - 1
|
to := from + fetchLimit - 1
|
||||||
if iter_end > int64(number) {
|
if to > number {
|
||||||
iter_end = int64(number)
|
to = number
|
||||||
}
|
}
|
||||||
err := c.worker.F(c.ctx, c.client, c.db, n, iter_end, c.addressList)
|
// watch for overflow here, tho its unlikely to happen
|
||||||
|
err := c.worker.F(c.ctx, c.client, c.db, int64(from), int64(to), c.addressList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(fmt.Sprintf("%s: failed!", c.worker.Name), "err", err)
|
log.Error(fmt.Sprintf("%s: failed!", c.worker.Name), "err", err)
|
||||||
break
|
break
|
||||||
@@ -142,7 +144,7 @@ func (c *CrossMsgFetcher) forwardFetchAndSaveMissingEvents(confirmation uint64)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CrossMsgFetcher) fetchMissingLatestHeaders() {
|
func (c *MsgFetcher) fetchMissingLatestHeaders() {
|
||||||
var start int64
|
var start int64
|
||||||
number, err := c.client.BlockNumber(c.ctx)
|
number, err := c.client.BlockNumber(c.ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -162,7 +164,7 @@ func (c *CrossMsgFetcher) fetchMissingLatestHeaders() {
|
|||||||
close(c.reorgEndCh)
|
close(c.reorgEndCh)
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
header, err := c.client.HeaderByNumber(c.ctx, big.NewInt(int64(i)))
|
header, err := c.client.HeaderByNumber(c.ctx, big.NewInt(i))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("failed to get latest header", "err", err)
|
log.Error("failed to get latest header", "err", err)
|
||||||
return
|
return
|
||||||
@@ -184,13 +186,13 @@ func (c *CrossMsgFetcher) fetchMissingLatestHeaders() {
|
|||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
index, ok, validHeaders := BackwardFindReorgBlock(c.ctx, c.cachedHeaders, c.client, header)
|
index, ok, validHeaders := BackwardFindReorgBlock(c.ctx, c.cachedHeaders, c.client, header)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Error("Reorg happended too earlier than cached headers", "reorg height", header.Number)
|
log.Error("Reorg happened too earlier than cached headers", "reorg height", header.Number)
|
||||||
num, err := utils.GetSafeBlockNumber(c.ctx, c.client, c.config.Confirmation)
|
num, getSafeErr := utils.GetSafeBlockNumber(c.ctx, c.client, c.config.Confirmation)
|
||||||
if err != nil {
|
if getSafeErr != nil {
|
||||||
log.Crit("Can not get safe number during reorg, quit the process", "err", err)
|
log.Crit("Can not get safe number during reorg, quit the process", "err", err)
|
||||||
}
|
}
|
||||||
// clear all our saved data, because no data is safe now
|
// clear all our saved data, because no data is safe now
|
||||||
err = c.reorgHandling(c.ctx, int64(num), c.db)
|
err = c.reorgHandling(c.ctx, num, c.db)
|
||||||
// if handling success then we can update the cachedHeaders
|
// if handling success then we can update the cachedHeaders
|
||||||
if err == nil {
|
if err == nil {
|
||||||
c.cachedHeaders = c.cachedHeaders[:0]
|
c.cachedHeaders = c.cachedHeaders[:0]
|
||||||
@@ -199,7 +201,7 @@ func (c *CrossMsgFetcher) fetchMissingLatestHeaders() {
|
|||||||
c.reorgEndCh <- struct{}{}
|
c.reorgEndCh <- struct{}{}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = c.reorgHandling(c.ctx, c.cachedHeaders[index].Number.Int64(), c.db)
|
err = c.reorgHandling(c.ctx, c.cachedHeaders[index].Number.Uint64(), c.db)
|
||||||
// if handling success then we can update the cachedHeaders
|
// if handling success then we can update the cachedHeaders
|
||||||
if err == nil {
|
if err == nil {
|
||||||
c.cachedHeaders = c.cachedHeaders[:index+1]
|
c.cachedHeaders = c.cachedHeaders[:index+1]
|
||||||
213
bridge-history-api/crossmsg/fetch_missing_event.go
Normal file
213
bridge-history-api/crossmsg/fetch_missing_event.go
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
package crossmsg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
geth "github.com/ethereum/go-ethereum"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
|
||||||
|
backendabi "bridge-history-api/abi"
|
||||||
|
"bridge-history-api/orm"
|
||||||
|
"bridge-history-api/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Todo : read from config
|
||||||
|
var (
|
||||||
|
// the number of blocks fetch per round
|
||||||
|
fetchLimit = uint64(3000)
|
||||||
|
)
|
||||||
|
|
||||||
|
// FetchAndSave is a function type that fetches events from blockchain and saves them to database
|
||||||
|
type FetchAndSave func(ctx context.Context, client *ethclient.Client, database *gorm.DB, from int64, to int64, addressList []common.Address) error
|
||||||
|
|
||||||
|
// GetLatestProcessed is a function type that gets the latest processed block height from database
|
||||||
|
type GetLatestProcessed func(ctx context.Context, db *gorm.DB) (uint64, error)
|
||||||
|
|
||||||
|
// FetchEventWorker defines worker with fetch and save function, processed number getter, and name
|
||||||
|
type FetchEventWorker struct {
|
||||||
|
F FetchAndSave
|
||||||
|
G GetLatestProcessed
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLatestL1ProcessedHeight get L1 the latest processed height
|
||||||
|
func GetLatestL1ProcessedHeight(ctx context.Context, db *gorm.DB) (uint64, error) {
|
||||||
|
l1CrossMsgOrm := orm.NewCrossMsg(db)
|
||||||
|
relayedOrm := orm.NewRelayedMsg(db)
|
||||||
|
crossHeight, err := l1CrossMsgOrm.GetLatestL1ProcessedHeight(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("failed to get L1 cross message processed height: ", "err", err)
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
relayedHeight, err := relayedOrm.GetLatestRelayedHeightOnL1(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("failed to get L1 relayed message processed height: ", "err", err)
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if crossHeight > relayedHeight {
|
||||||
|
return crossHeight, nil
|
||||||
|
}
|
||||||
|
return relayedHeight, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLatestL2ProcessedHeight get L2 latest processed height
|
||||||
|
func GetLatestL2ProcessedHeight(ctx context.Context, db *gorm.DB) (uint64, error) {
|
||||||
|
l2CrossMsgOrm := orm.NewCrossMsg(db)
|
||||||
|
relayedOrm := orm.NewRelayedMsg(db)
|
||||||
|
l2SentMsgOrm := orm.NewL2SentMsg(db)
|
||||||
|
crossHeight, err := l2CrossMsgOrm.GetLatestL2ProcessedHeight(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("failed to get L2 cross message processed height", "err", err)
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
relayedHeight, err := relayedOrm.GetLatestRelayedHeightOnL2(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("failed to get L2 relayed message processed height", "err", err)
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
l2SentHeight, err := l2SentMsgOrm.GetLatestSentMsgHeightOnL2(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("failed to get L2 sent message processed height", "err", err)
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
maxHeight := crossHeight
|
||||||
|
if maxHeight < relayedHeight {
|
||||||
|
maxHeight = relayedHeight
|
||||||
|
}
|
||||||
|
if maxHeight < l2SentHeight {
|
||||||
|
maxHeight = l2SentHeight
|
||||||
|
}
|
||||||
|
return maxHeight, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// L1FetchAndSaveEvents fetch and save events on L1
|
||||||
|
func L1FetchAndSaveEvents(ctx context.Context, client *ethclient.Client, db *gorm.DB, from int64, to int64, addrList []common.Address) error {
|
||||||
|
l1CrossMsgOrm := orm.NewCrossMsg(db)
|
||||||
|
relayedOrm := orm.NewRelayedMsg(db)
|
||||||
|
query := geth.FilterQuery{
|
||||||
|
FromBlock: big.NewInt(from), // inclusive
|
||||||
|
ToBlock: big.NewInt(to), // inclusive
|
||||||
|
Addresses: addrList,
|
||||||
|
Topics: make([][]common.Hash, 1),
|
||||||
|
}
|
||||||
|
query.Topics[0] = make([]common.Hash, 7)
|
||||||
|
query.Topics[0][0] = backendabi.L1DepositETHSig
|
||||||
|
query.Topics[0][1] = backendabi.L1DepositERC20Sig
|
||||||
|
query.Topics[0][2] = backendabi.L1RelayedMessageEventSignature
|
||||||
|
query.Topics[0][3] = backendabi.L1SentMessageEventSignature
|
||||||
|
query.Topics[0][4] = backendabi.L1DepositERC721Sig
|
||||||
|
query.Topics[0][5] = backendabi.L1DepositERC1155Sig
|
||||||
|
query.Topics[0][6] = backendabi.L1DepositWETHSig
|
||||||
|
|
||||||
|
logs, err := client.FilterLogs(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Failed to get l1 event logs", "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
depositL1CrossMsgs, relayedMsg, err := utils.ParseBackendL1EventLogs(logs)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("l1FetchAndSaveEvents: Failed to parse cross msg event logs", "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = db.Transaction(func(tx *gorm.DB) error {
|
||||||
|
if txErr := l1CrossMsgOrm.InsertL1CrossMsg(ctx, depositL1CrossMsgs, tx); txErr != nil {
|
||||||
|
log.Error("l1FetchAndSaveEvents: Failed to insert cross msg event logs", "err", txErr)
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
if txErr := relayedOrm.InsertRelayedMsg(ctx, relayedMsg, tx); txErr != nil {
|
||||||
|
log.Error("l1FetchAndSaveEvents: Failed to insert relayed msg event logs", "err", txErr)
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Crit("l2FetchAndSaveEvents: Failed to finish transaction", "err", err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// L2FetchAndSaveEvents fetche and save events on L2
|
||||||
|
func L2FetchAndSaveEvents(ctx context.Context, client *ethclient.Client, db *gorm.DB, from int64, to int64, addrList []common.Address) error {
|
||||||
|
l2CrossMsgOrm := orm.NewCrossMsg(db)
|
||||||
|
relayedOrm := orm.NewRelayedMsg(db)
|
||||||
|
l2SentMsgOrm := orm.NewL2SentMsg(db)
|
||||||
|
query := geth.FilterQuery{
|
||||||
|
FromBlock: big.NewInt(from), // inclusive
|
||||||
|
ToBlock: big.NewInt(to), // inclusive
|
||||||
|
Addresses: addrList,
|
||||||
|
Topics: make([][]common.Hash, 1),
|
||||||
|
}
|
||||||
|
query.Topics[0] = make([]common.Hash, 7)
|
||||||
|
query.Topics[0][0] = backendabi.L2WithdrawETHSig
|
||||||
|
query.Topics[0][1] = backendabi.L2WithdrawERC20Sig
|
||||||
|
query.Topics[0][2] = backendabi.L2RelayedMessageEventSignature
|
||||||
|
query.Topics[0][3] = backendabi.L2SentMessageEventSignature
|
||||||
|
query.Topics[0][4] = backendabi.L2WithdrawERC721Sig
|
||||||
|
query.Topics[0][5] = backendabi.L2WithdrawERC1155Sig
|
||||||
|
query.Topics[0][6] = backendabi.L2WithdrawWETHSig
|
||||||
|
|
||||||
|
logs, err := client.FilterLogs(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Failed to get l2 event logs", "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
depositL2CrossMsgs, relayedMsg, l2SentMsgs, err := utils.ParseBackendL2EventLogs(logs)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("l2FetchAndSaveEvents: Failed to parse cross msg event logs", "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.Transaction(func(tx *gorm.DB) error {
|
||||||
|
if txErr := l2CrossMsgOrm.InsertL2CrossMsg(ctx, depositL2CrossMsgs, tx); txErr != nil {
|
||||||
|
log.Error("l2FetchAndSaveEvents: Failed to insert cross msg event logs", "err", txErr)
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
|
||||||
|
if txErr := relayedOrm.InsertRelayedMsg(ctx, relayedMsg, tx); txErr != nil {
|
||||||
|
log.Error("l2FetchAndSaveEvents: Failed to insert relayed message event logs", "err", txErr)
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
|
||||||
|
if txErr := l2SentMsgOrm.InsertL2SentMsg(ctx, l2SentMsgs, tx); txErr != nil {
|
||||||
|
log.Error("l2FetchAndSaveEvents: Failed to insert l2 sent message", "err", txErr)
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Crit("l2FetchAndSaveEvents: Failed to begin db transaction", "err", err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchAndSaveBatchIndex fetche and save batch index
|
||||||
|
func FetchAndSaveBatchIndex(ctx context.Context, client *ethclient.Client, db *gorm.DB, from int64, to int64, scrollChainAddr common.Address) error {
|
||||||
|
rollupBatchOrm := orm.NewRollupBatch(db)
|
||||||
|
query := geth.FilterQuery{
|
||||||
|
FromBlock: big.NewInt(from), // inclusive
|
||||||
|
ToBlock: big.NewInt(to), // inclusive
|
||||||
|
Addresses: []common.Address{scrollChainAddr},
|
||||||
|
Topics: make([][]common.Hash, 1),
|
||||||
|
}
|
||||||
|
query.Topics[0] = make([]common.Hash, 1)
|
||||||
|
query.Topics[0][0] = backendabi.L1CommitBatchEventSignature
|
||||||
|
logs, err := client.FilterLogs(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Failed to get batch commit event logs", "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rollupBatches, err := utils.ParseBatchInfoFromScrollChain(ctx, client, logs)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("FetchAndSaveBatchIndex: Failed to parse batch commit msg event logs", "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if txErr := rollupBatchOrm.InsertRollupBatch(ctx, rollupBatches); txErr != nil {
|
||||||
|
log.Crit("FetchAndSaveBatchIndex: Failed to insert batch commit msg event logs", "err", txErr)
|
||||||
|
return txErr
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
237
bridge-history-api/crossmsg/messageproof/msg_proof_updater.go
Normal file
237
bridge-history-api/crossmsg/messageproof/msg_proof_updater.go
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
package messageproof
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
|
||||||
|
"bridge-history-api/orm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MsgProofUpdater is used to update message proof in db
|
||||||
|
type MsgProofUpdater struct {
|
||||||
|
ctx context.Context
|
||||||
|
db *gorm.DB
|
||||||
|
l2SentMsgOrm *orm.L2SentMsg
|
||||||
|
rollupOrm *orm.RollupBatch
|
||||||
|
withdrawTrie *WithdrawTrie
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgProofUpdater new MsgProofUpdater instance
|
||||||
|
func NewMsgProofUpdater(ctx context.Context, confirmations uint64, startBlock uint64, db *gorm.DB) *MsgProofUpdater {
|
||||||
|
return &MsgProofUpdater{
|
||||||
|
ctx: ctx,
|
||||||
|
db: db,
|
||||||
|
l2SentMsgOrm: orm.NewL2SentMsg(db),
|
||||||
|
rollupOrm: orm.NewRollupBatch(db),
|
||||||
|
withdrawTrie: NewWithdrawTrie(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the MsgProofUpdater
|
||||||
|
func (m *MsgProofUpdater) Start() {
|
||||||
|
log.Info("MsgProofUpdater Start")
|
||||||
|
m.initialize(m.ctx)
|
||||||
|
go func() {
|
||||||
|
tick := time.NewTicker(10 * time.Second)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-m.ctx.Done():
|
||||||
|
tick.Stop()
|
||||||
|
return
|
||||||
|
case <-tick.C:
|
||||||
|
latestBatch, err := m.rollupOrm.GetLatestRollupBatch(m.ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("MsgProofUpdater: Can not get latest RollupBatch: ", "err", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if latestBatch == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
latestBatchIndexWithProof, err := m.l2SentMsgOrm.GetLatestL2SentMsgBatchIndex(m.ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("MsgProofUpdater: Can not get latest L2SentMsgBatchIndex: ", "err", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var start uint64
|
||||||
|
if latestBatchIndexWithProof < 0 {
|
||||||
|
start = 1
|
||||||
|
} else {
|
||||||
|
start = uint64(latestBatchIndexWithProof) + 1
|
||||||
|
}
|
||||||
|
for i := start; i <= latestBatch.BatchIndex; i++ {
|
||||||
|
batch, err := m.rollupOrm.GetRollupBatchByIndex(m.ctx, i)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("MsgProofUpdater: Can not get RollupBatch: ", "err", err, "index", i)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// get all l2 messages in this batch
|
||||||
|
msgs, proofs, err := m.appendL2Messages(batch.StartBlockNumber, batch.EndBlockNumber)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("MsgProofUpdater: can not append l2messages", "startBlockNumber", batch.StartBlockNumber, "endBlockNumber", batch.EndBlockNumber, "err", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
err = m.updateMsgProof(msgs, proofs, batch.BatchIndex)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("MsgProofUpdater: can not update msg proof", "err", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the MsgProofUpdater
|
||||||
|
func (m *MsgProofUpdater) Stop() {
|
||||||
|
log.Info("MsgProofUpdater Stop")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MsgProofUpdater) initialize(ctx context.Context) {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
err := m.initializeWithdrawTrie()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("can not initialize withdraw trie", "err", err)
|
||||||
|
// give it some time to retry
|
||||||
|
time.Sleep(10 * time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MsgProofUpdater) initializeWithdrawTrie() error {
|
||||||
|
var batch *orm.RollupBatch
|
||||||
|
firstMsg, err := m.l2SentMsgOrm.GetL2SentMessageByNonce(m.ctx, 0)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get first l2 message: %v", err)
|
||||||
|
}
|
||||||
|
// no l2 message
|
||||||
|
// TO DO: check if we really dont have l2 sent message with nonce 0
|
||||||
|
if firstMsg == nil {
|
||||||
|
log.Info("No first l2sentmsg in db")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// if no batch, return and wait for next try round
|
||||||
|
batch, err = m.rollupOrm.GetLatestRollupBatch(m.ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get latest batch: %v", err)
|
||||||
|
}
|
||||||
|
if batch == nil {
|
||||||
|
return fmt.Errorf("no batch found")
|
||||||
|
}
|
||||||
|
|
||||||
|
var batches []*orm.RollupBatch
|
||||||
|
batchIndex := batch.BatchIndex
|
||||||
|
for {
|
||||||
|
var msg *orm.L2SentMsg
|
||||||
|
msg, err = m.l2SentMsgOrm.GetLatestL2SentMsgLEHeight(m.ctx, batch.EndBlockNumber)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("failed to get l2 sent message less than height", "endBlocknum", batch.EndBlockNumber, "err", err)
|
||||||
|
}
|
||||||
|
if msg != nil && msg.MsgProof != "" {
|
||||||
|
log.Info("Found latest l2 sent msg with proof: ", "msg_proof", msg.MsgProof, "height", msg.Height, "msg_hash", msg.MsgHash)
|
||||||
|
// initialize withdrawTrie
|
||||||
|
proofBytes := common.Hex2Bytes(msg.MsgProof)
|
||||||
|
m.withdrawTrie.Initialize(msg.Nonce, common.HexToHash(msg.MsgHash), proofBytes)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// append unprocessed batch
|
||||||
|
batches = append(batches, batch)
|
||||||
|
|
||||||
|
if batchIndex == 1 {
|
||||||
|
// otherwise overflow
|
||||||
|
// and batchIndex 0 is not in DB
|
||||||
|
// To Do: check if we dont have batch with index 0 in future
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// iterate for next batch
|
||||||
|
batchIndex--
|
||||||
|
|
||||||
|
batch, err = m.rollupOrm.GetRollupBatchByIndex(m.ctx, batchIndex)
|
||||||
|
if err != nil || batch == nil {
|
||||||
|
return fmt.Errorf("failed to get block batch %v: %v", batchIndex, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Build withdraw trie with pending messages")
|
||||||
|
for i := len(batches) - 1; i >= 0; i-- {
|
||||||
|
b := batches[i]
|
||||||
|
msgs, proofs, err := m.appendL2Messages(b.StartBlockNumber, b.EndBlockNumber)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = m.updateMsgProof(msgs, proofs, b.BatchIndex)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Info("Build withdraw trie finished")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MsgProofUpdater) updateMsgProof(msgs []*orm.L2SentMsg, proofs [][]byte, batchIndex uint64) error {
|
||||||
|
if len(msgs) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// this should not happen, but double check
|
||||||
|
if len(msgs) != len(proofs) {
|
||||||
|
return fmt.Errorf("illegal state: len(msgs) != len(proofs)")
|
||||||
|
}
|
||||||
|
err := m.db.Transaction(func(tx *gorm.DB) error {
|
||||||
|
for i, msg := range msgs {
|
||||||
|
proofHex := common.Bytes2Hex(proofs[i])
|
||||||
|
log.Debug("updateMsgProof", "msgHash", msg.MsgHash, "batchIndex", batchIndex, "proof", proofHex)
|
||||||
|
if err := m.l2SentMsgOrm.UpdateL2MessageProof(m.ctx, msg.MsgHash, proofHex, batchIndex, tx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendL2Messages will append all messages between firstBlock and lastBlock (both inclusive) to withdrawTrie and compute corresponding merkle proof of each message.
|
||||||
|
func (m *MsgProofUpdater) appendL2Messages(firstBlock, lastBlock uint64) ([]*orm.L2SentMsg, [][]byte, error) {
|
||||||
|
var msgProofs [][]byte
|
||||||
|
messages, err := m.l2SentMsgOrm.GetL2SentMsgMsgHashByHeightRange(m.ctx, firstBlock, lastBlock)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("GetL2SentMsgMsgHashByHeightRange failed", "error", err, "firstBlock", firstBlock, "lastBlock", lastBlock)
|
||||||
|
return messages, msgProofs, err
|
||||||
|
}
|
||||||
|
if len(messages) == 0 {
|
||||||
|
return messages, msgProofs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// double check whether nonce is matched
|
||||||
|
if messages[0].Nonce != m.withdrawTrie.NextMessageNonce {
|
||||||
|
log.Error("L2 message nonce mismatch", "expected", m.withdrawTrie.NextMessageNonce, "found", messages[0].Nonce)
|
||||||
|
return messages, msgProofs, fmt.Errorf("l2 message nonce mismatch, expected: %v, found: %v", m.withdrawTrie.NextMessageNonce, messages[0].Nonce)
|
||||||
|
}
|
||||||
|
|
||||||
|
var hashes []common.Hash
|
||||||
|
for _, msg := range messages {
|
||||||
|
hashes = append(hashes, common.HexToHash(msg.MsgHash))
|
||||||
|
}
|
||||||
|
msgProofs = m.withdrawTrie.AppendMessages(hashes)
|
||||||
|
|
||||||
|
return messages, msgProofs, nil
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package cross_msg
|
package messageproof
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
@@ -42,7 +42,6 @@ func NewWithdrawTrie() *WithdrawTrie {
|
|||||||
func (w *WithdrawTrie) Initialize(currentMessageNonce uint64, msgHash common.Hash, proofBytes []byte) {
|
func (w *WithdrawTrie) Initialize(currentMessageNonce uint64, msgHash common.Hash, proofBytes []byte) {
|
||||||
proof := DecodeBytesToMerkleProof(proofBytes)
|
proof := DecodeBytesToMerkleProof(proofBytes)
|
||||||
branches := RecoverBranchFromProof(proof, currentMessageNonce, msgHash)
|
branches := RecoverBranchFromProof(proof, currentMessageNonce, msgHash)
|
||||||
|
|
||||||
w.height = len(proof)
|
w.height = len(proof)
|
||||||
w.branches = branches
|
w.branches = branches
|
||||||
w.NextMessageNonce = currentMessageNonce + 1
|
w.NextMessageNonce = currentMessageNonce + 1
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package cross_msg_test
|
package messageproof
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/big"
|
"math/big"
|
||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"bridge-history-api/cross_msg"
|
|
||||||
"bridge-history-api/utils"
|
"bridge-history-api/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -19,29 +18,29 @@ func TestUpdateBranchWithNewMessage(t *testing.T) {
|
|||||||
zeroes[i] = utils.Keccak2(zeroes[i-1], zeroes[i-1])
|
zeroes[i] = utils.Keccak2(zeroes[i-1], zeroes[i-1])
|
||||||
}
|
}
|
||||||
|
|
||||||
cross_msg.UpdateBranchWithNewMessage(zeroes, branches, 0, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000001"))
|
UpdateBranchWithNewMessage(zeroes, branches, 0, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000001"))
|
||||||
if branches[0] != common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000001") {
|
if branches[0] != common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000001") {
|
||||||
t.Fatalf("Invalid root, want %s, got %s", "0x0000000000000000000000000000000000000000000000000000000000000001", branches[0].Hex())
|
t.Fatalf("Invalid root, want %s, got %s", "0x0000000000000000000000000000000000000000000000000000000000000001", branches[0].Hex())
|
||||||
}
|
}
|
||||||
|
|
||||||
cross_msg.UpdateBranchWithNewMessage(zeroes, branches, 1, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000002"))
|
UpdateBranchWithNewMessage(zeroes, branches, 1, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000002"))
|
||||||
if branches[1] != common.HexToHash("0xe90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e0") {
|
if branches[1] != common.HexToHash("0xe90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e0") {
|
||||||
t.Fatalf("Invalid root, want %s, got %s", "0xe90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e0", branches[1].Hex())
|
t.Fatalf("Invalid root, want %s, got %s", "0xe90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e0", branches[1].Hex())
|
||||||
}
|
}
|
||||||
|
|
||||||
cross_msg.UpdateBranchWithNewMessage(zeroes, branches, 2, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000003"))
|
UpdateBranchWithNewMessage(zeroes, branches, 2, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000003"))
|
||||||
if branches[2] != common.HexToHash("0x222ff5e0b5877792c2bc1670e2ccd0c2c97cd7bb1672a57d598db05092d3d72c") {
|
if branches[2] != common.HexToHash("0x222ff5e0b5877792c2bc1670e2ccd0c2c97cd7bb1672a57d598db05092d3d72c") {
|
||||||
t.Fatalf("Invalid root, want %s, got %s", "0x222ff5e0b5877792c2bc1670e2ccd0c2c97cd7bb1672a57d598db05092d3d72c", branches[2].Hex())
|
t.Fatalf("Invalid root, want %s, got %s", "0x222ff5e0b5877792c2bc1670e2ccd0c2c97cd7bb1672a57d598db05092d3d72c", branches[2].Hex())
|
||||||
}
|
}
|
||||||
|
|
||||||
cross_msg.UpdateBranchWithNewMessage(zeroes, branches, 3, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000004"))
|
UpdateBranchWithNewMessage(zeroes, branches, 3, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000004"))
|
||||||
if branches[2] != common.HexToHash("0xa9bb8c3f1f12e9aa903a50c47f314b57610a3ab32f2d463293f58836def38d36") {
|
if branches[2] != common.HexToHash("0xa9bb8c3f1f12e9aa903a50c47f314b57610a3ab32f2d463293f58836def38d36") {
|
||||||
t.Fatalf("Invalid root, want %s, got %s", "0xa9bb8c3f1f12e9aa903a50c47f314b57610a3ab32f2d463293f58836def38d36", branches[2].Hex())
|
t.Fatalf("Invalid root, want %s, got %s", "0xa9bb8c3f1f12e9aa903a50c47f314b57610a3ab32f2d463293f58836def38d36", branches[2].Hex())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDecodeEncodeMerkleProof(t *testing.T) {
|
func TestDecodeEncodeMerkleProof(t *testing.T) {
|
||||||
proof := cross_msg.DecodeBytesToMerkleProof(common.Hex2Bytes("2ebffc1a6671c51e30777a680904b103992630ec995b6e6ff76a04d5259d49012ebffc1a6671c51e30777a680904b103992630ec995b6e6ff76a04d5259d49022ebffc1a6671c51e30777a680904b103992630ec995b6e6ff76a04d5259d49032ebffc1a6671c51e30777a680904b103992630ec995b6e6ff76a04d5259d4904"))
|
proof := DecodeBytesToMerkleProof(common.Hex2Bytes("2ebffc1a6671c51e30777a680904b103992630ec995b6e6ff76a04d5259d49012ebffc1a6671c51e30777a680904b103992630ec995b6e6ff76a04d5259d49022ebffc1a6671c51e30777a680904b103992630ec995b6e6ff76a04d5259d49032ebffc1a6671c51e30777a680904b103992630ec995b6e6ff76a04d5259d4904"))
|
||||||
if len(proof) != 4 {
|
if len(proof) != 4 {
|
||||||
t.Fatalf("proof length mismatch, want %d, got %d", 4, len(proof))
|
t.Fatalf("proof length mismatch, want %d, got %d", 4, len(proof))
|
||||||
}
|
}
|
||||||
@@ -58,7 +57,7 @@ func TestDecodeEncodeMerkleProof(t *testing.T) {
|
|||||||
t.Fatalf("proof[3] mismatch, want %s, got %s", "0x2ebffc1a6671c51e30777a680904b103992630ec995b6e6ff76a04d5259d4904", proof[0].Hex())
|
t.Fatalf("proof[3] mismatch, want %s, got %s", "0x2ebffc1a6671c51e30777a680904b103992630ec995b6e6ff76a04d5259d4904", proof[0].Hex())
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes := cross_msg.EncodeMerkleProofToBytes(proof)
|
bytes := EncodeMerkleProofToBytes(proof)
|
||||||
if common.Bytes2Hex(bytes) != "2ebffc1a6671c51e30777a680904b103992630ec995b6e6ff76a04d5259d49012ebffc1a6671c51e30777a680904b103992630ec995b6e6ff76a04d5259d49022ebffc1a6671c51e30777a680904b103992630ec995b6e6ff76a04d5259d49032ebffc1a6671c51e30777a680904b103992630ec995b6e6ff76a04d5259d4904" {
|
if common.Bytes2Hex(bytes) != "2ebffc1a6671c51e30777a680904b103992630ec995b6e6ff76a04d5259d49012ebffc1a6671c51e30777a680904b103992630ec995b6e6ff76a04d5259d49022ebffc1a6671c51e30777a680904b103992630ec995b6e6ff76a04d5259d49032ebffc1a6671c51e30777a680904b103992630ec995b6e6ff76a04d5259d4904" {
|
||||||
t.Fatalf("wrong encoded bytes")
|
t.Fatalf("wrong encoded bytes")
|
||||||
}
|
}
|
||||||
@@ -72,32 +71,32 @@ func TestRecoverBranchFromProof(t *testing.T) {
|
|||||||
zeroes[i] = utils.Keccak2(zeroes[i-1], zeroes[i-1])
|
zeroes[i] = utils.Keccak2(zeroes[i-1], zeroes[i-1])
|
||||||
}
|
}
|
||||||
|
|
||||||
proof := cross_msg.UpdateBranchWithNewMessage(zeroes, branches, 0, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000001"))
|
proof := UpdateBranchWithNewMessage(zeroes, branches, 0, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000001"))
|
||||||
tmpBranches := cross_msg.RecoverBranchFromProof(proof, 0, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000001"))
|
tmpBranches := RecoverBranchFromProof(proof, 0, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000001"))
|
||||||
for i := 0; i < 64; i++ {
|
for i := 0; i < 64; i++ {
|
||||||
if tmpBranches[i] != branches[i] {
|
if tmpBranches[i] != branches[i] {
|
||||||
t.Fatalf("Invalid branch, want %s, got %s", branches[i].Hex(), tmpBranches[i].Hex())
|
t.Fatalf("Invalid branch, want %s, got %s", branches[i].Hex(), tmpBranches[i].Hex())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
proof = cross_msg.UpdateBranchWithNewMessage(zeroes, branches, 1, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000002"))
|
proof = UpdateBranchWithNewMessage(zeroes, branches, 1, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000002"))
|
||||||
tmpBranches = cross_msg.RecoverBranchFromProof(proof, 1, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000002"))
|
tmpBranches = RecoverBranchFromProof(proof, 1, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000002"))
|
||||||
for i := 0; i < 64; i++ {
|
for i := 0; i < 64; i++ {
|
||||||
if tmpBranches[i] != branches[i] {
|
if tmpBranches[i] != branches[i] {
|
||||||
t.Fatalf("Invalid branch, want %s, got %s", branches[i].Hex(), tmpBranches[i].Hex())
|
t.Fatalf("Invalid branch, want %s, got %s", branches[i].Hex(), tmpBranches[i].Hex())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
proof = cross_msg.UpdateBranchWithNewMessage(zeroes, branches, 2, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000003"))
|
proof = UpdateBranchWithNewMessage(zeroes, branches, 2, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000003"))
|
||||||
tmpBranches = cross_msg.RecoverBranchFromProof(proof, 2, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000003"))
|
tmpBranches = RecoverBranchFromProof(proof, 2, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000003"))
|
||||||
for i := 0; i < 64; i++ {
|
for i := 0; i < 64; i++ {
|
||||||
if tmpBranches[i] != branches[i] {
|
if tmpBranches[i] != branches[i] {
|
||||||
t.Fatalf("Invalid branch, want %s, got %s", branches[i].Hex(), tmpBranches[i].Hex())
|
t.Fatalf("Invalid branch, want %s, got %s", branches[i].Hex(), tmpBranches[i].Hex())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
proof = cross_msg.UpdateBranchWithNewMessage(zeroes, branches, 3, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000004"))
|
proof = UpdateBranchWithNewMessage(zeroes, branches, 3, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000004"))
|
||||||
tmpBranches = cross_msg.RecoverBranchFromProof(proof, 3, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000004"))
|
tmpBranches = RecoverBranchFromProof(proof, 3, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000004"))
|
||||||
for i := 0; i < 64; i++ {
|
for i := 0; i < 64; i++ {
|
||||||
if tmpBranches[i] != branches[i] {
|
if tmpBranches[i] != branches[i] {
|
||||||
t.Fatalf("Invalid branch, want %s, got %s", branches[i].Hex(), tmpBranches[i].Hex())
|
t.Fatalf("Invalid branch, want %s, got %s", branches[i].Hex(), tmpBranches[i].Hex())
|
||||||
@@ -107,7 +106,7 @@ func TestRecoverBranchFromProof(t *testing.T) {
|
|||||||
|
|
||||||
func TestWithdrawTrieOneByOne(t *testing.T) {
|
func TestWithdrawTrieOneByOne(t *testing.T) {
|
||||||
for initial := 0; initial < 128; initial++ {
|
for initial := 0; initial < 128; initial++ {
|
||||||
withdrawTrie := cross_msg.NewWithdrawTrie()
|
withdrawTrie := NewWithdrawTrie()
|
||||||
var hashes []common.Hash
|
var hashes []common.Hash
|
||||||
for i := 0; i < initial; i++ {
|
for i := 0; i < initial; i++ {
|
||||||
hash := common.BigToHash(big.NewInt(int64(i + 1)))
|
hash := common.BigToHash(big.NewInt(int64(i + 1)))
|
||||||
@@ -126,7 +125,7 @@ func TestWithdrawTrieOneByOne(t *testing.T) {
|
|||||||
})
|
})
|
||||||
assert.Equal(t, withdrawTrie.NextMessageNonce, uint64(i+1))
|
assert.Equal(t, withdrawTrie.NextMessageNonce, uint64(i+1))
|
||||||
assert.Equal(t, expectedRoot.String(), withdrawTrie.MessageRoot().String())
|
assert.Equal(t, expectedRoot.String(), withdrawTrie.MessageRoot().String())
|
||||||
proof := cross_msg.DecodeBytesToMerkleProof(proofBytes[0])
|
proof := DecodeBytesToMerkleProof(proofBytes[0])
|
||||||
verifiedRoot := verifyMerkleProof(uint64(i), hash, proof)
|
verifiedRoot := verifyMerkleProof(uint64(i), hash, proof)
|
||||||
assert.Equal(t, expectedRoot.String(), verifiedRoot.String())
|
assert.Equal(t, expectedRoot.String(), verifiedRoot.String())
|
||||||
}
|
}
|
||||||
@@ -153,7 +152,7 @@ func TestWithdrawTrieMultiple(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for finish := initial; finish < 100; finish++ {
|
for finish := initial; finish < 100; finish++ {
|
||||||
withdrawTrie := cross_msg.NewWithdrawTrie()
|
withdrawTrie := NewWithdrawTrie()
|
||||||
withdrawTrie.AppendMessages(hashes)
|
withdrawTrie.AppendMessages(hashes)
|
||||||
|
|
||||||
var newHashes []common.Hash
|
var newHashes []common.Hash
|
||||||
@@ -167,7 +166,7 @@ func TestWithdrawTrieMultiple(t *testing.T) {
|
|||||||
|
|
||||||
for i := initial; i <= finish; i++ {
|
for i := initial; i <= finish; i++ {
|
||||||
hash := common.BigToHash(big.NewInt(int64(i + 1)))
|
hash := common.BigToHash(big.NewInt(int64(i + 1)))
|
||||||
proof := cross_msg.DecodeBytesToMerkleProof(proofBytes[i-initial])
|
proof := DecodeBytesToMerkleProof(proofBytes[i-initial])
|
||||||
verifiedRoot := verifyMerkleProof(uint64(i), hash, proof)
|
verifiedRoot := verifyMerkleProof(uint64(i), hash, proof)
|
||||||
assert.Equal(t, expectedRoots[finish].String(), verifiedRoot.String())
|
assert.Equal(t, expectedRoots[finish].String(), verifiedRoot.String())
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package cross_msg_test
|
package crossmsg_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"bridge-history-api/cross_msg"
|
"bridge-history-api/crossmsg"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMergeIntoList(t *testing.T) {
|
func TestMergeIntoList(t *testing.T) {
|
||||||
@@ -18,7 +18,7 @@ func TestMergeIntoList(t *testing.T) {
|
|||||||
assert.Equal(t, headers[0].Hash(), headers[1].ParentHash)
|
assert.Equal(t, headers[0].Hash(), headers[1].ParentHash)
|
||||||
headers2, err := generateHeaders(18)
|
headers2, err := generateHeaders(18)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
result := cross_msg.MergeAddIntoHeaderList(headers, headers2, 64)
|
result := crossmsg.MergeAddIntoHeaderList(headers, headers2, 64)
|
||||||
assert.Equal(t, 64, len(result))
|
assert.Equal(t, 64, len(result))
|
||||||
assert.Equal(t, headers2[len(headers2)-1], result[len(result)-1])
|
assert.Equal(t, headers2[len(headers2)-1], result[len(result)-1])
|
||||||
assert.NotEqual(t, headers[0], result[0])
|
assert.NotEqual(t, headers[0], result[0])
|
||||||
@@ -53,7 +53,7 @@ func generateHeaders(amount int) ([]*types.Header, error) {
|
|||||||
Time: uint64(i * 15),
|
Time: uint64(i * 15),
|
||||||
Extra: []byte{},
|
Extra: []byte{},
|
||||||
MixDigest: common.Hash{},
|
MixDigest: common.Hash{},
|
||||||
Nonce: types.EncodeNonce(uint64(nonce.Uint64())),
|
Nonce: types.EncodeNonce(nonce.Uint64()),
|
||||||
}
|
}
|
||||||
headers[i] = header
|
headers[i] = header
|
||||||
}
|
}
|
||||||
108
bridge-history-api/crossmsg/reorg_handle.go
Normal file
108
bridge-history-api/crossmsg/reorg_handle.go
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
package crossmsg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
|
||||||
|
"bridge-history-api/orm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReorgHandling handles reorg function type
|
||||||
|
type ReorgHandling func(ctx context.Context, reorgHeight uint64, db *gorm.DB) error
|
||||||
|
|
||||||
|
func reverseArray(arr []*types.Header) []*types.Header {
|
||||||
|
for i := 0; i < len(arr)/2; i++ {
|
||||||
|
j := len(arr) - i - 1
|
||||||
|
arr[i], arr[j] = arr[j], arr[i]
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsParentAndChild match the child header ParentHash with parent header Hash
|
||||||
|
func IsParentAndChild(parentHeader *types.Header, header *types.Header) bool {
|
||||||
|
return header.ParentHash == parentHeader.Hash()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergeAddIntoHeaderList merges two header lists, if exceed the max length then drop the oldest entries
|
||||||
|
func MergeAddIntoHeaderList(baseArr, extraArr []*types.Header, maxLength int) []*types.Header {
|
||||||
|
mergedArr := append(baseArr, extraArr...)
|
||||||
|
if len(mergedArr) <= maxLength {
|
||||||
|
return mergedArr
|
||||||
|
}
|
||||||
|
|
||||||
|
startIndex := len(mergedArr) - maxLength
|
||||||
|
return mergedArr[startIndex:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// BackwardFindReorgBlock finds the reorg block by backward search
|
||||||
|
func BackwardFindReorgBlock(ctx context.Context, headers []*types.Header, client *ethclient.Client, lastHeader *types.Header) (int, bool, []*types.Header) {
|
||||||
|
maxStep := len(headers)
|
||||||
|
backwardHeaderList := []*types.Header{lastHeader}
|
||||||
|
for iterRound := 0; iterRound < maxStep; iterRound++ {
|
||||||
|
header, err := client.HeaderByHash(ctx, lastHeader.ParentHash)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("BackwardFindReorgBlock failed", "error", err)
|
||||||
|
return -1, false, nil
|
||||||
|
}
|
||||||
|
backwardHeaderList = append(backwardHeaderList, header)
|
||||||
|
for j := len(headers) - 1; j >= 0; j-- {
|
||||||
|
if IsParentAndChild(headers[j], header) {
|
||||||
|
backwardHeaderList = reverseArray(backwardHeaderList)
|
||||||
|
return j, true, backwardHeaderList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastHeader = header
|
||||||
|
}
|
||||||
|
return -1, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// L1ReorgHandling handles l1 reorg
|
||||||
|
func L1ReorgHandling(ctx context.Context, reorgHeight uint64, db *gorm.DB) error {
|
||||||
|
l1CrossMsgOrm := orm.NewCrossMsg(db)
|
||||||
|
relayedOrm := orm.NewRelayedMsg(db)
|
||||||
|
err := db.Transaction(func(tx *gorm.DB) error {
|
||||||
|
if err := l1CrossMsgOrm.DeleteL1CrossMsgAfterHeight(ctx, reorgHeight, tx); err != nil {
|
||||||
|
log.Error("delete l1 cross msg from height", "height", reorgHeight, "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := relayedOrm.DeleteL1RelayedHashAfterHeight(ctx, reorgHeight, tx); err != nil {
|
||||||
|
log.Error("delete l1 relayed msg from height", "height", reorgHeight, "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Crit("l1 reorg handling failed", "err", err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// L2ReorgHandling handles l2 reorg
|
||||||
|
func L2ReorgHandling(ctx context.Context, reorgHeight uint64, db *gorm.DB) error {
|
||||||
|
l2CrossMsgOrm := orm.NewCrossMsg(db)
|
||||||
|
relayedOrm := orm.NewRelayedMsg(db)
|
||||||
|
l2SentMsgOrm := orm.NewL2SentMsg(db)
|
||||||
|
err := db.Transaction(func(tx *gorm.DB) error {
|
||||||
|
if err := l2CrossMsgOrm.DeleteL2CrossMsgFromHeight(ctx, reorgHeight, tx); err != nil {
|
||||||
|
log.Error("delete l2 cross msg from height", "height", reorgHeight, "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := relayedOrm.DeleteL2RelayedHashAfterHeight(ctx, reorgHeight, tx); err != nil {
|
||||||
|
log.Error("delete l2 relayed msg from height", "height", reorgHeight, "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := l2SentMsgOrm.DeleteL2SentMsgAfterHeight(ctx, reorgHeight, tx); err != nil {
|
||||||
|
log.Error("delete l2 sent msg from height", "height", reorgHeight, "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Crit("l2 reorg handling failed", "err", err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
package orm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
)
|
|
||||||
|
|
||||||
type AssetType int
|
|
||||||
type MsgType int
|
|
||||||
|
|
||||||
func (a AssetType) String() string {
|
|
||||||
switch a {
|
|
||||||
case ETH:
|
|
||||||
return "ETH"
|
|
||||||
case ERC20:
|
|
||||||
return "ERC20"
|
|
||||||
case ERC1155:
|
|
||||||
return "ERC1155"
|
|
||||||
case ERC721:
|
|
||||||
return "ERC721"
|
|
||||||
}
|
|
||||||
return "Unknown Asset Type"
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
ETH AssetType = iota
|
|
||||||
ERC20
|
|
||||||
ERC721
|
|
||||||
ERC1155
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
UnknownMsg MsgType = iota
|
|
||||||
Layer1Msg
|
|
||||||
Layer2Msg
|
|
||||||
)
|
|
||||||
|
|
||||||
// CrossMsg represents a cross message from layer 1 to layer 2
|
|
||||||
type CrossMsg struct {
|
|
||||||
ID uint64 `json:"id" db:"id"`
|
|
||||||
MsgHash string `json:"msg_hash" db:"msg_hash"`
|
|
||||||
Height uint64 `json:"height" db:"height"`
|
|
||||||
Sender string `json:"sender" db:"sender"`
|
|
||||||
Target string `json:"target" db:"target"`
|
|
||||||
Amount string `json:"amount" db:"amount"`
|
|
||||||
Layer1Hash string `json:"layer1_hash" db:"layer1_hash"`
|
|
||||||
Layer2Hash string `json:"layer2_hash" db:"layer2_hash"`
|
|
||||||
Layer1Token string `json:"layer1_token" db:"layer1_token"`
|
|
||||||
Layer2Token string `json:"layer2_token" db:"layer2_token"`
|
|
||||||
TokenID uint64 `json:"token_id" db:"token_id"`
|
|
||||||
Asset int `json:"asset" db:"asset"`
|
|
||||||
MsgType int `json:"msg_type" db:"msg_type"`
|
|
||||||
IsDeleted bool `json:"is_deleted" db:"is_deleted"`
|
|
||||||
Timestamp *time.Time `json:"timestamp" db:"block_timestamp"`
|
|
||||||
CreatedAt *time.Time `json:"created_at" db:"created_at"`
|
|
||||||
UpdatedAt *time.Time `json:"updated_at" db:"updated_at"`
|
|
||||||
DeletedAt *time.Time `json:"deleted_at" db:"deleted_at"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RelayedMsg struct {
|
|
||||||
MsgHash string `json:"msg_hash" db:"msg_hash"`
|
|
||||||
Height uint64 `json:"height" db:"height"`
|
|
||||||
Layer1Hash string `json:"layer1_hash" db:"layer1_hash"`
|
|
||||||
Layer2Hash string `json:"layer2_hash" db:"layer2_hash"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// L1CrossMsgOrm provides operations on l1_cross_message table
|
|
||||||
type L1CrossMsgOrm interface {
|
|
||||||
GetL1CrossMsgByHash(l1Hash common.Hash) (*CrossMsg, error)
|
|
||||||
GetL1CrossMsgsByAddress(sender common.Address) ([]*CrossMsg, error)
|
|
||||||
BatchInsertL1CrossMsgDBTx(dbTx *sqlx.Tx, messages []*CrossMsg) error
|
|
||||||
// UpdateL1CrossMsgHash invoked when SentMessage event is received
|
|
||||||
UpdateL1CrossMsgHashDBTx(ctx context.Context, dbTx *sqlx.Tx, l1Hash, msgHash common.Hash) error
|
|
||||||
UpdateL1CrossMsgHash(ctx context.Context, l1Hash, msgHash common.Hash) error
|
|
||||||
GetLatestL1ProcessedHeight() (int64, error)
|
|
||||||
DeleteL1CrossMsgAfterHeightDBTx(dbTx *sqlx.Tx, height int64) error
|
|
||||||
UpdateL1Blocktimestamp(height uint64, timestamp time.Time) error
|
|
||||||
GetL1EarliestNoBlocktimestampHeight() (uint64, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// L2CrossMsgOrm provides operations on l2_cross_message table
|
|
||||||
type L2CrossMsgOrm interface {
|
|
||||||
GetL2CrossMsgByHash(l2Hash common.Hash) (*CrossMsg, error)
|
|
||||||
GetL2CrossMsgByAddress(sender common.Address) ([]*CrossMsg, error)
|
|
||||||
BatchInsertL2CrossMsgDBTx(dbTx *sqlx.Tx, messages []*CrossMsg) error
|
|
||||||
// UpdateL2CrossMsgHash invoked when SentMessage event is received
|
|
||||||
UpdateL2CrossMsgHashDBTx(ctx context.Context, dbTx *sqlx.Tx, l2Hash, msgHash common.Hash) error
|
|
||||||
UpdateL2CrossMsgHash(ctx context.Context, l2Hash, msgHash common.Hash) error
|
|
||||||
GetLatestL2ProcessedHeight() (int64, error)
|
|
||||||
DeleteL2CrossMsgFromHeightDBTx(dbTx *sqlx.Tx, height int64) error
|
|
||||||
UpdateL2Blocktimestamp(height uint64, timestamp time.Time) error
|
|
||||||
GetL2EarliestNoBlocktimestampHeight() (uint64, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type RelayedMsgOrm interface {
|
|
||||||
BatchInsertRelayedMsgDBTx(dbTx *sqlx.Tx, messages []*RelayedMsg) error
|
|
||||||
GetRelayedMsgByHash(msg_hash string) (*RelayedMsg, error)
|
|
||||||
GetLatestRelayedHeightOnL1() (int64, error)
|
|
||||||
GetLatestRelayedHeightOnL2() (int64, error)
|
|
||||||
DeleteL1RelayedHashAfterHeightDBTx(dbTx *sqlx.Tx, height int64) error
|
|
||||||
DeleteL2RelayedHashAfterHeightDBTx(dbTx *sqlx.Tx, height int64) error
|
|
||||||
}
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
package orm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"database/sql"
|
|
||||||
"errors"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
)
|
|
||||||
|
|
||||||
type l1CrossMsgOrm struct {
|
|
||||||
db *sqlx.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewL1CrossMsgOrm create an NewL1CrossMsgOrm instance
|
|
||||||
func NewL1CrossMsgOrm(db *sqlx.DB) L1CrossMsgOrm {
|
|
||||||
return &l1CrossMsgOrm{db: db}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *l1CrossMsgOrm) GetL1CrossMsgByHash(l1Hash common.Hash) (*CrossMsg, error) {
|
|
||||||
result := &CrossMsg{}
|
|
||||||
row := l.db.QueryRowx(`SELECT * FROM cross_message WHERE layer1_hash = $1 AND msg_type = $2 AND NOT is_deleted;`, l1Hash.String(), Layer1Msg)
|
|
||||||
if err := row.StructScan(result); err != nil {
|
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetL1CrossMsgsByAddress returns all layer1 cross messages under given address
|
|
||||||
// Warning: return empty slice if no data found
|
|
||||||
func (l *l1CrossMsgOrm) GetL1CrossMsgsByAddress(sender common.Address) ([]*CrossMsg, error) {
|
|
||||||
var results []*CrossMsg
|
|
||||||
rows, err := l.db.Queryx(`SELECT * FROM cross_message WHERE sender = $1 AND msg_type = 1 AND NOT is_deleted;`, sender.String(), Layer1Msg)
|
|
||||||
|
|
||||||
for rows.Next() {
|
|
||||||
msg := &CrossMsg{}
|
|
||||||
if err = rows.StructScan(msg); err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
results = append(results, msg)
|
|
||||||
}
|
|
||||||
if len(results) == 0 && errors.Is(err, sql.ErrNoRows) {
|
|
||||||
} else if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return results, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *l1CrossMsgOrm) BatchInsertL1CrossMsgDBTx(dbTx *sqlx.Tx, messages []*CrossMsg) error {
|
|
||||||
if len(messages) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
messageMaps := make([]map[string]interface{}, len(messages))
|
|
||||||
for i, msg := range messages {
|
|
||||||
messageMaps[i] = map[string]interface{}{
|
|
||||||
"height": msg.Height,
|
|
||||||
"sender": msg.Sender,
|
|
||||||
"target": msg.Target,
|
|
||||||
"amount": msg.Amount,
|
|
||||||
"asset": msg.Asset,
|
|
||||||
"layer1_hash": msg.Layer1Hash,
|
|
||||||
"layer1_token": msg.Layer1Token,
|
|
||||||
"layer2_token": msg.Layer2Token,
|
|
||||||
"token_id": msg.TokenID,
|
|
||||||
"msg_type": Layer1Msg,
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = dbTx.NamedExec(`insert into cross_message(height, sender, target, asset, layer1_hash, layer1_token, layer2_token, token_id, amount, msg_type) select :height, :sender, :target, :asset, :layer1_hash, :layer1_token, :layer2_token, :token_id, :amount, :msg_type WHERE NOT EXISTS (SELECT 1 FROM cross_message WHERE layer1_hash = :layer1_hash AND NOT is_deleted);`, messageMaps[i])
|
|
||||||
if err != nil {
|
|
||||||
log.Error("BatchInsertL1CrossMsgDBTx: failed to insert l1 cross msgs", "l1hashes", msg.Layer1Hash, "heights", msg.Height, "err", err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateL1CrossMsgHashDBTx update l1 cross msg hash in db, no need to check msg_type since layer1_hash wont be empty if its layer1 msg
|
|
||||||
func (l *l1CrossMsgOrm) UpdateL1CrossMsgHashDBTx(ctx context.Context, dbTx *sqlx.Tx, l1Hash, msgHash common.Hash) error {
|
|
||||||
if _, err := dbTx.ExecContext(ctx, l.db.Rebind("update public.cross_message set msg_hash = ? where layer1_hash = ? AND NOT is_deleted;"), msgHash.String(), l1Hash.String()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *l1CrossMsgOrm) UpdateL1CrossMsgHash(ctx context.Context, l1Hash, msgHash common.Hash) error {
|
|
||||||
if _, err := l.db.ExecContext(ctx, l.db.Rebind("update public.l1_cross_message set msg_hash = ? where layer1_hash = ? AND NOT is_deleted;"), msgHash.String(), l1Hash.String()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *l1CrossMsgOrm) GetLatestL1ProcessedHeight() (int64, error) {
|
|
||||||
row := l.db.QueryRowx(`SELECT height FROM cross_message WHERE msg_type = $1 AND NOT is_deleted ORDER BY id DESC LIMIT 1;`, Layer1Msg)
|
|
||||||
var result sql.NullInt64
|
|
||||||
if err := row.Scan(&result); err != nil {
|
|
||||||
if err == sql.ErrNoRows || !result.Valid {
|
|
||||||
return -1, nil
|
|
||||||
}
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
if result.Valid {
|
|
||||||
return result.Int64, nil
|
|
||||||
}
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *l1CrossMsgOrm) DeleteL1CrossMsgAfterHeightDBTx(dbTx *sqlx.Tx, height int64) error {
|
|
||||||
if _, err := l.db.Exec(`UPDATE cross_message SET is_deleted = true WHERE height > $1 AND msg_type = $2;`, height, Layer1Msg); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *l1CrossMsgOrm) UpdateL1Blocktimestamp(height uint64, timestamp time.Time) error {
|
|
||||||
if _, err := l.db.Exec(`UPDATE cross_message SET block_timestamp = $1 where height = $2 AND msg_type = $3 AND NOT is_deleted`, timestamp, height, Layer1Msg); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *l1CrossMsgOrm) GetL1EarliestNoBlocktimestampHeight() (uint64, error) {
|
|
||||||
row := l.db.QueryRowx(`SELECT height FROM cross_message WHERE block_timestamp IS NULL AND msg_type = $1 AND NOT is_deleted ORDER BY height ASC LIMIT 1;`, Layer1Msg)
|
|
||||||
var result uint64
|
|
||||||
if err := row.Scan(&result); err != nil {
|
|
||||||
if err == sql.ErrNoRows {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
package orm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"database/sql"
|
|
||||||
"errors"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
)
|
|
||||||
|
|
||||||
type l2CrossMsgOrm struct {
|
|
||||||
db *sqlx.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewL2CrossMsgOrm create an NewL2CrossMsgOrm instance
|
|
||||||
func NewL2CrossMsgOrm(db *sqlx.DB) L2CrossMsgOrm {
|
|
||||||
return &l2CrossMsgOrm{db: db}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *l2CrossMsgOrm) GetL2CrossMsgByHash(l2Hash common.Hash) (*CrossMsg, error) {
|
|
||||||
result := &CrossMsg{}
|
|
||||||
row := l.db.QueryRowx(`SELECT * FROM cross_message WHERE layer2_hash = $1 AND NOT is_deleted;`, l2Hash.String())
|
|
||||||
if err := row.StructScan(result); err != nil {
|
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetL2CrossMsgsByAddress returns all layer2 cross messages under given address
|
|
||||||
// Warning: return empty slice if no data found
|
|
||||||
func (l *l2CrossMsgOrm) GetL2CrossMsgByAddress(sender common.Address) ([]*CrossMsg, error) {
|
|
||||||
var results []*CrossMsg
|
|
||||||
rows, err := l.db.Queryx(`SELECT * FROM cross_message WHERE sender = $1 AND msg_type = $2 AND NOT is_deleted;`, sender.String(), Layer2Msg)
|
|
||||||
|
|
||||||
for rows.Next() {
|
|
||||||
msg := &CrossMsg{}
|
|
||||||
if err = rows.StructScan(msg); err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
results = append(results, msg)
|
|
||||||
}
|
|
||||||
if len(results) == 0 && errors.Is(err, sql.ErrNoRows) {
|
|
||||||
// log.Warn("no unprocessed layer1 messages in db", "err", err)
|
|
||||||
} else if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return results, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *l2CrossMsgOrm) DeleteL2CrossMsgFromHeightDBTx(dbTx *sqlx.Tx, height int64) error {
|
|
||||||
_, err := dbTx.Exec(`UPDATE cross_message SET is_deleted = true where height > $1 AND msg_type = $2 ;`, height, Layer2Msg)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("DeleteL1CrossMsgAfterHeightDBTx: failed to delete", "height", height, "err", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *l2CrossMsgOrm) BatchInsertL2CrossMsgDBTx(dbTx *sqlx.Tx, messages []*CrossMsg) error {
|
|
||||||
if len(messages) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
messageMaps := make([]map[string]interface{}, len(messages))
|
|
||||||
for i, msg := range messages {
|
|
||||||
|
|
||||||
messageMaps[i] = map[string]interface{}{
|
|
||||||
"height": msg.Height,
|
|
||||||
"sender": msg.Sender,
|
|
||||||
"target": msg.Target,
|
|
||||||
"asset": msg.Asset,
|
|
||||||
"layer2_hash": msg.Layer2Hash,
|
|
||||||
"layer1_token": msg.Layer1Token,
|
|
||||||
"layer2_token": msg.Layer2Token,
|
|
||||||
"token_id": msg.TokenID,
|
|
||||||
"amount": msg.Amount,
|
|
||||||
"msg_type": Layer2Msg,
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = dbTx.NamedExec(`insert into cross_message(height, sender, target, asset, layer2_hash, layer1_token, layer2_token, token_id, amount, msg_type) select :height, :sender, :target, :asset, :layer2_hash, :layer1_token, :layer2_token, :token_id, :amount, :msg_type WHERE NOT EXISTS (SELECT 1 FROM cross_message WHERE layer2_hash = :layer2_hash AND NOT is_deleted);`, messageMaps[i])
|
|
||||||
if err != nil {
|
|
||||||
log.Error("BatchInsertL2CrossMsgDBTx: failed to insert l2 cross msgs", "layer2hash", msg.Layer2Hash, "heights", msg.Height, "err", err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *l2CrossMsgOrm) UpdateL2CrossMsgHashDBTx(ctx context.Context, dbTx *sqlx.Tx, l2Hash, msgHash common.Hash) error {
|
|
||||||
if _, err := dbTx.ExecContext(ctx, l.db.Rebind("update public.cross_message set msg_hash = ? where layer2_hash = ? AND NOT is_deleted;"), msgHash.String(), l2Hash.String()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *l2CrossMsgOrm) UpdateL2CrossMsgHash(ctx context.Context, l2Hash, msgHash common.Hash) error {
|
|
||||||
if _, err := l.db.ExecContext(ctx, l.db.Rebind("update public.cross_message set msg_hash = ? where layer2_hash = ? AND NOT is_deleted;"), msgHash.String(), l2Hash.String()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *l2CrossMsgOrm) GetLatestL2ProcessedHeight() (int64, error) {
|
|
||||||
row := l.db.QueryRowx(`SELECT height FROM cross_message WHERE msg_type = $1 AND NOT is_deleted ORDER BY id DESC LIMIT 1;`, Layer2Msg)
|
|
||||||
var result sql.NullInt64
|
|
||||||
if err := row.Scan(&result); err != nil {
|
|
||||||
if err == sql.ErrNoRows || !result.Valid {
|
|
||||||
return -1, nil
|
|
||||||
}
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
if result.Valid {
|
|
||||||
return result.Int64, nil
|
|
||||||
}
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *l2CrossMsgOrm) UpdateL2Blocktimestamp(height uint64, timestamp time.Time) error {
|
|
||||||
if _, err := l.db.Exec(`UPDATE cross_message SET block_timestamp = $1 where height = $2 AND msg_type = $3 AND NOT is_deleted`, timestamp, height, Layer2Msg); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *l2CrossMsgOrm) GetL2EarliestNoBlocktimestampHeight() (uint64, error) {
|
|
||||||
row := l.db.QueryRowx(`SELECT height FROM cross_message WHERE block_timestamp IS NULL AND msg_type = $1 AND NOT is_deleted ORDER BY height ASC LIMIT 1;`, Layer2Msg)
|
|
||||||
var result uint64
|
|
||||||
if err := row.Scan(&result); err != nil {
|
|
||||||
if err == sql.ErrNoRows {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
package orm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"errors"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
)
|
|
||||||
|
|
||||||
type relayedMsgOrm struct {
|
|
||||||
db *sqlx.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRelayedMsgOrm create an NewRelayedMsgOrm instance
|
|
||||||
func NewRelayedMsgOrm(db *sqlx.DB) RelayedMsgOrm {
|
|
||||||
return &relayedMsgOrm{db: db}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *relayedMsgOrm) BatchInsertRelayedMsgDBTx(dbTx *sqlx.Tx, messages []*RelayedMsg) error {
|
|
||||||
if len(messages) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
messageMaps := make([]map[string]interface{}, len(messages))
|
|
||||||
for i, msg := range messages {
|
|
||||||
messageMaps[i] = map[string]interface{}{
|
|
||||||
"msg_hash": msg.MsgHash,
|
|
||||||
"height": msg.Height,
|
|
||||||
"layer1_hash": msg.Layer1Hash,
|
|
||||||
"layer2_hash": msg.Layer2Hash,
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = dbTx.NamedExec(`insert into relayed_msg(msg_hash, height, layer1_hash, layer2_hash) values(:msg_hash, :height, :layer1_hash, :layer2_hash);`, messageMaps[i])
|
|
||||||
if err != nil && !strings.Contains(err.Error(), "pq: duplicate key value violates unique constraint \"relayed_msg_hash_uindex") {
|
|
||||||
log.Error("BatchInsertRelayedMsgDBTx: failed to insert l1 cross msgs", "msg_Hashe", msg.MsgHash, "height", msg.Height, "err", err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *relayedMsgOrm) GetRelayedMsgByHash(msg_hash string) (*RelayedMsg, error) {
|
|
||||||
result := &RelayedMsg{}
|
|
||||||
row := l.db.QueryRowx(`SELECT msg_hash, height, layer1_hash, layer2_hash FROM relayed_msg WHERE msg_hash = $1 AND NOT is_deleted;`, msg_hash)
|
|
||||||
if err := row.StructScan(result); err != nil {
|
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *relayedMsgOrm) GetLatestRelayedHeightOnL1() (int64, error) {
|
|
||||||
row := l.db.QueryRow(`SELECT height FROM relayed_msg WHERE layer1_hash != '' AND NOT is_deleted ORDER BY height DESC LIMIT 1;`)
|
|
||||||
var result sql.NullInt64
|
|
||||||
if err := row.Scan(&result); err != nil {
|
|
||||||
if err == sql.ErrNoRows || !result.Valid {
|
|
||||||
return -1, nil
|
|
||||||
}
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
if result.Valid {
|
|
||||||
return result.Int64, nil
|
|
||||||
}
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *relayedMsgOrm) GetLatestRelayedHeightOnL2() (int64, error) {
|
|
||||||
row := l.db.QueryRow(`SELECT height FROM relayed_msg WHERE layer2_hash != '' AND NOT is_deleted ORDER BY height DESC LIMIT 1;`)
|
|
||||||
var result sql.NullInt64
|
|
||||||
if err := row.Scan(&result); err != nil {
|
|
||||||
if err == sql.ErrNoRows || !result.Valid {
|
|
||||||
return -1, nil
|
|
||||||
}
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
if result.Valid {
|
|
||||||
return result.Int64, nil
|
|
||||||
}
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *relayedMsgOrm) DeleteL1RelayedHashAfterHeightDBTx(dbTx *sqlx.Tx, height int64) error {
|
|
||||||
_, err := dbTx.Exec(`UPDATE relayed_msg SET is_deleted = true WHERE height > $1 AND layer1_hash != '';`, height)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *relayedMsgOrm) DeleteL2RelayedHashAfterHeightDBTx(dbTx *sqlx.Tx, height int64) error {
|
|
||||||
_, err := dbTx.Exec(`UPDATE relayed_msg SET is_deleted = true WHERE height > $1 AND layer2_hash != '';`, height)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
package db
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
_ "github.com/lib/pq" //nolint:golint
|
|
||||||
|
|
||||||
"bridge-history-api/config"
|
|
||||||
"bridge-history-api/db/orm"
|
|
||||||
)
|
|
||||||
|
|
||||||
// OrmFactory include all ormFactory interface
|
|
||||||
type OrmFactory interface {
|
|
||||||
orm.L1CrossMsgOrm
|
|
||||||
orm.L2CrossMsgOrm
|
|
||||||
orm.RelayedMsgOrm
|
|
||||||
GetTotalCrossMsgCountByAddress(sender string) (uint64, error)
|
|
||||||
GetCrossMsgsByAddressWithOffset(sender string, offset int64, limit int64) ([]*orm.CrossMsg, error)
|
|
||||||
GetDB() *sqlx.DB
|
|
||||||
Beginx() (*sqlx.Tx, error)
|
|
||||||
Close() error
|
|
||||||
}
|
|
||||||
|
|
||||||
type ormFactory struct {
|
|
||||||
orm.L1CrossMsgOrm
|
|
||||||
orm.L2CrossMsgOrm
|
|
||||||
orm.RelayedMsgOrm
|
|
||||||
*sqlx.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewOrmFactory create an ormFactory factory include all ormFactory interface
|
|
||||||
func NewOrmFactory(cfg *config.Config) (OrmFactory, error) {
|
|
||||||
// Initialize sql/sqlx
|
|
||||||
db, err := sqlx.Open(cfg.DB.DriverName, cfg.DB.DSN)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
db.SetMaxOpenConns(cfg.DB.MaxOpenNum)
|
|
||||||
db.SetMaxIdleConns(cfg.DB.MaxIdleNum)
|
|
||||||
if err = db.Ping(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &ormFactory{
|
|
||||||
L1CrossMsgOrm: orm.NewL1CrossMsgOrm(db),
|
|
||||||
L2CrossMsgOrm: orm.NewL2CrossMsgOrm(db),
|
|
||||||
RelayedMsgOrm: orm.NewRelayedMsgOrm(db),
|
|
||||||
DB: db,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *ormFactory) GetDB() *sqlx.DB {
|
|
||||||
return o.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *ormFactory) Beginx() (*sqlx.Tx, error) {
|
|
||||||
return o.DB.Beginx()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *ormFactory) GetTotalCrossMsgCountByAddress(sender string) (uint64, error) {
|
|
||||||
var count uint64
|
|
||||||
row := o.DB.QueryRowx(`SELECT COUNT(*) FROM cross_message WHERE sender = $1 AND NOT is_deleted;`, sender)
|
|
||||||
if err := row.Scan(&count); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return count, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *ormFactory) GetCrossMsgsByAddressWithOffset(sender string, offset int64, limit int64) ([]*orm.CrossMsg, error) {
|
|
||||||
para := sender
|
|
||||||
var results []*orm.CrossMsg
|
|
||||||
rows, err := o.DB.Queryx(`SELECT * FROM cross_message WHERE sender = $1 AND NOT is_deleted ORDER BY block_timestamp DESC NULLS FIRST, id DESC LIMIT $2 OFFSET $3;`, para, limit, offset)
|
|
||||||
if err != nil || rows == nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for rows.Next() {
|
|
||||||
msg := &orm.CrossMsg{}
|
|
||||||
if err = rows.StructScan(msg); err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
results = append(results, msg)
|
|
||||||
}
|
|
||||||
if len(results) == 0 && errors.Is(err, sql.ErrNoRows) {
|
|
||||||
} else if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return results, nil
|
|
||||||
}
|
|
||||||
@@ -5,19 +5,18 @@ go 1.19
|
|||||||
require (
|
require (
|
||||||
github.com/ethereum/go-ethereum v1.12.0
|
github.com/ethereum/go-ethereum v1.12.0
|
||||||
github.com/iris-contrib/middleware/cors v0.0.0-20230531125531-980d3a09a458
|
github.com/iris-contrib/middleware/cors v0.0.0-20230531125531-980d3a09a458
|
||||||
github.com/jmoiron/sqlx v1.3.5
|
|
||||||
github.com/kataras/iris/v12 v12.2.0
|
github.com/kataras/iris/v12 v12.2.0
|
||||||
github.com/lib/pq v1.10.7
|
|
||||||
github.com/mattn/go-colorable v0.1.13
|
github.com/mattn/go-colorable v0.1.13
|
||||||
github.com/mattn/go-isatty v0.0.18
|
github.com/mattn/go-isatty v0.0.19
|
||||||
github.com/modern-go/reflect2 v1.0.2
|
github.com/modern-go/reflect2 v1.0.2
|
||||||
github.com/pressly/goose/v3 v3.7.0
|
github.com/pressly/goose/v3 v3.7.0
|
||||||
github.com/stretchr/testify v1.8.2
|
github.com/stretchr/testify v1.8.3
|
||||||
github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa
|
github.com/urfave/cli/v2 v2.25.7
|
||||||
|
gorm.io/gorm v1.25.2
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/BurntSushi/toml v1.2.1 // indirect
|
github.com/BurntSushi/toml v1.3.2 // indirect
|
||||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect
|
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect
|
||||||
github.com/CloudyKit/jet/v6 v6.2.0 // indirect
|
github.com/CloudyKit/jet/v6 v6.2.0 // indirect
|
||||||
github.com/DataDog/zstd v1.5.2 // indirect
|
github.com/DataDog/zstd v1.5.2 // indirect
|
||||||
@@ -44,7 +43,7 @@ require (
|
|||||||
github.com/deckarep/golang-set/v2 v2.1.0 // indirect
|
github.com/deckarep/golang-set/v2 v2.1.0 // indirect
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
|
||||||
github.com/deepmap/oapi-codegen v1.8.2 // indirect
|
github.com/deepmap/oapi-codegen v1.8.2 // indirect
|
||||||
github.com/docker/docker v20.10.21+incompatible // indirect
|
github.com/docker/docker v23.0.6+incompatible // indirect
|
||||||
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 // indirect
|
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 // indirect
|
||||||
github.com/ethereum/c-kzg-4844 v0.2.0 // indirect
|
github.com/ethereum/c-kzg-4844 v0.2.0 // indirect
|
||||||
github.com/fatih/structs v1.1.0 // indirect
|
github.com/fatih/structs v1.1.0 // indirect
|
||||||
@@ -54,7 +53,6 @@ require (
|
|||||||
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect
|
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect
|
||||||
github.com/getsentry/sentry-go v0.18.0 // indirect
|
github.com/getsentry/sentry-go v0.18.0 // indirect
|
||||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
|
||||||
github.com/go-stack/stack v1.8.1 // indirect
|
github.com/go-stack/stack v1.8.1 // indirect
|
||||||
github.com/gobwas/httphead v0.1.0 // indirect
|
github.com/gobwas/httphead v0.1.0 // indirect
|
||||||
github.com/gobwas/pool v0.2.1 // indirect
|
github.com/gobwas/pool v0.2.1 // indirect
|
||||||
@@ -78,6 +76,8 @@ require (
|
|||||||
github.com/iris-contrib/go.uuid v2.0.0+incompatible // indirect
|
github.com/iris-contrib/go.uuid v2.0.0+incompatible // indirect
|
||||||
github.com/iris-contrib/schema v0.0.6 // indirect
|
github.com/iris-contrib/schema v0.0.6 // indirect
|
||||||
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
|
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
|
||||||
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/kataras/blocks v0.0.7 // indirect
|
github.com/kataras/blocks v0.0.7 // indirect
|
||||||
github.com/kataras/golog v0.1.8 // indirect
|
github.com/kataras/golog v0.1.8 // indirect
|
||||||
@@ -91,10 +91,9 @@ require (
|
|||||||
github.com/mailgun/raymond/v2 v2.0.48 // indirect
|
github.com/mailgun/raymond/v2 v2.0.48 // indirect
|
||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||||
github.com/mattn/go-sqlite3 v1.14.14 // indirect
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||||
github.com/mediocregopher/radix/v3 v3.8.1 // indirect
|
github.com/mediocregopher/radix/v3 v3.8.1 // indirect
|
||||||
github.com/microcosm-cc/bluemonday v1.0.23 // indirect
|
github.com/microcosm-cc/bluemonday v1.0.25 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/mitchellh/pointerstructure v1.2.0 // indirect
|
github.com/mitchellh/pointerstructure v1.2.0 // indirect
|
||||||
github.com/mmcloughlin/addchain v0.4.0 // indirect
|
github.com/mmcloughlin/addchain v0.4.0 // indirect
|
||||||
@@ -134,16 +133,16 @@ require (
|
|||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||||
github.com/yosssi/ace v0.0.5 // indirect
|
github.com/yosssi/ace v0.0.5 // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||||
golang.org/x/crypto v0.9.0 // indirect
|
golang.org/x/crypto v0.11.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20230206171751-46f607a40771 // indirect
|
golang.org/x/exp v0.0.0-20230206171751-46f607a40771 // indirect
|
||||||
golang.org/x/net v0.10.0 // indirect
|
golang.org/x/net v0.12.0 // indirect
|
||||||
golang.org/x/sync v0.1.0 // indirect
|
golang.org/x/sync v0.3.0 // indirect
|
||||||
golang.org/x/sys v0.8.0 // indirect
|
golang.org/x/sys v0.10.0 // indirect
|
||||||
golang.org/x/text v0.9.0 // indirect
|
golang.org/x/text v0.11.0 // indirect
|
||||||
golang.org/x/time v0.3.0 // indirect
|
golang.org/x/time v0.3.0 // indirect
|
||||||
golang.org/x/tools v0.8.0 // indirect
|
golang.org/x/tools v0.11.0 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect
|
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect
|
||||||
google.golang.org/protobuf v1.29.0 // indirect
|
google.golang.org/protobuf v1.31.0 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
|
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
|
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||||
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=
|
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=
|
||||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
|
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
|
||||||
github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo=
|
github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo=
|
||||||
@@ -97,8 +97,8 @@ github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6ps
|
|||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||||
github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE=
|
github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE=
|
||||||
github.com/docker/docker v20.10.21+incompatible h1:UTLdBmHk3bEY+w8qeO5KttOhy6OmXWsl/FEet9Uswog=
|
github.com/docker/docker v23.0.6+incompatible h1:aBD4np894vatVX99UTx/GyOUOK4uEcROwA3+bQhEcoU=
|
||||||
github.com/docker/docker v20.10.21+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
github.com/docker/docker v23.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o=
|
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o=
|
||||||
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
|
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
|
||||||
@@ -143,9 +143,6 @@ github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
|||||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
|
||||||
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
|
||||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
|
||||||
github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
|
github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
|
||||||
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
|
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
@@ -251,8 +248,10 @@ github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm
|
|||||||
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
|
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
|
||||||
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
||||||
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
|
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||||
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||||
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
||||||
@@ -302,9 +301,6 @@ github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4F
|
|||||||
github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y=
|
github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y=
|
||||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
||||||
github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=
|
github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=
|
||||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
|
||||||
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
|
|
||||||
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
|
||||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
github.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=
|
github.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=
|
||||||
github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=
|
github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=
|
||||||
@@ -326,15 +322,12 @@ github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2y
|
|||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
|
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||||
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
||||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.14 h1:qZgc/Rwetq+MtyE18WhzjokPD93dNqLGNT3QJuLvBGw=
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
|
||||||
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
|
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||||
@@ -342,8 +335,8 @@ github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i
|
|||||||
github.com/mediocregopher/radix/v3 v3.8.1 h1:rOkHflVuulFKlwsLY01/M2cM2tWCjDoETcMqKbAWu1M=
|
github.com/mediocregopher/radix/v3 v3.8.1 h1:rOkHflVuulFKlwsLY01/M2cM2tWCjDoETcMqKbAWu1M=
|
||||||
github.com/mediocregopher/radix/v3 v3.8.1/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=
|
github.com/mediocregopher/radix/v3 v3.8.1/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
|
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.23 h1:SMZe2IGa0NuHvnVNAZ+6B38gsTbi5e4sViiWJyDDqFY=
|
github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.23/go.mod h1:mN70sk7UkkF8TUr2IGBpNN0jAgStuPzlK76QuruE/z4=
|
github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE=
|
||||||
github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
|
github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
||||||
@@ -456,18 +449,14 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM
|
|||||||
github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA=
|
github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA=
|
||||||
github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg=
|
github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
|
||||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
|
||||||
github.com/supranational/blst v0.3.11-0.20230406105308-e9dfc5ee724b h1:u49mjRnygnB34h8OKbnNJFVUtWSKIKb1KukdV8bILUM=
|
github.com/supranational/blst v0.3.11-0.20230406105308-e9dfc5ee724b h1:u49mjRnygnB34h8OKbnNJFVUtWSKIKb1KukdV8bILUM=
|
||||||
github.com/supranational/blst v0.3.11-0.20230406105308-e9dfc5ee724b/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
|
github.com/supranational/blst v0.3.11-0.20230406105308-e9dfc5ee724b/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
|
||||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
|
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
|
||||||
@@ -488,8 +477,8 @@ github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGr
|
|||||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||||
github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa h1:5SqCsI/2Qya2bCzK15ozrqo2sZxkh0FHynJZOTVoV6Q=
|
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
|
||||||
github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI=
|
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
|
||||||
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
|
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
@@ -537,8 +526,8 @@ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWP
|
|||||||
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
|
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
|
||||||
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20230206171751-46f607a40771 h1:xP7rWLUr1e1n2xkK5YB4LI0hPEy3LJC6Wk+D4pGlOJg=
|
golang.org/x/exp v0.0.0-20230206171751-46f607a40771 h1:xP7rWLUr1e1n2xkK5YB4LI0hPEy3LJC6Wk+D4pGlOJg=
|
||||||
golang.org/x/exp v0.0.0-20230206171751-46f607a40771/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
golang.org/x/exp v0.0.0-20230206171751-46f607a40771/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||||
@@ -551,7 +540,7 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||||
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
|
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@@ -574,8 +563,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
|||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
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-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -585,8 +574,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@@ -628,8 +617,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
@@ -638,8 +627,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||||
@@ -660,8 +649,8 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f
|
|||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||||
golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y=
|
golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8=
|
||||||
golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
|
golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@@ -692,8 +681,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
|
|||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.29.0 h1:44S3JjaKmLEE4YIkjzexaP+NzZsudE3Zin5Njn/pYX0=
|
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||||
google.golang.org/protobuf v1.29.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
@@ -724,6 +713,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
|||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gorm.io/gorm v1.25.2 h1:gs1o6Vsa+oVKG/a9ElL3XgyGfghFfkKA2SInQaCyMho=
|
||||||
|
gorm.io/gorm v1.25.2/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
|
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
|
// QueryByAddressRequest the request parameter of address api
|
||||||
type QueryByAddressRequest struct {
|
type QueryByAddressRequest struct {
|
||||||
Address string `url:"address"`
|
Address string `url:"address"`
|
||||||
Offset int `url:"offset"`
|
Offset int `url:"offset"`
|
||||||
Limit int `url:"limit"`
|
Limit int `url:"limit"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QueryByHashRequest the request parameter of hash api
|
||||||
type QueryByHashRequest struct {
|
type QueryByHashRequest struct {
|
||||||
Txs []string `url:"txs"`
|
Txs []string `url:"txs"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,16 +2,19 @@ package model
|
|||||||
|
|
||||||
import "bridge-history-api/service"
|
import "bridge-history-api/service"
|
||||||
|
|
||||||
|
// Data the return struct of apis
|
||||||
type Data struct {
|
type Data struct {
|
||||||
Result []*service.TxHistoryInfo `json:"result"`
|
Result []*service.TxHistoryInfo `json:"result"`
|
||||||
Total uint64 `json:"total"`
|
Total uint64 `json:"total"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QueryByAddressResponse the schema of address api response
|
||||||
type QueryByAddressResponse struct {
|
type QueryByAddressResponse struct {
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
Data *Data `json:"data"`
|
Data *Data `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QueryByHashResponse the schema of hash api response
|
||||||
type QueryByHashResponse struct {
|
type QueryByHashResponse struct {
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
Data *Data `json:"data"`
|
Data *Data `json:"data"`
|
||||||
|
|||||||
94
bridge-history-api/orm/batch.go
Normal file
94
bridge-history-api/orm/batch.go
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
package orm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RollupBatch is the struct for rollup_batch table
|
||||||
|
type RollupBatch struct {
|
||||||
|
db *gorm.DB `gorm:"column:-"`
|
||||||
|
|
||||||
|
ID uint64 `json:"id" gorm:"column:id"`
|
||||||
|
BatchIndex uint64 `json:"batch_index" gorm:"column:batch_index"`
|
||||||
|
BatchHash string `json:"batch_hash" gorm:"column:batch_hash"`
|
||||||
|
CommitHeight uint64 `json:"commit_height" gorm:"column:commit_height"`
|
||||||
|
StartBlockNumber uint64 `json:"start_block_number" gorm:"column:start_block_number"`
|
||||||
|
EndBlockNumber uint64 `json:"end_block_number" gorm:"column:end_block_number"`
|
||||||
|
CreatedAt *time.Time `json:"created_at" gorm:"column:created_at"`
|
||||||
|
UpdatedAt *time.Time `json:"updated_at" gorm:"column:updated_at"`
|
||||||
|
DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"column:deleted_at;default:NULL"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRollupBatch create an RollupBatch instance
|
||||||
|
func NewRollupBatch(db *gorm.DB) *RollupBatch {
|
||||||
|
return &RollupBatch{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName returns the table name for the Batch model.
|
||||||
|
func (*RollupBatch) TableName() string {
|
||||||
|
return "rollup_batch"
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLatestRollupBatchProcessedHeight return latest processed height from rollup_batch table
|
||||||
|
func (r *RollupBatch) GetLatestRollupBatchProcessedHeight(ctx context.Context) (uint64, error) {
|
||||||
|
var result RollupBatch
|
||||||
|
err := r.db.WithContext(ctx).Unscoped().Select("commit_height").Order("id desc").First(&result).Error
|
||||||
|
if err != nil {
|
||||||
|
if err == gorm.ErrRecordNotFound {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("RollupBatch.GetLatestRollupBatchProcessedHeight error: %w", err)
|
||||||
|
}
|
||||||
|
return result.CommitHeight, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLatestRollupBatch return the latest rollup batch in db
|
||||||
|
func (r *RollupBatch) GetLatestRollupBatch(ctx context.Context) (*RollupBatch, error) {
|
||||||
|
var result RollupBatch
|
||||||
|
err := r.db.WithContext(ctx).Model(&RollupBatch{}).Where("batch_hash is not NULL").Order("batch_index desc").First(&result).Error
|
||||||
|
if err != nil {
|
||||||
|
if err == gorm.ErrRecordNotFound {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("RollupBatch.GetLatestRollupBatch error: %w", err)
|
||||||
|
}
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRollupBatchByIndex return the rollup batch by index
|
||||||
|
func (r *RollupBatch) GetRollupBatchByIndex(ctx context.Context, index uint64) (*RollupBatch, error) {
|
||||||
|
var result RollupBatch
|
||||||
|
err := r.db.WithContext(ctx).Model(&RollupBatch{}).Where("batch_index = ?", index).First(&result).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("RollupBatch.GetRollupBatchByIndex error: %w", err)
|
||||||
|
}
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertRollupBatch batch insert rollup batch into db and return the transaction
|
||||||
|
func (r *RollupBatch) InsertRollupBatch(ctx context.Context, batches []*RollupBatch, dbTx ...*gorm.DB) error {
|
||||||
|
if len(batches) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
db := r.db
|
||||||
|
if len(dbTx) > 0 && dbTx[0] != nil {
|
||||||
|
db = dbTx[0]
|
||||||
|
}
|
||||||
|
err := db.WithContext(ctx).Model(&RollupBatch{}).Create(&batches).Error
|
||||||
|
if err != nil {
|
||||||
|
batchIndexes := make([]uint64, 0, len(batches))
|
||||||
|
heights := make([]uint64, 0, len(batches))
|
||||||
|
for _, batch := range batches {
|
||||||
|
batchIndexes = append(batchIndexes, batch.BatchIndex)
|
||||||
|
heights = append(heights, batch.CommitHeight)
|
||||||
|
}
|
||||||
|
log.Error("failed to insert rollup batch", "batchIndexes", batchIndexes, "heights", heights)
|
||||||
|
return fmt.Errorf("RollupBatch.InsertRollupBatch error: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
370
bridge-history-api/orm/cross_msg.go
Normal file
370
bridge-history-api/orm/cross_msg.go
Normal file
@@ -0,0 +1,370 @@
|
|||||||
|
package orm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AssetType can be ETH/ERC20/ERC1155/ERC721
|
||||||
|
type AssetType int
|
||||||
|
|
||||||
|
// MsgType can be layer1/layer2 msg
|
||||||
|
type MsgType int
|
||||||
|
|
||||||
|
func (a AssetType) String() string {
|
||||||
|
switch a {
|
||||||
|
case ETH:
|
||||||
|
return "ETH"
|
||||||
|
case ERC20:
|
||||||
|
return "ERC20"
|
||||||
|
case ERC1155:
|
||||||
|
return "ERC1155"
|
||||||
|
case ERC721:
|
||||||
|
return "ERC721"
|
||||||
|
}
|
||||||
|
return "Unknown Asset Type"
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ETH = 0
|
||||||
|
ETH AssetType = iota
|
||||||
|
// ERC20 = 1
|
||||||
|
ERC20
|
||||||
|
// ERC721 = 2
|
||||||
|
ERC721
|
||||||
|
// ERC1155 = 3
|
||||||
|
ERC1155
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// UnknownMsg = 0
|
||||||
|
UnknownMsg MsgType = iota
|
||||||
|
// Layer1Msg = 1
|
||||||
|
Layer1Msg
|
||||||
|
// Layer2Msg = 2
|
||||||
|
Layer2Msg
|
||||||
|
)
|
||||||
|
|
||||||
|
// CrossMsg represents a cross message from layer 1 to layer 2
|
||||||
|
type CrossMsg struct {
|
||||||
|
db *gorm.DB `gorm:"column:-"`
|
||||||
|
|
||||||
|
ID uint64 `json:"id" gorm:"column:id"`
|
||||||
|
MsgHash string `json:"msg_hash" gorm:"column:msg_hash"`
|
||||||
|
Height uint64 `json:"height" gorm:"column:height"`
|
||||||
|
Sender string `json:"sender" gorm:"column:sender"`
|
||||||
|
Target string `json:"target" gorm:"column:target"`
|
||||||
|
Amount string `json:"amount" gorm:"column:amount"`
|
||||||
|
Layer1Hash string `json:"layer1_hash" gorm:"column:layer1_hash;default:''"`
|
||||||
|
Layer2Hash string `json:"layer2_hash" gorm:"column:layer2_hash;default:''"`
|
||||||
|
Layer1Token string `json:"layer1_token" gorm:"column:layer1_token;default:''"`
|
||||||
|
Layer2Token string `json:"layer2_token" gorm:"column:layer2_token;default:''"`
|
||||||
|
TokenIDs string `json:"token_ids" gorm:"column:token_ids;default:''"`
|
||||||
|
TokenAmounts string `json:"token_amounts" gorm:"column:token_amounts;default:''"`
|
||||||
|
Asset int `json:"asset" gorm:"column:asset"`
|
||||||
|
MsgType int `json:"msg_type" gorm:"column:msg_type"`
|
||||||
|
Timestamp *time.Time `json:"timestamp" gorm:"column:block_timestamp;default;NULL"`
|
||||||
|
CreatedAt *time.Time `json:"created_at" gorm:"column:created_at"`
|
||||||
|
UpdatedAt *time.Time `json:"updated_at" gorm:"column:updated_at"`
|
||||||
|
DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"column:deleted_at;default:NULL"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName returns the table name for the CrossMsg model.
|
||||||
|
func (*CrossMsg) TableName() string {
|
||||||
|
return "cross_message"
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCrossMsg returns a new instance of CrossMsg.
|
||||||
|
func NewCrossMsg(db *gorm.DB) *CrossMsg {
|
||||||
|
return &CrossMsg{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
// L1 Cross Msgs Operations
|
||||||
|
|
||||||
|
// GetL1CrossMsgByHash returns layer1 cross message by given hash
|
||||||
|
func (c *CrossMsg) GetL1CrossMsgByHash(ctx context.Context, l1Hash common.Hash) (*CrossMsg, error) {
|
||||||
|
var result CrossMsg
|
||||||
|
err := c.db.WithContext(ctx).Model(&CrossMsg{}).Where("layer1_hash = ? AND msg_type = ?", l1Hash.String(), Layer1Msg).First(&result).Error
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("CrossMsg.GetL1CrossMsgByHash error: %w", err)
|
||||||
|
}
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLatestL1ProcessedHeight returns the latest processed height of layer1 cross messages
|
||||||
|
func (c *CrossMsg) GetLatestL1ProcessedHeight(ctx context.Context) (uint64, error) {
|
||||||
|
var result CrossMsg
|
||||||
|
err := c.db.WithContext(ctx).Model(&CrossMsg{}).Where("msg_type = ?", Layer1Msg).
|
||||||
|
Select("height").
|
||||||
|
Order("id DESC").
|
||||||
|
First(&result).
|
||||||
|
Error
|
||||||
|
if err != nil {
|
||||||
|
if err == gorm.ErrRecordNotFound {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("CrossMsg.GetLatestL1ProcessedHeight error: %w", err)
|
||||||
|
}
|
||||||
|
return result.Height, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetL1EarliestNoBlockTimestampHeight returns the earliest layer1 cross message height which has no block timestamp
|
||||||
|
func (c *CrossMsg) GetL1EarliestNoBlockTimestampHeight(ctx context.Context) (uint64, error) {
|
||||||
|
var result CrossMsg
|
||||||
|
err := c.db.WithContext(ctx).Model(&CrossMsg{}).
|
||||||
|
Where("block_timestamp IS NULL AND msg_type = ?", Layer1Msg).
|
||||||
|
Select("height").
|
||||||
|
Order("height ASC").
|
||||||
|
First(&result).
|
||||||
|
Error
|
||||||
|
if err != nil {
|
||||||
|
if err == gorm.ErrRecordNotFound {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("CrossMsg.GetL1EarliestNoBlockTimestampHeight error: %w", err)
|
||||||
|
}
|
||||||
|
return result.Height, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertL1CrossMsg batch insert layer1 cross messages into db
|
||||||
|
func (c *CrossMsg) InsertL1CrossMsg(ctx context.Context, messages []*CrossMsg, dbTx ...*gorm.DB) error {
|
||||||
|
if len(messages) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
db := c.db
|
||||||
|
if len(dbTx) > 0 && dbTx[0] != nil {
|
||||||
|
db = dbTx[0]
|
||||||
|
}
|
||||||
|
db.WithContext(ctx)
|
||||||
|
err := db.Model(&CrossMsg{}).Create(&messages).Error
|
||||||
|
if err != nil {
|
||||||
|
l1hashes := make([]string, 0, len(messages))
|
||||||
|
heights := make([]uint64, 0, len(messages))
|
||||||
|
for _, msg := range messages {
|
||||||
|
l1hashes = append(l1hashes, msg.Layer1Hash)
|
||||||
|
heights = append(heights, msg.Height)
|
||||||
|
}
|
||||||
|
log.Error("failed to insert l1 cross messages", "l1hashes", l1hashes, "heights", heights, "err", err)
|
||||||
|
return fmt.Errorf("CrossMsg.InsertL1CrossMsg error: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateL1CrossMsgHash update l1 cross msg hash in db, no need to check msg_type since layer1_hash wont be empty if its layer1 msg
|
||||||
|
func (c *CrossMsg) UpdateL1CrossMsgHash(ctx context.Context, l1Hash, msgHash common.Hash, dbTx ...*gorm.DB) error {
|
||||||
|
db := c.db
|
||||||
|
if len(dbTx) > 0 && dbTx[0] != nil {
|
||||||
|
db = dbTx[0]
|
||||||
|
}
|
||||||
|
db.WithContext(ctx)
|
||||||
|
err := c.db.Model(&CrossMsg{}).Where("layer1_hash = ?", l1Hash.Hex()).Update("msg_hash", msgHash.Hex()).Error
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("CrossMsg.UpdateL1CrossMsgHash error: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateL1BlockTimestamp update layer1 block timestamp
|
||||||
|
func (c *CrossMsg) UpdateL1BlockTimestamp(ctx context.Context, height uint64, timestamp time.Time) error {
|
||||||
|
err := c.db.WithContext(ctx).Model(&CrossMsg{}).
|
||||||
|
Where("height = ? AND msg_type = ?", height, Layer1Msg).
|
||||||
|
Update("block_timestamp", timestamp).Error
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("CrossMsg.UpdateL1BlockTimestamp error: %w", err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteL1CrossMsgAfterHeight soft delete layer1 cross messages after given height
|
||||||
|
func (c *CrossMsg) DeleteL1CrossMsgAfterHeight(ctx context.Context, height uint64, dbTx ...*gorm.DB) error {
|
||||||
|
db := c.db
|
||||||
|
if len(dbTx) > 0 && dbTx[0] != nil {
|
||||||
|
db = dbTx[0]
|
||||||
|
}
|
||||||
|
db.WithContext(ctx)
|
||||||
|
err := db.Delete(&CrossMsg{}, "height > ? AND msg_type = ?", height, Layer1Msg).Error
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("CrossMsg.DeleteL1CrossMsgAfterHeight error: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// L2 Cross Msgs Operations
|
||||||
|
|
||||||
|
// GetL2CrossMsgByHash returns layer2 cross message by given hash
|
||||||
|
func (c *CrossMsg) GetL2CrossMsgByHash(ctx context.Context, l2Hash common.Hash) (*CrossMsg, error) {
|
||||||
|
var result CrossMsg
|
||||||
|
err := c.db.WithContext(ctx).Model(&CrossMsg{}).Where("layer2_hash = ? AND msg_type = ?", l2Hash.String(), Layer1Msg).First(&result).Error
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("CrossMsg.GetL2CrossMsgByHash error: %w", err)
|
||||||
|
}
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLatestL2ProcessedHeight returns the latest processed height of layer2 cross messages
|
||||||
|
func (c *CrossMsg) GetLatestL2ProcessedHeight(ctx context.Context) (uint64, error) {
|
||||||
|
var result CrossMsg
|
||||||
|
err := c.db.WithContext(ctx).Model(&CrossMsg{}).
|
||||||
|
Select("height").
|
||||||
|
Where("msg_type = ?", Layer2Msg).
|
||||||
|
Order("id DESC").
|
||||||
|
First(&result).
|
||||||
|
Error
|
||||||
|
if err != nil {
|
||||||
|
if err == gorm.ErrRecordNotFound {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("CrossMsg.GetLatestL2ProcessedHeight error: %w", err)
|
||||||
|
}
|
||||||
|
return result.Height, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetL2CrossMsgByMsgHashList returns layer2 cross messages under given msg hashes
|
||||||
|
func (c *CrossMsg) GetL2CrossMsgByMsgHashList(ctx context.Context, msgHashList []string) ([]*CrossMsg, error) {
|
||||||
|
var results []*CrossMsg
|
||||||
|
err := c.db.WithContext(ctx).Model(&CrossMsg{}).
|
||||||
|
Where("msg_hash IN (?) AND msg_type = ?", msgHashList, Layer2Msg).
|
||||||
|
Find(&results).
|
||||||
|
Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("CrossMsg.GetL2CrossMsgByMsgHashList error: %w", err)
|
||||||
|
}
|
||||||
|
if len(results) == 0 {
|
||||||
|
log.Debug("no CrossMsg under given msg hashes", "msg hash list", msgHashList)
|
||||||
|
}
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetL2EarliestNoBlockTimestampHeight returns the earliest layer2 cross message height which has no block timestamp
|
||||||
|
func (c *CrossMsg) GetL2EarliestNoBlockTimestampHeight(ctx context.Context) (uint64, error) {
|
||||||
|
var result CrossMsg
|
||||||
|
err := c.db.WithContext(ctx).Model(&CrossMsg{}).
|
||||||
|
Where("block_timestamp IS NULL AND msg_type = ?", Layer2Msg).
|
||||||
|
Select("height").
|
||||||
|
Order("height ASC").
|
||||||
|
First(&result).
|
||||||
|
Error
|
||||||
|
if err != nil {
|
||||||
|
if err == gorm.ErrRecordNotFound {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("CrossMsg.GetL2EarliestNoBlockTimestampHeight error: %w", err)
|
||||||
|
}
|
||||||
|
return result.Height, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertL2CrossMsg batch insert layer2 cross messages
|
||||||
|
func (c *CrossMsg) InsertL2CrossMsg(ctx context.Context, messages []*CrossMsg, dbTx ...*gorm.DB) error {
|
||||||
|
if len(messages) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
db := c.db
|
||||||
|
if len(dbTx) > 0 && dbTx[0] != nil {
|
||||||
|
db = dbTx[0]
|
||||||
|
}
|
||||||
|
db.WithContext(ctx)
|
||||||
|
err := db.Model(&CrossMsg{}).Create(&messages).Error
|
||||||
|
if err != nil {
|
||||||
|
l2hashes := make([]string, 0, len(messages))
|
||||||
|
heights := make([]uint64, 0, len(messages))
|
||||||
|
for _, msg := range messages {
|
||||||
|
l2hashes = append(l2hashes, msg.Layer2Hash)
|
||||||
|
heights = append(heights, msg.Height)
|
||||||
|
}
|
||||||
|
log.Error("failed to insert l2 cross messages", "l2hashes", l2hashes, "heights", heights, "err", err)
|
||||||
|
return fmt.Errorf("CrossMsg.InsertL2CrossMsg error: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateL2CrossMsgHash update layer2 cross message hash
|
||||||
|
func (c *CrossMsg) UpdateL2CrossMsgHash(ctx context.Context, l2Hash, msgHash common.Hash, dbTx ...*gorm.DB) error {
|
||||||
|
db := c.db
|
||||||
|
if len(dbTx) > 0 && dbTx[0] != nil {
|
||||||
|
db = dbTx[0]
|
||||||
|
}
|
||||||
|
db.WithContext(ctx)
|
||||||
|
err := db.Model(&CrossMsg{}).
|
||||||
|
Where("layer2_hash = ?", l2Hash.String()).
|
||||||
|
Update("msg_hash", msgHash.String()).
|
||||||
|
Error
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("CrossMsg.UpdateL2CrossMsgHash error: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateL2BlockTimestamp update layer2 cross message block timestamp
|
||||||
|
func (c *CrossMsg) UpdateL2BlockTimestamp(ctx context.Context, height uint64, timestamp time.Time) error {
|
||||||
|
err := c.db.WithContext(ctx).Model(&CrossMsg{}).
|
||||||
|
Where("height = ? AND msg_type = ?", height, Layer2Msg).
|
||||||
|
Update("block_timestamp", timestamp).Error
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("CrossMsg.UpdateL2BlockTimestamp error: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteL2CrossMsgFromHeight delete layer2 cross messages from given height
|
||||||
|
func (c *CrossMsg) DeleteL2CrossMsgFromHeight(ctx context.Context, height uint64, dbTx ...*gorm.DB) error {
|
||||||
|
db := c.db
|
||||||
|
if len(dbTx) > 0 && dbTx[0] != nil {
|
||||||
|
db = dbTx[0]
|
||||||
|
}
|
||||||
|
db.WithContext(ctx)
|
||||||
|
err := db.Model(&CrossMsg{}).Delete("height > ? AND msg_type = ?", height, Layer2Msg).Error
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("CrossMsg.DeleteL2CrossMsgFromHeight error: %w", err)
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// General Operations
|
||||||
|
|
||||||
|
// GetTotalCrossMsgCountByAddress get total cross msg count by address
|
||||||
|
func (c *CrossMsg) GetTotalCrossMsgCountByAddress(ctx context.Context, sender string) (uint64, error) {
|
||||||
|
var count int64
|
||||||
|
err := c.db.WithContext(ctx).Model(&CrossMsg{}).
|
||||||
|
Where("sender = ?", sender).
|
||||||
|
Count(&count).
|
||||||
|
Error
|
||||||
|
if err != nil {
|
||||||
|
if err == gorm.ErrRecordNotFound {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("CrossMsg.GetTotalCrossMsgCountByAddress error: %w", err)
|
||||||
|
|
||||||
|
}
|
||||||
|
return uint64(count), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCrossMsgsByAddressWithOffset get cross msgs by address with offset
|
||||||
|
func (c *CrossMsg) GetCrossMsgsByAddressWithOffset(ctx context.Context, sender string, offset int, limit int) ([]CrossMsg, error) {
|
||||||
|
var messages []CrossMsg
|
||||||
|
err := c.db.WithContext(ctx).Model(&CrossMsg{}).
|
||||||
|
Where("sender = ?", sender).
|
||||||
|
Order("block_timestamp DESC NULLS FIRST, id DESC").
|
||||||
|
Limit(limit).
|
||||||
|
Offset(offset).
|
||||||
|
Find(&messages).
|
||||||
|
Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("CrossMsg.GetCrossMsgsByAddressWithOffset error: %w", err)
|
||||||
|
}
|
||||||
|
return messages, nil
|
||||||
|
}
|
||||||
215
bridge-history-api/orm/l2_sent_msg.go
Normal file
215
bridge-history-api/orm/l2_sent_msg.go
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
package orm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// L2SentMsg defines the struct for l2_sent_msg table record
|
||||||
|
type L2SentMsg struct {
|
||||||
|
db *gorm.DB `gorm:"column:-"`
|
||||||
|
|
||||||
|
ID uint64 `json:"id" gorm:"column:id"`
|
||||||
|
OriginalSender string `json:"original_sender" gorm:"column:original_sender;default:''"`
|
||||||
|
TxHash string `json:"tx_hash" gorm:"column:tx_hash"`
|
||||||
|
MsgHash string `json:"msg_hash" gorm:"column:msg_hash"`
|
||||||
|
Sender string `json:"sender" gorm:"column:sender"`
|
||||||
|
Target string `json:"target" gorm:"column:target"`
|
||||||
|
Value string `json:"value" gorm:"column:value"`
|
||||||
|
Height uint64 `json:"height" gorm:"column:height"`
|
||||||
|
Nonce uint64 `json:"nonce" gorm:"column:nonce"`
|
||||||
|
BatchIndex uint64 `json:"batch_index" gorm:"column:batch_index;default:0"`
|
||||||
|
MsgProof string `json:"msg_proof" gorm:"column:msg_proof;default:''"`
|
||||||
|
MsgData string `json:"msg_data" gorm:"column:msg_data;default:''"`
|
||||||
|
CreatedAt *time.Time `json:"created_at" gorm:"column:created_at"`
|
||||||
|
UpdatedAt *time.Time `json:"updated_at" gorm:"column:updated_at"`
|
||||||
|
DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"column:deleted_at;default:NULL"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewL2SentMsg create an NewL2SentMsg instance
|
||||||
|
func NewL2SentMsg(db *gorm.DB) *L2SentMsg {
|
||||||
|
return &L2SentMsg{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName returns the table name for the L2SentMsg model.
|
||||||
|
func (*L2SentMsg) TableName() string {
|
||||||
|
return "l2_sent_msg"
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetL2SentMsgByHash get l2 sent msg by hash
|
||||||
|
func (l *L2SentMsg) GetL2SentMsgByHash(ctx context.Context, msgHash string) (*L2SentMsg, error) {
|
||||||
|
var result L2SentMsg
|
||||||
|
err := l.db.WithContext(ctx).Model(&L2SentMsg{}).
|
||||||
|
Where("msg_hash = ?", msgHash).
|
||||||
|
First(&result).
|
||||||
|
Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("L2SentMsg.GetL2SentMsgByHash error: %w", err)
|
||||||
|
}
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLatestSentMsgHeightOnL2 get latest sent msg height on l2
|
||||||
|
func (l *L2SentMsg) GetLatestSentMsgHeightOnL2(ctx context.Context) (uint64, error) {
|
||||||
|
var result L2SentMsg
|
||||||
|
err := l.db.WithContext(ctx).Model(&L2SentMsg{}).
|
||||||
|
Select("height").
|
||||||
|
Order("nonce DESC").
|
||||||
|
First(&result).Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if err == gorm.ErrRecordNotFound {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("L2SentMsg.GetLatestSentMsgHeightOnL2 error: %w", err)
|
||||||
|
|
||||||
|
}
|
||||||
|
return result.Height, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetClaimableL2SentMsgByAddressWithOffset get claimable l2 sent msg by address with offset
|
||||||
|
func (l *L2SentMsg) GetClaimableL2SentMsgByAddressWithOffset(ctx context.Context, address string, offset int, limit int) ([]*L2SentMsg, error) {
|
||||||
|
var results []*L2SentMsg
|
||||||
|
err := l.db.WithContext(ctx).Raw(`SELECT * FROM l2_sent_msg WHERE id NOT IN (SELECT l2_sent_msg.id FROM l2_sent_msg INNER JOIN relayed_msg ON l2_sent_msg.msg_hash = relayed_msg.msg_hash WHERE l2_sent_msg.deleted_at IS NULL AND relayed_msg.deleted_at IS NULL) AND (original_sender=$1 OR sender = $1) AND msg_proof !='' ORDER BY id DESC LIMIT $2 OFFSET $3;`, address, limit, offset).
|
||||||
|
Scan(&results).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("L2SentMsg.GetClaimableL2SentMsgByAddressWithOffset error: %w", err)
|
||||||
|
}
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetClaimableL2SentMsgByAddressTotalNum get claimable l2 sent msg by address total num
|
||||||
|
func (l *L2SentMsg) GetClaimableL2SentMsgByAddressTotalNum(ctx context.Context, address string) (uint64, error) {
|
||||||
|
var count uint64
|
||||||
|
err := l.db.WithContext(ctx).Raw(`SELECT COUNT(*) FROM l2_sent_msg WHERE id NOT IN (SELECT l2_sent_msg.id FROM l2_sent_msg INNER JOIN relayed_msg ON l2_sent_msg.msg_hash = relayed_msg.msg_hash WHERE l2_sent_msg.deleted_at IS NULL AND relayed_msg.deleted_at IS NULL) AND (original_sender=$1 OR sender = $1) AND msg_proof !='';`, address).
|
||||||
|
Scan(&count).Error
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("L2SentMsg.GetClaimableL2SentMsgByAddressTotalNum error: %w", err)
|
||||||
|
}
|
||||||
|
return count, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLatestL2SentMsgBatchIndex get latest l2 sent msg batch index
|
||||||
|
func (l *L2SentMsg) GetLatestL2SentMsgBatchIndex(ctx context.Context) (int64, error) {
|
||||||
|
var result L2SentMsg
|
||||||
|
err := l.db.WithContext(ctx).Model(&L2SentMsg{}).
|
||||||
|
Where("batch_index != 0").
|
||||||
|
Order("batch_index DESC").
|
||||||
|
Select("batch_index").
|
||||||
|
First(&result).
|
||||||
|
Error
|
||||||
|
if err != nil {
|
||||||
|
return -1, fmt.Errorf("L2SentMsg.GetLatestL2SentMsgBatchIndex error: %w", err)
|
||||||
|
}
|
||||||
|
// Watch for overflow, tho its not likely to happen
|
||||||
|
return int64(result.Height), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetL2SentMsgMsgHashByHeightRange get l2 sent msg msg hash by height range
|
||||||
|
func (l *L2SentMsg) GetL2SentMsgMsgHashByHeightRange(ctx context.Context, startHeight, endHeight uint64) ([]*L2SentMsg, error) {
|
||||||
|
var results []*L2SentMsg
|
||||||
|
err := l.db.WithContext(ctx).Model(&L2SentMsg{}).
|
||||||
|
Where("height >= ? AND height <= ?", startHeight, endHeight).
|
||||||
|
Order("nonce ASC").
|
||||||
|
Find(&results).
|
||||||
|
Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("L2SentMsg.GetL2SentMsgMsgHashByHeightRange error: %w", err)
|
||||||
|
}
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetL2SentMessageByNonce get l2 sent message by nonce
|
||||||
|
func (l *L2SentMsg) GetL2SentMessageByNonce(ctx context.Context, nonce uint64) (*L2SentMsg, error) {
|
||||||
|
var result L2SentMsg
|
||||||
|
err := l.db.WithContext(ctx).Model(&L2SentMsg{}).
|
||||||
|
Where("nonce = ?", nonce).
|
||||||
|
First(&result).
|
||||||
|
Error
|
||||||
|
if err != nil {
|
||||||
|
if err == gorm.ErrRecordNotFound {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("L2SentMsg.GetL2SentMessageByNonce error: %w", err)
|
||||||
|
|
||||||
|
}
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLatestL2SentMsgLEHeight get latest l2 sent msg less than or equal to end block number
|
||||||
|
func (l *L2SentMsg) GetLatestL2SentMsgLEHeight(ctx context.Context, endBlockNumber uint64) (*L2SentMsg, error) {
|
||||||
|
var result L2SentMsg
|
||||||
|
err := l.db.WithContext(ctx).Model(&L2SentMsg{}).
|
||||||
|
Where("height <= ?", endBlockNumber).
|
||||||
|
Order("nonce DESC").
|
||||||
|
First(&result).
|
||||||
|
Error
|
||||||
|
if err != nil {
|
||||||
|
if err == gorm.ErrRecordNotFound {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("L2SentMsg.GetLatestL2SentMsgLEHeight error: %w", err)
|
||||||
|
|
||||||
|
}
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertL2SentMsg batch insert l2 sent msg
|
||||||
|
func (l *L2SentMsg) InsertL2SentMsg(ctx context.Context, messages []*L2SentMsg, dbTx ...*gorm.DB) error {
|
||||||
|
if len(messages) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
db := l.db
|
||||||
|
if len(dbTx) > 0 && dbTx[0] != nil {
|
||||||
|
db = dbTx[0]
|
||||||
|
}
|
||||||
|
db.WithContext(ctx)
|
||||||
|
err := db.Model(&L2SentMsg{}).Create(&messages).Error
|
||||||
|
if err != nil {
|
||||||
|
l2hashes := make([]string, 0, len(messages))
|
||||||
|
heights := make([]uint64, 0, len(messages))
|
||||||
|
for _, msg := range messages {
|
||||||
|
l2hashes = append(l2hashes, msg.TxHash)
|
||||||
|
heights = append(heights, msg.Height)
|
||||||
|
}
|
||||||
|
log.Error("failed to insert l2 sent messages", "l2hashes", l2hashes, "heights", heights, "err", err)
|
||||||
|
return fmt.Errorf("L2SentMsg.InsertL2SentMsg error: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateL2MessageProof update l2 message proof in db tx
|
||||||
|
func (l *L2SentMsg) UpdateL2MessageProof(ctx context.Context, msgHash string, proof string, batchIndex uint64, dbTx ...*gorm.DB) error {
|
||||||
|
db := l.db
|
||||||
|
if len(dbTx) > 0 && dbTx[0] != nil {
|
||||||
|
db = dbTx[0]
|
||||||
|
}
|
||||||
|
db.WithContext(ctx)
|
||||||
|
err := db.Model(&L2SentMsg{}).
|
||||||
|
Where("msg_hash = ?", msgHash).
|
||||||
|
Updates(map[string]interface{}{
|
||||||
|
"msg_proof": proof,
|
||||||
|
"batch_index": batchIndex,
|
||||||
|
}).Error
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("L2SentMsg.UpdateL2MessageProof error: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteL2SentMsgAfterHeight delete l2 sent msg after height
|
||||||
|
func (l *L2SentMsg) DeleteL2SentMsgAfterHeight(ctx context.Context, height uint64, dbTx ...*gorm.DB) error {
|
||||||
|
db := l.db
|
||||||
|
if len(dbTx) > 0 && dbTx[0] != nil {
|
||||||
|
db = dbTx[0]
|
||||||
|
}
|
||||||
|
err := db.WithContext(ctx).Model(&L2SentMsg{}).Delete("height > ?", height).Error
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("L2SentMsg.DeleteL2SentMsgAfterHeight error: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
create table cross_message
|
create table cross_message
|
||||||
(
|
(
|
||||||
id BIGSERIAL PRIMARY KEY,
|
id BIGSERIAL PRIMARY KEY,
|
||||||
msg_hash VARCHAR NOT NULL DEFAULT '',
|
msg_hash VARCHAR NOT NULL,
|
||||||
height BIGINT NOT NULL,
|
height BIGINT NOT NULL,
|
||||||
sender VARCHAR NOT NULL,
|
sender VARCHAR NOT NULL,
|
||||||
target VARCHAR NOT NULL,
|
target VARCHAR NOT NULL,
|
||||||
@@ -12,30 +12,30 @@ create table cross_message
|
|||||||
layer2_hash VARCHAR NOT NULL DEFAULT '',
|
layer2_hash VARCHAR NOT NULL DEFAULT '',
|
||||||
layer1_token VARCHAR NOT NULL DEFAULT '',
|
layer1_token VARCHAR NOT NULL DEFAULT '',
|
||||||
layer2_token VARCHAR NOT NULL DEFAULT '',
|
layer2_token VARCHAR NOT NULL DEFAULT '',
|
||||||
token_id BIGINT NOT NULL DEFAULT 0,
|
|
||||||
asset SMALLINT NOT NULL,
|
asset SMALLINT NOT NULL,
|
||||||
msg_type SMALLINT NOT NULL,
|
msg_type SMALLINT NOT NULL,
|
||||||
is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
token_ids TEXT NOT NULL DEFAULT '',
|
||||||
|
token_amounts TEXT NOT NULL DEFAULT '',
|
||||||
block_timestamp TIMESTAMP(0) DEFAULT NULL,
|
block_timestamp TIMESTAMP(0) DEFAULT NULL,
|
||||||
created_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
updated_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
deleted_at TIMESTAMP(0) DEFAULT NULL
|
deleted_at TIMESTAMP(0) DEFAULT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
create unique index uk_msg_hash_msg_type
|
||||||
|
on cross_message (msg_hash, msg_type) where deleted_at IS NULL;
|
||||||
|
|
||||||
comment
|
comment
|
||||||
on column cross_message.asset is 'ETH, ERC20, ERC721, ERC1155';
|
on column cross_message.asset is 'ETH, ERC20, ERC721, ERC1155';
|
||||||
|
|
||||||
comment
|
comment
|
||||||
on column cross_message.msg_type is 'unknown, l1msg, l2msg';
|
on column cross_message.msg_type is 'unknown, l1msg, l2msg';
|
||||||
|
|
||||||
comment
|
CREATE INDEX idx_l1_msg_index ON cross_message (layer1_hash, deleted_at);
|
||||||
on column cross_message.is_deleted is 'NotDeleted false, Deleted true';
|
|
||||||
|
|
||||||
CREATE INDEX valid_l1_msg_index ON cross_message (layer1_hash, is_deleted);
|
CREATE INDEX idx_l2_msg_index ON cross_message (layer2_hash, deleted_at);
|
||||||
|
|
||||||
CREATE INDEX valid_l2_msg_index ON cross_message (layer2_hash, is_deleted);
|
CREATE INDEX idx_height_msg_type_index ON cross_message (height, msg_type, deleted_at);
|
||||||
|
|
||||||
CREATE INDEX valid_height_index ON cross_message (height, msg_type, is_deleted);
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION update_timestamp()
|
CREATE OR REPLACE FUNCTION update_timestamp()
|
||||||
RETURNS TRIGGER AS $$
|
RETURNS TRIGGER AS $$
|
||||||
@@ -49,22 +49,6 @@ CREATE TRIGGER update_timestamp BEFORE UPDATE
|
|||||||
ON cross_message FOR EACH ROW EXECUTE PROCEDURE
|
ON cross_message FOR EACH ROW EXECUTE PROCEDURE
|
||||||
update_timestamp();
|
update_timestamp();
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION deleted_at_trigger()
|
|
||||||
RETURNS TRIGGER AS $$
|
|
||||||
BEGIN
|
|
||||||
IF NEW.is_deleted AND OLD.is_deleted != NEW.is_deleted THEN
|
|
||||||
UPDATE cross_message SET deleted_at = NOW() WHERE id = NEW.id;
|
|
||||||
END IF;
|
|
||||||
RETURN NEW;
|
|
||||||
END;
|
|
||||||
$$ LANGUAGE plpgsql;
|
|
||||||
|
|
||||||
CREATE TRIGGER deleted_at_trigger
|
|
||||||
AFTER UPDATE ON cross_message
|
|
||||||
FOR EACH ROW
|
|
||||||
EXECUTE FUNCTION deleted_at_trigger();
|
|
||||||
|
|
||||||
|
|
||||||
-- +goose StatementEnd
|
-- +goose StatementEnd
|
||||||
|
|
||||||
-- +goose Down
|
-- +goose Down
|
||||||
@@ -7,17 +7,19 @@ create table relayed_msg
|
|||||||
height BIGINT NOT NULL,
|
height BIGINT NOT NULL,
|
||||||
layer1_hash VARCHAR NOT NULL DEFAULT '',
|
layer1_hash VARCHAR NOT NULL DEFAULT '',
|
||||||
layer2_hash VARCHAR NOT NULL DEFAULT '',
|
layer2_hash VARCHAR NOT NULL DEFAULT '',
|
||||||
is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
created_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
updated_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
deleted_at TIMESTAMP(0) DEFAULT NULL
|
deleted_at TIMESTAMP(0) DEFAULT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
comment
|
create unique index uk_msg_hash_l1_hash_l2_hash
|
||||||
on column relayed_msg.is_deleted is 'NotDeleted, Deleted';
|
on relayed_msg (msg_hash, layer1_hash, layer2_hash) where deleted_at IS NULL;
|
||||||
|
|
||||||
create unique index relayed_msg_hash_uindex
|
CREATE INDEX idx_l1_msg_relayed_msg ON relayed_msg (layer1_hash, deleted_at);
|
||||||
on relayed_msg (msg_hash);
|
|
||||||
|
CREATE INDEX idx_l2_msg_relayed_msg ON relayed_msg (layer2_hash, deleted_at);
|
||||||
|
|
||||||
|
CREATE INDEX idx_msg_hash_deleted_at_relayed_msg on relayed_msg (msg_hash, deleted_at);
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION update_timestamp()
|
CREATE OR REPLACE FUNCTION update_timestamp()
|
||||||
RETURNS TRIGGER AS $$
|
RETURNS TRIGGER AS $$
|
||||||
@@ -31,22 +33,6 @@ CREATE TRIGGER update_timestamp BEFORE UPDATE
|
|||||||
ON relayed_msg FOR EACH ROW EXECUTE PROCEDURE
|
ON relayed_msg FOR EACH ROW EXECUTE PROCEDURE
|
||||||
update_timestamp();
|
update_timestamp();
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION deleted_at_trigger()
|
|
||||||
RETURNS TRIGGER AS $$
|
|
||||||
BEGIN
|
|
||||||
IF NEW.is_deleted AND OLD.is_deleted != NEW.is_deleted THEN
|
|
||||||
UPDATE relayed_msg SET deleted_at = NOW() WHERE id = NEW.id;
|
|
||||||
END IF;
|
|
||||||
RETURN NEW;
|
|
||||||
END;
|
|
||||||
$$ LANGUAGE plpgsql;
|
|
||||||
|
|
||||||
CREATE TRIGGER deleted_at_trigger
|
|
||||||
AFTER UPDATE ON relayed_msg
|
|
||||||
FOR EACH ROW
|
|
||||||
EXECUTE FUNCTION deleted_at_trigger();
|
|
||||||
|
|
||||||
|
|
||||||
-- +goose StatementEnd
|
-- +goose StatementEnd
|
||||||
|
|
||||||
-- +goose Down
|
-- +goose Down
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
create table l2_sent_msg
|
||||||
|
(
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
original_sender VARCHAR NOT NULL DEFAULT '',
|
||||||
|
tx_hash VARCHAR NOT NULL,
|
||||||
|
sender VARCHAR NOT NULL,
|
||||||
|
target VARCHAR NOT NULL,
|
||||||
|
value VARCHAR NOT NULL,
|
||||||
|
msg_hash VARCHAR NOT NULL,
|
||||||
|
height BIGINT NOT NULL,
|
||||||
|
nonce BIGINT NOT NULL,
|
||||||
|
batch_index BIGINT NOT NULL DEFAULT 0,
|
||||||
|
msg_proof TEXT NOT NULL DEFAULT '',
|
||||||
|
msg_data TEXT NOT NULL DEFAULT '',
|
||||||
|
created_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
deleted_at TIMESTAMP(0) DEFAULT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
create unique index uk_msg_hash
|
||||||
|
on l2_sent_msg (msg_hash) where deleted_at IS NULL;
|
||||||
|
|
||||||
|
create unique index uk_nonce
|
||||||
|
on l2_sent_msg (nonce) where deleted_at IS NULL;
|
||||||
|
|
||||||
|
CREATE INDEX idx_msg_hash_deleted_at_l2_sent_msg on l2_sent_msg (msg_hash, deleted_at);
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION update_timestamp()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
BEGIN
|
||||||
|
NEW.updated_at = CURRENT_TIMESTAMP;
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ language 'plpgsql';
|
||||||
|
|
||||||
|
CREATE TRIGGER update_timestamp BEFORE UPDATE
|
||||||
|
ON l2_sent_msg FOR EACH ROW EXECUTE PROCEDURE
|
||||||
|
update_timestamp();
|
||||||
|
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
drop table if exists l2_sent_msg;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
create table rollup_batch
|
||||||
|
(
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
batch_index BIGINT NOT NULL,
|
||||||
|
commit_height BIGINT NOT NULL,
|
||||||
|
start_block_number BIGINT NOT NULL,
|
||||||
|
end_block_number BIGINT NOT NULL,
|
||||||
|
batch_hash VARCHAR NOT NULL,
|
||||||
|
created_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
deleted_at TIMESTAMP(0) DEFAULT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
create unique index uk_batch_index
|
||||||
|
on rollup_batch (batch_index) where deleted_at IS NULL;
|
||||||
|
|
||||||
|
create unique index uk_batch_hash
|
||||||
|
on rollup_batch (batch_hash) where deleted_at IS NULL;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION update_timestamp()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
BEGIN
|
||||||
|
NEW.updated_at = CURRENT_TIMESTAMP;
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ language 'plpgsql';
|
||||||
|
|
||||||
|
CREATE TRIGGER update_timestamp BEFORE UPDATE
|
||||||
|
ON rollup_batch FOR EACH ROW EXECUTE PROCEDURE
|
||||||
|
update_timestamp();
|
||||||
|
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
drop table if exists rollup_batch;
|
||||||
|
-- +goose StatementEnd
|
||||||
142
bridge-history-api/orm/relayed_msg.go
Normal file
142
bridge-history-api/orm/relayed_msg.go
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
package orm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RelayedMsg is the struct for relayed_msg table
|
||||||
|
type RelayedMsg struct {
|
||||||
|
db *gorm.DB `gorm:"column:-"`
|
||||||
|
|
||||||
|
ID uint64 `json:"id" gorm:"column:id"`
|
||||||
|
MsgHash string `json:"msg_hash" gorm:"column:msg_hash"`
|
||||||
|
Height uint64 `json:"height" gorm:"column:height"`
|
||||||
|
Layer1Hash string `json:"layer1_hash" gorm:"column:layer1_hash;default:''"`
|
||||||
|
Layer2Hash string `json:"layer2_hash" gorm:"column:layer2_hash;default:''"`
|
||||||
|
CreatedAt *time.Time `json:"created_at" gorm:"column:created_at"`
|
||||||
|
UpdatedAt *time.Time `json:"updated_at" gorm:"column:updated_at"`
|
||||||
|
DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"column:deleted_at;default:NULL"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRelayedMsg create an NewRelayedMsg instance
|
||||||
|
func NewRelayedMsg(db *gorm.DB) *RelayedMsg {
|
||||||
|
return &RelayedMsg{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName returns the table name for the RelayedMsg model.
|
||||||
|
func (*RelayedMsg) TableName() string {
|
||||||
|
return "relayed_msg"
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRelayedMsgByHash get relayed msg by hash
|
||||||
|
func (r *RelayedMsg) GetRelayedMsgByHash(ctx context.Context, msgHash string) (*RelayedMsg, error) {
|
||||||
|
var result RelayedMsg
|
||||||
|
err := r.db.WithContext(ctx).Model(&RelayedMsg{}).
|
||||||
|
Where("msg_hash = ?", msgHash).
|
||||||
|
First(&result).
|
||||||
|
Error
|
||||||
|
if err != nil {
|
||||||
|
if err == gorm.ErrRecordNotFound {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("RelayedMsg.GetRelayedMsgByHash error: %w", err)
|
||||||
|
}
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLatestRelayedHeightOnL1 get latest relayed height on l1
|
||||||
|
func (r *RelayedMsg) GetLatestRelayedHeightOnL1(ctx context.Context) (uint64, error) {
|
||||||
|
var result RelayedMsg
|
||||||
|
err := r.db.WithContext(ctx).Model(&RelayedMsg{}).
|
||||||
|
Select("height").
|
||||||
|
Where("layer1_hash != ''").
|
||||||
|
Order("height DESC").
|
||||||
|
First(&result).
|
||||||
|
Error
|
||||||
|
if err != nil {
|
||||||
|
if err == gorm.ErrRecordNotFound {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("RelayedMsg.GetLatestRelayedHeightOnL1 error: %w", err)
|
||||||
|
}
|
||||||
|
return result.Height, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLatestRelayedHeightOnL2 get latest relayed height on l2
|
||||||
|
func (r *RelayedMsg) GetLatestRelayedHeightOnL2(ctx context.Context) (uint64, error) {
|
||||||
|
var result RelayedMsg
|
||||||
|
err := r.db.WithContext(ctx).Model(&RelayedMsg{}).
|
||||||
|
Select("height").
|
||||||
|
Where("layer2_hash != ''").
|
||||||
|
Order("height DESC").
|
||||||
|
First(&result).
|
||||||
|
Error
|
||||||
|
if err != nil {
|
||||||
|
if err == gorm.ErrRecordNotFound {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("RelayedMsg.GetLatestRelayedHeightOnL2 error: %w", err)
|
||||||
|
}
|
||||||
|
return result.Height, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertRelayedMsg batch insert relayed msg into db and return the transaction
|
||||||
|
func (r *RelayedMsg) InsertRelayedMsg(ctx context.Context, messages []*RelayedMsg, dbTx ...*gorm.DB) error {
|
||||||
|
if len(messages) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
db := r.db
|
||||||
|
if len(dbTx) > 0 && dbTx[0] != nil {
|
||||||
|
db = dbTx[0]
|
||||||
|
}
|
||||||
|
db.WithContext(ctx)
|
||||||
|
err := db.Model(&RelayedMsg{}).Create(&messages).Error
|
||||||
|
if err != nil {
|
||||||
|
l2hashes := make([]string, 0, len(messages))
|
||||||
|
l1hashes := make([]string, 0, len(messages))
|
||||||
|
heights := make([]uint64, 0, len(messages))
|
||||||
|
for _, msg := range messages {
|
||||||
|
l2hashes = append(l2hashes, msg.Layer2Hash)
|
||||||
|
l1hashes = append(l1hashes, msg.Layer1Hash)
|
||||||
|
heights = append(heights, msg.Height)
|
||||||
|
}
|
||||||
|
log.Error("failed to insert l2 sent messages", "l2hashes", l2hashes, "l1hashes", l1hashes, "heights", heights, "err", err)
|
||||||
|
return fmt.Errorf("RelayedMsg.InsertRelayedMsg error: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteL1RelayedHashAfterHeight delete l1 relayed hash after height
|
||||||
|
func (r *RelayedMsg) DeleteL1RelayedHashAfterHeight(ctx context.Context, height uint64, dbTx ...*gorm.DB) error {
|
||||||
|
db := r.db
|
||||||
|
if len(dbTx) > 0 && dbTx[0] != nil {
|
||||||
|
db = dbTx[0]
|
||||||
|
}
|
||||||
|
db.WithContext(ctx)
|
||||||
|
err := db.Model(&RelayedMsg{}).
|
||||||
|
Delete("height > ? AND layer1_hash != ''", height).Error
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("RelayedMsg.DeleteL1RelayedHashAfterHeight error: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteL2RelayedHashAfterHeight delete l2 relayed hash after heights
|
||||||
|
func (r *RelayedMsg) DeleteL2RelayedHashAfterHeight(ctx context.Context, height uint64, dbTx ...*gorm.DB) error {
|
||||||
|
db := r.db
|
||||||
|
if len(dbTx) > 0 && dbTx[0] != nil {
|
||||||
|
db = dbTx[0]
|
||||||
|
}
|
||||||
|
db.WithContext(ctx)
|
||||||
|
err := db.Model(&RelayedMsg{}).
|
||||||
|
Delete("height > ? AND layer2_hash != ''", height).Error
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("RelayedMsg.DeleteL2RelayedHashAfterHeight error: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,15 +1,18 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"bridge-history-api/db"
|
"bridge-history-api/orm"
|
||||||
"bridge-history-api/db/orm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Finalized the schema of tx finalized infos
|
||||||
type Finalized struct {
|
type Finalized struct {
|
||||||
Hash string `json:"hash"`
|
Hash string `json:"hash"`
|
||||||
Amount string `json:"amount"`
|
Amount string `json:"amount"`
|
||||||
@@ -19,38 +22,82 @@ type Finalized struct {
|
|||||||
BlockTimestamp *time.Time `json:"blockTimestamp"` // uselesss
|
BlockTimestamp *time.Time `json:"blockTimestamp"` // uselesss
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UserClaimInfo the schema of tx claim infos
|
||||||
|
type UserClaimInfo struct {
|
||||||
|
From string `json:"from"`
|
||||||
|
To string `json:"to"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
Nonce string `json:"nonce"`
|
||||||
|
BatchHash string `json:"batch_hash"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Proof string `json:"proof"`
|
||||||
|
BatchIndex string `json:"batch_index"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TxHistoryInfo the schema of tx history infos
|
||||||
type TxHistoryInfo struct {
|
type TxHistoryInfo struct {
|
||||||
Hash string `json:"hash"`
|
Hash string `json:"hash"`
|
||||||
Amount string `json:"amount"`
|
Amount string `json:"amount"`
|
||||||
To string `json:"to"` // useless
|
To string `json:"to"` // useless
|
||||||
IsL1 bool `json:"isL1"`
|
IsL1 bool `json:"isL1"`
|
||||||
BlockNumber uint64 `json:"blockNumber"`
|
BlockNumber uint64 `json:"blockNumber"`
|
||||||
BlockTimestamp *time.Time `json:"blockTimestamp"` // useless
|
BlockTimestamp *time.Time `json:"blockTimestamp"` // useless
|
||||||
FinalizeTx *Finalized `json:"finalizeTx"`
|
FinalizeTx *Finalized `json:"finalizeTx"`
|
||||||
CreatedAt *time.Time `json:"createdTime"`
|
ClaimInfo *UserClaimInfo `json:"claimInfo"`
|
||||||
|
CreatedAt *time.Time `json:"createdTime"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// HistoryService example service.
|
// HistoryService example service.
|
||||||
type HistoryService interface {
|
type HistoryService interface {
|
||||||
GetTxsByAddress(address common.Address, offset int64, limit int64) ([]*TxHistoryInfo, uint64, error)
|
GetTxsByAddress(address common.Address, offset int, limit int) ([]*TxHistoryInfo, uint64, error)
|
||||||
GetTxsByHashes(hashes []string) ([]*TxHistoryInfo, error)
|
GetTxsByHashes(hashes []string) ([]*TxHistoryInfo, error)
|
||||||
|
GetClaimableTxsByAddress(address common.Address, offset int, limit int) ([]*TxHistoryInfo, uint64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHistoryService returns a service backed with a "db"
|
// NewHistoryService returns a service backed with a "db"
|
||||||
func NewHistoryService(db db.OrmFactory) HistoryService {
|
func NewHistoryService(ctx context.Context, db *gorm.DB) HistoryService {
|
||||||
service := &historyBackend{db: db, prefix: "Scroll-Bridge-History-Server"}
|
service := &historyBackend{ctx: ctx, db: db, prefix: "Scroll-Bridge-History-Server"}
|
||||||
return service
|
return service
|
||||||
}
|
}
|
||||||
|
|
||||||
type historyBackend struct {
|
type historyBackend struct {
|
||||||
prefix string
|
prefix string
|
||||||
db db.OrmFactory
|
ctx context.Context
|
||||||
|
db *gorm.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateCrossTxHash(msgHash string, txInfo *TxHistoryInfo, db db.OrmFactory) {
|
// GetCrossTxClaimInfo get UserClaimInfos by address
|
||||||
relayed, err := db.GetRelayedMsgByHash(msgHash)
|
func GetCrossTxClaimInfo(ctx context.Context, msgHash string, db *gorm.DB) *UserClaimInfo {
|
||||||
|
l2SentMsgOrm := orm.NewL2SentMsg(db)
|
||||||
|
rollupOrm := orm.NewRollupBatch(db)
|
||||||
|
l2sentMsg, err := l2SentMsgOrm.GetL2SentMsgByHash(ctx, msgHash)
|
||||||
|
if err != nil || l2sentMsg == nil {
|
||||||
|
log.Debug("GetCrossTxClaimInfo failed", "error", err)
|
||||||
|
return &UserClaimInfo{}
|
||||||
|
}
|
||||||
|
batch, err := rollupOrm.GetRollupBatchByIndex(ctx, l2sentMsg.BatchIndex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("updateCrossTxHash failed", "error", err)
|
log.Debug("GetCrossTxClaimInfo failed", "error", err)
|
||||||
|
return &UserClaimInfo{}
|
||||||
|
}
|
||||||
|
return &UserClaimInfo{
|
||||||
|
From: l2sentMsg.Sender,
|
||||||
|
To: l2sentMsg.Target,
|
||||||
|
Value: l2sentMsg.Value,
|
||||||
|
Nonce: strconv.FormatUint(l2sentMsg.Nonce, 10),
|
||||||
|
Message: l2sentMsg.MsgData,
|
||||||
|
Proof: "0x" + l2sentMsg.MsgProof,
|
||||||
|
BatchHash: batch.BatchHash,
|
||||||
|
BatchIndex: strconv.FormatUint(l2sentMsg.BatchIndex, 10),
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateCrossTxHash(ctx context.Context, msgHash string, txInfo *TxHistoryInfo, db *gorm.DB) {
|
||||||
|
relayed := orm.NewRelayedMsg(db)
|
||||||
|
relayed, err := relayed.GetRelayedMsgByHash(ctx, msgHash)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("updateCrossTxHash failed", "error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if relayed == nil {
|
if relayed == nil {
|
||||||
@@ -69,13 +116,61 @@ func updateCrossTxHash(msgHash string, txInfo *TxHistoryInfo, db db.OrmFactory)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *historyBackend) GetTxsByAddress(address common.Address, offset int64, limit int64) ([]*TxHistoryInfo, uint64, error) {
|
// GetClaimableTxsByAddress get all claimable txs under given address
|
||||||
|
func (h *historyBackend) GetClaimableTxsByAddress(address common.Address, offset int, limit int) ([]*TxHistoryInfo, uint64, error) {
|
||||||
var txHistories []*TxHistoryInfo
|
var txHistories []*TxHistoryInfo
|
||||||
total, err := h.db.GetTotalCrossMsgCountByAddress(address.String())
|
l2SentMsgOrm := orm.NewL2SentMsg(h.db)
|
||||||
|
l2CrossMsgOrm := orm.NewCrossMsg(h.db)
|
||||||
|
total, err := l2SentMsgOrm.GetClaimableL2SentMsgByAddressTotalNum(h.ctx, address.Hex())
|
||||||
if err != nil || total == 0 {
|
if err != nil || total == 0 {
|
||||||
return txHistories, 0, err
|
return txHistories, 0, err
|
||||||
}
|
}
|
||||||
result, err := h.db.GetCrossMsgsByAddressWithOffset(address.String(), offset, limit)
|
results, err := l2SentMsgOrm.GetClaimableL2SentMsgByAddressWithOffset(h.ctx, address.Hex(), offset, limit)
|
||||||
|
if err != nil || len(results) == 0 {
|
||||||
|
return txHistories, 0, err
|
||||||
|
}
|
||||||
|
var msgHashList []string
|
||||||
|
for _, result := range results {
|
||||||
|
msgHashList = append(msgHashList, result.MsgHash)
|
||||||
|
}
|
||||||
|
crossMsgs, err := l2CrossMsgOrm.GetL2CrossMsgByMsgHashList(h.ctx, msgHashList)
|
||||||
|
// crossMsgs can be empty, because they can be emitted by user directly call contract
|
||||||
|
if err != nil {
|
||||||
|
return txHistories, 0, err
|
||||||
|
}
|
||||||
|
crossMsgMap := make(map[string]*orm.CrossMsg)
|
||||||
|
for _, crossMsg := range crossMsgs {
|
||||||
|
crossMsgMap[crossMsg.MsgHash] = crossMsg
|
||||||
|
}
|
||||||
|
for _, result := range results {
|
||||||
|
txInfo := &TxHistoryInfo{
|
||||||
|
Hash: result.TxHash,
|
||||||
|
IsL1: false,
|
||||||
|
BlockNumber: result.Height,
|
||||||
|
FinalizeTx: &Finalized{},
|
||||||
|
ClaimInfo: GetCrossTxClaimInfo(h.ctx, result.MsgHash, h.db),
|
||||||
|
}
|
||||||
|
if crossMsg, exist := crossMsgMap[result.MsgHash]; exist {
|
||||||
|
txInfo.Amount = crossMsg.Amount
|
||||||
|
txInfo.To = crossMsg.Target
|
||||||
|
txInfo.BlockTimestamp = crossMsg.Timestamp
|
||||||
|
txInfo.CreatedAt = crossMsg.CreatedAt
|
||||||
|
}
|
||||||
|
txHistories = append(txHistories, txInfo)
|
||||||
|
}
|
||||||
|
return txHistories, total, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTxsByAddress get all txs under given address
|
||||||
|
func (h *historyBackend) GetTxsByAddress(address common.Address, offset int, limit int) ([]*TxHistoryInfo, uint64, error) {
|
||||||
|
var txHistories []*TxHistoryInfo
|
||||||
|
utilOrm := orm.NewCrossMsg(h.db)
|
||||||
|
total, err := utilOrm.GetTotalCrossMsgCountByAddress(h.ctx, address.String())
|
||||||
|
if err != nil || total == 0 {
|
||||||
|
return txHistories, 0, err
|
||||||
|
}
|
||||||
|
result, err := utilOrm.GetCrossMsgsByAddressWithOffset(h.ctx, address.String(), offset, limit)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
@@ -91,17 +186,20 @@ func (h *historyBackend) GetTxsByAddress(address common.Address, offset int64, l
|
|||||||
FinalizeTx: &Finalized{
|
FinalizeTx: &Finalized{
|
||||||
Hash: "",
|
Hash: "",
|
||||||
},
|
},
|
||||||
|
ClaimInfo: GetCrossTxClaimInfo(h.ctx, msg.MsgHash, h.db),
|
||||||
}
|
}
|
||||||
updateCrossTxHash(msg.MsgHash, txHistory, h.db)
|
updateCrossTxHash(h.ctx, msg.MsgHash, txHistory, h.db)
|
||||||
txHistories = append(txHistories, txHistory)
|
txHistories = append(txHistories, txHistory)
|
||||||
}
|
}
|
||||||
return txHistories, total, nil
|
return txHistories, total, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetTxsByHashes get tx infos under given tx hashes
|
||||||
func (h *historyBackend) GetTxsByHashes(hashes []string) ([]*TxHistoryInfo, error) {
|
func (h *historyBackend) GetTxsByHashes(hashes []string) ([]*TxHistoryInfo, error) {
|
||||||
txHistories := make([]*TxHistoryInfo, 0)
|
txHistories := make([]*TxHistoryInfo, 0)
|
||||||
|
CrossMsgOrm := orm.NewCrossMsg(h.db)
|
||||||
for _, hash := range hashes {
|
for _, hash := range hashes {
|
||||||
l1result, err := h.db.GetL1CrossMsgByHash(common.HexToHash(hash))
|
l1result, err := CrossMsgOrm.GetL1CrossMsgByHash(h.ctx, common.HexToHash(hash))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -118,11 +216,11 @@ func (h *historyBackend) GetTxsByHashes(hashes []string) ([]*TxHistoryInfo, erro
|
|||||||
Hash: "",
|
Hash: "",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
updateCrossTxHash(l1result.MsgHash, txHistory, h.db)
|
updateCrossTxHash(h.ctx, l1result.MsgHash, txHistory, h.db)
|
||||||
txHistories = append(txHistories, txHistory)
|
txHistories = append(txHistories, txHistory)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
l2result, err := h.db.GetL2CrossMsgByHash(common.HexToHash(hash))
|
l2result, err := CrossMsgOrm.GetL2CrossMsgByHash(h.ctx, common.HexToHash(hash))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -138,8 +236,9 @@ func (h *historyBackend) GetTxsByHashes(hashes []string) ([]*TxHistoryInfo, erro
|
|||||||
FinalizeTx: &Finalized{
|
FinalizeTx: &Finalized{
|
||||||
Hash: "",
|
Hash: "",
|
||||||
},
|
},
|
||||||
|
ClaimInfo: GetCrossTxClaimInfo(h.ctx, l2result.MsgHash, h.db),
|
||||||
}
|
}
|
||||||
updateCrossTxHash(l2result.MsgHash, txHistory, h.db)
|
updateCrossTxHash(h.ctx, l2result.MsgHash, txHistory, h.db)
|
||||||
txHistories = append(txHistories, txHistory)
|
txHistories = append(txHistories, txHistory)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
391
bridge-history-api/utils/parse_event.go
Normal file
391
bridge-history-api/utils/parse_event.go
Normal file
@@ -0,0 +1,391 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"math/big"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
|
||||||
|
backendabi "bridge-history-api/abi"
|
||||||
|
"bridge-history-api/orm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CachedParsedTxCalldata store parsed batch infos
|
||||||
|
type CachedParsedTxCalldata struct {
|
||||||
|
CallDataIndex uint64
|
||||||
|
BatchIndices []uint64
|
||||||
|
StartBlocks []uint64
|
||||||
|
EndBlocks []uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseBackendL1EventLogs parses L1 watched events
|
||||||
|
func ParseBackendL1EventLogs(logs []types.Log) ([]*orm.CrossMsg, []*orm.RelayedMsg, error) {
|
||||||
|
// Need use contract abi to parse event Log
|
||||||
|
// Can only be tested after we have our contracts set up
|
||||||
|
|
||||||
|
var l1CrossMsg []*orm.CrossMsg
|
||||||
|
var relayedMsgs []*orm.RelayedMsg
|
||||||
|
var msgHash string
|
||||||
|
for _, vlog := range logs {
|
||||||
|
switch vlog.Topics[0] {
|
||||||
|
case backendabi.L1DepositETHSig:
|
||||||
|
event := backendabi.DepositETH{}
|
||||||
|
err := UnpackLog(backendabi.L1ETHGatewayABI, &event, "DepositETH", vlog)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Failed to unpack DepositETH event", "err", err)
|
||||||
|
return l1CrossMsg, relayedMsgs, err
|
||||||
|
}
|
||||||
|
l1CrossMsg = append(l1CrossMsg, &orm.CrossMsg{
|
||||||
|
Height: vlog.BlockNumber,
|
||||||
|
Sender: event.From.String(),
|
||||||
|
Target: event.To.String(),
|
||||||
|
Amount: event.Amount.String(),
|
||||||
|
Asset: int(orm.ETH),
|
||||||
|
Layer1Hash: vlog.TxHash.Hex(),
|
||||||
|
MsgType: int(orm.Layer1Msg),
|
||||||
|
MsgHash: msgHash,
|
||||||
|
})
|
||||||
|
case backendabi.L1DepositERC20Sig:
|
||||||
|
event := backendabi.ERC20MessageEvent{}
|
||||||
|
err := UnpackLog(backendabi.L1StandardERC20GatewayABI, &event, "DepositERC20", vlog)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Failed to unpack DepositERC20 event", "err", err)
|
||||||
|
return l1CrossMsg, relayedMsgs, err
|
||||||
|
}
|
||||||
|
l1CrossMsg = append(l1CrossMsg, &orm.CrossMsg{
|
||||||
|
Height: vlog.BlockNumber,
|
||||||
|
Sender: event.From.String(),
|
||||||
|
Target: event.To.String(),
|
||||||
|
Amount: event.Amount.String(),
|
||||||
|
Asset: int(orm.ERC20),
|
||||||
|
Layer1Hash: vlog.TxHash.Hex(),
|
||||||
|
Layer1Token: event.L1Token.Hex(),
|
||||||
|
Layer2Token: event.L2Token.Hex(),
|
||||||
|
MsgType: int(orm.Layer1Msg),
|
||||||
|
MsgHash: msgHash,
|
||||||
|
})
|
||||||
|
case backendabi.L1DepositERC721Sig:
|
||||||
|
event := backendabi.ERC721MessageEvent{}
|
||||||
|
err := UnpackLog(backendabi.L1ERC721GatewayABI, &event, "DepositERC721", vlog)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Failed to unpack DepositERC721 event", "err", err)
|
||||||
|
return l1CrossMsg, relayedMsgs, err
|
||||||
|
}
|
||||||
|
l1CrossMsg = append(l1CrossMsg, &orm.CrossMsg{
|
||||||
|
Height: vlog.BlockNumber,
|
||||||
|
Sender: event.From.String(),
|
||||||
|
Target: event.To.String(),
|
||||||
|
Asset: int(orm.ERC721),
|
||||||
|
Layer1Hash: vlog.TxHash.Hex(),
|
||||||
|
Layer1Token: event.L1Token.Hex(),
|
||||||
|
Layer2Token: event.L2Token.Hex(),
|
||||||
|
TokenIDs: event.TokenID.String(),
|
||||||
|
MsgType: int(orm.Layer1Msg),
|
||||||
|
MsgHash: msgHash,
|
||||||
|
})
|
||||||
|
case backendabi.L1DepositERC1155Sig:
|
||||||
|
event := backendabi.ERC1155MessageEvent{}
|
||||||
|
err := UnpackLog(backendabi.L1ERC1155GatewayABI, &event, "DepositERC1155", vlog)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Failed to unpack DepositERC1155 event", "err", err)
|
||||||
|
return l1CrossMsg, relayedMsgs, err
|
||||||
|
}
|
||||||
|
l1CrossMsg = append(l1CrossMsg, &orm.CrossMsg{
|
||||||
|
Height: vlog.BlockNumber,
|
||||||
|
Sender: event.From.String(),
|
||||||
|
Target: event.To.String(),
|
||||||
|
Asset: int(orm.ERC1155),
|
||||||
|
Layer1Hash: vlog.TxHash.Hex(),
|
||||||
|
Layer1Token: event.L1Token.Hex(),
|
||||||
|
Layer2Token: event.L2Token.Hex(),
|
||||||
|
TokenIDs: event.TokenID.String(),
|
||||||
|
Amount: event.Amount.String(),
|
||||||
|
MsgType: int(orm.Layer1Msg),
|
||||||
|
MsgHash: msgHash,
|
||||||
|
})
|
||||||
|
case backendabi.L1SentMessageEventSignature:
|
||||||
|
event := backendabi.L1SentMessageEvent{}
|
||||||
|
err := UnpackLog(backendabi.L1ScrollMessengerABI, &event, "SentMessage", vlog)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Failed to unpack SentMessage event", "err", err)
|
||||||
|
return l1CrossMsg, relayedMsgs, err
|
||||||
|
}
|
||||||
|
// since every deposit event will emit after a sent event, so can use this msg_hash as next withdraw event's msg_hash
|
||||||
|
msgHash = ComputeMessageHash(event.Sender, event.Target, event.Value, event.MessageNonce, event.Message).Hex()
|
||||||
|
case backendabi.L1BatchDepositERC721Sig:
|
||||||
|
event := backendabi.BatchERC721MessageEvent{}
|
||||||
|
err := UnpackLog(backendabi.L1ERC721GatewayABI, &event, "BatchDepositERC721", vlog)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Failed to unpack BatchDepositERC721 event", "err", err)
|
||||||
|
return l1CrossMsg, relayedMsgs, err
|
||||||
|
}
|
||||||
|
l1CrossMsg = append(l1CrossMsg, &orm.CrossMsg{
|
||||||
|
Height: vlog.BlockNumber,
|
||||||
|
Sender: event.From.String(),
|
||||||
|
Target: event.To.String(),
|
||||||
|
Asset: int(orm.ERC721),
|
||||||
|
Layer1Hash: vlog.TxHash.Hex(),
|
||||||
|
Layer1Token: event.L1Token.Hex(),
|
||||||
|
Layer2Token: event.L2Token.Hex(),
|
||||||
|
TokenIDs: convertBigIntArrayToString(event.TokenIDs),
|
||||||
|
MsgType: int(orm.Layer1Msg),
|
||||||
|
MsgHash: msgHash,
|
||||||
|
})
|
||||||
|
case backendabi.L1BatchDepositERC1155Sig:
|
||||||
|
event := backendabi.BatchERC1155MessageEvent{}
|
||||||
|
err := UnpackLog(backendabi.L1ERC1155GatewayABI, &event, "BatchDepositERC1155", vlog)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Failed to unpack BatchDepositERC1155 event", "err", err)
|
||||||
|
return l1CrossMsg, relayedMsgs, err
|
||||||
|
}
|
||||||
|
l1CrossMsg = append(l1CrossMsg, &orm.CrossMsg{
|
||||||
|
Height: vlog.BlockNumber,
|
||||||
|
Sender: event.From.String(),
|
||||||
|
Target: event.To.String(),
|
||||||
|
Asset: int(orm.ERC1155),
|
||||||
|
Layer1Hash: vlog.TxHash.Hex(),
|
||||||
|
Layer1Token: event.L1Token.Hex(),
|
||||||
|
Layer2Token: event.L2Token.Hex(),
|
||||||
|
TokenIDs: convertBigIntArrayToString(event.TokenIDs),
|
||||||
|
TokenAmounts: convertBigIntArrayToString(event.TokenAmounts),
|
||||||
|
MsgType: int(orm.Layer1Msg),
|
||||||
|
MsgHash: msgHash,
|
||||||
|
})
|
||||||
|
case backendabi.L1RelayedMessageEventSignature:
|
||||||
|
event := backendabi.L1RelayedMessageEvent{}
|
||||||
|
err := UnpackLog(backendabi.L1ScrollMessengerABI, &event, "RelayedMessage", vlog)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Failed to unpack RelayedMessage event", "err", err)
|
||||||
|
return l1CrossMsg, relayedMsgs, err
|
||||||
|
}
|
||||||
|
relayedMsgs = append(relayedMsgs, &orm.RelayedMsg{
|
||||||
|
MsgHash: event.MessageHash.String(),
|
||||||
|
Height: vlog.BlockNumber,
|
||||||
|
Layer1Hash: vlog.TxHash.Hex(),
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return l1CrossMsg, relayedMsgs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseBackendL2EventLogs parses L2 watched events
|
||||||
|
func ParseBackendL2EventLogs(logs []types.Log) ([]*orm.CrossMsg, []*orm.RelayedMsg, []*orm.L2SentMsg, error) {
|
||||||
|
// Need use contract abi to parse event Log
|
||||||
|
// Can only be tested after we have our contracts set up
|
||||||
|
|
||||||
|
var l2CrossMsg []*orm.CrossMsg
|
||||||
|
// this is use to confirm finalized l1 msg
|
||||||
|
var relayedMsgs []*orm.RelayedMsg
|
||||||
|
var l2SentMsgs []*orm.L2SentMsg
|
||||||
|
for _, vlog := range logs {
|
||||||
|
switch vlog.Topics[0] {
|
||||||
|
case backendabi.L2WithdrawETHSig:
|
||||||
|
event := backendabi.DepositETH{}
|
||||||
|
err := UnpackLog(backendabi.L2ETHGatewayABI, &event, "WithdrawETH", vlog)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Failed to unpack WithdrawETH event", "err", err)
|
||||||
|
return l2CrossMsg, relayedMsgs, l2SentMsgs, err
|
||||||
|
}
|
||||||
|
l2SentMsgs[len(l2SentMsgs)-1].OriginalSender = event.From.Hex()
|
||||||
|
l2CrossMsg = append(l2CrossMsg, &orm.CrossMsg{
|
||||||
|
Height: vlog.BlockNumber,
|
||||||
|
Sender: event.From.String(),
|
||||||
|
Target: event.To.String(),
|
||||||
|
Amount: event.Amount.String(),
|
||||||
|
Asset: int(orm.ETH),
|
||||||
|
Layer2Hash: vlog.TxHash.Hex(),
|
||||||
|
MsgType: int(orm.Layer2Msg),
|
||||||
|
MsgHash: l2SentMsgs[len(l2SentMsgs)-1].MsgHash,
|
||||||
|
})
|
||||||
|
case backendabi.L2WithdrawERC20Sig:
|
||||||
|
event := backendabi.ERC20MessageEvent{}
|
||||||
|
err := UnpackLog(backendabi.L2StandardERC20GatewayABI, &event, "WithdrawERC20", vlog)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Failed to unpack WithdrawERC20 event", "err", err)
|
||||||
|
return l2CrossMsg, relayedMsgs, l2SentMsgs, err
|
||||||
|
}
|
||||||
|
l2SentMsgs[len(l2SentMsgs)-1].OriginalSender = event.From.Hex()
|
||||||
|
l2CrossMsg = append(l2CrossMsg, &orm.CrossMsg{
|
||||||
|
Height: vlog.BlockNumber,
|
||||||
|
Sender: event.From.String(),
|
||||||
|
Target: event.To.String(),
|
||||||
|
Amount: event.Amount.String(),
|
||||||
|
Asset: int(orm.ERC20),
|
||||||
|
Layer2Hash: vlog.TxHash.Hex(),
|
||||||
|
Layer1Token: event.L1Token.Hex(),
|
||||||
|
Layer2Token: event.L2Token.Hex(),
|
||||||
|
MsgType: int(orm.Layer2Msg),
|
||||||
|
MsgHash: l2SentMsgs[len(l2SentMsgs)-1].MsgHash,
|
||||||
|
})
|
||||||
|
case backendabi.L2WithdrawERC721Sig:
|
||||||
|
event := backendabi.ERC721MessageEvent{}
|
||||||
|
err := UnpackLog(backendabi.L2ERC721GatewayABI, &event, "WithdrawERC721", vlog)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Failed to unpack WithdrawERC721 event", "err", err)
|
||||||
|
return l2CrossMsg, relayedMsgs, l2SentMsgs, err
|
||||||
|
}
|
||||||
|
l2SentMsgs[len(l2SentMsgs)-1].OriginalSender = event.From.Hex()
|
||||||
|
l2CrossMsg = append(l2CrossMsg, &orm.CrossMsg{
|
||||||
|
Height: vlog.BlockNumber,
|
||||||
|
Sender: event.From.String(),
|
||||||
|
Target: event.To.String(),
|
||||||
|
Asset: int(orm.ERC721),
|
||||||
|
Layer2Hash: vlog.TxHash.Hex(),
|
||||||
|
Layer1Token: event.L1Token.Hex(),
|
||||||
|
Layer2Token: event.L2Token.Hex(),
|
||||||
|
TokenIDs: event.TokenID.String(),
|
||||||
|
MsgType: int(orm.Layer2Msg),
|
||||||
|
MsgHash: l2SentMsgs[len(l2SentMsgs)-1].MsgHash,
|
||||||
|
})
|
||||||
|
case backendabi.L2WithdrawERC1155Sig:
|
||||||
|
event := backendabi.ERC1155MessageEvent{}
|
||||||
|
err := UnpackLog(backendabi.L2ERC1155GatewayABI, &event, "WithdrawERC1155", vlog)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Failed to unpack WithdrawERC1155 event", "err", err)
|
||||||
|
return l2CrossMsg, relayedMsgs, l2SentMsgs, err
|
||||||
|
}
|
||||||
|
l2SentMsgs[len(l2SentMsgs)-1].OriginalSender = event.From.Hex()
|
||||||
|
l2CrossMsg = append(l2CrossMsg, &orm.CrossMsg{
|
||||||
|
Height: vlog.BlockNumber,
|
||||||
|
Sender: event.From.String(),
|
||||||
|
Target: event.To.String(),
|
||||||
|
Asset: int(orm.ERC1155),
|
||||||
|
Layer2Hash: vlog.TxHash.Hex(),
|
||||||
|
Layer1Token: event.L1Token.Hex(),
|
||||||
|
Layer2Token: event.L2Token.Hex(),
|
||||||
|
TokenIDs: event.TokenID.String(),
|
||||||
|
Amount: event.Amount.String(),
|
||||||
|
MsgType: int(orm.Layer2Msg),
|
||||||
|
MsgHash: l2SentMsgs[len(l2SentMsgs)-1].MsgHash,
|
||||||
|
})
|
||||||
|
case backendabi.L2BatchWithdrawERC721Sig:
|
||||||
|
event := backendabi.BatchERC721MessageEvent{}
|
||||||
|
err := UnpackLog(backendabi.L2ERC721GatewayABI, &event, "BatchWithdrawERC721", vlog)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Failed to unpack BatchWithdrawERC721 event", "err", err)
|
||||||
|
return l2CrossMsg, relayedMsgs, l2SentMsgs, err
|
||||||
|
}
|
||||||
|
l2SentMsgs[len(l2SentMsgs)-1].OriginalSender = event.From.Hex()
|
||||||
|
l2CrossMsg = append(l2CrossMsg, &orm.CrossMsg{
|
||||||
|
Height: vlog.BlockNumber,
|
||||||
|
Sender: event.From.String(),
|
||||||
|
Target: event.To.String(),
|
||||||
|
Asset: int(orm.ERC721),
|
||||||
|
Layer1Hash: vlog.TxHash.Hex(),
|
||||||
|
Layer1Token: event.L1Token.Hex(),
|
||||||
|
Layer2Token: event.L2Token.Hex(),
|
||||||
|
MsgType: int(orm.Layer2Msg),
|
||||||
|
TokenIDs: convertBigIntArrayToString(event.TokenIDs),
|
||||||
|
MsgHash: l2SentMsgs[len(l2SentMsgs)-1].MsgHash,
|
||||||
|
})
|
||||||
|
case backendabi.L2BatchWithdrawERC1155Sig:
|
||||||
|
event := backendabi.BatchERC1155MessageEvent{}
|
||||||
|
err := UnpackLog(backendabi.L2ERC1155GatewayABI, &event, "BatchWithdrawERC1155", vlog)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Failed to unpack BatchWithdrawERC1155 event", "err", err)
|
||||||
|
return l2CrossMsg, relayedMsgs, l2SentMsgs, err
|
||||||
|
}
|
||||||
|
l2SentMsgs[len(l2SentMsgs)-1].OriginalSender = event.From.Hex()
|
||||||
|
l2CrossMsg = append(l2CrossMsg, &orm.CrossMsg{
|
||||||
|
Height: vlog.BlockNumber,
|
||||||
|
Sender: event.From.String(),
|
||||||
|
Target: event.To.String(),
|
||||||
|
Asset: int(orm.ERC1155),
|
||||||
|
Layer1Hash: vlog.TxHash.Hex(),
|
||||||
|
Layer1Token: event.L1Token.Hex(),
|
||||||
|
Layer2Token: event.L2Token.Hex(),
|
||||||
|
MsgType: int(orm.Layer2Msg),
|
||||||
|
TokenIDs: convertBigIntArrayToString(event.TokenIDs),
|
||||||
|
TokenAmounts: convertBigIntArrayToString(event.TokenAmounts),
|
||||||
|
MsgHash: l2SentMsgs[len(l2SentMsgs)-1].MsgHash,
|
||||||
|
})
|
||||||
|
case backendabi.L2SentMessageEventSignature:
|
||||||
|
event := backendabi.L2SentMessageEvent{}
|
||||||
|
err := UnpackLog(backendabi.L2ScrollMessengerABI, &event, "SentMessage", vlog)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Failed to unpack SentMessage event", "err", err)
|
||||||
|
return l2CrossMsg, relayedMsgs, l2SentMsgs, err
|
||||||
|
}
|
||||||
|
// since every withdraw event will emit after a sent event, so can use this msg_hash as next withdraw event's msg_hash
|
||||||
|
msgHash := ComputeMessageHash(event.Sender, event.Target, event.Value, event.MessageNonce, event.Message)
|
||||||
|
l2SentMsgs = append(l2SentMsgs,
|
||||||
|
&orm.L2SentMsg{
|
||||||
|
Sender: event.Sender.Hex(),
|
||||||
|
TxHash: vlog.TxHash.Hex(),
|
||||||
|
Target: event.Target.Hex(),
|
||||||
|
Value: event.Value.String(),
|
||||||
|
MsgHash: msgHash.Hex(),
|
||||||
|
Height: vlog.BlockNumber,
|
||||||
|
Nonce: event.MessageNonce.Uint64(),
|
||||||
|
MsgData: hexutil.Encode(event.Message),
|
||||||
|
})
|
||||||
|
case backendabi.L2RelayedMessageEventSignature:
|
||||||
|
event := backendabi.L2RelayedMessageEvent{}
|
||||||
|
err := UnpackLog(backendabi.L2ScrollMessengerABI, &event, "RelayedMessage", vlog)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Failed to unpack RelayedMessage event", "err", err)
|
||||||
|
return l2CrossMsg, relayedMsgs, l2SentMsgs, err
|
||||||
|
}
|
||||||
|
relayedMsgs = append(relayedMsgs, &orm.RelayedMsg{
|
||||||
|
MsgHash: event.MessageHash.String(),
|
||||||
|
Height: vlog.BlockNumber,
|
||||||
|
Layer2Hash: vlog.TxHash.Hex(),
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return l2CrossMsg, relayedMsgs, l2SentMsgs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseBatchInfoFromScrollChain parses ScrollChain events
|
||||||
|
func ParseBatchInfoFromScrollChain(ctx context.Context, client *ethclient.Client, logs []types.Log) ([]*orm.RollupBatch, error) {
|
||||||
|
var rollupBatches []*orm.RollupBatch
|
||||||
|
for _, vlog := range logs {
|
||||||
|
switch vlog.Topics[0] {
|
||||||
|
case backendabi.L1CommitBatchEventSignature:
|
||||||
|
event := backendabi.L1CommitBatchEvent{}
|
||||||
|
err := UnpackLog(backendabi.ScrollChainABI, &event, "CommitBatch", vlog)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Failed to unpack CommitBatch event", "err", err)
|
||||||
|
return rollupBatches, err
|
||||||
|
}
|
||||||
|
commitTx, isPending, err := client.TransactionByHash(ctx, vlog.TxHash)
|
||||||
|
if err != nil || isPending {
|
||||||
|
log.Warn("Failed to get commit Batch tx receipt or the tx is still pending", "err", err)
|
||||||
|
return rollupBatches, err
|
||||||
|
}
|
||||||
|
index, startBlock, endBlock, err := GetBatchRangeFromCalldataV2(commitTx.Data())
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Failed to get batch range from calldata", "hash", commitTx.Hash().Hex(), "height", vlog.BlockNumber)
|
||||||
|
return rollupBatches, err
|
||||||
|
}
|
||||||
|
rollupBatches = append(rollupBatches, &orm.RollupBatch{
|
||||||
|
CommitHeight: vlog.BlockNumber,
|
||||||
|
BatchIndex: index,
|
||||||
|
BatchHash: event.BatchHash.Hex(),
|
||||||
|
StartBlockNumber: startBlock,
|
||||||
|
EndBlockNumber: endBlock,
|
||||||
|
})
|
||||||
|
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rollupBatches, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertBigIntArrayToString(array []*big.Int) string {
|
||||||
|
stringArray := make([]string, len(array))
|
||||||
|
for i, num := range array {
|
||||||
|
stringArray[i] = num.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
result := strings.Join(stringArray, ", ")
|
||||||
|
return result
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package utils
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
@@ -21,6 +22,7 @@ func Keccak2(a common.Hash, b common.Hash) common.Hash {
|
|||||||
return common.BytesToHash(crypto.Keccak256(append(a.Bytes()[:], b.Bytes()[:]...)))
|
return common.BytesToHash(crypto.Keccak256(append(a.Bytes()[:], b.Bytes()[:]...)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSafeBlockNumber get the safe block number, which is the current block number minus the confirmations
|
||||||
func GetSafeBlockNumber(ctx context.Context, client *ethclient.Client, confirmations uint64) (uint64, error) {
|
func GetSafeBlockNumber(ctx context.Context, client *ethclient.Client, confirmations uint64) (uint64, error) {
|
||||||
number, err := client.BlockNumber(ctx)
|
number, err := client.BlockNumber(ctx)
|
||||||
if err != nil || number <= confirmations {
|
if err != nil || number <= confirmations {
|
||||||
@@ -62,6 +64,53 @@ func ComputeMessageHash(
|
|||||||
return common.BytesToHash(crypto.Keccak256(data))
|
return common.BytesToHash(crypto.Keccak256(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type commitBatchArgs struct {
|
||||||
|
Version uint8
|
||||||
|
ParentBatchHeader []byte
|
||||||
|
Chunks [][]byte
|
||||||
|
SkippedL1MessageBitmap []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBatchRangeFromCalldataV2 find the block range from calldata, both inclusive.
|
||||||
|
func GetBatchRangeFromCalldataV2(calldata []byte) (uint64, uint64, uint64, error) {
|
||||||
|
method := backendabi.ScrollChainV2ABI.Methods["commitBatch"]
|
||||||
|
values, err := method.Inputs.Unpack(calldata[4:])
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, 0, err
|
||||||
|
}
|
||||||
|
args := commitBatchArgs{}
|
||||||
|
err = method.Inputs.Copy(&args, values)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var startBlock uint64
|
||||||
|
var finishBlock uint64
|
||||||
|
|
||||||
|
// decode batchIndex from ParentBatchHeader
|
||||||
|
if len(args.ParentBatchHeader) < 9 {
|
||||||
|
return 0, 0, 0, errors.New("invalid parent batch header")
|
||||||
|
}
|
||||||
|
batchIndex := binary.BigEndian.Uint64(args.ParentBatchHeader[1:9]) + 1
|
||||||
|
|
||||||
|
// decode blocks from chunk and assume that there's no empty chunk
|
||||||
|
// | 1 byte | 60 bytes | ... | 60 bytes |
|
||||||
|
// | num blocks | block 1 | ... | block n |
|
||||||
|
if len(args.Chunks) == 0 {
|
||||||
|
return 0, 0, 0, errors.New("invalid chunks")
|
||||||
|
}
|
||||||
|
chunk := args.Chunks[0]
|
||||||
|
block := chunk[1:61] // first block in chunk
|
||||||
|
startBlock = binary.BigEndian.Uint64(block[0:8])
|
||||||
|
|
||||||
|
chunk = args.Chunks[len(args.Chunks)-1]
|
||||||
|
lastBlockIndex := int(chunk[0]) - 1
|
||||||
|
block = chunk[1+lastBlockIndex*60 : 1+lastBlockIndex*60+60] // last block in chunk
|
||||||
|
finishBlock = binary.BigEndian.Uint64(block[0:8])
|
||||||
|
|
||||||
|
return batchIndex, startBlock, finishBlock, err
|
||||||
|
}
|
||||||
|
|
||||||
// GetBatchRangeFromCalldataV1 find the block range from calldata, both inclusive.
|
// GetBatchRangeFromCalldataV1 find the block range from calldata, both inclusive.
|
||||||
func GetBatchRangeFromCalldataV1(calldata []byte) ([]uint64, []uint64, []uint64, error) {
|
func GetBatchRangeFromCalldataV1(calldata []byte) ([]uint64, []uint64, []uint64, error) {
|
||||||
var batchIndices []uint64
|
var batchIndices []uint64
|
||||||
|
|||||||
@@ -20,6 +20,22 @@ func TestKeccak2(t *testing.T) {
|
|||||||
assert.Equal(t, "0xc0ffbd7f501bd3d49721b0724b2bff657cb2378f15d5a9b97cd7ea5bf630d512", c.Hex())
|
assert.Equal(t, "0xc0ffbd7f501bd3d49721b0724b2bff657cb2378f15d5a9b97cd7ea5bf630d512", c.Hex())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetBatchRangeFromCalldataV2(t *testing.T) {
|
||||||
|
// single chunk
|
||||||
|
batchIndex, start, finish, err := utils.GetBatchRangeFromCalldataV2(common.Hex2Bytes("1325aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000005900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003d0100000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000100000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000"))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, start, uint64(1))
|
||||||
|
assert.Equal(t, finish, uint64(1))
|
||||||
|
assert.Equal(t, batchIndex, uint64(1))
|
||||||
|
|
||||||
|
// multiple chunk
|
||||||
|
batchIndex, start, finish, err = utils.GetBatchRangeFromCalldataV2(common.Hex2Bytes("1325aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000003e0000000000000000000000000000000000000000000000000000000000000007900000000000000000100000000000000010000000000000001038433daac85a0b03cd443ed50bc85e832c883061651ae2182b2984751e0b340119b828c2a2798d2c957228ebeaff7e10bb099ae0d4e224f3eeb779ff61cba610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000004c01000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000010000000001000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b403000000000000000b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005000300000000000000000b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00050000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012c01000000000100000000010000000001000000000100000000010000000001000000000100000000010000000001000000000100000000010000000001000000000100000000010000000001000000000100000000010000000001000000000100000000010000000001000000000100000000010000000001000000000100000000010000000001000000000100000000010000000001000000000100000000010000000001000000000100000000010000000001000000000100000000010000000001000000000100000000010000000001000000000100000000010000000001000000000100000000010000000001000000000100000000010000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa800000000000000000000000000000000000000000000000000000000000000aa"))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, start, uint64(10))
|
||||||
|
assert.Equal(t, finish, uint64(20))
|
||||||
|
assert.Equal(t, batchIndex, uint64(2))
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetBatchRangeFromCalldataV1(t *testing.T) {
|
func TestGetBatchRangeFromCalldataV1(t *testing.T) {
|
||||||
calldata, err := os.ReadFile("../testdata/commit-batches-0x3095e91db7ba4a6fbf4654d607db322e58ff5579c502219c8024acaea74cf311.txt")
|
calldata, err := os.ReadFile("../testdata/commit-batches-0x3095e91db7ba4a6fbf4654d607db322e58ff5579c502219c8024acaea74cf311.txt")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ IMAGE_VERSION=latest
|
|||||||
REPO_ROOT_DIR=./..
|
REPO_ROOT_DIR=./..
|
||||||
|
|
||||||
mock_abi:
|
mock_abi:
|
||||||
go run github.com/scroll-tech/go-ethereum/cmd/abigen --sol mock_bridge/MockBridgeL1.sol --pkg mock_bridge --out mock_bridge/MockBridgeL1.go
|
cd .. && go run github.com/scroll-tech/go-ethereum/cmd/abigen --sol ./bridge/mock_bridge/MockBridgeL1.sol --pkg mock_bridge --out ./bridge/mock_bridge/MockBridgeL1.go
|
||||||
go run github.com/scroll-tech/go-ethereum/cmd/abigen --sol mock_bridge/MockBridgeL2.sol --pkg mock_bridge --out mock_bridge/MockBridgeL2.go
|
cd .. && go run github.com/scroll-tech/go-ethereum/cmd/abigen --sol ./bridge/mock_bridge/MockBridgeL2.sol --pkg mock_bridge --out ./bridge/mock_bridge/MockBridgeL2.go
|
||||||
|
|
||||||
bridge_bins: ## Builds the Bridge bins.
|
bridge_bins: ## Builds the Bridge bins.
|
||||||
go build -o $(PWD)/build/bin/event_watcher ./cmd/event_watcher/
|
go build -o $(PWD)/build/bin/event_watcher ./cmd/event_watcher/
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -16,9 +16,9 @@ func TestEventSignature(t *testing.T) {
|
|||||||
assert.Equal(L1FailedRelayedMessageEventSignature, common.HexToHash("99d0e048484baa1b1540b1367cb128acd7ab2946d1ed91ec10e3c85e4bf51b8f"))
|
assert.Equal(L1FailedRelayedMessageEventSignature, common.HexToHash("99d0e048484baa1b1540b1367cb128acd7ab2946d1ed91ec10e3c85e4bf51b8f"))
|
||||||
|
|
||||||
assert.Equal(L1CommitBatchEventSignature, common.HexToHash("2cdc615c74452778c0fb6184735e014c13aad2b62774fe0b09bd1dcc2cc14a62"))
|
assert.Equal(L1CommitBatchEventSignature, common.HexToHash("2cdc615c74452778c0fb6184735e014c13aad2b62774fe0b09bd1dcc2cc14a62"))
|
||||||
assert.Equal(L1FinalizeBatchEventSignature, common.HexToHash("6be443154c959a7a1645b4392b6fa97d8e8ab6e8fd853d7085e8867083737d79"))
|
assert.Equal(L1FinalizeBatchEventSignature, common.HexToHash("9d3058a3cb9739a2527f22dd9a4138065844037d3004254952e2458d808cc364"))
|
||||||
|
|
||||||
assert.Equal(L1QueueTransactionEventSignature, common.HexToHash("bdcc7517f8fe3db6506dfd910942d0bbecaf3d6a506dadea65b0d988e75b9439"))
|
assert.Equal(L1QueueTransactionEventSignature, common.HexToHash("69cfcb8e6d4192b8aba9902243912587f37e550d75c1fa801491fce26717f37e"))
|
||||||
|
|
||||||
assert.Equal(L2SentMessageEventSignature, common.HexToHash("104371f3b442861a2a7b82a070afbbaab748bb13757bf47769e170e37809ec1e"))
|
assert.Equal(L2SentMessageEventSignature, common.HexToHash("104371f3b442861a2a7b82a070afbbaab748bb13757bf47769e170e37809ec1e"))
|
||||||
assert.Equal(L2RelayedMessageEventSignature, common.HexToHash("4641df4a962071e12719d8c8c8e5ac7fc4d97b927346a3d7a335b1f7517e133c"))
|
assert.Equal(L2RelayedMessageEventSignature, common.HexToHash("4641df4a962071e12719d8c8c8e5ac7fc4d97b927346a3d7a335b1f7517e133c"))
|
||||||
@@ -35,10 +35,10 @@ func TestPackRelayL2MessageWithProof(t *testing.T) {
|
|||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
|
|
||||||
proof := IL1ScrollMessengerL2MessageProof{
|
proof := IL1ScrollMessengerL2MessageProof{
|
||||||
BatchHash: common.Hash{},
|
BatchIndex: big.NewInt(0),
|
||||||
MerkleProof: make([]byte, 0),
|
MerkleProof: []byte{},
|
||||||
}
|
}
|
||||||
_, err = l1MessengerABI.Pack("relayMessageWithProof", common.Address{}, common.Address{}, big.NewInt(0), big.NewInt(0), make([]byte, 0), proof)
|
_, err = l1MessengerABI.Pack("relayMessageWithProof", common.Address{}, common.Address{}, big.NewInt(0), big.NewInt(0), []byte{}, proof)
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,27 +48,12 @@ func TestPackCommitBatch(t *testing.T) {
|
|||||||
scrollChainABI, err := ScrollChainMetaData.GetAbi()
|
scrollChainABI, err := ScrollChainMetaData.GetAbi()
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
|
|
||||||
header := IScrollChainBlockContext{
|
version := uint8(1)
|
||||||
BlockHash: common.Hash{},
|
var parentBatchHeader []byte
|
||||||
ParentHash: common.Hash{},
|
var chunks [][]byte
|
||||||
BlockNumber: 0,
|
var skippedL1MessageBitmap []byte
|
||||||
Timestamp: 0,
|
|
||||||
BaseFee: big.NewInt(0),
|
|
||||||
GasLimit: 0,
|
|
||||||
NumTransactions: 0,
|
|
||||||
NumL1Messages: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
batch := IScrollChainBatch{
|
_, err = scrollChainABI.Pack("commitBatch", version, parentBatchHeader, chunks, skippedL1MessageBitmap)
|
||||||
Blocks: []IScrollChainBlockContext{header},
|
|
||||||
PrevStateRoot: common.Hash{},
|
|
||||||
NewStateRoot: common.Hash{},
|
|
||||||
WithdrawTrieRoot: common.Hash{},
|
|
||||||
BatchIndex: 0,
|
|
||||||
L2Transactions: make([]byte, 0),
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = scrollChainABI.Pack("commitBatch", batch)
|
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,14 +63,26 @@ func TestPackFinalizeBatchWithProof(t *testing.T) {
|
|||||||
l1RollupABI, err := ScrollChainMetaData.GetAbi()
|
l1RollupABI, err := ScrollChainMetaData.GetAbi()
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
|
|
||||||
proof := make([]*big.Int, 10)
|
batchHeader := []byte{}
|
||||||
instance := make([]*big.Int, 10)
|
prevStateRoot := common.Hash{}
|
||||||
for i := 0; i < 10; i++ {
|
postStateRoot := common.Hash{}
|
||||||
proof[i] = big.NewInt(0)
|
withdrawRoot := common.Hash{}
|
||||||
instance[i] = big.NewInt(0)
|
aggrProof := []byte{}
|
||||||
}
|
|
||||||
|
|
||||||
_, err = l1RollupABI.Pack("finalizeBatchWithProof", common.Hash{}, proof, instance)
|
_, err = l1RollupABI.Pack("finalizeBatchWithProof", batchHeader, prevStateRoot, postStateRoot, withdrawRoot, aggrProof)
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPackImportGenesisBatch(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
l1RollupABI, err := ScrollChainMetaData.GetAbi()
|
||||||
|
assert.NoError(err)
|
||||||
|
|
||||||
|
batchHeader := []byte{}
|
||||||
|
stateRoot := common.Hash{}
|
||||||
|
|
||||||
|
_, err = l1RollupABI.Pack("importGenesisBatch", batchHeader, stateRoot)
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +92,7 @@ func TestPackRelayL1Message(t *testing.T) {
|
|||||||
l2MessengerABI, err := L2ScrollMessengerMetaData.GetAbi()
|
l2MessengerABI, err := L2ScrollMessengerMetaData.GetAbi()
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
|
|
||||||
_, err = l2MessengerABI.Pack("relayMessage", common.Address{}, common.Address{}, big.NewInt(0), big.NewInt(0), make([]byte, 0))
|
_, err = l2MessengerABI.Pack("relayMessage", common.Address{}, common.Address{}, big.NewInt(0), big.NewInt(0), []byte{})
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,6 +123,6 @@ func TestPackImportBlock(t *testing.T) {
|
|||||||
|
|
||||||
l1BlockContainerABI := L1BlockContainerABI
|
l1BlockContainerABI := L1BlockContainerABI
|
||||||
|
|
||||||
_, err := l1BlockContainerABI.Pack("importBlockHeader", common.Hash{}, make([]byte, 0), false)
|
_, err := l1BlockContainerABI.Pack("importBlockHeader", common.Hash{}, []byte{}, false)
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ import (
|
|||||||
"github.com/scroll-tech/go-ethereum/log"
|
"github.com/scroll-tech/go-ethereum/log"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"scroll-tech/common/database"
|
||||||
"scroll-tech/common/metrics"
|
"scroll-tech/common/metrics"
|
||||||
cutils "scroll-tech/common/utils"
|
"scroll-tech/common/utils"
|
||||||
"scroll-tech/common/version"
|
"scroll-tech/common/version"
|
||||||
|
|
||||||
"scroll-tech/bridge/internal/config"
|
"scroll-tech/bridge/internal/config"
|
||||||
"scroll-tech/bridge/internal/controller/watcher"
|
"scroll-tech/bridge/internal/controller/watcher"
|
||||||
"scroll-tech/bridge/internal/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var app *cli.App
|
var app *cli.App
|
||||||
@@ -29,18 +29,18 @@ func init() {
|
|||||||
app.Name = "event-watcher"
|
app.Name = "event-watcher"
|
||||||
app.Usage = "The Scroll Event Watcher"
|
app.Usage = "The Scroll Event Watcher"
|
||||||
app.Version = version.Version
|
app.Version = version.Version
|
||||||
app.Flags = append(app.Flags, cutils.CommonFlags...)
|
app.Flags = append(app.Flags, utils.CommonFlags...)
|
||||||
app.Commands = []*cli.Command{}
|
app.Commands = []*cli.Command{}
|
||||||
app.Before = func(ctx *cli.Context) error {
|
app.Before = func(ctx *cli.Context) error {
|
||||||
return cutils.LogSetup(ctx)
|
return utils.LogSetup(ctx)
|
||||||
}
|
}
|
||||||
// Register `event-watcher-test` app for integration-test.
|
// Register `event-watcher-test` app for integration-test.
|
||||||
cutils.RegisterSimulation(app, cutils.EventWatcherApp)
|
utils.RegisterSimulation(app, utils.EventWatcherApp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func action(ctx *cli.Context) error {
|
func action(ctx *cli.Context) error {
|
||||||
// Load config file.
|
// Load config file.
|
||||||
cfgFile := ctx.String(cutils.ConfigFileFlag.Name)
|
cfgFile := ctx.String(utils.ConfigFileFlag.Name)
|
||||||
cfg, err := config.NewConfig(cfgFile)
|
cfg, err := config.NewConfig(cfgFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Crit("failed to load config file", "config file", cfgFile, "error", err)
|
log.Crit("failed to load config file", "config file", cfgFile, "error", err)
|
||||||
@@ -48,13 +48,13 @@ func action(ctx *cli.Context) error {
|
|||||||
|
|
||||||
subCtx, cancel := context.WithCancel(ctx.Context)
|
subCtx, cancel := context.WithCancel(ctx.Context)
|
||||||
// Init db connection
|
// Init db connection
|
||||||
db, err := utils.InitDB(cfg.DBConfig)
|
db, err := database.InitDB(cfg.DBConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Crit("failed to init db connection", "err", err)
|
log.Crit("failed to init db connection", "err", err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
cancel()
|
cancel()
|
||||||
if err = utils.CloseDB(db); err != nil {
|
if err = database.CloseDB(db); err != nil {
|
||||||
log.Error("can not close ormFactory", "error", err)
|
log.Error("can not close ormFactory", "error", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -75,14 +75,14 @@ func action(ctx *cli.Context) error {
|
|||||||
l1watcher := watcher.NewL1WatcherClient(ctx.Context, l1client, cfg.L1Config.StartHeight, cfg.L1Config.Confirmations, cfg.L1Config.L1MessengerAddress, cfg.L1Config.L1MessageQueueAddress, cfg.L1Config.ScrollChainContractAddress, db)
|
l1watcher := watcher.NewL1WatcherClient(ctx.Context, l1client, cfg.L1Config.StartHeight, cfg.L1Config.Confirmations, cfg.L1Config.L1MessengerAddress, cfg.L1Config.L1MessageQueueAddress, cfg.L1Config.ScrollChainContractAddress, db)
|
||||||
l2watcher := watcher.NewL2WatcherClient(ctx.Context, l2client, cfg.L2Config.Confirmations, cfg.L2Config.L2MessengerAddress, cfg.L2Config.L2MessageQueueAddress, cfg.L2Config.WithdrawTrieRootSlot, db)
|
l2watcher := watcher.NewL2WatcherClient(ctx.Context, l2client, cfg.L2Config.Confirmations, cfg.L2Config.L2MessengerAddress, cfg.L2Config.L2MessageQueueAddress, cfg.L2Config.WithdrawTrieRootSlot, db)
|
||||||
|
|
||||||
go cutils.Loop(subCtx, 10*time.Second, func() {
|
go utils.Loop(subCtx, 10*time.Second, func() {
|
||||||
if loopErr := l1watcher.FetchContractEvent(); loopErr != nil {
|
if loopErr := l1watcher.FetchContractEvent(); loopErr != nil {
|
||||||
log.Error("Failed to fetch bridge contract", "err", loopErr)
|
log.Error("Failed to fetch bridge contract", "err", loopErr)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Start l2 watcher process
|
// Start l2 watcher process
|
||||||
go cutils.Loop(subCtx, 2*time.Second, l2watcher.FetchContractEvent)
|
go utils.Loop(subCtx, 2*time.Second, l2watcher.FetchContractEvent)
|
||||||
// Finish start all l2 functions
|
// Finish start all l2 functions
|
||||||
log.Info("Start event-watcher successfully")
|
log.Info("Start event-watcher successfully")
|
||||||
|
|
||||||
|
|||||||
@@ -11,14 +11,15 @@ import (
|
|||||||
"github.com/scroll-tech/go-ethereum/log"
|
"github.com/scroll-tech/go-ethereum/log"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"scroll-tech/common/database"
|
||||||
"scroll-tech/common/metrics"
|
"scroll-tech/common/metrics"
|
||||||
cutils "scroll-tech/common/utils"
|
"scroll-tech/common/utils"
|
||||||
"scroll-tech/common/version"
|
"scroll-tech/common/version"
|
||||||
|
|
||||||
"scroll-tech/bridge/internal/config"
|
"scroll-tech/bridge/internal/config"
|
||||||
"scroll-tech/bridge/internal/controller/relayer"
|
"scroll-tech/bridge/internal/controller/relayer"
|
||||||
"scroll-tech/bridge/internal/controller/watcher"
|
"scroll-tech/bridge/internal/controller/watcher"
|
||||||
"scroll-tech/bridge/internal/utils"
|
butils "scroll-tech/bridge/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var app *cli.App
|
var app *cli.App
|
||||||
@@ -31,31 +32,31 @@ func init() {
|
|||||||
app.Usage = "The Scroll Gas Oracle"
|
app.Usage = "The Scroll Gas Oracle"
|
||||||
app.Description = "Scroll Gas Oracle."
|
app.Description = "Scroll Gas Oracle."
|
||||||
app.Version = version.Version
|
app.Version = version.Version
|
||||||
app.Flags = append(app.Flags, cutils.CommonFlags...)
|
app.Flags = append(app.Flags, utils.CommonFlags...)
|
||||||
app.Commands = []*cli.Command{}
|
app.Commands = []*cli.Command{}
|
||||||
app.Before = func(ctx *cli.Context) error {
|
app.Before = func(ctx *cli.Context) error {
|
||||||
return cutils.LogSetup(ctx)
|
return utils.LogSetup(ctx)
|
||||||
}
|
}
|
||||||
// Register `gas-oracle-test` app for integration-test.
|
// Register `gas-oracle-test` app for integration-test.
|
||||||
cutils.RegisterSimulation(app, cutils.GasOracleApp)
|
utils.RegisterSimulation(app, utils.GasOracleApp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func action(ctx *cli.Context) error {
|
func action(ctx *cli.Context) error {
|
||||||
// Load config file.
|
// Load config file.
|
||||||
cfgFile := ctx.String(cutils.ConfigFileFlag.Name)
|
cfgFile := ctx.String(utils.ConfigFileFlag.Name)
|
||||||
cfg, err := config.NewConfig(cfgFile)
|
cfg, err := config.NewConfig(cfgFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Crit("failed to load config file", "config file", cfgFile, "error", err)
|
log.Crit("failed to load config file", "config file", cfgFile, "error", err)
|
||||||
}
|
}
|
||||||
subCtx, cancel := context.WithCancel(ctx.Context)
|
subCtx, cancel := context.WithCancel(ctx.Context)
|
||||||
// Init db connection
|
// Init db connection
|
||||||
db, err := utils.InitDB(cfg.DBConfig)
|
db, err := database.InitDB(cfg.DBConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Crit("failed to init db connection", "err", err)
|
log.Crit("failed to init db connection", "err", err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
cancel()
|
cancel()
|
||||||
if err = utils.CloseDB(db); err != nil {
|
if err = database.CloseDB(db); err != nil {
|
||||||
log.Error("can not close ormFactory", "error", err)
|
log.Error("can not close ormFactory", "error", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -76,21 +77,22 @@ func action(ctx *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
l1watcher := watcher.NewL1WatcherClient(ctx.Context, l1client, cfg.L1Config.StartHeight, cfg.L1Config.Confirmations, cfg.L1Config.L1MessengerAddress, cfg.L1Config.L1MessageQueueAddress, cfg.L1Config.ScrollChainContractAddress, db)
|
l1watcher := watcher.NewL1WatcherClient(ctx.Context, l1client, cfg.L1Config.StartHeight, cfg.L1Config.Confirmations,
|
||||||
|
cfg.L1Config.L1MessengerAddress, cfg.L1Config.L1MessageQueueAddress, cfg.L1Config.ScrollChainContractAddress, db)
|
||||||
|
|
||||||
l1relayer, err := relayer.NewLayer1Relayer(ctx.Context, db, cfg.L1Config.RelayerConfig)
|
l1relayer, err := relayer.NewLayer1Relayer(ctx.Context, db, cfg.L1Config.RelayerConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("failed to create new l1 relayer", "config file", cfgFile, "error", err)
|
log.Error("failed to create new l1 relayer", "config file", cfgFile, "error", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
l2relayer, err := relayer.NewLayer2Relayer(ctx.Context, l2client, db, cfg.L2Config.RelayerConfig)
|
l2relayer, err := relayer.NewLayer2Relayer(ctx.Context, l2client, db, cfg.L2Config.RelayerConfig, false /* initGenesis */)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("failed to create new l2 relayer", "config file", cfgFile, "error", err)
|
log.Error("failed to create new l2 relayer", "config file", cfgFile, "error", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Start l1 watcher process
|
// Start l1 watcher process
|
||||||
go cutils.LoopWithContext(subCtx, 10*time.Second, func(ctx context.Context) {
|
go utils.LoopWithContext(subCtx, 10*time.Second, func(ctx context.Context) {
|
||||||
number, loopErr := utils.GetLatestConfirmedBlockNumber(ctx, l1client, cfg.L1Config.Confirmations)
|
number, loopErr := butils.GetLatestConfirmedBlockNumber(ctx, l1client, cfg.L1Config.Confirmations)
|
||||||
if loopErr != nil {
|
if loopErr != nil {
|
||||||
log.Error("failed to get block number", "err", loopErr)
|
log.Error("failed to get block number", "err", loopErr)
|
||||||
return
|
return
|
||||||
@@ -102,8 +104,8 @@ func action(ctx *cli.Context) error {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Start l1relayer process
|
// Start l1relayer process
|
||||||
go cutils.Loop(subCtx, 10*time.Second, l1relayer.ProcessGasPriceOracle)
|
go utils.Loop(subCtx, 10*time.Second, l1relayer.ProcessGasPriceOracle)
|
||||||
go cutils.Loop(subCtx, 2*time.Second, l2relayer.ProcessGasPriceOracle)
|
go utils.Loop(subCtx, 2*time.Second, l2relayer.ProcessGasPriceOracle)
|
||||||
|
|
||||||
// Finish start all message relayer functions
|
// Finish start all message relayer functions
|
||||||
log.Info("Start gas-oracle successfully")
|
log.Info("Start gas-oracle successfully")
|
||||||
|
|||||||
@@ -7,17 +7,16 @@ import (
|
|||||||
"os/signal"
|
"os/signal"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/ethclient"
|
|
||||||
"github.com/scroll-tech/go-ethereum/log"
|
"github.com/scroll-tech/go-ethereum/log"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"scroll-tech/common/database"
|
||||||
"scroll-tech/common/metrics"
|
"scroll-tech/common/metrics"
|
||||||
cutils "scroll-tech/common/utils"
|
"scroll-tech/common/utils"
|
||||||
"scroll-tech/common/version"
|
"scroll-tech/common/version"
|
||||||
|
|
||||||
"scroll-tech/bridge/internal/config"
|
"scroll-tech/bridge/internal/config"
|
||||||
"scroll-tech/bridge/internal/controller/relayer"
|
"scroll-tech/bridge/internal/controller/relayer"
|
||||||
"scroll-tech/bridge/internal/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var app *cli.App
|
var app *cli.App
|
||||||
@@ -30,18 +29,18 @@ func init() {
|
|||||||
app.Usage = "The Scroll 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.Description = "Message Relayer contains two main service: 1) relay l1 message to l2. 2) relay l2 message to l1."
|
||||||
app.Version = version.Version
|
app.Version = version.Version
|
||||||
app.Flags = append(app.Flags, cutils.CommonFlags...)
|
app.Flags = append(app.Flags, utils.CommonFlags...)
|
||||||
app.Commands = []*cli.Command{}
|
app.Commands = []*cli.Command{}
|
||||||
app.Before = func(ctx *cli.Context) error {
|
app.Before = func(ctx *cli.Context) error {
|
||||||
return cutils.LogSetup(ctx)
|
return utils.LogSetup(ctx)
|
||||||
}
|
}
|
||||||
// Register `message-relayer-test` app for integration-test.
|
// Register `message-relayer-test` app for integration-test.
|
||||||
cutils.RegisterSimulation(app, cutils.MessageRelayerApp)
|
utils.RegisterSimulation(app, utils.MessageRelayerApp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func action(ctx *cli.Context) error {
|
func action(ctx *cli.Context) error {
|
||||||
// Load config file.
|
// Load config file.
|
||||||
cfgFile := ctx.String(cutils.ConfigFileFlag.Name)
|
cfgFile := ctx.String(utils.ConfigFileFlag.Name)
|
||||||
cfg, err := config.NewConfig(cfgFile)
|
cfg, err := config.NewConfig(cfgFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Crit("failed to load config file", "config file", cfgFile, "error", err)
|
log.Crit("failed to load config file", "config file", cfgFile, "error", err)
|
||||||
@@ -49,13 +48,13 @@ func action(ctx *cli.Context) error {
|
|||||||
|
|
||||||
subCtx, cancel := context.WithCancel(ctx.Context)
|
subCtx, cancel := context.WithCancel(ctx.Context)
|
||||||
// Init db connection
|
// Init db connection
|
||||||
db, err := utils.InitDB(cfg.DBConfig)
|
db, err := database.InitDB(cfg.DBConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Crit("failed to init db connection", "err", err)
|
log.Crit("failed to init db connection", "err", err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
cancel()
|
cancel()
|
||||||
if err = utils.CloseDB(db); err != nil {
|
if err = database.CloseDB(db); err != nil {
|
||||||
log.Error("can not close ormFactory", "error", err)
|
log.Error("can not close ormFactory", "error", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -63,29 +62,14 @@ func action(ctx *cli.Context) error {
|
|||||||
// Start metrics server.
|
// Start metrics server.
|
||||||
metrics.Serve(subCtx, ctx)
|
metrics.Serve(subCtx, ctx)
|
||||||
|
|
||||||
// Init l2geth connection
|
|
||||||
l2client, err := ethclient.Dial(cfg.L2Config.Endpoint)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("failed to connect l2 geth", "config file", cfgFile, "error", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
l1relayer, err := relayer.NewLayer1Relayer(ctx.Context, db, cfg.L1Config.RelayerConfig)
|
l1relayer, err := relayer.NewLayer1Relayer(ctx.Context, db, cfg.L1Config.RelayerConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("failed to create new l1 relayer", "config file", cfgFile, "error", err)
|
log.Error("failed to create new l1 relayer", "config file", cfgFile, "error", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
l2relayer, err := relayer.NewLayer2Relayer(ctx.Context, l2client, db, cfg.L2Config.RelayerConfig)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("failed to create new l2 relayer", "config file", cfgFile, "error", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start l1relayer process
|
// Start l1relayer process
|
||||||
go cutils.Loop(subCtx, 10*time.Second, l1relayer.ProcessSavedEvents)
|
go utils.Loop(subCtx, 10*time.Second, l1relayer.ProcessSavedEvents)
|
||||||
|
|
||||||
// Start l2relayer process
|
|
||||||
go cutils.Loop(subCtx, 2*time.Second, l2relayer.ProcessSavedEvents)
|
|
||||||
|
|
||||||
// Finish start all message relayer functions
|
// Finish start all message relayer functions
|
||||||
log.Info("Start message-relayer successfully")
|
log.Info("Start message-relayer successfully")
|
||||||
|
|||||||
@@ -11,14 +11,15 @@ import (
|
|||||||
"github.com/scroll-tech/go-ethereum/log"
|
"github.com/scroll-tech/go-ethereum/log"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"scroll-tech/common/database"
|
||||||
"scroll-tech/common/metrics"
|
"scroll-tech/common/metrics"
|
||||||
cutils "scroll-tech/common/utils"
|
"scroll-tech/common/utils"
|
||||||
"scroll-tech/common/version"
|
"scroll-tech/common/version"
|
||||||
|
|
||||||
"scroll-tech/bridge/internal/config"
|
"scroll-tech/bridge/internal/config"
|
||||||
"scroll-tech/bridge/internal/controller/relayer"
|
"scroll-tech/bridge/internal/controller/relayer"
|
||||||
"scroll-tech/bridge/internal/controller/watcher"
|
"scroll-tech/bridge/internal/controller/watcher"
|
||||||
"scroll-tech/bridge/internal/utils"
|
butils "scroll-tech/bridge/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var app *cli.App
|
var app *cli.App
|
||||||
@@ -30,18 +31,19 @@ func init() {
|
|||||||
app.Name = "rollup-relayer"
|
app.Name = "rollup-relayer"
|
||||||
app.Usage = "The Scroll Rollup Relayer"
|
app.Usage = "The Scroll Rollup Relayer"
|
||||||
app.Version = version.Version
|
app.Version = version.Version
|
||||||
app.Flags = append(app.Flags, cutils.CommonFlags...)
|
app.Flags = append(app.Flags, utils.CommonFlags...)
|
||||||
|
app.Flags = append(app.Flags, utils.RollupRelayerFlags...)
|
||||||
app.Commands = []*cli.Command{}
|
app.Commands = []*cli.Command{}
|
||||||
app.Before = func(ctx *cli.Context) error {
|
app.Before = func(ctx *cli.Context) error {
|
||||||
return cutils.LogSetup(ctx)
|
return utils.LogSetup(ctx)
|
||||||
}
|
}
|
||||||
// Register `rollup-relayer-test` app for integration-test.
|
// Register `rollup-relayer-test` app for integration-test.
|
||||||
cutils.RegisterSimulation(app, cutils.RollupRelayerApp)
|
utils.RegisterSimulation(app, utils.RollupRelayerApp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func action(ctx *cli.Context) error {
|
func action(ctx *cli.Context) error {
|
||||||
// Load config file.
|
// Load config file.
|
||||||
cfgFile := ctx.String(cutils.ConfigFileFlag.Name)
|
cfgFile := ctx.String(utils.ConfigFileFlag.Name)
|
||||||
cfg, err := config.NewConfig(cfgFile)
|
cfg, err := config.NewConfig(cfgFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Crit("failed to load config file", "config file", cfgFile, "error", err)
|
log.Crit("failed to load config file", "config file", cfgFile, "error", err)
|
||||||
@@ -49,13 +51,13 @@ func action(ctx *cli.Context) error {
|
|||||||
|
|
||||||
subCtx, cancel := context.WithCancel(ctx.Context)
|
subCtx, cancel := context.WithCancel(ctx.Context)
|
||||||
// Init db connection
|
// Init db connection
|
||||||
db, err := utils.InitDB(cfg.DBConfig)
|
db, err := database.InitDB(cfg.DBConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Crit("failed to init db connection", "err", err)
|
log.Crit("failed to init db connection", "err", err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
cancel()
|
cancel()
|
||||||
if err = utils.CloseDB(db); err != nil {
|
if err = database.CloseDB(db); err != nil {
|
||||||
log.Error("can not close ormFactory", "error", err)
|
log.Error("can not close ormFactory", "error", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -70,37 +72,45 @@ func action(ctx *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
l2relayer, err := relayer.NewLayer2Relayer(ctx.Context, l2client, db, cfg.L2Config.RelayerConfig)
|
initGenesis := ctx.Bool(utils.ImportGenesisFlag.Name)
|
||||||
|
l2relayer, err := relayer.NewLayer2Relayer(ctx.Context, l2client, db, cfg.L2Config.RelayerConfig, initGenesis)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("failed to create l2 relayer", "config file", cfgFile, "error", err)
|
log.Error("failed to create l2 relayer", "config file", cfgFile, "error", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
batchProposer := watcher.NewBatchProposer(subCtx, cfg.L2Config.BatchProposerConfig, l2relayer, db)
|
chunkProposer := watcher.NewChunkProposer(subCtx, cfg.L2Config.ChunkProposerConfig, db)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("failed to create chunkProposer", "config file", cfgFile, "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
batchProposer := watcher.NewBatchProposer(subCtx, cfg.L2Config.BatchProposerConfig, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("failed to create batchProposer", "config file", cfgFile, "error", err)
|
log.Error("failed to create batchProposer", "config file", cfgFile, "error", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
l2watcher := watcher.NewL2WatcherClient(subCtx, l2client, cfg.L2Config.Confirmations, cfg.L2Config.L2MessengerAddress, cfg.L2Config.L2MessageQueueAddress, cfg.L2Config.WithdrawTrieRootSlot, db)
|
l2watcher := watcher.NewL2WatcherClient(subCtx, l2client, cfg.L2Config.Confirmations, cfg.L2Config.L2MessengerAddress,
|
||||||
|
cfg.L2Config.L2MessageQueueAddress, cfg.L2Config.WithdrawTrieRootSlot, db)
|
||||||
|
|
||||||
// Watcher loop to fetch missing blocks
|
// Watcher loop to fetch missing blocks
|
||||||
go cutils.LoopWithContext(subCtx, 2*time.Second, func(ctx context.Context) {
|
go utils.LoopWithContext(subCtx, 2*time.Second, func(ctx context.Context) {
|
||||||
number, loopErr := utils.GetLatestConfirmedBlockNumber(ctx, l2client, cfg.L2Config.Confirmations)
|
number, loopErr := butils.GetLatestConfirmedBlockNumber(ctx, l2client, cfg.L2Config.Confirmations)
|
||||||
if loopErr != nil {
|
if loopErr != nil {
|
||||||
log.Error("failed to get block number", "err", loopErr)
|
log.Error("failed to get block number", "err", loopErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
l2watcher.TryFetchRunningMissingBlocks(ctx, number)
|
l2watcher.TryFetchRunningMissingBlocks(number)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Batch proposer loop
|
go utils.Loop(subCtx, 2*time.Second, chunkProposer.TryProposeChunk)
|
||||||
go cutils.Loop(subCtx, 2*time.Second, func() {
|
|
||||||
batchProposer.TryProposeBatch()
|
|
||||||
batchProposer.TryCommitBatches()
|
|
||||||
})
|
|
||||||
|
|
||||||
go cutils.Loop(subCtx, 2*time.Second, l2relayer.ProcessCommittedBatches)
|
go utils.Loop(subCtx, 2*time.Second, batchProposer.TryProposeBatch)
|
||||||
|
|
||||||
|
go utils.Loop(subCtx, 2*time.Second, l2relayer.ProcessPendingBatches)
|
||||||
|
|
||||||
|
go utils.Loop(subCtx, 2*time.Second, l2relayer.ProcessCommittedBatches)
|
||||||
|
|
||||||
// Finish start all rollup relayer functions.
|
// Finish start all rollup relayer functions.
|
||||||
log.Info("Start rollup-relayer successfully")
|
log.Info("Start rollup-relayer successfully")
|
||||||
|
|||||||
@@ -72,19 +72,20 @@
|
|||||||
"1414141414141414141414141414141414141414141414141414141414141414"
|
"1414141414141414141414141414141414141414141414141414141414141414"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"chunk_proposer_config": {
|
||||||
|
"max_tx_gas_per_chunk": 1123456,
|
||||||
|
"max_l2_tx_num_per_chunk": 1123,
|
||||||
|
"max_l1_commit_gas_per_chunk": 11234567,
|
||||||
|
"max_l1_commit_calldata_size_per_chunk": 112345,
|
||||||
|
"min_l1_commit_calldata_size_per_chunk": 11234,
|
||||||
|
"chunk_timeout_sec": 300
|
||||||
|
},
|
||||||
"batch_proposer_config": {
|
"batch_proposer_config": {
|
||||||
"proof_generation_freq": 1,
|
"max_chunk_num_per_batch": 112,
|
||||||
"batch_gas_threshold": 3000000,
|
"max_l1_commit_gas_per_batch": 11234567,
|
||||||
"batch_tx_num_threshold": 44,
|
"max_l1_commit_calldata_size_per_batch": 112345,
|
||||||
"batch_time_sec": 300,
|
"min_chunk_num_per_batch": 11,
|
||||||
"batch_commit_time_sec": 1200,
|
"batch_timeout_sec": 300
|
||||||
"batch_blocks_limit": 100,
|
|
||||||
"commit_tx_calldata_size_limit": 200000,
|
|
||||||
"commit_tx_batch_count_limit": 30,
|
|
||||||
"public_input_config": {
|
|
||||||
"max_tx_num": 44,
|
|
||||||
"padding_tx_hash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"db_config": {
|
"db_config": {
|
||||||
|
|||||||
@@ -4,19 +4,14 @@ go 1.19
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/agiledragon/gomonkey/v2 v2.9.0
|
github.com/agiledragon/gomonkey/v2 v2.9.0
|
||||||
github.com/jmoiron/sqlx v1.3.5
|
|
||||||
github.com/lib/pq v1.10.7
|
|
||||||
github.com/orcaman/concurrent-map v1.0.0
|
github.com/orcaman/concurrent-map v1.0.0
|
||||||
github.com/orcaman/concurrent-map/v2 v2.0.1
|
github.com/orcaman/concurrent-map/v2 v2.0.1
|
||||||
github.com/pressly/goose/v3 v3.7.0
|
github.com/scroll-tech/go-ethereum v1.10.14-0.20230613025759-f055f50f9d56
|
||||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20230508165858-27a3830afa61
|
|
||||||
github.com/smartystreets/goconvey v1.8.0
|
github.com/smartystreets/goconvey v1.8.0
|
||||||
github.com/stretchr/testify v1.8.2
|
github.com/stretchr/testify v1.8.3
|
||||||
github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa
|
github.com/urfave/cli/v2 v2.25.7
|
||||||
golang.org/x/sync v0.1.0
|
golang.org/x/sync v0.3.0
|
||||||
gorm.io/driver/postgres v1.5.0
|
gorm.io/gorm v1.25.2
|
||||||
gorm.io/gorm v1.25.1
|
|
||||||
modernc.org/mathutil v1.4.1
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -26,7 +21,6 @@ require (
|
|||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/deckarep/golang-set v1.8.0 // indirect
|
github.com/deckarep/golang-set v1.8.0 // indirect
|
||||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
|
||||||
github.com/go-stack/stack v1.8.1 // indirect
|
github.com/go-stack/stack v1.8.1 // indirect
|
||||||
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect
|
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect
|
||||||
github.com/google/uuid v1.3.0 // indirect
|
github.com/google/uuid v1.3.0 // indirect
|
||||||
@@ -35,20 +29,16 @@ require (
|
|||||||
github.com/holiman/uint256 v1.2.2 // indirect
|
github.com/holiman/uint256 v1.2.2 // indirect
|
||||||
github.com/huin/goupnp v1.0.3 // indirect
|
github.com/huin/goupnp v1.0.3 // indirect
|
||||||
github.com/iden3/go-iden3-crypto v0.0.15 // indirect
|
github.com/iden3/go-iden3-crypto v0.0.15 // indirect
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
|
||||||
github.com/jackc/pgx/v5 v5.3.0 // indirect
|
|
||||||
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
|
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
github.com/jtolds/gls v4.20.0+incompatible // indirect
|
github.com/jtolds/gls v4.20.0+incompatible // indirect
|
||||||
github.com/kr/pretty v0.3.1 // indirect
|
github.com/kr/pretty v0.3.1 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.18 // indirect
|
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||||
github.com/mattn/go-sqlite3 v1.14.14 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
|
||||||
github.com/rivo/uniseg v0.4.4 // indirect
|
github.com/rivo/uniseg v0.4.4 // indirect
|
||||||
github.com/rjeczalik/notify v0.9.1 // indirect
|
github.com/rjeczalik/notify v0.9.1 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.10.0 // indirect
|
github.com/rogpeppe/go-internal v1.10.0 // indirect
|
||||||
@@ -62,11 +52,10 @@ require (
|
|||||||
github.com/tyler-smith/go-bip39 v1.1.0 // indirect
|
github.com/tyler-smith/go-bip39 v1.1.0 // indirect
|
||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||||
golang.org/x/crypto v0.9.0 // indirect
|
golang.org/x/crypto v0.11.0 // indirect
|
||||||
golang.org/x/sys v0.8.0 // indirect
|
golang.org/x/sys v0.10.0 // indirect
|
||||||
golang.org/x/text v0.9.0 // indirect
|
|
||||||
golang.org/x/time v0.3.0 // indirect
|
golang.org/x/time v0.3.0 // indirect
|
||||||
golang.org/x/tools v0.8.0 // indirect
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
|
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
116
bridge/go.sum
116
bridge/go.sum
@@ -18,7 +18,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHH
|
|||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4=
|
github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4=
|
||||||
@@ -29,9 +28,6 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
|
|||||||
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI=
|
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI=
|
||||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
|
||||||
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
|
||||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
|
||||||
github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
|
github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
|
||||||
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
|
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
@@ -55,13 +51,6 @@ github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixH
|
|||||||
github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o=
|
github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o=
|
||||||
github.com/iden3/go-iden3-crypto v0.0.15 h1:4MJYlrot1l31Fzlo2sF56u7EVFeHHJkxGXXZCtESgK4=
|
github.com/iden3/go-iden3-crypto v0.0.15 h1:4MJYlrot1l31Fzlo2sF56u7EVFeHHJkxGXXZCtESgK4=
|
||||||
github.com/iden3/go-iden3-crypto v0.0.15/go.mod h1:dLpM4vEPJ3nDHzhWFXDjzkn1qHoBeOT/3UEhXsEsP3E=
|
github.com/iden3/go-iden3-crypto v0.0.15/go.mod h1:dLpM4vEPJ3nDHzhWFXDjzkn1qHoBeOT/3UEhXsEsP3E=
|
||||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
|
||||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
|
||||||
github.com/jackc/pgx/v5 v5.3.0 h1:/NQi8KHMpKWHInxXesC8yD4DhkXPrVhmnwYkjp9AmBA=
|
|
||||||
github.com/jackc/pgx/v5 v5.3.0/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8=
|
|
||||||
github.com/jackc/puddle/v2 v2.2.0/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
|
||||||
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
|
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
|
||||||
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
||||||
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
@@ -69,16 +58,11 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
|
|||||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
|
|
||||||
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
|
|
||||||
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
|
||||||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
@@ -86,20 +70,15 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=
|
github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=
|
||||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
|
||||||
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
|
|
||||||
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
|
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||||
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
||||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.14 h1:qZgc/Rwetq+MtyE18WhzjokPD93dNqLGNT3QJuLvBGw=
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
|
||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A=
|
github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A=
|
||||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
@@ -113,25 +92,20 @@ github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsK
|
|||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pressly/goose/v3 v3.7.0 h1:jblaZul15uCIEKHRu5KUdA+5wDA7E60JC0TOthdrtf8=
|
|
||||||
github.com/pressly/goose/v3 v3.7.0/go.mod h1:N5gqPdIzdxf3BiPWdmoPreIwHStkxsvKWE5xjUvfYNk=
|
|
||||||
github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA=
|
github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
||||||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/rjeczalik/notify v0.9.1 h1:CLCKso/QK1snAlnhNR/CNvNiFU2saUtjV0bx3EwNeCE=
|
github.com/rjeczalik/notify v0.9.1 h1:CLCKso/QK1snAlnhNR/CNvNiFU2saUtjV0bx3EwNeCE=
|
||||||
github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho=
|
github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho=
|
||||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
|
||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||||
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
|
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20230508165858-27a3830afa61 h1:4fslVpNOPLeJPYX3tivrVWgqNvChPs7/y9OqWvQSNCw=
|
github.com/scroll-tech/go-ethereum v1.10.14-0.20230613025759-f055f50f9d56 h1:Cqj7haxwvzI2O4n9ZZ25helShzFGCy7Z/B+FFSBFHNI=
|
||||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20230508165858-27a3830afa61/go.mod h1:45PZqlQCqV0dU4o4+SE8LoJLEvXkK5j45ligvbih9QY=
|
github.com/scroll-tech/go-ethereum v1.10.14-0.20230613025759-f055f50f9d56/go.mod h1:45PZqlQCqV0dU4o4+SE8LoJLEvXkK5j45ligvbih9QY=
|
||||||
github.com/scroll-tech/zktrie v0.5.3 h1:jjzQchGU6XPL5s1C5bwwivSadefSRuYASE9OL7UKAdE=
|
github.com/scroll-tech/zktrie v0.5.3 h1:jjzQchGU6XPL5s1C5bwwivSadefSRuYASE9OL7UKAdE=
|
||||||
github.com/scroll-tech/zktrie v0.5.3/go.mod h1:XvNo7vAk8yxNyTjBDj5WIiFzYW4bx/gJ78+NK6Zn6Uk=
|
github.com/scroll-tech/zktrie v0.5.3/go.mod h1:XvNo7vAk8yxNyTjBDj5WIiFzYW4bx/gJ78+NK6Zn6Uk=
|
||||||
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
|
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
|
||||||
@@ -144,16 +118,8 @@ github.com/smartystreets/goconvey v1.8.0 h1:Oi49ha/2MURE0WexF052Z0m+BNSGirfjg5RL
|
|||||||
github.com/smartystreets/goconvey v1.8.0/go.mod h1:EdX8jtrTIj26jmjCOVNMVSIYAtgexqXKHOXW2Dx9JLg=
|
github.com/smartystreets/goconvey v1.8.0/go.mod h1:EdX8jtrTIj26jmjCOVNMVSIYAtgexqXKHOXW2Dx9JLg=
|
||||||
github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA=
|
github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA=
|
||||||
github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg=
|
github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
|
||||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
|
||||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
|
||||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
|
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
|
||||||
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
|
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
|
||||||
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
|
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
|
||||||
@@ -161,94 +127,48 @@ github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYm
|
|||||||
github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
|
github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
|
||||||
github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8=
|
github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8=
|
||||||
github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U=
|
github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U=
|
||||||
github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa h1:5SqCsI/2Qya2bCzK15ozrqo2sZxkh0FHynJZOTVoV6Q=
|
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
|
||||||
github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI=
|
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
|
||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
|
||||||
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
||||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
|
||||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||||
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
|
|
||||||
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
|
||||||
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
|
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
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/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|
||||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
|
||||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
|
||||||
golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y=
|
|
||||||
golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
|
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
|
||||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
|
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0=
|
gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gorm.io/driver/postgres v1.5.0 h1:u2FXTy14l45qc3UeCJ7QaAXZmZfDDv0YrthvmRq1l0U=
|
gorm.io/gorm v1.25.2 h1:gs1o6Vsa+oVKG/a9ElL3XgyGfghFfkKA2SInQaCyMho=
|
||||||
gorm.io/driver/postgres v1.5.0/go.mod h1:FUZXzO+5Uqg5zzwzv4KK49R8lvGIyscBOqYrtI1Ce9A=
|
gorm.io/gorm v1.25.2/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||||
gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
|
||||||
gorm.io/gorm v1.25.1 h1:nsSALe5Pr+cM3V1qwwQ7rOkw+6UeLrX5O4v3llhHa64=
|
|
||||||
gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
|
||||||
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
|
|
||||||
modernc.org/cc/v3 v3.36.1 h1:CICrjwr/1M4+6OQ4HJZ/AHxjcwe67r5vPUF518MkO8A=
|
|
||||||
modernc.org/ccgo/v3 v3.16.8 h1:G0QNlTqI5uVgczBWfGKs7B++EPwCfXPWGD2MdeKloDs=
|
|
||||||
modernc.org/libc v1.16.19 h1:S8flPn5ZeXx6iw/8yNa986hwTQDrY8RXU7tObZuAozo=
|
|
||||||
modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
|
|
||||||
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
|
||||||
modernc.org/memory v1.1.1 h1:bDOL0DIDLQv7bWhP3gMvIrnoFw+Eo6F7a2QK9HPDiFU=
|
|
||||||
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
|
||||||
modernc.org/sqlite v1.18.1 h1:ko32eKt3jf7eqIkCgPAeHMBXw3riNSLhl2f3loEF7o8=
|
|
||||||
modernc.org/strutil v1.1.2 h1:iFBDH6j1Z0bN/Q9udJnnFoFpENA4252qe/7/5woE5MI=
|
|
||||||
modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk=
|
|
||||||
|
|||||||
@@ -4,13 +4,15 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"scroll-tech/common/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config load configuration items.
|
// Config load configuration items.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
L1Config *L1Config `json:"l1_config"`
|
L1Config *L1Config `json:"l1_config"`
|
||||||
L2Config *L2Config `json:"l2_config"`
|
L2Config *L2Config `json:"l2_config"`
|
||||||
DBConfig *DBConfig `json:"db_config"`
|
DBConfig *database.Config `json:"db_config"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConfig returns a new instance of Config.
|
// NewConfig returns a new instance of Config.
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DBConfig db config
|
|
||||||
type DBConfig struct {
|
|
||||||
// data source name
|
|
||||||
DSN string `json:"dsn"`
|
|
||||||
DriverName string `json:"driver_name"`
|
|
||||||
|
|
||||||
MaxOpenNum int `json:"maxOpenNum"`
|
|
||||||
MaxIdleNum int `json:"maxIdleNum"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDBConfig returns a new instance of Config.
|
|
||||||
func NewDBConfig(file string) (*DBConfig, error) {
|
|
||||||
buf, err := os.ReadFile(filepath.Clean(file))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg := &DBConfig{}
|
|
||||||
err = json.Unmarshal(buf, cfg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return cfg, nil
|
|
||||||
}
|
|
||||||
@@ -4,8 +4,6 @@ import (
|
|||||||
"github.com/scroll-tech/go-ethereum/rpc"
|
"github.com/scroll-tech/go-ethereum/rpc"
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
|
|
||||||
"scroll-tech/bridge/internal/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// L2Config loads l2geth configuration items.
|
// L2Config loads l2geth configuration items.
|
||||||
@@ -22,30 +20,27 @@ type L2Config struct {
|
|||||||
WithdrawTrieRootSlot common.Hash `json:"withdraw_trie_root_slot,omitempty"`
|
WithdrawTrieRootSlot common.Hash `json:"withdraw_trie_root_slot,omitempty"`
|
||||||
// The relayer config
|
// The relayer config
|
||||||
RelayerConfig *RelayerConfig `json:"relayer_config"`
|
RelayerConfig *RelayerConfig `json:"relayer_config"`
|
||||||
|
// The chunk_proposer config
|
||||||
|
ChunkProposerConfig *ChunkProposerConfig `json:"chunk_proposer_config"`
|
||||||
// The batch_proposer config
|
// The batch_proposer config
|
||||||
BatchProposerConfig *BatchProposerConfig `json:"batch_proposer_config"`
|
BatchProposerConfig *BatchProposerConfig `json:"batch_proposer_config"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// BatchProposerConfig loads l2watcher batch_proposer configuration items.
|
// ChunkProposerConfig loads chunk_proposer configuration items.
|
||||||
type BatchProposerConfig struct {
|
type ChunkProposerConfig struct {
|
||||||
// Proof generation frequency, generating proof every k blocks
|
MaxTxGasPerChunk uint64 `json:"max_tx_gas_per_chunk"`
|
||||||
ProofGenerationFreq uint64 `json:"proof_generation_freq"`
|
MaxL2TxNumPerChunk uint64 `json:"max_l2_tx_num_per_chunk"`
|
||||||
// Txnum threshold in a batch
|
MaxL1CommitGasPerChunk uint64 `json:"max_l1_commit_gas_per_chunk"`
|
||||||
BatchTxNumThreshold uint64 `json:"batch_tx_num_threshold"`
|
MaxL1CommitCalldataSizePerChunk uint64 `json:"max_l1_commit_calldata_size_per_chunk"`
|
||||||
// Gas threshold in a batch
|
MinL1CommitCalldataSizePerChunk uint64 `json:"min_l1_commit_calldata_size_per_chunk"`
|
||||||
BatchGasThreshold uint64 `json:"batch_gas_threshold"`
|
ChunkTimeoutSec uint64 `json:"chunk_timeout_sec"`
|
||||||
// Time waited to generate a batch even if gas_threshold not met
|
}
|
||||||
BatchTimeSec uint64 `json:"batch_time_sec"`
|
|
||||||
// Time waited to commit batches before the calldata met CommitTxCalldataSizeLimit
|
// BatchProposerConfig loads batch_proposer configuration items.
|
||||||
BatchCommitTimeSec uint64 `json:"batch_commit_time_sec"`
|
type BatchProposerConfig struct {
|
||||||
// Max number of blocks in a batch
|
MaxChunkNumPerBatch uint64 `json:"max_chunk_num_per_batch"`
|
||||||
BatchBlocksLimit uint64 `json:"batch_blocks_limit"`
|
MaxL1CommitGasPerBatch uint64 `json:"max_l1_commit_gas_per_batch"`
|
||||||
// Commit tx calldata size limit in bytes, target to cap the gas use of commit tx at 2M gas
|
MaxL1CommitCalldataSizePerBatch uint32 `json:"max_l1_commit_calldata_size_per_batch"`
|
||||||
CommitTxCalldataSizeLimit uint64 `json:"commit_tx_calldata_size_limit"`
|
MinChunkNumPerBatch uint64 `json:"min_chunk_num_per_batch"`
|
||||||
// Commit tx calldata min size limit in bytes
|
BatchTimeoutSec uint64 `json:"batch_timeout_sec"`
|
||||||
CommitTxCalldataMinSize uint64 `json:"commit_tx_calldata_min_size,omitempty"`
|
|
||||||
// Max number of batches in a commit transaction
|
|
||||||
CommitTxBatchCountLimit uint64 `json:"commit_tx_batch_count_limit"`
|
|
||||||
// The public input hash config
|
|
||||||
PublicInputConfig *types.PublicInputHashConfig `json:"public_input_config"`
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ func (r *RelayerConfig) UnmarshalJSON(input []byte) error {
|
|||||||
for _, privStr := range jsonConfig.RollupSenderPrivateKeys {
|
for _, privStr := range jsonConfig.RollupSenderPrivateKeys {
|
||||||
priv, err := crypto.ToECDSA(common.FromHex(privStr))
|
priv, err := crypto.ToECDSA(common.FromHex(privStr))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("incorrect roller_private_key format, err: %v", err)
|
return fmt.Errorf("incorrect prover_private_key format, err: %v", err)
|
||||||
}
|
}
|
||||||
r.RollupSenderPrivateKeys = append(r.RollupSenderPrivateKeys, priv)
|
r.RollupSenderPrivateKeys = append(r.RollupSenderPrivateKeys, priv)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ type Layer1Relayer struct {
|
|||||||
gasPriceDiff uint64
|
gasPriceDiff uint64
|
||||||
|
|
||||||
l1MessageOrm *orm.L1Message
|
l1MessageOrm *orm.L1Message
|
||||||
l1Block *orm.L1Block
|
l1BlockOrm *orm.L1Block
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLayer1Relayer will return a new instance of Layer1RelayerClient
|
// NewLayer1Relayer will return a new instance of Layer1RelayerClient
|
||||||
@@ -90,7 +90,7 @@ func NewLayer1Relayer(ctx context.Context, db *gorm.DB, cfg *config.RelayerConfi
|
|||||||
l1Relayer := &Layer1Relayer{
|
l1Relayer := &Layer1Relayer{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
l1MessageOrm: orm.NewL1Message(db),
|
l1MessageOrm: orm.NewL1Message(db),
|
||||||
l1Block: orm.NewL1Block(db),
|
l1BlockOrm: orm.NewL1Block(db),
|
||||||
|
|
||||||
messageSender: messageSender,
|
messageSender: messageSender,
|
||||||
l2MessengerABI: bridgeAbi.L2ScrollMessengerABI,
|
l2MessengerABI: bridgeAbi.L2ScrollMessengerABI,
|
||||||
@@ -159,13 +159,13 @@ func (r *Layer1Relayer) processSavedEvent(msg *orm.L1Message) error {
|
|||||||
|
|
||||||
// ProcessGasPriceOracle imports gas price to layer2
|
// ProcessGasPriceOracle imports gas price to layer2
|
||||||
func (r *Layer1Relayer) ProcessGasPriceOracle() {
|
func (r *Layer1Relayer) ProcessGasPriceOracle() {
|
||||||
latestBlockHeight, err := r.l1Block.GetLatestL1BlockHeight()
|
latestBlockHeight, err := r.l1BlockOrm.GetLatestL1BlockHeight(r.ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("Failed to fetch latest L1 block height from db", "err", err)
|
log.Warn("Failed to fetch latest L1 block height from db", "err", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
blocks, err := r.l1Block.GetL1Blocks(map[string]interface{}{
|
blocks, err := r.l1BlockOrm.GetL1Blocks(r.ctx, map[string]interface{}{
|
||||||
"number": latestBlockHeight,
|
"number": latestBlockHeight,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -197,7 +197,7 @@ func (r *Layer1Relayer) ProcessGasPriceOracle() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = r.l1Block.UpdateL1GasOracleStatusAndOracleTxHash(r.ctx, block.Hash, types.GasOracleImporting, hash.String())
|
err = r.l1BlockOrm.UpdateL1GasOracleStatusAndOracleTxHash(r.ctx, block.Hash, types.GasOracleImporting, hash.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("UpdateGasOracleStatusAndOracleTxHash failed", "block.Hash", block.Hash, "block.Height", block.Number, "err", err)
|
log.Error("UpdateGasOracleStatusAndOracleTxHash failed", "block.Hash", block.Hash, "block.Height", block.Number, "err", err)
|
||||||
return
|
return
|
||||||
@@ -232,14 +232,14 @@ func (r *Layer1Relayer) handleConfirmLoop(ctx context.Context) {
|
|||||||
case cfm := <-r.gasOracleSender.ConfirmChan():
|
case cfm := <-r.gasOracleSender.ConfirmChan():
|
||||||
if !cfm.IsSuccessful {
|
if !cfm.IsSuccessful {
|
||||||
// @discuss: maybe make it pending again?
|
// @discuss: maybe make it pending again?
|
||||||
err := r.l1Block.UpdateL1GasOracleStatusAndOracleTxHash(r.ctx, cfm.ID, types.GasOracleFailed, cfm.TxHash.String())
|
err := r.l1BlockOrm.UpdateL1GasOracleStatusAndOracleTxHash(r.ctx, cfm.ID, types.GasOracleFailed, cfm.TxHash.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("UpdateL1GasOracleStatusAndOracleTxHash failed", "err", err)
|
log.Warn("UpdateL1GasOracleStatusAndOracleTxHash failed", "err", err)
|
||||||
}
|
}
|
||||||
log.Warn("transaction confirmed but failed in layer2", "confirmation", cfm)
|
log.Warn("transaction confirmed but failed in layer2", "confirmation", cfm)
|
||||||
} else {
|
} else {
|
||||||
// @todo handle db error
|
// @todo handle db error
|
||||||
err := r.l1Block.UpdateL1GasOracleStatusAndOracleTxHash(r.ctx, cfm.ID, types.GasOracleImported, cfm.TxHash.String())
|
err := r.l1BlockOrm.UpdateL1GasOracleStatusAndOracleTxHash(r.ctx, cfm.ID, types.GasOracleImported, cfm.TxHash.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("UpdateGasOracleStatusAndOracleTxHash failed", "err", err)
|
log.Warn("UpdateGasOracleStatusAndOracleTxHash failed", "err", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,13 +12,14 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
|
"scroll-tech/common/database"
|
||||||
"scroll-tech/common/types"
|
"scroll-tech/common/types"
|
||||||
"scroll-tech/common/utils"
|
"scroll-tech/common/utils"
|
||||||
|
|
||||||
|
"scroll-tech/database/migrate"
|
||||||
|
|
||||||
"scroll-tech/bridge/internal/controller/sender"
|
"scroll-tech/bridge/internal/controller/sender"
|
||||||
"scroll-tech/bridge/internal/orm"
|
"scroll-tech/bridge/internal/orm"
|
||||||
"scroll-tech/bridge/internal/orm/migrate"
|
|
||||||
bridgeUtils "scroll-tech/bridge/internal/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -49,7 +50,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func setupL1RelayerDB(t *testing.T) *gorm.DB {
|
func setupL1RelayerDB(t *testing.T) *gorm.DB {
|
||||||
db, err := bridgeUtils.InitDB(cfg.DBConfig)
|
db, err := database.InitDB(cfg.DBConfig)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
sqlDB, err := db.DB()
|
sqlDB, err := db.DB()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@@ -60,7 +61,7 @@ func setupL1RelayerDB(t *testing.T) *gorm.DB {
|
|||||||
// testCreateNewRelayer test create new relayer instance and stop
|
// testCreateNewRelayer test create new relayer instance and stop
|
||||||
func testCreateNewL1Relayer(t *testing.T) {
|
func testCreateNewL1Relayer(t *testing.T) {
|
||||||
db := setupL1RelayerDB(t)
|
db := setupL1RelayerDB(t)
|
||||||
defer bridgeUtils.CloseDB(db)
|
defer database.CloseDB(db)
|
||||||
relayer, err := NewLayer1Relayer(context.Background(), db, cfg.L2Config.RelayerConfig)
|
relayer, err := NewLayer1Relayer(context.Background(), db, cfg.L2Config.RelayerConfig)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotNil(t, relayer)
|
assert.NotNil(t, relayer)
|
||||||
@@ -68,7 +69,7 @@ func testCreateNewL1Relayer(t *testing.T) {
|
|||||||
|
|
||||||
func testL1RelayerProcessSaveEvents(t *testing.T) {
|
func testL1RelayerProcessSaveEvents(t *testing.T) {
|
||||||
db := setupL1RelayerDB(t)
|
db := setupL1RelayerDB(t)
|
||||||
defer bridgeUtils.CloseDB(db)
|
defer database.CloseDB(db)
|
||||||
l1MessageOrm := orm.NewL1Message(db)
|
l1MessageOrm := orm.NewL1Message(db)
|
||||||
l1Cfg := cfg.L1Config
|
l1Cfg := cfg.L1Config
|
||||||
relayer, err := NewLayer1Relayer(context.Background(), db, l1Cfg.RelayerConfig)
|
relayer, err := NewLayer1Relayer(context.Background(), db, l1Cfg.RelayerConfig)
|
||||||
@@ -86,7 +87,7 @@ func testL1RelayerProcessSaveEvents(t *testing.T) {
|
|||||||
|
|
||||||
func testL1RelayerMsgConfirm(t *testing.T) {
|
func testL1RelayerMsgConfirm(t *testing.T) {
|
||||||
db := setupL1RelayerDB(t)
|
db := setupL1RelayerDB(t)
|
||||||
defer bridgeUtils.CloseDB(db)
|
defer database.CloseDB(db)
|
||||||
l1MessageOrm := orm.NewL1Message(db)
|
l1MessageOrm := orm.NewL1Message(db)
|
||||||
l1Messages := []*orm.L1Message{
|
l1Messages := []*orm.L1Message{
|
||||||
{MsgHash: "msg-1", QueueIndex: 0},
|
{MsgHash: "msg-1", QueueIndex: 0},
|
||||||
@@ -123,12 +124,12 @@ func testL1RelayerMsgConfirm(t *testing.T) {
|
|||||||
|
|
||||||
func testL1RelayerGasOracleConfirm(t *testing.T) {
|
func testL1RelayerGasOracleConfirm(t *testing.T) {
|
||||||
db := setupL1RelayerDB(t)
|
db := setupL1RelayerDB(t)
|
||||||
defer bridgeUtils.CloseDB(db)
|
defer database.CloseDB(db)
|
||||||
l1BlockOrm := orm.NewL1Block(db)
|
l1BlockOrm := orm.NewL1Block(db)
|
||||||
|
|
||||||
l1Block := []orm.L1Block{
|
l1Block := []orm.L1Block{
|
||||||
{Hash: "gas-oracle-1", Number: 0},
|
{Hash: "gas-oracle-1", Number: 0, GasOracleStatus: int16(types.GasOraclePending)},
|
||||||
{Hash: "gas-oracle-2", Number: 1},
|
{Hash: "gas-oracle-2", Number: 1, GasOracleStatus: int16(types.GasOraclePending)},
|
||||||
}
|
}
|
||||||
// Insert test data.
|
// Insert test data.
|
||||||
assert.NoError(t, l1BlockOrm.InsertL1Blocks(context.Background(), l1Block))
|
assert.NoError(t, l1BlockOrm.InsertL1Blocks(context.Background(), l1Block))
|
||||||
@@ -152,8 +153,8 @@ func testL1RelayerGasOracleConfirm(t *testing.T) {
|
|||||||
|
|
||||||
// Check the database for the updated status using TryTimes.
|
// Check the database for the updated status using TryTimes.
|
||||||
ok := utils.TryTimes(5, func() bool {
|
ok := utils.TryTimes(5, func() bool {
|
||||||
msg1, err1 := l1BlockOrm.GetL1Blocks(map[string]interface{}{"hash": "gas-oracle-1"})
|
msg1, err1 := l1BlockOrm.GetL1Blocks(ctx, map[string]interface{}{"hash": "gas-oracle-1"})
|
||||||
msg2, err2 := l1BlockOrm.GetL1Blocks(map[string]interface{}{"hash": "gas-oracle-2"})
|
msg2, err2 := l1BlockOrm.GetL1Blocks(ctx, map[string]interface{}{"hash": "gas-oracle-2"})
|
||||||
return err1 == nil && len(msg1) == 1 && types.GasOracleStatus(msg1[0].GasOracleStatus) == types.GasOracleImported &&
|
return err1 == nil && len(msg1) == 1 && types.GasOracleStatus(msg1[0].GasOracleStatus) == types.GasOracleImported &&
|
||||||
err2 == nil && len(msg2) == 1 && types.GasOracleStatus(msg2[0].GasOracleStatus) == types.GasOracleFailed
|
err2 == nil && len(msg2) == 1 && types.GasOracleStatus(msg2[0].GasOracleStatus) == types.GasOracleFailed
|
||||||
})
|
})
|
||||||
@@ -162,7 +163,7 @@ func testL1RelayerGasOracleConfirm(t *testing.T) {
|
|||||||
|
|
||||||
func testL1RelayerProcessGasPriceOracle(t *testing.T) {
|
func testL1RelayerProcessGasPriceOracle(t *testing.T) {
|
||||||
db := setupL1RelayerDB(t)
|
db := setupL1RelayerDB(t)
|
||||||
defer bridgeUtils.CloseDB(db)
|
defer database.CloseDB(db)
|
||||||
|
|
||||||
l1Cfg := cfg.L1Config
|
l1Cfg := cfg.L1Config
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
@@ -174,28 +175,28 @@ func testL1RelayerProcessGasPriceOracle(t *testing.T) {
|
|||||||
var l1BlockOrm *orm.L1Block
|
var l1BlockOrm *orm.L1Block
|
||||||
convey.Convey("GetLatestL1BlockHeight failure", t, func() {
|
convey.Convey("GetLatestL1BlockHeight failure", t, func() {
|
||||||
targetErr := errors.New("GetLatestL1BlockHeight error")
|
targetErr := errors.New("GetLatestL1BlockHeight error")
|
||||||
patchGuard := gomonkey.ApplyMethodFunc(l1BlockOrm, "GetLatestL1BlockHeight", func() (uint64, error) {
|
patchGuard := gomonkey.ApplyMethodFunc(l1BlockOrm, "GetLatestL1BlockHeight", func(ctx context.Context) (uint64, error) {
|
||||||
return 0, targetErr
|
return 0, targetErr
|
||||||
})
|
})
|
||||||
defer patchGuard.Reset()
|
defer patchGuard.Reset()
|
||||||
l1Relayer.ProcessGasPriceOracle()
|
l1Relayer.ProcessGasPriceOracle()
|
||||||
})
|
})
|
||||||
|
|
||||||
patchGuard := gomonkey.ApplyMethodFunc(l1BlockOrm, "GetLatestL1BlockHeight", func() (uint64, error) {
|
patchGuard := gomonkey.ApplyMethodFunc(l1BlockOrm, "GetLatestL1BlockHeight", func(ctx context.Context) (uint64, error) {
|
||||||
return 100, nil
|
return 100, nil
|
||||||
})
|
})
|
||||||
defer patchGuard.Reset()
|
defer patchGuard.Reset()
|
||||||
|
|
||||||
convey.Convey("GetL1Blocks failure", t, func() {
|
convey.Convey("GetL1Blocks failure", t, func() {
|
||||||
targetErr := errors.New("GetL1Blocks error")
|
targetErr := errors.New("GetL1Blocks error")
|
||||||
patchGuard.ApplyMethodFunc(l1BlockOrm, "GetL1Blocks", func(fields map[string]interface{}) ([]orm.L1Block, error) {
|
patchGuard.ApplyMethodFunc(l1BlockOrm, "GetL1Blocks", func(ctx context.Context, fields map[string]interface{}) ([]orm.L1Block, error) {
|
||||||
return nil, targetErr
|
return nil, targetErr
|
||||||
})
|
})
|
||||||
l1Relayer.ProcessGasPriceOracle()
|
l1Relayer.ProcessGasPriceOracle()
|
||||||
})
|
})
|
||||||
|
|
||||||
convey.Convey("Block not exist", t, func() {
|
convey.Convey("Block not exist", t, func() {
|
||||||
patchGuard.ApplyMethodFunc(l1BlockOrm, "GetL1Blocks", func(fields map[string]interface{}) ([]orm.L1Block, error) {
|
patchGuard.ApplyMethodFunc(l1BlockOrm, "GetL1Blocks", func(ctx context.Context, fields map[string]interface{}) ([]orm.L1Block, error) {
|
||||||
tmpInfo := []orm.L1Block{
|
tmpInfo := []orm.L1Block{
|
||||||
{Hash: "gas-oracle-1", Number: 0},
|
{Hash: "gas-oracle-1", Number: 0},
|
||||||
{Hash: "gas-oracle-2", Number: 1},
|
{Hash: "gas-oracle-2", Number: 1},
|
||||||
@@ -205,12 +206,12 @@ func testL1RelayerProcessGasPriceOracle(t *testing.T) {
|
|||||||
l1Relayer.ProcessGasPriceOracle()
|
l1Relayer.ProcessGasPriceOracle()
|
||||||
})
|
})
|
||||||
|
|
||||||
patchGuard.ApplyMethodFunc(l1BlockOrm, "GetL1Blocks", func(fields map[string]interface{}) ([]orm.L1Block, error) {
|
patchGuard.ApplyMethodFunc(l1BlockOrm, "GetL1Blocks", func(ctx context.Context, fields map[string]interface{}) ([]orm.L1Block, error) {
|
||||||
tmpInfo := []orm.L1Block{
|
tmpInfo := []orm.L1Block{
|
||||||
{
|
{
|
||||||
Hash: "gas-oracle-1",
|
Hash: "gas-oracle-1",
|
||||||
Number: 0,
|
Number: 0,
|
||||||
GasOracleStatus: int(types.GasOraclePending),
|
GasOracleStatus: int16(types.GasOraclePending),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return tmpInfo, nil
|
return tmpInfo, nil
|
||||||
|
|||||||
@@ -3,19 +3,17 @@ package relayer
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"runtime"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/accounts/abi"
|
"github.com/scroll-tech/go-ethereum/accounts/abi"
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
"github.com/scroll-tech/go-ethereum/crypto"
|
|
||||||
"github.com/scroll-tech/go-ethereum/ethclient"
|
"github.com/scroll-tech/go-ethereum/ethclient"
|
||||||
"github.com/scroll-tech/go-ethereum/log"
|
"github.com/scroll-tech/go-ethereum/log"
|
||||||
gethMetrics "github.com/scroll-tech/go-ethereum/metrics"
|
gethMetrics "github.com/scroll-tech/go-ethereum/metrics"
|
||||||
"golang.org/x/sync/errgroup"
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"modernc.org/mathutil"
|
|
||||||
|
|
||||||
"scroll-tech/common/metrics"
|
"scroll-tech/common/metrics"
|
||||||
"scroll-tech/common/types"
|
"scroll-tech/common/types"
|
||||||
@@ -24,18 +22,13 @@ import (
|
|||||||
"scroll-tech/bridge/internal/config"
|
"scroll-tech/bridge/internal/config"
|
||||||
"scroll-tech/bridge/internal/controller/sender"
|
"scroll-tech/bridge/internal/controller/sender"
|
||||||
"scroll-tech/bridge/internal/orm"
|
"scroll-tech/bridge/internal/orm"
|
||||||
bridgeTypes "scroll-tech/bridge/internal/types"
|
|
||||||
"scroll-tech/bridge/internal/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
bridgeL2MsgsRelayedTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/msgs/relayed/total", metrics.ScrollRegistry)
|
|
||||||
bridgeL2BatchesFinalizedTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/batches/finalized/total", metrics.ScrollRegistry)
|
bridgeL2BatchesFinalizedTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/batches/finalized/total", metrics.ScrollRegistry)
|
||||||
bridgeL2BatchesCommittedTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/batches/committed/total", metrics.ScrollRegistry)
|
bridgeL2BatchesCommittedTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/batches/committed/total", metrics.ScrollRegistry)
|
||||||
bridgeL2MsgsRelayedConfirmedTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/msgs/relayed/confirmed/total", metrics.ScrollRegistry)
|
|
||||||
bridgeL2BatchesFinalizedConfirmedTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/batches/finalized/confirmed/total", metrics.ScrollRegistry)
|
bridgeL2BatchesFinalizedConfirmedTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/batches/finalized/confirmed/total", metrics.ScrollRegistry)
|
||||||
bridgeL2BatchesCommittedConfirmedTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/batches/committed/confirmed/total", metrics.ScrollRegistry)
|
bridgeL2BatchesCommittedConfirmedTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/batches/committed/confirmed/total", metrics.ScrollRegistry)
|
||||||
bridgeL2BatchesSkippedTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/batches/skipped/total", metrics.ScrollRegistry)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Layer2Relayer is responsible for
|
// Layer2Relayer is responsible for
|
||||||
@@ -49,9 +42,10 @@ type Layer2Relayer struct {
|
|||||||
|
|
||||||
l2Client *ethclient.Client
|
l2Client *ethclient.Client
|
||||||
|
|
||||||
blockBatchOrm *orm.BlockBatch
|
db *gorm.DB
|
||||||
blockTraceOrm *orm.BlockTrace
|
batchOrm *orm.Batch
|
||||||
l2MessageOrm *orm.L2Message
|
chunkOrm *orm.Chunk
|
||||||
|
l2BlockOrm *orm.L2Block
|
||||||
|
|
||||||
cfg *config.RelayerConfig
|
cfg *config.RelayerConfig
|
||||||
|
|
||||||
@@ -75,8 +69,8 @@ type Layer2Relayer struct {
|
|||||||
processingMessage sync.Map
|
processingMessage sync.Map
|
||||||
|
|
||||||
// A list of processing batches commitment.
|
// A list of processing batches commitment.
|
||||||
// key(string): confirmation ID, value([]string): batch hashes.
|
// key(string): confirmation ID, value(string): batch hash.
|
||||||
processingBatchesCommitment sync.Map
|
processingCommitment sync.Map
|
||||||
|
|
||||||
// A list of processing batch finalization.
|
// A list of processing batch finalization.
|
||||||
// key(string): confirmation ID, value(string): batch hash.
|
// key(string): confirmation ID, value(string): batch hash.
|
||||||
@@ -84,7 +78,7 @@ type Layer2Relayer struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewLayer2Relayer will return a new instance of Layer2RelayerClient
|
// NewLayer2Relayer will return a new instance of Layer2RelayerClient
|
||||||
func NewLayer2Relayer(ctx context.Context, l2Client *ethclient.Client, db *gorm.DB, cfg *config.RelayerConfig) (*Layer2Relayer, error) {
|
func NewLayer2Relayer(ctx context.Context, l2Client *ethclient.Client, db *gorm.DB, cfg *config.RelayerConfig, initGenesis bool) (*Layer2Relayer, error) {
|
||||||
// @todo use different sender for relayer, block commit and proof finalize
|
// @todo use different sender for relayer, block commit and proof finalize
|
||||||
messageSender, err := sender.NewSender(ctx, cfg.SenderConfig, cfg.MessageSenderPrivateKeys)
|
messageSender, err := sender.NewSender(ctx, cfg.SenderConfig, cfg.MessageSenderPrivateKeys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -121,10 +115,11 @@ func NewLayer2Relayer(ctx context.Context, l2Client *ethclient.Client, db *gorm.
|
|||||||
|
|
||||||
layer2Relayer := &Layer2Relayer{
|
layer2Relayer := &Layer2Relayer{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
|
db: db,
|
||||||
|
|
||||||
blockBatchOrm: orm.NewBlockBatch(db),
|
batchOrm: orm.NewBatch(db),
|
||||||
l2MessageOrm: orm.NewL2Message(db),
|
l2BlockOrm: orm.NewL2Block(db),
|
||||||
blockTraceOrm: orm.NewBlockTrace(db),
|
chunkOrm: orm.NewChunk(db),
|
||||||
|
|
||||||
l2Client: l2Client,
|
l2Client: l2Client,
|
||||||
|
|
||||||
@@ -142,134 +137,135 @@ func NewLayer2Relayer(ctx context.Context, l2Client *ethclient.Client, db *gorm.
|
|||||||
minGasPrice: minGasPrice,
|
minGasPrice: minGasPrice,
|
||||||
gasPriceDiff: gasPriceDiff,
|
gasPriceDiff: gasPriceDiff,
|
||||||
|
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
processingMessage: sync.Map{},
|
processingMessage: sync.Map{},
|
||||||
processingBatchesCommitment: sync.Map{},
|
processingCommitment: sync.Map{},
|
||||||
processingFinalization: sync.Map{},
|
processingFinalization: sync.Map{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize genesis before we do anything else
|
||||||
|
if initGenesis {
|
||||||
|
if err := layer2Relayer.initializeGenesis(); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to initialize and commit genesis batch, err: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
go layer2Relayer.handleConfirmLoop(ctx)
|
go layer2Relayer.handleConfirmLoop(ctx)
|
||||||
return layer2Relayer, nil
|
return layer2Relayer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const processMsgLimit = 100
|
func (r *Layer2Relayer) initializeGenesis() error {
|
||||||
|
if count, err := r.batchOrm.GetBatchCount(r.ctx); err != nil {
|
||||||
// ProcessSavedEvents relays saved un-processed cross-domain transactions to desired blockchain
|
return fmt.Errorf("failed to get batch count: %v", err)
|
||||||
func (r *Layer2Relayer) ProcessSavedEvents() {
|
} else if count > 0 {
|
||||||
batch, err := r.blockBatchOrm.GetLatestBatchByRollupStatus([]types.RollupStatus{types.RollupFinalized})
|
log.Info("genesis already imported", "batch count", count)
|
||||||
if err != nil {
|
|
||||||
log.Error("GetLatestFinalizedBatch failed", "err", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// msgs are sorted by nonce in increasing order
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"status": int(types.MsgPending),
|
|
||||||
"height <= (?)": batch.EndBlockNumber,
|
|
||||||
}
|
|
||||||
orderByList := []string{
|
|
||||||
"nonce ASC",
|
|
||||||
}
|
|
||||||
limit := processMsgLimit
|
|
||||||
|
|
||||||
msgs, err := r.l2MessageOrm.GetL2Messages(fields, orderByList, limit)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Failed to fetch unprocessed L2 messages", "err", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// process messages in batches
|
|
||||||
batchSize := mathutil.Min((runtime.GOMAXPROCS(0)+1)/2, r.messageSender.NumberOfAccounts())
|
|
||||||
for size := 0; len(msgs) > 0; msgs = msgs[size:] {
|
|
||||||
if size = len(msgs); size > batchSize {
|
|
||||||
size = batchSize
|
|
||||||
}
|
|
||||||
var g errgroup.Group
|
|
||||||
for _, msg := range msgs[:size] {
|
|
||||||
msg := msg
|
|
||||||
g.Go(func() error {
|
|
||||||
return r.processSavedEvent(&msg)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if err := g.Wait(); err != nil {
|
|
||||||
if !errors.Is(err, sender.ErrNoAvailableAccount) && !errors.Is(err, sender.ErrFullPending) {
|
|
||||||
log.Error("failed to process l2 saved event", "err", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Layer2Relayer) processSavedEvent(msg *orm.L2Message) error {
|
|
||||||
// @todo fetch merkle proof from l2geth
|
|
||||||
log.Info("Processing L2 Message", "msg.nonce", msg.Nonce, "msg.height", msg.Height)
|
|
||||||
|
|
||||||
// Get the block info that contains the message
|
|
||||||
blockInfos, err := r.blockTraceOrm.GetL2BlockInfos(map[string]interface{}{"number": msg.Height}, nil, 0)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Failed to GetL2BlockInfos from DB", "number", msg.Height)
|
|
||||||
}
|
|
||||||
if len(blockInfos) == 0 {
|
|
||||||
return errors.New("get block trace len is 0, exit")
|
|
||||||
}
|
|
||||||
|
|
||||||
blockInfo := blockInfos[0]
|
|
||||||
if blockInfo.BatchHash == "" {
|
|
||||||
log.Error("Block has not been batched yet", "number", blockInfo.Number, "msg.nonce", msg.Nonce)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: rebuild the withdraw trie to generate the merkle proof
|
genesis, err := r.l2Client.HeaderByNumber(r.ctx, big.NewInt(0))
|
||||||
proof := bridgeAbi.IL1ScrollMessengerL2MessageProof{
|
|
||||||
BatchHash: common.HexToHash(blockInfo.BatchHash),
|
|
||||||
MerkleProof: make([]byte, 0),
|
|
||||||
}
|
|
||||||
from := common.HexToAddress(msg.Sender)
|
|
||||||
target := common.HexToAddress(msg.Target)
|
|
||||||
value, ok := big.NewInt(0).SetString(msg.Value, 10)
|
|
||||||
if !ok {
|
|
||||||
// @todo maybe panic?
|
|
||||||
log.Error("Failed to parse message value", "msg.nonce", msg.Nonce, "msg.height", msg.Height)
|
|
||||||
// TODO: need to skip this message by changing its status to MsgError
|
|
||||||
}
|
|
||||||
msgNonce := big.NewInt(int64(msg.Nonce))
|
|
||||||
calldata := common.Hex2Bytes(msg.Calldata)
|
|
||||||
data, err := r.l1MessengerABI.Pack("relayMessageWithProof", from, target, value, msgNonce, calldata, proof)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Failed to pack relayMessageWithProof", "msg.nonce", msg.Nonce, "err", err)
|
return fmt.Errorf("failed to retrieve L2 genesis header: %v", err)
|
||||||
// TODO: need to skip this message by changing its status to MsgError
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hash, err := r.messageSender.SendTransaction(msg.MsgHash, &r.cfg.MessengerContractAddress, big.NewInt(0), data, r.minGasLimitForMessageRelay)
|
log.Info("retrieved L2 genesis header", "hash", genesis.Hash().String())
|
||||||
if err != nil && errors.Is(err, ErrExecutionRevertedMessageExpired) {
|
|
||||||
return r.l2MessageOrm.UpdateLayer2Status(r.ctx, msg.MsgHash, types.MsgExpired)
|
chunk := &types.Chunk{
|
||||||
|
Blocks: []*types.WrappedBlock{{
|
||||||
|
Header: genesis,
|
||||||
|
Transactions: nil,
|
||||||
|
WithdrawRoot: common.Hash{},
|
||||||
|
}},
|
||||||
}
|
}
|
||||||
if err != nil && errors.Is(err, ErrExecutionRevertedAlreadySuccessExecuted) {
|
|
||||||
return r.l2MessageOrm.UpdateLayer2Status(r.ctx, msg.MsgHash, types.MsgConfirmed)
|
err = r.db.Transaction(func(dbTX *gorm.DB) error {
|
||||||
}
|
var dbChunk *orm.Chunk
|
||||||
if err != nil {
|
dbChunk, err = r.chunkOrm.InsertChunk(r.ctx, chunk, dbTX)
|
||||||
if !errors.Is(err, sender.ErrNoAvailableAccount) && !errors.Is(err, sender.ErrFullPending) {
|
if err != nil {
|
||||||
log.Error("Failed to send relayMessageWithProof tx to layer1 ", "msg.height", msg.Height, "msg.MsgHash", msg.MsgHash, "err", err)
|
return fmt.Errorf("failed to insert chunk: %v", err)
|
||||||
}
|
}
|
||||||
return err
|
|
||||||
}
|
|
||||||
bridgeL2MsgsRelayedTotalCounter.Inc(1)
|
|
||||||
log.Info("relayMessageWithProof to layer1", "msgHash", msg.MsgHash, "txhash", hash.String())
|
|
||||||
|
|
||||||
// save status in db
|
if err = r.chunkOrm.UpdateProvingStatus(r.ctx, dbChunk.Hash, types.ProvingTaskVerified, dbTX); err != nil {
|
||||||
// @todo handle db error
|
return fmt.Errorf("failed to update genesis chunk proving status: %v", err)
|
||||||
err = r.l2MessageOrm.UpdateLayer2StatusAndLayer1Hash(r.ctx, msg.MsgHash, types.MsgSubmitted, hash.String())
|
}
|
||||||
|
|
||||||
|
var batch *orm.Batch
|
||||||
|
batch, err = r.batchOrm.InsertBatch(r.ctx, 0, 0, dbChunk.Hash, dbChunk.Hash, []*types.Chunk{chunk}, dbTX)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to insert batch: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = r.chunkOrm.UpdateBatchHashInRange(r.ctx, 0, 0, batch.Hash, dbTX); err != nil {
|
||||||
|
return fmt.Errorf("failed to update batch hash for chunks: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = r.batchOrm.UpdateProvingStatus(r.ctx, batch.Hash, types.ProvingTaskVerified, dbTX); err != nil {
|
||||||
|
return fmt.Errorf("failed to update genesis batch proving status: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = r.batchOrm.UpdateRollupStatus(r.ctx, batch.Hash, types.RollupFinalized, dbTX); err != nil {
|
||||||
|
return fmt.Errorf("failed to update genesis batch rollup status: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// commit genesis batch on L1
|
||||||
|
// note: we do this inside the DB transaction so that we can revert all DB changes if this step fails
|
||||||
|
return r.commitGenesisBatch(batch.Hash, batch.BatchHeader, common.HexToHash(batch.StateRoot))
|
||||||
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("UpdateLayer2StatusAndLayer1Hash failed", "msgHash", msg.MsgHash, "err", err)
|
return fmt.Errorf("update genesis transaction failed: %v", err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
r.processingMessage.Store(msg.MsgHash, msg.MsgHash)
|
|
||||||
|
log.Info("successfully imported genesis chunk and batch")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Layer2Relayer) commitGenesisBatch(batchHash string, batchHeader []byte, stateRoot common.Hash) error {
|
||||||
|
// encode "importGenesisBatch" transaction calldata
|
||||||
|
calldata, err := r.l1RollupABI.Pack("importGenesisBatch", batchHeader, stateRoot)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to pack importGenesisBatch with batch header: %v and state root: %v. error: %v", common.Bytes2Hex(batchHeader), stateRoot, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// submit genesis batch to L1 rollup contract
|
||||||
|
txHash, err := r.rollupSender.SendTransaction(batchHash, &r.cfg.RollupContractAddress, big.NewInt(0), calldata, 0)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to send import genesis batch tx to L1, error: %v", err)
|
||||||
|
}
|
||||||
|
log.Info("importGenesisBatch transaction sent", "contract", r.cfg.RollupContractAddress, "txHash", txHash.String(), "batchHash", batchHash)
|
||||||
|
|
||||||
|
// wait for confirmation
|
||||||
|
// we assume that no other transactions are sent before initializeGenesis completes
|
||||||
|
ticker := time.NewTicker(10 * time.Second)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
// print progress
|
||||||
|
case <-ticker.C:
|
||||||
|
log.Info("Waiting for confirmation", "pending count", r.rollupSender.PendingCount())
|
||||||
|
|
||||||
|
// timeout
|
||||||
|
case <-time.After(5 * time.Minute):
|
||||||
|
return fmt.Errorf("import genesis timeout after 5 minutes, original txHash: %v", txHash.String())
|
||||||
|
|
||||||
|
// handle confirmation
|
||||||
|
case confirmation := <-r.rollupSender.ConfirmChan():
|
||||||
|
if confirmation.ID != batchHash {
|
||||||
|
return fmt.Errorf("unexpected import genesis confirmation id, expected: %v, got: %v", batchHash, confirmation.ID)
|
||||||
|
}
|
||||||
|
if !confirmation.IsSuccessful {
|
||||||
|
return fmt.Errorf("import genesis batch tx failed")
|
||||||
|
}
|
||||||
|
log.Info("Successfully committed genesis batch on L1", "txHash", confirmation.TxHash.String())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ProcessGasPriceOracle imports gas price to layer1
|
// ProcessGasPriceOracle imports gas price to layer1
|
||||||
func (r *Layer2Relayer) ProcessGasPriceOracle() {
|
func (r *Layer2Relayer) ProcessGasPriceOracle() {
|
||||||
batch, err := r.blockBatchOrm.GetLatestBatch()
|
batch, err := r.batchOrm.GetLatestBatch(r.ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Failed to GetLatestBatch", "err", err)
|
log.Error("Failed to GetLatestBatch", "err", err)
|
||||||
return
|
return
|
||||||
@@ -300,7 +296,7 @@ func (r *Layer2Relayer) ProcessGasPriceOracle() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = r.blockBatchOrm.UpdateL2GasOracleStatusAndOracleTxHash(r.ctx, batch.Hash, types.GasOracleImporting, hash.String())
|
err = r.batchOrm.UpdateL2GasOracleStatusAndOracleTxHash(r.ctx, batch.Hash, types.GasOracleImporting, hash.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("UpdateGasOracleStatusAndOracleTxHash failed", "batch.Hash", batch.Hash, "err", err)
|
log.Error("UpdateGasOracleStatusAndOracleTxHash failed", "batch.Hash", batch.Hash, "err", err)
|
||||||
return
|
return
|
||||||
@@ -311,153 +307,146 @@ func (r *Layer2Relayer) ProcessGasPriceOracle() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendCommitTx sends commitBatches tx to L1.
|
// ProcessPendingBatches processes the pending batches by sending commitBatch transactions to layer 1.
|
||||||
func (r *Layer2Relayer) SendCommitTx(batchData []*bridgeTypes.BatchData) error {
|
func (r *Layer2Relayer) ProcessPendingBatches() {
|
||||||
if len(batchData) == 0 {
|
// get pending batches from database in ascending order by their index.
|
||||||
log.Error("SendCommitTx receives empty batch")
|
pendingBatches, err := r.batchOrm.GetPendingBatches(r.ctx, 10)
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// pack calldata
|
|
||||||
commitBatches := make([]bridgeAbi.IScrollChainBatch, len(batchData))
|
|
||||||
for i, batch := range batchData {
|
|
||||||
commitBatches[i] = batch.Batch
|
|
||||||
}
|
|
||||||
calldata, err := r.l1RollupABI.Pack("commitBatches", commitBatches)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Failed to pack commitBatches",
|
log.Error("Failed to fetch pending L2 batches", "err", err)
|
||||||
"error", err,
|
return
|
||||||
"start_batch_index", commitBatches[0].BatchIndex,
|
|
||||||
"end_batch_index", commitBatches[len(commitBatches)-1].BatchIndex)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
for _, batch := range pendingBatches {
|
||||||
// generate a unique txID and send transaction
|
// get current header and parent header.
|
||||||
var bytes []byte
|
currentBatchHeader, err := types.DecodeBatchHeader(batch.BatchHeader)
|
||||||
for _, batch := range batchData {
|
|
||||||
bytes = append(bytes, batch.Hash().Bytes()...)
|
|
||||||
}
|
|
||||||
txID := crypto.Keccak256Hash(bytes).String()
|
|
||||||
txHash, err := r.rollupSender.SendTransaction(txID, &r.cfg.RollupContractAddress, big.NewInt(0), calldata, 0)
|
|
||||||
if err != nil {
|
|
||||||
if !errors.Is(err, sender.ErrNoAvailableAccount) && !errors.Is(err, sender.ErrFullPending) {
|
|
||||||
log.Error("Failed to send commitBatches tx to layer1 ", "err", err)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
bridgeL2BatchesCommittedTotalCounter.Inc(int64(len(commitBatches)))
|
|
||||||
log.Info("Sent the commitBatches tx to layer1",
|
|
||||||
"tx_hash", txHash.Hex(),
|
|
||||||
"start_batch_index", commitBatches[0].BatchIndex,
|
|
||||||
"end_batch_index", commitBatches[len(commitBatches)-1].BatchIndex)
|
|
||||||
|
|
||||||
// record and sync with db, @todo handle db error
|
|
||||||
batchHashes := make([]string, len(batchData))
|
|
||||||
for i, batch := range batchData {
|
|
||||||
batchHashes[i] = batch.Hash().Hex()
|
|
||||||
err = r.blockBatchOrm.UpdateCommitTxHashAndRollupStatus(r.ctx, batchHashes[i], txHash.String(), types.RollupCommitting)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("UpdateCommitTxHashAndRollupStatus failed", "hash", batchHashes[i], "index", batch.Batch.BatchIndex, "err", err)
|
log.Error("Failed to decode batch header", "index", batch.Index, "error", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
parentBatch := &orm.Batch{}
|
||||||
|
if batch.Index > 0 {
|
||||||
|
parentBatch, err = r.batchOrm.GetBatchByIndex(r.ctx, batch.Index-1)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to get parent batch header", "index", batch.Index-1, "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the chunks for the batch
|
||||||
|
startChunkIndex := batch.StartChunkIndex
|
||||||
|
endChunkIndex := batch.EndChunkIndex
|
||||||
|
dbChunks, err := r.chunkOrm.GetChunksInRange(r.ctx, startChunkIndex, endChunkIndex)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to fetch chunks",
|
||||||
|
"start index", startChunkIndex,
|
||||||
|
"end index", endChunkIndex, "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
encodedChunks := make([][]byte, len(dbChunks))
|
||||||
|
for i, c := range dbChunks {
|
||||||
|
var wrappedBlocks []*types.WrappedBlock
|
||||||
|
wrappedBlocks, err = r.l2BlockOrm.GetL2BlocksInRange(r.ctx, c.StartBlockNumber, c.EndBlockNumber)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to fetch wrapped blocks",
|
||||||
|
"start number", c.StartBlockNumber,
|
||||||
|
"end number", c.EndBlockNumber, "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
chunk := &types.Chunk{
|
||||||
|
Blocks: wrappedBlocks,
|
||||||
|
}
|
||||||
|
var chunkBytes []byte
|
||||||
|
chunkBytes, err = chunk.Encode(c.TotalL1MessagesPoppedBefore)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to encode chunk", "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
encodedChunks[i] = chunkBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
calldata, err := r.l1RollupABI.Pack("commitBatch", currentBatchHeader.Version(), parentBatch.BatchHeader, encodedChunks, currentBatchHeader.SkippedL1MessageBitmap())
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to pack commitBatch", "index", batch.Index, "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// send transaction
|
||||||
|
txID := batch.Hash + "-commit"
|
||||||
|
txHash, err := r.rollupSender.SendTransaction(txID, &r.cfg.RollupContractAddress, big.NewInt(0), calldata, 0)
|
||||||
|
if err != nil {
|
||||||
|
if !errors.Is(err, sender.ErrNoAvailableAccount) && !errors.Is(err, sender.ErrFullPending) {
|
||||||
|
log.Error("Failed to send commitBatch tx to layer1 ", "err", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = r.batchOrm.UpdateCommitTxHashAndRollupStatus(r.ctx, batch.Hash, txHash.String(), types.RollupCommitting)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("UpdateCommitTxHashAndRollupStatus failed", "hash", batch.Hash, "index", batch.Index, "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
bridgeL2BatchesCommittedTotalCounter.Inc(1)
|
||||||
|
r.processingCommitment.Store(txID, batch.Hash)
|
||||||
|
log.Info("Sent the commitBatch tx to layer1", "batch index", batch.Index, "batch hash", batch.Hash, "tx hash", txHash.Hex())
|
||||||
}
|
}
|
||||||
r.processingBatchesCommitment.Store(txID, batchHashes)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProcessCommittedBatches submit proof to layer 1 rollup contract
|
// ProcessCommittedBatches submit proof to layer 1 rollup contract
|
||||||
func (r *Layer2Relayer) ProcessCommittedBatches() {
|
func (r *Layer2Relayer) ProcessCommittedBatches() {
|
||||||
// set skipped batches in a single db operation
|
// retrieves the earliest batch whose rollup status is 'committed'
|
||||||
if count, err := r.blockBatchOrm.UpdateSkippedBatches(); err != nil {
|
fields := map[string]interface{}{
|
||||||
log.Error("UpdateSkippedBatches failed", "err", err)
|
"rollup_status": types.RollupCommitted,
|
||||||
// continue anyway
|
|
||||||
} else if count > 0 {
|
|
||||||
bridgeL2BatchesSkippedTotalCounter.Inc(count)
|
|
||||||
log.Info("Skipping batches", "count", count)
|
|
||||||
}
|
}
|
||||||
|
orderByList := []string{"index ASC"}
|
||||||
// batches are sorted by batch index in increasing order
|
limit := 1
|
||||||
batchHashes, err := r.blockBatchOrm.GetBlockBatchesHashByRollupStatus(types.RollupCommitted, 1)
|
batches, err := r.batchOrm.GetBatches(r.ctx, fields, orderByList, limit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Failed to fetch committed L2 batches", "err", err)
|
log.Error("Failed to fetch committed L2 batches", "err", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(batchHashes) == 0 {
|
if len(batches) != 1 {
|
||||||
return
|
log.Warn("Unexpected result for GetBlockBatches", "number of batches", len(batches))
|
||||||
}
|
|
||||||
hash := batchHashes[0]
|
|
||||||
// @todo add support to relay multiple batches
|
|
||||||
|
|
||||||
batches, err := r.blockBatchOrm.GetBlockBatches(map[string]interface{}{"hash": hash}, nil, 1)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Failed to fetch committed L2 batch", "hash", hash, "err", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(batches) == 0 {
|
|
||||||
log.Error("Unexpected result for GetBlockBatches", "hash", hash, "len", 0)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
batch := batches[0]
|
batch := batches[0]
|
||||||
|
hash := batch.Hash
|
||||||
status := types.ProvingStatus(batch.ProvingStatus)
|
status := types.ProvingStatus(batch.ProvingStatus)
|
||||||
switch status {
|
switch status {
|
||||||
case types.ProvingTaskUnassigned, types.ProvingTaskAssigned:
|
case types.ProvingTaskUnassigned, types.ProvingTaskAssigned:
|
||||||
// The proof for this block is not ready yet.
|
// The proof for this block is not ready yet.
|
||||||
return
|
return
|
||||||
case types.ProvingTaskProved:
|
case types.ProvingTaskProved:
|
||||||
// It's an intermediate state. The roller manager received the proof but has not verified
|
// It's an intermediate state. The prover manager received the proof but has not verified
|
||||||
// the proof yet. We don't roll up the proof until it's verified.
|
// the proof yet. We don't roll up the proof until it's verified.
|
||||||
return
|
return
|
||||||
case types.ProvingTaskFailed, types.ProvingTaskSkipped:
|
|
||||||
// note: this is covered by UpdateSkippedBatches, but we keep it for completeness's sake
|
|
||||||
if err = r.blockBatchOrm.UpdateRollupStatus(r.ctx, hash, types.RollupFinalizationSkipped); err != nil {
|
|
||||||
log.Warn("UpdateRollupStatus failed", "hash", hash, "err", err)
|
|
||||||
}
|
|
||||||
case types.ProvingTaskVerified:
|
case types.ProvingTaskVerified:
|
||||||
log.Info("Start to roll up zk proof", "hash", hash)
|
log.Info("Start to roll up zk proof", "hash", hash)
|
||||||
success := false
|
success := false
|
||||||
|
|
||||||
rollupStatues := []types.RollupStatus{
|
var parentBatchStateRoot string
|
||||||
types.RollupFinalizing,
|
if batch.Index > 0 {
|
||||||
types.RollupFinalized,
|
var parentBatch *orm.Batch
|
||||||
}
|
parentBatch, err = r.batchOrm.GetBatchByIndex(r.ctx, batch.Index-1)
|
||||||
previousBatch, err := r.blockBatchOrm.GetLatestBatchByRollupStatus(rollupStatues)
|
// handle unexpected db error
|
||||||
// skip submitting proof
|
if err != nil {
|
||||||
if err == nil && uint64(batch.CreatedAt.Sub(previousBatch.CreatedAt).Seconds()) < r.cfg.FinalizeBatchIntervalSec {
|
log.Error("Failed to get batch", "index", batch.Index-1, "err", err)
|
||||||
log.Info(
|
return
|
||||||
"Not enough time passed, skipping",
|
|
||||||
"hash", hash,
|
|
||||||
"createdAt", batch.CreatedAt,
|
|
||||||
"lastFinalizingHash", previousBatch.Hash,
|
|
||||||
"lastFinalizingStatus", previousBatch.RollupStatus,
|
|
||||||
"lastFinalizingCreatedAt", previousBatch.CreatedAt,
|
|
||||||
)
|
|
||||||
|
|
||||||
if err = r.blockBatchOrm.UpdateRollupStatus(r.ctx, hash, types.RollupFinalizationSkipped); err != nil {
|
|
||||||
log.Warn("UpdateRollupStatus failed", "hash", hash, "err", err)
|
|
||||||
} else {
|
|
||||||
success = true
|
|
||||||
}
|
}
|
||||||
|
parentBatchStateRoot = parentBatch.StateRoot
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle unexpected db error
|
|
||||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
||||||
log.Error("Failed to get latest finalized batch", "err", err)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
// TODO: need to revisit this and have a more fine-grained error handling
|
// TODO: need to revisit this and have a more fine-grained error handling
|
||||||
if !success {
|
if !success {
|
||||||
log.Info("Failed to upload the proof, change rollup status to FinalizationSkipped", "hash", hash)
|
log.Info("Failed to upload the proof, change rollup status to RollupFinalizeFailed", "hash", hash)
|
||||||
if err = r.blockBatchOrm.UpdateRollupStatus(r.ctx, hash, types.RollupFinalizationSkipped); err != nil {
|
if err = r.batchOrm.UpdateRollupStatus(r.ctx, hash, types.RollupFinalizeFailed); err != nil {
|
||||||
log.Warn("UpdateRollupStatus failed", "hash", hash, "err", err)
|
log.Warn("UpdateRollupStatus failed", "hash", hash, "err", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
aggProof, err := r.blockBatchOrm.GetVerifiedProofByHash(hash)
|
aggProof, err := r.batchOrm.GetVerifiedProofByHash(r.ctx, hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("get verified proof by hash failed", "hash", hash, "err", err)
|
log.Warn("get verified proof by hash failed", "hash", hash, "err", err)
|
||||||
return
|
return
|
||||||
@@ -468,9 +457,14 @@ func (r *Layer2Relayer) ProcessCommittedBatches() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
proof := utils.BufferToUint256Le(aggProof.Proof)
|
data, err := r.l1RollupABI.Pack(
|
||||||
finalPair := utils.BufferToUint256Le(aggProof.FinalPair)
|
"finalizeBatchWithProof",
|
||||||
data, err := r.l1RollupABI.Pack("finalizeBatchWithProof", common.HexToHash(hash), proof, finalPair)
|
batch.BatchHeader,
|
||||||
|
common.HexToHash(parentBatchStateRoot),
|
||||||
|
common.HexToHash(batch.StateRoot),
|
||||||
|
common.HexToHash(batch.WithdrawRoot),
|
||||||
|
aggProof.Proof,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Pack finalizeBatchWithProof failed", "err", err)
|
log.Error("Pack finalizeBatchWithProof failed", "err", err)
|
||||||
return
|
return
|
||||||
@@ -482,53 +476,34 @@ func (r *Layer2Relayer) ProcessCommittedBatches() {
|
|||||||
finalizeTxHash := &txHash
|
finalizeTxHash := &txHash
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.Is(err, sender.ErrNoAvailableAccount) && !errors.Is(err, sender.ErrFullPending) {
|
if !errors.Is(err, sender.ErrNoAvailableAccount) && !errors.Is(err, sender.ErrFullPending) {
|
||||||
log.Error("finalizeBatchWithProof in layer1 failed", "hash", hash, "err", err)
|
log.Error("finalizeBatchWithProof in layer1 failed",
|
||||||
|
"index", batch.Index, "hash", batch.Hash, "err", err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
bridgeL2BatchesFinalizedTotalCounter.Inc(1)
|
bridgeL2BatchesFinalizedTotalCounter.Inc(1)
|
||||||
log.Info("finalizeBatchWithProof in layer1", "batch_hash", hash, "tx_hash", hash)
|
log.Info("finalizeBatchWithProof in layer1", "index", batch.Index, "batch hash", batch.Hash, "tx hash", hash)
|
||||||
|
|
||||||
// record and sync with db, @todo handle db error
|
// record and sync with db, @todo handle db error
|
||||||
err = r.blockBatchOrm.UpdateFinalizeTxHashAndRollupStatus(r.ctx, hash, finalizeTxHash.String(), types.RollupFinalizing)
|
err = r.batchOrm.UpdateFinalizeTxHashAndRollupStatus(r.ctx, hash, finalizeTxHash.String(), types.RollupFinalizing)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("UpdateFinalizeTxHashAndRollupStatus failed", "batch_hash", hash, "err", err)
|
log.Warn("UpdateFinalizeTxHashAndRollupStatus failed",
|
||||||
|
"index", batch.Index, "batch hash", batch.Hash,
|
||||||
|
"tx hash", finalizeTxHash.String(), "err", err)
|
||||||
}
|
}
|
||||||
success = true
|
success = true
|
||||||
r.processingFinalization.Store(txID, hash)
|
r.processingFinalization.Store(txID, hash)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
log.Error("encounter unreachable case in ProcessCommittedBatches",
|
log.Error("encounter unreachable case in ProcessCommittedBatches", "proving status", status)
|
||||||
"block_status", status,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Layer2Relayer) handleConfirmation(confirmation *sender.Confirmation) {
|
func (r *Layer2Relayer) handleConfirmation(confirmation *sender.Confirmation) {
|
||||||
transactionType := "Unknown"
|
transactionType := "Unknown"
|
||||||
// check whether it is message relay transaction
|
|
||||||
if msgHash, ok := r.processingMessage.Load(confirmation.ID); ok {
|
|
||||||
transactionType = "MessageRelay"
|
|
||||||
var status types.MsgStatus
|
|
||||||
if confirmation.IsSuccessful {
|
|
||||||
status = types.MsgConfirmed
|
|
||||||
} else {
|
|
||||||
status = types.MsgRelayFailed
|
|
||||||
log.Warn("transaction confirmed but failed in layer1", "confirmation", confirmation)
|
|
||||||
}
|
|
||||||
// @todo handle db error
|
|
||||||
err := r.l2MessageOrm.UpdateLayer2StatusAndLayer1Hash(r.ctx, msgHash.(string), status, confirmation.TxHash.String())
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("UpdateLayer2StatusAndLayer1Hash failed", "msgHash", msgHash.(string), "err", err)
|
|
||||||
}
|
|
||||||
bridgeL2MsgsRelayedConfirmedTotalCounter.Inc(1)
|
|
||||||
r.processingMessage.Delete(confirmation.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// check whether it is CommitBatches transaction
|
// check whether it is CommitBatches transaction
|
||||||
if batchBatches, ok := r.processingBatchesCommitment.Load(confirmation.ID); ok {
|
if batchHash, ok := r.processingCommitment.Load(confirmation.ID); ok {
|
||||||
transactionType = "BatchesCommitment"
|
transactionType = "BatchesCommitment"
|
||||||
batchHashes := batchBatches.([]string)
|
|
||||||
var status types.RollupStatus
|
var status types.RollupStatus
|
||||||
if confirmation.IsSuccessful {
|
if confirmation.IsSuccessful {
|
||||||
status = types.RollupCommitted
|
status = types.RollupCommitted
|
||||||
@@ -536,15 +511,15 @@ func (r *Layer2Relayer) handleConfirmation(confirmation *sender.Confirmation) {
|
|||||||
status = types.RollupCommitFailed
|
status = types.RollupCommitFailed
|
||||||
log.Warn("transaction confirmed but failed in layer1", "confirmation", confirmation)
|
log.Warn("transaction confirmed but failed in layer1", "confirmation", confirmation)
|
||||||
}
|
}
|
||||||
for _, batchHash := range batchHashes {
|
// @todo handle db error
|
||||||
// @todo handle db error
|
err := r.batchOrm.UpdateCommitTxHashAndRollupStatus(r.ctx, batchHash.(string), confirmation.TxHash.String(), status)
|
||||||
err := r.blockBatchOrm.UpdateCommitTxHashAndRollupStatus(r.ctx, batchHash, confirmation.TxHash.String(), status)
|
if err != nil {
|
||||||
if err != nil {
|
log.Warn("UpdateCommitTxHashAndRollupStatus failed",
|
||||||
log.Warn("UpdateCommitTxHashAndRollupStatus failed", "batch_hash", batchHash, "err", err)
|
"batch hash", batchHash.(string),
|
||||||
}
|
"tx hash", confirmation.TxHash.String(), "err", err)
|
||||||
}
|
}
|
||||||
bridgeL2BatchesCommittedConfirmedTotalCounter.Inc(int64(len(batchHashes)))
|
bridgeL2BatchesCommittedConfirmedTotalCounter.Inc(1)
|
||||||
r.processingBatchesCommitment.Delete(confirmation.ID)
|
r.processingCommitment.Delete(confirmation.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check whether it is proof finalization transaction
|
// check whether it is proof finalization transaction
|
||||||
@@ -557,10 +532,13 @@ func (r *Layer2Relayer) handleConfirmation(confirmation *sender.Confirmation) {
|
|||||||
status = types.RollupFinalizeFailed
|
status = types.RollupFinalizeFailed
|
||||||
log.Warn("transaction confirmed but failed in layer1", "confirmation", confirmation)
|
log.Warn("transaction confirmed but failed in layer1", "confirmation", confirmation)
|
||||||
}
|
}
|
||||||
|
|
||||||
// @todo handle db error
|
// @todo handle db error
|
||||||
err := r.blockBatchOrm.UpdateFinalizeTxHashAndRollupStatus(r.ctx, batchHash.(string), confirmation.TxHash.String(), status)
|
err := r.batchOrm.UpdateFinalizeTxHashAndRollupStatus(r.ctx, batchHash.(string), confirmation.TxHash.String(), status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("UpdateFinalizeTxHashAndRollupStatus failed", "batch_hash", batchHash.(string), "err", err)
|
log.Warn("UpdateFinalizeTxHashAndRollupStatus failed",
|
||||||
|
"batch hash", batchHash.(string),
|
||||||
|
"tx hash", confirmation.TxHash.String(), "err", err)
|
||||||
}
|
}
|
||||||
bridgeL2BatchesFinalizedConfirmedTotalCounter.Inc(1)
|
bridgeL2BatchesFinalizedConfirmedTotalCounter.Inc(1)
|
||||||
r.processingFinalization.Delete(confirmation.ID)
|
r.processingFinalization.Delete(confirmation.ID)
|
||||||
@@ -580,14 +558,14 @@ func (r *Layer2Relayer) handleConfirmLoop(ctx context.Context) {
|
|||||||
case cfm := <-r.gasOracleSender.ConfirmChan():
|
case cfm := <-r.gasOracleSender.ConfirmChan():
|
||||||
if !cfm.IsSuccessful {
|
if !cfm.IsSuccessful {
|
||||||
// @discuss: maybe make it pending again?
|
// @discuss: maybe make it pending again?
|
||||||
err := r.blockBatchOrm.UpdateL2GasOracleStatusAndOracleTxHash(r.ctx, cfm.ID, types.GasOracleFailed, cfm.TxHash.String())
|
err := r.batchOrm.UpdateL2GasOracleStatusAndOracleTxHash(r.ctx, cfm.ID, types.GasOracleFailed, cfm.TxHash.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("UpdateL2GasOracleStatusAndOracleTxHash failed", "err", err)
|
log.Warn("UpdateL2GasOracleStatusAndOracleTxHash failed", "err", err)
|
||||||
}
|
}
|
||||||
log.Warn("transaction confirmed but failed in layer1", "confirmation", cfm)
|
log.Warn("transaction confirmed but failed in layer1", "confirmation", cfm)
|
||||||
} else {
|
} else {
|
||||||
// @todo handle db error
|
// @todo handle db error
|
||||||
err := r.blockBatchOrm.UpdateL2GasOracleStatusAndOracleTxHash(r.ctx, cfm.ID, types.GasOracleImported, cfm.TxHash.String())
|
err := r.batchOrm.UpdateL2GasOracleStatusAndOracleTxHash(r.ctx, cfm.ID, types.GasOracleImported, cfm.TxHash.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("UpdateL2GasOracleStatusAndOracleTxHash failed", "err", err)
|
log.Warn("UpdateL2GasOracleStatusAndOracleTxHash failed", "err", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,48 +2,29 @@ package relayer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/agiledragon/gomonkey/v2"
|
"github.com/agiledragon/gomonkey/v2"
|
||||||
"github.com/scroll-tech/go-ethereum/accounts/abi"
|
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
gethTypes "github.com/scroll-tech/go-ethereum/core/types"
|
|
||||||
"github.com/smartystreets/goconvey/convey"
|
"github.com/smartystreets/goconvey/convey"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
|
"scroll-tech/common/database"
|
||||||
"scroll-tech/common/types"
|
"scroll-tech/common/types"
|
||||||
"scroll-tech/common/types/message"
|
"scroll-tech/common/types/message"
|
||||||
"scroll-tech/common/utils"
|
"scroll-tech/common/utils"
|
||||||
|
|
||||||
|
"scroll-tech/database/migrate"
|
||||||
|
|
||||||
"scroll-tech/bridge/internal/controller/sender"
|
"scroll-tech/bridge/internal/controller/sender"
|
||||||
"scroll-tech/bridge/internal/orm"
|
"scroll-tech/bridge/internal/orm"
|
||||||
"scroll-tech/bridge/internal/orm/migrate"
|
|
||||||
bridgeTypes "scroll-tech/bridge/internal/types"
|
|
||||||
bridgeUtils "scroll-tech/bridge/internal/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
templateL2Message = []orm.L2Message{
|
|
||||||
{
|
|
||||||
Nonce: 1,
|
|
||||||
Height: 1,
|
|
||||||
Sender: "0x596a746661dbed76a84556111c2872249b070e15",
|
|
||||||
Value: "100",
|
|
||||||
Target: "0x2c73620b223808297ea734d946813f0dd78eb8f7",
|
|
||||||
Calldata: "testdata",
|
|
||||||
Layer2Hash: "hash0",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func setupL2RelayerDB(t *testing.T) *gorm.DB {
|
func setupL2RelayerDB(t *testing.T) *gorm.DB {
|
||||||
db, err := bridgeUtils.InitDB(cfg.DBConfig)
|
db, err := database.InitDB(cfg.DBConfig)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
sqlDB, err := db.DB()
|
sqlDB, err := db.DB()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@@ -53,300 +34,118 @@ func setupL2RelayerDB(t *testing.T) *gorm.DB {
|
|||||||
|
|
||||||
func testCreateNewRelayer(t *testing.T) {
|
func testCreateNewRelayer(t *testing.T) {
|
||||||
db := setupL2RelayerDB(t)
|
db := setupL2RelayerDB(t)
|
||||||
defer bridgeUtils.CloseDB(db)
|
defer database.CloseDB(db)
|
||||||
relayer, err := NewLayer2Relayer(context.Background(), l2Cli, db, cfg.L2Config.RelayerConfig)
|
relayer, err := NewLayer2Relayer(context.Background(), l2Cli, db, cfg.L2Config.RelayerConfig, false)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotNil(t, relayer)
|
assert.NotNil(t, relayer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testL2RelayerProcessSaveEvents(t *testing.T) {
|
func testL2RelayerProcessPendingBatches(t *testing.T) {
|
||||||
db := setupL2RelayerDB(t)
|
db := setupL2RelayerDB(t)
|
||||||
defer bridgeUtils.CloseDB(db)
|
defer database.CloseDB(db)
|
||||||
|
|
||||||
l2Cfg := cfg.L2Config
|
l2Cfg := cfg.L2Config
|
||||||
relayer, err := NewLayer2Relayer(context.Background(), l2Cli, db, l2Cfg.RelayerConfig)
|
relayer, err := NewLayer2Relayer(context.Background(), l2Cli, db, l2Cfg.RelayerConfig, false)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
l2MessageOrm := orm.NewL2Message(db)
|
l2BlockOrm := orm.NewL2Block(db)
|
||||||
err = l2MessageOrm.SaveL2Messages(context.Background(), templateL2Message)
|
err = l2BlockOrm.InsertL2Blocks(context.Background(), []*types.WrappedBlock{wrappedBlock1, wrappedBlock2})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
chunkOrm := orm.NewChunk(db)
|
||||||
|
dbChunk1, err := chunkOrm.InsertChunk(context.Background(), chunk1)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
dbChunk2, err := chunkOrm.InsertChunk(context.Background(), chunk2)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
batchOrm := orm.NewBatch(db)
|
||||||
|
batch, err := batchOrm.InsertBatch(context.Background(), 0, 1, dbChunk1.Hash, dbChunk2.Hash, []*types.Chunk{chunk1, chunk2})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
traces := []*bridgeTypes.WrappedBlock{
|
relayer.ProcessPendingBatches()
|
||||||
{
|
|
||||||
Header: &gethTypes.Header{
|
|
||||||
Number: big.NewInt(int64(templateL2Message[0].Height)),
|
|
||||||
},
|
|
||||||
Transactions: nil,
|
|
||||||
WithdrawTrieRoot: common.Hash{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Header: &gethTypes.Header{
|
|
||||||
Number: big.NewInt(int64(templateL2Message[0].Height + 1)),
|
|
||||||
},
|
|
||||||
Transactions: nil,
|
|
||||||
WithdrawTrieRoot: common.Hash{},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
blockTraceOrm := orm.NewBlockTrace(db)
|
statuses, err := batchOrm.GetRollupStatusByHashList(context.Background(), []string{batch.Hash})
|
||||||
assert.NoError(t, blockTraceOrm.InsertWrappedBlocks(traces))
|
|
||||||
blockBatchOrm := orm.NewBlockBatch(db)
|
|
||||||
parentBatch1 := &bridgeTypes.BatchInfo{
|
|
||||||
Index: 0,
|
|
||||||
Hash: common.Hash{}.Hex(),
|
|
||||||
StateRoot: common.Hash{}.Hex(),
|
|
||||||
}
|
|
||||||
batchData1 := bridgeTypes.NewBatchData(parentBatch1, []*bridgeTypes.WrappedBlock{wrappedBlock1}, nil)
|
|
||||||
batchHash := batchData1.Hash().Hex()
|
|
||||||
err = db.Transaction(func(tx *gorm.DB) error {
|
|
||||||
rowsAffected, dbTxErr := blockBatchOrm.InsertBlockBatchByBatchData(tx, batchData1)
|
|
||||||
if dbTxErr != nil {
|
|
||||||
return dbTxErr
|
|
||||||
}
|
|
||||||
if rowsAffected != 1 {
|
|
||||||
dbTxErr = errors.New("the InsertBlockBatchByBatchData affected row is not 1")
|
|
||||||
return dbTxErr
|
|
||||||
}
|
|
||||||
dbTxErr = blockTraceOrm.UpdateBatchHashForL2Blocks(tx, []uint64{1}, batchHash)
|
|
||||||
if dbTxErr != nil {
|
|
||||||
return dbTxErr
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 1, len(statuses))
|
||||||
err = blockBatchOrm.UpdateRollupStatus(context.Background(), batchHash, types.RollupFinalized)
|
assert.Equal(t, types.RollupCommitting, statuses[0])
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
relayer.ProcessSavedEvents()
|
|
||||||
|
|
||||||
msg, err := l2MessageOrm.GetL2MessageByNonce(templateL2Message[0].Nonce)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, types.MsgSubmitted, types.MsgStatus(msg.Status))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func testL2RelayerProcessCommittedBatches(t *testing.T) {
|
func testL2RelayerProcessCommittedBatches(t *testing.T) {
|
||||||
db := setupL2RelayerDB(t)
|
db := setupL2RelayerDB(t)
|
||||||
defer bridgeUtils.CloseDB(db)
|
defer database.CloseDB(db)
|
||||||
|
|
||||||
l2Cfg := cfg.L2Config
|
l2Cfg := cfg.L2Config
|
||||||
relayer, err := NewLayer2Relayer(context.Background(), l2Cli, db, l2Cfg.RelayerConfig)
|
relayer, err := NewLayer2Relayer(context.Background(), l2Cli, db, l2Cfg.RelayerConfig, false)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
batchOrm := orm.NewBatch(db)
|
||||||
|
batch, err := batchOrm.InsertBatch(context.Background(), 0, 1, chunkHash1.Hex(), chunkHash2.Hex(), []*types.Chunk{chunk1, chunk2})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
parentBatch1 := &bridgeTypes.BatchInfo{
|
err = batchOrm.UpdateRollupStatus(context.Background(), batch.Hash, types.RollupCommitted)
|
||||||
Index: 0,
|
|
||||||
Hash: common.Hash{}.Hex(),
|
|
||||||
StateRoot: common.Hash{}.Hex(),
|
|
||||||
}
|
|
||||||
|
|
||||||
blockBatchOrm := orm.NewBlockBatch(db)
|
|
||||||
batchData1 := bridgeTypes.NewBatchData(parentBatch1, []*bridgeTypes.WrappedBlock{wrappedBlock1}, nil)
|
|
||||||
batchHash := batchData1.Hash().Hex()
|
|
||||||
err = db.Transaction(func(tx *gorm.DB) error {
|
|
||||||
rowsAffected, dbTxErr := blockBatchOrm.InsertBlockBatchByBatchData(tx, batchData1)
|
|
||||||
if dbTxErr != nil {
|
|
||||||
return dbTxErr
|
|
||||||
}
|
|
||||||
if rowsAffected != 1 {
|
|
||||||
dbTxErr = errors.New("the InsertBlockBatchByBatchData affected row is not 1")
|
|
||||||
return dbTxErr
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
err = blockBatchOrm.UpdateRollupStatus(context.Background(), batchHash, types.RollupCommitted)
|
err = batchOrm.UpdateProvingStatus(context.Background(), batch.Hash, types.ProvingTaskVerified)
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
proof := &message.AggProof{
|
|
||||||
Proof: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31},
|
|
||||||
FinalPair: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31},
|
|
||||||
}
|
|
||||||
err = blockBatchOrm.UpdateProofByHash(context.Background(), batchHash, proof, 100)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
err = blockBatchOrm.UpdateProvingStatus(batchHash, types.ProvingTaskVerified)
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
relayer.ProcessCommittedBatches()
|
relayer.ProcessCommittedBatches()
|
||||||
|
|
||||||
statuses, err := blockBatchOrm.GetRollupStatusByHashList([]string{batchHash})
|
statuses, err := batchOrm.GetRollupStatusByHashList(context.Background(), []string{batch.Hash})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 1, len(statuses))
|
||||||
|
assert.Equal(t, types.RollupFinalizeFailed, statuses[0])
|
||||||
|
|
||||||
|
err = batchOrm.UpdateRollupStatus(context.Background(), batch.Hash, types.RollupCommitted)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
proof := &message.AggProof{
|
||||||
|
Proof: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31},
|
||||||
|
FinalPair: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31},
|
||||||
|
}
|
||||||
|
err = batchOrm.UpdateProofByHash(context.Background(), batch.Hash, proof, 100)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
relayer.ProcessCommittedBatches()
|
||||||
|
statuses, err = batchOrm.GetRollupStatusByHashList(context.Background(), []string{batch.Hash})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, 1, len(statuses))
|
assert.Equal(t, 1, len(statuses))
|
||||||
assert.Equal(t, types.RollupFinalizing, statuses[0])
|
assert.Equal(t, types.RollupFinalizing, statuses[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
func testL2RelayerSkipBatches(t *testing.T) {
|
|
||||||
db := setupL2RelayerDB(t)
|
|
||||||
defer bridgeUtils.CloseDB(db)
|
|
||||||
|
|
||||||
l2Cfg := cfg.L2Config
|
|
||||||
relayer, err := NewLayer2Relayer(context.Background(), l2Cli, db, l2Cfg.RelayerConfig)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
blockBatchOrm := orm.NewBlockBatch(db)
|
|
||||||
createBatch := func(rollupStatus types.RollupStatus, provingStatus types.ProvingStatus, index uint64) string {
|
|
||||||
batchData := genBatchData(t, index)
|
|
||||||
err = db.Transaction(func(tx *gorm.DB) error {
|
|
||||||
rowsAffected, dbTxErr := blockBatchOrm.InsertBlockBatchByBatchData(tx, batchData)
|
|
||||||
if dbTxErr != nil {
|
|
||||||
return dbTxErr
|
|
||||||
}
|
|
||||||
if rowsAffected != 1 {
|
|
||||||
dbTxErr = errors.New("the InsertBlockBatchByBatchData affected row is not 1")
|
|
||||||
return dbTxErr
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
batchHash := batchData.Hash().Hex()
|
|
||||||
err = blockBatchOrm.UpdateRollupStatus(context.Background(), batchHash, rollupStatus)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
proof := &message.AggProof{
|
|
||||||
Proof: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31},
|
|
||||||
FinalPair: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31},
|
|
||||||
}
|
|
||||||
err = blockBatchOrm.UpdateProofByHash(context.Background(), batchHash, proof, 100)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
err = blockBatchOrm.UpdateProvingStatus(batchHash, provingStatus)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
return batchHash
|
|
||||||
}
|
|
||||||
|
|
||||||
skipped := []string{
|
|
||||||
createBatch(types.RollupCommitted, types.ProvingTaskSkipped, 1),
|
|
||||||
createBatch(types.RollupCommitted, types.ProvingTaskFailed, 2),
|
|
||||||
}
|
|
||||||
|
|
||||||
notSkipped := []string{
|
|
||||||
createBatch(types.RollupPending, types.ProvingTaskSkipped, 3),
|
|
||||||
createBatch(types.RollupCommitting, types.ProvingTaskSkipped, 4),
|
|
||||||
createBatch(types.RollupFinalizing, types.ProvingTaskSkipped, 5),
|
|
||||||
createBatch(types.RollupFinalized, types.ProvingTaskSkipped, 6),
|
|
||||||
createBatch(types.RollupPending, types.ProvingTaskFailed, 7),
|
|
||||||
createBatch(types.RollupCommitting, types.ProvingTaskFailed, 8),
|
|
||||||
createBatch(types.RollupFinalizing, types.ProvingTaskFailed, 9),
|
|
||||||
createBatch(types.RollupFinalized, types.ProvingTaskFailed, 10),
|
|
||||||
createBatch(types.RollupCommitted, types.ProvingTaskVerified, 11),
|
|
||||||
}
|
|
||||||
|
|
||||||
relayer.ProcessCommittedBatches()
|
|
||||||
|
|
||||||
for _, id := range skipped {
|
|
||||||
statuses, err := blockBatchOrm.GetRollupStatusByHashList([]string{id})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, 1, len(statuses))
|
|
||||||
assert.Equal(t, types.RollupFinalizationSkipped, statuses[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, id := range notSkipped {
|
|
||||||
statuses, err := blockBatchOrm.GetRollupStatusByHashList([]string{id})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, 1, len(statuses))
|
|
||||||
assert.NotEqual(t, types.RollupFinalizationSkipped, statuses[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testL2RelayerMsgConfirm(t *testing.T) {
|
|
||||||
db := setupL2RelayerDB(t)
|
|
||||||
defer bridgeUtils.CloseDB(db)
|
|
||||||
l2MessageOrm := orm.NewL2Message(db)
|
|
||||||
insertL2Messages := []orm.L2Message{
|
|
||||||
{MsgHash: "msg-1", Nonce: 0},
|
|
||||||
{MsgHash: "msg-2", Nonce: 1},
|
|
||||||
}
|
|
||||||
err := l2MessageOrm.SaveL2Messages(context.Background(), insertL2Messages)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
// Create and set up the Layer2 Relayer.
|
|
||||||
l2Cfg := cfg.L2Config
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer cancel()
|
|
||||||
l2Relayer, err := NewLayer2Relayer(ctx, l2Cli, db, l2Cfg.RelayerConfig)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
// Simulate message confirmations.
|
|
||||||
l2Relayer.processingMessage.Store("msg-1", "msg-1")
|
|
||||||
l2Relayer.messageSender.SendConfirmation(&sender.Confirmation{
|
|
||||||
ID: "msg-1",
|
|
||||||
IsSuccessful: true,
|
|
||||||
})
|
|
||||||
l2Relayer.processingMessage.Store("msg-2", "msg-2")
|
|
||||||
l2Relayer.messageSender.SendConfirmation(&sender.Confirmation{
|
|
||||||
ID: "msg-2",
|
|
||||||
IsSuccessful: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Check the database for the updated status using TryTimes.
|
|
||||||
assert.True(t, utils.TryTimes(5, func() bool {
|
|
||||||
fields1 := map[string]interface{}{"msg_hash": "msg-1"}
|
|
||||||
msg1, err1 := l2MessageOrm.GetL2Messages(fields1, nil, 0)
|
|
||||||
if len(msg1) != 1 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
fields2 := map[string]interface{}{"msg_hash": "msg-2"}
|
|
||||||
msg2, err2 := l2MessageOrm.GetL2Messages(fields2, nil, 0)
|
|
||||||
if len(msg2) != 1 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return err1 == nil && types.MsgStatus(msg1[0].Status) == types.MsgConfirmed &&
|
|
||||||
err2 == nil && types.MsgStatus(msg2[0].Status) == types.MsgRelayFailed
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
func testL2RelayerRollupConfirm(t *testing.T) {
|
func testL2RelayerRollupConfirm(t *testing.T) {
|
||||||
db := setupL2RelayerDB(t)
|
db := setupL2RelayerDB(t)
|
||||||
defer bridgeUtils.CloseDB(db)
|
defer database.CloseDB(db)
|
||||||
|
|
||||||
// Insert test data.
|
|
||||||
batches := make([]*bridgeTypes.BatchData, 6)
|
|
||||||
for i := 0; i < 6; i++ {
|
|
||||||
batches[i] = genBatchData(t, uint64(i))
|
|
||||||
}
|
|
||||||
|
|
||||||
blockBatchOrm := orm.NewBlockBatch(db)
|
|
||||||
err := db.Transaction(func(tx *gorm.DB) error {
|
|
||||||
for _, batch := range batches {
|
|
||||||
rowsAffected, dbTxErr := blockBatchOrm.InsertBlockBatchByBatchData(tx, batch)
|
|
||||||
if dbTxErr != nil {
|
|
||||||
return dbTxErr
|
|
||||||
}
|
|
||||||
if rowsAffected != 1 {
|
|
||||||
dbTxErr = errors.New("the InsertBlockBatchByBatchData affected row is not 1")
|
|
||||||
return dbTxErr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
// Create and set up the Layer2 Relayer.
|
// Create and set up the Layer2 Relayer.
|
||||||
l2Cfg := cfg.L2Config
|
l2Cfg := cfg.L2Config
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
l2Relayer, err := NewLayer2Relayer(ctx, l2Cli, db, l2Cfg.RelayerConfig)
|
l2Relayer, err := NewLayer2Relayer(ctx, l2Cli, db, l2Cfg.RelayerConfig, false)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Simulate message confirmations.
|
// Simulate message confirmations.
|
||||||
processingKeys := []string{"committed-1", "committed-2", "finalized-1", "finalized-2"}
|
processingKeys := []string{"committed-1", "committed-2", "finalized-1", "finalized-2"}
|
||||||
isSuccessful := []bool{true, false, true, false}
|
isSuccessful := []bool{true, false, true, false}
|
||||||
|
|
||||||
|
batchOrm := orm.NewBatch(db)
|
||||||
|
batchHashes := make([]string, len(processingKeys))
|
||||||
|
for i := range batchHashes {
|
||||||
|
batch, err := batchOrm.InsertBatch(context.Background(), 0, 1, chunkHash1.Hex(), chunkHash2.Hex(), []*types.Chunk{chunk1, chunk2})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
batchHashes[i] = batch.Hash
|
||||||
|
}
|
||||||
|
|
||||||
for i, key := range processingKeys[:2] {
|
for i, key := range processingKeys[:2] {
|
||||||
batchHashes := []string{batches[i*2].Hash().Hex(), batches[i*2+1].Hash().Hex()}
|
l2Relayer.processingCommitment.Store(key, batchHashes[i])
|
||||||
l2Relayer.processingBatchesCommitment.Store(key, batchHashes)
|
l2Relayer.rollupSender.SendConfirmation(&sender.Confirmation{
|
||||||
l2Relayer.messageSender.SendConfirmation(&sender.Confirmation{
|
|
||||||
ID: key,
|
ID: key,
|
||||||
IsSuccessful: isSuccessful[i],
|
IsSuccessful: isSuccessful[i],
|
||||||
|
TxHash: common.HexToHash("0x123456789abcdef"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, key := range processingKeys[2:] {
|
for i, key := range processingKeys[2:] {
|
||||||
batchHash := batches[i+4].Hash().Hex()
|
l2Relayer.processingFinalization.Store(key, batchHashes[i+2])
|
||||||
l2Relayer.processingFinalization.Store(key, batchHash)
|
|
||||||
l2Relayer.rollupSender.SendConfirmation(&sender.Confirmation{
|
l2Relayer.rollupSender.SendConfirmation(&sender.Confirmation{
|
||||||
ID: key,
|
ID: key,
|
||||||
IsSuccessful: isSuccessful[i+2],
|
IsSuccessful: isSuccessful[i+2],
|
||||||
TxHash: common.HexToHash("0x56789abcdef1234"),
|
TxHash: common.HexToHash("0x123456789abcdef"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -354,15 +153,13 @@ func testL2RelayerRollupConfirm(t *testing.T) {
|
|||||||
ok := utils.TryTimes(5, func() bool {
|
ok := utils.TryTimes(5, func() bool {
|
||||||
expectedStatuses := []types.RollupStatus{
|
expectedStatuses := []types.RollupStatus{
|
||||||
types.RollupCommitted,
|
types.RollupCommitted,
|
||||||
types.RollupCommitted,
|
|
||||||
types.RollupCommitFailed,
|
|
||||||
types.RollupCommitFailed,
|
types.RollupCommitFailed,
|
||||||
types.RollupFinalized,
|
types.RollupFinalized,
|
||||||
types.RollupFinalizeFailed,
|
types.RollupFinalizeFailed,
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, batch := range batches[:6] {
|
for i, batchHash := range batchHashes {
|
||||||
batchInDB, err := blockBatchOrm.GetBlockBatches(map[string]interface{}{"hash": batch.Hash().Hex()}, nil, 0)
|
batchInDB, err := batchOrm.GetBatches(context.Background(), map[string]interface{}{"hash": batchHash}, nil, 0)
|
||||||
if err != nil || len(batchInDB) != 1 || types.RollupStatus(batchInDB[0].RollupStatus) != expectedStatuses[i] {
|
if err != nil || len(batchInDB) != 1 || types.RollupStatus(batchInDB[0].RollupStatus) != expectedStatuses[i] {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -374,51 +171,44 @@ func testL2RelayerRollupConfirm(t *testing.T) {
|
|||||||
|
|
||||||
func testL2RelayerGasOracleConfirm(t *testing.T) {
|
func testL2RelayerGasOracleConfirm(t *testing.T) {
|
||||||
db := setupL2RelayerDB(t)
|
db := setupL2RelayerDB(t)
|
||||||
defer bridgeUtils.CloseDB(db)
|
defer database.CloseDB(db)
|
||||||
|
|
||||||
// Insert test data.
|
batchOrm := orm.NewBatch(db)
|
||||||
batches := make([]*bridgeTypes.BatchData, 2)
|
batch1, err := batchOrm.InsertBatch(context.Background(), 0, 0, chunkHash1.Hex(), chunkHash1.Hex(), []*types.Chunk{chunk1})
|
||||||
for i := 0; i < 2; i++ {
|
assert.NoError(t, err)
|
||||||
batches[i] = genBatchData(t, uint64(i))
|
|
||||||
}
|
|
||||||
|
|
||||||
blockBatchOrm := orm.NewBlockBatch(db)
|
batch2, err := batchOrm.InsertBatch(context.Background(), 1, 1, chunkHash2.Hex(), chunkHash2.Hex(), []*types.Chunk{chunk2})
|
||||||
err := db.Transaction(func(tx *gorm.DB) error {
|
|
||||||
for _, batch := range batches {
|
|
||||||
rowsAffected, dbTxErr := blockBatchOrm.InsertBlockBatchByBatchData(tx, batch)
|
|
||||||
if dbTxErr != nil {
|
|
||||||
return dbTxErr
|
|
||||||
}
|
|
||||||
if rowsAffected != 1 {
|
|
||||||
dbTxErr = errors.New("the InsertBlockBatchByBatchData affected row is not 1")
|
|
||||||
return dbTxErr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Create and set up the Layer2 Relayer.
|
// Create and set up the Layer2 Relayer.
|
||||||
l2Cfg := cfg.L2Config
|
l2Cfg := cfg.L2Config
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
l2Relayer, err := NewLayer2Relayer(ctx, l2Cli, db, l2Cfg.RelayerConfig)
|
l2Relayer, err := NewLayer2Relayer(ctx, l2Cli, db, l2Cfg.RelayerConfig, false)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Simulate message confirmations.
|
// Simulate message confirmations.
|
||||||
isSuccessful := []bool{true, false}
|
type BatchConfirmation struct {
|
||||||
for i, batch := range batches {
|
batchHash string
|
||||||
l2Relayer.gasOracleSender.SendConfirmation(&sender.Confirmation{
|
isSuccessful bool
|
||||||
ID: batch.Hash().Hex(),
|
|
||||||
IsSuccessful: isSuccessful[i],
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
confirmations := []BatchConfirmation{
|
||||||
|
{batchHash: batch1.Hash, isSuccessful: true},
|
||||||
|
{batchHash: batch2.Hash, isSuccessful: false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, confirmation := range confirmations {
|
||||||
|
l2Relayer.gasOracleSender.SendConfirmation(&sender.Confirmation{
|
||||||
|
ID: confirmation.batchHash,
|
||||||
|
IsSuccessful: confirmation.isSuccessful,
|
||||||
|
})
|
||||||
|
}
|
||||||
// Check the database for the updated status using TryTimes.
|
// Check the database for the updated status using TryTimes.
|
||||||
ok := utils.TryTimes(5, func() bool {
|
ok := utils.TryTimes(5, func() bool {
|
||||||
expectedStatuses := []types.GasOracleStatus{types.GasOracleImported, types.GasOracleFailed}
|
expectedStatuses := []types.GasOracleStatus{types.GasOracleImported, types.GasOracleFailed}
|
||||||
for i, batch := range batches {
|
for i, confirmation := range confirmations {
|
||||||
gasOracle, err := blockBatchOrm.GetBlockBatches(map[string]interface{}{"hash": batch.Hash().Hex()}, nil, 0)
|
gasOracle, err := batchOrm.GetBatches(context.Background(), map[string]interface{}{"hash": confirmation.batchHash}, nil, 0)
|
||||||
if err != nil || len(gasOracle) != 1 || types.GasOracleStatus(gasOracle[0].OracleStatus) != expectedStatuses[i] {
|
if err != nil || len(gasOracle) != 1 || types.GasOracleStatus(gasOracle[0].OracleStatus) != expectedStatuses[i] {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -428,42 +218,27 @@ func testL2RelayerGasOracleConfirm(t *testing.T) {
|
|||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
func genBatchData(t *testing.T, index uint64) *bridgeTypes.BatchData {
|
|
||||||
templateBlockTrace, err := os.ReadFile("../../../testdata/blockTrace_02.json")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
// unmarshal blockTrace
|
|
||||||
wrappedBlock := &bridgeTypes.WrappedBlock{}
|
|
||||||
err = json.Unmarshal(templateBlockTrace, wrappedBlock)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
wrappedBlock.Header.ParentHash = common.HexToHash("0x" + strconv.FormatUint(index+1, 16))
|
|
||||||
parentBatch := &bridgeTypes.BatchInfo{
|
|
||||||
Index: index,
|
|
||||||
Hash: "0x0000000000000000000000000000000000000000",
|
|
||||||
}
|
|
||||||
return bridgeTypes.NewBatchData(parentBatch, []*bridgeTypes.WrappedBlock{wrappedBlock}, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testLayer2RelayerProcessGasPriceOracle(t *testing.T) {
|
func testLayer2RelayerProcessGasPriceOracle(t *testing.T) {
|
||||||
db := setupL2RelayerDB(t)
|
db := setupL2RelayerDB(t)
|
||||||
defer bridgeUtils.CloseDB(db)
|
defer database.CloseDB(db)
|
||||||
|
|
||||||
relayer, err := NewLayer2Relayer(context.Background(), l2Cli, db, cfg.L2Config.RelayerConfig)
|
relayer, err := NewLayer2Relayer(context.Background(), l2Cli, db, cfg.L2Config.RelayerConfig, false)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotNil(t, relayer)
|
assert.NotNil(t, relayer)
|
||||||
|
|
||||||
var blockBatchOrm *orm.BlockBatch
|
var batchOrm *orm.Batch
|
||||||
convey.Convey("Failed to GetLatestBatch", t, func() {
|
convey.Convey("Failed to GetLatestBatch", t, func() {
|
||||||
targetErr := errors.New("GetLatestBatch error")
|
targetErr := errors.New("GetLatestBatch error")
|
||||||
patchGuard := gomonkey.ApplyMethodFunc(blockBatchOrm, "GetLatestBatch", func() (*orm.BlockBatch, error) {
|
patchGuard := gomonkey.ApplyMethodFunc(batchOrm, "GetLatestBatch", func(context.Context) (*orm.Batch, error) {
|
||||||
return nil, targetErr
|
return nil, targetErr
|
||||||
})
|
})
|
||||||
defer patchGuard.Reset()
|
defer patchGuard.Reset()
|
||||||
relayer.ProcessGasPriceOracle()
|
relayer.ProcessGasPriceOracle()
|
||||||
})
|
})
|
||||||
|
|
||||||
patchGuard := gomonkey.ApplyMethodFunc(blockBatchOrm, "GetLatestBatch", func() (*orm.BlockBatch, error) {
|
patchGuard := gomonkey.ApplyMethodFunc(batchOrm, "GetLatestBatch", func(context.Context) (*orm.Batch, error) {
|
||||||
batch := orm.BlockBatch{
|
batch := orm.Batch{
|
||||||
OracleStatus: int(types.GasOraclePending),
|
OracleStatus: int16(types.GasOraclePending),
|
||||||
Hash: "0x0000000000000000000000000000000000000000",
|
Hash: "0x0000000000000000000000000000000000000000",
|
||||||
}
|
}
|
||||||
return &batch, nil
|
return &batch, nil
|
||||||
@@ -508,97 +283,14 @@ func testLayer2RelayerProcessGasPriceOracle(t *testing.T) {
|
|||||||
|
|
||||||
convey.Convey("UpdateGasOracleStatusAndOracleTxHash failed", t, func() {
|
convey.Convey("UpdateGasOracleStatusAndOracleTxHash failed", t, func() {
|
||||||
targetErr := errors.New("UpdateL2GasOracleStatusAndOracleTxHash error")
|
targetErr := errors.New("UpdateL2GasOracleStatusAndOracleTxHash error")
|
||||||
patchGuard.ApplyMethodFunc(blockBatchOrm, "UpdateL2GasOracleStatusAndOracleTxHash", func(ctx context.Context, hash string, status types.GasOracleStatus, txHash string) error {
|
patchGuard.ApplyMethodFunc(batchOrm, "UpdateL2GasOracleStatusAndOracleTxHash", func(ctx context.Context, hash string, status types.GasOracleStatus, txHash string) error {
|
||||||
return targetErr
|
return targetErr
|
||||||
})
|
})
|
||||||
relayer.ProcessGasPriceOracle()
|
relayer.ProcessGasPriceOracle()
|
||||||
})
|
})
|
||||||
|
|
||||||
patchGuard.ApplyMethodFunc(blockBatchOrm, "UpdateL2GasOracleStatusAndOracleTxHash", func(ctx context.Context, hash string, status types.GasOracleStatus, txHash string) error {
|
patchGuard.ApplyMethodFunc(batchOrm, "UpdateL2GasOracleStatusAndOracleTxHash", func(ctx context.Context, hash string, status types.GasOracleStatus, txHash string) error {
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
relayer.ProcessGasPriceOracle()
|
relayer.ProcessGasPriceOracle()
|
||||||
}
|
}
|
||||||
|
|
||||||
func testLayer2RelayerSendCommitTx(t *testing.T) {
|
|
||||||
db := setupL2RelayerDB(t)
|
|
||||||
defer bridgeUtils.CloseDB(db)
|
|
||||||
|
|
||||||
relayer, err := NewLayer2Relayer(context.Background(), l2Cli, db, cfg.L2Config.RelayerConfig)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.NotNil(t, relayer)
|
|
||||||
|
|
||||||
var batchDataList []*bridgeTypes.BatchData
|
|
||||||
convey.Convey("SendCommitTx receives empty batch", t, func() {
|
|
||||||
err = relayer.SendCommitTx(batchDataList)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
})
|
|
||||||
|
|
||||||
parentBatch := &bridgeTypes.BatchInfo{
|
|
||||||
Index: 0,
|
|
||||||
Hash: "0x0000000000000000000000000000000000000000",
|
|
||||||
}
|
|
||||||
|
|
||||||
traces := []*bridgeTypes.WrappedBlock{
|
|
||||||
{
|
|
||||||
Header: &gethTypes.Header{
|
|
||||||
Number: big.NewInt(1000),
|
|
||||||
ParentHash: common.Hash{},
|
|
||||||
Difficulty: big.NewInt(0),
|
|
||||||
BaseFee: big.NewInt(0),
|
|
||||||
},
|
|
||||||
Transactions: nil,
|
|
||||||
WithdrawTrieRoot: common.Hash{},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
blocks := []*bridgeTypes.WrappedBlock{traces[0]}
|
|
||||||
tmpBatchData := bridgeTypes.NewBatchData(parentBatch, blocks, cfg.L2Config.BatchProposerConfig.PublicInputConfig)
|
|
||||||
batchDataList = append(batchDataList, tmpBatchData)
|
|
||||||
|
|
||||||
var s abi.ABI
|
|
||||||
convey.Convey("Failed to pack commitBatches", t, func() {
|
|
||||||
targetErr := errors.New("commitBatches error")
|
|
||||||
patchGuard := gomonkey.ApplyMethodFunc(s, "Pack", func(name string, args ...interface{}) ([]byte, error) {
|
|
||||||
return nil, targetErr
|
|
||||||
})
|
|
||||||
defer patchGuard.Reset()
|
|
||||||
|
|
||||||
err = relayer.SendCommitTx(batchDataList)
|
|
||||||
assert.EqualError(t, err, targetErr.Error())
|
|
||||||
})
|
|
||||||
|
|
||||||
patchGuard := gomonkey.ApplyMethodFunc(s, "Pack", func(name string, args ...interface{}) ([]byte, error) {
|
|
||||||
return nil, nil
|
|
||||||
})
|
|
||||||
defer patchGuard.Reset()
|
|
||||||
|
|
||||||
convey.Convey("Failed to send commitBatches tx to layer1", t, func() {
|
|
||||||
targetErr := errors.New("SendTransaction failure")
|
|
||||||
patchGuard.ApplyMethodFunc(relayer.rollupSender, "SendTransaction", func(ID string, target *common.Address, value *big.Int, data []byte, minGasLimit uint64) (hash common.Hash, err error) {
|
|
||||||
return common.Hash{}, targetErr
|
|
||||||
})
|
|
||||||
err = relayer.SendCommitTx(batchDataList)
|
|
||||||
assert.EqualError(t, err, targetErr.Error())
|
|
||||||
})
|
|
||||||
|
|
||||||
patchGuard.ApplyMethodFunc(relayer.rollupSender, "SendTransaction", func(ID string, target *common.Address, value *big.Int, data []byte, minGasLimit uint64) (hash common.Hash, err error) {
|
|
||||||
return common.HexToHash("0x56789abcdef1234"), nil
|
|
||||||
})
|
|
||||||
|
|
||||||
var blockBatchOrm *orm.BlockBatch
|
|
||||||
convey.Convey("UpdateCommitTxHashAndRollupStatus failed", t, func() {
|
|
||||||
targetErr := errors.New("UpdateCommitTxHashAndRollupStatus failure")
|
|
||||||
patchGuard.ApplyMethodFunc(blockBatchOrm, "UpdateCommitTxHashAndRollupStatus", func(ctx context.Context, hash string, commitTxHash string, status types.RollupStatus) error {
|
|
||||||
return targetErr
|
|
||||||
})
|
|
||||||
err = relayer.SendCommitTx(batchDataList)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
})
|
|
||||||
|
|
||||||
patchGuard.ApplyMethodFunc(blockBatchOrm, "UpdateCommitTxHashAndRollupStatus", func(ctx context.Context, hash string, commitTxHash string, status types.RollupStatus) error {
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
err = relayer.SendCommitTx(batchDataList)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,14 +5,15 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
"github.com/scroll-tech/go-ethereum/ethclient"
|
"github.com/scroll-tech/go-ethereum/ethclient"
|
||||||
"github.com/scroll-tech/go-ethereum/log"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"scroll-tech/common/database"
|
||||||
"scroll-tech/common/docker"
|
"scroll-tech/common/docker"
|
||||||
|
"scroll-tech/common/types"
|
||||||
|
|
||||||
"scroll-tech/bridge/internal/config"
|
"scroll-tech/bridge/internal/config"
|
||||||
bridgeTypes "scroll-tech/bridge/internal/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -24,17 +25,20 @@ var (
|
|||||||
// l2geth client
|
// l2geth client
|
||||||
l2Cli *ethclient.Client
|
l2Cli *ethclient.Client
|
||||||
|
|
||||||
// block trace
|
// l2 block
|
||||||
wrappedBlock1 *bridgeTypes.WrappedBlock
|
wrappedBlock1 *types.WrappedBlock
|
||||||
wrappedBlock2 *bridgeTypes.WrappedBlock
|
wrappedBlock2 *types.WrappedBlock
|
||||||
|
|
||||||
// batch data
|
// chunk
|
||||||
batchData1 *bridgeTypes.BatchData
|
chunk1 *types.Chunk
|
||||||
batchData2 *bridgeTypes.BatchData
|
chunk2 *types.Chunk
|
||||||
|
chunkHash1 common.Hash
|
||||||
|
chunkHash2 common.Hash
|
||||||
)
|
)
|
||||||
|
|
||||||
func setupEnv(t *testing.T) (err error) {
|
func setupEnv(t *testing.T) {
|
||||||
// Load config.
|
// Load config.
|
||||||
|
var err error
|
||||||
cfg, err = config.NewConfig("../../../conf/config.json")
|
cfg, err = config.NewConfig("../../../conf/config.json")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
@@ -42,7 +46,7 @@ func setupEnv(t *testing.T) (err error) {
|
|||||||
|
|
||||||
cfg.L2Config.RelayerConfig.SenderConfig.Endpoint = base.L1gethImg.Endpoint()
|
cfg.L2Config.RelayerConfig.SenderConfig.Endpoint = base.L1gethImg.Endpoint()
|
||||||
cfg.L1Config.RelayerConfig.SenderConfig.Endpoint = base.L2gethImg.Endpoint()
|
cfg.L1Config.RelayerConfig.SenderConfig.Endpoint = base.L2gethImg.Endpoint()
|
||||||
cfg.DBConfig = &config.DBConfig{
|
cfg.DBConfig = &database.Config{
|
||||||
DSN: base.DBConfig.DSN,
|
DSN: base.DBConfig.DSN,
|
||||||
DriverName: base.DBConfig.DriverName,
|
DriverName: base.DBConfig.DriverName,
|
||||||
MaxOpenNum: base.DBConfig.MaxOpenNum,
|
MaxOpenNum: base.DBConfig.MaxOpenNum,
|
||||||
@@ -54,40 +58,22 @@ func setupEnv(t *testing.T) (err error) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
templateBlockTrace1, err := os.ReadFile("../../../testdata/blockTrace_02.json")
|
templateBlockTrace1, err := os.ReadFile("../../../testdata/blockTrace_02.json")
|
||||||
if err != nil {
|
assert.NoError(t, err)
|
||||||
return err
|
wrappedBlock1 = &types.WrappedBlock{}
|
||||||
}
|
err = json.Unmarshal(templateBlockTrace1, wrappedBlock1)
|
||||||
// unmarshal blockTrace
|
assert.NoError(t, err)
|
||||||
wrappedBlock1 = &bridgeTypes.WrappedBlock{}
|
chunk1 = &types.Chunk{Blocks: []*types.WrappedBlock{wrappedBlock1}}
|
||||||
if err = json.Unmarshal(templateBlockTrace1, wrappedBlock1); err != nil {
|
chunkHash1, err = chunk1.Hash(0)
|
||||||
return err
|
assert.NoError(t, err)
|
||||||
}
|
|
||||||
parentBatch1 := &bridgeTypes.BatchInfo{
|
|
||||||
Index: 0,
|
|
||||||
Hash: "0x0cc6b102c2924402c14b2e3a19baccc316252bfdc44d9ec62e942d34e39ec729",
|
|
||||||
StateRoot: "0x2579122e8f9ec1e862e7d415cef2fb495d7698a8e5f0dddc5651ba4236336e7d",
|
|
||||||
}
|
|
||||||
batchData1 = bridgeTypes.NewBatchData(parentBatch1, []*bridgeTypes.WrappedBlock{wrappedBlock1}, nil)
|
|
||||||
|
|
||||||
templateBlockTrace2, err := os.ReadFile("../../../testdata/blockTrace_03.json")
|
templateBlockTrace2, err := os.ReadFile("../../../testdata/blockTrace_03.json")
|
||||||
if err != nil {
|
assert.NoError(t, err)
|
||||||
return err
|
wrappedBlock2 = &types.WrappedBlock{}
|
||||||
}
|
err = json.Unmarshal(templateBlockTrace2, wrappedBlock2)
|
||||||
// unmarshal blockTrace
|
assert.NoError(t, err)
|
||||||
wrappedBlock2 = &bridgeTypes.WrappedBlock{}
|
chunk2 = &types.Chunk{Blocks: []*types.WrappedBlock{wrappedBlock2}}
|
||||||
if err = json.Unmarshal(templateBlockTrace2, wrappedBlock2); err != nil {
|
chunkHash2, err = chunk2.Hash(chunk1.NumL1Messages(0))
|
||||||
return err
|
assert.NoError(t, err)
|
||||||
}
|
|
||||||
parentBatch2 := &bridgeTypes.BatchInfo{
|
|
||||||
Index: batchData1.Batch.BatchIndex,
|
|
||||||
Hash: batchData1.Hash().Hex(),
|
|
||||||
StateRoot: batchData1.Batch.NewStateRoot.String(),
|
|
||||||
}
|
|
||||||
batchData2 = bridgeTypes.NewBatchData(parentBatch2, []*bridgeTypes.WrappedBlock{wrappedBlock2}, nil)
|
|
||||||
|
|
||||||
log.Info("batchHash", "batchhash1", batchData1.Hash().Hex(), "batchhash2", batchData2.Hash().Hex())
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
@@ -99,9 +85,7 @@ func TestMain(m *testing.M) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFunctions(t *testing.T) {
|
func TestFunctions(t *testing.T) {
|
||||||
if err := setupEnv(t); err != nil {
|
setupEnv(t)
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
// Run l1 relayer test cases.
|
// Run l1 relayer test cases.
|
||||||
t.Run("TestCreateNewL1Relayer", testCreateNewL1Relayer)
|
t.Run("TestCreateNewL1Relayer", testCreateNewL1Relayer)
|
||||||
t.Run("TestL1RelayerProcessSaveEvents", testL1RelayerProcessSaveEvents)
|
t.Run("TestL1RelayerProcessSaveEvents", testL1RelayerProcessSaveEvents)
|
||||||
@@ -111,12 +95,9 @@ func TestFunctions(t *testing.T) {
|
|||||||
|
|
||||||
// Run l2 relayer test cases.
|
// Run l2 relayer test cases.
|
||||||
t.Run("TestCreateNewRelayer", testCreateNewRelayer)
|
t.Run("TestCreateNewRelayer", testCreateNewRelayer)
|
||||||
t.Run("TestL2RelayerProcessSaveEvents", testL2RelayerProcessSaveEvents)
|
t.Run("TestL2RelayerProcessPendingBatches", testL2RelayerProcessPendingBatches)
|
||||||
t.Run("TestL2RelayerProcessCommittedBatches", testL2RelayerProcessCommittedBatches)
|
t.Run("TestL2RelayerProcessCommittedBatches", testL2RelayerProcessCommittedBatches)
|
||||||
t.Run("TestL2RelayerSkipBatches", testL2RelayerSkipBatches)
|
|
||||||
t.Run("TestL2RelayerMsgConfirm", testL2RelayerMsgConfirm)
|
|
||||||
t.Run("TestL2RelayerRollupConfirm", testL2RelayerRollupConfirm)
|
t.Run("TestL2RelayerRollupConfirm", testL2RelayerRollupConfirm)
|
||||||
t.Run("TestL2RelayerGasOracleConfirm", testL2RelayerGasOracleConfirm)
|
t.Run("TestL2RelayerGasOracleConfirm", testL2RelayerGasOracleConfirm)
|
||||||
t.Run("TestLayer2RelayerProcessGasPriceOracle", testLayer2RelayerProcessGasPriceOracle)
|
t.Run("TestLayer2RelayerProcessGasPriceOracle", testLayer2RelayerProcessGasPriceOracle)
|
||||||
t.Run("TestLayer2RelayerSendCommitTx", testLayer2RelayerSendCommitTx)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -343,6 +343,20 @@ func (s *Sender) resubmitTransaction(feeData *FeeData, auth *bind.TransactOpts,
|
|||||||
if gasTipCap.Cmp(feeData.gasTipCap) < 0 {
|
if gasTipCap.Cmp(feeData.gasTipCap) < 0 {
|
||||||
gasTipCap = feeData.gasTipCap
|
gasTipCap = feeData.gasTipCap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// adjust for rising basefee
|
||||||
|
adjBaseFee := big.NewInt(0)
|
||||||
|
if feeGas := atomic.LoadUint64(&s.baseFeePerGas); feeGas != 0 {
|
||||||
|
adjBaseFee.SetUint64(feeGas)
|
||||||
|
}
|
||||||
|
adjBaseFee = adjBaseFee.Mul(adjBaseFee, escalateMultipleNum)
|
||||||
|
adjBaseFee = adjBaseFee.Div(adjBaseFee, escalateMultipleDen)
|
||||||
|
currentGasFeeCap := new(big.Int).Add(gasTipCap, adjBaseFee)
|
||||||
|
if gasFeeCap.Cmp(currentGasFeeCap) < 0 {
|
||||||
|
gasFeeCap = currentGasFeeCap
|
||||||
|
}
|
||||||
|
|
||||||
|
// but don't exceed maxGasPrice
|
||||||
if gasFeeCap.Cmp(maxGasPrice) > 0 {
|
if gasFeeCap.Cmp(maxGasPrice) > 0 {
|
||||||
gasFeeCap = maxGasPrice
|
gasFeeCap = maxGasPrice
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ func TestSender(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("test min gas limit", testMinGasLimit)
|
t.Run("test min gas limit", testMinGasLimit)
|
||||||
t.Run("test resubmit transaction", testResubmitTransaction)
|
t.Run("test resubmit transaction", testResubmitTransaction)
|
||||||
|
t.Run("test resubmit transaction with rising base fee", testResubmitTransactionWithRisingBaseFee)
|
||||||
t.Run("test check pending transaction", testCheckPendingTransaction)
|
t.Run("test check pending transaction", testCheckPendingTransaction)
|
||||||
|
|
||||||
t.Run("test 1 account sender", func(t *testing.T) { testBatchSender(t, 1) })
|
t.Run("test 1 account sender", func(t *testing.T) { testBatchSender(t, 1) })
|
||||||
@@ -154,6 +155,43 @@ func testResubmitTransaction(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testResubmitTransactionWithRisingBaseFee(t *testing.T) {
|
||||||
|
txType := "DynamicFeeTx"
|
||||||
|
|
||||||
|
cfgCopy := *cfg.L1Config.RelayerConfig.SenderConfig
|
||||||
|
cfgCopy.TxType = txType
|
||||||
|
s, err := NewSender(context.Background(), &cfgCopy, privateKeys)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
auth := s.auths.getAccount()
|
||||||
|
tx := types.NewTransaction(auth.Nonce.Uint64(), common.Address{}, big.NewInt(0), 0, big.NewInt(0), nil)
|
||||||
|
s.baseFeePerGas = 1000
|
||||||
|
feeData, err := s.getFeeData(auth, &common.Address{}, big.NewInt(0), nil, 0)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
// bump the basefee by 10x
|
||||||
|
s.baseFeePerGas *= 10
|
||||||
|
// resubmit and check that the gas fee has been adjusted accordingly
|
||||||
|
newTx, err := s.resubmitTransaction(feeData, auth, tx)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
escalateMultipleNum := new(big.Int).SetUint64(s.config.EscalateMultipleNum)
|
||||||
|
escalateMultipleDen := new(big.Int).SetUint64(s.config.EscalateMultipleDen)
|
||||||
|
maxGasPrice := new(big.Int).SetUint64(s.config.MaxGasPrice)
|
||||||
|
|
||||||
|
adjBaseFee := new(big.Int)
|
||||||
|
adjBaseFee.SetUint64(s.baseFeePerGas)
|
||||||
|
adjBaseFee = adjBaseFee.Mul(adjBaseFee, escalateMultipleNum)
|
||||||
|
adjBaseFee = adjBaseFee.Div(adjBaseFee, escalateMultipleDen)
|
||||||
|
|
||||||
|
expectedGasFeeCap := new(big.Int).Add(feeData.gasTipCap, adjBaseFee)
|
||||||
|
if expectedGasFeeCap.Cmp(maxGasPrice) > 0 {
|
||||||
|
expectedGasFeeCap = maxGasPrice
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, expectedGasFeeCap.Int64(), newTx.GasFeeCap().Int64())
|
||||||
|
|
||||||
|
s.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
func testCheckPendingTransaction(t *testing.T) {
|
func testCheckPendingTransaction(t *testing.T) {
|
||||||
for _, txType := range txTypes {
|
for _, txType := range txTypes {
|
||||||
cfgCopy := *cfg.L1Config.RelayerConfig.SenderConfig
|
cfgCopy := *cfg.L1Config.RelayerConfig.SenderConfig
|
||||||
|
|||||||
@@ -3,393 +3,169 @@ package watcher
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/log"
|
"github.com/scroll-tech/go-ethereum/log"
|
||||||
gethMetrics "github.com/scroll-tech/go-ethereum/metrics"
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"scroll-tech/common/metrics"
|
|
||||||
"scroll-tech/common/types"
|
"scroll-tech/common/types"
|
||||||
|
|
||||||
bridgeAbi "scroll-tech/bridge/abi"
|
|
||||||
"scroll-tech/bridge/internal/config"
|
"scroll-tech/bridge/internal/config"
|
||||||
"scroll-tech/bridge/internal/controller/relayer"
|
|
||||||
"scroll-tech/bridge/internal/orm"
|
"scroll-tech/bridge/internal/orm"
|
||||||
bridgeTypes "scroll-tech/bridge/internal/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
// BatchProposer proposes batches based on available unbatched chunks.
|
||||||
bridgeL2BatchesGasOverThresholdTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/batches/gas/over/threshold/total", metrics.ScrollRegistry)
|
|
||||||
bridgeL2BatchesTxsOverThresholdTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/batches/txs/over/threshold/total", metrics.ScrollRegistry)
|
|
||||||
bridgeL2BatchesBlocksCreatedTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/batches/blocks/created/total", metrics.ScrollRegistry)
|
|
||||||
bridgeL2BatchesCommitsSentTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/batches/commits/sent/total", metrics.ScrollRegistry)
|
|
||||||
bridgeL2BatchesOversizedTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/batches/oversized/total", metrics.ScrollRegistry)
|
|
||||||
bridgeL2BatchesTxsCreatedPerBatchGauge = gethMetrics.NewRegisteredGauge("bridge/l2/batches/txs/created/per/batch", metrics.ScrollRegistry)
|
|
||||||
bridgeL2BatchesGasCreatedPerBatchGauge = gethMetrics.NewRegisteredGauge("bridge/l2/batches/gas/created/per/batch", metrics.ScrollRegistry)
|
|
||||||
bridgeL2BatchesPayloadSizePerBatchGauge = gethMetrics.NewRegisteredGauge("bridge/l2/batches/payload/size/per/batch", metrics.ScrollRegistry)
|
|
||||||
)
|
|
||||||
|
|
||||||
// BatchProposer sends batches commit transactions to relayer.
|
|
||||||
type BatchProposer struct {
|
type BatchProposer struct {
|
||||||
mutex sync.Mutex
|
ctx context.Context
|
||||||
ctx context.Context
|
db *gorm.DB
|
||||||
db *gorm.DB
|
|
||||||
|
|
||||||
batchTimeSec uint64
|
batchOrm *orm.Batch
|
||||||
batchGasThreshold uint64
|
chunkOrm *orm.Chunk
|
||||||
batchTxNumThreshold uint64
|
l2BlockOrm *orm.L2Block
|
||||||
batchBlocksLimit uint64
|
|
||||||
batchCommitTimeSec uint64
|
|
||||||
commitCalldataSizeLimit uint64
|
|
||||||
batchDataBufferSizeLimit uint64
|
|
||||||
commitCalldataMinSize uint64
|
|
||||||
commitBatchCountLimit int
|
|
||||||
|
|
||||||
proofGenerationFreq uint64
|
maxChunkNumPerBatch uint64
|
||||||
batchDataBuffer []*bridgeTypes.BatchData
|
maxL1CommitGasPerBatch uint64
|
||||||
relayer *relayer.Layer2Relayer
|
maxL1CommitCalldataSizePerBatch uint32
|
||||||
|
minChunkNumPerBatch uint64
|
||||||
blockBatchOrm *orm.BlockBatch
|
batchTimeoutSec uint64
|
||||||
blockTraceOrm *orm.BlockTrace
|
|
||||||
|
|
||||||
piCfg *bridgeTypes.PublicInputHashConfig
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBatchProposer will return a new instance of BatchProposer.
|
// NewBatchProposer creates a new BatchProposer instance.
|
||||||
func NewBatchProposer(ctx context.Context, cfg *config.BatchProposerConfig, relayer *relayer.Layer2Relayer, db *gorm.DB) *BatchProposer {
|
func NewBatchProposer(ctx context.Context, cfg *config.BatchProposerConfig, db *gorm.DB) *BatchProposer {
|
||||||
p := &BatchProposer{
|
return &BatchProposer{
|
||||||
mutex: sync.Mutex{},
|
ctx: ctx,
|
||||||
ctx: ctx,
|
db: db,
|
||||||
db: db,
|
batchOrm: orm.NewBatch(db),
|
||||||
blockBatchOrm: orm.NewBlockBatch(db),
|
chunkOrm: orm.NewChunk(db),
|
||||||
blockTraceOrm: orm.NewBlockTrace(db),
|
l2BlockOrm: orm.NewL2Block(db),
|
||||||
batchTimeSec: cfg.BatchTimeSec,
|
maxChunkNumPerBatch: cfg.MaxChunkNumPerBatch,
|
||||||
batchGasThreshold: cfg.BatchGasThreshold,
|
maxL1CommitGasPerBatch: cfg.MaxL1CommitGasPerBatch,
|
||||||
batchTxNumThreshold: cfg.BatchTxNumThreshold,
|
maxL1CommitCalldataSizePerBatch: cfg.MaxL1CommitCalldataSizePerBatch,
|
||||||
batchBlocksLimit: cfg.BatchBlocksLimit,
|
minChunkNumPerBatch: cfg.MinChunkNumPerBatch,
|
||||||
batchCommitTimeSec: cfg.BatchCommitTimeSec,
|
batchTimeoutSec: cfg.BatchTimeoutSec,
|
||||||
commitCalldataSizeLimit: cfg.CommitTxCalldataSizeLimit,
|
|
||||||
commitCalldataMinSize: cfg.CommitTxCalldataMinSize,
|
|
||||||
commitBatchCountLimit: int(cfg.CommitTxBatchCountLimit),
|
|
||||||
batchDataBufferSizeLimit: 100*cfg.CommitTxCalldataSizeLimit + 1*1024*1024, // @todo: determine the value.
|
|
||||||
proofGenerationFreq: cfg.ProofGenerationFreq,
|
|
||||||
piCfg: cfg.PublicInputConfig,
|
|
||||||
relayer: relayer,
|
|
||||||
}
|
|
||||||
|
|
||||||
// for graceful restart.
|
|
||||||
p.recoverBatchDataBuffer()
|
|
||||||
|
|
||||||
// try to commit the leftover pending batches
|
|
||||||
p.TryCommitBatches()
|
|
||||||
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *BatchProposer) recoverBatchDataBuffer() {
|
|
||||||
// batches are sorted by batch index in increasing order
|
|
||||||
batchHashes, err := p.blockBatchOrm.GetBlockBatchesHashByRollupStatus(types.RollupPending, math.MaxInt32)
|
|
||||||
if err != nil {
|
|
||||||
log.Crit("Failed to fetch pending L2 batches", "err", err)
|
|
||||||
}
|
|
||||||
if len(batchHashes) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Info("Load pending batches into batchDataBuffer")
|
|
||||||
|
|
||||||
// helper function to cache and get BlockBatch from DB
|
|
||||||
blockBatchCache := make(map[string]orm.BlockBatch)
|
|
||||||
getBlockBatch := func(batchHash string) (*orm.BlockBatch, error) {
|
|
||||||
if blockBatch, ok := blockBatchCache[batchHash]; ok {
|
|
||||||
return &blockBatch, nil
|
|
||||||
}
|
|
||||||
blockBatches, err := p.blockBatchOrm.GetBlockBatches(map[string]interface{}{"hash": batchHash}, nil, 0)
|
|
||||||
if err != nil || len(blockBatches) == 0 {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
blockBatchCache[batchHash] = blockBatches[0]
|
|
||||||
return &blockBatches[0], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// recover the in-memory batchData from DB
|
|
||||||
for _, batchHash := range batchHashes {
|
|
||||||
log.Info("recover batch data from pending batch", "batch_hash", batchHash)
|
|
||||||
blockBatch, err := getBlockBatch(batchHash)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("could not get BlockBatch", "batch_hash", batchHash, "error", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
parentBatch, err := getBlockBatch(blockBatch.ParentHash)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("could not get parent BlockBatch", "batch_hash", batchHash, "error", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
whereFileds := map[string]interface{}{
|
|
||||||
"batch_hash": batchHash,
|
|
||||||
}
|
|
||||||
orderByList := []string{
|
|
||||||
"number ASC",
|
|
||||||
}
|
|
||||||
|
|
||||||
blockTraces, err := p.blockTraceOrm.GetL2BlockInfos(whereFileds, orderByList, 0)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("could not GetL2BlockInfos", "batch_hash", batchHash, "error", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if len(blockTraces) != int(blockBatch.EndBlockNumber-blockBatch.StartBlockNumber+1) {
|
|
||||||
log.Error("the number of block info retrieved from DB mistmatches the batch info in the DB",
|
|
||||||
"len(blockInfos)", len(blockTraces),
|
|
||||||
"expected", blockBatch.EndBlockNumber-blockBatch.StartBlockNumber+1)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
batchData, err := p.generateBatchData(parentBatch, blockTraces)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if batchData.Hash().Hex() != batchHash {
|
|
||||||
log.Error("the hash from recovered batch data mismatches the DB entry",
|
|
||||||
"recovered_batch_hash", batchData.Hash().Hex(),
|
|
||||||
"expected", batchHash)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
p.batchDataBuffer = append(p.batchDataBuffer, batchData)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TryProposeBatch will try to propose a batch.
|
// TryProposeBatch tries to propose a new batches.
|
||||||
func (p *BatchProposer) TryProposeBatch() {
|
func (p *BatchProposer) TryProposeBatch() {
|
||||||
p.mutex.Lock()
|
dbChunks, err := p.proposeBatchChunks()
|
||||||
defer p.mutex.Unlock()
|
if err != nil {
|
||||||
|
log.Error("proposeBatchChunks failed", "err", err)
|
||||||
for p.getBatchDataBufferSize() < p.batchDataBufferSizeLimit {
|
|
||||||
orderBy := []string{"number ASC"}
|
|
||||||
blockTraces, err := p.blockTraceOrm.GetUnbatchedL2Blocks(map[string]interface{}{}, orderBy, int(p.batchBlocksLimit))
|
|
||||||
if err != nil {
|
|
||||||
log.Error("failed to get unbatched blocks", "err", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
batchCreated := p.proposeBatch(blockTraces)
|
|
||||||
|
|
||||||
// while size of batchDataBuffer < commitCalldataMinSize,
|
|
||||||
// proposer keeps fetching and porposing batches.
|
|
||||||
if p.getBatchDataBufferSize() >= p.commitCalldataMinSize {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !batchCreated {
|
|
||||||
// wait for watcher to insert l2 traces.
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TryCommitBatches will try to commit the pending batches.
|
|
||||||
func (p *BatchProposer) TryCommitBatches() {
|
|
||||||
p.mutex.Lock()
|
|
||||||
defer p.mutex.Unlock()
|
|
||||||
|
|
||||||
if len(p.batchDataBuffer) == 0 {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if err := p.updateBatchInfoInDB(dbChunks); err != nil {
|
||||||
// estimate the calldata length to determine whether to commit the pending batches
|
log.Error("update batch info in db failed", "err", err)
|
||||||
index := 0
|
|
||||||
commit := false
|
|
||||||
calldataByteLen := uint64(0)
|
|
||||||
for ; index < len(p.batchDataBuffer) && index < p.commitBatchCountLimit; index++ {
|
|
||||||
calldataByteLen += bridgeAbi.GetBatchCalldataLength(&p.batchDataBuffer[index].Batch)
|
|
||||||
if calldataByteLen > p.commitCalldataSizeLimit {
|
|
||||||
commit = true
|
|
||||||
if index == 0 {
|
|
||||||
log.Warn(
|
|
||||||
"The calldata size of one batch is larger than the threshold",
|
|
||||||
"batch_hash", p.batchDataBuffer[0].Hash().Hex(),
|
|
||||||
"calldata_size", calldataByteLen,
|
|
||||||
)
|
|
||||||
index = 1
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !commit && p.batchDataBuffer[0].Timestamp()+p.batchCommitTimeSec > uint64(time.Now().Unix()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send commit tx for batchDataBuffer[0:index]
|
|
||||||
log.Info("Commit batches", "start_index", p.batchDataBuffer[0].Batch.BatchIndex,
|
|
||||||
"end_index", p.batchDataBuffer[index-1].Batch.BatchIndex)
|
|
||||||
err := p.relayer.SendCommitTx(p.batchDataBuffer[:index])
|
|
||||||
if err != nil {
|
|
||||||
// leave the retry to the next ticker
|
|
||||||
log.Error("SendCommitTx failed", "error", err)
|
|
||||||
} else {
|
|
||||||
// pop the processed batches from the buffer
|
|
||||||
bridgeL2BatchesCommitsSentTotalCounter.Inc(1)
|
|
||||||
p.batchDataBuffer = p.batchDataBuffer[index:]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *BatchProposer) proposeBatch(blockTraces []orm.BlockTrace) bool {
|
func (p *BatchProposer) updateBatchInfoInDB(dbChunks []*orm.Chunk) error {
|
||||||
if len(blockTraces) == 0 {
|
numChunks := len(dbChunks)
|
||||||
return false
|
if numChunks <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
chunks, err := p.dbChunksToBridgeChunks(dbChunks)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
approximatePayloadSize := func(hash string) (uint64, error) {
|
startChunkIndex := dbChunks[0].Index
|
||||||
traces, err := p.blockTraceOrm.GetL2WrappedBlocks(map[string]interface{}{"hash": hash})
|
startChunkHash := dbChunks[0].Hash
|
||||||
|
endChunkIndex := dbChunks[numChunks-1].Index
|
||||||
|
endChunkHash := dbChunks[numChunks-1].Hash
|
||||||
|
err = p.db.Transaction(func(dbTX *gorm.DB) error {
|
||||||
|
batch, dbErr := p.batchOrm.InsertBatch(p.ctx, startChunkIndex, endChunkIndex, startChunkHash, endChunkHash, chunks, dbTX)
|
||||||
|
if dbErr != nil {
|
||||||
|
return dbErr
|
||||||
|
}
|
||||||
|
dbErr = p.chunkOrm.UpdateBatchHashInRange(p.ctx, startChunkIndex, endChunkIndex, batch.Hash, dbTX)
|
||||||
|
if dbErr != nil {
|
||||||
|
return dbErr
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BatchProposer) proposeBatchChunks() ([]*orm.Chunk, error) {
|
||||||
|
dbChunks, err := p.chunkOrm.GetUnbatchedChunks(p.ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(dbChunks) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
firstChunk := dbChunks[0]
|
||||||
|
totalL1CommitCalldataSize := firstChunk.TotalL1CommitCalldataSize
|
||||||
|
totalL1CommitGas := firstChunk.TotalL1CommitGas
|
||||||
|
var totalChunks uint64 = 1
|
||||||
|
|
||||||
|
// Check if the first chunk breaks hard limits.
|
||||||
|
// If so, it indicates there are bugs in chunk-proposer, manual fix is needed.
|
||||||
|
if totalL1CommitGas > p.maxL1CommitGasPerBatch {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"the first chunk exceeds l1 commit gas limit; start block number: %v, end block number: %v, commit gas: %v, max commit gas limit: %v",
|
||||||
|
firstChunk.StartBlockNumber,
|
||||||
|
firstChunk.EndBlockNumber,
|
||||||
|
totalL1CommitGas,
|
||||||
|
p.maxL1CommitGasPerBatch,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if totalL1CommitCalldataSize > p.maxL1CommitCalldataSizePerBatch {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"the first chunk exceeds l1 commit calldata size limit; start block number: %v, end block number %v, calldata size: %v, max calldata size limit: %v",
|
||||||
|
firstChunk.StartBlockNumber,
|
||||||
|
firstChunk.EndBlockNumber,
|
||||||
|
totalL1CommitCalldataSize,
|
||||||
|
p.maxL1CommitCalldataSizePerBatch,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, chunk := range dbChunks[1:] {
|
||||||
|
totalChunks++
|
||||||
|
totalL1CommitCalldataSize += chunk.TotalL1CommitCalldataSize
|
||||||
|
totalL1CommitGas += chunk.TotalL1CommitGas
|
||||||
|
if totalChunks > p.maxChunkNumPerBatch ||
|
||||||
|
totalL1CommitCalldataSize > p.maxL1CommitCalldataSizePerBatch ||
|
||||||
|
totalL1CommitGas > p.maxL1CommitGasPerBatch {
|
||||||
|
return dbChunks[:i+1], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasChunkTimeout bool
|
||||||
|
currentTimeSec := uint64(time.Now().Unix())
|
||||||
|
if dbChunks[0].StartBlockTime+p.batchTimeoutSec < currentTimeSec {
|
||||||
|
log.Warn("first block timeout",
|
||||||
|
"start block number", dbChunks[0].StartBlockNumber,
|
||||||
|
"first block timestamp", dbChunks[0].StartBlockTime,
|
||||||
|
"chunk outdated time threshold", currentTimeSec,
|
||||||
|
)
|
||||||
|
hasChunkTimeout = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasChunkTimeout && uint64(len(dbChunks)) < p.minChunkNumPerBatch {
|
||||||
|
log.Warn("The chunk number of the batch is less than the minimum limit",
|
||||||
|
"chunk num", len(dbChunks), "minChunkNumPerBatch", p.minChunkNumPerBatch,
|
||||||
|
)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return dbChunks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BatchProposer) dbChunksToBridgeChunks(dbChunks []*orm.Chunk) ([]*types.Chunk, error) {
|
||||||
|
chunks := make([]*types.Chunk, len(dbChunks))
|
||||||
|
for i, c := range dbChunks {
|
||||||
|
wrappedBlocks, err := p.l2BlockOrm.GetL2BlocksInRange(p.ctx, c.StartBlockNumber, c.EndBlockNumber)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
log.Error("Failed to fetch wrapped blocks",
|
||||||
}
|
"start number", c.StartBlockNumber, "end number", c.EndBlockNumber, "error", err)
|
||||||
if len(traces) != 1 {
|
|
||||||
return 0, fmt.Errorf("unexpected traces length, expected = 1, actual = %d", len(traces))
|
|
||||||
}
|
|
||||||
size := 0
|
|
||||||
for _, tx := range traces[0].Transactions {
|
|
||||||
size += len(tx.Data)
|
|
||||||
}
|
|
||||||
return uint64(size), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
firstSize, err := approximatePayloadSize(blockTraces[0].Hash)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("failed to create batch", "number", blockTraces[0].Number, "err", err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if firstSize > p.commitCalldataSizeLimit {
|
|
||||||
log.Warn("oversized payload even for only 1 block", "height", blockTraces[0].Number, "size", firstSize)
|
|
||||||
// note: we should probably fail here once we can ensure this will not happen
|
|
||||||
if err := p.createBatchForBlocks(blockTraces[:1]); err != nil {
|
|
||||||
log.Error("failed to create batch", "number", blockTraces[0].Number, "err", err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
bridgeL2BatchesTxsCreatedPerBatchGauge.Update(int64(blockTraces[0].TxNum))
|
|
||||||
bridgeL2BatchesGasCreatedPerBatchGauge.Update(int64(blockTraces[0].GasUsed))
|
|
||||||
bridgeL2BatchesPayloadSizePerBatchGauge.Update(int64(firstSize))
|
|
||||||
bridgeL2BatchesBlocksCreatedTotalCounter.Inc(1)
|
|
||||||
bridgeL2BatchesOversizedTotalCounter.Inc(1)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if blockTraces[0].GasUsed > p.batchGasThreshold {
|
|
||||||
bridgeL2BatchesGasOverThresholdTotalCounter.Inc(1)
|
|
||||||
log.Warn("gas overflow even for only 1 block", "height", blockTraces[0].Number, "gas", blockTraces[0].GasUsed)
|
|
||||||
if err := p.createBatchForBlocks(blockTraces[:1]); err != nil {
|
|
||||||
log.Error("failed to create batch", "number", blockTraces[0].Number, "err", err)
|
|
||||||
} else {
|
|
||||||
bridgeL2BatchesTxsCreatedPerBatchGauge.Update(int64(blockTraces[0].TxNum))
|
|
||||||
bridgeL2BatchesGasCreatedPerBatchGauge.Update(int64(blockTraces[0].GasUsed))
|
|
||||||
bridgeL2BatchesPayloadSizePerBatchGauge.Update(int64(firstSize))
|
|
||||||
bridgeL2BatchesBlocksCreatedTotalCounter.Inc(1)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if blockTraces[0].TxNum > p.batchTxNumThreshold {
|
|
||||||
bridgeL2BatchesTxsOverThresholdTotalCounter.Inc(1)
|
|
||||||
log.Warn("too many txs even for only 1 block", "height", blockTraces[0].Number, "tx_num", blockTraces[0].TxNum)
|
|
||||||
if err := p.createBatchForBlocks(blockTraces[:1]); err != nil {
|
|
||||||
log.Error("failed to create batch", "number", blockTraces[0].Number, "err", err)
|
|
||||||
} else {
|
|
||||||
bridgeL2BatchesTxsCreatedPerBatchGauge.Update(int64(blockTraces[0].TxNum))
|
|
||||||
bridgeL2BatchesGasCreatedPerBatchGauge.Update(int64(blockTraces[0].GasUsed))
|
|
||||||
bridgeL2BatchesPayloadSizePerBatchGauge.Update(int64(firstSize))
|
|
||||||
bridgeL2BatchesBlocksCreatedTotalCounter.Inc(1)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
var gasUsed, txNum, payloadSize uint64
|
|
||||||
reachThreshold := false
|
|
||||||
// add blocks into batch until reach batchGasThreshold
|
|
||||||
for i, block := range blockTraces {
|
|
||||||
size, err := approximatePayloadSize(block.Hash)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("failed to create batch", "number", block.Number, "err", err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gasUsed+block.GasUsed > p.batchGasThreshold) || (txNum+block.TxNum > p.batchTxNumThreshold) || (payloadSize+size > p.commitCalldataSizeLimit) {
|
|
||||||
blockTraces = blockTraces[:i]
|
|
||||||
reachThreshold = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
gasUsed += block.GasUsed
|
|
||||||
txNum += block.TxNum
|
|
||||||
payloadSize += size
|
|
||||||
}
|
|
||||||
|
|
||||||
// if too few gas gathered, but we don't want to halt, we then check the first block in the batch:
|
|
||||||
// if it's not old enough we will skip proposing the batch,
|
|
||||||
// otherwise we will still propose a batch
|
|
||||||
if !reachThreshold && blockTraces[0].BlockTimestamp+p.batchTimeSec > uint64(time.Now().Unix()) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := p.createBatchForBlocks(blockTraces); err != nil {
|
|
||||||
log.Error("failed to create batch", "from", blockTraces[0].Number, "to", blockTraces[len(blockTraces)-1].Number, "err", err)
|
|
||||||
} else {
|
|
||||||
bridgeL2BatchesTxsCreatedPerBatchGauge.Update(int64(txNum))
|
|
||||||
bridgeL2BatchesGasCreatedPerBatchGauge.Update(int64(gasUsed))
|
|
||||||
bridgeL2BatchesPayloadSizePerBatchGauge.Update(int64(payloadSize))
|
|
||||||
bridgeL2BatchesBlocksCreatedTotalCounter.Inc(int64(len(blockTraces)))
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *BatchProposer) createBatchForBlocks(blocks []orm.BlockTrace) error {
|
|
||||||
lastBatch, err := p.blockBatchOrm.GetLatestBatch()
|
|
||||||
if err != nil {
|
|
||||||
// We should not receive sql.ErrNoRows error. The DB should have the batch entry that contains the genesis block.
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
batchData, err := p.generateBatchData(lastBatch, blocks)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("createBatchData failed", "error", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := orm.AddBatchInfoToDB(p.db, batchData); err != nil {
|
|
||||||
log.Error("addBatchInfoToDB failed", "BatchHash", batchData.Hash(), "error", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
p.batchDataBuffer = append(p.batchDataBuffer, batchData)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *BatchProposer) generateBatchData(parentBatch *orm.BlockBatch, blocks []orm.BlockTrace) (*bridgeTypes.BatchData, error) {
|
|
||||||
var wrappedBlocks []*bridgeTypes.WrappedBlock
|
|
||||||
for _, block := range blocks {
|
|
||||||
trs, err := p.blockTraceOrm.GetL2WrappedBlocks(map[string]interface{}{"hash": block.Hash})
|
|
||||||
if err != nil || len(trs) != 1 {
|
|
||||||
log.Error("Failed to GetBlockTraces", "hash", block.Hash, "err", err)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
chunks[i] = &types.Chunk{
|
||||||
wrappedBlocks = append(wrappedBlocks, trs[0])
|
Blocks: wrappedBlocks,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return chunks, nil
|
||||||
parentBatchInfo := bridgeTypes.BatchInfo{
|
|
||||||
Index: parentBatch.Index,
|
|
||||||
Hash: parentBatch.Hash,
|
|
||||||
StateRoot: parentBatch.StateRoot,
|
|
||||||
}
|
|
||||||
return bridgeTypes.NewBatchData(&parentBatchInfo, wrappedBlocks, p.piCfg), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *BatchProposer) getBatchDataBufferSize() (size uint64) {
|
|
||||||
for _, batchData := range p.batchDataBuffer {
|
|
||||||
size += bridgeAbi.GetBatchCalldataLength(&batchData.Batch)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,207 +2,70 @@ package watcher
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"math"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/agiledragon/gomonkey/v2"
|
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
|
||||||
gethTtypes "github.com/scroll-tech/go-ethereum/core/types"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gorm.io/gorm"
|
|
||||||
|
|
||||||
|
"scroll-tech/common/database"
|
||||||
"scroll-tech/common/types"
|
"scroll-tech/common/types"
|
||||||
|
|
||||||
"scroll-tech/bridge/internal/config"
|
"scroll-tech/bridge/internal/config"
|
||||||
"scroll-tech/bridge/internal/controller/relayer"
|
|
||||||
"scroll-tech/bridge/internal/orm"
|
"scroll-tech/bridge/internal/orm"
|
||||||
bridgeTypes "scroll-tech/bridge/internal/types"
|
|
||||||
bridgeUtils "scroll-tech/bridge/internal/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func testBatchProposerProposeBatch(t *testing.T) {
|
// TODO: Add unit tests that the limits are enforced correctly.
|
||||||
|
func testBatchProposer(t *testing.T) {
|
||||||
db := setupDB(t)
|
db := setupDB(t)
|
||||||
defer bridgeUtils.CloseDB(db)
|
defer database.CloseDB(db)
|
||||||
|
|
||||||
p := &BatchProposer{
|
l2BlockOrm := orm.NewL2Block(db)
|
||||||
batchGasThreshold: 1000,
|
err := l2BlockOrm.InsertL2Blocks(context.Background(), []*types.WrappedBlock{wrappedBlock1, wrappedBlock2})
|
||||||
batchTxNumThreshold: 10,
|
assert.NoError(t, err)
|
||||||
batchTimeSec: 300,
|
|
||||||
commitCalldataSizeLimit: 500,
|
|
||||||
}
|
|
||||||
|
|
||||||
var blockTrace *orm.BlockTrace
|
cp := NewChunkProposer(context.Background(), &config.ChunkProposerConfig{
|
||||||
patchGuard := gomonkey.ApplyMethodFunc(blockTrace, "GetL2WrappedBlocks", func(fields map[string]interface{}) ([]*bridgeTypes.WrappedBlock, error) {
|
MaxTxGasPerChunk: 1000000000,
|
||||||
hash, _ := fields["hash"].(string)
|
MaxL2TxNumPerChunk: 10000,
|
||||||
if hash == "blockWithLongData" {
|
MaxL1CommitGasPerChunk: 50000000000,
|
||||||
longData := strings.Repeat("0", 1000)
|
MaxL1CommitCalldataSizePerChunk: 1000000,
|
||||||
return []*bridgeTypes.WrappedBlock{{
|
MinL1CommitCalldataSizePerChunk: 0,
|
||||||
Transactions: []*gethTtypes.TransactionData{{
|
ChunkTimeoutSec: 300,
|
||||||
Data: longData,
|
}, db)
|
||||||
}},
|
cp.TryProposeChunk()
|
||||||
}}, nil
|
|
||||||
}
|
|
||||||
return []*bridgeTypes.WrappedBlock{{
|
|
||||||
Transactions: []*gethTtypes.TransactionData{{
|
|
||||||
Data: "short",
|
|
||||||
}},
|
|
||||||
}}, nil
|
|
||||||
})
|
|
||||||
defer patchGuard.Reset()
|
|
||||||
patchGuard.ApplyPrivateMethod(p, "createBatchForBlocks", func(*BatchProposer, []*types.BlockInfo) error {
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
block1 := orm.BlockTrace{Number: 1, GasUsed: 100, TxNum: 1, BlockTimestamp: uint64(time.Now().Unix()) - 200}
|
bp := NewBatchProposer(context.Background(), &config.BatchProposerConfig{
|
||||||
block2 := orm.BlockTrace{Number: 2, GasUsed: 200, TxNum: 2, BlockTimestamp: uint64(time.Now().Unix())}
|
MaxChunkNumPerBatch: 10,
|
||||||
block3 := orm.BlockTrace{Number: 3, GasUsed: 300, TxNum: 11, BlockTimestamp: uint64(time.Now().Unix())}
|
MaxL1CommitGasPerBatch: 50000000000,
|
||||||
block4 := orm.BlockTrace{Number: 4, GasUsed: 1001, TxNum: 3, BlockTimestamp: uint64(time.Now().Unix())}
|
MaxL1CommitCalldataSizePerBatch: 1000000,
|
||||||
blockOutdated := orm.BlockTrace{Number: 1, GasUsed: 100, TxNum: 1, BlockTimestamp: uint64(time.Now().Add(-400 * time.Second).Unix())}
|
MinChunkNumPerBatch: 1,
|
||||||
blockWithLongData := orm.BlockTrace{Hash: "blockWithLongData", Number: 5, GasUsed: 500, TxNum: 1, BlockTimestamp: uint64(time.Now().Unix())}
|
BatchTimeoutSec: 300,
|
||||||
|
}, db)
|
||||||
|
bp.TryProposeBatch()
|
||||||
|
|
||||||
testCases := []struct {
|
chunkOrm := orm.NewChunk(db)
|
||||||
description string
|
chunks, err := chunkOrm.GetUnbatchedChunks(context.Background())
|
||||||
blocks []orm.BlockTrace
|
assert.NoError(t, err)
|
||||||
expectedRes bool
|
assert.Empty(t, chunks)
|
||||||
}{
|
|
||||||
{"Empty block list", []orm.BlockTrace{}, false},
|
|
||||||
{"Single block exceeding gas threshold", []orm.BlockTrace{block4}, true},
|
|
||||||
{"Single block exceeding transaction number threshold", []orm.BlockTrace{block3}, true},
|
|
||||||
{"Multiple blocks meeting thresholds", []orm.BlockTrace{block1, block2, block3}, true},
|
|
||||||
{"Multiple blocks not meeting thresholds", []orm.BlockTrace{block1, block2}, false},
|
|
||||||
{"Outdated and valid block", []orm.BlockTrace{blockOutdated, block2}, true},
|
|
||||||
{"Single block with long data", []orm.BlockTrace{blockWithLongData}, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range testCases {
|
batchOrm := orm.NewBatch(db)
|
||||||
t.Run(tc.description, func(t *testing.T) {
|
// get all batches.
|
||||||
assert.Equal(t, tc.expectedRes, p.proposeBatch(tc.blocks), "Failed on: %s", tc.description)
|
batches, err := batchOrm.GetBatches(context.Background(), map[string]interface{}{}, []string{}, 0)
|
||||||
})
|
assert.NoError(t, err)
|
||||||
}
|
assert.Len(t, batches, 1)
|
||||||
}
|
assert.Equal(t, uint64(0), batches[0].StartChunkIndex)
|
||||||
|
assert.Equal(t, uint64(0), batches[0].EndChunkIndex)
|
||||||
func testBatchProposerBatchGeneration(t *testing.T) {
|
assert.Equal(t, types.RollupPending, types.RollupStatus(batches[0].RollupStatus))
|
||||||
db := setupDB(t)
|
assert.Equal(t, types.ProvingTaskUnassigned, types.ProvingStatus(batches[0].ProvingStatus))
|
||||||
subCtx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer func() {
|
dbChunks, err := chunkOrm.GetChunksInRange(context.Background(), 0, 0)
|
||||||
bridgeUtils.CloseDB(db)
|
assert.NoError(t, err)
|
||||||
cancel()
|
assert.Len(t, batches, 1)
|
||||||
}()
|
assert.Equal(t, batches[0].Hash, dbChunks[0].BatchHash)
|
||||||
blockTraceOrm := orm.NewBlockTrace(db)
|
assert.Equal(t, types.ProvingTaskUnassigned, types.ProvingStatus(dbChunks[0].ProvingStatus))
|
||||||
// Insert traces into db.
|
|
||||||
assert.NoError(t, blockTraceOrm.InsertWrappedBlocks([]*bridgeTypes.WrappedBlock{wrappedBlock1}))
|
blockOrm := orm.NewL2Block(db)
|
||||||
|
blocks, err := blockOrm.GetL2Blocks(context.Background(), map[string]interface{}{}, []string{}, 0)
|
||||||
l2cfg := cfg.L2Config
|
assert.NoError(t, err)
|
||||||
wc := NewL2WatcherClient(context.Background(), l2Cli, l2cfg.Confirmations, l2cfg.L2MessengerAddress, l2cfg.L2MessageQueueAddress, l2cfg.WithdrawTrieRootSlot, db)
|
assert.Len(t, blocks, 2)
|
||||||
loopToFetchEvent(subCtx, wc)
|
assert.Equal(t, dbChunks[0].Hash, blocks[0].ChunkHash)
|
||||||
|
assert.Equal(t, dbChunks[0].Hash, blocks[1].ChunkHash)
|
||||||
blockBatchOrm := orm.NewBlockBatch(db)
|
|
||||||
batch, err := blockBatchOrm.GetLatestBatch()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
// Create a new batch.
|
|
||||||
batchData := bridgeTypes.NewBatchData(&bridgeTypes.BatchInfo{
|
|
||||||
Index: 0,
|
|
||||||
Hash: batch.Hash,
|
|
||||||
StateRoot: batch.StateRoot,
|
|
||||||
}, []*bridgeTypes.WrappedBlock{wrappedBlock1}, nil)
|
|
||||||
|
|
||||||
relayer, err := relayer.NewLayer2Relayer(context.Background(), l2Cli, db, cfg.L2Config.RelayerConfig)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
proposer := NewBatchProposer(context.Background(), &config.BatchProposerConfig{
|
|
||||||
ProofGenerationFreq: 1,
|
|
||||||
BatchGasThreshold: 3000000,
|
|
||||||
BatchTxNumThreshold: 135,
|
|
||||||
BatchTimeSec: 1,
|
|
||||||
BatchBlocksLimit: 100,
|
|
||||||
CommitTxBatchCountLimit: 30,
|
|
||||||
}, relayer, db)
|
|
||||||
proposer.TryProposeBatch()
|
|
||||||
|
|
||||||
infos, err := blockTraceOrm.GetUnbatchedL2Blocks(map[string]interface{}{}, []string{"number ASC"}, 100)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, 0, len(infos))
|
|
||||||
|
|
||||||
batches, err := blockBatchOrm.GetBlockBatches(map[string]interface{}{"hash": batchData.Hash().Hex()}, nil, 1)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, 1, len(batches))
|
|
||||||
}
|
|
||||||
|
|
||||||
func testBatchProposerGracefulRestart(t *testing.T) {
|
|
||||||
db := setupDB(t)
|
|
||||||
defer bridgeUtils.CloseDB(db)
|
|
||||||
|
|
||||||
relayer, err := relayer.NewLayer2Relayer(context.Background(), l2Cli, db, cfg.L2Config.RelayerConfig)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
blockTraceOrm := orm.NewBlockTrace(db)
|
|
||||||
// Insert traces into db.
|
|
||||||
assert.NoError(t, blockTraceOrm.InsertWrappedBlocks([]*bridgeTypes.WrappedBlock{wrappedBlock2}))
|
|
||||||
|
|
||||||
// Insert block batch into db.
|
|
||||||
parentBatch1 := &bridgeTypes.BatchInfo{
|
|
||||||
Index: 0,
|
|
||||||
Hash: common.Hash{}.String(),
|
|
||||||
StateRoot: common.Hash{}.String(),
|
|
||||||
}
|
|
||||||
batchData1 := bridgeTypes.NewBatchData(parentBatch1, []*bridgeTypes.WrappedBlock{wrappedBlock1}, nil)
|
|
||||||
|
|
||||||
parentBatch2 := &bridgeTypes.BatchInfo{
|
|
||||||
Index: batchData1.Batch.BatchIndex,
|
|
||||||
Hash: batchData1.Hash().Hex(),
|
|
||||||
StateRoot: batchData1.Batch.NewStateRoot.String(),
|
|
||||||
}
|
|
||||||
batchData2 := bridgeTypes.NewBatchData(parentBatch2, []*bridgeTypes.WrappedBlock{wrappedBlock2}, nil)
|
|
||||||
|
|
||||||
blockBatchOrm := orm.NewBlockBatch(db)
|
|
||||||
err = db.Transaction(func(tx *gorm.DB) error {
|
|
||||||
_, dbTxErr := blockBatchOrm.InsertBlockBatchByBatchData(tx, batchData1)
|
|
||||||
if dbTxErr != nil {
|
|
||||||
return dbTxErr
|
|
||||||
}
|
|
||||||
_, dbTxErr = blockBatchOrm.InsertBlockBatchByBatchData(tx, batchData2)
|
|
||||||
if dbTxErr != nil {
|
|
||||||
return dbTxErr
|
|
||||||
}
|
|
||||||
numbers1 := []uint64{batchData1.Batch.Blocks[0].BlockNumber}
|
|
||||||
hash1 := batchData1.Hash().Hex()
|
|
||||||
dbTxErr = blockTraceOrm.UpdateBatchHashForL2Blocks(tx, numbers1, hash1)
|
|
||||||
if dbTxErr != nil {
|
|
||||||
return dbTxErr
|
|
||||||
}
|
|
||||||
numbers2 := []uint64{batchData2.Batch.Blocks[0].BlockNumber}
|
|
||||||
hash2 := batchData2.Hash().Hex()
|
|
||||||
dbTxErr = blockTraceOrm.UpdateBatchHashForL2Blocks(tx, numbers2, hash2)
|
|
||||||
if dbTxErr != nil {
|
|
||||||
return dbTxErr
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
err = blockBatchOrm.UpdateRollupStatus(context.Background(), batchData1.Hash().Hex(), types.RollupFinalized)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
batchHashes, err := blockBatchOrm.GetBlockBatchesHashByRollupStatus(types.RollupPending, math.MaxInt32)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, 1, len(batchHashes))
|
|
||||||
assert.Equal(t, batchData2.Hash().Hex(), batchHashes[0])
|
|
||||||
// test p.recoverBatchDataBuffer().
|
|
||||||
_ = NewBatchProposer(context.Background(), &config.BatchProposerConfig{
|
|
||||||
ProofGenerationFreq: 1,
|
|
||||||
BatchGasThreshold: 3000000,
|
|
||||||
BatchTxNumThreshold: 135,
|
|
||||||
BatchTimeSec: 1,
|
|
||||||
BatchBlocksLimit: 100,
|
|
||||||
CommitTxBatchCountLimit: 30,
|
|
||||||
}, relayer, db)
|
|
||||||
|
|
||||||
batchHashes, err = blockBatchOrm.GetBlockBatchesHashByRollupStatus(types.RollupPending, math.MaxInt32)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, 0, len(batchHashes))
|
|
||||||
|
|
||||||
batches, err := blockBatchOrm.GetBlockBatches(map[string]interface{}{"hash": batchData2.Hash().Hex()}, nil, 1)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, 1, len(batches))
|
|
||||||
}
|
}
|
||||||
|
|||||||
169
bridge/internal/controller/watcher/chunk_proposer.go
Normal file
169
bridge/internal/controller/watcher/chunk_proposer.go
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
package watcher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/scroll-tech/go-ethereum/log"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
|
||||||
|
"scroll-tech/common/types"
|
||||||
|
|
||||||
|
"scroll-tech/bridge/internal/config"
|
||||||
|
"scroll-tech/bridge/internal/orm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ChunkProposer proposes chunks based on available unchunked blocks.
|
||||||
|
type ChunkProposer struct {
|
||||||
|
ctx context.Context
|
||||||
|
db *gorm.DB
|
||||||
|
|
||||||
|
chunkOrm *orm.Chunk
|
||||||
|
l2BlockOrm *orm.L2Block
|
||||||
|
|
||||||
|
maxTxGasPerChunk uint64
|
||||||
|
maxL2TxNumPerChunk uint64
|
||||||
|
maxL1CommitGasPerChunk uint64
|
||||||
|
maxL1CommitCalldataSizePerChunk uint64
|
||||||
|
minL1CommitCalldataSizePerChunk uint64
|
||||||
|
chunkTimeoutSec uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewChunkProposer creates a new ChunkProposer instance.
|
||||||
|
func NewChunkProposer(ctx context.Context, cfg *config.ChunkProposerConfig, db *gorm.DB) *ChunkProposer {
|
||||||
|
return &ChunkProposer{
|
||||||
|
ctx: ctx,
|
||||||
|
db: db,
|
||||||
|
chunkOrm: orm.NewChunk(db),
|
||||||
|
l2BlockOrm: orm.NewL2Block(db),
|
||||||
|
maxTxGasPerChunk: cfg.MaxTxGasPerChunk,
|
||||||
|
maxL2TxNumPerChunk: cfg.MaxL2TxNumPerChunk,
|
||||||
|
maxL1CommitGasPerChunk: cfg.MaxL1CommitGasPerChunk,
|
||||||
|
maxL1CommitCalldataSizePerChunk: cfg.MaxL1CommitCalldataSizePerChunk,
|
||||||
|
minL1CommitCalldataSizePerChunk: cfg.MinL1CommitCalldataSizePerChunk,
|
||||||
|
chunkTimeoutSec: cfg.ChunkTimeoutSec,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryProposeChunk tries to propose a new chunk.
|
||||||
|
func (p *ChunkProposer) TryProposeChunk() {
|
||||||
|
proposedChunk, err := p.proposeChunk()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("propose new chunk failed", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.updateChunkInfoInDB(proposedChunk); err != nil {
|
||||||
|
log.Error("update chunk info in orm failed", "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ChunkProposer) updateChunkInfoInDB(chunk *types.Chunk) error {
|
||||||
|
if chunk == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := p.db.Transaction(func(dbTX *gorm.DB) error {
|
||||||
|
dbChunk, err := p.chunkOrm.InsertChunk(p.ctx, chunk, dbTX)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := p.l2BlockOrm.UpdateChunkHashInRange(p.ctx, dbChunk.StartBlockNumber, dbChunk.EndBlockNumber, dbChunk.Hash, dbTX); err != nil {
|
||||||
|
log.Error("failed to update chunk_hash for l2_blocks", "chunk hash", chunk.Hash, "start block", 0, "end block", 0, "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ChunkProposer) proposeChunk() (*types.Chunk, error) {
|
||||||
|
blocks, err := p.l2BlockOrm.GetUnchunkedBlocks(p.ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(blocks) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
firstBlock := blocks[0]
|
||||||
|
totalTxGasUsed := firstBlock.Header.GasUsed
|
||||||
|
totalL2TxNum := firstBlock.L2TxsNum()
|
||||||
|
totalL1CommitCalldataSize := firstBlock.EstimateL1CommitCalldataSize()
|
||||||
|
totalL1CommitGas := firstBlock.EstimateL1CommitGas()
|
||||||
|
|
||||||
|
// Check if the first block breaks hard limits.
|
||||||
|
// If so, it indicates there are bugs in sequencer, manual fix is needed.
|
||||||
|
if totalL2TxNum > p.maxL2TxNumPerChunk {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"the first block exceeds l2 tx number limit; block number: %v, number of transactions: %v, max transaction number limit: %v",
|
||||||
|
firstBlock.Header.Number,
|
||||||
|
totalL2TxNum,
|
||||||
|
p.maxL2TxNumPerChunk,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if totalL1CommitGas > p.maxL1CommitGasPerChunk {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"the first block exceeds l1 commit gas limit; block number: %v, commit gas: %v, max commit gas limit: %v",
|
||||||
|
firstBlock.Header.Number,
|
||||||
|
totalL1CommitGas,
|
||||||
|
p.maxL1CommitGasPerChunk,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if totalL1CommitCalldataSize > p.maxL1CommitCalldataSizePerChunk {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"the first block exceeds l1 commit calldata size limit; block number: %v, calldata size: %v, max calldata size limit: %v",
|
||||||
|
firstBlock.Header.Number,
|
||||||
|
totalL1CommitCalldataSize,
|
||||||
|
p.maxL1CommitCalldataSizePerChunk,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the first block breaks any soft limits.
|
||||||
|
if totalTxGasUsed > p.maxTxGasPerChunk {
|
||||||
|
log.Warn(
|
||||||
|
"The first block in chunk exceeds l2 tx gas limit",
|
||||||
|
"block number", firstBlock.Header.Number,
|
||||||
|
"gas used", totalTxGasUsed,
|
||||||
|
"max gas limit", p.maxTxGasPerChunk,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, block := range blocks[1:] {
|
||||||
|
totalTxGasUsed += block.Header.GasUsed
|
||||||
|
totalL2TxNum += block.L2TxsNum()
|
||||||
|
totalL1CommitCalldataSize += block.EstimateL1CommitCalldataSize()
|
||||||
|
totalL1CommitGas += block.EstimateL1CommitGas()
|
||||||
|
if totalTxGasUsed > p.maxTxGasPerChunk ||
|
||||||
|
totalL2TxNum > p.maxL2TxNumPerChunk ||
|
||||||
|
totalL1CommitCalldataSize > p.maxL1CommitCalldataSizePerChunk ||
|
||||||
|
totalL1CommitGas > p.maxL1CommitGasPerChunk {
|
||||||
|
blocks = blocks[:i+1]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasBlockTimeout bool
|
||||||
|
currentTimeSec := uint64(time.Now().Unix())
|
||||||
|
if blocks[0].Header.Time+p.chunkTimeoutSec < currentTimeSec {
|
||||||
|
log.Warn("first block timeout",
|
||||||
|
"block number", blocks[0].Header.Number,
|
||||||
|
"block timestamp", blocks[0].Header.Time,
|
||||||
|
"block outdated time threshold", currentTimeSec,
|
||||||
|
)
|
||||||
|
hasBlockTimeout = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasBlockTimeout && totalL1CommitCalldataSize < p.minL1CommitCalldataSizePerChunk {
|
||||||
|
log.Warn("The calldata size of the chunk is less than the minimum limit",
|
||||||
|
"totalL1CommitCalldataSize", totalL1CommitCalldataSize,
|
||||||
|
"minL1CommitCalldataSizePerChunk", p.minL1CommitCalldataSizePerChunk,
|
||||||
|
)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return &types.Chunk{Blocks: blocks}, nil
|
||||||
|
}
|
||||||
46
bridge/internal/controller/watcher/chunk_proposer_test.go
Normal file
46
bridge/internal/controller/watcher/chunk_proposer_test.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package watcher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"scroll-tech/common/database"
|
||||||
|
"scroll-tech/common/types"
|
||||||
|
|
||||||
|
"scroll-tech/bridge/internal/config"
|
||||||
|
"scroll-tech/bridge/internal/orm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: Add unit tests that the limits are enforced correctly.
|
||||||
|
func testChunkProposer(t *testing.T) {
|
||||||
|
db := setupDB(t)
|
||||||
|
defer database.CloseDB(db)
|
||||||
|
|
||||||
|
l2BlockOrm := orm.NewL2Block(db)
|
||||||
|
err := l2BlockOrm.InsertL2Blocks(context.Background(), []*types.WrappedBlock{wrappedBlock1, wrappedBlock2})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
cp := NewChunkProposer(context.Background(), &config.ChunkProposerConfig{
|
||||||
|
MaxTxGasPerChunk: 1000000000,
|
||||||
|
MaxL2TxNumPerChunk: 10000,
|
||||||
|
MaxL1CommitGasPerChunk: 50000000000,
|
||||||
|
MaxL1CommitCalldataSizePerChunk: 1000000,
|
||||||
|
MinL1CommitCalldataSizePerChunk: 0,
|
||||||
|
ChunkTimeoutSec: 300,
|
||||||
|
}, db)
|
||||||
|
cp.TryProposeChunk()
|
||||||
|
|
||||||
|
expectedChunk := &types.Chunk{
|
||||||
|
Blocks: []*types.WrappedBlock{wrappedBlock1, wrappedBlock2},
|
||||||
|
}
|
||||||
|
expectedHash, err := expectedChunk.Hash(0)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
chunkOrm := orm.NewChunk(db)
|
||||||
|
chunks, err := chunkOrm.GetUnbatchedChunks(context.Background())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, chunks, 1)
|
||||||
|
assert.Equal(t, expectedHash.Hex(), chunks[0].Hash)
|
||||||
|
}
|
||||||
@@ -24,10 +24,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
bridgeL1MsgsSyncHeightGauge = gethMetrics.NewRegisteredGauge("bridge/l1/msgs/sync/height", metrics.ScrollRegistry)
|
bridgeL1MsgsSyncHeightGauge = gethMetrics.NewRegisteredGauge("bridge/l1/msgs/sync/height", metrics.ScrollRegistry)
|
||||||
bridgeL1MsgsSentEventsTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l1/msgs/sent/events/total", metrics.ScrollRegistry)
|
bridgeL1MsgsSentEventsTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l1/msgs/sent/events/total", metrics.ScrollRegistry)
|
||||||
bridgeL1MsgsRelayedEventsTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l1/msgs/relayed/events/total", metrics.ScrollRegistry)
|
bridgeL1MsgsRollupEventsTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l1/msgs/rollup/events/total", metrics.ScrollRegistry)
|
||||||
bridgeL1MsgsRollupEventsTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l1/msgs/rollup/events/total", metrics.ScrollRegistry)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type rollupEvent struct {
|
type rollupEvent struct {
|
||||||
@@ -41,9 +40,8 @@ type L1WatcherClient struct {
|
|||||||
ctx context.Context
|
ctx context.Context
|
||||||
client *ethclient.Client
|
client *ethclient.Client
|
||||||
l1MessageOrm *orm.L1Message
|
l1MessageOrm *orm.L1Message
|
||||||
l2MessageOrm *orm.L2Message
|
|
||||||
l1BlockOrm *orm.L1Block
|
l1BlockOrm *orm.L1Block
|
||||||
l1BatchOrm *orm.BlockBatch
|
batchOrm *orm.Batch
|
||||||
|
|
||||||
// The number of new blocks to wait for a block to be confirmed
|
// The number of new blocks to wait for a block to be confirmed
|
||||||
confirmations rpc.BlockNumber
|
confirmations rpc.BlockNumber
|
||||||
@@ -76,7 +74,7 @@ func NewL1WatcherClient(ctx context.Context, client *ethclient.Client, startHeig
|
|||||||
}
|
}
|
||||||
|
|
||||||
l1BlockOrm := orm.NewL1Block(db)
|
l1BlockOrm := orm.NewL1Block(db)
|
||||||
savedL1BlockHeight, err := l1BlockOrm.GetLatestL1BlockHeight()
|
savedL1BlockHeight, err := l1BlockOrm.GetLatestL1BlockHeight(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("Failed to fetch latest L1 block height from db", "err", err)
|
log.Warn("Failed to fetch latest L1 block height from db", "err", err)
|
||||||
savedL1BlockHeight = 0
|
savedL1BlockHeight = 0
|
||||||
@@ -90,8 +88,7 @@ func NewL1WatcherClient(ctx context.Context, client *ethclient.Client, startHeig
|
|||||||
client: client,
|
client: client,
|
||||||
l1MessageOrm: l1MessageOrm,
|
l1MessageOrm: l1MessageOrm,
|
||||||
l1BlockOrm: l1BlockOrm,
|
l1BlockOrm: l1BlockOrm,
|
||||||
l1BatchOrm: orm.NewBlockBatch(db),
|
batchOrm: orm.NewBatch(db),
|
||||||
l2MessageOrm: orm.NewL2Message(db),
|
|
||||||
confirmations: confirmations,
|
confirmations: confirmations,
|
||||||
|
|
||||||
messengerAddress: messengerAddress,
|
messengerAddress: messengerAddress,
|
||||||
@@ -152,9 +149,10 @@ func (w *L1WatcherClient) FetchBlockHeader(blockHeight uint64) error {
|
|||||||
baseFee = block.BaseFee.Uint64()
|
baseFee = block.BaseFee.Uint64()
|
||||||
}
|
}
|
||||||
blocks = append(blocks, orm.L1Block{
|
blocks = append(blocks, orm.L1Block{
|
||||||
Number: uint64(height),
|
Number: uint64(height),
|
||||||
Hash: block.Hash().String(),
|
Hash: block.Hash().String(),
|
||||||
BaseFee: baseFee,
|
BaseFee: baseFee,
|
||||||
|
GasOracleStatus: int16(types.GasOraclePending),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,25 +225,23 @@ func (w *L1WatcherClient) FetchContractEvent() error {
|
|||||||
}
|
}
|
||||||
log.Info("Received new L1 events", "fromBlock", from, "toBlock", to, "cnt", len(logs))
|
log.Info("Received new L1 events", "fromBlock", from, "toBlock", to, "cnt", len(logs))
|
||||||
|
|
||||||
sentMessageEvents, relayedMessageEvents, rollupEvents, err := w.parseBridgeEventLogs(logs)
|
sentMessageEvents, rollupEvents, err := w.parseBridgeEventLogs(logs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Failed to parse emitted events log", "err", err)
|
log.Error("Failed to parse emitted events log", "err", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
sentMessageCount := int64(len(sentMessageEvents))
|
sentMessageCount := int64(len(sentMessageEvents))
|
||||||
relayedMessageCount := int64(len(relayedMessageEvents))
|
|
||||||
rollupEventCount := int64(len(rollupEvents))
|
rollupEventCount := int64(len(rollupEvents))
|
||||||
bridgeL1MsgsSentEventsTotalCounter.Inc(sentMessageCount)
|
bridgeL1MsgsSentEventsTotalCounter.Inc(sentMessageCount)
|
||||||
bridgeL1MsgsRelayedEventsTotalCounter.Inc(relayedMessageCount)
|
|
||||||
bridgeL1MsgsRollupEventsTotalCounter.Inc(rollupEventCount)
|
bridgeL1MsgsRollupEventsTotalCounter.Inc(rollupEventCount)
|
||||||
log.Info("L1 events types", "SentMessageCount", sentMessageCount, "RelayedMessageCount", relayedMessageCount, "RollupEventCount", rollupEventCount)
|
log.Info("L1 events types", "SentMessageCount", sentMessageCount, "RollupEventCount", rollupEventCount)
|
||||||
|
|
||||||
// use rollup event to update rollup results db status
|
// use rollup event to update rollup results db status
|
||||||
var batchHashes []string
|
var batchHashes []string
|
||||||
for _, event := range rollupEvents {
|
for _, event := range rollupEvents {
|
||||||
batchHashes = append(batchHashes, event.batchHash.String())
|
batchHashes = append(batchHashes, event.batchHash.String())
|
||||||
}
|
}
|
||||||
statuses, err := w.l1BatchOrm.GetRollupStatusByHashList(batchHashes)
|
statuses, err := w.batchOrm.GetRollupStatusByHashList(w.ctx, batchHashes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Failed to GetRollupStatusByHashList", "err", err)
|
log.Error("Failed to GetRollupStatusByHashList", "err", err)
|
||||||
return err
|
return err
|
||||||
@@ -261,9 +257,9 @@ func (w *L1WatcherClient) FetchContractEvent() error {
|
|||||||
// only update when db status is before event status
|
// only update when db status is before event status
|
||||||
if event.status > status {
|
if event.status > status {
|
||||||
if event.status == types.RollupFinalized {
|
if event.status == types.RollupFinalized {
|
||||||
err = w.l1BatchOrm.UpdateFinalizeTxHashAndRollupStatus(w.ctx, batchHash, event.txHash.String(), event.status)
|
err = w.batchOrm.UpdateFinalizeTxHashAndRollupStatus(w.ctx, batchHash, event.txHash.String(), event.status)
|
||||||
} else if event.status == types.RollupCommitted {
|
} else if event.status == types.RollupCommitted {
|
||||||
err = w.l1BatchOrm.UpdateCommitTxHashAndRollupStatus(w.ctx, batchHash, event.txHash.String(), event.status)
|
err = w.batchOrm.UpdateCommitTxHashAndRollupStatus(w.ctx, batchHash, event.txHash.String(), event.status)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Failed to update Rollup/Finalize TxHash and Status", "err", err)
|
log.Error("Failed to update Rollup/Finalize TxHash and Status", "err", err)
|
||||||
@@ -272,21 +268,6 @@ func (w *L1WatcherClient) FetchContractEvent() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update relayed message first to make sure we don't forget to update submitted message.
|
|
||||||
// Since, we always start sync from the latest unprocessed message.
|
|
||||||
for _, msg := range relayedMessageEvents {
|
|
||||||
var msgStatus types.MsgStatus
|
|
||||||
if msg.isSuccessful {
|
|
||||||
msgStatus = types.MsgConfirmed
|
|
||||||
} else {
|
|
||||||
msgStatus = types.MsgFailed
|
|
||||||
}
|
|
||||||
if err = w.l2MessageOrm.UpdateLayer2StatusAndLayer1Hash(w.ctx, msg.msgHash.String(), msgStatus, msg.txHash.String()); err != nil {
|
|
||||||
log.Error("Failed to update layer1 status and layer2 hash", "err", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = w.l1MessageOrm.SaveL1Messages(w.ctx, sentMessageEvents); err != nil {
|
if err = w.l1MessageOrm.SaveL1Messages(w.ctx, sentMessageEvents); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -298,11 +279,10 @@ func (w *L1WatcherClient) FetchContractEvent() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *L1WatcherClient) parseBridgeEventLogs(logs []gethTypes.Log) ([]*orm.L1Message, []relayedMessage, []rollupEvent, error) {
|
func (w *L1WatcherClient) parseBridgeEventLogs(logs []gethTypes.Log) ([]*orm.L1Message, []rollupEvent, error) {
|
||||||
// Need use contract abi to parse event Log
|
// Need use contract abi to parse event Log
|
||||||
// Can only be tested after we have our contracts set up
|
// Can only be tested after we have our contracts set up
|
||||||
var l1Messages []*orm.L1Message
|
var l1Messages []*orm.L1Message
|
||||||
var relayedMessages []relayedMessage
|
|
||||||
var rollupEvents []rollupEvent
|
var rollupEvents []rollupEvent
|
||||||
for _, vLog := range logs {
|
for _, vLog := range logs {
|
||||||
switch vLog.Topics[0] {
|
switch vLog.Topics[0] {
|
||||||
@@ -311,13 +291,13 @@ func (w *L1WatcherClient) parseBridgeEventLogs(logs []gethTypes.Log) ([]*orm.L1M
|
|||||||
err := utils.UnpackLog(w.messageQueueABI, &event, "QueueTransaction", vLog)
|
err := utils.UnpackLog(w.messageQueueABI, &event, "QueueTransaction", vLog)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("Failed to unpack layer1 QueueTransaction event", "err", err)
|
log.Warn("Failed to unpack layer1 QueueTransaction event", "err", err)
|
||||||
return l1Messages, relayedMessages, rollupEvents, err
|
return l1Messages, rollupEvents, err
|
||||||
}
|
}
|
||||||
|
|
||||||
msgHash := common.BytesToHash(crypto.Keccak256(event.Data))
|
msgHash := common.BytesToHash(crypto.Keccak256(event.Data))
|
||||||
|
|
||||||
l1Messages = append(l1Messages, &orm.L1Message{
|
l1Messages = append(l1Messages, &orm.L1Message{
|
||||||
QueueIndex: event.QueueIndex.Uint64(),
|
QueueIndex: event.QueueIndex,
|
||||||
MsgHash: msgHash.String(),
|
MsgHash: msgHash.String(),
|
||||||
Height: vLog.BlockNumber,
|
Height: vLog.BlockNumber,
|
||||||
Sender: event.Sender.String(),
|
Sender: event.Sender.String(),
|
||||||
@@ -327,38 +307,12 @@ func (w *L1WatcherClient) parseBridgeEventLogs(logs []gethTypes.Log) ([]*orm.L1M
|
|||||||
GasLimit: event.GasLimit.Uint64(),
|
GasLimit: event.GasLimit.Uint64(),
|
||||||
Layer1Hash: vLog.TxHash.Hex(),
|
Layer1Hash: vLog.TxHash.Hex(),
|
||||||
})
|
})
|
||||||
case bridgeAbi.L1RelayedMessageEventSignature:
|
|
||||||
event := bridgeAbi.L1RelayedMessageEvent{}
|
|
||||||
err := utils.UnpackLog(w.messengerABI, &event, "RelayedMessage", vLog)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Failed to unpack layer1 RelayedMessage event", "err", err)
|
|
||||||
return l1Messages, relayedMessages, rollupEvents, err
|
|
||||||
}
|
|
||||||
|
|
||||||
relayedMessages = append(relayedMessages, relayedMessage{
|
|
||||||
msgHash: event.MessageHash,
|
|
||||||
txHash: vLog.TxHash,
|
|
||||||
isSuccessful: true,
|
|
||||||
})
|
|
||||||
case bridgeAbi.L1FailedRelayedMessageEventSignature:
|
|
||||||
event := bridgeAbi.L1FailedRelayedMessageEvent{}
|
|
||||||
err := utils.UnpackLog(w.messengerABI, &event, "FailedRelayedMessage", vLog)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Failed to unpack layer1 FailedRelayedMessage event", "err", err)
|
|
||||||
return l1Messages, relayedMessages, rollupEvents, err
|
|
||||||
}
|
|
||||||
|
|
||||||
relayedMessages = append(relayedMessages, relayedMessage{
|
|
||||||
msgHash: event.MessageHash,
|
|
||||||
txHash: vLog.TxHash,
|
|
||||||
isSuccessful: false,
|
|
||||||
})
|
|
||||||
case bridgeAbi.L1CommitBatchEventSignature:
|
case bridgeAbi.L1CommitBatchEventSignature:
|
||||||
event := bridgeAbi.L1CommitBatchEvent{}
|
event := bridgeAbi.L1CommitBatchEvent{}
|
||||||
err := utils.UnpackLog(w.scrollChainABI, &event, "CommitBatch", vLog)
|
err := utils.UnpackLog(w.scrollChainABI, &event, "CommitBatch", vLog)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("Failed to unpack layer1 CommitBatch event", "err", err)
|
log.Warn("Failed to unpack layer1 CommitBatch event", "err", err)
|
||||||
return l1Messages, relayedMessages, rollupEvents, err
|
return l1Messages, rollupEvents, err
|
||||||
}
|
}
|
||||||
|
|
||||||
rollupEvents = append(rollupEvents, rollupEvent{
|
rollupEvents = append(rollupEvents, rollupEvent{
|
||||||
@@ -371,7 +325,7 @@ func (w *L1WatcherClient) parseBridgeEventLogs(logs []gethTypes.Log) ([]*orm.L1M
|
|||||||
err := utils.UnpackLog(w.scrollChainABI, &event, "FinalizeBatch", vLog)
|
err := utils.UnpackLog(w.scrollChainABI, &event, "FinalizeBatch", vLog)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("Failed to unpack layer1 FinalizeBatch event", "err", err)
|
log.Warn("Failed to unpack layer1 FinalizeBatch event", "err", err)
|
||||||
return l1Messages, relayedMessages, rollupEvents, err
|
return l1Messages, rollupEvents, err
|
||||||
}
|
}
|
||||||
|
|
||||||
rollupEvents = append(rollupEvents, rollupEvent{
|
rollupEvents = append(rollupEvents, rollupEvent{
|
||||||
@@ -384,5 +338,5 @@ func (w *L1WatcherClient) parseBridgeEventLogs(logs []gethTypes.Log) ([]*orm.L1M
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return l1Messages, relayedMessages, rollupEvents, nil
|
return l1Messages, rollupEvents, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
|
"scroll-tech/common/database"
|
||||||
commonTypes "scroll-tech/common/types"
|
commonTypes "scroll-tech/common/types"
|
||||||
|
|
||||||
bridgeAbi "scroll-tech/bridge/abi"
|
bridgeAbi "scroll-tech/bridge/abi"
|
||||||
@@ -36,13 +37,13 @@ func setupL1Watcher(t *testing.T) (*L1WatcherClient, *gorm.DB) {
|
|||||||
|
|
||||||
func testFetchContractEvent(t *testing.T) {
|
func testFetchContractEvent(t *testing.T) {
|
||||||
watcher, db := setupL1Watcher(t)
|
watcher, db := setupL1Watcher(t)
|
||||||
defer utils.CloseDB(db)
|
defer database.CloseDB(db)
|
||||||
assert.NoError(t, watcher.FetchContractEvent())
|
assert.NoError(t, watcher.FetchContractEvent())
|
||||||
}
|
}
|
||||||
|
|
||||||
func testL1WatcherClientFetchBlockHeader(t *testing.T) {
|
func testL1WatcherClientFetchBlockHeader(t *testing.T) {
|
||||||
watcher, db := setupL1Watcher(t)
|
watcher, db := setupL1Watcher(t)
|
||||||
defer utils.CloseDB(db)
|
defer database.CloseDB(db)
|
||||||
convey.Convey("test toBlock < fromBlock", t, func() {
|
convey.Convey("test toBlock < fromBlock", t, func() {
|
||||||
var blockHeight uint64
|
var blockHeight uint64
|
||||||
if watcher.ProcessedBlockHeight() <= 0 {
|
if watcher.ProcessedBlockHeight() <= 0 {
|
||||||
@@ -114,7 +115,7 @@ func testL1WatcherClientFetchBlockHeader(t *testing.T) {
|
|||||||
|
|
||||||
func testL1WatcherClientFetchContractEvent(t *testing.T) {
|
func testL1WatcherClientFetchContractEvent(t *testing.T) {
|
||||||
watcher, db := setupL1Watcher(t)
|
watcher, db := setupL1Watcher(t)
|
||||||
defer utils.CloseDB(db)
|
defer database.CloseDB(db)
|
||||||
|
|
||||||
watcher.SetConfirmations(rpc.SafeBlockNumber)
|
watcher.SetConfirmations(rpc.SafeBlockNumber)
|
||||||
convey.Convey("get latest confirmed block number failure", t, func() {
|
convey.Convey("get latest confirmed block number failure", t, func() {
|
||||||
@@ -159,14 +160,14 @@ func testL1WatcherClientFetchContractEvent(t *testing.T) {
|
|||||||
|
|
||||||
convey.Convey("parse bridge event logs failure", t, func() {
|
convey.Convey("parse bridge event logs failure", t, func() {
|
||||||
targetErr := errors.New("parse log failure")
|
targetErr := errors.New("parse log failure")
|
||||||
patchGuard.ApplyPrivateMethod(watcher, "parseBridgeEventLogs", func(*L1WatcherClient, []types.Log) ([]*orm.L1Message, []relayedMessage, []rollupEvent, error) {
|
patchGuard.ApplyPrivateMethod(watcher, "parseBridgeEventLogs", func(*L1WatcherClient, []types.Log) ([]*orm.L1Message, []rollupEvent, error) {
|
||||||
return nil, nil, nil, targetErr
|
return nil, nil, targetErr
|
||||||
})
|
})
|
||||||
err := watcher.FetchContractEvent()
|
err := watcher.FetchContractEvent()
|
||||||
assert.Equal(t, err.Error(), targetErr.Error())
|
assert.EqualError(t, err, targetErr.Error())
|
||||||
})
|
})
|
||||||
|
|
||||||
patchGuard.ApplyPrivateMethod(watcher, "parseBridgeEventLogs", func(*L1WatcherClient, []types.Log) ([]*orm.L1Message, []relayedMessage, []rollupEvent, error) {
|
patchGuard.ApplyPrivateMethod(watcher, "parseBridgeEventLogs", func(*L1WatcherClient, []types.Log) ([]*orm.L1Message, []rollupEvent, error) {
|
||||||
rollupEvents := []rollupEvent{
|
rollupEvents := []rollupEvent{
|
||||||
{
|
{
|
||||||
batchHash: common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"),
|
batchHash: common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"),
|
||||||
@@ -179,26 +180,13 @@ func testL1WatcherClientFetchContractEvent(t *testing.T) {
|
|||||||
status: commonTypes.RollupCommitted,
|
status: commonTypes.RollupCommitted,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
return nil, rollupEvents, nil
|
||||||
relayedMessageEvents := []relayedMessage{
|
|
||||||
{
|
|
||||||
msgHash: common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"),
|
|
||||||
txHash: common.HexToHash("0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5"),
|
|
||||||
isSuccessful: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
msgHash: common.HexToHash("0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5"),
|
|
||||||
txHash: common.HexToHash("0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30"),
|
|
||||||
isSuccessful: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return nil, relayedMessageEvents, rollupEvents, nil
|
|
||||||
})
|
})
|
||||||
|
|
||||||
var blockBatchOrm *orm.BlockBatch
|
var batchOrm *orm.Batch
|
||||||
convey.Convey("db get rollup status by hash list failure", t, func() {
|
convey.Convey("db get rollup status by hash list failure", t, func() {
|
||||||
targetErr := errors.New("get db failure")
|
targetErr := errors.New("get db failure")
|
||||||
patchGuard.ApplyMethodFunc(blockBatchOrm, "GetRollupStatusByHashList", func(hashes []string) ([]commonTypes.RollupStatus, error) {
|
patchGuard.ApplyMethodFunc(batchOrm, "GetRollupStatusByHashList", func(context.Context, []string) ([]commonTypes.RollupStatus, error) {
|
||||||
return nil, targetErr
|
return nil, targetErr
|
||||||
})
|
})
|
||||||
err := watcher.FetchContractEvent()
|
err := watcher.FetchContractEvent()
|
||||||
@@ -206,7 +194,7 @@ func testL1WatcherClientFetchContractEvent(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
convey.Convey("rollup status mismatch batch hashes length", t, func() {
|
convey.Convey("rollup status mismatch batch hashes length", t, func() {
|
||||||
patchGuard.ApplyMethodFunc(blockBatchOrm, "GetRollupStatusByHashList", func(hashes []string) ([]commonTypes.RollupStatus, error) {
|
patchGuard.ApplyMethodFunc(batchOrm, "GetRollupStatusByHashList", func(context.Context, []string) ([]commonTypes.RollupStatus, error) {
|
||||||
s := []commonTypes.RollupStatus{
|
s := []commonTypes.RollupStatus{
|
||||||
commonTypes.RollupFinalized,
|
commonTypes.RollupFinalized,
|
||||||
}
|
}
|
||||||
@@ -216,7 +204,7 @@ func testL1WatcherClientFetchContractEvent(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
patchGuard.ApplyMethodFunc(blockBatchOrm, "GetRollupStatusByHashList", func(hashes []string) ([]commonTypes.RollupStatus, error) {
|
patchGuard.ApplyMethodFunc(batchOrm, "GetRollupStatusByHashList", func(context.Context, []string) ([]commonTypes.RollupStatus, error) {
|
||||||
s := []commonTypes.RollupStatus{
|
s := []commonTypes.RollupStatus{
|
||||||
commonTypes.RollupPending,
|
commonTypes.RollupPending,
|
||||||
commonTypes.RollupCommitting,
|
commonTypes.RollupCommitting,
|
||||||
@@ -226,41 +214,27 @@ func testL1WatcherClientFetchContractEvent(t *testing.T) {
|
|||||||
|
|
||||||
convey.Convey("db update RollupFinalized status failure", t, func() {
|
convey.Convey("db update RollupFinalized status failure", t, func() {
|
||||||
targetErr := errors.New("UpdateFinalizeTxHashAndRollupStatus RollupFinalized failure")
|
targetErr := errors.New("UpdateFinalizeTxHashAndRollupStatus RollupFinalized failure")
|
||||||
patchGuard.ApplyMethodFunc(blockBatchOrm, "UpdateFinalizeTxHashAndRollupStatus", func(context.Context, string, string, commonTypes.RollupStatus) error {
|
patchGuard.ApplyMethodFunc(batchOrm, "UpdateFinalizeTxHashAndRollupStatus", func(context.Context, string, string, commonTypes.RollupStatus) error {
|
||||||
return targetErr
|
return targetErr
|
||||||
})
|
})
|
||||||
err := watcher.FetchContractEvent()
|
err := watcher.FetchContractEvent()
|
||||||
assert.Equal(t, targetErr.Error(), err.Error())
|
assert.Equal(t, targetErr.Error(), err.Error())
|
||||||
})
|
})
|
||||||
|
|
||||||
patchGuard.ApplyMethodFunc(blockBatchOrm, "UpdateFinalizeTxHashAndRollupStatus", func(context.Context, string, string, commonTypes.RollupStatus) error {
|
patchGuard.ApplyMethodFunc(batchOrm, "UpdateFinalizeTxHashAndRollupStatus", func(context.Context, string, string, commonTypes.RollupStatus) error {
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
convey.Convey("db update RollupCommitted status failure", t, func() {
|
convey.Convey("db update RollupCommitted status failure", t, func() {
|
||||||
targetErr := errors.New("UpdateCommitTxHashAndRollupStatus RollupCommitted failure")
|
targetErr := errors.New("UpdateCommitTxHashAndRollupStatus RollupCommitted failure")
|
||||||
patchGuard.ApplyMethodFunc(blockBatchOrm, "UpdateCommitTxHashAndRollupStatus", func(context.Context, string, string, commonTypes.RollupStatus) error {
|
patchGuard.ApplyMethodFunc(batchOrm, "UpdateCommitTxHashAndRollupStatus", func(context.Context, string, string, commonTypes.RollupStatus) error {
|
||||||
return targetErr
|
return targetErr
|
||||||
})
|
})
|
||||||
err := watcher.FetchContractEvent()
|
err := watcher.FetchContractEvent()
|
||||||
assert.Equal(t, targetErr.Error(), err.Error())
|
assert.Equal(t, targetErr.Error(), err.Error())
|
||||||
})
|
})
|
||||||
|
|
||||||
patchGuard.ApplyMethodFunc(blockBatchOrm, "UpdateCommitTxHashAndRollupStatus", func(context.Context, string, string, commonTypes.RollupStatus) error {
|
patchGuard.ApplyMethodFunc(batchOrm, "UpdateCommitTxHashAndRollupStatus", func(context.Context, string, string, commonTypes.RollupStatus) error {
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
var l2MessageOrm *orm.L2Message
|
|
||||||
convey.Convey("db update layer2 status and layer1 hash failure", t, func() {
|
|
||||||
targetErr := errors.New("UpdateLayer2StatusAndLayer1Hash failure")
|
|
||||||
patchGuard.ApplyMethodFunc(l2MessageOrm, "UpdateLayer2StatusAndLayer1Hash", func(context.Context, string, commonTypes.MsgStatus, string) error {
|
|
||||||
return targetErr
|
|
||||||
})
|
|
||||||
err := watcher.FetchContractEvent()
|
|
||||||
assert.Equal(t, targetErr.Error(), err.Error())
|
|
||||||
})
|
|
||||||
|
|
||||||
patchGuard.ApplyMethodFunc(l2MessageOrm, "UpdateLayer2StatusAndLayer1Hash", func(context.Context, string, commonTypes.MsgStatus, string) error {
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -286,7 +260,7 @@ func testL1WatcherClientFetchContractEvent(t *testing.T) {
|
|||||||
|
|
||||||
func testParseBridgeEventLogsL1QueueTransactionEventSignature(t *testing.T) {
|
func testParseBridgeEventLogsL1QueueTransactionEventSignature(t *testing.T) {
|
||||||
watcher, db := setupL1Watcher(t)
|
watcher, db := setupL1Watcher(t)
|
||||||
defer utils.CloseDB(db)
|
defer database.CloseDB(db)
|
||||||
|
|
||||||
logs := []types.Log{
|
logs := []types.Log{
|
||||||
{
|
{
|
||||||
@@ -303,17 +277,16 @@ func testParseBridgeEventLogsL1QueueTransactionEventSignature(t *testing.T) {
|
|||||||
})
|
})
|
||||||
defer patchGuard.Reset()
|
defer patchGuard.Reset()
|
||||||
|
|
||||||
l2Messages, relayedMessages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
l2Messages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
||||||
assert.EqualError(t, err, targetErr.Error())
|
assert.EqualError(t, err, targetErr.Error())
|
||||||
assert.Empty(t, l2Messages)
|
assert.Empty(t, l2Messages)
|
||||||
assert.Empty(t, relayedMessages)
|
|
||||||
assert.Empty(t, rollupEvents)
|
assert.Empty(t, rollupEvents)
|
||||||
})
|
})
|
||||||
|
|
||||||
convey.Convey("L1QueueTransactionEventSignature success", t, func() {
|
convey.Convey("L1QueueTransactionEventSignature success", t, func() {
|
||||||
patchGuard := gomonkey.ApplyFunc(utils.UnpackLog, func(c *abi.ABI, out interface{}, event string, log types.Log) error {
|
patchGuard := gomonkey.ApplyFunc(utils.UnpackLog, func(c *abi.ABI, out interface{}, event string, log types.Log) error {
|
||||||
tmpOut := out.(*bridgeAbi.L1QueueTransactionEvent)
|
tmpOut := out.(*bridgeAbi.L1QueueTransactionEvent)
|
||||||
tmpOut.QueueIndex = big.NewInt(100)
|
tmpOut.QueueIndex = 100
|
||||||
tmpOut.Data = []byte("test data")
|
tmpOut.Data = []byte("test data")
|
||||||
tmpOut.Sender = common.HexToAddress("0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30")
|
tmpOut.Sender = common.HexToAddress("0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30")
|
||||||
tmpOut.Value = big.NewInt(1000)
|
tmpOut.Value = big.NewInt(1000)
|
||||||
@@ -323,105 +296,17 @@ func testParseBridgeEventLogsL1QueueTransactionEventSignature(t *testing.T) {
|
|||||||
})
|
})
|
||||||
defer patchGuard.Reset()
|
defer patchGuard.Reset()
|
||||||
|
|
||||||
l2Messages, relayedMessages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
l2Messages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Empty(t, relayedMessages)
|
|
||||||
assert.Empty(t, rollupEvents)
|
assert.Empty(t, rollupEvents)
|
||||||
assert.Len(t, l2Messages, 1)
|
assert.Len(t, l2Messages, 1)
|
||||||
assert.Equal(t, l2Messages[0].Value, big.NewInt(1000).String())
|
assert.Equal(t, l2Messages[0].Value, big.NewInt(1000).String())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func testParseBridgeEventLogsL1RelayedMessageEventSignature(t *testing.T) {
|
|
||||||
watcher, db := setupL1Watcher(t)
|
|
||||||
defer utils.CloseDB(db)
|
|
||||||
|
|
||||||
logs := []types.Log{
|
|
||||||
{
|
|
||||||
Topics: []common.Hash{bridgeAbi.L1RelayedMessageEventSignature},
|
|
||||||
BlockNumber: 100,
|
|
||||||
TxHash: common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
convey.Convey("unpack RelayedMessage log failure", t, func() {
|
|
||||||
targetErr := errors.New("UnpackLog RelayedMessage failure")
|
|
||||||
patchGuard := gomonkey.ApplyFunc(utils.UnpackLog, func(c *abi.ABI, out interface{}, event string, log types.Log) error {
|
|
||||||
return targetErr
|
|
||||||
})
|
|
||||||
defer patchGuard.Reset()
|
|
||||||
|
|
||||||
l2Messages, relayedMessages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
|
||||||
assert.EqualError(t, err, targetErr.Error())
|
|
||||||
assert.Empty(t, l2Messages)
|
|
||||||
assert.Empty(t, relayedMessages)
|
|
||||||
assert.Empty(t, rollupEvents)
|
|
||||||
})
|
|
||||||
|
|
||||||
convey.Convey("L1RelayedMessageEventSignature success", t, func() {
|
|
||||||
msgHash := common.HexToHash("0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5")
|
|
||||||
patchGuard := gomonkey.ApplyFunc(utils.UnpackLog, func(c *abi.ABI, out interface{}, event string, log types.Log) error {
|
|
||||||
tmpOut := out.(*bridgeAbi.L1RelayedMessageEvent)
|
|
||||||
tmpOut.MessageHash = msgHash
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
defer patchGuard.Reset()
|
|
||||||
|
|
||||||
l2Messages, relayedMessages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Empty(t, l2Messages)
|
|
||||||
assert.Empty(t, rollupEvents)
|
|
||||||
assert.Len(t, relayedMessages, 1)
|
|
||||||
assert.Equal(t, relayedMessages[0].msgHash, msgHash)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func testParseBridgeEventLogsL1FailedRelayedMessageEventSignature(t *testing.T) {
|
|
||||||
watcher, db := setupL1Watcher(t)
|
|
||||||
defer utils.CloseDB(db)
|
|
||||||
logs := []types.Log{
|
|
||||||
{
|
|
||||||
Topics: []common.Hash{bridgeAbi.L1FailedRelayedMessageEventSignature},
|
|
||||||
BlockNumber: 100,
|
|
||||||
TxHash: common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
convey.Convey("unpack FailedRelayedMessage log failure", t, func() {
|
|
||||||
targetErr := errors.New("UnpackLog FailedRelayedMessage failure")
|
|
||||||
patchGuard := gomonkey.ApplyFunc(utils.UnpackLog, func(c *abi.ABI, out interface{}, event string, log types.Log) error {
|
|
||||||
return targetErr
|
|
||||||
})
|
|
||||||
defer patchGuard.Reset()
|
|
||||||
|
|
||||||
l2Messages, relayedMessages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
|
||||||
assert.EqualError(t, err, targetErr.Error())
|
|
||||||
assert.Empty(t, l2Messages)
|
|
||||||
assert.Empty(t, relayedMessages)
|
|
||||||
assert.Empty(t, rollupEvents)
|
|
||||||
})
|
|
||||||
|
|
||||||
convey.Convey("L1FailedRelayedMessageEventSignature success", t, func() {
|
|
||||||
msgHash := common.HexToHash("0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5")
|
|
||||||
patchGuard := gomonkey.ApplyFunc(utils.UnpackLog, func(c *abi.ABI, out interface{}, event string, log types.Log) error {
|
|
||||||
tmpOut := out.(*bridgeAbi.L1FailedRelayedMessageEvent)
|
|
||||||
tmpOut.MessageHash = msgHash
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
defer patchGuard.Reset()
|
|
||||||
|
|
||||||
l2Messages, relayedMessages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Empty(t, l2Messages)
|
|
||||||
assert.Empty(t, rollupEvents)
|
|
||||||
assert.Len(t, relayedMessages, 1)
|
|
||||||
assert.Equal(t, relayedMessages[0].msgHash, msgHash)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func testParseBridgeEventLogsL1CommitBatchEventSignature(t *testing.T) {
|
func testParseBridgeEventLogsL1CommitBatchEventSignature(t *testing.T) {
|
||||||
watcher, db := setupL1Watcher(t)
|
watcher, db := setupL1Watcher(t)
|
||||||
defer utils.CloseDB(db)
|
defer database.CloseDB(db)
|
||||||
logs := []types.Log{
|
logs := []types.Log{
|
||||||
{
|
{
|
||||||
Topics: []common.Hash{bridgeAbi.L1CommitBatchEventSignature},
|
Topics: []common.Hash{bridgeAbi.L1CommitBatchEventSignature},
|
||||||
@@ -437,10 +322,9 @@ func testParseBridgeEventLogsL1CommitBatchEventSignature(t *testing.T) {
|
|||||||
})
|
})
|
||||||
defer patchGuard.Reset()
|
defer patchGuard.Reset()
|
||||||
|
|
||||||
l2Messages, relayedMessages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
l2Messages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
||||||
assert.EqualError(t, err, targetErr.Error())
|
assert.EqualError(t, err, targetErr.Error())
|
||||||
assert.Empty(t, l2Messages)
|
assert.Empty(t, l2Messages)
|
||||||
assert.Empty(t, relayedMessages)
|
|
||||||
assert.Empty(t, rollupEvents)
|
assert.Empty(t, rollupEvents)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -453,10 +337,9 @@ func testParseBridgeEventLogsL1CommitBatchEventSignature(t *testing.T) {
|
|||||||
})
|
})
|
||||||
defer patchGuard.Reset()
|
defer patchGuard.Reset()
|
||||||
|
|
||||||
l2Messages, relayedMessages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
l2Messages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Empty(t, l2Messages)
|
assert.Empty(t, l2Messages)
|
||||||
assert.Empty(t, relayedMessages)
|
|
||||||
assert.Len(t, rollupEvents, 1)
|
assert.Len(t, rollupEvents, 1)
|
||||||
assert.Equal(t, rollupEvents[0].batchHash, msgHash)
|
assert.Equal(t, rollupEvents[0].batchHash, msgHash)
|
||||||
assert.Equal(t, rollupEvents[0].status, commonTypes.RollupCommitted)
|
assert.Equal(t, rollupEvents[0].status, commonTypes.RollupCommitted)
|
||||||
@@ -465,7 +348,7 @@ func testParseBridgeEventLogsL1CommitBatchEventSignature(t *testing.T) {
|
|||||||
|
|
||||||
func testParseBridgeEventLogsL1FinalizeBatchEventSignature(t *testing.T) {
|
func testParseBridgeEventLogsL1FinalizeBatchEventSignature(t *testing.T) {
|
||||||
watcher, db := setupL1Watcher(t)
|
watcher, db := setupL1Watcher(t)
|
||||||
defer utils.CloseDB(db)
|
defer database.CloseDB(db)
|
||||||
logs := []types.Log{
|
logs := []types.Log{
|
||||||
{
|
{
|
||||||
Topics: []common.Hash{bridgeAbi.L1FinalizeBatchEventSignature},
|
Topics: []common.Hash{bridgeAbi.L1FinalizeBatchEventSignature},
|
||||||
@@ -481,10 +364,9 @@ func testParseBridgeEventLogsL1FinalizeBatchEventSignature(t *testing.T) {
|
|||||||
})
|
})
|
||||||
defer patchGuard.Reset()
|
defer patchGuard.Reset()
|
||||||
|
|
||||||
l2Messages, relayedMessages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
l2Messages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
||||||
assert.EqualError(t, err, targetErr.Error())
|
assert.EqualError(t, err, targetErr.Error())
|
||||||
assert.Empty(t, l2Messages)
|
assert.Empty(t, l2Messages)
|
||||||
assert.Empty(t, relayedMessages)
|
|
||||||
assert.Empty(t, rollupEvents)
|
assert.Empty(t, rollupEvents)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -497,10 +379,9 @@ func testParseBridgeEventLogsL1FinalizeBatchEventSignature(t *testing.T) {
|
|||||||
})
|
})
|
||||||
defer patchGuard.Reset()
|
defer patchGuard.Reset()
|
||||||
|
|
||||||
l2Messages, relayedMessages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
l2Messages, rollupEvents, err := watcher.parseBridgeEventLogs(logs)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Empty(t, l2Messages)
|
assert.Empty(t, l2Messages)
|
||||||
assert.Empty(t, relayedMessages)
|
|
||||||
assert.Len(t, rollupEvents, 1)
|
assert.Len(t, rollupEvents, 1)
|
||||||
assert.Equal(t, rollupEvents[0].batchHash, msgHash)
|
assert.Equal(t, rollupEvents[0].batchHash, msgHash)
|
||||||
assert.Equal(t, rollupEvents[0].status, commonTypes.RollupFinalized)
|
assert.Equal(t, rollupEvents[0].status, commonTypes.RollupFinalized)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package watcher
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
@@ -23,7 +22,6 @@ import (
|
|||||||
|
|
||||||
bridgeAbi "scroll-tech/bridge/abi"
|
bridgeAbi "scroll-tech/bridge/abi"
|
||||||
"scroll-tech/bridge/internal/orm"
|
"scroll-tech/bridge/internal/orm"
|
||||||
bridgeTypes "scroll-tech/bridge/internal/types"
|
|
||||||
"scroll-tech/bridge/internal/utils"
|
"scroll-tech/bridge/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -32,8 +30,6 @@ var (
|
|||||||
bridgeL2MsgsSyncHeightGauge = gethMetrics.NewRegisteredGauge("bridge/l2/msgs/sync/height", metrics.ScrollRegistry)
|
bridgeL2MsgsSyncHeightGauge = gethMetrics.NewRegisteredGauge("bridge/l2/msgs/sync/height", metrics.ScrollRegistry)
|
||||||
bridgeL2BlocksFetchedHeightGauge = gethMetrics.NewRegisteredGauge("bridge/l2/blocks/fetched/height", metrics.ScrollRegistry)
|
bridgeL2BlocksFetchedHeightGauge = gethMetrics.NewRegisteredGauge("bridge/l2/blocks/fetched/height", metrics.ScrollRegistry)
|
||||||
bridgeL2BlocksFetchedGapGauge = gethMetrics.NewRegisteredGauge("bridge/l2/blocks/fetched/gap", metrics.ScrollRegistry)
|
bridgeL2BlocksFetchedGapGauge = gethMetrics.NewRegisteredGauge("bridge/l2/blocks/fetched/gap", metrics.ScrollRegistry)
|
||||||
bridgeL2MsgsSentEventsTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/msgs/sent/events/total", metrics.ScrollRegistry)
|
|
||||||
bridgeL2MsgsAppendEventsTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/msgs/append/events/total", metrics.ScrollRegistry)
|
|
||||||
bridgeL2MsgsRelayedEventsTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/msgs/relayed/events/total", metrics.ScrollRegistry)
|
bridgeL2MsgsRelayedEventsTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/msgs/relayed/events/total", metrics.ScrollRegistry)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -44,11 +40,8 @@ type L2WatcherClient struct {
|
|||||||
|
|
||||||
*ethclient.Client
|
*ethclient.Client
|
||||||
|
|
||||||
db *gorm.DB
|
l2BlockOrm *orm.L2Block
|
||||||
blockBatchOrm *orm.BlockBatch
|
l1MessageOrm *orm.L1Message
|
||||||
blockTraceOrm *orm.BlockTrace
|
|
||||||
l1MessageOrm *orm.L1Message
|
|
||||||
l2MessageOrm *orm.L2Message
|
|
||||||
|
|
||||||
confirmations rpc.BlockNumber
|
confirmations rpc.BlockNumber
|
||||||
|
|
||||||
@@ -67,23 +60,29 @@ type L2WatcherClient struct {
|
|||||||
|
|
||||||
// NewL2WatcherClient take a l2geth instance to generate a l2watcherclient instance
|
// NewL2WatcherClient take a l2geth instance to generate a l2watcherclient instance
|
||||||
func NewL2WatcherClient(ctx context.Context, client *ethclient.Client, confirmations rpc.BlockNumber, messengerAddress, messageQueueAddress common.Address, withdrawTrieRootSlot common.Hash, db *gorm.DB) *L2WatcherClient {
|
func NewL2WatcherClient(ctx context.Context, client *ethclient.Client, confirmations rpc.BlockNumber, messengerAddress, messageQueueAddress common.Address, withdrawTrieRootSlot common.Hash, db *gorm.DB) *L2WatcherClient {
|
||||||
l2MessageOrm := orm.NewL2Message(db)
|
l1MessageOrm := orm.NewL1Message(db)
|
||||||
savedHeight, err := l2MessageOrm.GetLayer2LatestWatchedHeight()
|
var savedHeight uint64
|
||||||
if err != nil {
|
l1msg, err := l1MessageOrm.GetLayer1LatestMessageWithLayer2Hash()
|
||||||
|
if err != nil || l1msg == nil {
|
||||||
log.Warn("fetch height from db failed", "err", err)
|
log.Warn("fetch height from db failed", "err", err)
|
||||||
savedHeight = 0
|
savedHeight = 0
|
||||||
|
} else {
|
||||||
|
receipt, err := client.TransactionReceipt(ctx, common.HexToHash(l1msg.Layer2Hash))
|
||||||
|
if err != nil || receipt == nil {
|
||||||
|
log.Warn("get tx from l2 failed", "err", err)
|
||||||
|
savedHeight = 0
|
||||||
|
} else {
|
||||||
|
savedHeight = receipt.BlockNumber.Uint64()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
w := L2WatcherClient{
|
w := L2WatcherClient{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
db: db,
|
|
||||||
Client: client,
|
Client: client,
|
||||||
|
|
||||||
blockBatchOrm: orm.NewBlockBatch(db),
|
l2BlockOrm: orm.NewL2Block(db),
|
||||||
blockTraceOrm: orm.NewBlockTrace(db),
|
|
||||||
l1MessageOrm: orm.NewL1Message(db),
|
l1MessageOrm: orm.NewL1Message(db),
|
||||||
l2MessageOrm: l2MessageOrm,
|
processedMsgHeight: savedHeight,
|
||||||
processedMsgHeight: uint64(savedHeight),
|
|
||||||
confirmations: confirmations,
|
confirmations: confirmations,
|
||||||
|
|
||||||
messengerAddress: messengerAddress,
|
messengerAddress: messengerAddress,
|
||||||
@@ -96,84 +95,28 @@ func NewL2WatcherClient(ctx context.Context, client *ethclient.Client, confirmat
|
|||||||
stopped: 0,
|
stopped: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize genesis before we do anything else
|
|
||||||
if err := w.initializeGenesis(); err != nil {
|
|
||||||
panic(fmt.Sprintf("failed to initialize L2 genesis batch, err: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
return &w
|
return &w
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *L2WatcherClient) initializeGenesis() error {
|
|
||||||
if count, err := w.blockBatchOrm.GetBatchCount(); err != nil {
|
|
||||||
return fmt.Errorf("failed to get batch count: %v", err)
|
|
||||||
} else if count > 0 {
|
|
||||||
log.Info("genesis already imported")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
genesis, err := w.HeaderByNumber(w.ctx, big.NewInt(0))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to retrieve L2 genesis header: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("retrieved L2 genesis header", "hash", genesis.Hash().String())
|
|
||||||
|
|
||||||
blockTrace := &bridgeTypes.WrappedBlock{
|
|
||||||
Header: genesis,
|
|
||||||
Transactions: nil,
|
|
||||||
WithdrawTrieRoot: common.Hash{},
|
|
||||||
}
|
|
||||||
batchData := bridgeTypes.NewGenesisBatchData(blockTrace)
|
|
||||||
|
|
||||||
if err = orm.AddBatchInfoToDB(w.db, batchData); err != nil {
|
|
||||||
log.Error("failed to add batch info to DB", "BatchHash", batchData.Hash(), "error", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
batchHash := batchData.Hash().Hex()
|
|
||||||
|
|
||||||
if err = w.blockBatchOrm.UpdateProvingStatus(batchHash, types.ProvingTaskProved); err != nil {
|
|
||||||
return fmt.Errorf("failed to update genesis batch proving status: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = w.blockBatchOrm.UpdateRollupStatus(w.ctx, batchHash, types.RollupFinalized); err != nil {
|
|
||||||
return fmt.Errorf("failed to update genesis batch rollup status: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("successfully imported genesis batch")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
const blockTracesFetchLimit = uint64(10)
|
const blockTracesFetchLimit = uint64(10)
|
||||||
|
|
||||||
// TryFetchRunningMissingBlocks try fetch missing blocks if inconsistent
|
// TryFetchRunningMissingBlocks attempts to fetch and store block traces for any missing blocks.
|
||||||
func (w *L2WatcherClient) TryFetchRunningMissingBlocks(ctx context.Context, blockHeight uint64) {
|
func (w *L2WatcherClient) TryFetchRunningMissingBlocks(blockHeight uint64) {
|
||||||
// Get newest block in DB. must have blocks at that time.
|
heightInDB, err := w.l2BlockOrm.GetL2BlocksLatestHeight(w.ctx)
|
||||||
// Don't use "block_trace" table "trace" column's BlockTrace.Number,
|
|
||||||
// because it might be empty if the corresponding rollup_result is finalized/finalization_skipped
|
|
||||||
heightInDB, err := w.blockTraceOrm.GetL2BlocksLatestHeight()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("failed to GetL2BlocksLatestHeight", "err", err)
|
log.Error("failed to GetL2BlocksLatestHeight", "err", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Can't get trace from genesis block, so the default start number is 1.
|
// Fetch and store block traces for missing blocks
|
||||||
var from = uint64(1)
|
for from := heightInDB + 1; from <= blockHeight; from += blockTracesFetchLimit {
|
||||||
if heightInDB > 0 {
|
|
||||||
from = uint64(heightInDB) + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
for ; from <= blockHeight; from += blockTracesFetchLimit {
|
|
||||||
to := from + blockTracesFetchLimit - 1
|
to := from + blockTracesFetchLimit - 1
|
||||||
|
|
||||||
if to > blockHeight {
|
if to > blockHeight {
|
||||||
to = blockHeight
|
to = blockHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get block traces and insert into db.
|
if err = w.getAndStoreBlockTraces(w.ctx, from, to); err != nil {
|
||||||
if err = w.getAndStoreBlockTraces(ctx, from, to); err != nil {
|
|
||||||
log.Error("fail to getAndStoreBlockTraces", "from", from, "to", to, "err", err)
|
log.Error("fail to getAndStoreBlockTraces", "from", from, "to", to, "err", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -186,10 +129,20 @@ func txsToTxsData(txs gethTypes.Transactions) []*gethTypes.TransactionData {
|
|||||||
txsData := make([]*gethTypes.TransactionData, len(txs))
|
txsData := make([]*gethTypes.TransactionData, len(txs))
|
||||||
for i, tx := range txs {
|
for i, tx := range txs {
|
||||||
v, r, s := tx.RawSignatureValues()
|
v, r, s := tx.RawSignatureValues()
|
||||||
|
|
||||||
|
nonce := tx.Nonce()
|
||||||
|
|
||||||
|
// We need QueueIndex in `NewBatchHeader`. However, `TransactionData`
|
||||||
|
// does not have this field. Since `L1MessageTx` do not have a nonce,
|
||||||
|
// we reuse this field for storing the queue index.
|
||||||
|
if msg := tx.AsL1MessageTx(); msg != nil {
|
||||||
|
nonce = msg.QueueIndex
|
||||||
|
}
|
||||||
|
|
||||||
txsData[i] = &gethTypes.TransactionData{
|
txsData[i] = &gethTypes.TransactionData{
|
||||||
Type: tx.Type(),
|
Type: tx.Type(),
|
||||||
TxHash: tx.Hash().String(),
|
TxHash: tx.Hash().String(),
|
||||||
Nonce: tx.Nonce(),
|
Nonce: nonce,
|
||||||
ChainId: (*hexutil.Big)(tx.ChainId()),
|
ChainId: (*hexutil.Big)(tx.ChainId()),
|
||||||
Gas: tx.Gas(),
|
Gas: tx.Gas(),
|
||||||
GasPrice: (*hexutil.Big)(tx.GasPrice()),
|
GasPrice: (*hexutil.Big)(tx.GasPrice()),
|
||||||
@@ -206,7 +159,7 @@ func txsToTxsData(txs gethTypes.Transactions) []*gethTypes.TransactionData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *L2WatcherClient) getAndStoreBlockTraces(ctx context.Context, from, to uint64) error {
|
func (w *L2WatcherClient) getAndStoreBlockTraces(ctx context.Context, from, to uint64) error {
|
||||||
var blocks []*bridgeTypes.WrappedBlock
|
var blocks []*types.WrappedBlock
|
||||||
for number := from; number <= to; number++ {
|
for number := from; number <= to; number++ {
|
||||||
log.Debug("retrieving block", "height", number)
|
log.Debug("retrieving block", "height", number)
|
||||||
block, err2 := w.BlockByNumber(ctx, big.NewInt(int64(number)))
|
block, err2 := w.BlockByNumber(ctx, big.NewInt(int64(number)))
|
||||||
@@ -216,20 +169,20 @@ func (w *L2WatcherClient) getAndStoreBlockTraces(ctx context.Context, from, to u
|
|||||||
|
|
||||||
log.Info("retrieved block", "height", block.Header().Number, "hash", block.Header().Hash().String())
|
log.Info("retrieved block", "height", block.Header().Number, "hash", block.Header().Hash().String())
|
||||||
|
|
||||||
withdrawTrieRoot, err3 := w.StorageAt(ctx, w.messageQueueAddress, w.withdrawTrieRootSlot, big.NewInt(int64(number)))
|
withdrawRoot, err3 := w.StorageAt(ctx, w.messageQueueAddress, w.withdrawTrieRootSlot, big.NewInt(int64(number)))
|
||||||
if err3 != nil {
|
if err3 != nil {
|
||||||
return fmt.Errorf("failed to get withdrawTrieRoot: %v. number: %v", err3, number)
|
return fmt.Errorf("failed to get withdrawRoot: %v. number: %v", err3, number)
|
||||||
}
|
}
|
||||||
|
|
||||||
blocks = append(blocks, &bridgeTypes.WrappedBlock{
|
blocks = append(blocks, &types.WrappedBlock{
|
||||||
Header: block.Header(),
|
Header: block.Header(),
|
||||||
Transactions: txsToTxsData(block.Transactions()),
|
Transactions: txsToTxsData(block.Transactions()),
|
||||||
WithdrawTrieRoot: common.BytesToHash(withdrawTrieRoot),
|
WithdrawRoot: common.BytesToHash(withdrawRoot),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(blocks) > 0 {
|
if len(blocks) > 0 {
|
||||||
if err := w.blockTraceOrm.InsertWrappedBlocks(blocks); err != nil {
|
if err := w.l2BlockOrm.InsertL2Blocks(w.ctx, blocks); err != nil {
|
||||||
return fmt.Errorf("failed to batch insert BlockTraces: %v", err)
|
return fmt.Errorf("failed to batch insert BlockTraces: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -287,17 +240,15 @@ func (w *L2WatcherClient) FetchContractEvent() {
|
|||||||
}
|
}
|
||||||
log.Info("received new L2 messages", "fromBlock", from, "toBlock", to, "cnt", len(logs))
|
log.Info("received new L2 messages", "fromBlock", from, "toBlock", to, "cnt", len(logs))
|
||||||
|
|
||||||
sentMessageEvents, relayedMessageEvents, err := w.parseBridgeEventLogs(logs)
|
relayedMessageEvents, err := w.parseBridgeEventLogs(logs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("failed to parse emitted event log", "err", err)
|
log.Error("failed to parse emitted event log", "err", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
sentMessageCount := int64(len(sentMessageEvents))
|
|
||||||
relayedMessageCount := int64(len(relayedMessageEvents))
|
relayedMessageCount := int64(len(relayedMessageEvents))
|
||||||
bridgeL2MsgsSentEventsTotalCounter.Inc(sentMessageCount)
|
|
||||||
bridgeL2MsgsRelayedEventsTotalCounter.Inc(relayedMessageCount)
|
bridgeL2MsgsRelayedEventsTotalCounter.Inc(relayedMessageCount)
|
||||||
log.Info("L2 events types", "SentMessageCount", sentMessageCount, "RelayedMessageCount", relayedMessageCount)
|
log.Info("L2 events types", "RelayedMessageCount", relayedMessageCount)
|
||||||
|
|
||||||
// Update relayed message first to make sure we don't forget to update submited message.
|
// Update relayed message first to make sure we don't forget to update submited message.
|
||||||
// Since, we always start sync from the latest unprocessed message.
|
// Since, we always start sync from the latest unprocessed message.
|
||||||
@@ -314,71 +265,24 @@ func (w *L2WatcherClient) FetchContractEvent() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = w.l2MessageOrm.SaveL2Messages(w.ctx, sentMessageEvents); err != nil {
|
|
||||||
log.Error("failed to save l2 messages", "err", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.processedMsgHeight = uint64(to)
|
w.processedMsgHeight = uint64(to)
|
||||||
bridgeL2MsgsSyncHeightGauge.Update(to)
|
bridgeL2MsgsSyncHeightGauge.Update(to)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *L2WatcherClient) parseBridgeEventLogs(logs []gethTypes.Log) ([]orm.L2Message, []relayedMessage, error) {
|
func (w *L2WatcherClient) parseBridgeEventLogs(logs []gethTypes.Log) ([]relayedMessage, error) {
|
||||||
// Need use contract abi to parse event Log
|
// Need use contract abi to parse event Log
|
||||||
// Can only be tested after we have our contracts set up
|
// Can only be tested after we have our contracts set up
|
||||||
|
|
||||||
var l2Messages []orm.L2Message
|
|
||||||
var relayedMessages []relayedMessage
|
var relayedMessages []relayedMessage
|
||||||
var lastAppendMsgHash common.Hash
|
|
||||||
var lastAppendMsgNonce uint64
|
|
||||||
for _, vLog := range logs {
|
for _, vLog := range logs {
|
||||||
switch vLog.Topics[0] {
|
switch vLog.Topics[0] {
|
||||||
case bridgeAbi.L2SentMessageEventSignature:
|
|
||||||
event := bridgeAbi.L2SentMessageEvent{}
|
|
||||||
err := utils.UnpackLog(w.messengerABI, &event, "SentMessage", vLog)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("failed to unpack layer2 SentMessage event", "err", err)
|
|
||||||
return l2Messages, relayedMessages, err
|
|
||||||
}
|
|
||||||
|
|
||||||
computedMsgHash := utils.ComputeMessageHash(
|
|
||||||
event.Sender,
|
|
||||||
event.Target,
|
|
||||||
event.Value,
|
|
||||||
event.MessageNonce,
|
|
||||||
event.Message,
|
|
||||||
)
|
|
||||||
|
|
||||||
// `AppendMessage` event is always emitted before `SentMessage` event
|
|
||||||
// So they should always match, just double check
|
|
||||||
if event.MessageNonce.Uint64() != lastAppendMsgNonce {
|
|
||||||
errMsg := fmt.Sprintf("l2 message nonces mismatch: AppendMessage.nonce=%v, SentMessage.nonce=%v, tx_hash=%v",
|
|
||||||
lastAppendMsgNonce, event.MessageNonce.Uint64(), vLog.TxHash.Hex())
|
|
||||||
return l2Messages, relayedMessages, errors.New(errMsg)
|
|
||||||
}
|
|
||||||
if computedMsgHash != lastAppendMsgHash {
|
|
||||||
errMsg := fmt.Sprintf("l2 message hashes mismatch: AppendMessage.msg_hash=%v, SentMessage.msg_hash=%v, tx_hash=%v",
|
|
||||||
lastAppendMsgHash.Hex(), computedMsgHash.Hex(), vLog.TxHash.Hex())
|
|
||||||
return l2Messages, relayedMessages, errors.New(errMsg)
|
|
||||||
}
|
|
||||||
|
|
||||||
l2Messages = append(l2Messages, orm.L2Message{
|
|
||||||
Nonce: event.MessageNonce.Uint64(),
|
|
||||||
MsgHash: computedMsgHash.String(),
|
|
||||||
Height: vLog.BlockNumber,
|
|
||||||
Sender: event.Sender.String(),
|
|
||||||
Value: event.Value.String(),
|
|
||||||
Target: event.Target.String(),
|
|
||||||
Calldata: common.Bytes2Hex(event.Message),
|
|
||||||
Layer2Hash: vLog.TxHash.Hex(),
|
|
||||||
})
|
|
||||||
case bridgeAbi.L2RelayedMessageEventSignature:
|
case bridgeAbi.L2RelayedMessageEventSignature:
|
||||||
event := bridgeAbi.L2RelayedMessageEvent{}
|
event := bridgeAbi.L2RelayedMessageEvent{}
|
||||||
err := utils.UnpackLog(w.messengerABI, &event, "RelayedMessage", vLog)
|
err := utils.UnpackLog(w.messengerABI, &event, "RelayedMessage", vLog)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("Failed to unpack layer2 RelayedMessage event", "err", err)
|
log.Warn("Failed to unpack layer2 RelayedMessage event", "err", err)
|
||||||
return l2Messages, relayedMessages, err
|
return relayedMessages, err
|
||||||
}
|
}
|
||||||
|
|
||||||
relayedMessages = append(relayedMessages, relayedMessage{
|
relayedMessages = append(relayedMessages, relayedMessage{
|
||||||
@@ -391,7 +295,7 @@ func (w *L2WatcherClient) parseBridgeEventLogs(logs []gethTypes.Log) ([]orm.L2Me
|
|||||||
err := utils.UnpackLog(w.messengerABI, &event, "FailedRelayedMessage", vLog)
|
err := utils.UnpackLog(w.messengerABI, &event, "FailedRelayedMessage", vLog)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("Failed to unpack layer2 FailedRelayedMessage event", "err", err)
|
log.Warn("Failed to unpack layer2 FailedRelayedMessage event", "err", err)
|
||||||
return l2Messages, relayedMessages, err
|
return relayedMessages, err
|
||||||
}
|
}
|
||||||
|
|
||||||
relayedMessages = append(relayedMessages, relayedMessage{
|
relayedMessages = append(relayedMessages, relayedMessage{
|
||||||
@@ -399,21 +303,9 @@ func (w *L2WatcherClient) parseBridgeEventLogs(logs []gethTypes.Log) ([]orm.L2Me
|
|||||||
txHash: vLog.TxHash,
|
txHash: vLog.TxHash,
|
||||||
isSuccessful: false,
|
isSuccessful: false,
|
||||||
})
|
})
|
||||||
case bridgeAbi.L2AppendMessageEventSignature:
|
|
||||||
event := bridgeAbi.L2AppendMessageEvent{}
|
|
||||||
err := utils.UnpackLog(w.messageQueueABI, &event, "AppendMessage", vLog)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Failed to unpack layer2 AppendMessage event", "err", err)
|
|
||||||
return l2Messages, relayedMessages, err
|
|
||||||
}
|
|
||||||
|
|
||||||
lastAppendMsgHash = event.MessageHash
|
|
||||||
lastAppendMsgNonce = event.Index.Uint64()
|
|
||||||
bridgeL2MsgsAppendEventsTotalCounter.Inc(1)
|
|
||||||
default:
|
|
||||||
log.Error("Unknown event", "topic", vLog.Topics[0], "txHash", vLog.TxHash)
|
log.Error("Unknown event", "topic", vLog.Topics[0], "txHash", vLog.TxHash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return l2Messages, relayedMessages, nil
|
return relayedMessages, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import (
|
|||||||
"github.com/smartystreets/goconvey/convey"
|
"github.com/smartystreets/goconvey/convey"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"scroll-tech/common/types"
|
"scroll-tech/common/database"
|
||||||
cutils "scroll-tech/common/utils"
|
cutils "scroll-tech/common/utils"
|
||||||
|
|
||||||
bridgeAbi "scroll-tech/bridge/abi"
|
bridgeAbi "scroll-tech/bridge/abi"
|
||||||
@@ -43,7 +43,7 @@ func testCreateNewWatcherAndStop(t *testing.T) {
|
|||||||
subCtx, cancel := context.WithCancel(context.Background())
|
subCtx, cancel := context.WithCancel(context.Background())
|
||||||
defer func() {
|
defer func() {
|
||||||
cancel()
|
cancel()
|
||||||
defer utils.CloseDB(db)
|
defer database.CloseDB(db)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
loopToFetchEvent(subCtx, wc)
|
loopToFetchEvent(subCtx, wc)
|
||||||
@@ -67,140 +67,9 @@ func testCreateNewWatcherAndStop(t *testing.T) {
|
|||||||
assert.GreaterOrEqual(t, blockNum, uint64(numTransactions))
|
assert.GreaterOrEqual(t, blockNum, uint64(numTransactions))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testMonitorBridgeContract(t *testing.T) {
|
|
||||||
wc, db := setupL2Watcher(t)
|
|
||||||
subCtx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer func() {
|
|
||||||
cancel()
|
|
||||||
defer utils.CloseDB(db)
|
|
||||||
}()
|
|
||||||
|
|
||||||
loopToFetchEvent(subCtx, wc)
|
|
||||||
|
|
||||||
previousHeight, err := l2Cli.BlockNumber(context.Background())
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
auth := prepareAuth(t, l2Cli, cfg.L2Config.RelayerConfig.MessageSenderPrivateKeys[0])
|
|
||||||
|
|
||||||
// deploy mock bridge
|
|
||||||
_, tx, instance, err := mock_bridge.DeployMockBridgeL2(auth, l2Cli)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
address, err := bind.WaitDeployed(context.Background(), l2Cli, tx)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
rc := prepareWatcherClient(l2Cli, db, address)
|
|
||||||
loopToFetchEvent(subCtx, rc)
|
|
||||||
// Call mock_bridge instance sendMessage to trigger emit events
|
|
||||||
toAddress := common.HexToAddress("0x4592d8f8d7b001e72cb26a73e4fa1806a51ac79d")
|
|
||||||
message := []byte("testbridgecontract")
|
|
||||||
fee := big.NewInt(0)
|
|
||||||
gasLimit := big.NewInt(1)
|
|
||||||
|
|
||||||
tx, err = instance.SendMessage(auth, toAddress, fee, message, gasLimit)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
receipt, err := bind.WaitMined(context.Background(), l2Cli, tx)
|
|
||||||
if receipt.Status != gethTypes.ReceiptStatusSuccessful || err != nil {
|
|
||||||
t.Fatalf("Call failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
// extra block mined
|
|
||||||
toAddress = common.HexToAddress("0x4592d8f8d7b001e72cb26a73e4fa1806a51ac79d")
|
|
||||||
message = []byte("testbridgecontract")
|
|
||||||
tx, err = instance.SendMessage(auth, toAddress, fee, message, gasLimit)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
receipt, err = bind.WaitMined(context.Background(), l2Cli, tx)
|
|
||||||
if receipt.Status != gethTypes.ReceiptStatusSuccessful || err != nil {
|
|
||||||
t.Fatalf("Call failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
l2MessageOrm := orm.NewL2Message(db)
|
|
||||||
// check if we successfully stored events
|
|
||||||
assert.True(t, cutils.TryTimes(10, func() bool {
|
|
||||||
height, err := l2MessageOrm.GetLayer2LatestWatchedHeight()
|
|
||||||
return err == nil && height > int64(previousHeight)
|
|
||||||
}))
|
|
||||||
|
|
||||||
// check l1 messages.
|
|
||||||
assert.True(t, cutils.TryTimes(10, func() bool {
|
|
||||||
msgs, err := l2MessageOrm.GetL2Messages(map[string]interface{}{"status": types.MsgPending}, nil, 0)
|
|
||||||
return err == nil && len(msgs) == 2
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
func testFetchMultipleSentMessageInOneBlock(t *testing.T) {
|
|
||||||
_, db := setupL2Watcher(t)
|
|
||||||
subCtx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer func() {
|
|
||||||
cancel()
|
|
||||||
defer utils.CloseDB(db)
|
|
||||||
}()
|
|
||||||
|
|
||||||
previousHeight, err := l2Cli.BlockNumber(context.Background()) // shallow the global previousHeight
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
auth := prepareAuth(t, l2Cli, cfg.L2Config.RelayerConfig.MessageSenderPrivateKeys[0])
|
|
||||||
|
|
||||||
_, trx, instance, err := mock_bridge.DeployMockBridgeL2(auth, l2Cli)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
address, err := bind.WaitDeployed(context.Background(), l2Cli, trx)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
wc := prepareWatcherClient(l2Cli, db, address)
|
|
||||||
loopToFetchEvent(subCtx, wc)
|
|
||||||
|
|
||||||
// Call mock_bridge instance sendMessage to trigger emit events multiple times
|
|
||||||
numTransactions := 4
|
|
||||||
var tx *gethTypes.Transaction
|
|
||||||
for i := 0; i < numTransactions; i++ {
|
|
||||||
addr := common.HexToAddress("0x1c5a77d9fa7ef466951b2f01f724bca3a5820b63")
|
|
||||||
nonce, nounceErr := l2Cli.PendingNonceAt(context.Background(), addr)
|
|
||||||
assert.NoError(t, nounceErr)
|
|
||||||
auth.Nonce = big.NewInt(int64(nonce))
|
|
||||||
toAddress := common.HexToAddress("0x4592d8f8d7b001e72cb26a73e4fa1806a51ac79d")
|
|
||||||
message := []byte("testbridgecontract")
|
|
||||||
fee := big.NewInt(0)
|
|
||||||
gasLimit := big.NewInt(1)
|
|
||||||
tx, err = instance.SendMessage(auth, toAddress, fee, message, gasLimit)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
receipt, err := bind.WaitMined(context.Background(), l2Cli, tx)
|
|
||||||
if receipt.Status != gethTypes.ReceiptStatusSuccessful || err != nil {
|
|
||||||
t.Fatalf("Call failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
// extra block mined
|
|
||||||
addr := common.HexToAddress("0x1c5a77d9fa7ef466951b2f01f724bca3a5820b63")
|
|
||||||
nonce, nounceErr := l2Cli.PendingNonceAt(context.Background(), addr)
|
|
||||||
assert.NoError(t, nounceErr)
|
|
||||||
auth.Nonce = big.NewInt(int64(nonce))
|
|
||||||
toAddress := common.HexToAddress("0x4592d8f8d7b001e72cb26a73e4fa1806a51ac79d")
|
|
||||||
message := []byte("testbridgecontract")
|
|
||||||
fee := big.NewInt(0)
|
|
||||||
gasLimit := big.NewInt(1)
|
|
||||||
tx, err = instance.SendMessage(auth, toAddress, fee, message, gasLimit)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
receipt, err = bind.WaitMined(context.Background(), l2Cli, tx)
|
|
||||||
if receipt.Status != gethTypes.ReceiptStatusSuccessful || err != nil {
|
|
||||||
t.Fatalf("Call failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
l2MessageOrm := orm.NewL2Message(db)
|
|
||||||
// check if we successfully stored events
|
|
||||||
assert.True(t, cutils.TryTimes(10, func() bool {
|
|
||||||
height, err := l2MessageOrm.GetLayer2LatestWatchedHeight()
|
|
||||||
return err == nil && height > int64(previousHeight)
|
|
||||||
}))
|
|
||||||
|
|
||||||
assert.True(t, cutils.TryTimes(10, func() bool {
|
|
||||||
msgs, err := l2MessageOrm.GetL2Messages(map[string]interface{}{"status": types.MsgPending}, nil, 0)
|
|
||||||
return err == nil && len(msgs) == 5
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
func testFetchRunningMissingBlocks(t *testing.T) {
|
func testFetchRunningMissingBlocks(t *testing.T) {
|
||||||
_, db := setupL2Watcher(t)
|
_, db := setupL2Watcher(t)
|
||||||
defer utils.CloseDB(db)
|
defer database.CloseDB(db)
|
||||||
|
|
||||||
auth := prepareAuth(t, l2Cli, cfg.L2Config.RelayerConfig.MessageSenderPrivateKeys[0])
|
auth := prepareAuth(t, l2Cli, cfg.L2Config.RelayerConfig.MessageSenderPrivateKeys[0])
|
||||||
|
|
||||||
@@ -210,16 +79,16 @@ func testFetchRunningMissingBlocks(t *testing.T) {
|
|||||||
address, err := bind.WaitDeployed(context.Background(), l2Cli, tx)
|
address, err := bind.WaitDeployed(context.Background(), l2Cli, tx)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
blockTraceOrm := orm.NewBlockTrace(db)
|
l2BlockOrm := orm.NewL2Block(db)
|
||||||
ok := cutils.TryTimes(10, func() bool {
|
ok := cutils.TryTimes(10, func() bool {
|
||||||
latestHeight, err := l2Cli.BlockNumber(context.Background())
|
latestHeight, err := l2Cli.BlockNumber(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
wc := prepareWatcherClient(l2Cli, db, address)
|
wc := prepareWatcherClient(l2Cli, db, address)
|
||||||
wc.TryFetchRunningMissingBlocks(context.Background(), latestHeight)
|
wc.TryFetchRunningMissingBlocks(latestHeight)
|
||||||
fetchedHeight, err := blockTraceOrm.GetL2BlocksLatestHeight()
|
fetchedHeight, err := l2BlockOrm.GetL2BlocksLatestHeight(context.Background())
|
||||||
return err == nil && uint64(fetchedHeight) == latestHeight
|
return err == nil && fetchedHeight == latestHeight
|
||||||
})
|
})
|
||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
}
|
}
|
||||||
@@ -244,60 +113,9 @@ func loopToFetchEvent(subCtx context.Context, watcher *L2WatcherClient) {
|
|||||||
go cutils.Loop(subCtx, 2*time.Second, watcher.FetchContractEvent)
|
go cutils.Loop(subCtx, 2*time.Second, watcher.FetchContractEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testParseBridgeEventLogsL2SentMessageEventSignature(t *testing.T) {
|
|
||||||
watcher, db := setupL2Watcher(t)
|
|
||||||
defer utils.CloseDB(db)
|
|
||||||
|
|
||||||
logs := []gethTypes.Log{
|
|
||||||
{
|
|
||||||
Topics: []common.Hash{
|
|
||||||
bridgeAbi.L2SentMessageEventSignature,
|
|
||||||
},
|
|
||||||
BlockNumber: 100,
|
|
||||||
TxHash: common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
convey.Convey("unpack SentMessage log failure", t, func() {
|
|
||||||
targetErr := errors.New("UnpackLog SentMessage failure")
|
|
||||||
patchGuard := gomonkey.ApplyFunc(utils.UnpackLog, func(c *abi.ABI, out interface{}, event string, log gethTypes.Log) error {
|
|
||||||
return targetErr
|
|
||||||
})
|
|
||||||
defer patchGuard.Reset()
|
|
||||||
|
|
||||||
l2Messages, relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
|
||||||
assert.EqualError(t, err, targetErr.Error())
|
|
||||||
assert.Empty(t, l2Messages)
|
|
||||||
assert.Empty(t, relayedMessages)
|
|
||||||
})
|
|
||||||
|
|
||||||
convey.Convey("L2SentMessageEventSignature success", t, func() {
|
|
||||||
tmpSendAddr := common.HexToAddress("0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30")
|
|
||||||
tmpTargetAddr := common.HexToAddress("0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5")
|
|
||||||
tmpValue := big.NewInt(1000)
|
|
||||||
tmpMessageNonce := big.NewInt(100)
|
|
||||||
tmpMessage := []byte("test for L2SentMessageEventSignature")
|
|
||||||
patchGuard := gomonkey.ApplyFunc(utils.UnpackLog, func(c *abi.ABI, out interface{}, event string, log gethTypes.Log) error {
|
|
||||||
tmpOut := out.(*bridgeAbi.L2SentMessageEvent)
|
|
||||||
tmpOut.Sender = tmpSendAddr
|
|
||||||
tmpOut.Value = tmpValue
|
|
||||||
tmpOut.Target = tmpTargetAddr
|
|
||||||
tmpOut.MessageNonce = tmpMessageNonce
|
|
||||||
tmpOut.Message = tmpMessage
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
defer patchGuard.Reset()
|
|
||||||
|
|
||||||
l2Messages, relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.Empty(t, relayedMessages)
|
|
||||||
assert.Empty(t, l2Messages)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func testParseBridgeEventLogsL2RelayedMessageEventSignature(t *testing.T) {
|
func testParseBridgeEventLogsL2RelayedMessageEventSignature(t *testing.T) {
|
||||||
watcher, db := setupL2Watcher(t)
|
watcher, db := setupL2Watcher(t)
|
||||||
defer utils.CloseDB(db)
|
defer database.CloseDB(db)
|
||||||
|
|
||||||
logs := []gethTypes.Log{
|
logs := []gethTypes.Log{
|
||||||
{
|
{
|
||||||
@@ -314,9 +132,8 @@ func testParseBridgeEventLogsL2RelayedMessageEventSignature(t *testing.T) {
|
|||||||
})
|
})
|
||||||
defer patchGuard.Reset()
|
defer patchGuard.Reset()
|
||||||
|
|
||||||
l2Messages, relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
||||||
assert.EqualError(t, err, targetErr.Error())
|
assert.EqualError(t, err, targetErr.Error())
|
||||||
assert.Empty(t, l2Messages)
|
|
||||||
assert.Empty(t, relayedMessages)
|
assert.Empty(t, relayedMessages)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -329,9 +146,8 @@ func testParseBridgeEventLogsL2RelayedMessageEventSignature(t *testing.T) {
|
|||||||
})
|
})
|
||||||
defer patchGuard.Reset()
|
defer patchGuard.Reset()
|
||||||
|
|
||||||
l2Messages, relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Empty(t, l2Messages)
|
|
||||||
assert.Len(t, relayedMessages, 1)
|
assert.Len(t, relayedMessages, 1)
|
||||||
assert.Equal(t, relayedMessages[0].msgHash, msgHash)
|
assert.Equal(t, relayedMessages[0].msgHash, msgHash)
|
||||||
})
|
})
|
||||||
@@ -339,7 +155,7 @@ func testParseBridgeEventLogsL2RelayedMessageEventSignature(t *testing.T) {
|
|||||||
|
|
||||||
func testParseBridgeEventLogsL2FailedRelayedMessageEventSignature(t *testing.T) {
|
func testParseBridgeEventLogsL2FailedRelayedMessageEventSignature(t *testing.T) {
|
||||||
watcher, db := setupL2Watcher(t)
|
watcher, db := setupL2Watcher(t)
|
||||||
defer utils.CloseDB(db)
|
defer database.CloseDB(db)
|
||||||
|
|
||||||
logs := []gethTypes.Log{
|
logs := []gethTypes.Log{
|
||||||
{
|
{
|
||||||
@@ -356,9 +172,8 @@ func testParseBridgeEventLogsL2FailedRelayedMessageEventSignature(t *testing.T)
|
|||||||
})
|
})
|
||||||
defer patchGuard.Reset()
|
defer patchGuard.Reset()
|
||||||
|
|
||||||
l2Messages, relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
||||||
assert.EqualError(t, err, targetErr.Error())
|
assert.EqualError(t, err, targetErr.Error())
|
||||||
assert.Empty(t, l2Messages)
|
|
||||||
assert.Empty(t, relayedMessages)
|
assert.Empty(t, relayedMessages)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -371,51 +186,9 @@ func testParseBridgeEventLogsL2FailedRelayedMessageEventSignature(t *testing.T)
|
|||||||
})
|
})
|
||||||
defer patchGuard.Reset()
|
defer patchGuard.Reset()
|
||||||
|
|
||||||
l2Messages, relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Empty(t, l2Messages)
|
|
||||||
assert.Len(t, relayedMessages, 1)
|
assert.Len(t, relayedMessages, 1)
|
||||||
assert.Equal(t, relayedMessages[0].msgHash, msgHash)
|
assert.Equal(t, relayedMessages[0].msgHash, msgHash)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func testParseBridgeEventLogsL2AppendMessageEventSignature(t *testing.T) {
|
|
||||||
watcher, db := setupL2Watcher(t)
|
|
||||||
defer utils.CloseDB(db)
|
|
||||||
logs := []gethTypes.Log{
|
|
||||||
{
|
|
||||||
Topics: []common.Hash{bridgeAbi.L2AppendMessageEventSignature},
|
|
||||||
BlockNumber: 100,
|
|
||||||
TxHash: common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
convey.Convey("unpack AppendMessage log failure", t, func() {
|
|
||||||
targetErr := errors.New("UnpackLog AppendMessage failure")
|
|
||||||
patchGuard := gomonkey.ApplyFunc(utils.UnpackLog, func(c *abi.ABI, out interface{}, event string, log gethTypes.Log) error {
|
|
||||||
return targetErr
|
|
||||||
})
|
|
||||||
defer patchGuard.Reset()
|
|
||||||
|
|
||||||
l2Messages, relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
|
||||||
assert.EqualError(t, err, targetErr.Error())
|
|
||||||
assert.Empty(t, l2Messages)
|
|
||||||
assert.Empty(t, relayedMessages)
|
|
||||||
})
|
|
||||||
|
|
||||||
convey.Convey("L2AppendMessageEventSignature success", t, func() {
|
|
||||||
msgHash := common.HexToHash("0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5")
|
|
||||||
patchGuard := gomonkey.ApplyFunc(utils.UnpackLog, func(c *abi.ABI, out interface{}, event string, log gethTypes.Log) error {
|
|
||||||
tmpOut := out.(*bridgeAbi.L2AppendMessageEvent)
|
|
||||||
tmpOut.MessageHash = msgHash
|
|
||||||
tmpOut.Index = big.NewInt(100)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
defer patchGuard.Reset()
|
|
||||||
|
|
||||||
l2Messages, relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Empty(t, l2Messages)
|
|
||||||
assert.Empty(t, relayedMessages)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -9,12 +9,13 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
|
"scroll-tech/common/database"
|
||||||
"scroll-tech/common/docker"
|
"scroll-tech/common/docker"
|
||||||
|
"scroll-tech/common/types"
|
||||||
|
|
||||||
|
"scroll-tech/database/migrate"
|
||||||
|
|
||||||
"scroll-tech/bridge/internal/config"
|
"scroll-tech/bridge/internal/config"
|
||||||
"scroll-tech/bridge/internal/orm/migrate"
|
|
||||||
bridgeTypes "scroll-tech/bridge/internal/types"
|
|
||||||
"scroll-tech/bridge/internal/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -27,8 +28,8 @@ var (
|
|||||||
l2Cli *ethclient.Client
|
l2Cli *ethclient.Client
|
||||||
|
|
||||||
// block trace
|
// block trace
|
||||||
wrappedBlock1 *bridgeTypes.WrappedBlock
|
wrappedBlock1 *types.WrappedBlock
|
||||||
wrappedBlock2 *bridgeTypes.WrappedBlock
|
wrappedBlock2 *types.WrappedBlock
|
||||||
)
|
)
|
||||||
|
|
||||||
func setupEnv(t *testing.T) (err error) {
|
func setupEnv(t *testing.T) (err error) {
|
||||||
@@ -40,7 +41,7 @@ func setupEnv(t *testing.T) (err error) {
|
|||||||
|
|
||||||
cfg.L2Config.RelayerConfig.SenderConfig.Endpoint = base.L1gethImg.Endpoint()
|
cfg.L2Config.RelayerConfig.SenderConfig.Endpoint = base.L1gethImg.Endpoint()
|
||||||
cfg.L1Config.RelayerConfig.SenderConfig.Endpoint = base.L2gethImg.Endpoint()
|
cfg.L1Config.RelayerConfig.SenderConfig.Endpoint = base.L2gethImg.Endpoint()
|
||||||
cfg.DBConfig = &config.DBConfig{
|
cfg.DBConfig = &database.Config{
|
||||||
DSN: base.DBConfig.DSN,
|
DSN: base.DBConfig.DSN,
|
||||||
DriverName: base.DBConfig.DriverName,
|
DriverName: base.DBConfig.DriverName,
|
||||||
MaxOpenNum: base.DBConfig.MaxOpenNum,
|
MaxOpenNum: base.DBConfig.MaxOpenNum,
|
||||||
@@ -56,7 +57,7 @@ func setupEnv(t *testing.T) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// unmarshal blockTrace
|
// unmarshal blockTrace
|
||||||
wrappedBlock1 = &bridgeTypes.WrappedBlock{}
|
wrappedBlock1 = &types.WrappedBlock{}
|
||||||
if err = json.Unmarshal(templateBlockTrace1, wrappedBlock1); err != nil {
|
if err = json.Unmarshal(templateBlockTrace1, wrappedBlock1); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -66,7 +67,7 @@ func setupEnv(t *testing.T) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// unmarshal blockTrace
|
// unmarshal blockTrace
|
||||||
wrappedBlock2 = &bridgeTypes.WrappedBlock{}
|
wrappedBlock2 = &types.WrappedBlock{}
|
||||||
if err = json.Unmarshal(templateBlockTrace2, wrappedBlock2); err != nil {
|
if err = json.Unmarshal(templateBlockTrace2, wrappedBlock2); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -74,7 +75,7 @@ func setupEnv(t *testing.T) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setupDB(t *testing.T) *gorm.DB {
|
func setupDB(t *testing.T) *gorm.DB {
|
||||||
db, err := utils.InitDB(cfg.DBConfig)
|
db, err := database.InitDB(cfg.DBConfig)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
sqlDB, err := db.DB()
|
sqlDB, err := db.DB()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@@ -100,23 +101,18 @@ func TestFunction(t *testing.T) {
|
|||||||
t.Run("TestL1WatcherClientFetchBlockHeader", testL1WatcherClientFetchBlockHeader)
|
t.Run("TestL1WatcherClientFetchBlockHeader", testL1WatcherClientFetchBlockHeader)
|
||||||
t.Run("TestL1WatcherClientFetchContractEvent", testL1WatcherClientFetchContractEvent)
|
t.Run("TestL1WatcherClientFetchContractEvent", testL1WatcherClientFetchContractEvent)
|
||||||
t.Run("TestParseBridgeEventLogsL1QueueTransactionEventSignature", testParseBridgeEventLogsL1QueueTransactionEventSignature)
|
t.Run("TestParseBridgeEventLogsL1QueueTransactionEventSignature", testParseBridgeEventLogsL1QueueTransactionEventSignature)
|
||||||
t.Run("TestParseBridgeEventLogsL1RelayedMessageEventSignature", testParseBridgeEventLogsL1RelayedMessageEventSignature)
|
|
||||||
t.Run("TestParseBridgeEventLogsL1FailedRelayedMessageEventSignature", testParseBridgeEventLogsL1FailedRelayedMessageEventSignature)
|
|
||||||
t.Run("TestParseBridgeEventLogsL1CommitBatchEventSignature", testParseBridgeEventLogsL1CommitBatchEventSignature)
|
t.Run("TestParseBridgeEventLogsL1CommitBatchEventSignature", testParseBridgeEventLogsL1CommitBatchEventSignature)
|
||||||
t.Run("TestParseBridgeEventLogsL1FinalizeBatchEventSignature", testParseBridgeEventLogsL1FinalizeBatchEventSignature)
|
t.Run("TestParseBridgeEventLogsL1FinalizeBatchEventSignature", testParseBridgeEventLogsL1FinalizeBatchEventSignature)
|
||||||
|
|
||||||
// Run l2 watcher test cases.
|
// Run l2 watcher test cases.
|
||||||
t.Run("TestCreateNewWatcherAndStop", testCreateNewWatcherAndStop)
|
t.Run("TestCreateNewWatcherAndStop", testCreateNewWatcherAndStop)
|
||||||
t.Run("TestMonitorBridgeContract", testMonitorBridgeContract)
|
|
||||||
t.Run("TestFetchMultipleSentMessageInOneBlock", testFetchMultipleSentMessageInOneBlock)
|
|
||||||
t.Run("TestFetchRunningMissingBlocks", testFetchRunningMissingBlocks)
|
t.Run("TestFetchRunningMissingBlocks", testFetchRunningMissingBlocks)
|
||||||
t.Run("TestParseBridgeEventLogsL2SentMessageEventSignature", testParseBridgeEventLogsL2SentMessageEventSignature)
|
|
||||||
t.Run("TestParseBridgeEventLogsL2RelayedMessageEventSignature", testParseBridgeEventLogsL2RelayedMessageEventSignature)
|
t.Run("TestParseBridgeEventLogsL2RelayedMessageEventSignature", testParseBridgeEventLogsL2RelayedMessageEventSignature)
|
||||||
t.Run("TestParseBridgeEventLogsL2FailedRelayedMessageEventSignature", testParseBridgeEventLogsL2FailedRelayedMessageEventSignature)
|
t.Run("TestParseBridgeEventLogsL2FailedRelayedMessageEventSignature", testParseBridgeEventLogsL2FailedRelayedMessageEventSignature)
|
||||||
t.Run("TestParseBridgeEventLogsL2AppendMessageEventSignature", testParseBridgeEventLogsL2AppendMessageEventSignature)
|
|
||||||
|
|
||||||
// Run batch proposer test cases.
|
// Run chunk-proposer test cases.
|
||||||
t.Run("TestBatchProposerProposeBatch", testBatchProposerProposeBatch)
|
t.Run("TestChunkProposer", testChunkProposer)
|
||||||
t.Run("TestBatchProposerBatchGeneration", testBatchProposerBatchGeneration)
|
|
||||||
t.Run("TestBatchProposerGracefulRestart", testBatchProposerGracefulRestart)
|
// Run batch-proposer test cases.
|
||||||
|
t.Run("TestBatchProposer", testBatchProposer)
|
||||||
}
|
}
|
||||||
|
|||||||
413
bridge/internal/orm/batch.go
Normal file
413
bridge/internal/orm/batch.go
Normal file
@@ -0,0 +1,413 @@
|
|||||||
|
package orm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"scroll-tech/common/types"
|
||||||
|
"scroll-tech/common/types/message"
|
||||||
|
|
||||||
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
|
"github.com/scroll-tech/go-ethereum/log"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultBatchHeaderVersion = 0
|
||||||
|
|
||||||
|
// Batch represents a batch of chunks.
|
||||||
|
type Batch struct {
|
||||||
|
db *gorm.DB `gorm:"column:-"`
|
||||||
|
|
||||||
|
// batch
|
||||||
|
Index uint64 `json:"index" gorm:"column:index"`
|
||||||
|
Hash string `json:"hash" gorm:"column:hash"`
|
||||||
|
StartChunkIndex uint64 `json:"start_chunk_index" gorm:"column:start_chunk_index"`
|
||||||
|
StartChunkHash string `json:"start_chunk_hash" gorm:"column:start_chunk_hash"`
|
||||||
|
EndChunkIndex uint64 `json:"end_chunk_index" gorm:"column:end_chunk_index"`
|
||||||
|
EndChunkHash string `json:"end_chunk_hash" gorm:"column:end_chunk_hash"`
|
||||||
|
StateRoot string `json:"state_root" gorm:"column:state_root"`
|
||||||
|
WithdrawRoot string `json:"withdraw_root" gorm:"column:withdraw_root"`
|
||||||
|
ParentBatchHash string `json:"parent_batch_hash" gorm:"column:parent_batch_hash"`
|
||||||
|
BatchHeader []byte `json:"batch_header" gorm:"column:batch_header"`
|
||||||
|
|
||||||
|
// proof
|
||||||
|
ChunkProofsStatus int16 `json:"chunk_proofs_status" gorm:"column:chunk_proofs_status;default:1"`
|
||||||
|
ProvingStatus int16 `json:"proving_status" gorm:"column:proving_status;default:1"`
|
||||||
|
Proof []byte `json:"proof" gorm:"column:proof;default:NULL"`
|
||||||
|
ProverAssignedAt *time.Time `json:"prover_assigned_at" gorm:"column:prover_assigned_at;default:NULL"`
|
||||||
|
ProvedAt *time.Time `json:"proved_at" gorm:"column:proved_at;default:NULL"`
|
||||||
|
ProofTimeSec int32 `json:"proof_time_sec" gorm:"column:proof_time_sec;default:NULL"`
|
||||||
|
|
||||||
|
// rollup
|
||||||
|
RollupStatus int16 `json:"rollup_status" gorm:"column:rollup_status;default:1"`
|
||||||
|
CommitTxHash string `json:"commit_tx_hash" gorm:"column:commit_tx_hash;default:NULL"`
|
||||||
|
CommittedAt *time.Time `json:"committed_at" gorm:"column:committed_at;default:NULL"`
|
||||||
|
FinalizeTxHash string `json:"finalize_tx_hash" gorm:"column:finalize_tx_hash;default:NULL"`
|
||||||
|
FinalizedAt *time.Time `json:"finalized_at" gorm:"column:finalized_at;default:NULL"`
|
||||||
|
|
||||||
|
// gas oracle
|
||||||
|
OracleStatus int16 `json:"oracle_status" gorm:"column:oracle_status;default:1"`
|
||||||
|
OracleTxHash string `json:"oracle_tx_hash" gorm:"column:oracle_tx_hash;default:NULL"`
|
||||||
|
|
||||||
|
// metadata
|
||||||
|
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at"`
|
||||||
|
DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"column:deleted_at;default:NULL"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBatch creates a new Batch database instance.
|
||||||
|
func NewBatch(db *gorm.DB) *Batch {
|
||||||
|
return &Batch{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName returns the table name for the Batch model.
|
||||||
|
func (*Batch) TableName() string {
|
||||||
|
return "batch"
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBatches retrieves selected batches from the database.
|
||||||
|
// The returned batches are sorted in ascending order by their index.
|
||||||
|
func (o *Batch) GetBatches(ctx context.Context, fields map[string]interface{}, orderByList []string, limit int) ([]*Batch, error) {
|
||||||
|
db := o.db.WithContext(ctx)
|
||||||
|
db = db.Model(&Batch{})
|
||||||
|
|
||||||
|
for key, value := range fields {
|
||||||
|
db = db.Where(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, orderBy := range orderByList {
|
||||||
|
db = db.Order(orderBy)
|
||||||
|
}
|
||||||
|
|
||||||
|
if limit > 0 {
|
||||||
|
db = db.Limit(limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
db = db.Order("index ASC")
|
||||||
|
|
||||||
|
var batches []*Batch
|
||||||
|
if err := db.Find(&batches).Error; err != nil {
|
||||||
|
return nil, fmt.Errorf("Batch.GetBatches error: %w, fields: %v, orderByList: %v", err, fields, orderByList)
|
||||||
|
}
|
||||||
|
return batches, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBatchCount retrieves the total number of batches in the database.
|
||||||
|
func (o *Batch) GetBatchCount(ctx context.Context) (uint64, error) {
|
||||||
|
db := o.db.WithContext(ctx)
|
||||||
|
db = db.Model(&Batch{})
|
||||||
|
|
||||||
|
var count int64
|
||||||
|
if err := db.Count(&count).Error; err != nil {
|
||||||
|
return 0, fmt.Errorf("Batch.GetBatchCount error: %w", err)
|
||||||
|
}
|
||||||
|
return uint64(count), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVerifiedProofByHash retrieves the verified aggregate proof for a batch with the given hash.
|
||||||
|
func (o *Batch) GetVerifiedProofByHash(ctx context.Context, hash string) (*message.AggProof, error) {
|
||||||
|
db := o.db.WithContext(ctx)
|
||||||
|
db = db.Model(&Batch{})
|
||||||
|
db = db.Select("proof")
|
||||||
|
db = db.Where("hash = ? AND proving_status = ?", hash, types.ProvingTaskVerified)
|
||||||
|
|
||||||
|
var batch Batch
|
||||||
|
if err := db.Find(&batch).Error; err != nil {
|
||||||
|
return nil, fmt.Errorf("Batch.GetVerifiedProofByHash error: %w, batch hash: %v", err, hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
var proof message.AggProof
|
||||||
|
if err := json.Unmarshal(batch.Proof, &proof); err != nil {
|
||||||
|
return nil, fmt.Errorf("Batch.GetVerifiedProofByHash error: %w, batch hash: %v", err, hash)
|
||||||
|
}
|
||||||
|
return &proof, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLatestBatch retrieves the latest batch from the database.
|
||||||
|
func (o *Batch) GetLatestBatch(ctx context.Context) (*Batch, error) {
|
||||||
|
db := o.db.WithContext(ctx)
|
||||||
|
db = db.Model(&Batch{})
|
||||||
|
db = db.Order("index desc")
|
||||||
|
|
||||||
|
var latestBatch Batch
|
||||||
|
if err := db.First(&latestBatch).Error; err != nil {
|
||||||
|
return nil, fmt.Errorf("Batch.GetLatestBatch error: %w", err)
|
||||||
|
}
|
||||||
|
return &latestBatch, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRollupStatusByHashList retrieves the rollup statuses for a list of batch hashes.
|
||||||
|
func (o *Batch) GetRollupStatusByHashList(ctx context.Context, hashes []string) ([]types.RollupStatus, error) {
|
||||||
|
if len(hashes) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
db := o.db.WithContext(ctx)
|
||||||
|
db = db.Model(&Batch{})
|
||||||
|
db = db.Select("hash, rollup_status")
|
||||||
|
db = db.Where("hash IN ?", hashes)
|
||||||
|
|
||||||
|
var batches []Batch
|
||||||
|
if err := db.Find(&batches).Error; err != nil {
|
||||||
|
return nil, fmt.Errorf("Batch.GetRollupStatusByHashList error: %w, hashes: %v", err, hashes)
|
||||||
|
}
|
||||||
|
|
||||||
|
hashToStatusMap := make(map[string]types.RollupStatus)
|
||||||
|
for _, batch := range batches {
|
||||||
|
hashToStatusMap[batch.Hash] = types.RollupStatus(batch.RollupStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
var statuses []types.RollupStatus
|
||||||
|
for _, hash := range hashes {
|
||||||
|
status, ok := hashToStatusMap[hash]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Batch.GetRollupStatusByHashList: hash not found in database: %s", hash)
|
||||||
|
}
|
||||||
|
statuses = append(statuses, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
return statuses, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPendingBatches retrieves pending batches up to the specified limit.
|
||||||
|
// The returned batches are sorted in ascending order by their index.
|
||||||
|
func (o *Batch) GetPendingBatches(ctx context.Context, limit int) ([]*Batch, error) {
|
||||||
|
if limit <= 0 {
|
||||||
|
return nil, errors.New("limit must be greater than zero")
|
||||||
|
}
|
||||||
|
|
||||||
|
db := o.db.WithContext(ctx)
|
||||||
|
db = db.Model(&Batch{})
|
||||||
|
db = db.Where("rollup_status = ?", types.RollupPending)
|
||||||
|
db = db.Order("index ASC")
|
||||||
|
db = db.Limit(limit)
|
||||||
|
|
||||||
|
var batches []*Batch
|
||||||
|
if err := db.Find(&batches).Error; err != nil {
|
||||||
|
return nil, fmt.Errorf("Batch.GetPendingBatches error: %w", err)
|
||||||
|
}
|
||||||
|
return batches, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBatchByIndex retrieves the batch by the given index.
|
||||||
|
func (o *Batch) GetBatchByIndex(ctx context.Context, index uint64) (*Batch, error) {
|
||||||
|
db := o.db.WithContext(ctx)
|
||||||
|
db = db.Model(&Batch{})
|
||||||
|
db = db.Where("index = ?", index)
|
||||||
|
|
||||||
|
var batch Batch
|
||||||
|
if err := db.First(&batch).Error; err != nil {
|
||||||
|
return nil, fmt.Errorf("Batch.GetBatchByIndex error: %w, index: %v", err, index)
|
||||||
|
}
|
||||||
|
return &batch, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertBatch inserts a new batch into the database.
|
||||||
|
func (o *Batch) InsertBatch(ctx context.Context, startChunkIndex, endChunkIndex uint64, startChunkHash, endChunkHash string, chunks []*types.Chunk, dbTX ...*gorm.DB) (*Batch, error) {
|
||||||
|
if len(chunks) == 0 {
|
||||||
|
return nil, errors.New("invalid args")
|
||||||
|
}
|
||||||
|
|
||||||
|
parentBatch, err := o.GetLatestBatch(ctx)
|
||||||
|
if err != nil && !errors.Is(errors.Unwrap(err), gorm.ErrRecordNotFound) {
|
||||||
|
log.Error("failed to get the latest batch", "err", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var batchIndex uint64
|
||||||
|
var parentBatchHash common.Hash
|
||||||
|
var totalL1MessagePoppedBefore uint64
|
||||||
|
var version uint8 = defaultBatchHeaderVersion
|
||||||
|
|
||||||
|
// if parentBatch==nil then err==gorm.ErrRecordNotFound, which means there's
|
||||||
|
// not batch record in the db, we then use default empty values for the creating batch;
|
||||||
|
// if parentBatch!=nil then err=nil, then we fill the parentBatch-related data into the creating batch
|
||||||
|
if parentBatch != nil {
|
||||||
|
batchIndex = parentBatch.Index + 1
|
||||||
|
parentBatchHash = common.HexToHash(parentBatch.Hash)
|
||||||
|
|
||||||
|
var parentBatchHeader *types.BatchHeader
|
||||||
|
parentBatchHeader, err = types.DecodeBatchHeader(parentBatch.BatchHeader)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("failed to decode parent batch header", "index", parentBatch.Index, "hash", parentBatch.Hash, "err", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
totalL1MessagePoppedBefore = parentBatchHeader.TotalL1MessagePopped()
|
||||||
|
version = parentBatchHeader.Version()
|
||||||
|
}
|
||||||
|
|
||||||
|
batchHeader, err := types.NewBatchHeader(version, batchIndex, totalL1MessagePoppedBefore, parentBatchHash, chunks)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("failed to create batch header",
|
||||||
|
"index", batchIndex, "total l1 message popped before", totalL1MessagePoppedBefore,
|
||||||
|
"parent hash", parentBatchHash, "number of chunks", len(chunks), "err", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
numChunks := len(chunks)
|
||||||
|
lastChunkBlockNum := len(chunks[numChunks-1].Blocks)
|
||||||
|
|
||||||
|
newBatch := Batch{
|
||||||
|
Index: batchIndex,
|
||||||
|
Hash: batchHeader.Hash().Hex(),
|
||||||
|
StartChunkHash: startChunkHash,
|
||||||
|
StartChunkIndex: startChunkIndex,
|
||||||
|
EndChunkHash: endChunkHash,
|
||||||
|
EndChunkIndex: endChunkIndex,
|
||||||
|
StateRoot: chunks[numChunks-1].Blocks[lastChunkBlockNum-1].Header.Root.Hex(),
|
||||||
|
WithdrawRoot: chunks[numChunks-1].Blocks[lastChunkBlockNum-1].WithdrawRoot.Hex(),
|
||||||
|
ParentBatchHash: parentBatchHash.Hex(),
|
||||||
|
BatchHeader: batchHeader.Encode(),
|
||||||
|
ChunkProofsStatus: int16(types.ChunkProofsStatusPending),
|
||||||
|
ProvingStatus: int16(types.ProvingTaskUnassigned),
|
||||||
|
RollupStatus: int16(types.RollupPending),
|
||||||
|
OracleStatus: int16(types.GasOraclePending),
|
||||||
|
}
|
||||||
|
|
||||||
|
db := o.db
|
||||||
|
if len(dbTX) > 0 && dbTX[0] != nil {
|
||||||
|
db = dbTX[0]
|
||||||
|
}
|
||||||
|
db.WithContext(ctx)
|
||||||
|
db = db.Model(&Batch{})
|
||||||
|
|
||||||
|
if err := db.Create(&newBatch).Error; err != nil {
|
||||||
|
log.Error("failed to insert batch", "batch", newBatch, "err", err)
|
||||||
|
return nil, fmt.Errorf("Batch.InsertBatch error: %w", err)
|
||||||
|
}
|
||||||
|
return &newBatch, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateL2GasOracleStatusAndOracleTxHash updates the L2 gas oracle status and transaction hash for a batch.
|
||||||
|
func (o *Batch) UpdateL2GasOracleStatusAndOracleTxHash(ctx context.Context, hash string, status types.GasOracleStatus, txHash string) error {
|
||||||
|
updateFields := make(map[string]interface{})
|
||||||
|
updateFields["oracle_status"] = int(status)
|
||||||
|
updateFields["oracle_tx_hash"] = txHash
|
||||||
|
|
||||||
|
db := o.db.WithContext(ctx)
|
||||||
|
db = db.Model(&Batch{})
|
||||||
|
db = db.Where("hash", hash)
|
||||||
|
|
||||||
|
if err := db.Updates(updateFields).Error; err != nil {
|
||||||
|
return fmt.Errorf("Batch.UpdateL2GasOracleStatusAndOracleTxHash error: %w, batch hash: %v, status: %v, txHash: %v", err, hash, status.String(), txHash)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateProvingStatus updates the proving status of a batch.
|
||||||
|
func (o *Batch) UpdateProvingStatus(ctx context.Context, hash string, status types.ProvingStatus, dbTX ...*gorm.DB) error {
|
||||||
|
updateFields := make(map[string]interface{})
|
||||||
|
updateFields["proving_status"] = int(status)
|
||||||
|
|
||||||
|
switch status {
|
||||||
|
case types.ProvingTaskAssigned:
|
||||||
|
updateFields["prover_assigned_at"] = time.Now()
|
||||||
|
case types.ProvingTaskUnassigned:
|
||||||
|
updateFields["prover_assigned_at"] = nil
|
||||||
|
case types.ProvingTaskProved, types.ProvingTaskVerified:
|
||||||
|
updateFields["proved_at"] = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
db := o.db
|
||||||
|
if len(dbTX) > 0 && dbTX[0] != nil {
|
||||||
|
db = dbTX[0]
|
||||||
|
}
|
||||||
|
db = db.WithContext(ctx)
|
||||||
|
db = db.Model(&Batch{})
|
||||||
|
db = db.Where("hash", hash)
|
||||||
|
|
||||||
|
if err := db.Updates(updateFields).Error; err != nil {
|
||||||
|
return fmt.Errorf("Batch.UpdateProvingStatus error: %w, batch hash: %v, status: %v", err, hash, status.String())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateRollupStatus updates the rollup status of a batch.
|
||||||
|
func (o *Batch) UpdateRollupStatus(ctx context.Context, hash string, status types.RollupStatus, dbTX ...*gorm.DB) error {
|
||||||
|
updateFields := make(map[string]interface{})
|
||||||
|
updateFields["rollup_status"] = int(status)
|
||||||
|
|
||||||
|
switch status {
|
||||||
|
case types.RollupCommitted:
|
||||||
|
updateFields["committed_at"] = time.Now()
|
||||||
|
case types.RollupFinalized:
|
||||||
|
updateFields["finalized_at"] = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
db := o.db
|
||||||
|
if len(dbTX) > 0 && dbTX[0] != nil {
|
||||||
|
db = dbTX[0]
|
||||||
|
}
|
||||||
|
db = db.WithContext(ctx)
|
||||||
|
db = db.Model(&Batch{})
|
||||||
|
db = db.Where("hash", hash)
|
||||||
|
|
||||||
|
if err := db.Updates(updateFields).Error; err != nil {
|
||||||
|
return fmt.Errorf("Batch.UpdateRollupStatus error: %w, batch hash: %v, status: %v", err, hash, status.String())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateCommitTxHashAndRollupStatus updates the commit transaction hash and rollup status for a batch.
|
||||||
|
func (o *Batch) UpdateCommitTxHashAndRollupStatus(ctx context.Context, hash string, commitTxHash string, status types.RollupStatus) error {
|
||||||
|
updateFields := make(map[string]interface{})
|
||||||
|
updateFields["commit_tx_hash"] = commitTxHash
|
||||||
|
updateFields["rollup_status"] = int(status)
|
||||||
|
if status == types.RollupCommitted {
|
||||||
|
updateFields["committed_at"] = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
db := o.db.WithContext(ctx)
|
||||||
|
db = db.Model(&Batch{})
|
||||||
|
db = db.Where("hash", hash)
|
||||||
|
|
||||||
|
if err := db.Updates(updateFields).Error; err != nil {
|
||||||
|
return fmt.Errorf("Batch.UpdateCommitTxHashAndRollupStatus error: %w, batch hash: %v, status: %v, commitTxHash: %v", err, hash, status.String(), commitTxHash)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateFinalizeTxHashAndRollupStatus updates the finalize transaction hash and rollup status for a batch.
|
||||||
|
func (o *Batch) UpdateFinalizeTxHashAndRollupStatus(ctx context.Context, hash string, finalizeTxHash string, status types.RollupStatus) error {
|
||||||
|
updateFields := make(map[string]interface{})
|
||||||
|
updateFields["finalize_tx_hash"] = finalizeTxHash
|
||||||
|
updateFields["rollup_status"] = int(status)
|
||||||
|
if status == types.RollupFinalized {
|
||||||
|
updateFields["finalized_at"] = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
db := o.db.WithContext(ctx)
|
||||||
|
db = db.Model(&Batch{})
|
||||||
|
db = db.Where("hash", hash)
|
||||||
|
|
||||||
|
if err := db.Updates(updateFields).Error; err != nil {
|
||||||
|
return fmt.Errorf("Batch.UpdateFinalizeTxHashAndRollupStatus error: %w, batch hash: %v, status: %v, commitTxHash: %v", err, hash, status.String(), finalizeTxHash)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateProofByHash updates the batch proof by hash.
|
||||||
|
// for unit test.
|
||||||
|
func (o *Batch) UpdateProofByHash(ctx context.Context, hash string, proof *message.AggProof, proofTimeSec uint64) error {
|
||||||
|
proofBytes, err := json.Marshal(proof)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Batch.UpdateProofByHash error: %w, batch hash: %v", err, hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFields := make(map[string]interface{})
|
||||||
|
updateFields["proof"] = proofBytes
|
||||||
|
updateFields["proof_time_sec"] = proofTimeSec
|
||||||
|
|
||||||
|
db := o.db.WithContext(ctx)
|
||||||
|
db = db.Model(&Batch{})
|
||||||
|
db = db.Where("hash", hash)
|
||||||
|
|
||||||
|
if err = db.Updates(updateFields).Error; err != nil {
|
||||||
|
return fmt.Errorf("Batch.UpdateProofByHash error: %w, batch hash: %v", err, hash)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,314 +0,0 @@
|
|||||||
package orm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/log"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
|
|
||||||
"scroll-tech/common/types"
|
|
||||||
"scroll-tech/common/types/message"
|
|
||||||
|
|
||||||
bridgeTypes "scroll-tech/bridge/internal/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// BlockBatch is structure of stored block batch message
|
|
||||||
type BlockBatch struct {
|
|
||||||
db *gorm.DB `gorm:"column:-"`
|
|
||||||
|
|
||||||
Hash string `json:"hash" gorm:"column:hash"`
|
|
||||||
Index uint64 `json:"index" gorm:"column:index"`
|
|
||||||
StartBlockNumber uint64 `json:"start_block_number" gorm:"column:start_block_number"`
|
|
||||||
StartBlockHash string `json:"start_block_hash" gorm:"column:start_block_hash"`
|
|
||||||
EndBlockNumber uint64 `json:"end_block_number" gorm:"column:end_block_number"`
|
|
||||||
EndBlockHash string `json:"end_block_hash" gorm:"column:end_block_hash"`
|
|
||||||
ParentHash string `json:"parent_hash" gorm:"column:parent_hash"`
|
|
||||||
StateRoot string `json:"state_root" gorm:"column:state_root"`
|
|
||||||
TotalTxNum uint64 `json:"total_tx_num" gorm:"column:total_tx_num"`
|
|
||||||
TotalL1TxNum uint64 `json:"total_l1_tx_num" gorm:"column:total_l1_tx_num"`
|
|
||||||
TotalL2Gas uint64 `json:"total_l2_gas" gorm:"column:total_l2_gas"`
|
|
||||||
ProvingStatus int `json:"proving_status" gorm:"column:proving_status;default:1"`
|
|
||||||
Proof []byte `json:"proof" gorm:"column:proof"`
|
|
||||||
ProofTimeSec uint64 `json:"proof_time_sec" gorm:"column:proof_time_sec;default:0"`
|
|
||||||
RollupStatus int `json:"rollup_status" gorm:"column:rollup_status;default:1"`
|
|
||||||
CommitTxHash string `json:"commit_tx_hash" gorm:"column:commit_tx_hash;default:NULL"`
|
|
||||||
OracleStatus int `json:"oracle_status" gorm:"column:oracle_status;default:1"`
|
|
||||||
OracleTxHash string `json:"oracle_tx_hash" gorm:"column:oracle_tx_hash;default:NULL"`
|
|
||||||
FinalizeTxHash string `json:"finalize_tx_hash" gorm:"column:finalize_tx_hash;default:NULL"`
|
|
||||||
CreatedAt time.Time `json:"created_at" gorm:"column:created_at;default:CURRENT_TIMESTAMP()"`
|
|
||||||
ProverAssignedAt *time.Time `json:"prover_assigned_at" gorm:"column:prover_assigned_at;default:NULL"`
|
|
||||||
ProvedAt *time.Time `json:"proved_at" gorm:"column:proved_at;default:NULL"`
|
|
||||||
CommittedAt *time.Time `json:"committed_at" gorm:"column:committed_at;default:NULL"`
|
|
||||||
FinalizedAt *time.Time `json:"finalized_at" gorm:"column:finalized_at;default:NULL"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBlockBatch create an blockBatchOrm instance
|
|
||||||
func NewBlockBatch(db *gorm.DB) *BlockBatch {
|
|
||||||
return &BlockBatch{db: db}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TableName define the BlockBatch table name
|
|
||||||
func (*BlockBatch) TableName() string {
|
|
||||||
return "block_batch"
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBatchCount get the batch count
|
|
||||||
func (o *BlockBatch) GetBatchCount() (int64, error) {
|
|
||||||
var count int64
|
|
||||||
if err := o.db.Model(&BlockBatch{}).Count(&count).Error; err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return count, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBlockBatches get the select block batches
|
|
||||||
func (o *BlockBatch) GetBlockBatches(fields map[string]interface{}, orderByList []string, limit int) ([]BlockBatch, error) {
|
|
||||||
var blockBatches []BlockBatch
|
|
||||||
db := o.db
|
|
||||||
for key, value := range fields {
|
|
||||||
db = db.Where(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, orderBy := range orderByList {
|
|
||||||
db = db.Order(orderBy)
|
|
||||||
}
|
|
||||||
|
|
||||||
if limit != 0 {
|
|
||||||
db = db.Limit(limit)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := db.Find(&blockBatches).Error; err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return blockBatches, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBlockBatchesHashByRollupStatus get the block batches by rollup status
|
|
||||||
func (o *BlockBatch) GetBlockBatchesHashByRollupStatus(status types.RollupStatus, limit int) ([]string, error) {
|
|
||||||
var blockBatches []BlockBatch
|
|
||||||
err := o.db.Select("hash").Where("rollup_status", int(status)).Order("index ASC").Limit(limit).Find(&blockBatches).Error
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var hashes []string
|
|
||||||
for _, v := range blockBatches {
|
|
||||||
hashes = append(hashes, v.Hash)
|
|
||||||
}
|
|
||||||
return hashes, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetVerifiedProofByHash get verified proof and instance comments by hash
|
|
||||||
func (o *BlockBatch) GetVerifiedProofByHash(hash string) (*message.AggProof, error) {
|
|
||||||
result := o.db.Model(&BlockBatch{}).Select("proof").Where("hash", hash).Where("proving_status", int(types.ProvingTaskVerified)).Row()
|
|
||||||
if result.Err() != nil {
|
|
||||||
return nil, result.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
var proofBytes []byte
|
|
||||||
if err := result.Scan(&proofBytes); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var proof message.AggProof
|
|
||||||
if err := json.Unmarshal(proofBytes, &proof); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &proof, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLatestBatch get the latest batch
|
|
||||||
// because we will `initializeGenesis()` when we start the `L2Watcher`, so a batch must exist.
|
|
||||||
func (o *BlockBatch) GetLatestBatch() (*BlockBatch, error) {
|
|
||||||
var blockBatch BlockBatch
|
|
||||||
err := o.db.Order("index DESC").Limit(1).First(&blockBatch).Error
|
|
||||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &blockBatch, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLatestBatchByRollupStatus get the latest block batch by rollup status
|
|
||||||
func (o *BlockBatch) GetLatestBatchByRollupStatus(rollupStatuses []types.RollupStatus) (*BlockBatch, error) {
|
|
||||||
var tmpRollupStatus []int
|
|
||||||
for _, v := range rollupStatuses {
|
|
||||||
tmpRollupStatus = append(tmpRollupStatus, int(v))
|
|
||||||
}
|
|
||||||
var blockBatch BlockBatch
|
|
||||||
err := o.db.Where("rollup_status IN (?)", tmpRollupStatus).Order("index DESC").Limit(1).First(&blockBatch).Error
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &blockBatch, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRollupStatusByHashList get rollup status by hash list
|
|
||||||
func (o *BlockBatch) GetRollupStatusByHashList(hashes []string) ([]types.RollupStatus, error) {
|
|
||||||
if len(hashes) == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var blockBatches []BlockBatch
|
|
||||||
err := o.db.Select("hash, rollup_status").Where("hash IN (?)", hashes).Find(&blockBatches).Error
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
statuses []types.RollupStatus
|
|
||||||
_statusMap = make(map[string]types.RollupStatus, len(hashes))
|
|
||||||
)
|
|
||||||
for _, _batch := range blockBatches {
|
|
||||||
_statusMap[_batch.Hash] = types.RollupStatus(_batch.RollupStatus)
|
|
||||||
}
|
|
||||||
for _, _hash := range hashes {
|
|
||||||
statuses = append(statuses, _statusMap[_hash])
|
|
||||||
}
|
|
||||||
|
|
||||||
return statuses, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InsertBlockBatchByBatchData insert a block batch data by the BatchData
|
|
||||||
func (o *BlockBatch) InsertBlockBatchByBatchData(tx *gorm.DB, batchData *bridgeTypes.BatchData) (int64, error) {
|
|
||||||
var db *gorm.DB
|
|
||||||
if tx != nil {
|
|
||||||
db = tx
|
|
||||||
} else {
|
|
||||||
db = o.db
|
|
||||||
}
|
|
||||||
|
|
||||||
numBlocks := len(batchData.Batch.Blocks)
|
|
||||||
insertBlockBatch := BlockBatch{
|
|
||||||
Hash: batchData.Hash().Hex(),
|
|
||||||
Index: batchData.Batch.BatchIndex,
|
|
||||||
StartBlockNumber: batchData.Batch.Blocks[0].BlockNumber,
|
|
||||||
StartBlockHash: batchData.Batch.Blocks[0].BlockHash.Hex(),
|
|
||||||
EndBlockNumber: batchData.Batch.Blocks[numBlocks-1].BlockNumber,
|
|
||||||
EndBlockHash: batchData.Batch.Blocks[numBlocks-1].BlockHash.Hex(),
|
|
||||||
ParentHash: batchData.Batch.ParentBatchHash.Hex(),
|
|
||||||
StateRoot: batchData.Batch.NewStateRoot.Hex(),
|
|
||||||
TotalTxNum: batchData.TotalTxNum,
|
|
||||||
TotalL1TxNum: batchData.TotalL1TxNum,
|
|
||||||
TotalL2Gas: batchData.TotalL2Gas,
|
|
||||||
CreatedAt: time.Now(),
|
|
||||||
}
|
|
||||||
result := db.Create(&insertBlockBatch)
|
|
||||||
if result.Error != nil {
|
|
||||||
log.Error("failed to insert block batch by batchData", "err", result.Error)
|
|
||||||
return 0, result.Error
|
|
||||||
}
|
|
||||||
return result.RowsAffected, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateProvingStatus update the proving status
|
|
||||||
func (o *BlockBatch) UpdateProvingStatus(hash string, status types.ProvingStatus) error {
|
|
||||||
updateFields := make(map[string]interface{})
|
|
||||||
updateFields["proving_status"] = int(status)
|
|
||||||
|
|
||||||
switch status {
|
|
||||||
case types.ProvingTaskAssigned:
|
|
||||||
updateFields["prover_assigned_at"] = time.Now()
|
|
||||||
case types.ProvingTaskUnassigned:
|
|
||||||
updateFields["prover_assigned_at"] = nil
|
|
||||||
case types.ProvingTaskProved, types.ProvingTaskVerified:
|
|
||||||
updateFields["proved_at"] = time.Now()
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := o.db.Model(&BlockBatch{}).Where("hash", hash).Updates(updateFields).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateRollupStatus update the rollup status
|
|
||||||
func (o *BlockBatch) UpdateRollupStatus(ctx context.Context, hash string, status types.RollupStatus) error {
|
|
||||||
updateFields := make(map[string]interface{})
|
|
||||||
updateFields["rollup_status"] = int(status)
|
|
||||||
|
|
||||||
switch status {
|
|
||||||
case types.RollupCommitted:
|
|
||||||
updateFields["committed_at"] = time.Now()
|
|
||||||
case types.RollupFinalized:
|
|
||||||
updateFields["finalized_at"] = time.Now()
|
|
||||||
}
|
|
||||||
if err := o.db.Model(&BlockBatch{}).WithContext(ctx).Where("hash", hash).Updates(updateFields).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateSkippedBatches update the skipped batches
|
|
||||||
func (o *BlockBatch) UpdateSkippedBatches() (int64, error) {
|
|
||||||
provingStatusList := []interface{}{
|
|
||||||
int(types.ProvingTaskSkipped),
|
|
||||||
int(types.ProvingTaskFailed),
|
|
||||||
}
|
|
||||||
result := o.db.Model(&BlockBatch{}).Where("rollup_status", int(types.RollupCommitted)).
|
|
||||||
Where("proving_status IN (?)", provingStatusList).Update("rollup_status", int(types.RollupFinalizationSkipped))
|
|
||||||
if result.Error != nil {
|
|
||||||
return 0, result.Error
|
|
||||||
}
|
|
||||||
return result.RowsAffected, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateCommitTxHashAndRollupStatus update the commit tx hash and rollup status
|
|
||||||
func (o *BlockBatch) UpdateCommitTxHashAndRollupStatus(ctx context.Context, hash string, commitTxHash string, status types.RollupStatus) error {
|
|
||||||
updateFields := make(map[string]interface{})
|
|
||||||
updateFields["commit_tx_hash"] = commitTxHash
|
|
||||||
updateFields["rollup_status"] = int(status)
|
|
||||||
if status == types.RollupCommitted {
|
|
||||||
updateFields["committed_at"] = time.Now()
|
|
||||||
}
|
|
||||||
if err := o.db.WithContext(ctx).Model(&BlockBatch{}).Where("hash", hash).Updates(updateFields).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateFinalizeTxHashAndRollupStatus update the finalize tx hash and rollup status
|
|
||||||
func (o *BlockBatch) UpdateFinalizeTxHashAndRollupStatus(ctx context.Context, hash string, finalizeTxHash string, status types.RollupStatus) error {
|
|
||||||
updateFields := make(map[string]interface{})
|
|
||||||
updateFields["finalize_tx_hash"] = finalizeTxHash
|
|
||||||
updateFields["rollup_status"] = int(status)
|
|
||||||
if status == types.RollupFinalized {
|
|
||||||
updateFields["finalized_at"] = time.Now()
|
|
||||||
}
|
|
||||||
if err := o.db.WithContext(ctx).Model(&BlockBatch{}).Where("hash", hash).Updates(updateFields).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateL2GasOracleStatusAndOracleTxHash update the l2 gas oracle status and oracle tx hash
|
|
||||||
func (o *BlockBatch) UpdateL2GasOracleStatusAndOracleTxHash(ctx context.Context, hash string, status types.GasOracleStatus, txHash string) error {
|
|
||||||
updateFields := make(map[string]interface{})
|
|
||||||
updateFields["oracle_status"] = int(status)
|
|
||||||
updateFields["oracle_tx_hash"] = txHash
|
|
||||||
if err := o.db.WithContext(ctx).Model(&BlockBatch{}).Where("hash", hash).Updates(updateFields).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateProofByHash update the block batch proof by hash
|
|
||||||
// for unit test
|
|
||||||
func (o *BlockBatch) UpdateProofByHash(ctx context.Context, hash string, proof *message.AggProof, proofTimeSec uint64) error {
|
|
||||||
proofBytes, err := json.Marshal(proof)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
updateFields := make(map[string]interface{})
|
|
||||||
updateFields["proof"] = proofBytes
|
|
||||||
updateFields["proof_time_sec"] = proofTimeSec
|
|
||||||
err = o.db.WithContext(ctx).Model(&BlockBatch{}).Where("hash", hash).Updates(updateFields).Error
|
|
||||||
if err != nil {
|
|
||||||
log.Error("failed to update proof", "err", err)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
@@ -1,155 +0,0 @@
|
|||||||
package orm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/log"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
|
|
||||||
"scroll-tech/bridge/internal/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// BlockTrace is structure of stored block trace message
|
|
||||||
type BlockTrace struct {
|
|
||||||
db *gorm.DB `gorm:"column:-"`
|
|
||||||
|
|
||||||
Number uint64 `json:"number" gorm:"number"`
|
|
||||||
Hash string `json:"hash" gorm:"hash"`
|
|
||||||
ParentHash string `json:"parent_hash" gorm:"parent_hash"`
|
|
||||||
Trace string `json:"trace" gorm:"column:trace"`
|
|
||||||
BatchHash string `json:"batch_hash" gorm:"batch_hash;default:NULL"`
|
|
||||||
TxNum uint64 `json:"tx_num" gorm:"tx_num"`
|
|
||||||
GasUsed uint64 `json:"gas_used" gorm:"gas_used"`
|
|
||||||
BlockTimestamp uint64 `json:"block_timestamp" gorm:"block_timestamp"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBlockTrace create an blockTraceOrm instance
|
|
||||||
func NewBlockTrace(db *gorm.DB) *BlockTrace {
|
|
||||||
return &BlockTrace{db: db}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TableName define the BlockTrace table name
|
|
||||||
func (*BlockTrace) TableName() string {
|
|
||||||
return "block_trace"
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetL2BlocksLatestHeight get the l2 blocks latest height
|
|
||||||
func (o *BlockTrace) GetL2BlocksLatestHeight() (int64, error) {
|
|
||||||
result := o.db.Model(&BlockTrace{}).Select("COALESCE(MAX(number), -1)").Row()
|
|
||||||
if result.Err() != nil {
|
|
||||||
return -1, result.Err()
|
|
||||||
}
|
|
||||||
var maxNumber int64
|
|
||||||
if err := result.Scan(&maxNumber); err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
return maxNumber, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetL2WrappedBlocks get the l2 wrapped blocks
|
|
||||||
func (o *BlockTrace) GetL2WrappedBlocks(fields map[string]interface{}) ([]*types.WrappedBlock, error) {
|
|
||||||
var blockTraces []BlockTrace
|
|
||||||
db := o.db.Select("trace")
|
|
||||||
for key, value := range fields {
|
|
||||||
db = db.Where(key, value)
|
|
||||||
}
|
|
||||||
if err := db.Find(&blockTraces).Error; err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var wrappedBlocks []*types.WrappedBlock
|
|
||||||
for _, v := range blockTraces {
|
|
||||||
var wrappedBlock types.WrappedBlock
|
|
||||||
if err := json.Unmarshal([]byte(v.Trace), &wrappedBlock); err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
wrappedBlocks = append(wrappedBlocks, &wrappedBlock)
|
|
||||||
}
|
|
||||||
return wrappedBlocks, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetL2BlockInfos get l2 block infos
|
|
||||||
func (o *BlockTrace) GetL2BlockInfos(fields map[string]interface{}, orderByList []string, limit int) ([]BlockTrace, error) {
|
|
||||||
var blockTraces []BlockTrace
|
|
||||||
db := o.db.Select("number, hash, parent_hash, batch_hash, tx_num, gas_used, block_timestamp")
|
|
||||||
for key, value := range fields {
|
|
||||||
db = db.Where(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, orderBy := range orderByList {
|
|
||||||
db = db.Order(orderBy)
|
|
||||||
}
|
|
||||||
|
|
||||||
if limit != 0 {
|
|
||||||
db = db.Limit(limit)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := db.Find(&blockTraces).Error; err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return blockTraces, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUnbatchedL2Blocks get unbatched l2 blocks
|
|
||||||
func (o *BlockTrace) GetUnbatchedL2Blocks(fields map[string]interface{}, orderByList []string, limit int) ([]BlockTrace, error) {
|
|
||||||
var unbatchedBlockTraces []BlockTrace
|
|
||||||
db := o.db.Select("number, hash, parent_hash, batch_hash, tx_num, gas_used, block_timestamp").Where("batch_hash is NULL")
|
|
||||||
for key, value := range fields {
|
|
||||||
db = db.Where(key, value)
|
|
||||||
}
|
|
||||||
if err := db.Find(&unbatchedBlockTraces).Error; err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return unbatchedBlockTraces, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InsertWrappedBlocks insert block to block trace
|
|
||||||
func (o *BlockTrace) InsertWrappedBlocks(blocks []*types.WrappedBlock) error {
|
|
||||||
var blockTraces []BlockTrace
|
|
||||||
for _, block := range blocks {
|
|
||||||
number := block.Header.Number.Uint64()
|
|
||||||
hash := block.Header.Hash().String()
|
|
||||||
txNum := len(block.Transactions)
|
|
||||||
mtime := block.Header.Time
|
|
||||||
gasCost := block.Header.GasUsed
|
|
||||||
|
|
||||||
data, err := json.Marshal(block)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("failed to marshal block", "hash", hash, "err", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpBlockTrace := BlockTrace{
|
|
||||||
Number: number,
|
|
||||||
Hash: hash,
|
|
||||||
ParentHash: block.Header.ParentHash.String(),
|
|
||||||
Trace: string(data),
|
|
||||||
TxNum: uint64(txNum),
|
|
||||||
GasUsed: gasCost,
|
|
||||||
BlockTimestamp: mtime,
|
|
||||||
}
|
|
||||||
blockTraces = append(blockTraces, tmpBlockTrace)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := o.db.Create(&blockTraces).Error; err != nil {
|
|
||||||
log.Error("failed to insert blockTraces", "err", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateBatchHashForL2Blocks update the batch_hash of block trace
|
|
||||||
func (o *BlockTrace) UpdateBatchHashForL2Blocks(tx *gorm.DB, numbers []uint64, batchHash string) error {
|
|
||||||
var db *gorm.DB
|
|
||||||
if tx != nil {
|
|
||||||
db = tx
|
|
||||||
} else {
|
|
||||||
db = o.db
|
|
||||||
}
|
|
||||||
|
|
||||||
err := db.Model(&BlockTrace{}).Where("number IN (?)", numbers).Update("batch_hash", batchHash).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
239
bridge/internal/orm/chunk.go
Normal file
239
bridge/internal/orm/chunk.go
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
package orm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"scroll-tech/common/types"
|
||||||
|
|
||||||
|
"github.com/scroll-tech/go-ethereum/log"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Chunk represents a chunk of blocks in the database.
|
||||||
|
type Chunk struct {
|
||||||
|
db *gorm.DB `gorm:"-"`
|
||||||
|
|
||||||
|
// chunk
|
||||||
|
Index uint64 `json:"index" gorm:"column:index"`
|
||||||
|
Hash string `json:"hash" gorm:"column:hash"`
|
||||||
|
StartBlockNumber uint64 `json:"start_block_number" gorm:"column:start_block_number"`
|
||||||
|
StartBlockHash string `json:"start_block_hash" gorm:"column:start_block_hash"`
|
||||||
|
EndBlockNumber uint64 `json:"end_block_number" gorm:"column:end_block_number"`
|
||||||
|
EndBlockHash string `json:"end_block_hash" gorm:"column:end_block_hash"`
|
||||||
|
StartBlockTime uint64 `json:"start_block_time" gorm:"column:start_block_time"`
|
||||||
|
TotalL1MessagesPoppedBefore uint64 `json:"total_l1_messages_popped_before" gorm:"column:total_l1_messages_popped_before"`
|
||||||
|
TotalL1MessagesPoppedInChunk uint32 `json:"total_l1_messages_popped_in_chunk" gorm:"column:total_l1_messages_popped_in_chunk"`
|
||||||
|
ParentChunkHash string `json:"parent_chunk_hash" gorm:"column:parent_chunk_hash"`
|
||||||
|
StateRoot string `json:"state_root" gorm:"column:state_root"`
|
||||||
|
ParentChunkStateRoot string `json:"parent_chunk_state_root" gorm:"column:parent_chunk_state_root"`
|
||||||
|
WithdrawRoot string `json:"withdraw_root" gorm:"column:withdraw_root"`
|
||||||
|
|
||||||
|
// proof
|
||||||
|
ProvingStatus int16 `json:"proving_status" gorm:"column:proving_status;default:1"`
|
||||||
|
Proof []byte `json:"proof" gorm:"column:proof;default:NULL"`
|
||||||
|
ProverAssignedAt *time.Time `json:"prover_assigned_at" gorm:"column:prover_assigned_at;default:NULL"`
|
||||||
|
ProvedAt *time.Time `json:"proved_at" gorm:"column:proved_at;default:NULL"`
|
||||||
|
ProofTimeSec int32 `json:"proof_time_sec" gorm:"column:proof_time_sec;default:NULL"`
|
||||||
|
|
||||||
|
// batch
|
||||||
|
BatchHash string `json:"batch_hash" gorm:"column:batch_hash;default:NULL"`
|
||||||
|
|
||||||
|
// metadata
|
||||||
|
TotalL2TxGas uint64 `json:"total_l2_tx_gas" gorm:"column:total_l2_tx_gas"`
|
||||||
|
TotalL2TxNum uint32 `json:"total_l2_tx_num" gorm:"column:total_l2_tx_num"`
|
||||||
|
TotalL1CommitCalldataSize uint32 `json:"total_l1_commit_calldata_size" gorm:"column:total_l1_commit_calldata_size"`
|
||||||
|
TotalL1CommitGas uint64 `json:"total_l1_commit_gas" gorm:"column:total_l1_commit_gas"`
|
||||||
|
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at"`
|
||||||
|
DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"column:deleted_at;default:NULL"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewChunk creates a new Chunk database instance.
|
||||||
|
func NewChunk(db *gorm.DB) *Chunk {
|
||||||
|
return &Chunk{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName returns the table name for the chunk model.
|
||||||
|
func (*Chunk) TableName() string {
|
||||||
|
return "chunk"
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetChunksInRange retrieves chunks within a given range (inclusive) from the database.
|
||||||
|
// The range is closed, i.e., it includes both start and end indices.
|
||||||
|
// The returned chunks are sorted in ascending order by their index.
|
||||||
|
func (o *Chunk) GetChunksInRange(ctx context.Context, startIndex uint64, endIndex uint64) ([]*Chunk, error) {
|
||||||
|
if startIndex > endIndex {
|
||||||
|
return nil, fmt.Errorf("Chunk.GetChunksInRange: start index should be less than or equal to end index, start index: %v, end index: %v", startIndex, endIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
db := o.db.WithContext(ctx)
|
||||||
|
db = db.Model(&Chunk{})
|
||||||
|
db = db.Where("index >= ? AND index <= ?", startIndex, endIndex)
|
||||||
|
db = db.Order("index ASC")
|
||||||
|
|
||||||
|
var chunks []*Chunk
|
||||||
|
if err := db.Find(&chunks).Error; err != nil {
|
||||||
|
return nil, fmt.Errorf("Chunk.GetChunksInRange error: %w, start index: %v, end index: %v", err, startIndex, endIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sanity check
|
||||||
|
if uint64(len(chunks)) != endIndex-startIndex+1 {
|
||||||
|
return nil, fmt.Errorf("Chunk.GetChunksInRange: incorrect number of chunks, expected: %v, got: %v, start index: %v, end index: %v", endIndex-startIndex+1, len(chunks), startIndex, endIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUnbatchedChunks retrieves unbatched chunks from the database.
|
||||||
|
func (o *Chunk) GetUnbatchedChunks(ctx context.Context) ([]*Chunk, error) {
|
||||||
|
db := o.db.WithContext(ctx)
|
||||||
|
db = db.Model(&Chunk{})
|
||||||
|
db = db.Where("batch_hash IS NULL")
|
||||||
|
db = db.Order("index asc")
|
||||||
|
|
||||||
|
var chunks []*Chunk
|
||||||
|
if err := db.Find(&chunks).Error; err != nil {
|
||||||
|
return nil, fmt.Errorf("Chunk.GetUnbatchedChunks error: %w", err)
|
||||||
|
}
|
||||||
|
return chunks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLatestChunk retrieves the latest chunk from the database.
|
||||||
|
func (o *Chunk) GetLatestChunk(ctx context.Context) (*Chunk, error) {
|
||||||
|
db := o.db.WithContext(ctx)
|
||||||
|
db = db.Model(&Chunk{})
|
||||||
|
db = db.Order("index desc")
|
||||||
|
|
||||||
|
var latestChunk Chunk
|
||||||
|
if err := db.First(&latestChunk).Error; err != nil {
|
||||||
|
return nil, fmt.Errorf("Chunk.GetLatestChunk error: %w", err)
|
||||||
|
}
|
||||||
|
return &latestChunk, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertChunk inserts a new chunk into the database.
|
||||||
|
func (o *Chunk) InsertChunk(ctx context.Context, chunk *types.Chunk, dbTX ...*gorm.DB) (*Chunk, error) {
|
||||||
|
if chunk == nil || len(chunk.Blocks) == 0 {
|
||||||
|
return nil, errors.New("invalid args")
|
||||||
|
}
|
||||||
|
|
||||||
|
var chunkIndex uint64
|
||||||
|
var totalL1MessagePoppedBefore uint64
|
||||||
|
var parentChunkHash string
|
||||||
|
var parentChunkStateRoot string
|
||||||
|
parentChunk, err := o.GetLatestChunk(ctx)
|
||||||
|
if err != nil && !errors.Is(errors.Unwrap(err), gorm.ErrRecordNotFound) {
|
||||||
|
log.Error("failed to get latest chunk", "err", err)
|
||||||
|
return nil, fmt.Errorf("Chunk.InsertChunk error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// if parentChunk==nil then err==gorm.ErrRecordNotFound, which means there's
|
||||||
|
// not chunk record in the db, we then use default empty values for the creating chunk;
|
||||||
|
// if parentChunk!=nil then err=nil, then we fill the parentChunk-related data into the creating chunk
|
||||||
|
if parentChunk != nil {
|
||||||
|
chunkIndex = parentChunk.Index + 1
|
||||||
|
totalL1MessagePoppedBefore = parentChunk.TotalL1MessagesPoppedBefore + uint64(parentChunk.TotalL1MessagesPoppedInChunk)
|
||||||
|
parentChunkHash = parentChunk.Hash
|
||||||
|
parentChunkStateRoot = parentChunk.StateRoot
|
||||||
|
}
|
||||||
|
|
||||||
|
hash, err := chunk.Hash(totalL1MessagePoppedBefore)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("failed to get chunk hash", "err", err)
|
||||||
|
return nil, fmt.Errorf("Chunk.InsertChunk error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalL2TxGas uint64
|
||||||
|
var totalL2TxNum uint64
|
||||||
|
var totalL1CommitCalldataSize uint64
|
||||||
|
var totalL1CommitGas uint64
|
||||||
|
for _, block := range chunk.Blocks {
|
||||||
|
totalL2TxGas += block.Header.GasUsed
|
||||||
|
totalL2TxNum += block.L2TxsNum()
|
||||||
|
totalL1CommitCalldataSize += block.EstimateL1CommitCalldataSize()
|
||||||
|
totalL1CommitGas += block.EstimateL1CommitGas()
|
||||||
|
}
|
||||||
|
|
||||||
|
numBlocks := len(chunk.Blocks)
|
||||||
|
newChunk := Chunk{
|
||||||
|
Index: chunkIndex,
|
||||||
|
Hash: hash.Hex(),
|
||||||
|
StartBlockNumber: chunk.Blocks[0].Header.Number.Uint64(),
|
||||||
|
StartBlockHash: chunk.Blocks[0].Header.Hash().Hex(),
|
||||||
|
EndBlockNumber: chunk.Blocks[numBlocks-1].Header.Number.Uint64(),
|
||||||
|
EndBlockHash: chunk.Blocks[numBlocks-1].Header.Hash().Hex(),
|
||||||
|
TotalL2TxGas: totalL2TxGas,
|
||||||
|
TotalL2TxNum: uint32(totalL2TxNum),
|
||||||
|
TotalL1CommitCalldataSize: uint32(totalL1CommitCalldataSize),
|
||||||
|
TotalL1CommitGas: totalL1CommitGas,
|
||||||
|
StartBlockTime: chunk.Blocks[0].Header.Time,
|
||||||
|
TotalL1MessagesPoppedBefore: totalL1MessagePoppedBefore,
|
||||||
|
TotalL1MessagesPoppedInChunk: uint32(chunk.NumL1Messages(totalL1MessagePoppedBefore)),
|
||||||
|
ParentChunkHash: parentChunkHash,
|
||||||
|
StateRoot: chunk.Blocks[numBlocks-1].Header.Root.Hex(),
|
||||||
|
ParentChunkStateRoot: parentChunkStateRoot,
|
||||||
|
WithdrawRoot: chunk.Blocks[numBlocks-1].WithdrawRoot.Hex(),
|
||||||
|
ProvingStatus: int16(types.ProvingTaskUnassigned),
|
||||||
|
}
|
||||||
|
|
||||||
|
db := o.db
|
||||||
|
if len(dbTX) > 0 && dbTX[0] != nil {
|
||||||
|
db = dbTX[0]
|
||||||
|
}
|
||||||
|
db = db.WithContext(ctx)
|
||||||
|
db = db.Model(&Chunk{})
|
||||||
|
|
||||||
|
if err := db.Create(&newChunk).Error; err != nil {
|
||||||
|
return nil, fmt.Errorf("Chunk.InsertChunk error: %w, chunk hash: %v", err, newChunk.Hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &newChunk, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateProvingStatus updates the proving status of a chunk.
|
||||||
|
func (o *Chunk) UpdateProvingStatus(ctx context.Context, hash string, status types.ProvingStatus, dbTX ...*gorm.DB) error {
|
||||||
|
updateFields := make(map[string]interface{})
|
||||||
|
updateFields["proving_status"] = int(status)
|
||||||
|
|
||||||
|
switch status {
|
||||||
|
case types.ProvingTaskAssigned:
|
||||||
|
updateFields["prover_assigned_at"] = time.Now()
|
||||||
|
case types.ProvingTaskUnassigned:
|
||||||
|
updateFields["prover_assigned_at"] = nil
|
||||||
|
case types.ProvingTaskProved, types.ProvingTaskVerified:
|
||||||
|
updateFields["proved_at"] = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
db := o.db
|
||||||
|
if len(dbTX) > 0 && dbTX[0] != nil {
|
||||||
|
db = dbTX[0]
|
||||||
|
}
|
||||||
|
db = db.WithContext(ctx)
|
||||||
|
db = db.Model(&Chunk{})
|
||||||
|
db = db.Where("hash", hash)
|
||||||
|
|
||||||
|
if err := db.Updates(updateFields).Error; err != nil {
|
||||||
|
return fmt.Errorf("Chunk.UpdateProvingStatus error: %w, chunk hash: %v, status: %v", err, hash, status.String())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateBatchHashInRange updates the batch_hash for chunks within the specified range (inclusive).
|
||||||
|
// The range is closed, i.e., it includes both start and end indices.
|
||||||
|
func (o *Chunk) UpdateBatchHashInRange(ctx context.Context, startIndex uint64, endIndex uint64, batchHash string, dbTX ...*gorm.DB) error {
|
||||||
|
db := o.db
|
||||||
|
if len(dbTX) > 0 && dbTX[0] != nil {
|
||||||
|
db = dbTX[0]
|
||||||
|
}
|
||||||
|
db = db.WithContext(ctx)
|
||||||
|
db = db.Model(&Chunk{})
|
||||||
|
db = db.Where("index >= ? AND index <= ?", startIndex, endIndex)
|
||||||
|
|
||||||
|
if err := db.Update("batch_hash", batchHash).Error; err != nil {
|
||||||
|
return fmt.Errorf("Chunk.UpdateBatchHashInRange error: %w, start index: %v, end index: %v, batch hash: %v", err, startIndex, endIndex, batchHash)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
package orm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"gorm.io/gorm"
|
|
||||||
|
|
||||||
bridgeTypes "scroll-tech/bridge/internal/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AddBatchInfoToDB inserts the batch information to the BlockBatch table and updates the batch_hash
|
|
||||||
// in all blocks included in the batch.
|
|
||||||
func AddBatchInfoToDB(db *gorm.DB, batchData *bridgeTypes.BatchData) error {
|
|
||||||
blockBatch := NewBlockBatch(db)
|
|
||||||
blockTrace := NewBlockTrace(db)
|
|
||||||
err := db.Transaction(func(tx *gorm.DB) error {
|
|
||||||
rowsAffected, dbTxErr := blockBatch.InsertBlockBatchByBatchData(tx, batchData)
|
|
||||||
if dbTxErr != nil {
|
|
||||||
return dbTxErr
|
|
||||||
}
|
|
||||||
if rowsAffected != 1 {
|
|
||||||
dbTxErr = errors.New("the InsertBlockBatchByBatchData affected row is not 1")
|
|
||||||
return dbTxErr
|
|
||||||
}
|
|
||||||
|
|
||||||
var blockIDs = make([]uint64, len(batchData.Batch.Blocks))
|
|
||||||
for i, block := range batchData.Batch.Blocks {
|
|
||||||
blockIDs[i] = block.BlockNumber
|
|
||||||
}
|
|
||||||
|
|
||||||
dbTxErr = blockTrace.UpdateBatchHashForL2Blocks(tx, blockIDs, batchData.Hash().Hex())
|
|
||||||
if dbTxErr != nil {
|
|
||||||
return dbTxErr
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
@@ -2,8 +2,9 @@ package orm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/log"
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"scroll-tech/common/types"
|
"scroll-tech/common/types"
|
||||||
@@ -13,14 +14,19 @@ import (
|
|||||||
type L1Block struct {
|
type L1Block struct {
|
||||||
db *gorm.DB `gorm:"column:-"`
|
db *gorm.DB `gorm:"column:-"`
|
||||||
|
|
||||||
Number uint64 `json:"number" gorm:"column:number"`
|
// block
|
||||||
Hash string `json:"hash" gorm:"column:hash"`
|
Number uint64 `json:"number" gorm:"column:number"`
|
||||||
HeaderRLP string `json:"header_rlp" gorm:"column:header_rlp"`
|
Hash string `json:"hash" gorm:"column:hash"`
|
||||||
BaseFee uint64 `json:"base_fee" gorm:"column:base_fee"`
|
BaseFee uint64 `json:"base_fee" gorm:"column:base_fee"`
|
||||||
BlockStatus int `json:"block_status" gorm:"column:block_status;default:1"`
|
|
||||||
ImportTxHash string `json:"import_tx_hash" gorm:"column:import_tx_hash;default:NULL"`
|
// oracle
|
||||||
GasOracleStatus int `json:"oracle_status" gorm:"column:oracle_status;default:1"`
|
GasOracleStatus int16 `json:"oracle_status" gorm:"column:oracle_status;default:1"`
|
||||||
OracleTxHash string `json:"oracle_tx_hash" gorm:"column:oracle_tx_hash;default:NULL"`
|
OracleTxHash string `json:"oracle_tx_hash" gorm:"column:oracle_tx_hash;default:NULL"`
|
||||||
|
|
||||||
|
// metadata
|
||||||
|
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at"`
|
||||||
|
DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"column:deleted_at;default:NULL"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewL1Block create an l1Block instance
|
// NewL1Block create an l1Block instance
|
||||||
@@ -34,54 +40,64 @@ func (*L1Block) TableName() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetLatestL1BlockHeight get the latest l1 block height
|
// GetLatestL1BlockHeight get the latest l1 block height
|
||||||
func (l *L1Block) GetLatestL1BlockHeight() (uint64, error) {
|
func (o *L1Block) GetLatestL1BlockHeight(ctx context.Context) (uint64, error) {
|
||||||
result := l.db.Model(&L1Block{}).Select("COALESCE(MAX(number), 0)").Row()
|
db := o.db.WithContext(ctx)
|
||||||
if result.Err() != nil {
|
db = db.Model(&L1Block{})
|
||||||
return 0, result.Err()
|
db = db.Select("COALESCE(MAX(number), 0)")
|
||||||
}
|
|
||||||
|
|
||||||
var maxNumber uint64
|
var maxNumber uint64
|
||||||
if err := result.Scan(&maxNumber); err != nil {
|
if err := db.Row().Scan(&maxNumber); err != nil {
|
||||||
return 0, err
|
return 0, fmt.Errorf("L1Block.GetLatestL1BlockHeight error: %w", err)
|
||||||
}
|
}
|
||||||
return maxNumber, nil
|
return maxNumber, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetL1Blocks get the l1 blocks
|
// GetL1Blocks get the l1 blocks
|
||||||
func (l *L1Block) GetL1Blocks(fields map[string]interface{}) ([]L1Block, error) {
|
func (o *L1Block) GetL1Blocks(ctx context.Context, fields map[string]interface{}) ([]L1Block, error) {
|
||||||
var l1Blocks []L1Block
|
db := o.db.WithContext(ctx)
|
||||||
db := l.db
|
db = db.Model(&L1Block{})
|
||||||
|
|
||||||
for key, value := range fields {
|
for key, value := range fields {
|
||||||
db = db.Where(key, value)
|
db = db.Where(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
db = db.Order("number ASC")
|
db = db.Order("number ASC")
|
||||||
|
|
||||||
|
var l1Blocks []L1Block
|
||||||
if err := db.Find(&l1Blocks).Error; err != nil {
|
if err := db.Find(&l1Blocks).Error; err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("L1Block.GetL1Blocks error: %w, fields: %v", err, fields)
|
||||||
}
|
}
|
||||||
return l1Blocks, nil
|
return l1Blocks, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// InsertL1Blocks batch insert l1 blocks
|
// InsertL1Blocks batch insert l1 blocks
|
||||||
func (l *L1Block) InsertL1Blocks(ctx context.Context, blocks []L1Block) error {
|
func (o *L1Block) InsertL1Blocks(ctx context.Context, blocks []L1Block) error {
|
||||||
if len(blocks) == 0 {
|
if len(blocks) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err := l.db.WithContext(ctx).Create(&blocks).Error
|
db := o.db.WithContext(ctx)
|
||||||
if err != nil {
|
db = db.Model(&L1Block{})
|
||||||
log.Error("failed to insert L1 Blocks", "err", err)
|
|
||||||
|
if err := db.Create(&blocks).Error; err != nil {
|
||||||
|
return fmt.Errorf("L1Block.InsertL1Blocks error: %w", err)
|
||||||
}
|
}
|
||||||
return err
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateL1GasOracleStatusAndOracleTxHash update l1 gas oracle status and oracle tx hash
|
// UpdateL1GasOracleStatusAndOracleTxHash update l1 gas oracle status and oracle tx hash
|
||||||
func (l *L1Block) UpdateL1GasOracleStatusAndOracleTxHash(ctx context.Context, blockHash string, status types.GasOracleStatus, txHash string) error {
|
func (o *L1Block) UpdateL1GasOracleStatusAndOracleTxHash(ctx context.Context, blockHash string, status types.GasOracleStatus, txHash string) error {
|
||||||
updateFields := map[string]interface{}{
|
updateFields := map[string]interface{}{
|
||||||
"oracle_status": int(status),
|
"oracle_status": int(status),
|
||||||
"oracle_tx_hash": txHash,
|
"oracle_tx_hash": txHash,
|
||||||
}
|
}
|
||||||
if err := l.db.WithContext(ctx).Model(&L1Block{}).Where("hash", blockHash).Updates(updateFields).Error; err != nil {
|
|
||||||
return err
|
db := o.db.WithContext(ctx)
|
||||||
|
db = db.Model(&L1Block{})
|
||||||
|
db = db.Where("hash", blockHash)
|
||||||
|
|
||||||
|
if err := db.Updates(updateFields).Error; err != nil {
|
||||||
|
return fmt.Errorf("L1Block.UpdateL1GasOracleStatusAndOracleTxHash error: %w, block hash: %v, status: %v, tx hash: %v", err, blockHash, status.String(), txHash)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package orm
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/log"
|
"github.com/scroll-tech/go-ethereum/log"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
@@ -25,6 +26,11 @@ type L1Message struct {
|
|||||||
Layer1Hash string `json:"layer1_hash" gorm:"column:layer1_hash"`
|
Layer1Hash string `json:"layer1_hash" gorm:"column:layer1_hash"`
|
||||||
Layer2Hash string `json:"layer2_hash" gorm:"column:layer2_hash;default:NULL"`
|
Layer2Hash string `json:"layer2_hash" gorm:"column:layer2_hash;default:NULL"`
|
||||||
Status int `json:"status" gorm:"column:status;default:1"`
|
Status int `json:"status" gorm:"column:status;default:1"`
|
||||||
|
|
||||||
|
// metadata
|
||||||
|
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at"`
|
||||||
|
DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"column:deleted_at;default:NULL"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewL1Message create an L1MessageOrm instance
|
// NewL1Message create an L1MessageOrm instance
|
||||||
@@ -52,6 +58,16 @@ func (m *L1Message) GetLayer1LatestWatchedHeight() (int64, error) {
|
|||||||
return -1, nil
|
return -1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetLayer1LatestMessageWithLayer2Hash returns latest l1 message with layer2 hash
|
||||||
|
func (m *L1Message) GetLayer1LatestMessageWithLayer2Hash() (*L1Message, error) {
|
||||||
|
var msg *L1Message
|
||||||
|
err := m.db.Where("layer2_hash IS NOT NULL").Order("queue_index DESC").First(&msg).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetL1MessagesByStatus fetch list of unprocessed messages given msg status
|
// GetL1MessagesByStatus fetch list of unprocessed messages given msg status
|
||||||
func (m *L1Message) GetL1MessagesByStatus(status types.MsgStatus, limit uint64) ([]L1Message, error) {
|
func (m *L1Message) GetL1MessagesByStatus(status types.MsgStatus, limit uint64) ([]L1Message, error) {
|
||||||
var msgs []L1Message
|
var msgs []L1Message
|
||||||
|
|||||||
235
bridge/internal/orm/l2_block.go
Normal file
235
bridge/internal/orm/l2_block.go
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
package orm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
|
gethTypes "github.com/scroll-tech/go-ethereum/core/types"
|
||||||
|
"github.com/scroll-tech/go-ethereum/log"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
|
||||||
|
"scroll-tech/common/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// L2Block represents a l2 block in the database.
|
||||||
|
type L2Block struct {
|
||||||
|
db *gorm.DB `gorm:"column:-"`
|
||||||
|
|
||||||
|
// block
|
||||||
|
Number uint64 `json:"number" gorm:"number"`
|
||||||
|
Hash string `json:"hash" gorm:"hash"`
|
||||||
|
ParentHash string `json:"parent_hash" gorm:"parent_hash"`
|
||||||
|
Header string `json:"header" gorm:"header"`
|
||||||
|
Transactions string `json:"transactions" gorm:"transactions"`
|
||||||
|
WithdrawRoot string `json:"withdraw_root" gorm:"withdraw_root"`
|
||||||
|
StateRoot string `json:"state_root" gorm:"state_root"`
|
||||||
|
TxNum uint32 `json:"tx_num" gorm:"tx_num"`
|
||||||
|
GasUsed uint64 `json:"gas_used" gorm:"gas_used"`
|
||||||
|
BlockTimestamp uint64 `json:"block_timestamp" gorm:"block_timestamp"`
|
||||||
|
|
||||||
|
// chunk
|
||||||
|
ChunkHash string `json:"chunk_hash" gorm:"chunk_hash;default:NULL"`
|
||||||
|
|
||||||
|
// metadata
|
||||||
|
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at"`
|
||||||
|
DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"column:deleted_at;default:NULL"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewL2Block creates a new L2Block instance
|
||||||
|
func NewL2Block(db *gorm.DB) *L2Block {
|
||||||
|
return &L2Block{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName returns the name of the "l2_block" table.
|
||||||
|
func (*L2Block) TableName() string {
|
||||||
|
return "l2_block"
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetL2BlocksLatestHeight retrieves the height of the latest L2 block.
|
||||||
|
// If the l2_block table is empty, it returns 0 to represent the genesis block height.
|
||||||
|
func (o *L2Block) GetL2BlocksLatestHeight(ctx context.Context) (uint64, error) {
|
||||||
|
db := o.db.WithContext(ctx)
|
||||||
|
db = db.Model(&L2Block{})
|
||||||
|
db = db.Select("COALESCE(MAX(number), 0)")
|
||||||
|
|
||||||
|
var maxNumber uint64
|
||||||
|
if err := db.Row().Scan(&maxNumber); err != nil {
|
||||||
|
return 0, fmt.Errorf("L2Block.GetL2BlocksLatestHeight error: %w", err)
|
||||||
|
}
|
||||||
|
return maxNumber, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUnchunkedBlocks get the l2 blocks that have not been put into a chunk.
|
||||||
|
// The returned blocks are sorted in ascending order by their block number.
|
||||||
|
func (o *L2Block) GetUnchunkedBlocks(ctx context.Context) ([]*types.WrappedBlock, error) {
|
||||||
|
db := o.db.WithContext(ctx)
|
||||||
|
db = db.Model(&L2Block{})
|
||||||
|
db = db.Select("header, transactions, withdraw_root")
|
||||||
|
db = db.Where("chunk_hash IS NULL")
|
||||||
|
db = db.Order("number ASC")
|
||||||
|
|
||||||
|
var l2Blocks []L2Block
|
||||||
|
if err := db.Find(&l2Blocks).Error; err != nil {
|
||||||
|
return nil, fmt.Errorf("L2Block.GetUnchunkedBlocks error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var wrappedBlocks []*types.WrappedBlock
|
||||||
|
for _, v := range l2Blocks {
|
||||||
|
var wrappedBlock types.WrappedBlock
|
||||||
|
|
||||||
|
if err := json.Unmarshal([]byte(v.Transactions), &wrappedBlock.Transactions); err != nil {
|
||||||
|
return nil, fmt.Errorf("L2Block.GetUnchunkedBlocks error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wrappedBlock.Header = &gethTypes.Header{}
|
||||||
|
if err := json.Unmarshal([]byte(v.Header), wrappedBlock.Header); err != nil {
|
||||||
|
return nil, fmt.Errorf("L2Block.GetUnchunkedBlocks error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wrappedBlock.WithdrawRoot = common.HexToHash(v.WithdrawRoot)
|
||||||
|
wrappedBlocks = append(wrappedBlocks, &wrappedBlock)
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrappedBlocks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetL2Blocks retrieves selected L2Blocks from the database.
|
||||||
|
// The returned L2Blocks are sorted in ascending order by their block number.
|
||||||
|
func (o *L2Block) GetL2Blocks(ctx context.Context, fields map[string]interface{}, orderByList []string, limit int) ([]*L2Block, error) {
|
||||||
|
db := o.db.WithContext(ctx)
|
||||||
|
db = db.Model(&L2Block{})
|
||||||
|
|
||||||
|
for key, value := range fields {
|
||||||
|
db = db.Where(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, orderBy := range orderByList {
|
||||||
|
db = db.Order(orderBy)
|
||||||
|
}
|
||||||
|
|
||||||
|
if limit > 0 {
|
||||||
|
db = db.Limit(limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
db = db.Order("number ASC")
|
||||||
|
|
||||||
|
var l2Blocks []*L2Block
|
||||||
|
if err := db.Find(&l2Blocks).Error; err != nil {
|
||||||
|
return nil, fmt.Errorf("L2Block.GetL2Blocks error: %w, fields: %v, orderByList: %v", err, fields, orderByList)
|
||||||
|
}
|
||||||
|
return l2Blocks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetL2BlocksInRange retrieves the L2 blocks within the specified range (inclusive).
|
||||||
|
// The range is closed, i.e., it includes both start and end block numbers.
|
||||||
|
// The returned blocks are sorted in ascending order by their block number.
|
||||||
|
func (o *L2Block) GetL2BlocksInRange(ctx context.Context, startBlockNumber uint64, endBlockNumber uint64) ([]*types.WrappedBlock, error) {
|
||||||
|
if startBlockNumber > endBlockNumber {
|
||||||
|
return nil, fmt.Errorf("L2Block.GetL2BlocksInRange: start block number should be less than or equal to end block number, start block: %v, end block: %v", startBlockNumber, endBlockNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
db := o.db.WithContext(ctx)
|
||||||
|
db = db.Model(&L2Block{})
|
||||||
|
db = db.Select("header, transactions, withdraw_root")
|
||||||
|
db = db.Where("number >= ? AND number <= ?", startBlockNumber, endBlockNumber)
|
||||||
|
db = db.Order("number ASC")
|
||||||
|
|
||||||
|
var l2Blocks []L2Block
|
||||||
|
if err := db.Find(&l2Blocks).Error; err != nil {
|
||||||
|
return nil, fmt.Errorf("L2Block.GetL2BlocksInRange error: %w, start block: %v, end block: %v", err, startBlockNumber, endBlockNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sanity check
|
||||||
|
if uint64(len(l2Blocks)) != endBlockNumber-startBlockNumber+1 {
|
||||||
|
return nil, fmt.Errorf("L2Block.GetL2BlocksInRange: unexpected number of results, expected: %v, got: %v", endBlockNumber-startBlockNumber+1, len(l2Blocks))
|
||||||
|
}
|
||||||
|
|
||||||
|
var wrappedBlocks []*types.WrappedBlock
|
||||||
|
for _, v := range l2Blocks {
|
||||||
|
var wrappedBlock types.WrappedBlock
|
||||||
|
|
||||||
|
if err := json.Unmarshal([]byte(v.Transactions), &wrappedBlock.Transactions); err != nil {
|
||||||
|
return nil, fmt.Errorf("L2Block.GetL2BlocksInRange error: %w, start block: %v, end block: %v", err, startBlockNumber, endBlockNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
wrappedBlock.Header = &gethTypes.Header{}
|
||||||
|
if err := json.Unmarshal([]byte(v.Header), wrappedBlock.Header); err != nil {
|
||||||
|
return nil, fmt.Errorf("L2Block.GetL2BlocksInRange error: %w, start block: %v, end block: %v", err, startBlockNumber, endBlockNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
wrappedBlock.WithdrawRoot = common.HexToHash(v.WithdrawRoot)
|
||||||
|
wrappedBlocks = append(wrappedBlocks, &wrappedBlock)
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrappedBlocks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertL2Blocks inserts l2 blocks into the "l2_block" table.
|
||||||
|
func (o *L2Block) InsertL2Blocks(ctx context.Context, blocks []*types.WrappedBlock) error {
|
||||||
|
var l2Blocks []L2Block
|
||||||
|
for _, block := range blocks {
|
||||||
|
header, err := json.Marshal(block.Header)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("failed to marshal block header", "hash", block.Header.Hash().String(), "err", err)
|
||||||
|
return fmt.Errorf("L2Block.InsertL2Blocks error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
txs, err := json.Marshal(block.Transactions)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("failed to marshal transactions", "hash", block.Header.Hash().String(), "err", err)
|
||||||
|
return fmt.Errorf("L2Block.InsertL2Blocks error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
l2Block := L2Block{
|
||||||
|
Number: block.Header.Number.Uint64(),
|
||||||
|
Hash: block.Header.Hash().String(),
|
||||||
|
ParentHash: block.Header.ParentHash.String(),
|
||||||
|
Transactions: string(txs),
|
||||||
|
WithdrawRoot: block.WithdrawRoot.Hex(),
|
||||||
|
StateRoot: block.Header.Root.Hex(),
|
||||||
|
TxNum: uint32(len(block.Transactions)),
|
||||||
|
GasUsed: block.Header.GasUsed,
|
||||||
|
BlockTimestamp: block.Header.Time,
|
||||||
|
Header: string(header),
|
||||||
|
}
|
||||||
|
l2Blocks = append(l2Blocks, l2Block)
|
||||||
|
}
|
||||||
|
|
||||||
|
db := o.db.WithContext(ctx)
|
||||||
|
db = db.Model(&L2Block{})
|
||||||
|
|
||||||
|
if err := db.Create(&l2Blocks).Error; err != nil {
|
||||||
|
return fmt.Errorf("L2Block.InsertL2Blocks error: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateChunkHashInRange updates the chunk_hash of block tx within the specified range (inclusive).
|
||||||
|
// The range is closed, i.e., it includes both start and end indices.
|
||||||
|
// This function ensures the number of rows updated must equal to (endIndex - startIndex + 1).
|
||||||
|
// If the rows affected do not match this expectation, an error is returned.
|
||||||
|
func (o *L2Block) UpdateChunkHashInRange(ctx context.Context, startIndex uint64, endIndex uint64, chunkHash string, dbTX ...*gorm.DB) error {
|
||||||
|
db := o.db
|
||||||
|
if len(dbTX) > 0 && dbTX[0] != nil {
|
||||||
|
db = dbTX[0]
|
||||||
|
}
|
||||||
|
db = db.WithContext(ctx)
|
||||||
|
db = db.Model(&L2Block{})
|
||||||
|
db = db.Where("number >= ? AND number <= ?", startIndex, endIndex)
|
||||||
|
|
||||||
|
tx := db.Update("chunk_hash", chunkHash)
|
||||||
|
if tx.Error != nil {
|
||||||
|
return fmt.Errorf("L2Block.UpdateChunkHashInRange error: %w, start index: %v, end index: %v, chunk hash: %v", tx.Error, startIndex, endIndex, chunkHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sanity check
|
||||||
|
if uint64(tx.RowsAffected) != endIndex-startIndex+1 {
|
||||||
|
return fmt.Errorf("L2Block.UpdateChunkHashInRange: incorrect number of rows affected, expected: %v, got: %v", endIndex-startIndex+1, tx.RowsAffected)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
package orm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/log"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
|
|
||||||
"scroll-tech/common/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// L2Message is structure of stored layer2 bridge message
|
|
||||||
type L2Message struct {
|
|
||||||
db *gorm.DB `gorm:"column:-"`
|
|
||||||
|
|
||||||
Nonce uint64 `json:"nonce" gorm:"column:nonce"`
|
|
||||||
MsgHash string `json:"msg_hash" gorm:"column:msg_hash"`
|
|
||||||
Height uint64 `json:"height" gorm:"column:height"`
|
|
||||||
Sender string `json:"sender" gorm:"column:sender"`
|
|
||||||
Value string `json:"value" gorm:"column:value"`
|
|
||||||
Target string `json:"target" gorm:"column:target"`
|
|
||||||
Calldata string `json:"calldata" gorm:"column:calldata"`
|
|
||||||
Layer2Hash string `json:"layer2_hash" gorm:"column:layer2_hash"`
|
|
||||||
Layer1Hash string `json:"layer1_hash" gorm:"column:layer1_hash;default:NULL"`
|
|
||||||
Proof string `json:"proof" gorm:"column:proof;default:NULL"`
|
|
||||||
Status int `json:"status" gorm:"column:status;default:1"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewL2Message create an L2Message instance
|
|
||||||
func NewL2Message(db *gorm.DB) *L2Message {
|
|
||||||
return &L2Message{db: db}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TableName define the L2Message table name
|
|
||||||
func (*L2Message) TableName() string {
|
|
||||||
return "l2_message"
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetL2Messages fetch list of messages given msg status
|
|
||||||
func (m *L2Message) GetL2Messages(fields map[string]interface{}, orderByList []string, limit int) ([]L2Message, error) {
|
|
||||||
var l2MsgList []L2Message
|
|
||||||
db := m.db
|
|
||||||
for key, value := range fields {
|
|
||||||
db = db.Where(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, orderBy := range orderByList {
|
|
||||||
db = db.Order(orderBy)
|
|
||||||
}
|
|
||||||
|
|
||||||
if limit != 0 {
|
|
||||||
db = db.Limit(limit)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := db.Find(&l2MsgList).Error; err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return l2MsgList, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLayer2LatestWatchedHeight returns latest height stored in the table
|
|
||||||
func (m *L2Message) GetLayer2LatestWatchedHeight() (int64, error) {
|
|
||||||
// @note It's not correct, since we may don't have message in some blocks.
|
|
||||||
// But it will only be called at start, some redundancy is acceptable.
|
|
||||||
result := m.db.Model(&L2Message{}).Select("COALESCE(MAX(height), -1)").Row()
|
|
||||||
if result.Err() != nil {
|
|
||||||
return -1, result.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
var maxNumber int64
|
|
||||||
if err := result.Scan(&maxNumber); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return maxNumber, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetL2MessageByNonce fetch message by nonce
|
|
||||||
// for unit test
|
|
||||||
func (m *L2Message) GetL2MessageByNonce(nonce uint64) (*L2Message, error) {
|
|
||||||
var msg L2Message
|
|
||||||
err := m.db.Where("nonce", nonce).First(&msg).Error
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &msg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SaveL2Messages batch save a list of layer2 messages
|
|
||||||
func (m *L2Message) SaveL2Messages(ctx context.Context, messages []L2Message) error {
|
|
||||||
if len(messages) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
err := m.db.WithContext(ctx).Create(&messages).Error
|
|
||||||
if err != nil {
|
|
||||||
nonces := make([]uint64, 0, len(messages))
|
|
||||||
heights := make([]uint64, 0, len(messages))
|
|
||||||
for _, msg := range messages {
|
|
||||||
nonces = append(nonces, msg.Nonce)
|
|
||||||
heights = append(heights, msg.Height)
|
|
||||||
}
|
|
||||||
log.Error("failed to insert layer2Messages", "nonces", nonces, "heights", heights, "err", err)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateLayer2Status updates message stauts, given message hash
|
|
||||||
func (m *L2Message) UpdateLayer2Status(ctx context.Context, msgHash string, status types.MsgStatus) error {
|
|
||||||
err := m.db.Model(&L2Message{}).WithContext(ctx).Where("msg_hash", msgHash).Update("status", int(status)).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateLayer2StatusAndLayer1Hash updates message stauts and layer1 transaction hash, given message hash
|
|
||||||
func (m *L2Message) UpdateLayer2StatusAndLayer1Hash(ctx context.Context, msgHash string, status types.MsgStatus, layer1Hash string) error {
|
|
||||||
updateFields := map[string]interface{}{
|
|
||||||
"status": int(status),
|
|
||||||
"layer1_hash": layer1Hash,
|
|
||||||
}
|
|
||||||
err := m.db.Model(&L2Message{}).WithContext(ctx).Where("msg_hash", msgHash).Updates(updateFields).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
package migrate
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"embed"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/pressly/goose/v3"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:embed migrations/*.sql
|
|
||||||
var embedMigrations embed.FS
|
|
||||||
|
|
||||||
// MigrationsDir migration dir
|
|
||||||
const MigrationsDir string = "migrations"
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
goose.SetBaseFS(embedMigrations)
|
|
||||||
goose.SetSequential(true)
|
|
||||||
goose.SetTableName("scroll_migrations")
|
|
||||||
|
|
||||||
verbose, _ := strconv.ParseBool(os.Getenv("LOG_SQL_MIGRATIONS"))
|
|
||||||
goose.SetVerbose(verbose)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Migrate migrate db
|
|
||||||
func Migrate(db *sql.DB) error {
|
|
||||||
return goose.Up(db, MigrationsDir, goose.WithAllowMissing())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rollback rollback to the given version
|
|
||||||
func Rollback(db *sql.DB, version *int64) error {
|
|
||||||
if version != nil {
|
|
||||||
return goose.DownTo(db, MigrationsDir, *version)
|
|
||||||
}
|
|
||||||
return goose.Down(db, MigrationsDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResetDB clean and migrate db.
|
|
||||||
func ResetDB(db *sql.DB) error {
|
|
||||||
if err := Rollback(db, new(int64)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return Migrate(db)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Current get current version
|
|
||||||
func Current(db *sql.DB) (int64, error) {
|
|
||||||
return goose.GetDBVersion(db)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Status is normal or not
|
|
||||||
func Status(db *sql.DB) error {
|
|
||||||
return goose.Version(db, MigrationsDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new migration folder
|
|
||||||
func Create(db *sql.DB, name, migrationType string) error {
|
|
||||||
return goose.Create(db, MigrationsDir, name, migrationType)
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
package migrate
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
_ "github.com/lib/pq"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
|
|
||||||
"scroll-tech/common/docker"
|
|
||||||
|
|
||||||
"scroll-tech/database"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
base *docker.App
|
|
||||||
pgDB *sqlx.DB
|
|
||||||
)
|
|
||||||
|
|
||||||
func initEnv(t *testing.T) error {
|
|
||||||
// Start db container.
|
|
||||||
base.RunDBImage(t)
|
|
||||||
|
|
||||||
// Create db orm handler.
|
|
||||||
factory, err := database.NewOrmFactory(base.DBConfig)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
pgDB = factory.GetDB()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMigrate(t *testing.T) {
|
|
||||||
base = docker.NewDockerApp()
|
|
||||||
if err := initEnv(t); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("testCurrent", testCurrent)
|
|
||||||
t.Run("testStatus", testStatus)
|
|
||||||
t.Run("testResetDB", testResetDB)
|
|
||||||
t.Run("testMigrate", testMigrate)
|
|
||||||
t.Run("testRollback", testRollback)
|
|
||||||
|
|
||||||
t.Cleanup(func() {
|
|
||||||
base.Free()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func testCurrent(t *testing.T) {
|
|
||||||
cur, err := Current(pgDB.DB)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, 0, int(cur))
|
|
||||||
}
|
|
||||||
|
|
||||||
func testStatus(t *testing.T) {
|
|
||||||
status := Status(pgDB.DB)
|
|
||||||
assert.NoError(t, status)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testResetDB(t *testing.T) {
|
|
||||||
assert.NoError(t, ResetDB(pgDB.DB))
|
|
||||||
cur, err := Current(pgDB.DB)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
// total number of tables.
|
|
||||||
assert.Equal(t, 5, int(cur))
|
|
||||||
}
|
|
||||||
|
|
||||||
func testMigrate(t *testing.T) {
|
|
||||||
assert.NoError(t, Migrate(pgDB.DB))
|
|
||||||
cur, err := Current(pgDB.DB)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, true, cur > 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testRollback(t *testing.T) {
|
|
||||||
version, err := Current(pgDB.DB)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, true, version > 0)
|
|
||||||
|
|
||||||
assert.NoError(t, Rollback(pgDB.DB, nil))
|
|
||||||
|
|
||||||
cur, err := Current(pgDB.DB)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, true, cur+1 == version)
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
-- +goose Up
|
|
||||||
-- +goose StatementBegin
|
|
||||||
|
|
||||||
-- TODO: use foreign key for batch_id?
|
|
||||||
-- TODO: why tx_num is bigint?
|
|
||||||
create table block_trace
|
|
||||||
(
|
|
||||||
number BIGINT NOT NULL,
|
|
||||||
hash VARCHAR NOT NULL,
|
|
||||||
parent_hash VARCHAR NOT NULL,
|
|
||||||
trace JSON NOT NULL,
|
|
||||||
batch_hash VARCHAR DEFAULT NULL,
|
|
||||||
tx_num INTEGER NOT NULL,
|
|
||||||
gas_used BIGINT NOT NULL,
|
|
||||||
block_timestamp NUMERIC NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
create unique index block_trace_hash_uindex
|
|
||||||
on block_trace (hash);
|
|
||||||
|
|
||||||
create unique index block_trace_number_uindex
|
|
||||||
on block_trace (number);
|
|
||||||
|
|
||||||
create unique index block_trace_parent_uindex
|
|
||||||
on block_trace (number, parent_hash);
|
|
||||||
|
|
||||||
create unique index block_trace_parent_hash_uindex
|
|
||||||
on block_trace (hash, parent_hash);
|
|
||||||
|
|
||||||
create index block_trace_batch_hash_index
|
|
||||||
on block_trace (batch_hash);
|
|
||||||
|
|
||||||
-- +goose StatementEnd
|
|
||||||
|
|
||||||
-- +goose Down
|
|
||||||
-- +goose StatementBegin
|
|
||||||
drop table if exists block_trace;
|
|
||||||
-- +goose StatementEnd
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
-- +goose Up
|
|
||||||
-- +goose StatementBegin
|
|
||||||
create table l1_message
|
|
||||||
(
|
|
||||||
queue_index BIGINT NOT NULL,
|
|
||||||
msg_hash VARCHAR NOT NULL,
|
|
||||||
height BIGINT NOT NULL,
|
|
||||||
gas_limit BIGINT NOT NULL,
|
|
||||||
sender VARCHAR NOT NULL,
|
|
||||||
target VARCHAR NOT NULL,
|
|
||||||
value VARCHAR NOT NULL,
|
|
||||||
calldata TEXT NOT NULL,
|
|
||||||
layer1_hash VARCHAR NOT NULL,
|
|
||||||
layer2_hash VARCHAR DEFAULT NULL,
|
|
||||||
status INTEGER DEFAULT 1,
|
|
||||||
created_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
||||||
comment
|
|
||||||
on column l1_message.status is 'undefined, pending, submitted, confirmed, failed, expired, relay_failed';
|
|
||||||
|
|
||||||
create unique index l1_message_hash_uindex
|
|
||||||
on l1_message (msg_hash);
|
|
||||||
|
|
||||||
create unique index l1_message_nonce_uindex
|
|
||||||
on l1_message (queue_index);
|
|
||||||
|
|
||||||
create index l1_message_height_index
|
|
||||||
on l1_message (height);
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION update_timestamp()
|
|
||||||
RETURNS TRIGGER AS $$
|
|
||||||
BEGIN
|
|
||||||
NEW.updated_at = CURRENT_TIMESTAMP;
|
|
||||||
RETURN NEW;
|
|
||||||
END;
|
|
||||||
$$ language 'plpgsql';
|
|
||||||
|
|
||||||
CREATE TRIGGER update_timestamp BEFORE UPDATE
|
|
||||||
ON l1_message FOR EACH ROW EXECUTE PROCEDURE
|
|
||||||
update_timestamp();
|
|
||||||
|
|
||||||
|
|
||||||
-- +goose StatementEnd
|
|
||||||
|
|
||||||
-- +goose Down
|
|
||||||
-- +goose StatementBegin
|
|
||||||
drop table if exists l1_message;
|
|
||||||
-- +goose StatementEnd
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
-- +goose Up
|
|
||||||
-- +goose StatementBegin
|
|
||||||
create table l2_message
|
|
||||||
(
|
|
||||||
nonce BIGINT NOT NULL,
|
|
||||||
msg_hash VARCHAR NOT NULL,
|
|
||||||
height BIGINT NOT NULL,
|
|
||||||
sender VARCHAR NOT NULL,
|
|
||||||
target VARCHAR NOT NULL,
|
|
||||||
value VARCHAR NOT NULL,
|
|
||||||
calldata TEXT NOT NULL,
|
|
||||||
layer2_hash VARCHAR NOT NULL,
|
|
||||||
layer1_hash VARCHAR DEFAULT NULL,
|
|
||||||
proof TEXT DEFAULT NULL,
|
|
||||||
status INTEGER DEFAULT 1,
|
|
||||||
created_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
||||||
comment
|
|
||||||
on column l2_message.status is 'undefined, pending, submitted, confirmed, failed, expired, relay_failed';
|
|
||||||
|
|
||||||
create unique index l2_message_hash_uindex
|
|
||||||
on l2_message (msg_hash);
|
|
||||||
|
|
||||||
create unique index l2_message_nonce_uindex
|
|
||||||
on l2_message (nonce);
|
|
||||||
|
|
||||||
create index l2_message_height_index
|
|
||||||
on l2_message (height);
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION update_timestamp()
|
|
||||||
RETURNS TRIGGER AS $$
|
|
||||||
BEGIN
|
|
||||||
NEW.updated_at = CURRENT_TIMESTAMP;
|
|
||||||
RETURN NEW;
|
|
||||||
END;
|
|
||||||
$$ language 'plpgsql';
|
|
||||||
|
|
||||||
CREATE TRIGGER update_timestamp BEFORE UPDATE
|
|
||||||
ON l2_message FOR EACH ROW EXECUTE PROCEDURE
|
|
||||||
update_timestamp();
|
|
||||||
|
|
||||||
|
|
||||||
-- +goose StatementEnd
|
|
||||||
|
|
||||||
-- +goose Down
|
|
||||||
-- +goose StatementBegin
|
|
||||||
drop table if exists l2_message;
|
|
||||||
-- +goose StatementEnd
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
-- +goose Up
|
|
||||||
-- +goose StatementBegin
|
|
||||||
|
|
||||||
create table block_batch
|
|
||||||
(
|
|
||||||
hash VARCHAR NOT NULL,
|
|
||||||
index BIGINT NOT NULL,
|
|
||||||
start_block_number BIGINT NOT NULL,
|
|
||||||
start_block_hash VARCHAR NOT NULL,
|
|
||||||
end_block_number BIGINT NOT NULL,
|
|
||||||
end_block_hash VARCHAR NOT NULL,
|
|
||||||
parent_hash VARCHAR NOT NULL,
|
|
||||||
state_root VARCHAR NOT NULL,
|
|
||||||
total_tx_num BIGINT NOT NULL,
|
|
||||||
total_l1_tx_num BIGINT NOT NULL,
|
|
||||||
total_l2_gas BIGINT NOT NULL,
|
|
||||||
proving_status INTEGER DEFAULT 1,
|
|
||||||
proof BYTEA DEFAULT NULL,
|
|
||||||
proof_time_sec INTEGER DEFAULT 0,
|
|
||||||
rollup_status INTEGER DEFAULT 1,
|
|
||||||
commit_tx_hash VARCHAR DEFAULT NULL,
|
|
||||||
finalize_tx_hash VARCHAR DEFAULT NULL,
|
|
||||||
oracle_status INTEGER DEFAULT 1,
|
|
||||||
oracle_tx_hash VARCHAR DEFAULT NULL,
|
|
||||||
created_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
prover_assigned_at TIMESTAMP(0) DEFAULT NULL,
|
|
||||||
proved_at TIMESTAMP(0) DEFAULT NULL,
|
|
||||||
committed_at TIMESTAMP(0) DEFAULT NULL,
|
|
||||||
finalized_at TIMESTAMP(0) DEFAULT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
comment
|
|
||||||
on column block_batch.proving_status is 'undefined, unassigned, skipped, assigned, proved, verified, failed';
|
|
||||||
comment
|
|
||||||
on column block_batch.rollup_status is 'undefined, pending, committing, committed, finalizing, finalized, finalization_skipped, commit_failed, finalize_failed';
|
|
||||||
comment
|
|
||||||
on column block_batch.oracle_status is 'undefined, pending, importing, imported, failed';
|
|
||||||
|
|
||||||
create unique index block_batch_hash_uindex
|
|
||||||
on block_batch (hash);
|
|
||||||
create unique index block_batch_index_uindex
|
|
||||||
on block_batch (index);
|
|
||||||
|
|
||||||
-- +goose StatementEnd
|
|
||||||
|
|
||||||
-- +goose Down
|
|
||||||
-- +goose StatementBegin
|
|
||||||
drop table if exists block_batch;
|
|
||||||
-- +goose StatementEnd
|
|
||||||
240
bridge/internal/orm/orm_test.go
Normal file
240
bridge/internal/orm/orm_test.go
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
package orm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
|
||||||
|
"scroll-tech/common/database"
|
||||||
|
"scroll-tech/common/docker"
|
||||||
|
"scroll-tech/common/types"
|
||||||
|
|
||||||
|
"scroll-tech/database/migrate"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
base *docker.App
|
||||||
|
|
||||||
|
db *gorm.DB
|
||||||
|
l2BlockOrm *L2Block
|
||||||
|
chunkOrm *Chunk
|
||||||
|
batchOrm *Batch
|
||||||
|
|
||||||
|
wrappedBlock1 *types.WrappedBlock
|
||||||
|
wrappedBlock2 *types.WrappedBlock
|
||||||
|
chunk1 *types.Chunk
|
||||||
|
chunk2 *types.Chunk
|
||||||
|
chunkHash1 common.Hash
|
||||||
|
chunkHash2 common.Hash
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
t := &testing.T{}
|
||||||
|
setupEnv(t)
|
||||||
|
defer tearDownEnv(t)
|
||||||
|
m.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupEnv(t *testing.T) {
|
||||||
|
base = docker.NewDockerApp()
|
||||||
|
base.RunDBImage(t)
|
||||||
|
var err error
|
||||||
|
db, err = database.InitDB(
|
||||||
|
&database.Config{
|
||||||
|
DSN: base.DBConfig.DSN,
|
||||||
|
DriverName: base.DBConfig.DriverName,
|
||||||
|
MaxOpenNum: base.DBConfig.MaxOpenNum,
|
||||||
|
MaxIdleNum: base.DBConfig.MaxIdleNum,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
sqlDB, err := db.DB()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, migrate.ResetDB(sqlDB))
|
||||||
|
|
||||||
|
batchOrm = NewBatch(db)
|
||||||
|
chunkOrm = NewChunk(db)
|
||||||
|
l2BlockOrm = NewL2Block(db)
|
||||||
|
|
||||||
|
templateBlockTrace, err := os.ReadFile("../../../common/testdata/blockTrace_02.json")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
wrappedBlock1 = &types.WrappedBlock{}
|
||||||
|
err = json.Unmarshal(templateBlockTrace, wrappedBlock1)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
templateBlockTrace, err = os.ReadFile("../../../common/testdata/blockTrace_03.json")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
wrappedBlock2 = &types.WrappedBlock{}
|
||||||
|
err = json.Unmarshal(templateBlockTrace, wrappedBlock2)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
chunk1 = &types.Chunk{Blocks: []*types.WrappedBlock{wrappedBlock1}}
|
||||||
|
chunkHash1, err = chunk1.Hash(0)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
chunk2 = &types.Chunk{Blocks: []*types.WrappedBlock{wrappedBlock2}}
|
||||||
|
chunkHash2, err = chunk2.Hash(chunk1.NumL1Messages(0))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func tearDownEnv(t *testing.T) {
|
||||||
|
sqlDB, err := db.DB()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
sqlDB.Close()
|
||||||
|
base.Free()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestL2BlockOrm(t *testing.T) {
|
||||||
|
sqlDB, err := db.DB()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, migrate.ResetDB(sqlDB))
|
||||||
|
|
||||||
|
err = l2BlockOrm.InsertL2Blocks(context.Background(), []*types.WrappedBlock{wrappedBlock1, wrappedBlock2})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
height, err := l2BlockOrm.GetL2BlocksLatestHeight(context.Background())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, uint64(3), height)
|
||||||
|
|
||||||
|
blocks, err := l2BlockOrm.GetUnchunkedBlocks(context.Background())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, blocks, 2)
|
||||||
|
assert.Equal(t, wrappedBlock1, blocks[0])
|
||||||
|
assert.Equal(t, wrappedBlock2, blocks[1])
|
||||||
|
|
||||||
|
blocks, err = l2BlockOrm.GetL2BlocksInRange(context.Background(), 2, 3)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, blocks, 2)
|
||||||
|
assert.Equal(t, wrappedBlock1, blocks[0])
|
||||||
|
assert.Equal(t, wrappedBlock2, blocks[1])
|
||||||
|
|
||||||
|
err = l2BlockOrm.UpdateChunkHashInRange(context.Background(), 2, 2, "test hash")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
blocks, err = l2BlockOrm.GetUnchunkedBlocks(context.Background())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, blocks, 1)
|
||||||
|
assert.Equal(t, wrappedBlock2, blocks[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChunkOrm(t *testing.T) {
|
||||||
|
sqlDB, err := db.DB()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, migrate.ResetDB(sqlDB))
|
||||||
|
|
||||||
|
dbChunk1, err := chunkOrm.InsertChunk(context.Background(), chunk1)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, dbChunk1.Hash, chunkHash1.Hex())
|
||||||
|
|
||||||
|
dbChunk2, err := chunkOrm.InsertChunk(context.Background(), chunk2)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, dbChunk2.Hash, chunkHash2.Hex())
|
||||||
|
|
||||||
|
chunks, err := chunkOrm.GetUnbatchedChunks(context.Background())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, chunks, 2)
|
||||||
|
assert.Equal(t, chunkHash1.Hex(), chunks[0].Hash)
|
||||||
|
assert.Equal(t, chunkHash2.Hex(), chunks[1].Hash)
|
||||||
|
|
||||||
|
err = chunkOrm.UpdateProvingStatus(context.Background(), chunkHash1.Hex(), types.ProvingTaskVerified)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = chunkOrm.UpdateProvingStatus(context.Background(), chunkHash2.Hex(), types.ProvingTaskAssigned)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
chunks, err = chunkOrm.GetChunksInRange(context.Background(), 0, 1)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, chunks, 2)
|
||||||
|
assert.Equal(t, chunkHash1.Hex(), chunks[0].Hash)
|
||||||
|
assert.Equal(t, chunkHash2.Hex(), chunks[1].Hash)
|
||||||
|
assert.Equal(t, types.ProvingTaskVerified, types.ProvingStatus(chunks[0].ProvingStatus))
|
||||||
|
assert.Equal(t, types.ProvingTaskAssigned, types.ProvingStatus(chunks[1].ProvingStatus))
|
||||||
|
|
||||||
|
err = chunkOrm.UpdateBatchHashInRange(context.Background(), 0, 0, "test hash")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
chunks, err = chunkOrm.GetUnbatchedChunks(context.Background())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, chunks, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBatchOrm(t *testing.T) {
|
||||||
|
sqlDB, err := db.DB()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, migrate.ResetDB(sqlDB))
|
||||||
|
|
||||||
|
batch1, err := batchOrm.InsertBatch(context.Background(), 0, 0, chunkHash1.Hex(), chunkHash1.Hex(), []*types.Chunk{chunk1})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
hash1 := batch1.Hash
|
||||||
|
|
||||||
|
batch1, err = batchOrm.GetBatchByIndex(context.Background(), 0)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
batchHeader1, err := types.DecodeBatchHeader(batch1.BatchHeader)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
batchHash1 := batchHeader1.Hash().Hex()
|
||||||
|
assert.Equal(t, hash1, batchHash1)
|
||||||
|
|
||||||
|
batch2, err := batchOrm.InsertBatch(context.Background(), 1, 1, chunkHash2.Hex(), chunkHash2.Hex(), []*types.Chunk{chunk2})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
hash2 := batch2.Hash
|
||||||
|
|
||||||
|
batch2, err = batchOrm.GetBatchByIndex(context.Background(), 1)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
batchHeader2, err := types.DecodeBatchHeader(batch2.BatchHeader)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
batchHash2 := batchHeader2.Hash().Hex()
|
||||||
|
assert.Equal(t, hash2, batchHash2)
|
||||||
|
|
||||||
|
count, err := batchOrm.GetBatchCount(context.Background())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, uint64(2), count)
|
||||||
|
|
||||||
|
pendingBatches, err := batchOrm.GetPendingBatches(context.Background(), 100)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 2, len(pendingBatches))
|
||||||
|
|
||||||
|
rollupStatus, err := batchOrm.GetRollupStatusByHashList(context.Background(), []string{batchHash1, batchHash2})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 2, len(rollupStatus))
|
||||||
|
assert.Equal(t, types.RollupPending, rollupStatus[0])
|
||||||
|
assert.Equal(t, types.RollupPending, rollupStatus[1])
|
||||||
|
|
||||||
|
err = batchOrm.UpdateProvingStatus(context.Background(), batchHash2, types.ProvingTaskVerified)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
dbProof, err := batchOrm.GetVerifiedProofByHash(context.Background(), batchHash1)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, dbProof)
|
||||||
|
|
||||||
|
err = batchOrm.UpdateProvingStatus(context.Background(), batchHash2, types.ProvingTaskVerified)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = batchOrm.UpdateRollupStatus(context.Background(), batchHash2, types.RollupFinalized)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = batchOrm.UpdateL2GasOracleStatusAndOracleTxHash(context.Background(), batchHash2, types.GasOracleImported, "oracleTxHash")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
updatedBatch, err := batchOrm.GetLatestBatch(context.Background())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, types.ProvingTaskVerified, types.ProvingStatus(updatedBatch.ProvingStatus))
|
||||||
|
assert.Equal(t, types.RollupFinalized, types.RollupStatus(updatedBatch.RollupStatus))
|
||||||
|
assert.Equal(t, types.GasOracleImported, types.GasOracleStatus(updatedBatch.OracleStatus))
|
||||||
|
assert.Equal(t, "oracleTxHash", updatedBatch.OracleTxHash)
|
||||||
|
|
||||||
|
err = batchOrm.UpdateCommitTxHashAndRollupStatus(context.Background(), batchHash2, "commitTxHash", types.RollupCommitted)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
updatedBatch, err = batchOrm.GetLatestBatch(context.Background())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "commitTxHash", updatedBatch.CommitTxHash)
|
||||||
|
assert.Equal(t, types.RollupCommitted, types.RollupStatus(updatedBatch.RollupStatus))
|
||||||
|
|
||||||
|
err = batchOrm.UpdateFinalizeTxHashAndRollupStatus(context.Background(), batchHash2, "finalizeTxHash", types.RollupFinalizeFailed)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
updatedBatch, err = batchOrm.GetLatestBatch(context.Background())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "finalizeTxHash", updatedBatch.FinalizeTxHash)
|
||||||
|
assert.Equal(t, types.RollupFinalizeFailed, types.RollupStatus(updatedBatch.RollupStatus))
|
||||||
|
}
|
||||||
@@ -1,236 +0,0 @@
|
|||||||
package types
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
|
||||||
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
|
||||||
"github.com/scroll-tech/go-ethereum/core/types"
|
|
||||||
"github.com/scroll-tech/go-ethereum/crypto"
|
|
||||||
|
|
||||||
abi "scroll-tech/bridge/abi"
|
|
||||||
)
|
|
||||||
|
|
||||||
// PublicInputHashConfig is the configuration of how to compute the public input hash.
|
|
||||||
type PublicInputHashConfig struct {
|
|
||||||
MaxTxNum int `json:"max_tx_num"`
|
|
||||||
PaddingTxHash common.Hash `json:"padding_tx_hash"`
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultMaxTxNum = 44
|
|
||||||
|
|
||||||
var defaultPaddingTxHash = [32]byte{}
|
|
||||||
|
|
||||||
// BatchData contains info of batch to be committed.
|
|
||||||
type BatchData struct {
|
|
||||||
Batch abi.IScrollChainBatch
|
|
||||||
TxHashes []common.Hash
|
|
||||||
TotalTxNum uint64
|
|
||||||
TotalL1TxNum uint64
|
|
||||||
TotalL2Gas uint64
|
|
||||||
|
|
||||||
// cache for the BatchHash
|
|
||||||
hash *common.Hash
|
|
||||||
// The config to compute the public input hash, or the block hash.
|
|
||||||
// If it is nil, the hash calculation will use `defaultMaxTxNum` and `defaultPaddingTxHash`.
|
|
||||||
piCfg *PublicInputHashConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
// Timestamp returns the timestamp of the first block in the BlockData.
|
|
||||||
func (b *BatchData) Timestamp() uint64 {
|
|
||||||
if len(b.Batch.Blocks) == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return b.Batch.Blocks[0].Timestamp
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hash calculates the hash of this batch.
|
|
||||||
func (b *BatchData) Hash() *common.Hash {
|
|
||||||
if b.hash != nil {
|
|
||||||
return b.hash
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := make([]byte, 8)
|
|
||||||
hasher := crypto.NewKeccakState()
|
|
||||||
|
|
||||||
// 1. hash PrevStateRoot, NewStateRoot, WithdrawTrieRoot
|
|
||||||
// @todo: panic on error here.
|
|
||||||
_, _ = hasher.Write(b.Batch.PrevStateRoot[:])
|
|
||||||
_, _ = hasher.Write(b.Batch.NewStateRoot[:])
|
|
||||||
_, _ = hasher.Write(b.Batch.WithdrawTrieRoot[:])
|
|
||||||
|
|
||||||
// 2. hash all block contexts
|
|
||||||
for _, block := range b.Batch.Blocks {
|
|
||||||
// write BlockHash & ParentHash
|
|
||||||
_, _ = hasher.Write(block.BlockHash[:])
|
|
||||||
_, _ = hasher.Write(block.ParentHash[:])
|
|
||||||
// write BlockNumber
|
|
||||||
binary.BigEndian.PutUint64(buf, block.BlockNumber)
|
|
||||||
_, _ = hasher.Write(buf)
|
|
||||||
// write Timestamp
|
|
||||||
binary.BigEndian.PutUint64(buf, block.Timestamp)
|
|
||||||
_, _ = hasher.Write(buf)
|
|
||||||
// write BaseFee
|
|
||||||
var baseFee [32]byte
|
|
||||||
if block.BaseFee != nil {
|
|
||||||
baseFee = newByte32FromBytes(block.BaseFee.Bytes())
|
|
||||||
}
|
|
||||||
_, _ = hasher.Write(baseFee[:])
|
|
||||||
// write GasLimit
|
|
||||||
binary.BigEndian.PutUint64(buf, block.GasLimit)
|
|
||||||
_, _ = hasher.Write(buf)
|
|
||||||
// write NumTransactions
|
|
||||||
binary.BigEndian.PutUint16(buf[:2], block.NumTransactions)
|
|
||||||
_, _ = hasher.Write(buf[:2])
|
|
||||||
// write NumL1Messages
|
|
||||||
binary.BigEndian.PutUint16(buf[:2], block.NumL1Messages)
|
|
||||||
_, _ = hasher.Write(buf[:2])
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. add all tx hashes
|
|
||||||
for _, txHash := range b.TxHashes {
|
|
||||||
_, _ = hasher.Write(txHash[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. append empty tx hash up to MaxTxNum
|
|
||||||
maxTxNum := defaultMaxTxNum
|
|
||||||
paddingTxHash := common.Hash(defaultPaddingTxHash)
|
|
||||||
if b.piCfg != nil {
|
|
||||||
maxTxNum = b.piCfg.MaxTxNum
|
|
||||||
paddingTxHash = b.piCfg.PaddingTxHash
|
|
||||||
}
|
|
||||||
for i := len(b.TxHashes); i < maxTxNum; i++ {
|
|
||||||
_, _ = hasher.Write(paddingTxHash[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
b.hash = new(common.Hash)
|
|
||||||
_, _ = hasher.Read(b.hash[:])
|
|
||||||
|
|
||||||
return b.hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBatchData creates a BatchData given the parent batch information and the traces of the blocks
|
|
||||||
// included in this batch
|
|
||||||
func NewBatchData(parentBatch *BatchInfo, blocks []*WrappedBlock, piCfg *PublicInputHashConfig) *BatchData {
|
|
||||||
batchData := new(BatchData)
|
|
||||||
batch := &batchData.Batch
|
|
||||||
|
|
||||||
// set BatchIndex, ParentBatchHash
|
|
||||||
batch.BatchIndex = parentBatch.Index + 1
|
|
||||||
batch.ParentBatchHash = common.HexToHash(parentBatch.Hash)
|
|
||||||
batch.Blocks = make([]abi.IScrollChainBlockContext, len(blocks))
|
|
||||||
|
|
||||||
var batchTxDataBuf bytes.Buffer
|
|
||||||
batchTxDataWriter := bufio.NewWriter(&batchTxDataBuf)
|
|
||||||
|
|
||||||
for i, block := range blocks {
|
|
||||||
batchData.TotalTxNum += uint64(len(block.Transactions))
|
|
||||||
batchData.TotalL2Gas += block.Header.GasUsed
|
|
||||||
|
|
||||||
// set baseFee to 0 when it's nil in the block header
|
|
||||||
baseFee := block.Header.BaseFee
|
|
||||||
if baseFee == nil {
|
|
||||||
baseFee = big.NewInt(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
batch.Blocks[i] = abi.IScrollChainBlockContext{
|
|
||||||
BlockHash: block.Header.Hash(),
|
|
||||||
ParentHash: block.Header.ParentHash,
|
|
||||||
BlockNumber: block.Header.Number.Uint64(),
|
|
||||||
Timestamp: block.Header.Time,
|
|
||||||
BaseFee: baseFee,
|
|
||||||
GasLimit: block.Header.GasLimit,
|
|
||||||
NumTransactions: uint16(len(block.Transactions)),
|
|
||||||
NumL1Messages: 0, // TODO: currently use 0, will re-enable after we use l2geth to include L1 messages
|
|
||||||
}
|
|
||||||
|
|
||||||
// fill in RLP-encoded transactions
|
|
||||||
for _, txData := range block.Transactions {
|
|
||||||
data, _ := hexutil.Decode(txData.Data)
|
|
||||||
// right now we only support legacy tx
|
|
||||||
tx := types.NewTx(&types.LegacyTx{
|
|
||||||
Nonce: txData.Nonce,
|
|
||||||
To: txData.To,
|
|
||||||
Value: txData.Value.ToInt(),
|
|
||||||
Gas: txData.Gas,
|
|
||||||
GasPrice: txData.GasPrice.ToInt(),
|
|
||||||
Data: data,
|
|
||||||
V: txData.V.ToInt(),
|
|
||||||
R: txData.R.ToInt(),
|
|
||||||
S: txData.S.ToInt(),
|
|
||||||
})
|
|
||||||
rlpTxData, _ := tx.MarshalBinary()
|
|
||||||
var txLen [4]byte
|
|
||||||
binary.BigEndian.PutUint32(txLen[:], uint32(len(rlpTxData)))
|
|
||||||
_, _ = batchTxDataWriter.Write(txLen[:])
|
|
||||||
_, _ = batchTxDataWriter.Write(rlpTxData)
|
|
||||||
batchData.TxHashes = append(batchData.TxHashes, tx.Hash())
|
|
||||||
}
|
|
||||||
|
|
||||||
if i == 0 {
|
|
||||||
batch.PrevStateRoot = common.HexToHash(parentBatch.StateRoot)
|
|
||||||
}
|
|
||||||
|
|
||||||
// set NewStateRoot & WithdrawTrieRoot from the last block
|
|
||||||
if i == len(blocks)-1 {
|
|
||||||
batch.NewStateRoot = block.Header.Root
|
|
||||||
batch.WithdrawTrieRoot = block.WithdrawTrieRoot
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := batchTxDataWriter.Flush(); err != nil {
|
|
||||||
panic("Buffered I/O flush failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
batch.L2Transactions = batchTxDataBuf.Bytes()
|
|
||||||
batchData.piCfg = piCfg
|
|
||||||
|
|
||||||
return batchData
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGenesisBatchData generates the batch that contains the genesis block.
|
|
||||||
func NewGenesisBatchData(genesisBlockTrace *WrappedBlock) *BatchData {
|
|
||||||
header := genesisBlockTrace.Header
|
|
||||||
if header.Number.Uint64() != 0 {
|
|
||||||
panic("invalid genesis block trace: block number is not 0")
|
|
||||||
}
|
|
||||||
|
|
||||||
batchData := new(BatchData)
|
|
||||||
batch := &batchData.Batch
|
|
||||||
|
|
||||||
// fill in batch information
|
|
||||||
batch.BatchIndex = 0
|
|
||||||
batch.Blocks = make([]abi.IScrollChainBlockContext, 1)
|
|
||||||
batch.NewStateRoot = header.Root
|
|
||||||
// PrevStateRoot, WithdrawTrieRoot, ParentBatchHash should all be 0
|
|
||||||
// L2Transactions should be empty
|
|
||||||
|
|
||||||
// fill in block context
|
|
||||||
batch.Blocks[0] = abi.IScrollChainBlockContext{
|
|
||||||
BlockHash: header.Hash(),
|
|
||||||
ParentHash: header.ParentHash,
|
|
||||||
BlockNumber: header.Number.Uint64(),
|
|
||||||
Timestamp: header.Time,
|
|
||||||
BaseFee: header.BaseFee,
|
|
||||||
GasLimit: header.GasLimit,
|
|
||||||
NumTransactions: 0,
|
|
||||||
NumL1Messages: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
return batchData
|
|
||||||
}
|
|
||||||
|
|
||||||
// newByte32FromBytes converts the bytes in big-endian encoding to 32 bytes in big-endian encoding
|
|
||||||
func newByte32FromBytes(b []byte) [32]byte {
|
|
||||||
var byte32 [32]byte
|
|
||||||
|
|
||||||
if len(b) > 32 {
|
|
||||||
b = b[len(b)-32:]
|
|
||||||
}
|
|
||||||
|
|
||||||
copy(byte32[32-len(b):], b)
|
|
||||||
return byte32
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
package types
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/big"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
|
||||||
gethTypes "github.com/scroll-tech/go-ethereum/core/types"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
|
|
||||||
abi "scroll-tech/bridge/abi"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBatchHash(t *testing.T) {
|
|
||||||
txBytes := common.Hex2Bytes("02f8710582fd14808506e38dccc9825208944d496ccc28058b1d74b7a19541663e21154f9c848801561db11e24a43380c080a0d890606d7a35b2ab0f9b866d62c092d5b163f3e6a55537ae1485aac08c3f8ff7a023997be2d32f53e146b160fff0ba81e81dbb4491c865ab174d15c5b3d28c41ae")
|
|
||||||
tx := new(gethTypes.Transaction)
|
|
||||||
if err := tx.UnmarshalBinary(txBytes); err != nil {
|
|
||||||
t.Fatalf("invalid tx hex string: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
batchData := new(BatchData)
|
|
||||||
batchData.TxHashes = append(batchData.TxHashes, tx.Hash())
|
|
||||||
batchData.piCfg = &PublicInputHashConfig{
|
|
||||||
MaxTxNum: 4,
|
|
||||||
PaddingTxHash: common.HexToHash("0xb5baa665b2664c3bfed7eb46e00ebc110ecf2ebd257854a9bf2b9dbc9b2c08f6"),
|
|
||||||
}
|
|
||||||
|
|
||||||
batch := &batchData.Batch
|
|
||||||
batch.PrevStateRoot = common.HexToHash("0x000000000000000000000000000000000000000000000000000000000000cafe")
|
|
||||||
|
|
||||||
block := abi.IScrollChainBlockContext{
|
|
||||||
BlockNumber: 51966,
|
|
||||||
Timestamp: 123456789,
|
|
||||||
BaseFee: new(big.Int).SetUint64(0),
|
|
||||||
GasLimit: 10000000000000000,
|
|
||||||
NumTransactions: 1,
|
|
||||||
NumL1Messages: 0,
|
|
||||||
}
|
|
||||||
batch.Blocks = append(batch.Blocks, block)
|
|
||||||
|
|
||||||
hash := batchData.Hash()
|
|
||||||
assert.Equal(t, *hash, common.HexToHash("0xa9f2ca3175794f91226a410ba1e60fff07a405c957562675c4149b77e659d805"))
|
|
||||||
|
|
||||||
// use a different tx hash
|
|
||||||
txBytes = common.Hex2Bytes("f8628001830f424094000000000000000000000000000000000000bbbb8080820a97a064e07cd8f939e2117724bdcbadc80dda421381cbc2a1f4e0d093d9cc5c5cf68ea03e264227f80852d88743cd9e43998f2746b619180366a87e4531debf9c3fa5dc")
|
|
||||||
tx = new(gethTypes.Transaction)
|
|
||||||
if err := tx.UnmarshalBinary(txBytes); err != nil {
|
|
||||||
t.Fatalf("invalid tx hex string: %s", err)
|
|
||||||
}
|
|
||||||
batchData.TxHashes[0] = tx.Hash()
|
|
||||||
|
|
||||||
batchData.hash = nil // clear the cache
|
|
||||||
assert.Equal(t, *batchData.Hash(), common.HexToHash("0x398cb22bbfa1665c1b342b813267538a4c933d7f92d8bd9184aba0dd1122987b"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewGenesisBatch(t *testing.T) {
|
|
||||||
genesisBlock := &gethTypes.Header{
|
|
||||||
UncleHash: common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"),
|
|
||||||
Root: common.HexToHash("0x1b186a7a90ec3b41a2417062fe44dce8ce82ae76bfbb09eae786a4f1be1895f5"),
|
|
||||||
TxHash: common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"),
|
|
||||||
ReceiptHash: common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"),
|
|
||||||
Difficulty: big.NewInt(1),
|
|
||||||
Number: big.NewInt(0),
|
|
||||||
GasLimit: 940000000,
|
|
||||||
GasUsed: 0,
|
|
||||||
Time: 1639724192,
|
|
||||||
Extra: common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000214f8d488aa9ebf83e30bad45fb8f9c8ee2509f5511caff794753d07e9dfb218cfc233bb62d2c57022783094e1a7edb6f069f8424bb68496a0926b130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
BaseFee: big.NewInt(1000000000),
|
|
||||||
}
|
|
||||||
assert.Equal(
|
|
||||||
t,
|
|
||||||
genesisBlock.Hash().Hex(),
|
|
||||||
"0x92826bd3aad2ef70d8061dc4e25150b305d1233d9cd7579433a77d6eb01dae1c",
|
|
||||||
"wrong genesis block header",
|
|
||||||
)
|
|
||||||
|
|
||||||
blockTrace := &WrappedBlock{genesisBlock, nil, common.Hash{}}
|
|
||||||
batchData := NewGenesisBatchData(blockTrace)
|
|
||||||
t.Log(batchData.Batch.Blocks[0])
|
|
||||||
batchData.piCfg = &PublicInputHashConfig{
|
|
||||||
MaxTxNum: 25,
|
|
||||||
PaddingTxHash: common.HexToHash("0xb5baa665b2664c3bfed7eb46e00ebc110ecf2ebd257854a9bf2b9dbc9b2c08f6"),
|
|
||||||
}
|
|
||||||
assert.Equal(
|
|
||||||
t,
|
|
||||||
batchData.Hash().Hex(),
|
|
||||||
"0x65cf210e30f75cf8fd198df124255f73bc08d6324759e828a784fa938e7ac43d",
|
|
||||||
"wrong genesis batch hash",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"gorm.io/driver/postgres"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
"gorm.io/gorm/logger"
|
|
||||||
|
|
||||||
"scroll-tech/bridge/internal/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
// InitDB init the db handler
|
|
||||||
func InitDB(config *config.DBConfig) (*gorm.DB, error) {
|
|
||||||
db, err := gorm.Open(postgres.Open(config.DSN), &gorm.Config{
|
|
||||||
Logger: logger.Default.LogMode(logger.Info),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
sqlDB, err := db.DB()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
sqlDB.SetMaxOpenConns(config.MaxOpenNum)
|
|
||||||
sqlDB.SetMaxIdleConns(config.MaxIdleNum)
|
|
||||||
|
|
||||||
if err = sqlDB.Ping(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return db, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CloseDB close the db handler. notice the db handler only can close when then program exit.
|
|
||||||
func CloseDB(db *gorm.DB) error {
|
|
||||||
sqlDB, err := db.DB()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := sqlDB.Close(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
// SPDX-License-Identifier: UNLICENSED
|
// SPDX-License-Identifier: UNLICENSED
|
||||||
pragma solidity ^0.8.0;
|
pragma solidity ^0.8.0;
|
||||||
|
|
||||||
|
import {BatchHeaderV0Codec} from "../../contracts/src/libraries/codec/BatchHeaderV0Codec.sol";
|
||||||
|
import {ChunkCodec} from "../../contracts/src/libraries/codec/ChunkCodec.sol";
|
||||||
|
import {IL1MessageQueue} from "../../contracts/src/L1/rollup/IL1MessageQueue.sol";
|
||||||
|
|
||||||
contract MockBridgeL1 {
|
contract MockBridgeL1 {
|
||||||
/******************************
|
/******************************
|
||||||
* Events from L1MessageQueue *
|
* Events from L1MessageQueue *
|
||||||
@@ -17,7 +21,7 @@ contract MockBridgeL1 {
|
|||||||
address indexed sender,
|
address indexed sender,
|
||||||
address indexed target,
|
address indexed target,
|
||||||
uint256 value,
|
uint256 value,
|
||||||
uint256 queueIndex,
|
uint64 queueIndex,
|
||||||
uint256 gasLimit,
|
uint256 gasLimit,
|
||||||
bytes data
|
bytes data
|
||||||
);
|
);
|
||||||
@@ -46,74 +50,27 @@ contract MockBridgeL1 {
|
|||||||
/// @param messageHash The hash of the message.
|
/// @param messageHash The hash of the message.
|
||||||
event RelayedMessage(bytes32 indexed messageHash);
|
event RelayedMessage(bytes32 indexed messageHash);
|
||||||
|
|
||||||
/// @dev The maximum number of transaction in on batch.
|
|
||||||
uint256 public immutable maxNumTxInBatch;
|
|
||||||
|
|
||||||
/// @dev The hash used for padding public inputs.
|
|
||||||
bytes32 public immutable paddingTxHash;
|
|
||||||
|
|
||||||
/***************************
|
/***************************
|
||||||
* Events from ScrollChain *
|
* Events from ScrollChain *
|
||||||
***************************/
|
***************************/
|
||||||
|
|
||||||
/// @notice Emitted when a new batch is commited.
|
/// @notice Emitted when a new batch is committed.
|
||||||
/// @param batchHash The hash of the batch
|
/// @param batchHash The hash of the batch.
|
||||||
event CommitBatch(bytes32 indexed batchHash);
|
event CommitBatch(bytes32 indexed batchHash);
|
||||||
|
|
||||||
/// @notice Emitted when a batch is reverted.
|
|
||||||
/// @param batchHash The identification of the batch.
|
|
||||||
event RevertBatch(bytes32 indexed batchHash);
|
|
||||||
|
|
||||||
/// @notice Emitted when a batch is finalized.
|
/// @notice Emitted when a batch is finalized.
|
||||||
/// @param batchHash The hash of the batch
|
/// @param batchHash The hash of the batch
|
||||||
event FinalizeBatch(bytes32 indexed batchHash);
|
/// @param stateRoot The state root on layer 2 after this batch.
|
||||||
|
/// @param withdrawRoot The merkle root on layer2 after this batch.
|
||||||
|
event FinalizeBatch(bytes32 indexed batchHash, bytes32 stateRoot, bytes32 withdrawRoot);
|
||||||
|
|
||||||
/***********
|
/***********
|
||||||
* Structs *
|
* Structs *
|
||||||
***********/
|
***********/
|
||||||
|
|
||||||
struct BlockContext {
|
|
||||||
// The hash of this block.
|
|
||||||
bytes32 blockHash;
|
|
||||||
// The parent hash of this block.
|
|
||||||
bytes32 parentHash;
|
|
||||||
// The height of this block.
|
|
||||||
uint64 blockNumber;
|
|
||||||
// The timestamp of this block.
|
|
||||||
uint64 timestamp;
|
|
||||||
// The base fee of this block.
|
|
||||||
// Currently, it is not used, because we disable EIP-1559.
|
|
||||||
// We keep it for future proof.
|
|
||||||
uint256 baseFee;
|
|
||||||
// The gas limit of this block.
|
|
||||||
uint64 gasLimit;
|
|
||||||
// The number of transactions in this block, both L1 & L2 txs.
|
|
||||||
uint16 numTransactions;
|
|
||||||
// The number of l1 messages in this block.
|
|
||||||
uint16 numL1Messages;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Batch {
|
|
||||||
// The list of blocks in this batch
|
|
||||||
BlockContext[] blocks; // MAX_NUM_BLOCKS = 100, about 5 min
|
|
||||||
// The state root of previous batch.
|
|
||||||
// The first batch will use 0x0 for prevStateRoot
|
|
||||||
bytes32 prevStateRoot;
|
|
||||||
// The state root of the last block in this batch.
|
|
||||||
bytes32 newStateRoot;
|
|
||||||
// The withdraw trie root of the last block in this batch.
|
|
||||||
bytes32 withdrawTrieRoot;
|
|
||||||
// The index of the batch.
|
|
||||||
uint64 batchIndex;
|
|
||||||
// The parent batch hash.
|
|
||||||
bytes32 parentBatchHash;
|
|
||||||
// Concatenated raw data of RLP encoded L2 txs
|
|
||||||
bytes l2Transactions;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct L2MessageProof {
|
struct L2MessageProof {
|
||||||
// The hash of the batch where the message belongs to.
|
// The index of the batch where the message belongs to.
|
||||||
bytes32 batchHash;
|
uint256 batchIndex;
|
||||||
// Concatenation of merkle proof for withdraw merkle trie.
|
// Concatenation of merkle proof for withdraw merkle trie.
|
||||||
bytes merkleProof;
|
bytes merkleProof;
|
||||||
}
|
}
|
||||||
@@ -125,14 +82,7 @@ contract MockBridgeL1 {
|
|||||||
/// @notice Message nonce, used to avoid relay attack.
|
/// @notice Message nonce, used to avoid relay attack.
|
||||||
uint256 public messageNonce;
|
uint256 public messageNonce;
|
||||||
|
|
||||||
/***************
|
mapping(uint256 => bytes32) public committedBatches;
|
||||||
* Constructor *
|
|
||||||
***************/
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
maxNumTxInBatch = 44;
|
|
||||||
paddingTxHash = 0x0000000000000000000000000000000000000000000000000000000000000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***********************************
|
/***********************************
|
||||||
* Functions from L2GasPriceOracle *
|
* Functions from L2GasPriceOracle *
|
||||||
@@ -154,7 +104,7 @@ contract MockBridgeL1 {
|
|||||||
bytes memory _xDomainCalldata = _encodeXDomainCalldata(msg.sender, target, value, messageNonce, message);
|
bytes memory _xDomainCalldata = _encodeXDomainCalldata(msg.sender, target, value, messageNonce, message);
|
||||||
{
|
{
|
||||||
address _sender = applyL1ToL2Alias(address(this));
|
address _sender = applyL1ToL2Alias(address(this));
|
||||||
emit QueueTransaction(_sender, target, 0, messageNonce, gasLimit, _xDomainCalldata);
|
emit QueueTransaction(_sender, target, 0, uint64(messageNonce), gasLimit, _xDomainCalldata);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit SentMessage(msg.sender, target, value, messageNonce, gasLimit, message);
|
emit SentMessage(msg.sender, target, value, messageNonce, gasLimit, message);
|
||||||
@@ -178,37 +128,65 @@ contract MockBridgeL1 {
|
|||||||
* Functions from ScrollChain *
|
* Functions from ScrollChain *
|
||||||
******************************/
|
******************************/
|
||||||
|
|
||||||
function commitBatch(Batch memory _batch) external {
|
function commitBatch(
|
||||||
_commitBatch(_batch);
|
uint8 /*version*/,
|
||||||
}
|
bytes calldata /*parentBatchHeader*/,
|
||||||
|
bytes[] memory chunks,
|
||||||
|
bytes calldata /*skippedL1MessageBitmap*/
|
||||||
|
) external {
|
||||||
|
// check whether the batch is empty
|
||||||
|
uint256 _chunksLength = chunks.length;
|
||||||
|
require(_chunksLength > 0, "batch is empty");
|
||||||
|
|
||||||
function commitBatches(Batch[] memory _batches) external {
|
uint256 dataPtr;
|
||||||
for (uint256 i = 0; i < _batches.length; i++) {
|
assembly {
|
||||||
_commitBatch(_batches[i]);
|
dataPtr := mload(0x40)
|
||||||
|
mstore(0x40, add(dataPtr, mul(_chunksLength, 32)))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function revertBatch(bytes32 _batchHash) external {
|
for (uint256 i = 0; i < _chunksLength; i++) {
|
||||||
emit RevertBatch(_batchHash);
|
_commitChunk(dataPtr, chunks[i]);
|
||||||
|
|
||||||
|
unchecked {
|
||||||
|
dataPtr += 32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes32 _dataHash;
|
||||||
|
assembly {
|
||||||
|
let dataLen := mul(_chunksLength, 0x20)
|
||||||
|
_dataHash := keccak256(sub(dataPtr, dataLen), dataLen)
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes memory paddedData = new bytes(89);
|
||||||
|
assembly {
|
||||||
|
mstore(add(paddedData, 57), _dataHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 batchPtr;
|
||||||
|
assembly {
|
||||||
|
batchPtr := add(paddedData, 32)
|
||||||
|
}
|
||||||
|
bytes32 _batchHash = BatchHeaderV0Codec.computeBatchHash(batchPtr, 89);
|
||||||
|
committedBatches[0] = _batchHash;
|
||||||
|
emit CommitBatch(_batchHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
function finalizeBatchWithProof(
|
function finalizeBatchWithProof(
|
||||||
bytes32 _batchHash,
|
bytes calldata /*batchHeader*/,
|
||||||
uint256[] memory,
|
bytes32 /*prevStateRoot*/,
|
||||||
uint256[] memory
|
bytes32 postStateRoot,
|
||||||
|
bytes32 withdrawRoot,
|
||||||
|
bytes calldata /*aggrProof*/
|
||||||
) external {
|
) external {
|
||||||
emit FinalizeBatch(_batchHash);
|
bytes32 _batchHash = committedBatches[0];
|
||||||
|
emit FinalizeBatch(_batchHash, postStateRoot, withdrawRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**********************
|
/**********************
|
||||||
* Internal Functions *
|
* Internal Functions *
|
||||||
**********************/
|
**********************/
|
||||||
|
|
||||||
function _commitBatch(Batch memory _batch) internal {
|
|
||||||
bytes32 _batchHash = _computePublicInputHash(_batch);
|
|
||||||
emit CommitBatch(_batchHash);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Internal function to generate the correct cross domain calldata for a message.
|
/// @dev Internal function to generate the correct cross domain calldata for a message.
|
||||||
/// @param _sender Message sender address.
|
/// @param _sender Message sender address.
|
||||||
/// @param _target Target contract address.
|
/// @param _target Target contract address.
|
||||||
@@ -234,6 +212,10 @@ contract MockBridgeL1 {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @notice Utility function that converts the address in the L1 that submitted a tx to
|
||||||
|
/// the inbox to the msg.sender viewed in the L2
|
||||||
|
/// @param l1Address the address in the L1 that triggered the tx to L2
|
||||||
|
/// @return l2Address L2 address as viewed in msg.sender
|
||||||
function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) {
|
function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) {
|
||||||
uint160 offset = uint160(0x1111000000000000000000000000000000001111);
|
uint160 offset = uint160(0x1111000000000000000000000000000000001111);
|
||||||
unchecked {
|
unchecked {
|
||||||
@@ -241,140 +223,67 @@ contract MockBridgeL1 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Internal function to compute the public input hash.
|
function _commitChunk(
|
||||||
/// @param batch The batch to compute.
|
uint256 memPtr,
|
||||||
function _computePublicInputHash(Batch memory batch)
|
bytes memory _chunk
|
||||||
internal
|
) internal pure {
|
||||||
view
|
uint256 chunkPtr;
|
||||||
returns (
|
uint256 startDataPtr;
|
||||||
bytes32
|
uint256 dataPtr;
|
||||||
)
|
uint256 blockPtr;
|
||||||
{
|
|
||||||
uint256 publicInputsPtr;
|
assembly {
|
||||||
// 1. append prevStateRoot, newStateRoot and withdrawTrieRoot to public inputs
|
dataPtr := mload(0x40)
|
||||||
{
|
startDataPtr := dataPtr
|
||||||
bytes32 prevStateRoot = batch.prevStateRoot;
|
chunkPtr := add(_chunk, 0x20) // skip chunkLength
|
||||||
bytes32 newStateRoot = batch.newStateRoot;
|
blockPtr := add(chunkPtr, 1) // skip numBlocks
|
||||||
bytes32 withdrawTrieRoot = batch.withdrawTrieRoot;
|
}
|
||||||
// number of bytes in public inputs: 32 * 3 + 124 * blocks + 32 * MAX_NUM_TXS
|
|
||||||
uint256 publicInputsSize = 32 * 3 + batch.blocks.length * 124 + 32 * maxNumTxInBatch;
|
uint256 _numBlocks = ChunkCodec.validateChunkLength(chunkPtr, _chunk.length);
|
||||||
assembly {
|
|
||||||
publicInputsPtr := mload(0x40)
|
// concatenate block contexts
|
||||||
mstore(0x40, add(publicInputsPtr, publicInputsSize))
|
uint256 _totalTransactionsInChunk;
|
||||||
mstore(publicInputsPtr, prevStateRoot)
|
for (uint256 i = 0; i < _numBlocks; i++) {
|
||||||
publicInputsPtr := add(publicInputsPtr, 0x20)
|
dataPtr = ChunkCodec.copyBlockContext(chunkPtr, dataPtr, i);
|
||||||
mstore(publicInputsPtr, newStateRoot)
|
uint256 _numTransactionsInBlock = ChunkCodec.numTransactions(blockPtr);
|
||||||
publicInputsPtr := add(publicInputsPtr, 0x20)
|
unchecked {
|
||||||
mstore(publicInputsPtr, withdrawTrieRoot)
|
_totalTransactionsInChunk += _numTransactionsInBlock;
|
||||||
publicInputsPtr := add(publicInputsPtr, 0x20)
|
blockPtr += ChunkCodec.BLOCK_CONTEXT_LENGTH;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64 numTransactionsInBatch;
|
assembly {
|
||||||
BlockContext memory _block;
|
mstore(0x40, add(dataPtr, mul(_totalTransactionsInChunk, 0x20))) // reserve memory for tx hashes
|
||||||
// 2. append block information to public inputs.
|
blockPtr := add(chunkPtr, 1) // reset block ptr
|
||||||
for (uint256 i = 0; i < batch.blocks.length; i++) {
|
}
|
||||||
// validate blocks, we won't check first block against previous batch.
|
|
||||||
{
|
|
||||||
BlockContext memory _currentBlock = batch.blocks[i];
|
|
||||||
if (i > 0) {
|
|
||||||
require(_block.blockHash == _currentBlock.parentHash, "Parent hash mismatch");
|
|
||||||
require(_block.blockNumber + 1 == _currentBlock.blockNumber, "Block number mismatch");
|
|
||||||
}
|
|
||||||
_block = _currentBlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
// append blockHash and parentHash to public inputs
|
// concatenate tx hashes
|
||||||
{
|
uint256 l2TxPtr = ChunkCodec.l2TxPtr(chunkPtr, _numBlocks);
|
||||||
bytes32 blockHash = _block.blockHash;
|
while (_numBlocks > 0) {
|
||||||
bytes32 parentHash = _block.parentHash;
|
// concatenate l2 transaction hashes
|
||||||
|
uint256 _numTransactionsInBlock = ChunkCodec.numTransactions(blockPtr);
|
||||||
|
for (uint256 j = 0; j < _numTransactionsInBlock; j++) {
|
||||||
|
bytes32 txHash;
|
||||||
|
(txHash, l2TxPtr) = ChunkCodec.loadL2TxHash(l2TxPtr);
|
||||||
assembly {
|
assembly {
|
||||||
mstore(publicInputsPtr, blockHash)
|
mstore(dataPtr, txHash)
|
||||||
publicInputsPtr := add(publicInputsPtr, 0x20)
|
dataPtr := add(dataPtr, 0x20)
|
||||||
mstore(publicInputsPtr, parentHash)
|
|
||||||
publicInputsPtr := add(publicInputsPtr, 0x20)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// append blockNumber and blockTimestamp to public inputs
|
|
||||||
{
|
|
||||||
uint256 blockNumber = _block.blockNumber;
|
|
||||||
uint256 blockTimestamp = _block.timestamp;
|
|
||||||
assembly {
|
|
||||||
mstore(publicInputsPtr, shl(192, blockNumber))
|
|
||||||
publicInputsPtr := add(publicInputsPtr, 0x8)
|
|
||||||
mstore(publicInputsPtr, shl(192, blockTimestamp))
|
|
||||||
publicInputsPtr := add(publicInputsPtr, 0x8)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// append baseFee to public inputs
|
|
||||||
{
|
|
||||||
uint256 baseFee = _block.baseFee;
|
|
||||||
assembly {
|
|
||||||
mstore(publicInputsPtr, baseFee)
|
|
||||||
publicInputsPtr := add(publicInputsPtr, 0x20)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
uint64 numTransactionsInBlock = _block.numTransactions;
|
|
||||||
// gasLimit, numTransactions and numL1Messages to public inputs
|
|
||||||
{
|
|
||||||
uint256 gasLimit = _block.gasLimit;
|
|
||||||
uint256 numL1MessagesInBlock = _block.numL1Messages;
|
|
||||||
assembly {
|
|
||||||
mstore(publicInputsPtr, shl(192, gasLimit))
|
|
||||||
publicInputsPtr := add(publicInputsPtr, 0x8)
|
|
||||||
mstore(publicInputsPtr, shl(240, numTransactionsInBlock))
|
|
||||||
publicInputsPtr := add(publicInputsPtr, 0x2)
|
|
||||||
mstore(publicInputsPtr, shl(240, numL1MessagesInBlock))
|
|
||||||
publicInputsPtr := add(publicInputsPtr, 0x2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
numTransactionsInBatch += numTransactionsInBlock;
|
|
||||||
}
|
|
||||||
require(numTransactionsInBatch <= maxNumTxInBatch, "Too many transactions in batch");
|
|
||||||
|
|
||||||
// 3. append transaction hash to public inputs.
|
unchecked {
|
||||||
uint256 _l2TxnPtr;
|
_numBlocks -= 1;
|
||||||
{
|
blockPtr += ChunkCodec.BLOCK_CONTEXT_LENGTH;
|
||||||
bytes memory l2Transactions = batch.l2Transactions;
|
|
||||||
assembly {
|
|
||||||
_l2TxnPtr := add(l2Transactions, 0x20)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (uint256 i = 0; i < batch.blocks.length; i++) {
|
|
||||||
uint256 numL1MessagesInBlock = batch.blocks[i].numL1Messages;
|
|
||||||
require(numL1MessagesInBlock == 0);
|
|
||||||
uint256 numTransactionsInBlock = batch.blocks[i].numTransactions;
|
|
||||||
for (uint256 j = numL1MessagesInBlock; j < numTransactionsInBlock; ++j) {
|
|
||||||
bytes32 hash;
|
|
||||||
assembly {
|
|
||||||
let txPayloadLength := shr(224, mload(_l2TxnPtr))
|
|
||||||
_l2TxnPtr := add(_l2TxnPtr, 4)
|
|
||||||
_l2TxnPtr := add(_l2TxnPtr, txPayloadLength)
|
|
||||||
hash := keccak256(sub(_l2TxnPtr, txPayloadLength), txPayloadLength)
|
|
||||||
mstore(publicInputsPtr, hash)
|
|
||||||
publicInputsPtr := add(publicInputsPtr, 0x20)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. append padding transaction to public inputs.
|
// check chunk has correct length
|
||||||
bytes32 txHashPadding = paddingTxHash;
|
require(l2TxPtr - chunkPtr == _chunk.length, "incomplete l2 transaction data");
|
||||||
for (uint256 i = numTransactionsInBatch; i < maxNumTxInBatch; i++) {
|
|
||||||
assembly {
|
|
||||||
mstore(publicInputsPtr, txHashPadding)
|
|
||||||
publicInputsPtr := add(publicInputsPtr, 0x20)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. compute public input hash
|
// compute data hash and store to memory
|
||||||
bytes32 publicInputHash;
|
assembly {
|
||||||
{
|
let dataHash := keccak256(startDataPtr, sub(dataPtr, startDataPtr))
|
||||||
uint256 publicInputsSize = 32 * 3 + batch.blocks.length * 124 + 32 * maxNumTxInBatch;
|
mstore(memPtr, dataHash)
|
||||||
assembly {
|
|
||||||
publicInputHash := keccak256(sub(publicInputsPtr, publicInputsSize), publicInputsSize)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return publicInputHash;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,24 +11,6 @@ contract MockBridgeL2 {
|
|||||||
/// @param messageHash The hash of the corresponding message.
|
/// @param messageHash The hash of the corresponding message.
|
||||||
event AppendMessage(uint256 index, bytes32 messageHash);
|
event AppendMessage(uint256 index, bytes32 messageHash);
|
||||||
|
|
||||||
/********************************
|
|
||||||
* Events from L1BlockContainer *
|
|
||||||
********************************/
|
|
||||||
|
|
||||||
/// @notice Emitted when a block is imported.
|
|
||||||
/// @param blockHash The hash of the imported block.
|
|
||||||
/// @param blockHeight The height of the imported block.
|
|
||||||
/// @param blockTimestamp The timestamp of the imported block.
|
|
||||||
/// @param baseFee The base fee of the imported block.
|
|
||||||
/// @param stateRoot The state root of the imported block.
|
|
||||||
event ImportBlock(
|
|
||||||
bytes32 indexed blockHash,
|
|
||||||
uint256 blockHeight,
|
|
||||||
uint256 blockTimestamp,
|
|
||||||
uint256 baseFee,
|
|
||||||
bytes32 stateRoot
|
|
||||||
);
|
|
||||||
|
|
||||||
/*********************************
|
/*********************************
|
||||||
* Events from L2ScrollMessenger *
|
* Events from L2ScrollMessenger *
|
||||||
*********************************/
|
*********************************/
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
|
"scroll-tech/common/database"
|
||||||
"scroll-tech/common/docker"
|
"scroll-tech/common/docker"
|
||||||
|
|
||||||
|
"scroll-tech/database/migrate"
|
||||||
|
|
||||||
bcmd "scroll-tech/bridge/cmd"
|
bcmd "scroll-tech/bridge/cmd"
|
||||||
"scroll-tech/bridge/internal/config"
|
|
||||||
"scroll-tech/bridge/internal/orm/migrate"
|
|
||||||
"scroll-tech/bridge/internal/utils"
|
|
||||||
"scroll-tech/bridge/mock_bridge"
|
"scroll-tech/bridge/mock_bridge"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -46,13 +46,13 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func setupDB(t *testing.T) *gorm.DB {
|
func setupDB(t *testing.T) *gorm.DB {
|
||||||
cfg := &config.DBConfig{
|
cfg := &database.Config{
|
||||||
DSN: base.DBConfig.DSN,
|
DSN: base.DBConfig.DSN,
|
||||||
DriverName: base.DBConfig.DriverName,
|
DriverName: base.DBConfig.DriverName,
|
||||||
MaxOpenNum: base.DBConfig.MaxOpenNum,
|
MaxOpenNum: base.DBConfig.MaxOpenNum,
|
||||||
MaxIdleNum: base.DBConfig.MaxIdleNum,
|
MaxIdleNum: base.DBConfig.MaxIdleNum,
|
||||||
}
|
}
|
||||||
db, err := utils.InitDB(cfg)
|
db, err := database.InitDB(cfg)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
sqlDB, err := db.DB()
|
sqlDB, err := db.DB()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@@ -63,9 +63,9 @@ func setupDB(t *testing.T) *gorm.DB {
|
|||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
base = docker.NewDockerApp()
|
base = docker.NewDockerApp()
|
||||||
bridgeApp = bcmd.NewBridgeApp(base, "../conf/config.json")
|
bridgeApp = bcmd.NewBridgeApp(base, "../conf/config.json")
|
||||||
|
defer bridgeApp.Free()
|
||||||
|
defer base.Free()
|
||||||
m.Run()
|
m.Run()
|
||||||
bridgeApp.Free()
|
|
||||||
base.Free()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupEnv(t *testing.T) {
|
func setupEnv(t *testing.T) {
|
||||||
@@ -129,6 +129,10 @@ func prepareContracts(t *testing.T) {
|
|||||||
func TestFunction(t *testing.T) {
|
func TestFunction(t *testing.T) {
|
||||||
setupEnv(t)
|
setupEnv(t)
|
||||||
|
|
||||||
|
// process start test
|
||||||
|
t.Run("TestProcessStart", testProcessStart)
|
||||||
|
t.Run("TestProcessStartEnableMetrics", testProcessStartEnableMetrics)
|
||||||
|
|
||||||
// l1 rollup and watch rollup events
|
// l1 rollup and watch rollup events
|
||||||
t.Run("TestCommitBatchAndFinalizeBatch", testCommitBatchAndFinalizeBatch)
|
t.Run("TestCommitBatchAndFinalizeBatch", testCommitBatchAndFinalizeBatch)
|
||||||
|
|
||||||
@@ -136,7 +140,7 @@ func TestFunction(t *testing.T) {
|
|||||||
t.Run("TestRelayL1MessageSucceed", testRelayL1MessageSucceed)
|
t.Run("TestRelayL1MessageSucceed", testRelayL1MessageSucceed)
|
||||||
|
|
||||||
// l2 message
|
// l2 message
|
||||||
t.Run("TestRelayL2MessageSucceed", testRelayL2MessageSucceed)
|
// TODO: add a "user relay l2msg Succeed" test
|
||||||
|
|
||||||
// l1/l2 gas oracle
|
// l1/l2 gas oracle
|
||||||
t.Run("TestImportL1GasPrice", testImportL1GasPrice)
|
t.Run("TestImportL1GasPrice", testImportL1GasPrice)
|
||||||
|
|||||||
@@ -8,20 +8,18 @@ import (
|
|||||||
"github.com/scroll-tech/go-ethereum/common"
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
gethTypes "github.com/scroll-tech/go-ethereum/core/types"
|
gethTypes "github.com/scroll-tech/go-ethereum/core/types"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gorm.io/gorm"
|
|
||||||
|
|
||||||
|
"scroll-tech/common/database"
|
||||||
"scroll-tech/common/types"
|
"scroll-tech/common/types"
|
||||||
|
|
||||||
"scroll-tech/bridge/internal/controller/relayer"
|
"scroll-tech/bridge/internal/controller/relayer"
|
||||||
"scroll-tech/bridge/internal/controller/watcher"
|
"scroll-tech/bridge/internal/controller/watcher"
|
||||||
"scroll-tech/bridge/internal/orm"
|
"scroll-tech/bridge/internal/orm"
|
||||||
bridgeTypes "scroll-tech/bridge/internal/types"
|
|
||||||
"scroll-tech/bridge/internal/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func testImportL1GasPrice(t *testing.T) {
|
func testImportL1GasPrice(t *testing.T) {
|
||||||
db := setupDB(t)
|
db := setupDB(t)
|
||||||
defer utils.CloseDB(db)
|
defer database.CloseDB(db)
|
||||||
|
|
||||||
prepareContracts(t)
|
prepareContracts(t)
|
||||||
|
|
||||||
@@ -45,10 +43,10 @@ func testImportL1GasPrice(t *testing.T) {
|
|||||||
|
|
||||||
l1BlockOrm := orm.NewL1Block(db)
|
l1BlockOrm := orm.NewL1Block(db)
|
||||||
// check db status
|
// check db status
|
||||||
latestBlockHeight, err := l1BlockOrm.GetLatestL1BlockHeight()
|
latestBlockHeight, err := l1BlockOrm.GetLatestL1BlockHeight(context.Background())
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, number, latestBlockHeight)
|
assert.Equal(t, number, latestBlockHeight)
|
||||||
blocks, err := l1BlockOrm.GetL1Blocks(map[string]interface{}{"number": latestBlockHeight})
|
blocks, err := l1BlockOrm.GetL1Blocks(context.Background(), map[string]interface{}{"number": latestBlockHeight})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, len(blocks), 1)
|
assert.Equal(t, len(blocks), 1)
|
||||||
assert.Empty(t, blocks[0].OracleTxHash)
|
assert.Empty(t, blocks[0].OracleTxHash)
|
||||||
@@ -56,7 +54,7 @@ func testImportL1GasPrice(t *testing.T) {
|
|||||||
|
|
||||||
// relay gas price
|
// relay gas price
|
||||||
l1Relayer.ProcessGasPriceOracle()
|
l1Relayer.ProcessGasPriceOracle()
|
||||||
blocks, err = l1BlockOrm.GetL1Blocks(map[string]interface{}{"number": latestBlockHeight})
|
blocks, err = l1BlockOrm.GetL1Blocks(context.Background(), map[string]interface{}{"number": latestBlockHeight})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, len(blocks), 1)
|
assert.Equal(t, len(blocks), 1)
|
||||||
assert.NotEmpty(t, blocks[0].OracleTxHash)
|
assert.NotEmpty(t, blocks[0].OracleTxHash)
|
||||||
@@ -65,54 +63,44 @@ func testImportL1GasPrice(t *testing.T) {
|
|||||||
|
|
||||||
func testImportL2GasPrice(t *testing.T) {
|
func testImportL2GasPrice(t *testing.T) {
|
||||||
db := setupDB(t)
|
db := setupDB(t)
|
||||||
defer utils.CloseDB(db)
|
defer database.CloseDB(db)
|
||||||
prepareContracts(t)
|
prepareContracts(t)
|
||||||
|
|
||||||
l2Cfg := bridgeApp.Config.L2Config
|
l2Cfg := bridgeApp.Config.L2Config
|
||||||
l2Relayer, err := relayer.NewLayer2Relayer(context.Background(), l2Client, db, l2Cfg.RelayerConfig)
|
l2Relayer, err := relayer.NewLayer2Relayer(context.Background(), l2Client, db, l2Cfg.RelayerConfig, false)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// add fake blocks
|
// add fake chunk
|
||||||
traces := []*bridgeTypes.WrappedBlock{
|
chunk := &types.Chunk{
|
||||||
{
|
Blocks: []*types.WrappedBlock{
|
||||||
Header: &gethTypes.Header{
|
{
|
||||||
Number: big.NewInt(1),
|
Header: &gethTypes.Header{
|
||||||
ParentHash: common.Hash{},
|
Number: big.NewInt(1),
|
||||||
Difficulty: big.NewInt(0),
|
ParentHash: common.Hash{},
|
||||||
BaseFee: big.NewInt(0),
|
Difficulty: big.NewInt(0),
|
||||||
|
BaseFee: big.NewInt(0),
|
||||||
|
},
|
||||||
|
Transactions: nil,
|
||||||
|
WithdrawRoot: common.Hash{},
|
||||||
},
|
},
|
||||||
Transactions: nil,
|
|
||||||
WithdrawTrieRoot: common.Hash{},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
chunkHash, err := chunk.Hash(0)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
blockTraceOrm := orm.NewBlockTrace(db)
|
batchOrm := orm.NewBatch(db)
|
||||||
assert.NoError(t, blockTraceOrm.InsertWrappedBlocks(traces))
|
_, err = batchOrm.InsertBatch(context.Background(), 0, 0, chunkHash.Hex(), chunkHash.Hex(), []*types.Chunk{chunk})
|
||||||
|
|
||||||
parentBatch := &bridgeTypes.BatchInfo{
|
|
||||||
Index: 0,
|
|
||||||
Hash: "0x0000000000000000000000000000000000000000",
|
|
||||||
}
|
|
||||||
batchData := bridgeTypes.NewBatchData(parentBatch, []*bridgeTypes.WrappedBlock{traces[0]}, l2Cfg.BatchProposerConfig.PublicInputConfig)
|
|
||||||
blockBatchOrm := orm.NewBlockBatch(db)
|
|
||||||
err = db.Transaction(func(tx *gorm.DB) error {
|
|
||||||
_, dbTxErr := blockBatchOrm.InsertBlockBatchByBatchData(tx, batchData)
|
|
||||||
if dbTxErr != nil {
|
|
||||||
return dbTxErr
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// check db status
|
// check db status
|
||||||
batch, err := blockBatchOrm.GetLatestBatch()
|
batch, err := batchOrm.GetLatestBatch(context.Background())
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Empty(t, batch.OracleTxHash)
|
assert.Empty(t, batch.OracleTxHash)
|
||||||
assert.Equal(t, types.GasOracleStatus(batch.OracleStatus), types.GasOraclePending)
|
assert.Equal(t, types.GasOracleStatus(batch.OracleStatus), types.GasOraclePending)
|
||||||
|
|
||||||
// relay gas price
|
// relay gas price
|
||||||
l2Relayer.ProcessGasPriceOracle()
|
l2Relayer.ProcessGasPriceOracle()
|
||||||
batch, err = blockBatchOrm.GetLatestBatch()
|
batch, err = batchOrm.GetLatestBatch(context.Background())
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotEmpty(t, batch.OracleTxHash)
|
assert.NotEmpty(t, batch.OracleTxHash)
|
||||||
assert.Equal(t, types.GasOracleStatus(batch.OracleStatus), types.GasOracleImporting)
|
assert.Equal(t, types.GasOracleStatus(batch.OracleStatus), types.GasOracleImporting)
|
||||||
|
|||||||
@@ -11,17 +11,17 @@ import (
|
|||||||
"github.com/scroll-tech/go-ethereum/rpc"
|
"github.com/scroll-tech/go-ethereum/rpc"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"scroll-tech/common/database"
|
||||||
"scroll-tech/common/types"
|
"scroll-tech/common/types"
|
||||||
|
|
||||||
"scroll-tech/bridge/internal/controller/relayer"
|
"scroll-tech/bridge/internal/controller/relayer"
|
||||||
"scroll-tech/bridge/internal/controller/watcher"
|
"scroll-tech/bridge/internal/controller/watcher"
|
||||||
"scroll-tech/bridge/internal/orm"
|
"scroll-tech/bridge/internal/orm"
|
||||||
"scroll-tech/bridge/internal/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func testRelayL1MessageSucceed(t *testing.T) {
|
func testRelayL1MessageSucceed(t *testing.T) {
|
||||||
db := setupDB(t)
|
db := setupDB(t)
|
||||||
defer utils.CloseDB(db)
|
defer database.CloseDB(db)
|
||||||
|
|
||||||
prepareContracts(t)
|
prepareContracts(t)
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user