mirror of
https://github.com/scroll-tech/scroll.git
synced 2026-01-11 23:18:07 -05:00
Compare commits
44 Commits
fix/prover
...
v4.1.115
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87f18efba8 | ||
|
|
fecd129a39 | ||
|
|
663156984f | ||
|
|
3499c595e7 | ||
|
|
95d2df46e3 | ||
|
|
102d29c54d | ||
|
|
7d50699344 | ||
|
|
e08b800d1d | ||
|
|
6139ca0df0 | ||
|
|
24a0fd08ac | ||
|
|
2840485f38 | ||
|
|
dab21fc712 | ||
|
|
c44b7f7bf4 | ||
|
|
a8c71b5e36 | ||
|
|
ae2f62df00 | ||
|
|
ce5c6e0aa3 | ||
|
|
e8ddf99184 | ||
|
|
ebf2b429a3 | ||
|
|
db46ce408d | ||
|
|
3d1a8374d0 | ||
|
|
574aa68491 | ||
|
|
589388b288 | ||
|
|
af8175800f | ||
|
|
e6793a85f5 | ||
|
|
7e9f3b7376 | ||
|
|
8b611b443a | ||
|
|
1f62596b0a | ||
|
|
a2b9878733 | ||
|
|
e166dfba48 | ||
|
|
d7d62eb8e6 | ||
|
|
61eb75fbb6 | ||
|
|
e9af5912ac | ||
|
|
c3b2f13fe0 | ||
|
|
0d847e0427 | ||
|
|
7de221ba89 | ||
|
|
16fd840896 | ||
|
|
20fb098a01 | ||
|
|
b734282a44 | ||
|
|
30bbfc0229 | ||
|
|
60bea74444 | ||
|
|
ae2e010324 | ||
|
|
767a2cbfdf | ||
|
|
74e5de156e | ||
|
|
becfd41b0e |
4
.github/workflows/bridge.yml
vendored
4
.github/workflows/bridge.yml
vendored
@@ -43,8 +43,6 @@ jobs:
|
||||
version: '0.8.16'
|
||||
- name: Install Geth Tools
|
||||
uses: gacts/install-geth-tools@v1
|
||||
with:
|
||||
version: 1.10.19
|
||||
- name: Lint
|
||||
working-directory: 'bridge'
|
||||
run: |
|
||||
@@ -94,8 +92,6 @@ jobs:
|
||||
version: '0.8.16'
|
||||
- name: Install Geth Tools
|
||||
uses: gacts/install-geth-tools@v1
|
||||
with:
|
||||
version: 1.10.19
|
||||
- name: Build prerequisites
|
||||
run: |
|
||||
make dev_docker
|
||||
|
||||
6
.github/workflows/bump_version.yml
vendored
6
.github/workflows/bump_version.yml
vendored
@@ -15,6 +15,8 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.head_ref }}
|
||||
- name: check diff
|
||||
id: check_diff
|
||||
run: |
|
||||
@@ -49,7 +51,11 @@ jobs:
|
||||
- name: bump version in common/version/version.go
|
||||
if: steps.check_diff.outputs.result == 'bump'
|
||||
run: node .github/scripts/bump_version_dot_go.mjs
|
||||
|
||||
# Commits made by this Action do not trigger new Workflow runs
|
||||
- uses: stefanzweifel/git-auto-commit-action@3ea6ae190baf489ba007f7c92608f33ce20ef04a
|
||||
if: steps.check_diff.outputs.result == 'bump'
|
||||
with:
|
||||
skip_fetch: true # already did fetch in check diff
|
||||
file_pattern: "common/version/version.go"
|
||||
commit_message: "chore: auto version bump [bot]"
|
||||
|
||||
2
.github/workflows/common.yml
vendored
2
.github/workflows/common.yml
vendored
@@ -88,8 +88,6 @@ jobs:
|
||||
version: '0.8.16'
|
||||
- name: Install Geth Tools
|
||||
uses: gacts/install-geth-tools@v1
|
||||
with:
|
||||
version: 1.10.19
|
||||
- name: Build prerequisites
|
||||
run: |
|
||||
make dev_docker
|
||||
|
||||
2
.github/workflows/coordinator.yml
vendored
2
.github/workflows/coordinator.yml
vendored
@@ -104,8 +104,6 @@ jobs:
|
||||
version: '0.8.16'
|
||||
- name: Install Geth Tools
|
||||
uses: gacts/install-geth-tools@v1
|
||||
with:
|
||||
version: 1.10.19
|
||||
- name: Build prerequisites
|
||||
run: |
|
||||
make dev_docker
|
||||
|
||||
2
.github/workflows/database.yml
vendored
2
.github/workflows/database.yml
vendored
@@ -81,8 +81,6 @@ jobs:
|
||||
version: '0.8.16'
|
||||
- name: Install Geth Tools
|
||||
uses: gacts/install-geth-tools@v1
|
||||
with:
|
||||
version: 1.10.19
|
||||
- name: Build prerequisites
|
||||
run: |
|
||||
make dev_docker
|
||||
|
||||
2
.github/workflows/integration.yaml
vendored
2
.github/workflows/integration.yaml
vendored
@@ -33,8 +33,6 @@ jobs:
|
||||
version: '0.8.16'
|
||||
- name: Install Geth Tools
|
||||
uses: gacts/install-geth-tools@v1
|
||||
with:
|
||||
version: 1.10.19
|
||||
- name: Build prerequisites
|
||||
run: |
|
||||
make dev_docker
|
||||
|
||||
@@ -107,7 +107,7 @@ func action(ctx *cli.Context) error {
|
||||
|
||||
go utils.Loop(subCtx, 2*time.Second, chunkProposer.TryProposeChunk)
|
||||
|
||||
go utils.Loop(subCtx, 2*time.Second, batchProposer.TryProposeBatch)
|
||||
go utils.Loop(subCtx, 10*time.Second, batchProposer.TryProposeBatch)
|
||||
|
||||
go utils.Loop(subCtx, 2*time.Second, l2relayer.ProcessPendingBatches)
|
||||
|
||||
|
||||
@@ -64,11 +64,9 @@
|
||||
"finalize_sender_private_key": "1515151515151515151515151515151515151515151515151515151515151515"
|
||||
},
|
||||
"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,
|
||||
"max_row_consumption_per_chunk": 1048319,
|
||||
"gas_cost_increase_multiplier": 1.2
|
||||
@@ -77,7 +75,6 @@
|
||||
"max_chunk_num_per_batch": 112,
|
||||
"max_l1_commit_gas_per_batch": 11234567,
|
||||
"max_l1_commit_calldata_size_per_batch": 112345,
|
||||
"min_chunk_num_per_batch": 11,
|
||||
"batch_timeout_sec": 300,
|
||||
"gas_cost_increase_multiplier": 1.2
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@@ -15,6 +16,13 @@ type Config struct {
|
||||
DBConfig *database.Config `json:"db_config"`
|
||||
}
|
||||
|
||||
func (c *Config) validate() error {
|
||||
if maxChunkPerBatch := c.L2Config.BatchProposerConfig.MaxChunkNumPerBatch; maxChunkPerBatch <= 0 {
|
||||
return fmt.Errorf("Invalid max_chunk_num_per_batch configuration: %v", maxChunkPerBatch)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewConfig returns a new instance of Config.
|
||||
func NewConfig(file string) (*Config, error) {
|
||||
buf, err := os.ReadFile(filepath.Clean(file))
|
||||
@@ -28,5 +36,8 @@ func NewConfig(file string) (*Config, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := cfg.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
@@ -28,11 +28,9 @@ type L2Config struct {
|
||||
|
||||
// ChunkProposerConfig loads chunk_proposer configuration items.
|
||||
type ChunkProposerConfig struct {
|
||||
MaxTxGasPerChunk uint64 `json:"max_tx_gas_per_chunk"`
|
||||
MaxL2TxNumPerChunk uint64 `json:"max_l2_tx_num_per_chunk"`
|
||||
MaxL1CommitGasPerChunk uint64 `json:"max_l1_commit_gas_per_chunk"`
|
||||
MaxL1CommitCalldataSizePerChunk uint64 `json:"max_l1_commit_calldata_size_per_chunk"`
|
||||
MinL1CommitCalldataSizePerChunk uint64 `json:"min_l1_commit_calldata_size_per_chunk"`
|
||||
ChunkTimeoutSec uint64 `json:"chunk_timeout_sec"`
|
||||
MaxRowConsumptionPerChunk uint64 `json:"max_row_consumption_per_chunk"`
|
||||
GasCostIncreaseMultiplier float64 `json:"gas_cost_increase_multiplier"`
|
||||
@@ -43,7 +41,6 @@ type BatchProposerConfig struct {
|
||||
MaxChunkNumPerBatch uint64 `json:"max_chunk_num_per_batch"`
|
||||
MaxL1CommitGasPerBatch uint64 `json:"max_l1_commit_gas_per_batch"`
|
||||
MaxL1CommitCalldataSizePerBatch uint32 `json:"max_l1_commit_calldata_size_per_batch"`
|
||||
MinChunkNumPerBatch uint64 `json:"min_chunk_num_per_batch"`
|
||||
BatchTimeoutSec uint64 `json:"batch_timeout_sec"`
|
||||
GasCostIncreaseMultiplier float64 `json:"gas_cost_increase_multiplier"`
|
||||
}
|
||||
|
||||
@@ -226,7 +226,7 @@ func (r *Layer1Relayer) handleConfirmLoop(ctx context.Context) {
|
||||
log.Info("transaction confirmed in layer2", "confirmation", cfm)
|
||||
}
|
||||
case cfm := <-r.gasOracleSender.ConfirmChan():
|
||||
r.metrics.bridgeL1MsgsRelayedConfirmedTotal.Inc()
|
||||
r.metrics.bridgeL1GasOraclerConfirmedTotal.Inc()
|
||||
if !cfm.IsSuccessful {
|
||||
// @discuss: maybe make it pending again?
|
||||
err := r.l1BlockOrm.UpdateL1GasOracleStatusAndOracleTxHash(r.ctx, cfm.ID, types.GasOracleFailed, cfm.TxHash.String())
|
||||
|
||||
@@ -430,10 +430,6 @@ func (r *Layer2Relayer) ProcessCommittedBatches() {
|
||||
case types.ProvingTaskUnassigned, types.ProvingTaskAssigned:
|
||||
// The proof for this block is not ready yet.
|
||||
return
|
||||
case types.ProvingTaskProved:
|
||||
// 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.
|
||||
return
|
||||
case types.ProvingTaskVerified:
|
||||
log.Info("Start to roll up zk proof", "hash", hash)
|
||||
r.metrics.bridgeL2RelayerProcessCommittedBatchesFinalizedTotal.Inc()
|
||||
|
||||
@@ -346,8 +346,15 @@ func (s *Sender) resubmitTransaction(feeData *FeeData, auth *bind.TransactOpts,
|
||||
escalateMultipleDen := new(big.Int).SetUint64(s.config.EscalateMultipleDen)
|
||||
maxGasPrice := new(big.Int).SetUint64(s.config.MaxGasPrice)
|
||||
|
||||
txInfo := map[string]interface{}{
|
||||
"tx_hash": tx.Hash().String(),
|
||||
"tx_type": s.config.TxType,
|
||||
"from": auth.From.String(),
|
||||
}
|
||||
|
||||
switch s.config.TxType {
|
||||
case LegacyTxType, AccessListTxType: // `LegacyTxType`is for ganache mock node
|
||||
originalGasPrice := feeData.gasPrice
|
||||
gasPrice := escalateMultipleNum.Mul(escalateMultipleNum, big.NewInt(feeData.gasPrice.Int64()))
|
||||
gasPrice = gasPrice.Div(gasPrice, escalateMultipleDen)
|
||||
if gasPrice.Cmp(feeData.gasPrice) < 0 {
|
||||
@@ -357,7 +364,13 @@ func (s *Sender) resubmitTransaction(feeData *FeeData, auth *bind.TransactOpts,
|
||||
gasPrice = maxGasPrice
|
||||
}
|
||||
feeData.gasPrice = gasPrice
|
||||
|
||||
txInfo["original_gas_price"] = originalGasPrice
|
||||
txInfo["adjusted_gas_price"] = gasPrice
|
||||
default:
|
||||
originalGasTipCap := big.NewInt(feeData.gasTipCap.Int64())
|
||||
originalGasFeeCap := big.NewInt(feeData.gasFeeCap.Int64())
|
||||
|
||||
gasTipCap := big.NewInt(feeData.gasTipCap.Int64())
|
||||
gasTipCap = gasTipCap.Mul(gasTipCap, escalateMultipleNum)
|
||||
gasTipCap = gasTipCap.Div(gasTipCap, escalateMultipleDen)
|
||||
@@ -389,8 +402,15 @@ func (s *Sender) resubmitTransaction(feeData *FeeData, auth *bind.TransactOpts,
|
||||
}
|
||||
feeData.gasFeeCap = gasFeeCap
|
||||
feeData.gasTipCap = gasTipCap
|
||||
|
||||
txInfo["original_gas_tip_cap"] = originalGasTipCap
|
||||
txInfo["adjusted_gas_tip_cap"] = gasTipCap
|
||||
txInfo["original_gas_fee_cap"] = originalGasFeeCap
|
||||
txInfo["adjusted_gas_fee_cap"] = gasFeeCap
|
||||
}
|
||||
|
||||
log.Debug("Transaction gas adjustment details", txInfo)
|
||||
|
||||
nonce := tx.Nonce()
|
||||
s.metrics.resubmitTransactionTotal.WithLabelValues(s.service, s.name).Inc()
|
||||
return s.createAndSendTx(auth, feeData, tx.To(), tx.Value(), tx.Data(), &nonce)
|
||||
@@ -429,6 +449,12 @@ func (s *Sender) checkPendingTransaction(header *types.Header, confirmed uint64)
|
||||
}
|
||||
}
|
||||
} else if s.config.EscalateBlocks+pending.submitAt < number {
|
||||
log.Debug("resubmit transaction",
|
||||
"tx hash", pending.tx.Hash().String(),
|
||||
"submit block number", pending.submitAt,
|
||||
"current block number", number,
|
||||
"escalateBlocks", s.config.EscalateBlocks)
|
||||
|
||||
var tx *types.Transaction
|
||||
tx, err := s.resubmitTransaction(pending.feeData, pending.signer, pending.tx)
|
||||
if err != nil {
|
||||
@@ -485,8 +511,8 @@ func (s *Sender) checkBalance(ctx context.Context) error {
|
||||
}
|
||||
|
||||
if bls.Cmp(s.minBalance) < 0 {
|
||||
return fmt.Errorf("insufficient account balance - actual balance: %s, minimum required balance: %s",
|
||||
bls.String(), s.minBalance.String())
|
||||
return fmt.Errorf("insufficient account balance - actual balance: %s, minimum required balance: %s, address: %s",
|
||||
bls.String(), s.minBalance.String(), s.auth.From.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -28,7 +28,6 @@ type BatchProposer struct {
|
||||
maxChunkNumPerBatch uint64
|
||||
maxL1CommitGasPerBatch uint64
|
||||
maxL1CommitCalldataSizePerBatch uint32
|
||||
minChunkNumPerBatch uint64
|
||||
batchTimeoutSec uint64
|
||||
gasCostIncreaseMultiplier float64
|
||||
|
||||
@@ -39,12 +38,18 @@ type BatchProposer struct {
|
||||
totalL1CommitGas prometheus.Gauge
|
||||
totalL1CommitCalldataSize prometheus.Gauge
|
||||
batchChunksNum prometheus.Gauge
|
||||
batchFirstChunkTimeoutReached prometheus.Counter
|
||||
batchChunksSuperposeNotEnoughTotal prometheus.Counter
|
||||
batchFirstBlockTimeoutReached prometheus.Counter
|
||||
batchChunksProposeNotEnoughTotal prometheus.Counter
|
||||
}
|
||||
|
||||
// NewBatchProposer creates a new BatchProposer instance.
|
||||
func NewBatchProposer(ctx context.Context, cfg *config.BatchProposerConfig, db *gorm.DB, reg prometheus.Registerer) *BatchProposer {
|
||||
log.Debug("new batch proposer",
|
||||
"maxChunkNumPerBatch", cfg.MaxChunkNumPerBatch,
|
||||
"maxL1CommitGasPerBatch", cfg.MaxL1CommitGasPerBatch,
|
||||
"maxL1CommitCalldataSizePerBatch", cfg.MaxL1CommitCalldataSizePerBatch,
|
||||
"batchTimeoutSec", cfg.BatchTimeoutSec)
|
||||
|
||||
return &BatchProposer{
|
||||
ctx: ctx,
|
||||
db: db,
|
||||
@@ -54,7 +59,6 @@ func NewBatchProposer(ctx context.Context, cfg *config.BatchProposerConfig, db *
|
||||
maxChunkNumPerBatch: cfg.MaxChunkNumPerBatch,
|
||||
maxL1CommitGasPerBatch: cfg.MaxL1CommitGasPerBatch,
|
||||
maxL1CommitCalldataSizePerBatch: cfg.MaxL1CommitCalldataSizePerBatch,
|
||||
minChunkNumPerBatch: cfg.MinChunkNumPerBatch,
|
||||
batchTimeoutSec: cfg.BatchTimeoutSec,
|
||||
gasCostIncreaseMultiplier: cfg.GasCostIncreaseMultiplier,
|
||||
|
||||
@@ -86,13 +90,13 @@ func NewBatchProposer(ctx context.Context, cfg *config.BatchProposerConfig, db *
|
||||
Name: "bridge_propose_batch_chunks_number",
|
||||
Help: "The number of chunks in the batch",
|
||||
}),
|
||||
batchFirstChunkTimeoutReached: promauto.With(reg).NewCounter(prometheus.CounterOpts{
|
||||
Name: "bridge_propose_batch_first_chunk_timeout_reached_total",
|
||||
Help: "Total times of batch's first chunk timeout reached",
|
||||
batchFirstBlockTimeoutReached: promauto.With(reg).NewCounter(prometheus.CounterOpts{
|
||||
Name: "bridge_propose_batch_first_block_timeout_reached_total",
|
||||
Help: "Total times of batch's first block timeout reached",
|
||||
}),
|
||||
batchChunksSuperposeNotEnoughTotal: promauto.With(reg).NewCounter(prometheus.CounterOpts{
|
||||
Name: "bridge_propose_batch_chunks_superpose_not_enough_total",
|
||||
Help: "Total number of batch chunk superpose not enough",
|
||||
batchChunksProposeNotEnoughTotal: promauto.With(reg).NewCounter(prometheus.CounterOpts{
|
||||
Name: "bridge_propose_batch_chunks_propose_not_enough_total",
|
||||
Help: "Total number of batch chunk propose not enough",
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -144,7 +148,7 @@ func (p *BatchProposer) updateBatchInfoInDB(dbChunks []*orm.Chunk) error {
|
||||
}
|
||||
|
||||
func (p *BatchProposer) proposeBatchChunks() ([]*orm.Chunk, error) {
|
||||
dbChunks, err := p.chunkOrm.GetUnbatchedChunks(p.ctx)
|
||||
dbChunks, err := p.chunkOrm.GetUnbatchedChunks(p.ctx, int(p.maxChunkNumPerBatch)+1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -153,87 +157,90 @@ func (p *BatchProposer) proposeBatchChunks() ([]*orm.Chunk, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
firstChunk := dbChunks[0]
|
||||
totalL1CommitCalldataSize := firstChunk.TotalL1CommitCalldataSize
|
||||
totalL1CommitGas := firstChunk.TotalL1CommitGas
|
||||
totalChunks := uint64(1)
|
||||
totalL1MessagePopped := firstChunk.TotalL1MessagesPoppedBefore + uint64(firstChunk.TotalL1MessagesPoppedInChunk)
|
||||
var totalL1CommitCalldataSize uint32
|
||||
var totalL1CommitGas uint64
|
||||
var totalChunks uint64
|
||||
var totalL1MessagePopped uint64
|
||||
|
||||
parentBatch, err := p.batchOrm.GetLatestBatch(p.ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
getKeccakGas := func(size uint64) uint64 {
|
||||
return 30 + 6*((size+31)/32) // 30 + 6 * ceil(size / 32)
|
||||
}
|
||||
|
||||
// Add extra gas costs
|
||||
totalL1CommitGas += 4 * 2100 // 4 one-time cold sload for commitBatch
|
||||
totalL1CommitGas += 20000 // 1 time sstore
|
||||
totalL1CommitGas += 16 // version in calldata
|
||||
totalL1CommitGas += 16 * (32 * (totalL1MessagePopped + 255) / 256) // _skippedL1MessageBitmap in calldata
|
||||
totalL1CommitGas += 4 * 2100 // 4 one-time cold sload for commitBatch
|
||||
totalL1CommitGas += 20000 // 1 time sstore
|
||||
totalL1CommitGas += types.CalldataNonZeroByteGas // version in calldata
|
||||
|
||||
// adjusting gas:
|
||||
// add 1 time cold sload (2100 gas) for L1MessageQueue
|
||||
// add 1 time cold address access (2600 gas) for L1MessageQueue
|
||||
// minus 1 time warm sload (100 gas) & 1 time warm address access (100 gas)
|
||||
totalL1CommitGas += (2100 + 2600 - 100 - 100)
|
||||
totalL1CommitGas += getKeccakGas(32 * totalChunks) // batch data hash
|
||||
if parentBatch != nil {
|
||||
totalL1CommitGas += getKeccakGas(uint64(len(parentBatch.BatchHeader))) // parent batch header hash
|
||||
totalL1CommitGas += 16 * uint64(len(parentBatch.BatchHeader)) // parent batch header in calldata
|
||||
}
|
||||
// batch header size: 89 + 32 * ceil(l1MessagePopped / 256)
|
||||
totalL1CommitGas += getKeccakGas(89 + 32*(totalL1MessagePopped+255)/256)
|
||||
|
||||
p.totalL1CommitGas.Set(float64(totalL1CommitGas))
|
||||
// Check if the first chunk breaks hard limits.
|
||||
// If so, it indicates there are bugs in chunk-proposer, manual fix is needed.
|
||||
if p.gasCostIncreaseMultiplier*float64(totalL1CommitGas) > float64(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,
|
||||
)
|
||||
totalL1CommitGas += types.GetKeccak256Gas(uint64(len(parentBatch.BatchHeader))) // parent batch header hash
|
||||
totalL1CommitGas += types.CalldataNonZeroByteGas * uint64(len(parentBatch.BatchHeader)) // parent batch header in calldata
|
||||
}
|
||||
|
||||
p.totalL1CommitCalldataSize.Set(float64(totalL1CommitCalldataSize))
|
||||
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 {
|
||||
// metric values
|
||||
lastTotalL1CommitCalldataSize := totalL1CommitCalldataSize
|
||||
lastTotalL1CommitGas := totalL1CommitGas
|
||||
|
||||
for i, chunk := range dbChunks[1:] {
|
||||
totalL1CommitCalldataSize += chunk.TotalL1CommitCalldataSize
|
||||
totalL1CommitGas += chunk.TotalL1CommitGas
|
||||
// adjust batch data hash gas cost
|
||||
totalL1CommitGas -= getKeccakGas(32 * totalChunks)
|
||||
totalL1CommitGas -= types.GetKeccak256Gas(32 * totalChunks)
|
||||
totalChunks++
|
||||
totalL1CommitGas += getKeccakGas(32 * totalChunks)
|
||||
// adjust batch header hash gas cost
|
||||
totalL1CommitGas -= getKeccakGas(89 + 32*(totalL1MessagePopped+255)/256)
|
||||
totalL1CommitGas -= 16 * (32 * (totalL1MessagePopped + 255) / 256)
|
||||
totalL1CommitGas += types.GetKeccak256Gas(32 * totalChunks)
|
||||
// adjust batch header hash gas cost, batch header size: 89 + 32 * ceil(l1MessagePopped / 256)
|
||||
totalL1CommitGas -= types.GetKeccak256Gas(89 + 32*(totalL1MessagePopped+255)/256)
|
||||
totalL1CommitGas -= types.CalldataNonZeroByteGas * (32 * (totalL1MessagePopped + 255) / 256)
|
||||
totalL1MessagePopped += uint64(chunk.TotalL1MessagesPoppedInChunk)
|
||||
totalL1CommitGas += 16 * (32 * (totalL1MessagePopped + 255) / 256)
|
||||
totalL1CommitGas += getKeccakGas(89 + 32*(totalL1MessagePopped+255)/256)
|
||||
totalL1CommitGas += types.CalldataNonZeroByteGas * (32 * (totalL1MessagePopped + 255) / 256)
|
||||
totalL1CommitGas += types.GetKeccak256Gas(89 + 32*(totalL1MessagePopped+255)/256)
|
||||
totalOverEstimateL1CommitGas := uint64(p.gasCostIncreaseMultiplier * float64(totalL1CommitGas))
|
||||
if totalChunks > p.maxChunkNumPerBatch ||
|
||||
totalL1CommitCalldataSize > p.maxL1CommitCalldataSizePerBatch ||
|
||||
p.gasCostIncreaseMultiplier*float64(totalL1CommitGas) > float64(p.maxL1CommitGasPerBatch) {
|
||||
return dbChunks[:i+1], nil
|
||||
totalOverEstimateL1CommitGas > p.maxL1CommitGasPerBatch {
|
||||
// Check if the first chunk breaks hard limits.
|
||||
// If so, it indicates there are bugs in chunk-proposer, manual fix is needed.
|
||||
if i == 0 {
|
||||
if totalOverEstimateL1CommitGas > 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",
|
||||
dbChunks[0].StartBlockNumber,
|
||||
dbChunks[0].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",
|
||||
dbChunks[0].StartBlockNumber,
|
||||
dbChunks[0].EndBlockNumber,
|
||||
totalL1CommitCalldataSize,
|
||||
p.maxL1CommitCalldataSizePerBatch,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
log.Debug("breaking limit condition in batching",
|
||||
"currentTotalChunks", totalChunks,
|
||||
"maxChunkNumPerBatch", p.maxChunkNumPerBatch,
|
||||
"currentL1CommitCalldataSize", totalL1CommitCalldataSize,
|
||||
"maxL1CommitCalldataSizePerBatch", p.maxL1CommitCalldataSizePerBatch,
|
||||
"currentOverEstimateL1CommitGas", totalOverEstimateL1CommitGas,
|
||||
"maxL1CommitGasPerBatch", p.maxL1CommitGasPerBatch)
|
||||
|
||||
p.totalL1CommitGas.Set(float64(lastTotalL1CommitGas))
|
||||
p.totalL1CommitCalldataSize.Set(float64(lastTotalL1CommitCalldataSize))
|
||||
p.batchChunksNum.Set(float64(i))
|
||||
return dbChunks[:i], nil
|
||||
}
|
||||
}
|
||||
|
||||
p.batchChunksNum.Set(float64(len(dbChunks)))
|
||||
|
||||
var hasChunkTimeout bool
|
||||
currentTimeSec := uint64(time.Now().Unix())
|
||||
if dbChunks[0].StartBlockTime+p.batchTimeoutSec < currentTimeSec {
|
||||
log.Warn("first block timeout",
|
||||
@@ -241,18 +248,16 @@ func (p *BatchProposer) proposeBatchChunks() ([]*orm.Chunk, error) {
|
||||
"first block timestamp", dbChunks[0].StartBlockTime,
|
||||
"chunk outdated time threshold", currentTimeSec,
|
||||
)
|
||||
hasChunkTimeout = true
|
||||
p.batchFirstChunkTimeoutReached.Inc()
|
||||
p.batchFirstBlockTimeoutReached.Inc()
|
||||
p.totalL1CommitGas.Set(float64(totalL1CommitGas))
|
||||
p.totalL1CommitCalldataSize.Set(float64(totalL1CommitCalldataSize))
|
||||
p.batchChunksNum.Set(float64(len(dbChunks)))
|
||||
return dbChunks, nil
|
||||
}
|
||||
|
||||
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,
|
||||
)
|
||||
p.batchChunksSuperposeNotEnoughTotal.Inc()
|
||||
return nil, nil
|
||||
}
|
||||
return dbChunks, nil
|
||||
log.Debug("pending chunks do not reach one of the constraints or contain a timeout block")
|
||||
p.batchChunksProposeNotEnoughTotal.Inc()
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (p *BatchProposer) dbChunksToBridgeChunks(dbChunks []*orm.Chunk) ([]*types.Chunk, error) {
|
||||
|
||||
@@ -23,11 +23,9 @@ func testBatchProposer(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
cp := NewChunkProposer(context.Background(), &config.ChunkProposerConfig{
|
||||
MaxTxGasPerChunk: 1000000000,
|
||||
MaxL2TxNumPerChunk: 10000,
|
||||
MaxL1CommitGasPerChunk: 50000000000,
|
||||
MaxL1CommitCalldataSizePerChunk: 1000000,
|
||||
MinL1CommitCalldataSizePerChunk: 0,
|
||||
MaxRowConsumptionPerChunk: 1048319,
|
||||
ChunkTimeoutSec: 300,
|
||||
}, db, nil)
|
||||
@@ -37,13 +35,12 @@ func testBatchProposer(t *testing.T) {
|
||||
MaxChunkNumPerBatch: 10,
|
||||
MaxL1CommitGasPerBatch: 50000000000,
|
||||
MaxL1CommitCalldataSizePerBatch: 1000000,
|
||||
MinChunkNumPerBatch: 1,
|
||||
BatchTimeoutSec: 300,
|
||||
}, db, nil)
|
||||
bp.TryProposeBatch()
|
||||
|
||||
chunkOrm := orm.NewChunk(db)
|
||||
chunks, err := chunkOrm.GetUnbatchedChunks(context.Background())
|
||||
chunks, err := chunkOrm.GetUnbatchedChunks(context.Background(), 0)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, chunks)
|
||||
|
||||
|
||||
@@ -18,6 +18,10 @@ import (
|
||||
"scroll-tech/bridge/internal/orm"
|
||||
)
|
||||
|
||||
// maxNumBlockPerChunk is the maximum number of blocks we allow per chunk.
|
||||
// Normally we will pack much fewer blocks because of other limits.
|
||||
const maxNumBlockPerChunk int = 100
|
||||
|
||||
// chunkRowConsumption is map(sub-circuit name => sub-circuit row count)
|
||||
type chunkRowConsumption map[string]uint64
|
||||
|
||||
@@ -51,11 +55,9 @@ type ChunkProposer struct {
|
||||
chunkOrm *orm.Chunk
|
||||
l2BlockOrm *orm.L2Block
|
||||
|
||||
maxTxGasPerChunk uint64
|
||||
maxL2TxNumPerChunk uint64
|
||||
maxL1CommitGasPerChunk uint64
|
||||
maxL1CommitCalldataSizePerChunk uint64
|
||||
minL1CommitCalldataSizePerChunk uint64
|
||||
maxRowConsumptionPerChunk uint64
|
||||
chunkTimeoutSec uint64
|
||||
gasCostIncreaseMultiplier float64
|
||||
@@ -70,22 +72,27 @@ type ChunkProposer struct {
|
||||
totalTxGasUsed prometheus.Gauge
|
||||
maxTxConsumption prometheus.Gauge
|
||||
chunkBlocksNum prometheus.Gauge
|
||||
chunkBlockTimeoutReached prometheus.Counter
|
||||
chunkBlocksSuperposeNotEnoughTotal prometheus.Counter
|
||||
chunkFirstBlockTimeoutReached prometheus.Counter
|
||||
chunkBlocksProposeNotEnoughTotal prometheus.Counter
|
||||
}
|
||||
|
||||
// NewChunkProposer creates a new ChunkProposer instance.
|
||||
func NewChunkProposer(ctx context.Context, cfg *config.ChunkProposerConfig, db *gorm.DB, reg prometheus.Registerer) *ChunkProposer {
|
||||
log.Debug("new chunk proposer",
|
||||
"maxL2TxNumPerChunk", cfg.MaxL2TxNumPerChunk,
|
||||
"maxL1CommitGasPerChunk", cfg.MaxL1CommitGasPerChunk,
|
||||
"maxL1CommitCalldataSizePerChunk", cfg.MaxL1CommitCalldataSizePerChunk,
|
||||
"maxRowConsumptionPerChunk", cfg.MaxRowConsumptionPerChunk,
|
||||
"chunkTimeoutSec", cfg.ChunkTimeoutSec)
|
||||
|
||||
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,
|
||||
maxRowConsumptionPerChunk: cfg.MaxRowConsumptionPerChunk,
|
||||
chunkTimeoutSec: cfg.ChunkTimeoutSec,
|
||||
gasCostIncreaseMultiplier: cfg.GasCostIncreaseMultiplier,
|
||||
@@ -130,13 +137,13 @@ func NewChunkProposer(ctx context.Context, cfg *config.ChunkProposerConfig, db *
|
||||
Name: "bridge_propose_chunk_chunk_block_number",
|
||||
Help: "The number of blocks in the chunk",
|
||||
}),
|
||||
chunkBlockTimeoutReached: promauto.With(reg).NewCounter(prometheus.CounterOpts{
|
||||
chunkFirstBlockTimeoutReached: promauto.With(reg).NewCounter(prometheus.CounterOpts{
|
||||
Name: "bridge_propose_chunk_first_block_timeout_reached_total",
|
||||
Help: "Total times of chunk's first block timeout reached",
|
||||
}),
|
||||
chunkBlocksSuperposeNotEnoughTotal: promauto.With(reg).NewCounter(prometheus.CounterOpts{
|
||||
Name: "bridge_propose_chunk_blocks_superpose_not_enough_total",
|
||||
Help: "Total number of chunk block superpose not enough",
|
||||
chunkBlocksProposeNotEnoughTotal: promauto.With(reg).NewCounter(prometheus.CounterOpts{
|
||||
Name: "bridge_propose_chunk_blocks_propose_not_enough_total",
|
||||
Help: "Total number of chunk block propose not enough",
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -179,7 +186,7 @@ func (p *ChunkProposer) updateChunkInfoInDB(chunk *types.Chunk) error {
|
||||
}
|
||||
|
||||
func (p *ChunkProposer) proposeChunk() (*types.Chunk, error) {
|
||||
blocks, err := p.l2BlockOrm.GetUnchunkedBlocks(p.ctx)
|
||||
blocks, err := p.l2BlockOrm.GetUnchunkedBlocks(p.ctx, maxNumBlockPerChunk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -188,97 +195,98 @@ func (p *ChunkProposer) proposeChunk() (*types.Chunk, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
chunk := &types.Chunk{Blocks: blocks[:1]}
|
||||
firstBlock := chunk.Blocks[0]
|
||||
totalTxGasUsed := firstBlock.Header.GasUsed
|
||||
totalL2TxNum := firstBlock.L2TxsNum()
|
||||
totalL1CommitCalldataSize := firstBlock.EstimateL1CommitCalldataSize()
|
||||
var chunk types.Chunk
|
||||
var totalTxGasUsed uint64
|
||||
var totalL2TxNum uint64
|
||||
var totalL1CommitCalldataSize uint64
|
||||
var totalL1CommitGas uint64
|
||||
crc := chunkRowConsumption{}
|
||||
totalL1CommitGas := chunk.EstimateL1CommitGas()
|
||||
|
||||
if err := crc.add(firstBlock.RowConsumption); err != nil {
|
||||
return nil, fmt.Errorf("chunk-proposer failed to update chunk row consumption: %v", err)
|
||||
}
|
||||
for i, block := range blocks {
|
||||
// metric values
|
||||
lastTotalL2TxNum := totalL2TxNum
|
||||
lastTotalL1CommitGas := totalL1CommitGas
|
||||
lastCrcMax := crc.max()
|
||||
lastTotalL1CommitCalldataSize := totalL1CommitCalldataSize
|
||||
lastTotalTxGasUsed := totalTxGasUsed
|
||||
|
||||
p.chunkL2TxNum.Set(float64(totalL2TxNum))
|
||||
// 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,
|
||||
)
|
||||
}
|
||||
|
||||
p.chunkEstimateL1CommitGas.Set(float64(totalL1CommitGas))
|
||||
if p.gasCostIncreaseMultiplier*float64(totalL1CommitGas) > float64(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,
|
||||
)
|
||||
}
|
||||
|
||||
p.totalL1CommitCalldataSize.Set(float64(totalL1CommitCalldataSize))
|
||||
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,
|
||||
)
|
||||
}
|
||||
|
||||
p.totalTxGasUsed.Set(float64(totalTxGasUsed))
|
||||
// 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,
|
||||
)
|
||||
}
|
||||
|
||||
max := crc.max()
|
||||
p.maxTxConsumption.Set(float64(max))
|
||||
if max > p.maxRowConsumptionPerChunk {
|
||||
return nil, fmt.Errorf(
|
||||
"the first block exceeds row consumption limit; block number: %v, row consumption: %v, max: %v, limit: %v",
|
||||
firstBlock.Header.Number,
|
||||
crc,
|
||||
max,
|
||||
p.maxRowConsumptionPerChunk,
|
||||
)
|
||||
}
|
||||
|
||||
for _, block := range blocks[1:] {
|
||||
chunk.Blocks = append(chunk.Blocks, block)
|
||||
totalTxGasUsed += block.Header.GasUsed
|
||||
totalL2TxNum += block.L2TxsNum()
|
||||
totalL1CommitCalldataSize += block.EstimateL1CommitCalldataSize()
|
||||
totalL1CommitGas = chunk.EstimateL1CommitGas()
|
||||
|
||||
totalOverEstimateL1CommitGas := uint64(p.gasCostIncreaseMultiplier * float64(totalL1CommitGas))
|
||||
if err := crc.add(block.RowConsumption); err != nil {
|
||||
return nil, fmt.Errorf("chunk-proposer failed to update chunk row consumption: %v", err)
|
||||
}
|
||||
crcMax := crc.max()
|
||||
|
||||
if totalTxGasUsed > p.maxTxGasPerChunk ||
|
||||
totalL2TxNum > p.maxL2TxNumPerChunk ||
|
||||
if totalL2TxNum > p.maxL2TxNumPerChunk ||
|
||||
totalL1CommitCalldataSize > p.maxL1CommitCalldataSizePerChunk ||
|
||||
p.gasCostIncreaseMultiplier*float64(totalL1CommitGas) > float64(p.maxL1CommitGasPerChunk) ||
|
||||
crc.max() > p.maxRowConsumptionPerChunk {
|
||||
chunk.Blocks = chunk.Blocks[:len(chunk.Blocks)-1] // remove the last block from chunk
|
||||
break
|
||||
totalOverEstimateL1CommitGas > p.maxL1CommitGasPerChunk ||
|
||||
crcMax > p.maxRowConsumptionPerChunk {
|
||||
// Check if the first block breaks hard limits.
|
||||
// If so, it indicates there are bugs in sequencer, manual fix is needed.
|
||||
if i == 0 {
|
||||
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",
|
||||
block.Header.Number,
|
||||
totalL2TxNum,
|
||||
p.maxL2TxNumPerChunk,
|
||||
)
|
||||
}
|
||||
|
||||
if totalOverEstimateL1CommitGas > 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",
|
||||
block.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",
|
||||
block.Header.Number,
|
||||
totalL1CommitCalldataSize,
|
||||
p.maxL1CommitCalldataSizePerChunk,
|
||||
)
|
||||
}
|
||||
|
||||
if crcMax > p.maxRowConsumptionPerChunk {
|
||||
return nil, fmt.Errorf(
|
||||
"the first block exceeds row consumption limit; block number: %v, row consumption: %v, max: %v, limit: %v",
|
||||
block.Header.Number,
|
||||
crc,
|
||||
crcMax,
|
||||
p.maxRowConsumptionPerChunk,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
log.Debug("breaking limit condition in chunking",
|
||||
"totalL2TxNum", totalL2TxNum,
|
||||
"maxL2TxNumPerChunk", p.maxL2TxNumPerChunk,
|
||||
"currentL1CommitCalldataSize", totalL1CommitCalldataSize,
|
||||
"maxL1CommitCalldataSizePerChunk", p.maxL1CommitCalldataSizePerChunk,
|
||||
"currentOverEstimateL1CommitGas", totalOverEstimateL1CommitGas,
|
||||
"maxL1CommitGasPerChunk", p.maxL1CommitGasPerChunk,
|
||||
"chunkRowConsumptionMax", crcMax,
|
||||
"chunkRowConsumption", crc,
|
||||
"p.maxRowConsumptionPerChunk", p.maxRowConsumptionPerChunk)
|
||||
|
||||
p.chunkL2TxNum.Set(float64(lastTotalL2TxNum))
|
||||
p.chunkEstimateL1CommitGas.Set(float64(lastTotalL1CommitGas))
|
||||
p.totalL1CommitCalldataSize.Set(float64(lastTotalL1CommitCalldataSize))
|
||||
p.maxTxConsumption.Set(float64(lastCrcMax))
|
||||
p.totalTxGasUsed.Set(float64(lastTotalTxGasUsed))
|
||||
p.chunkBlocksNum.Set(float64(len(chunk.Blocks)))
|
||||
return &chunk, nil
|
||||
}
|
||||
chunk.Blocks = append(chunk.Blocks, block)
|
||||
}
|
||||
|
||||
p.chunkBlocksNum.Set(float64(len(chunk.Blocks)))
|
||||
|
||||
var hasBlockTimeout bool
|
||||
currentTimeSec := uint64(time.Now().Unix())
|
||||
if blocks[0].Header.Time+p.chunkTimeoutSec < currentTimeSec {
|
||||
log.Warn("first block timeout",
|
||||
@@ -286,17 +294,17 @@ func (p *ChunkProposer) proposeChunk() (*types.Chunk, error) {
|
||||
"block timestamp", blocks[0].Header.Time,
|
||||
"block outdated time threshold", currentTimeSec,
|
||||
)
|
||||
p.chunkBlockTimeoutReached.Inc()
|
||||
hasBlockTimeout = true
|
||||
p.chunkFirstBlockTimeoutReached.Inc()
|
||||
p.chunkL2TxNum.Set(float64(totalL2TxNum))
|
||||
p.chunkEstimateL1CommitGas.Set(float64(totalL1CommitGas))
|
||||
p.totalL1CommitCalldataSize.Set(float64(totalL1CommitCalldataSize))
|
||||
p.maxTxConsumption.Set(float64(crc.max()))
|
||||
p.totalTxGasUsed.Set(float64(totalTxGasUsed))
|
||||
p.chunkBlocksNum.Set(float64(len(chunk.Blocks)))
|
||||
return &chunk, nil
|
||||
}
|
||||
|
||||
if !hasBlockTimeout && totalL1CommitCalldataSize < p.minL1CommitCalldataSizePerChunk {
|
||||
log.Warn("The calldata size of the chunk is less than the minimum limit",
|
||||
"totalL1CommitCalldataSize", totalL1CommitCalldataSize,
|
||||
"minL1CommitCalldataSizePerChunk", p.minL1CommitCalldataSizePerChunk,
|
||||
)
|
||||
p.chunkBlocksSuperposeNotEnoughTotal.Inc()
|
||||
return nil, nil
|
||||
}
|
||||
return chunk, nil
|
||||
log.Debug("pending blocks do not reach one of the constraints or contain a timeout block")
|
||||
p.chunkBlocksProposeNotEnoughTotal.Inc()
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -23,11 +23,9 @@ func testChunkProposer(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
cp := NewChunkProposer(context.Background(), &config.ChunkProposerConfig{
|
||||
MaxTxGasPerChunk: 1000000000,
|
||||
MaxL2TxNumPerChunk: 10000,
|
||||
MaxL1CommitGasPerChunk: 50000000000,
|
||||
MaxL1CommitCalldataSizePerChunk: 1000000,
|
||||
MinL1CommitCalldataSizePerChunk: 0,
|
||||
MaxRowConsumptionPerChunk: 1048319,
|
||||
ChunkTimeoutSec: 300,
|
||||
}, db, nil)
|
||||
@@ -40,7 +38,7 @@ func testChunkProposer(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
chunkOrm := orm.NewChunk(db)
|
||||
chunks, err := chunkOrm.GetUnbatchedChunks(context.Background())
|
||||
chunks, err := chunkOrm.GetUnbatchedChunks(context.Background(), 0)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, chunks, 1)
|
||||
assert.Equal(t, expectedHash.Hex(), chunks[0].Hash)
|
||||
@@ -55,18 +53,16 @@ func testChunkProposerRowConsumption(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
cp := NewChunkProposer(context.Background(), &config.ChunkProposerConfig{
|
||||
MaxTxGasPerChunk: 1000000000,
|
||||
MaxL2TxNumPerChunk: 10000,
|
||||
MaxL1CommitGasPerChunk: 50000000000,
|
||||
MaxL1CommitCalldataSizePerChunk: 1000000,
|
||||
MinL1CommitCalldataSizePerChunk: 0,
|
||||
MaxRowConsumptionPerChunk: 0, // !
|
||||
ChunkTimeoutSec: 300,
|
||||
}, db, nil)
|
||||
cp.TryProposeChunk()
|
||||
|
||||
chunkOrm := orm.NewChunk(db)
|
||||
chunks, err := chunkOrm.GetUnbatchedChunks(context.Background())
|
||||
chunks, err := chunkOrm.GetUnbatchedChunks(context.Background(), 0)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, chunks, 0)
|
||||
}
|
||||
|
||||
@@ -311,7 +311,7 @@ func (o *Batch) UpdateProvingStatus(ctx context.Context, hash string, status typ
|
||||
updateFields["prover_assigned_at"] = time.Now()
|
||||
case types.ProvingTaskUnassigned:
|
||||
updateFields["prover_assigned_at"] = nil
|
||||
case types.ProvingTaskProved, types.ProvingTaskVerified:
|
||||
case types.ProvingTaskVerified:
|
||||
updateFields["proved_at"] = time.Now()
|
||||
}
|
||||
|
||||
|
||||
@@ -88,12 +88,16 @@ func (o *Chunk) GetChunksInRange(ctx context.Context, startIndex uint64, endInde
|
||||
}
|
||||
|
||||
// GetUnbatchedChunks retrieves unbatched chunks from the database.
|
||||
func (o *Chunk) GetUnbatchedChunks(ctx context.Context) ([]*Chunk, error) {
|
||||
func (o *Chunk) GetUnbatchedChunks(ctx context.Context, limit int) ([]*Chunk, error) {
|
||||
db := o.db.WithContext(ctx)
|
||||
db = db.Model(&Chunk{})
|
||||
db = db.Where("batch_hash IS NULL")
|
||||
db = db.Order("index asc")
|
||||
|
||||
if limit > 0 {
|
||||
db = db.Limit(limit)
|
||||
}
|
||||
|
||||
var chunks []*Chunk
|
||||
if err := db.Find(&chunks).Error; err != nil {
|
||||
return nil, fmt.Errorf("Chunk.GetUnbatchedChunks error: %w", err)
|
||||
@@ -201,7 +205,7 @@ func (o *Chunk) UpdateProvingStatus(ctx context.Context, hash string, status typ
|
||||
updateFields["prover_assigned_at"] = time.Now()
|
||||
case types.ProvingTaskUnassigned:
|
||||
updateFields["prover_assigned_at"] = nil
|
||||
case types.ProvingTaskProved, types.ProvingTaskVerified:
|
||||
case types.ProvingTaskVerified:
|
||||
updateFields["proved_at"] = time.Now()
|
||||
}
|
||||
|
||||
|
||||
@@ -66,13 +66,17 @@ func (o *L2Block) GetL2BlocksLatestHeight(ctx context.Context) (uint64, error) {
|
||||
|
||||
// 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) {
|
||||
func (o *L2Block) GetUnchunkedBlocks(ctx context.Context, limit int) ([]*types.WrappedBlock, error) {
|
||||
db := o.db.WithContext(ctx)
|
||||
db = db.Model(&L2Block{})
|
||||
db = db.Select("header, transactions, withdraw_root, row_consumption")
|
||||
db = db.Where("chunk_hash IS NULL")
|
||||
db = db.Order("number ASC")
|
||||
|
||||
if limit > 0 {
|
||||
db = db.Limit(limit)
|
||||
}
|
||||
|
||||
var l2Blocks []L2Block
|
||||
if err := db.Find(&l2Blocks).Error; err != nil {
|
||||
return nil, fmt.Errorf("L2Block.GetUnchunkedBlocks error: %w", err)
|
||||
|
||||
@@ -101,7 +101,7 @@ func TestL2BlockOrm(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, uint64(3), height)
|
||||
|
||||
blocks, err := l2BlockOrm.GetUnchunkedBlocks(context.Background())
|
||||
blocks, err := l2BlockOrm.GetUnchunkedBlocks(context.Background(), 0)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, blocks, 2)
|
||||
assert.Equal(t, wrappedBlock1, blocks[0])
|
||||
@@ -116,7 +116,7 @@ func TestL2BlockOrm(t *testing.T) {
|
||||
err = l2BlockOrm.UpdateChunkHashInRange(context.Background(), 2, 2, "test hash")
|
||||
assert.NoError(t, err)
|
||||
|
||||
blocks, err = l2BlockOrm.GetUnchunkedBlocks(context.Background())
|
||||
blocks, err = l2BlockOrm.GetUnchunkedBlocks(context.Background(), 0)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, blocks, 1)
|
||||
assert.Equal(t, wrappedBlock2, blocks[0])
|
||||
@@ -135,7 +135,7 @@ func TestChunkOrm(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, dbChunk2.Hash, chunkHash2.Hex())
|
||||
|
||||
chunks, err := chunkOrm.GetUnbatchedChunks(context.Background())
|
||||
chunks, err := chunkOrm.GetUnbatchedChunks(context.Background(), 0)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, chunks, 2)
|
||||
assert.Equal(t, chunkHash1.Hex(), chunks[0].Hash)
|
||||
@@ -156,7 +156,7 @@ func TestChunkOrm(t *testing.T) {
|
||||
|
||||
err = chunkOrm.UpdateBatchHashInRange(context.Background(), 0, 0, "test hash")
|
||||
assert.NoError(t, err)
|
||||
chunks, err = chunkOrm.GetUnbatchedChunks(context.Background())
|
||||
chunks, err = chunkOrm.GetUnbatchedChunks(context.Background(), 0)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, chunks, 1)
|
||||
}
|
||||
|
||||
@@ -58,18 +58,16 @@ func testCommitBatchAndFinalizeBatch(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
cp := watcher.NewChunkProposer(context.Background(), &config.ChunkProposerConfig{
|
||||
MaxTxGasPerChunk: 1000000000,
|
||||
MaxL2TxNumPerChunk: 10000,
|
||||
MaxL1CommitGasPerChunk: 50000000000,
|
||||
MaxL1CommitCalldataSizePerChunk: 1000000,
|
||||
MinL1CommitCalldataSizePerChunk: 0,
|
||||
MaxRowConsumptionPerChunk: 1048319,
|
||||
ChunkTimeoutSec: 300,
|
||||
}, db, nil)
|
||||
cp.TryProposeChunk()
|
||||
|
||||
chunkOrm := orm.NewChunk(db)
|
||||
chunks, err := chunkOrm.GetUnbatchedChunks(context.Background())
|
||||
chunks, err := chunkOrm.GetUnbatchedChunks(context.Background(), 0)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, chunks, 1)
|
||||
|
||||
@@ -77,7 +75,6 @@ func testCommitBatchAndFinalizeBatch(t *testing.T) {
|
||||
MaxChunkNumPerBatch: 10,
|
||||
MaxL1CommitGasPerBatch: 50000000000,
|
||||
MaxL1CommitCalldataSizePerBatch: 1000000,
|
||||
MinChunkNumPerBatch: 1,
|
||||
BatchTimeoutSec: 300,
|
||||
}, db, nil)
|
||||
bp.TryProposeBatch()
|
||||
|
||||
83
common/libzkp/impl/Cargo.lock
generated
83
common/libzkp/impl/Cargo.lock
generated
@@ -32,7 +32,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "aggregator"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.6.2#750169c0e7282c267c8216c800ed7c1b7e021b16"
|
||||
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.7.5#aa9a9aff698a5b253d1f3c29ea3d3006364777bf"
|
||||
dependencies = [
|
||||
"ark-std",
|
||||
"env_logger 0.10.0",
|
||||
@@ -433,7 +433,7 @@ checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
|
||||
[[package]]
|
||||
name = "bus-mapping"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.6.2#750169c0e7282c267c8216c800ed7c1b7e021b16"
|
||||
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.7.5#aa9a9aff698a5b253d1f3c29ea3d3006364777bf"
|
||||
dependencies = [
|
||||
"eth-types",
|
||||
"ethers-core",
|
||||
@@ -1049,7 +1049,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "eth-types"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.6.2#750169c0e7282c267c8216c800ed7c1b7e021b16"
|
||||
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.7.5#aa9a9aff698a5b253d1f3c29ea3d3006364777bf"
|
||||
dependencies = [
|
||||
"ethers-core",
|
||||
"ethers-signers",
|
||||
@@ -1226,7 +1226,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "external-tracer"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.6.2#750169c0e7282c267c8216c800ed7c1b7e021b16"
|
||||
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.7.5#aa9a9aff698a5b253d1f3c29ea3d3006364777bf"
|
||||
dependencies = [
|
||||
"eth-types",
|
||||
"geth-utils",
|
||||
@@ -1439,7 +1439,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gadgets"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.6.2#750169c0e7282c267c8216c800ed7c1b7e021b16"
|
||||
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.7.5#aa9a9aff698a5b253d1f3c29ea3d3006364777bf"
|
||||
dependencies = [
|
||||
"digest 0.7.6",
|
||||
"eth-types",
|
||||
@@ -1479,7 +1479,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "geth-utils"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.6.2#750169c0e7282c267c8216c800ed7c1b7e021b16"
|
||||
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.7.5#aa9a9aff698a5b253d1f3c29ea3d3006364777bf"
|
||||
dependencies = [
|
||||
"env_logger 0.9.3",
|
||||
"gobuild 0.1.0-alpha.2 (git+https://github.com/scroll-tech/gobuild.git)",
|
||||
@@ -1595,21 +1595,6 @@ dependencies = [
|
||||
"rustc-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "halo2-base"
|
||||
version = "0.2.2"
|
||||
source = "git+https://github.com/scroll-tech/halo2-lib?branch=develop#2c225864227e74b207d9f4b9e08c4d5f1afc69a1"
|
||||
dependencies = [
|
||||
"ff",
|
||||
"halo2_proofs",
|
||||
"itertools",
|
||||
"num-bigint",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"rand_chacha",
|
||||
"rustc-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "halo2-ecc"
|
||||
version = "0.2.2"
|
||||
@@ -1617,26 +1602,7 @@ source = "git+https://github.com/scroll-tech/halo2-lib?tag=v0.1.0#2c225864227e74
|
||||
dependencies = [
|
||||
"ff",
|
||||
"group",
|
||||
"halo2-base 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?tag=v0.1.0)",
|
||||
"itertools",
|
||||
"num-bigint",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"rand",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "halo2-ecc"
|
||||
version = "0.2.2"
|
||||
source = "git+https://github.com/scroll-tech/halo2-lib?branch=develop#2c225864227e74b207d9f4b9e08c4d5f1afc69a1"
|
||||
dependencies = [
|
||||
"ff",
|
||||
"group",
|
||||
"halo2-base 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=develop)",
|
||||
"halo2-base",
|
||||
"itertools",
|
||||
"num-bigint",
|
||||
"num-integer",
|
||||
@@ -1667,7 +1633,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "halo2-mpt-circuits"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/scroll-tech/mpt-circuit.git?branch=v0.5#2163a9c436ed85363c954ecf7e6e1044a1b991dc"
|
||||
source = "git+https://github.com/scroll-tech/mpt-circuit.git?tag=v0.5.1#2163a9c436ed85363c954ecf7e6e1044a1b991dc"
|
||||
dependencies = [
|
||||
"ethers-core",
|
||||
"halo2_proofs",
|
||||
@@ -1689,7 +1655,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "halo2_proofs"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/scroll-tech/halo2.git?branch=develop#b612b1e2a9fa2ccd150a6cb99e67592c8d62cd99"
|
||||
source = "git+https://github.com/scroll-tech/halo2.git?branch=develop#19de67c07a9b9b567580466763f93ebfbc3bb799"
|
||||
dependencies = [
|
||||
"ark-std",
|
||||
"blake2b_simd",
|
||||
@@ -2111,7 +2077,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "keccak256"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.6.2#750169c0e7282c267c8216c800ed7c1b7e021b16"
|
||||
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.7.5#aa9a9aff698a5b253d1f3c29ea3d3006364777bf"
|
||||
dependencies = [
|
||||
"env_logger 0.9.3",
|
||||
"eth-types",
|
||||
@@ -2298,7 +2264,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "mock"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.6.2#750169c0e7282c267c8216c800ed7c1b7e021b16"
|
||||
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.7.5#aa9a9aff698a5b253d1f3c29ea3d3006364777bf"
|
||||
dependencies = [
|
||||
"eth-types",
|
||||
"ethers-core",
|
||||
@@ -2313,7 +2279,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "mpt-zktrie"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.6.2#750169c0e7282c267c8216c800ed7c1b7e021b16"
|
||||
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.7.5#aa9a9aff698a5b253d1f3c29ea3d3006364777bf"
|
||||
dependencies = [
|
||||
"bus-mapping",
|
||||
"eth-types",
|
||||
@@ -2788,8 +2754,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "prover"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/scroll-tech/scroll-prover?tag=v0.6.2#8c439b1dd62c429223221484fb8a5470242d1cbc"
|
||||
version = "0.7.5"
|
||||
source = "git+https://github.com/scroll-tech/scroll-prover?tag=v0.7.5#9699d40940aed2f14d8e1958167d714bca2c9984"
|
||||
dependencies = [
|
||||
"aggregator",
|
||||
"anyhow",
|
||||
@@ -3658,12 +3624,12 @@ checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
|
||||
[[package]]
|
||||
name = "snark-verifier"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/scroll-tech//snark-verifier?tag=v0.1.1#11a09d4a37c31c659b29e2dac0ceb544a776ad7b"
|
||||
source = "git+https://github.com/scroll-tech/snark-verifier?tag=v0.1.2#4466059ce9a6dfaf26455e4ffb61d72af775cf52"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"ethereum-types 0.14.1",
|
||||
"halo2-base 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?tag=v0.1.0)",
|
||||
"halo2-ecc 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?tag=v0.1.0)",
|
||||
"halo2-base",
|
||||
"halo2-ecc",
|
||||
"hex",
|
||||
"itertools",
|
||||
"lazy_static",
|
||||
@@ -3682,12 +3648,12 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "snark-verifier-sdk"
|
||||
version = "0.0.1"
|
||||
source = "git+https://github.com/scroll-tech//snark-verifier?tag=v0.1.1#11a09d4a37c31c659b29e2dac0ceb544a776ad7b"
|
||||
source = "git+https://github.com/scroll-tech/snark-verifier?tag=v0.1.2#4466059ce9a6dfaf26455e4ffb61d72af775cf52"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"env_logger 0.10.0",
|
||||
"ethereum-types 0.14.1",
|
||||
"halo2-base 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?tag=v0.1.0)",
|
||||
"halo2-base",
|
||||
"hex",
|
||||
"itertools",
|
||||
"lazy_static",
|
||||
@@ -4073,8 +4039,8 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
||||
|
||||
[[package]]
|
||||
name = "types"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/scroll-tech/scroll-prover?tag=v0.6.2#8c439b1dd62c429223221484fb8a5470242d1cbc"
|
||||
version = "0.7.5"
|
||||
source = "git+https://github.com/scroll-tech/scroll-prover?tag=v0.7.5#9699d40940aed2f14d8e1958167d714bca2c9984"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"blake2",
|
||||
@@ -4525,7 +4491,7 @@ checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9"
|
||||
[[package]]
|
||||
name = "zkevm-circuits"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.6.2#750169c0e7282c267c8216c800ed7c1b7e021b16"
|
||||
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.7.5#aa9a9aff698a5b253d1f3c29ea3d3006364777bf"
|
||||
dependencies = [
|
||||
"array-init",
|
||||
"bus-mapping",
|
||||
@@ -4535,8 +4501,8 @@ dependencies = [
|
||||
"ethers-core",
|
||||
"ethers-signers",
|
||||
"gadgets",
|
||||
"halo2-base 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=develop)",
|
||||
"halo2-ecc 0.2.2 (git+https://github.com/scroll-tech/halo2-lib?branch=develop)",
|
||||
"halo2-base",
|
||||
"halo2-ecc",
|
||||
"halo2_proofs",
|
||||
"hex",
|
||||
"itertools",
|
||||
@@ -4570,6 +4536,7 @@ dependencies = [
|
||||
name = "zkp"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"env_logger 0.9.3",
|
||||
"halo2_proofs",
|
||||
"libc",
|
||||
|
||||
@@ -19,23 +19,19 @@ maingate = { git = "https://github.com/scroll-tech/halo2wrong", branch = "halo2-
|
||||
[patch."https://github.com/privacy-scaling-explorations/halo2curves.git"]
|
||||
halo2curves = { git = "https://github.com/scroll-tech/halo2curves.git", branch = "0.3.1-derive-serde" }
|
||||
|
||||
[patch."https://github.com/scroll-tech/snark-verifier"]
|
||||
snark-verifier = { git = "https://github.com/scroll-tech//snark-verifier", tag = "v0.1.1" }
|
||||
snark-verifier-sdk = { git = "https://github.com/scroll-tech//snark-verifier", tag = "v0.1.1" }
|
||||
|
||||
[dependencies]
|
||||
prover = { git = "https://github.com/scroll-tech/scroll-prover", tag = "v0.6.2" }
|
||||
types = { git = "https://github.com/scroll-tech/scroll-prover", tag = "v0.6.2" }
|
||||
prover = { git = "https://github.com/scroll-tech/scroll-prover", tag = "v0.7.5" }
|
||||
types = { git = "https://github.com/scroll-tech/scroll-prover", tag = "v0.7.5" }
|
||||
halo2_proofs = { git = "https://github.com/scroll-tech/halo2.git", branch = "develop" }
|
||||
|
||||
log = "0.4"
|
||||
base64 = "0.13.0"
|
||||
env_logger = "0.9.0"
|
||||
libc = "0.2"
|
||||
log = "0.4"
|
||||
once_cell = "1.8.0"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0.66"
|
||||
libc = "0.2"
|
||||
once_cell = "1.8.0"
|
||||
|
||||
|
||||
[profile.test]
|
||||
opt-level = 3
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use crate::utils::{c_char_to_str, c_char_to_vec, vec_to_c_char, OUTPUT_DIR};
|
||||
use crate::utils::{c_char_to_str, c_char_to_vec, string_to_c_char, vec_to_c_char, OUTPUT_DIR};
|
||||
use libc::c_char;
|
||||
use prover::{
|
||||
aggregator::{Prover, Verifier},
|
||||
utils::{chunk_trace_to_witness_block, init_env_and_log},
|
||||
BatchProof, ChunkHash, ChunkProof,
|
||||
};
|
||||
use std::{cell::OnceCell, panic, ptr::null};
|
||||
use std::{cell::OnceCell, env, panic, ptr::null};
|
||||
use types::eth::BlockTrace;
|
||||
|
||||
static mut PROVER: OnceCell<Prover> = OnceCell::new();
|
||||
@@ -13,11 +13,15 @@ static mut VERIFIER: OnceCell<Verifier> = OnceCell::new();
|
||||
|
||||
/// # Safety
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn init_batch_prover(params_dir: *const c_char) {
|
||||
pub unsafe extern "C" fn init_batch_prover(params_dir: *const c_char, assets_dir: *const c_char) {
|
||||
init_env_and_log("ffi_batch_prove");
|
||||
|
||||
let params_dir = c_char_to_str(params_dir);
|
||||
let prover = Prover::from_params_dir(params_dir);
|
||||
let assets_dir = c_char_to_str(assets_dir);
|
||||
|
||||
// TODO: add a settings in scroll-prover.
|
||||
env::set_var("SCROLL_PROVER_ASSETS_DIR", assets_dir);
|
||||
let prover = Prover::from_dirs(params_dir, assets_dir);
|
||||
|
||||
PROVER.set(prover).unwrap();
|
||||
}
|
||||
@@ -30,11 +34,35 @@ pub unsafe extern "C" fn init_batch_verifier(params_dir: *const c_char, assets_d
|
||||
let params_dir = c_char_to_str(params_dir);
|
||||
let assets_dir = c_char_to_str(assets_dir);
|
||||
|
||||
// TODO: add a settings in scroll-prover.
|
||||
env::set_var("SCROLL_PROVER_ASSETS_DIR", assets_dir);
|
||||
let verifier = Verifier::from_dirs(params_dir, assets_dir);
|
||||
|
||||
VERIFIER.set(verifier).unwrap();
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn get_batch_vk() -> *const c_char {
|
||||
let vk_result = panic::catch_unwind(|| PROVER.get_mut().unwrap().get_vk());
|
||||
|
||||
vk_result
|
||||
.ok()
|
||||
.flatten()
|
||||
.map_or(null(), |vk| string_to_c_char(base64::encode(vk)))
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn check_chunk_proofs(chunk_proofs: *const c_char) -> c_char {
|
||||
let chunk_proofs = c_char_to_vec(chunk_proofs);
|
||||
let chunk_proofs = serde_json::from_slice::<Vec<ChunkProof>>(&chunk_proofs).unwrap();
|
||||
assert!(!chunk_proofs.is_empty());
|
||||
|
||||
let valid = panic::catch_unwind(|| PROVER.get().unwrap().check_chunk_proofs(&chunk_proofs));
|
||||
valid.unwrap_or(false) as c_char
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn gen_batch_proof(
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use crate::utils::{c_char_to_str, c_char_to_vec, vec_to_c_char, OUTPUT_DIR};
|
||||
use crate::utils::{c_char_to_str, c_char_to_vec, string_to_c_char, vec_to_c_char, OUTPUT_DIR};
|
||||
use libc::c_char;
|
||||
use prover::{
|
||||
utils::init_env_and_log,
|
||||
zkevm::{Prover, Verifier},
|
||||
ChunkProof,
|
||||
};
|
||||
use std::{cell::OnceCell, panic, ptr::null};
|
||||
use std::{cell::OnceCell, env, panic, ptr::null};
|
||||
use types::eth::BlockTrace;
|
||||
|
||||
static mut PROVER: OnceCell<Prover> = OnceCell::new();
|
||||
@@ -13,10 +13,14 @@ static mut VERIFIER: OnceCell<Verifier> = OnceCell::new();
|
||||
|
||||
/// # Safety
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn init_chunk_prover(params_dir: *const c_char) {
|
||||
pub unsafe extern "C" fn init_chunk_prover(params_dir: *const c_char, assets_dir: *const c_char) {
|
||||
init_env_and_log("ffi_chunk_prove");
|
||||
|
||||
let params_dir = c_char_to_str(params_dir);
|
||||
let assets_dir = c_char_to_str(assets_dir);
|
||||
|
||||
// TODO: add a settings in scroll-prover.
|
||||
env::set_var("SCROLL_PROVER_ASSETS_DIR", assets_dir);
|
||||
let prover = Prover::from_params_dir(params_dir);
|
||||
|
||||
PROVER.set(prover).unwrap();
|
||||
@@ -30,11 +34,24 @@ pub unsafe extern "C" fn init_chunk_verifier(params_dir: *const c_char, assets_d
|
||||
let params_dir = c_char_to_str(params_dir);
|
||||
let assets_dir = c_char_to_str(assets_dir);
|
||||
|
||||
// TODO: add a settings in scroll-prover.
|
||||
env::set_var("SCROLL_PROVER_ASSETS_DIR", assets_dir);
|
||||
let verifier = Verifier::from_dirs(params_dir, assets_dir);
|
||||
|
||||
VERIFIER.set(verifier).unwrap();
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn get_chunk_vk() -> *const c_char {
|
||||
let vk_result = panic::catch_unwind(|| PROVER.get_mut().unwrap().get_vk());
|
||||
|
||||
vk_result
|
||||
.ok()
|
||||
.flatten()
|
||||
.map_or(null(), |vk| string_to_c_char(base64::encode(vk)))
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn gen_chunk_proof(block_traces: *const c_char) -> *const c_char {
|
||||
|
||||
@@ -19,6 +19,10 @@ pub(crate) fn c_char_to_vec(c: *const c_char) -> Vec<u8> {
|
||||
cstr.to_bytes().to_vec()
|
||||
}
|
||||
|
||||
pub(crate) fn string_to_c_char(string: String) -> *const c_char {
|
||||
CString::new(string).unwrap().into_raw()
|
||||
}
|
||||
|
||||
pub(crate) fn vec_to_c_char(bytes: Vec<u8>) -> *const c_char {
|
||||
CString::new(bytes).unwrap().into_raw()
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
void init_batch_prover(char* params_dir);
|
||||
void init_batch_prover(char* params_dir, char* assets_dir);
|
||||
void init_batch_verifier(char* params_dir, char* assets_dir);
|
||||
char* get_batch_vk();
|
||||
char check_chunk_proofs(char* chunk_proofs);
|
||||
char* gen_batch_proof(char* chunk_hashes, char* chunk_proofs);
|
||||
char verify_batch_proof(char* proof);
|
||||
|
||||
void init_chunk_prover(char* params_dir);
|
||||
void init_chunk_prover(char* params_dir, char* assets_dir);
|
||||
void init_chunk_verifier(char* params_dir, char* assets_dir);
|
||||
char* get_chunk_vk();
|
||||
char* gen_chunk_proof(char* block_traces);
|
||||
char verify_chunk_proof(char* proof);
|
||||
|
||||
|
||||
@@ -3,20 +3,31 @@ package types
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
||||
"github.com/scroll-tech/go-ethereum/core/types"
|
||||
"github.com/scroll-tech/go-ethereum/log"
|
||||
)
|
||||
|
||||
// CalldataNonZeroByteGas is the gas consumption per non zero byte in calldata.
|
||||
const CalldataNonZeroByteGas = 16
|
||||
|
||||
// GetKeccak256Gas calculates keccak256 hash gas.
|
||||
func GetKeccak256Gas(size uint64) uint64 {
|
||||
return 30 + 6*((size+31)/32) // 30 + 6 * ceil(size / 32)
|
||||
}
|
||||
|
||||
// WrappedBlock contains the block's Header, Transactions and WithdrawTrieRoot hash.
|
||||
type WrappedBlock struct {
|
||||
Header *types.Header `json:"header"`
|
||||
// Transactions is only used for recover types.Transactions, the from of types.TransactionData field is missing.
|
||||
Transactions []*types.TransactionData `json:"transactions"`
|
||||
WithdrawRoot common.Hash `json:"withdraw_trie_root,omitempty"`
|
||||
RowConsumption *types.RowConsumption `json:"row_consumption"`
|
||||
Transactions []*types.TransactionData `json:"transactions"`
|
||||
WithdrawRoot common.Hash `json:"withdraw_trie_root,omitempty"`
|
||||
RowConsumption *types.RowConsumption `json:"row_consumption"`
|
||||
txPayloadLengthCache map[string]uint64
|
||||
}
|
||||
|
||||
// NumL1Messages returns the number of L1 messages in this block.
|
||||
@@ -87,17 +98,14 @@ func (w *WrappedBlock) EstimateL1CommitCalldataSize() uint64 {
|
||||
if txData.Type == types.L1MessageTxType {
|
||||
continue
|
||||
}
|
||||
size += uint64(len(txData.Data))
|
||||
size += 64 // 60 bytes BlockContext + 4 bytes payload length
|
||||
size += w.getTxPayloadLength(txData)
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
// EstimateL1CommitGas calculates the total L1 commit gas for this block approximately.
|
||||
func (w *WrappedBlock) EstimateL1CommitGas() uint64 {
|
||||
getKeccakGas := func(size uint64) uint64 {
|
||||
return 30 + 6*((size+31)/32) // 30 + 6 * ceil(size / 32)
|
||||
}
|
||||
|
||||
var total uint64
|
||||
var numL1Messages uint64
|
||||
for _, txData := range w.Transactions {
|
||||
@@ -106,23 +114,10 @@ func (w *WrappedBlock) EstimateL1CommitGas() uint64 {
|
||||
continue
|
||||
}
|
||||
|
||||
data, _ := hexutil.Decode(txData.Data)
|
||||
tx := types.NewTx(&types.LegacyTx{
|
||||
Nonce: txData.Nonce,
|
||||
To: txData.To,
|
||||
Value: txData.Value.ToInt(),
|
||||
Gas: txData.Gas,
|
||||
GasPrice: txData.GasPrice.ToInt(),
|
||||
Data: data,
|
||||
V: txData.V.ToInt(),
|
||||
R: txData.R.ToInt(),
|
||||
S: txData.S.ToInt(),
|
||||
})
|
||||
rlpTxData, _ := tx.MarshalBinary()
|
||||
txPayloadLength := uint64(len(rlpTxData))
|
||||
total += 16 * txPayloadLength // an over-estimate: treat each byte as non-zero
|
||||
total += 16 * 4 // size of a uint32 field
|
||||
total += getKeccakGas(txPayloadLength) // l2 tx hash
|
||||
txPayloadLength := w.getTxPayloadLength(txData)
|
||||
total += CalldataNonZeroByteGas * txPayloadLength // an over-estimate: treat each byte as non-zero
|
||||
total += CalldataNonZeroByteGas * 64 // 60 bytes BlockContext + 4 bytes payload length
|
||||
total += GetKeccak256Gas(txPayloadLength) // l2 tx hash
|
||||
}
|
||||
|
||||
// sload
|
||||
@@ -145,3 +140,48 @@ func (w *WrappedBlock) L2TxsNum() uint64 {
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func (w *WrappedBlock) getTxPayloadLength(txData *types.TransactionData) uint64 {
|
||||
if w.txPayloadLengthCache == nil {
|
||||
w.txPayloadLengthCache = make(map[string]uint64)
|
||||
}
|
||||
|
||||
if length, exists := w.txPayloadLengthCache[txData.TxHash]; exists {
|
||||
return length
|
||||
}
|
||||
|
||||
rlpTxData, err := convertTxDataToRLPEncoding(txData)
|
||||
if err != nil {
|
||||
log.Crit("convertTxDataToRLPEncoding failed, which should not happen", "hash", txData.TxHash, "err", err)
|
||||
return 0
|
||||
}
|
||||
txPayloadLength := uint64(len(rlpTxData))
|
||||
w.txPayloadLengthCache[txData.TxHash] = txPayloadLength
|
||||
return txPayloadLength
|
||||
}
|
||||
|
||||
func convertTxDataToRLPEncoding(txData *types.TransactionData) ([]byte, error) {
|
||||
data, err := hexutil.Decode(txData.Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode txData.Data: %s, err: %w", txData.Data, err)
|
||||
}
|
||||
|
||||
tx := types.NewTx(&types.LegacyTx{
|
||||
Nonce: txData.Nonce,
|
||||
To: txData.To,
|
||||
Value: txData.Value.ToInt(),
|
||||
Gas: txData.Gas,
|
||||
GasPrice: txData.GasPrice.ToInt(),
|
||||
Data: data,
|
||||
V: txData.V.ToInt(),
|
||||
R: txData.R.ToInt(),
|
||||
S: txData.S.ToInt(),
|
||||
})
|
||||
|
||||
rlpTxData, err := tx.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal binary of the tx: %+v, err: %w", tx, err)
|
||||
}
|
||||
|
||||
return rlpTxData, nil
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
||||
"github.com/scroll-tech/go-ethereum/core/types"
|
||||
"github.com/scroll-tech/go-ethereum/crypto"
|
||||
)
|
||||
@@ -65,23 +64,7 @@ func (c *Chunk) Encode(totalL1MessagePoppedBefore uint64) ([]byte, error) {
|
||||
if txData.Type == types.L1MessageTxType {
|
||||
continue
|
||||
}
|
||||
data, err := hexutil.Decode(txData.Data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// right now we only support legacy tx
|
||||
tx := types.NewTx(&types.LegacyTx{
|
||||
Nonce: txData.Nonce,
|
||||
To: txData.To,
|
||||
Value: txData.Value.ToInt(),
|
||||
Gas: txData.Gas,
|
||||
GasPrice: txData.GasPrice.ToInt(),
|
||||
Data: data,
|
||||
V: txData.V.ToInt(),
|
||||
R: txData.R.ToInt(),
|
||||
S: txData.S.ToInt(),
|
||||
})
|
||||
rlpTxData, err := tx.MarshalBinary()
|
||||
rlpTxData, err := convertTxDataToRLPEncoding(txData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -146,14 +129,10 @@ func (c *Chunk) EstimateL1CommitGas() uint64 {
|
||||
}
|
||||
|
||||
numBlocks := uint64(len(c.Blocks))
|
||||
totalL1CommitGas += 100 * numBlocks // numBlocks times warm sload
|
||||
totalL1CommitGas += 16 // numBlocks field of chunk encoding in calldata
|
||||
totalL1CommitGas += 16 * 60 * numBlocks // BlockContext in chunk
|
||||
totalL1CommitGas += 100 * numBlocks // numBlocks times warm sload
|
||||
totalL1CommitGas += CalldataNonZeroByteGas // numBlocks field of chunk encoding in calldata
|
||||
totalL1CommitGas += CalldataNonZeroByteGas * numBlocks * 60 // numBlocks of BlockContext in chunk
|
||||
|
||||
getKeccakGas := func(size uint64) uint64 {
|
||||
return 30 + 6*((size+31)/32) // 30 + 6 * ceil(size / 32)
|
||||
}
|
||||
|
||||
totalL1CommitGas += getKeccakGas(58*numBlocks + 32*totalTxNum) // chunk hash
|
||||
totalL1CommitGas += GetKeccak256Gas(58*numBlocks + 32*totalTxNum) // chunk hash
|
||||
return totalL1CommitGas
|
||||
}
|
||||
|
||||
@@ -38,11 +38,15 @@ func TestChunkEncode(t *testing.T) {
|
||||
wrappedBlock := &WrappedBlock{}
|
||||
assert.NoError(t, json.Unmarshal(templateBlockTrace, wrappedBlock))
|
||||
assert.Equal(t, uint64(0), wrappedBlock.NumL1Messages(0))
|
||||
assert.Equal(t, uint64(358), wrappedBlock.EstimateL1CommitCalldataSize())
|
||||
assert.Equal(t, uint64(2), wrappedBlock.L2TxsNum())
|
||||
chunk = &Chunk{
|
||||
Blocks: []*WrappedBlock{
|
||||
wrappedBlock,
|
||||
},
|
||||
}
|
||||
assert.Equal(t, uint64(0), chunk.NumL1Messages(0))
|
||||
assert.Equal(t, uint64(6966), chunk.EstimateL1CommitGas())
|
||||
bytes, err = chunk.Encode(0)
|
||||
hexString := hex.EncodeToString(bytes)
|
||||
assert.NoError(t, err)
|
||||
@@ -56,11 +60,15 @@ func TestChunkEncode(t *testing.T) {
|
||||
wrappedBlock2 := &WrappedBlock{}
|
||||
assert.NoError(t, json.Unmarshal(templateBlockTrace2, wrappedBlock2))
|
||||
assert.Equal(t, uint64(11), wrappedBlock2.NumL1Messages(0)) // 0..=9 skipped, 10 included
|
||||
assert.Equal(t, uint64(96), wrappedBlock2.EstimateL1CommitCalldataSize())
|
||||
assert.Equal(t, uint64(1), wrappedBlock2.L2TxsNum())
|
||||
chunk = &Chunk{
|
||||
Blocks: []*WrappedBlock{
|
||||
wrappedBlock2,
|
||||
},
|
||||
}
|
||||
assert.Equal(t, uint64(11), chunk.NumL1Messages(0))
|
||||
assert.Equal(t, uint64(5002), chunk.EstimateL1CommitGas())
|
||||
bytes, err = chunk.Encode(0)
|
||||
hexString = hex.EncodeToString(bytes)
|
||||
assert.NoError(t, err)
|
||||
@@ -75,6 +83,8 @@ func TestChunkEncode(t *testing.T) {
|
||||
wrappedBlock2,
|
||||
},
|
||||
}
|
||||
assert.Equal(t, uint64(11), chunk.NumL1Messages(0))
|
||||
assert.Equal(t, uint64(9958), chunk.EstimateL1CommitGas())
|
||||
bytes, err = chunk.Encode(0)
|
||||
hexString = hex.EncodeToString(bytes)
|
||||
assert.NoError(t, err)
|
||||
@@ -136,3 +146,81 @@ func TestChunkHash(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "0x2eb7dd63bf8fc29a0f8c10d16c2ae6f9da446907c79d50f5c164d30dc8526b60", hash.Hex())
|
||||
}
|
||||
|
||||
func TestErrorPaths(t *testing.T) {
|
||||
// test 1: Header.Number is not a uint64
|
||||
templateBlockTrace, err := os.ReadFile("../testdata/blockTrace_02.json")
|
||||
assert.NoError(t, err)
|
||||
|
||||
wrappedBlock := &WrappedBlock{}
|
||||
|
||||
assert.NoError(t, json.Unmarshal(templateBlockTrace, wrappedBlock))
|
||||
wrappedBlock.Header.Number = wrappedBlock.Header.Number.Lsh(wrappedBlock.Header.Number, 64)
|
||||
bytes, err := wrappedBlock.Encode(0)
|
||||
assert.Nil(t, bytes)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "block number is not uint64")
|
||||
|
||||
assert.NoError(t, json.Unmarshal(templateBlockTrace, wrappedBlock))
|
||||
for i := 0; i < 65537; i++ {
|
||||
wrappedBlock.Transactions = append(wrappedBlock.Transactions, wrappedBlock.Transactions[0])
|
||||
}
|
||||
|
||||
bytes, err = wrappedBlock.Encode(0)
|
||||
assert.Nil(t, bytes)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "number of transactions exceeds max uint16")
|
||||
|
||||
chunk := &Chunk{
|
||||
Blocks: []*WrappedBlock{
|
||||
wrappedBlock,
|
||||
},
|
||||
}
|
||||
|
||||
bytes, err = chunk.Encode(0)
|
||||
assert.Nil(t, bytes)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "number of transactions exceeds max uint16")
|
||||
|
||||
wrappedBlock.Transactions = wrappedBlock.Transactions[:1]
|
||||
wrappedBlock.Transactions[0].Data = "not-a-hex"
|
||||
bytes, err = chunk.Encode(0)
|
||||
assert.Nil(t, bytes)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "hex string without 0x prefix")
|
||||
|
||||
assert.NoError(t, json.Unmarshal(templateBlockTrace, wrappedBlock))
|
||||
wrappedBlock.Transactions[0].TxHash = "not-a-hex"
|
||||
_, err = chunk.Hash(0)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "invalid byte")
|
||||
|
||||
templateBlockTrace2, err := os.ReadFile("../testdata/blockTrace_04.json")
|
||||
assert.NoError(t, err)
|
||||
|
||||
wrappedBlock2 := &WrappedBlock{}
|
||||
assert.NoError(t, json.Unmarshal(templateBlockTrace2, wrappedBlock2))
|
||||
for i := 0; i < 65535; i++ {
|
||||
tx := &wrappedBlock2.Transactions[i]
|
||||
txCopy := *tx
|
||||
txCopy.Nonce = uint64(i + 1)
|
||||
wrappedBlock2.Transactions = append(wrappedBlock2.Transactions, txCopy)
|
||||
}
|
||||
|
||||
bytes, err = wrappedBlock2.Encode(0)
|
||||
assert.Nil(t, bytes)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "number of L1 messages exceeds max uint16")
|
||||
|
||||
chunk = &Chunk{
|
||||
Blocks: []*WrappedBlock{
|
||||
wrappedBlock2,
|
||||
},
|
||||
}
|
||||
|
||||
bytes, err = chunk.Encode(0)
|
||||
assert.Nil(t, bytes)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "number of L1 messages exceeds max uint16")
|
||||
|
||||
}
|
||||
|
||||
@@ -126,8 +126,8 @@ const (
|
||||
ProvingTaskUnassigned
|
||||
// ProvingTaskAssigned : proving_task is assigned to be proved
|
||||
ProvingTaskAssigned
|
||||
// ProvingTaskProved DEPRECATED: proof has been returned by prover
|
||||
ProvingTaskProved
|
||||
// ProvingTaskProvedDEPRECATED DEPRECATED: proof has been returned by prover
|
||||
ProvingTaskProvedDEPRECATED
|
||||
// ProvingTaskVerified : proof is valid
|
||||
ProvingTaskVerified
|
||||
// ProvingTaskFailed : fail to generate proof
|
||||
@@ -140,7 +140,7 @@ func (ps ProvingStatus) String() string {
|
||||
return "unassigned"
|
||||
case ProvingTaskAssigned:
|
||||
return "assigned"
|
||||
case ProvingTaskProved:
|
||||
case ProvingTaskProvedDEPRECATED:
|
||||
return "proved"
|
||||
case ProvingTaskVerified:
|
||||
return "verified"
|
||||
|
||||
@@ -58,8 +58,8 @@ func TestProvingStatus(t *testing.T) {
|
||||
"assigned",
|
||||
},
|
||||
{
|
||||
"ProvingTaskProved",
|
||||
ProvingTaskProved,
|
||||
"ProvingTaskProvedDEPRECATED",
|
||||
ProvingTaskProvedDEPRECATED,
|
||||
"proved",
|
||||
},
|
||||
{
|
||||
|
||||
@@ -3,10 +3,11 @@ package version
|
||||
import (
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var tag = "v4.1.72"
|
||||
var tag = "v4.1.115"
|
||||
|
||||
var commit = func() string {
|
||||
if info, ok := debug.ReadBuildInfo(); ok {
|
||||
@@ -46,3 +47,39 @@ func CheckScrollProverVersion(proverVersion string) bool {
|
||||
// compare the `scroll_prover` version
|
||||
return remote[2] == local[2]
|
||||
}
|
||||
|
||||
// CheckScrollProverVersionTag check the "scroll-prover" version's tag, if it's too old, return false
|
||||
func CheckScrollProverVersionTag(proverVersion string) bool {
|
||||
// note the the version is in fact in the format of "tag-commit-scroll_prover-halo2",
|
||||
// so split-by-'-' length should be 4
|
||||
remote := strings.Split(proverVersion, "-")
|
||||
if len(remote) != 4 {
|
||||
return false
|
||||
}
|
||||
remoteTagNums := strings.Split(strings.TrimPrefix(remote[0], "v"), ".")
|
||||
if len(remoteTagNums) != 3 {
|
||||
return false
|
||||
}
|
||||
remoteTagMajor, err := strconv.Atoi(remoteTagNums[0])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
remoteTagMinor, err := strconv.Atoi(remoteTagNums[1])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
remoteTagPatch, err := strconv.Atoi(remoteTagNums[2])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if remoteTagMajor != 4 {
|
||||
return false
|
||||
}
|
||||
if remoteTagMinor != 1 {
|
||||
return false
|
||||
}
|
||||
if remoteTagPatch < 98 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -260,6 +260,23 @@ function owner() external view returns (address)
|
||||
*Returns the address of the current owner.*
|
||||
|
||||
|
||||
#### Returns
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _0 | address | undefined |
|
||||
|
||||
### rateLimiter
|
||||
|
||||
```solidity
|
||||
function rateLimiter() external view returns (address)
|
||||
```
|
||||
|
||||
The address of token rate limiter contract.
|
||||
|
||||
|
||||
|
||||
|
||||
#### Returns
|
||||
|
||||
| Name | Type | Description |
|
||||
@@ -354,6 +371,22 @@ function transferOwnership(address newOwner) external nonpayable
|
||||
|---|---|---|
|
||||
| newOwner | address | undefined |
|
||||
|
||||
### updateRateLimiter
|
||||
|
||||
```solidity
|
||||
function updateRateLimiter(address _newRateLimiter) external nonpayable
|
||||
```
|
||||
|
||||
Update rate limiter contract.
|
||||
|
||||
*This function can only called by contract owner.*
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _newRateLimiter | address | The address of new rate limiter contract. |
|
||||
|
||||
### updateTokenMapping
|
||||
|
||||
```solidity
|
||||
@@ -530,6 +563,23 @@ Emitted when some ERC1155 token is refunded.
|
||||
| tokenId | uint256 | undefined |
|
||||
| amount | uint256 | undefined |
|
||||
|
||||
### UpdateRateLimiter
|
||||
|
||||
```solidity
|
||||
event UpdateRateLimiter(address indexed _oldRateLimiter, address indexed _newRateLimiter)
|
||||
```
|
||||
|
||||
Emitted when owner updates rate limiter contract.
|
||||
|
||||
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _oldRateLimiter `indexed` | address | undefined |
|
||||
| _newRateLimiter `indexed` | address | undefined |
|
||||
|
||||
### UpdateTokenMapping
|
||||
|
||||
```solidity
|
||||
|
||||
@@ -227,6 +227,23 @@ function owner() external view returns (address)
|
||||
*Returns the address of the current owner.*
|
||||
|
||||
|
||||
#### Returns
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _0 | address | undefined |
|
||||
|
||||
### rateLimiter
|
||||
|
||||
```solidity
|
||||
function rateLimiter() external view returns (address)
|
||||
```
|
||||
|
||||
The address of token rate limiter contract.
|
||||
|
||||
|
||||
|
||||
|
||||
#### Returns
|
||||
|
||||
| Name | Type | Description |
|
||||
@@ -299,6 +316,22 @@ function transferOwnership(address newOwner) external nonpayable
|
||||
|---|---|---|
|
||||
| newOwner | address | undefined |
|
||||
|
||||
### updateRateLimiter
|
||||
|
||||
```solidity
|
||||
function updateRateLimiter(address _newRateLimiter) external nonpayable
|
||||
```
|
||||
|
||||
Update rate limiter contract.
|
||||
|
||||
*This function can only called by contract owner.*
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _newRateLimiter | address | The address of new rate limiter contract. |
|
||||
|
||||
### updateTokenMapping
|
||||
|
||||
```solidity
|
||||
@@ -469,6 +502,23 @@ Emitted when some ERC721 token is refunded.
|
||||
| recipient `indexed` | address | undefined |
|
||||
| tokenId | uint256 | undefined |
|
||||
|
||||
### UpdateRateLimiter
|
||||
|
||||
```solidity
|
||||
event UpdateRateLimiter(address indexed _oldRateLimiter, address indexed _newRateLimiter)
|
||||
```
|
||||
|
||||
Emitted when owner updates rate limiter contract.
|
||||
|
||||
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _oldRateLimiter `indexed` | address | undefined |
|
||||
| _newRateLimiter `indexed` | address | undefined |
|
||||
|
||||
### UpdateTokenMapping
|
||||
|
||||
```solidity
|
||||
|
||||
@@ -239,6 +239,23 @@ Mapping from queue index to previous replay queue index.
|
||||
|---|---|---|
|
||||
| _0 | uint256 | undefined |
|
||||
|
||||
### rateLimiter
|
||||
|
||||
```solidity
|
||||
function rateLimiter() external view returns (address)
|
||||
```
|
||||
|
||||
The address of ETH rate limiter contract.
|
||||
|
||||
|
||||
|
||||
|
||||
#### Returns
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _0 | address | undefined |
|
||||
|
||||
### relayMessageWithProof
|
||||
|
||||
```solidity
|
||||
@@ -436,6 +453,22 @@ Update max replay times.
|
||||
|---|---|---|
|
||||
| _newMaxReplayTimes | uint256 | The new max replay times. |
|
||||
|
||||
### updateRateLimiter
|
||||
|
||||
```solidity
|
||||
function updateRateLimiter(address _newRateLimiter) external nonpayable
|
||||
```
|
||||
|
||||
Update rate limiter contract.
|
||||
|
||||
*This function can only called by contract owner.*
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _newRateLimiter | address | The address of new rate limiter contract. |
|
||||
|
||||
### xDomainMessageSender
|
||||
|
||||
```solidity
|
||||
@@ -609,5 +642,22 @@ Emitted when the maximum number of times each message can be replayed is updated
|
||||
| oldMaxReplayTimes | uint256 | undefined |
|
||||
| newMaxReplayTimes | uint256 | undefined |
|
||||
|
||||
### UpdateRateLimiter
|
||||
|
||||
```solidity
|
||||
event UpdateRateLimiter(address indexed _oldRateLimiter, address indexed _newRateLimiter)
|
||||
```
|
||||
|
||||
Emitted when owner updates rate limiter contract.
|
||||
|
||||
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _oldRateLimiter `indexed` | address | undefined |
|
||||
| _newRateLimiter `indexed` | address | undefined |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -225,6 +225,23 @@ function owner() external view returns (address)
|
||||
*Returns the address of the current owner.*
|
||||
|
||||
|
||||
#### Returns
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _0 | address | undefined |
|
||||
|
||||
### rateLimiter
|
||||
|
||||
```solidity
|
||||
function rateLimiter() external view returns (address)
|
||||
```
|
||||
|
||||
The address of token rate limiter contract.
|
||||
|
||||
|
||||
|
||||
|
||||
#### Returns
|
||||
|
||||
| Name | Type | Description |
|
||||
@@ -275,6 +292,22 @@ function transferOwnership(address newOwner) external nonpayable
|
||||
|---|---|---|
|
||||
| newOwner | address | undefined |
|
||||
|
||||
### updateRateLimiter
|
||||
|
||||
```solidity
|
||||
function updateRateLimiter(address _newRateLimiter) external nonpayable
|
||||
```
|
||||
|
||||
Update rate limiter contract.
|
||||
|
||||
*This function can only called by contract owner.*
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _newRateLimiter | address | The address of new rate limiter contract. |
|
||||
|
||||
|
||||
|
||||
## Events
|
||||
@@ -372,5 +405,22 @@ Emitted when some ERC20 token is refunded.
|
||||
| recipient `indexed` | address | undefined |
|
||||
| amount | uint256 | undefined |
|
||||
|
||||
### UpdateRateLimiter
|
||||
|
||||
```solidity
|
||||
event UpdateRateLimiter(address indexed _oldRateLimiter, address indexed _newRateLimiter)
|
||||
```
|
||||
|
||||
Emitted when owner updates rate limiter contract.
|
||||
|
||||
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _oldRateLimiter `indexed` | address | undefined |
|
||||
| _newRateLimiter `indexed` | address | undefined |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -223,6 +223,23 @@ function owner() external view returns (address)
|
||||
*Returns the address of the current owner.*
|
||||
|
||||
|
||||
#### Returns
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _0 | address | undefined |
|
||||
|
||||
### rateLimiter
|
||||
|
||||
```solidity
|
||||
function rateLimiter() external view returns (address)
|
||||
```
|
||||
|
||||
The address of token rate limiter contract.
|
||||
|
||||
|
||||
|
||||
|
||||
#### Returns
|
||||
|
||||
| Name | Type | Description |
|
||||
@@ -273,6 +290,22 @@ function transferOwnership(address newOwner) external nonpayable
|
||||
|---|---|---|
|
||||
| newOwner | address | undefined |
|
||||
|
||||
### updateRateLimiter
|
||||
|
||||
```solidity
|
||||
function updateRateLimiter(address _newRateLimiter) external nonpayable
|
||||
```
|
||||
|
||||
Update rate limiter contract.
|
||||
|
||||
*This function can only called by contract owner.*
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _newRateLimiter | address | The address of new rate limiter contract. |
|
||||
|
||||
|
||||
|
||||
## Events
|
||||
@@ -370,5 +403,22 @@ Emitted when some ERC20 token is refunded.
|
||||
| recipient `indexed` | address | undefined |
|
||||
| amount | uint256 | undefined |
|
||||
|
||||
### UpdateRateLimiter
|
||||
|
||||
```solidity
|
||||
event UpdateRateLimiter(address indexed _oldRateLimiter, address indexed _newRateLimiter)
|
||||
```
|
||||
|
||||
Emitted when owner updates rate limiter contract.
|
||||
|
||||
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _oldRateLimiter `indexed` | address | undefined |
|
||||
| _newRateLimiter `indexed` | address | undefined |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -205,6 +205,23 @@ function owner() external view returns (address)
|
||||
*Returns the address of the current owner.*
|
||||
|
||||
|
||||
#### Returns
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _0 | address | undefined |
|
||||
|
||||
### rateLimiter
|
||||
|
||||
```solidity
|
||||
function rateLimiter() external view returns (address)
|
||||
```
|
||||
|
||||
The address of token rate limiter contract.
|
||||
|
||||
|
||||
|
||||
|
||||
#### Returns
|
||||
|
||||
| Name | Type | Description |
|
||||
@@ -299,6 +316,22 @@ function transferOwnership(address newOwner) external nonpayable
|
||||
|---|---|---|
|
||||
| newOwner | address | undefined |
|
||||
|
||||
### updateRateLimiter
|
||||
|
||||
```solidity
|
||||
function updateRateLimiter(address _newRateLimiter) external nonpayable
|
||||
```
|
||||
|
||||
Update rate limiter contract.
|
||||
|
||||
*This function can only called by contract owner.*
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _newRateLimiter | address | The address of new rate limiter contract. |
|
||||
|
||||
### updateTokenMapping
|
||||
|
||||
```solidity
|
||||
@@ -455,6 +488,23 @@ event OwnershipTransferred(address indexed previousOwner, address indexed newOwn
|
||||
| previousOwner `indexed` | address | undefined |
|
||||
| newOwner `indexed` | address | undefined |
|
||||
|
||||
### UpdateRateLimiter
|
||||
|
||||
```solidity
|
||||
event UpdateRateLimiter(address indexed _oldRateLimiter, address indexed _newRateLimiter)
|
||||
```
|
||||
|
||||
Emitted when owner updates rate limiter contract.
|
||||
|
||||
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _oldRateLimiter `indexed` | address | undefined |
|
||||
| _newRateLimiter `indexed` | address | undefined |
|
||||
|
||||
### UpdateTokenMapping
|
||||
|
||||
```solidity
|
||||
|
||||
@@ -174,6 +174,23 @@ function owner() external view returns (address)
|
||||
*Returns the address of the current owner.*
|
||||
|
||||
|
||||
#### Returns
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _0 | address | undefined |
|
||||
|
||||
### rateLimiter
|
||||
|
||||
```solidity
|
||||
function rateLimiter() external view returns (address)
|
||||
```
|
||||
|
||||
The address of token rate limiter contract.
|
||||
|
||||
|
||||
|
||||
|
||||
#### Returns
|
||||
|
||||
| Name | Type | Description |
|
||||
@@ -246,6 +263,22 @@ function transferOwnership(address newOwner) external nonpayable
|
||||
|---|---|---|
|
||||
| newOwner | address | undefined |
|
||||
|
||||
### updateRateLimiter
|
||||
|
||||
```solidity
|
||||
function updateRateLimiter(address _newRateLimiter) external nonpayable
|
||||
```
|
||||
|
||||
Update rate limiter contract.
|
||||
|
||||
*This function can only called by contract owner.*
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _newRateLimiter | address | The address of new rate limiter contract. |
|
||||
|
||||
### updateTokenMapping
|
||||
|
||||
```solidity
|
||||
@@ -397,6 +430,23 @@ event OwnershipTransferred(address indexed previousOwner, address indexed newOwn
|
||||
| previousOwner `indexed` | address | undefined |
|
||||
| newOwner `indexed` | address | undefined |
|
||||
|
||||
### UpdateRateLimiter
|
||||
|
||||
```solidity
|
||||
event UpdateRateLimiter(address indexed _oldRateLimiter, address indexed _newRateLimiter)
|
||||
```
|
||||
|
||||
Emitted when owner updates rate limiter contract.
|
||||
|
||||
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _oldRateLimiter `indexed` | address | undefined |
|
||||
| _newRateLimiter `indexed` | address | undefined |
|
||||
|
||||
### UpdateTokenMapping
|
||||
|
||||
```solidity
|
||||
|
||||
@@ -194,6 +194,23 @@ function paused() external view returns (bool)
|
||||
|---|---|---|
|
||||
| _0 | bool | undefined |
|
||||
|
||||
### rateLimiter
|
||||
|
||||
```solidity
|
||||
function rateLimiter() external view returns (address)
|
||||
```
|
||||
|
||||
The address of ETH rate limiter contract.
|
||||
|
||||
|
||||
|
||||
|
||||
#### Returns
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _0 | address | undefined |
|
||||
|
||||
### relayMessage
|
||||
|
||||
```solidity
|
||||
@@ -328,6 +345,22 @@ Update max failed execution times.
|
||||
|---|---|---|
|
||||
| _newMaxFailedExecutionTimes | uint256 | The new max failed execution times. |
|
||||
|
||||
### updateRateLimiter
|
||||
|
||||
```solidity
|
||||
function updateRateLimiter(address _newRateLimiter) external nonpayable
|
||||
```
|
||||
|
||||
Update rate limiter contract.
|
||||
|
||||
*This function can only called by contract owner.*
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _newRateLimiter | address | The address of new rate limiter contract. |
|
||||
|
||||
### xDomainMessageSender
|
||||
|
||||
```solidity
|
||||
@@ -501,5 +534,22 @@ Emitted when the maximum number of times each message can fail in L2 is updated.
|
||||
| oldMaxFailedExecutionTimes | uint256 | undefined |
|
||||
| newMaxFailedExecutionTimes | uint256 | undefined |
|
||||
|
||||
### UpdateRateLimiter
|
||||
|
||||
```solidity
|
||||
event UpdateRateLimiter(address indexed _oldRateLimiter, address indexed _newRateLimiter)
|
||||
```
|
||||
|
||||
Emitted when owner updates rate limiter contract.
|
||||
|
||||
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _oldRateLimiter `indexed` | address | undefined |
|
||||
| _newRateLimiter `indexed` | address | undefined |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -139,6 +139,23 @@ function owner() external view returns (address)
|
||||
*Returns the address of the current owner.*
|
||||
|
||||
|
||||
#### Returns
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _0 | address | undefined |
|
||||
|
||||
### rateLimiter
|
||||
|
||||
```solidity
|
||||
function rateLimiter() external view returns (address)
|
||||
```
|
||||
|
||||
The address of token rate limiter contract.
|
||||
|
||||
|
||||
|
||||
|
||||
#### Returns
|
||||
|
||||
| Name | Type | Description |
|
||||
@@ -206,6 +223,22 @@ function transferOwnership(address newOwner) external nonpayable
|
||||
|---|---|---|
|
||||
| newOwner | address | undefined |
|
||||
|
||||
### updateRateLimiter
|
||||
|
||||
```solidity
|
||||
function updateRateLimiter(address _newRateLimiter) external nonpayable
|
||||
```
|
||||
|
||||
Update rate limiter contract.
|
||||
|
||||
*This function can only called by contract owner.*
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _newRateLimiter | address | The address of new rate limiter contract. |
|
||||
|
||||
### withdrawERC20
|
||||
|
||||
```solidity
|
||||
@@ -321,6 +354,23 @@ event OwnershipTransferred(address indexed previousOwner, address indexed newOwn
|
||||
| previousOwner `indexed` | address | undefined |
|
||||
| newOwner `indexed` | address | undefined |
|
||||
|
||||
### UpdateRateLimiter
|
||||
|
||||
```solidity
|
||||
event UpdateRateLimiter(address indexed _oldRateLimiter, address indexed _newRateLimiter)
|
||||
```
|
||||
|
||||
Emitted when owner updates rate limiter contract.
|
||||
|
||||
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _oldRateLimiter `indexed` | address | undefined |
|
||||
| _newRateLimiter `indexed` | address | undefined |
|
||||
|
||||
### WithdrawERC20
|
||||
|
||||
```solidity
|
||||
|
||||
@@ -172,6 +172,23 @@ function owner() external view returns (address)
|
||||
*Returns the address of the current owner.*
|
||||
|
||||
|
||||
#### Returns
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _0 | address | undefined |
|
||||
|
||||
### rateLimiter
|
||||
|
||||
```solidity
|
||||
function rateLimiter() external view returns (address)
|
||||
```
|
||||
|
||||
The address of token rate limiter contract.
|
||||
|
||||
|
||||
|
||||
|
||||
#### Returns
|
||||
|
||||
| Name | Type | Description |
|
||||
@@ -222,6 +239,22 @@ function transferOwnership(address newOwner) external nonpayable
|
||||
|---|---|---|
|
||||
| newOwner | address | undefined |
|
||||
|
||||
### updateRateLimiter
|
||||
|
||||
```solidity
|
||||
function updateRateLimiter(address _newRateLimiter) external nonpayable
|
||||
```
|
||||
|
||||
Update rate limiter contract.
|
||||
|
||||
*This function can only called by contract owner.*
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _newRateLimiter | address | The address of new rate limiter contract. |
|
||||
|
||||
### withdrawERC20
|
||||
|
||||
```solidity
|
||||
@@ -337,6 +370,23 @@ event OwnershipTransferred(address indexed previousOwner, address indexed newOwn
|
||||
| previousOwner `indexed` | address | undefined |
|
||||
| newOwner `indexed` | address | undefined |
|
||||
|
||||
### UpdateRateLimiter
|
||||
|
||||
```solidity
|
||||
event UpdateRateLimiter(address indexed _oldRateLimiter, address indexed _newRateLimiter)
|
||||
```
|
||||
|
||||
Emitted when owner updates rate limiter contract.
|
||||
|
||||
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _oldRateLimiter `indexed` | address | undefined |
|
||||
| _newRateLimiter `indexed` | address | undefined |
|
||||
|
||||
### WithdrawERC20
|
||||
|
||||
```solidity
|
||||
|
||||
@@ -12,6 +12,7 @@ import {L1ScrollMessenger} from "../../src/L1/L1ScrollMessenger.sol";
|
||||
import {L1StandardERC20Gateway} from "../../src/L1/gateways/L1StandardERC20Gateway.sol";
|
||||
import {L1WETHGateway} from "../../src/L1/gateways/L1WETHGateway.sol";
|
||||
import {L1DAIGateway} from "../../src/L1/gateways/L1DAIGateway.sol";
|
||||
import {MultipleVersionRollupVerifier} from "../../src/L1/rollup/MultipleVersionRollupVerifier.sol";
|
||||
import {ScrollChain} from "../../src/L1/rollup/ScrollChain.sol";
|
||||
import {L1MessageQueue} from "../../src/L1/rollup/L1MessageQueue.sol";
|
||||
import {L2GasPriceOracle} from "../../src/L1/rollup/L2GasPriceOracle.sol";
|
||||
@@ -71,6 +72,9 @@ contract InitializeL1BridgeContracts is Script {
|
||||
ScrollChain(L1_SCROLL_CHAIN_PROXY_ADDR).addSequencer(L1_COMMIT_SENDER_ADDRESS);
|
||||
ScrollChain(L1_SCROLL_CHAIN_PROXY_ADDR).addProver(L1_FINALIZE_SENDER_ADDRESS);
|
||||
|
||||
// initialize MultipleVersionRollupVerifier
|
||||
MultipleVersionRollupVerifier(L1_MULTIPLE_VERSION_ROLLUP_VERIFIER_ADDR).initialize(L1_SCROLL_CHAIN_PROXY_ADDR);
|
||||
|
||||
// initialize L2GasPriceOracle
|
||||
L2GasPriceOracle(L2_GAS_PRICE_ORACLE_PROXY_ADDR).initialize(
|
||||
21000, // _txGas
|
||||
|
||||
@@ -98,6 +98,9 @@ contract L1ScrollMessenger is ScrollMessengerBase, IL1ScrollMessenger {
|
||||
|
||||
rollup = _rollup;
|
||||
messageQueue = _messageQueue;
|
||||
|
||||
maxReplayTimes = 3;
|
||||
emit UpdateMaxReplayTimes(0, 3);
|
||||
}
|
||||
|
||||
/*****************************
|
||||
@@ -149,7 +152,7 @@ contract L1ScrollMessenger is ScrollMessengerBase, IL1ScrollMessenger {
|
||||
|
||||
// @note check more `_to` address to avoid attack in the future when we add more gateways.
|
||||
require(_to != messageQueue, "Forbid to call message queue");
|
||||
require(_to != address(this), "Forbid to call self");
|
||||
_validateTargetAddress(_to);
|
||||
|
||||
// @note This usually will never happen, just in case.
|
||||
require(_from != xDomainMessageSender, "Invalid message sender");
|
||||
@@ -312,6 +315,8 @@ contract L1ScrollMessenger is ScrollMessengerBase, IL1ScrollMessenger {
|
||||
uint256 _gasLimit,
|
||||
address _refundAddress
|
||||
) internal nonReentrant {
|
||||
_addUsedAmount(_value);
|
||||
|
||||
address _messageQueue = messageQueue; // gas saving
|
||||
address _counterpart = counterpart; // gas saving
|
||||
|
||||
|
||||
@@ -14,8 +14,6 @@ import {ScrollConstants} from "../../libraries/constants/ScrollConstants.sol";
|
||||
import {ScrollGatewayBase} from "../../libraries/gateway/ScrollGatewayBase.sol";
|
||||
import {IMessageDropCallback} from "../../libraries/callbacks/IMessageDropCallback.sol";
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
|
||||
abstract contract L1ERC20Gateway is IL1ERC20Gateway, IMessageDropCallback, ScrollGatewayBase {
|
||||
using SafeERC20Upgradeable for IERC20Upgradeable;
|
||||
|
||||
@@ -161,6 +159,9 @@ abstract contract L1ERC20Gateway is IL1ERC20Gateway, IMessageDropCallback, Scrol
|
||||
// ignore weird fee on transfer token
|
||||
require(_amount > 0, "deposit zero amount");
|
||||
|
||||
// rate limit
|
||||
_addUsedAmount(_token, _amount);
|
||||
|
||||
return (_from, _amount, _data);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ contract L1ETHGateway is ScrollGatewayBase, IL1ETHGateway, IMessageDropCallback
|
||||
address _to,
|
||||
uint256 _amount,
|
||||
uint256 _gasLimit
|
||||
) public payable override {
|
||||
) external payable override {
|
||||
_deposit(_to, _amount, new bytes(0), _gasLimit);
|
||||
}
|
||||
|
||||
@@ -124,6 +124,8 @@ contract L1ETHGateway is ScrollGatewayBase, IL1ETHGateway, IMessageDropCallback
|
||||
(_from, _data) = abi.decode(_data, (address, bytes));
|
||||
}
|
||||
|
||||
// @note no rate limit here, since ETH is limited in messenger
|
||||
|
||||
// 2. Generate message passed to L1ScrollMessenger.
|
||||
bytes memory _message = abi.encodeCall(IL2ETHGateway.finalizeDepositETH, (_from, _to, _amount, _data));
|
||||
|
||||
|
||||
@@ -28,6 +28,9 @@ interface IScrollChain {
|
||||
* Public View Functions *
|
||||
*************************/
|
||||
|
||||
/// @notice The latest finalized batch index.
|
||||
function lastFinalizedBatchIndex() external view returns (uint256);
|
||||
|
||||
/// @notice Return the batch hash of a committed batch.
|
||||
/// @param batchIndex The index of the batch.
|
||||
function committedBatches(uint256 batchIndex) external view returns (bytes32);
|
||||
|
||||
@@ -4,6 +4,7 @@ pragma solidity =0.8.16;
|
||||
|
||||
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
|
||||
|
||||
import {IScrollChain} from "./IScrollChain.sol";
|
||||
import {IRollupVerifier} from "../../libraries/verifier/IRollupVerifier.sol";
|
||||
import {IZkEvmVerifier} from "../../libraries/verifier/IZkEvmVerifier.sol";
|
||||
|
||||
@@ -38,6 +39,9 @@ contract MultipleVersionRollupVerifier is IRollupVerifier, Ownable {
|
||||
/// @notice The lastest used zkevm verifier.
|
||||
Verifier public latestVerifier;
|
||||
|
||||
/// @notice The address of ScrollChain contract.
|
||||
address public scrollChain;
|
||||
|
||||
/***************
|
||||
* Constructor *
|
||||
***************/
|
||||
@@ -48,6 +52,12 @@ contract MultipleVersionRollupVerifier is IRollupVerifier, Ownable {
|
||||
latestVerifier.verifier = _verifier;
|
||||
}
|
||||
|
||||
function initialize(address _scrollChain) external onlyOwner {
|
||||
require(scrollChain == address(0), "initialized");
|
||||
|
||||
scrollChain = _scrollChain;
|
||||
}
|
||||
|
||||
/*************************
|
||||
* Public View Functions *
|
||||
*************************/
|
||||
@@ -101,6 +111,8 @@ contract MultipleVersionRollupVerifier is IRollupVerifier, Ownable {
|
||||
/// @param _startBatchIndex The start batch index when the verifier will be used.
|
||||
/// @param _verifier The address of new verifier.
|
||||
function updateVerifier(uint64 _startBatchIndex, address _verifier) external onlyOwner {
|
||||
require(_startBatchIndex > IScrollChain(scrollChain).lastFinalizedBatchIndex(), "start batch index finalized");
|
||||
|
||||
Verifier memory _latestVerifier = latestVerifier;
|
||||
require(_startBatchIndex >= _latestVerifier.startBatchIndex, "start batch index too small");
|
||||
require(_verifier != address(0), "zero verifier address");
|
||||
|
||||
@@ -67,8 +67,8 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain {
|
||||
/// @notice Whether an account is a prover.
|
||||
mapping(address => bool) public isProver;
|
||||
|
||||
/// @notice The latest finalized batch index.
|
||||
uint256 public lastFinalizedBatchIndex;
|
||||
/// @inheritdoc IScrollChain
|
||||
uint256 public override lastFinalizedBatchIndex;
|
||||
|
||||
/// @inheritdoc IScrollChain
|
||||
mapping(uint256 => bytes32) public override committedBatches;
|
||||
@@ -272,9 +272,10 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain {
|
||||
require(_batchIndex > lastFinalizedBatchIndex, "can only revert unfinalized batch");
|
||||
|
||||
while (_count > 0) {
|
||||
committedBatches[_batchIndex] = bytes32(0);
|
||||
|
||||
emit RevertBatch(_batchIndex, _batchHash);
|
||||
|
||||
committedBatches[_batchIndex] = bytes32(0);
|
||||
unchecked {
|
||||
_batchIndex += 1;
|
||||
_count -= 1;
|
||||
|
||||
@@ -40,12 +40,6 @@ contract L2ScrollMessenger is ScrollMessengerBase, IL2ScrollMessenger {
|
||||
/// @notice Mapping from L1 message hash to a boolean value indicating if the message has been successfully executed.
|
||||
mapping(bytes32 => bool) public isL1MessageExecuted;
|
||||
|
||||
/// @notice Mapping from L1 message hash to the number of failure times.
|
||||
mapping(bytes32 => uint256) public l1MessageFailedTimes;
|
||||
|
||||
/// @notice The maximum number of times each L1 message can fail on L2.
|
||||
uint256 public maxFailedExecutionTimes;
|
||||
|
||||
/***************
|
||||
* Constructor *
|
||||
***************/
|
||||
@@ -58,8 +52,6 @@ contract L2ScrollMessenger is ScrollMessengerBase, IL2ScrollMessenger {
|
||||
|
||||
function initialize(address _counterpart) external initializer {
|
||||
ScrollMessengerBase.__ScrollMessengerBase_init(_counterpart, address(0));
|
||||
|
||||
maxFailedExecutionTimes = 3;
|
||||
}
|
||||
|
||||
/*****************************
|
||||
@@ -105,22 +97,6 @@ contract L2ScrollMessenger is ScrollMessengerBase, IL2ScrollMessenger {
|
||||
_executeMessage(_from, _to, _value, _message, _xDomainCalldataHash);
|
||||
}
|
||||
|
||||
/************************
|
||||
* Restricted Functions *
|
||||
************************/
|
||||
|
||||
/// @notice Update max failed execution times.
|
||||
/// @dev This function can only called by contract owner.
|
||||
/// @param _newMaxFailedExecutionTimes The new max failed execution times.
|
||||
function updateMaxFailedExecutionTimes(uint256 _newMaxFailedExecutionTimes) external onlyOwner {
|
||||
require(_newMaxFailedExecutionTimes > 0, "maxFailedExecutionTimes cannot be zero");
|
||||
|
||||
uint256 _oldMaxFailedExecutionTimes = maxFailedExecutionTimes;
|
||||
maxFailedExecutionTimes = _newMaxFailedExecutionTimes;
|
||||
|
||||
emit UpdateMaxFailedExecutionTimes(_oldMaxFailedExecutionTimes, _newMaxFailedExecutionTimes);
|
||||
}
|
||||
|
||||
/**********************
|
||||
* Internal Functions *
|
||||
**********************/
|
||||
@@ -137,6 +113,7 @@ contract L2ScrollMessenger is ScrollMessengerBase, IL2ScrollMessenger {
|
||||
uint256 _gasLimit
|
||||
) internal nonReentrant {
|
||||
require(msg.value == _value, "msg.value mismatch");
|
||||
_addUsedAmount(_value);
|
||||
|
||||
uint256 _nonce = L2MessageQueue(messageQueue).nextMessageIndex();
|
||||
bytes32 _xDomainCalldataHash = keccak256(_encodeXDomainCalldata(msg.sender, _to, _value, _nonce, _message));
|
||||
@@ -165,7 +142,7 @@ contract L2ScrollMessenger is ScrollMessengerBase, IL2ScrollMessenger {
|
||||
) internal {
|
||||
// @note check more `_to` address to avoid attack in the future when we add more gateways.
|
||||
require(_to != messageQueue, "Forbid to call message queue");
|
||||
require(_to != address(this), "Forbid to call self");
|
||||
_validateTargetAddress(_to);
|
||||
|
||||
// @note This usually will never happen, just in case.
|
||||
require(_from != xDomainMessageSender, "Invalid message sender");
|
||||
@@ -180,11 +157,6 @@ contract L2ScrollMessenger is ScrollMessengerBase, IL2ScrollMessenger {
|
||||
isL1MessageExecuted[_xDomainCalldataHash] = true;
|
||||
emit RelayedMessage(_xDomainCalldataHash);
|
||||
} else {
|
||||
unchecked {
|
||||
uint256 _failedTimes = l1MessageFailedTimes[_xDomainCalldataHash] + 1;
|
||||
require(_failedTimes <= maxFailedExecutionTimes, "Exceed maximum failure times");
|
||||
l1MessageFailedTimes[_xDomainCalldataHash] = _failedTimes;
|
||||
}
|
||||
emit FailedRelayedMessage(_xDomainCalldataHash);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +126,9 @@ contract L2CustomERC20Gateway is L2ERC20Gateway {
|
||||
(_from, _data) = abi.decode(_data, (address, bytes));
|
||||
}
|
||||
|
||||
// rate limit
|
||||
_addUsedAmount(_token, _amount);
|
||||
|
||||
// 2. Burn token.
|
||||
IScrollERC20Upgradeable(_token).burn(_from, _amount);
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@ import {IL2ERC20Gateway} from "./IL2ERC20Gateway.sol";
|
||||
|
||||
import {ScrollGatewayBase} from "../../libraries/gateway/ScrollGatewayBase.sol";
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
|
||||
abstract contract L2ERC20Gateway is ScrollGatewayBase, IL2ERC20Gateway {
|
||||
/*************
|
||||
* Variables *
|
||||
|
||||
@@ -98,6 +98,8 @@ contract L2ETHGateway is ScrollGatewayBase, IL2ETHGateway {
|
||||
(_from, _data) = abi.decode(_data, (address, bytes));
|
||||
}
|
||||
|
||||
// @note no rate limit here, since ETH is limited in messenger
|
||||
|
||||
bytes memory _message = abi.encodeCall(IL1ETHGateway.finalizeWithdrawETH, (_from, _to, _amount, _data));
|
||||
IL2ScrollMessenger(messenger).sendMessage{value: msg.value}(counterpart, _amount, _message, _gasLimit);
|
||||
|
||||
|
||||
@@ -140,6 +140,9 @@ contract L2StandardERC20Gateway is L2ERC20Gateway {
|
||||
address _l1Token = tokenMapping[_token];
|
||||
require(_l1Token != address(0), "no corresponding l1 token");
|
||||
|
||||
// rate limit
|
||||
_addUsedAmount(_token, _amount);
|
||||
|
||||
// 2. Burn token.
|
||||
IScrollERC20Upgradeable(_token).burn(_from, _amount);
|
||||
|
||||
|
||||
@@ -116,6 +116,9 @@ contract L2WETHGateway is L2ERC20Gateway {
|
||||
(_from, _data) = abi.decode(_data, (address, bytes));
|
||||
}
|
||||
|
||||
// rate limit
|
||||
_addUsedAmount(_token, _amount);
|
||||
|
||||
// 2. Transfer token into this contract.
|
||||
IERC20Upgradeable(_token).safeTransferFrom(_from, address(this), _amount);
|
||||
IWETH(_token).withdraw(_amount);
|
||||
|
||||
@@ -128,7 +128,7 @@ contract L2USDCGateway is L2ERC20Gateway {
|
||||
require(_token == l2USDC, "only USDC is allowed");
|
||||
require(!withdrawPaused, "withdraw paused");
|
||||
|
||||
// 1. Extract real sender if this call is from L1GatewayRouter.
|
||||
// 1. Extract real sender if this call is from L2GatewayRouter.
|
||||
address _from = msg.sender;
|
||||
if (router == msg.sender) {
|
||||
(_from, _data) = abi.decode(_data, (address, bytes));
|
||||
|
||||
@@ -6,7 +6,6 @@ import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol"
|
||||
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
|
||||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol";
|
||||
|
||||
import {OwnableBase} from "../libraries/common/OwnableBase.sol";
|
||||
@@ -15,6 +14,7 @@ import {OwnableBase} from "../libraries/common/OwnableBase.sol";
|
||||
|
||||
contract GasSwap is ERC2771Context, ReentrancyGuard, OwnableBase {
|
||||
using SafeERC20 for IERC20;
|
||||
using SafeERC20 for IERC20Permit;
|
||||
|
||||
/**********
|
||||
* Events *
|
||||
@@ -94,7 +94,7 @@ contract GasSwap is ERC2771Context, ReentrancyGuard, OwnableBase {
|
||||
address _sender = _msgSender();
|
||||
|
||||
// do permit
|
||||
IERC20Permit(_permit.token).permit(
|
||||
IERC20Permit(_permit.token).safePermit(
|
||||
_sender,
|
||||
address(this),
|
||||
_permit.value,
|
||||
|
||||
@@ -7,6 +7,7 @@ import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/
|
||||
import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
|
||||
|
||||
import {ScrollConstants} from "./constants/ScrollConstants.sol";
|
||||
import {IETHRateLimiter} from "../rate-limiter/IETHRateLimiter.sol";
|
||||
import {IScrollMessenger} from "./IScrollMessenger.sol";
|
||||
|
||||
// solhint-disable var-name-mixedcase
|
||||
@@ -26,6 +27,11 @@ abstract contract ScrollMessengerBase is
|
||||
/// @param _newFeeVault The address of new fee vault contract.
|
||||
event UpdateFeeVault(address _oldFeeVault, address _newFeeVault);
|
||||
|
||||
/// @notice Emitted when owner updates rate limiter contract.
|
||||
/// @param _oldRateLimiter The address of old rate limiter contract.
|
||||
/// @param _newRateLimiter The address of new rate limiter contract.
|
||||
event UpdateRateLimiter(address indexed _oldRateLimiter, address indexed _newRateLimiter);
|
||||
|
||||
/*************
|
||||
* Variables *
|
||||
*************/
|
||||
@@ -39,8 +45,11 @@ abstract contract ScrollMessengerBase is
|
||||
/// @notice The address of fee vault, collecting cross domain messaging fee.
|
||||
address public feeVault;
|
||||
|
||||
/// @notice The address of ETH rate limiter contract.
|
||||
address public rateLimiter;
|
||||
|
||||
/// @dev The storage slots for future usage.
|
||||
uint256[47] private __gap;
|
||||
uint256[46] private __gap;
|
||||
|
||||
/**********************
|
||||
* Function Modifiers *
|
||||
@@ -89,6 +98,16 @@ abstract contract ScrollMessengerBase is
|
||||
emit UpdateFeeVault(_oldFeeVault, _newFeeVault);
|
||||
}
|
||||
|
||||
/// @notice Update rate limiter contract.
|
||||
/// @dev This function can only called by contract owner.
|
||||
/// @param _newRateLimiter The address of new rate limiter contract.
|
||||
function updateRateLimiter(address _newRateLimiter) external onlyOwner {
|
||||
address _oldRateLimiter = rateLimiter;
|
||||
|
||||
rateLimiter = _newRateLimiter;
|
||||
emit UpdateRateLimiter(_oldRateLimiter, _newRateLimiter);
|
||||
}
|
||||
|
||||
/// @notice Pause the contract
|
||||
/// @dev This function can only called by contract owner.
|
||||
/// @param _status The pause status to update.
|
||||
@@ -128,4 +147,27 @@ abstract contract ScrollMessengerBase is
|
||||
_message
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Internal function to increase ETH usage for the given `_sender`.
|
||||
/// @param _amount The amount of ETH used.
|
||||
function _addUsedAmount(uint256 _amount) internal {
|
||||
if (_amount == 0) return;
|
||||
|
||||
address _rateLimiter = rateLimiter;
|
||||
if (_rateLimiter != address(0)) {
|
||||
IETHRateLimiter(_rateLimiter).addUsedAmount(_amount);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Internal function to check whether the `_target` address is allowed to avoid attack.
|
||||
/// @param _target The address of target address to check.
|
||||
function _validateTargetAddress(address _target) internal view {
|
||||
// @note check more `_target` address to avoid attack in the future when we add more external contracts.
|
||||
|
||||
address _rateLimiter = rateLimiter;
|
||||
if (_rateLimiter != address(0)) {
|
||||
require(_target != _rateLimiter, "Forbid to call rate limiter");
|
||||
}
|
||||
require(_target != address(this), "Forbid to call self");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,18 @@ import {IScrollGateway} from "./IScrollGateway.sol";
|
||||
import {IScrollMessenger} from "../IScrollMessenger.sol";
|
||||
import {IScrollGatewayCallback} from "../callbacks/IScrollGatewayCallback.sol";
|
||||
import {ScrollConstants} from "../constants/ScrollConstants.sol";
|
||||
import {ITokenRateLimiter} from "../../rate-limiter/ITokenRateLimiter.sol";
|
||||
|
||||
abstract contract ScrollGatewayBase is ReentrancyGuardUpgradeable, OwnableUpgradeable, IScrollGateway {
|
||||
/**********
|
||||
* Events *
|
||||
**********/
|
||||
|
||||
/// @notice Emitted when owner updates rate limiter contract.
|
||||
/// @param _oldRateLimiter The address of old rate limiter contract.
|
||||
/// @param _newRateLimiter The address of new rate limiter contract.
|
||||
event UpdateRateLimiter(address indexed _oldRateLimiter, address indexed _newRateLimiter);
|
||||
|
||||
/*************
|
||||
* Variables *
|
||||
*************/
|
||||
@@ -24,8 +34,11 @@ abstract contract ScrollGatewayBase is ReentrancyGuardUpgradeable, OwnableUpgrad
|
||||
/// @inheritdoc IScrollGateway
|
||||
address public override messenger;
|
||||
|
||||
/// @notice The address of token rate limiter contract.
|
||||
address public rateLimiter;
|
||||
|
||||
/// @dev The storage slots for future usage.
|
||||
uint256[47] private __gap;
|
||||
uint256[46] private __gap;
|
||||
|
||||
/**********************
|
||||
* Function Modifiers *
|
||||
@@ -72,6 +85,20 @@ abstract contract ScrollGatewayBase is ReentrancyGuardUpgradeable, OwnableUpgrad
|
||||
}
|
||||
}
|
||||
|
||||
/************************
|
||||
* Restricted Functions *
|
||||
************************/
|
||||
|
||||
/// @notice Update rate limiter contract.
|
||||
/// @dev This function can only called by contract owner.
|
||||
/// @param _newRateLimiter The address of new rate limiter contract.
|
||||
function updateRateLimiter(address _newRateLimiter) external onlyOwner {
|
||||
address _oldRateLimiter = rateLimiter;
|
||||
|
||||
rateLimiter = _newRateLimiter;
|
||||
emit UpdateRateLimiter(_oldRateLimiter, _newRateLimiter);
|
||||
}
|
||||
|
||||
/**********************
|
||||
* Internal Functions *
|
||||
**********************/
|
||||
@@ -84,4 +111,16 @@ abstract contract ScrollGatewayBase is ReentrancyGuardUpgradeable, OwnableUpgrad
|
||||
IScrollGatewayCallback(_to).onScrollGatewayCallback(_data);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Internal function to increase token usage for the given `_sender`.
|
||||
/// @param _token The address of token.
|
||||
/// @param _amount The amount of token used.
|
||||
function _addUsedAmount(address _token, uint256 _amount) internal {
|
||||
if (_amount == 0) return;
|
||||
|
||||
address _rateLimiter = rateLimiter;
|
||||
if (_rateLimiter != address(0)) {
|
||||
ITokenRateLimiter(_rateLimiter).addUsedAmount(_token, _amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
116
contracts/src/rate-limiter/ETHRateLimiter.sol
Normal file
116
contracts/src/rate-limiter/ETHRateLimiter.sol
Normal file
@@ -0,0 +1,116 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity =0.8.16;
|
||||
|
||||
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
|
||||
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
|
||||
|
||||
import {IETHRateLimiter} from "./IETHRateLimiter.sol";
|
||||
|
||||
// solhint-disable func-name-mixedcase
|
||||
// solhint-disable not-rely-on-time
|
||||
|
||||
contract ETHRateLimiter is Ownable, IETHRateLimiter {
|
||||
/***********
|
||||
* Structs *
|
||||
***********/
|
||||
|
||||
struct TokenAmount {
|
||||
// The timestamp when the amount is updated.
|
||||
uint48 lastUpdateTs;
|
||||
// The ETH limit in wei.
|
||||
uint104 limit;
|
||||
// The amount of ETH in current period.
|
||||
uint104 amount;
|
||||
}
|
||||
|
||||
/*************
|
||||
* Constants *
|
||||
*************/
|
||||
|
||||
/// @notice The period length in seconds.
|
||||
/// @dev The time frame for the `k`-th period is `[periodDuration * k, periodDuration * (k + 1))`.
|
||||
uint256 public immutable periodDuration;
|
||||
|
||||
/// @notice The address of ETH spender.
|
||||
address public immutable spender;
|
||||
|
||||
/*************
|
||||
* Variables *
|
||||
*************/
|
||||
|
||||
/// @notice The token amount used in current period.
|
||||
TokenAmount public currentPeriod;
|
||||
|
||||
/***************
|
||||
* Constructor *
|
||||
***************/
|
||||
|
||||
constructor(
|
||||
uint256 _periodDuration,
|
||||
address _spender,
|
||||
uint104 _totalLimit
|
||||
) {
|
||||
if (_periodDuration == 0) {
|
||||
revert PeriodIsZero();
|
||||
}
|
||||
|
||||
if (_totalLimit == 0) {
|
||||
revert TotalLimitIsZero();
|
||||
}
|
||||
|
||||
periodDuration = _periodDuration;
|
||||
spender = _spender;
|
||||
|
||||
currentPeriod.limit = _totalLimit;
|
||||
}
|
||||
|
||||
/*****************************
|
||||
* Public Mutating Functions *
|
||||
*****************************/
|
||||
|
||||
/// @inheritdoc IETHRateLimiter
|
||||
function addUsedAmount(uint256 _amount) external override {
|
||||
if (msg.sender != spender) {
|
||||
revert CallerNotSpender();
|
||||
}
|
||||
if (_amount == 0) return;
|
||||
|
||||
uint256 _currentPeriodStart = (block.timestamp / periodDuration) * periodDuration;
|
||||
|
||||
// check total limit
|
||||
uint256 _currentTotalAmount;
|
||||
TokenAmount memory _currentPeriod = currentPeriod;
|
||||
|
||||
if (_currentPeriod.lastUpdateTs < _currentPeriodStart) {
|
||||
_currentTotalAmount = _amount;
|
||||
} else {
|
||||
_currentTotalAmount = _currentPeriod.amount + _amount;
|
||||
}
|
||||
if (_currentTotalAmount > _currentPeriod.limit) {
|
||||
revert ExceedTotalLimit();
|
||||
}
|
||||
|
||||
_currentPeriod.lastUpdateTs = uint48(block.timestamp);
|
||||
_currentPeriod.amount = SafeCast.toUint104(_currentTotalAmount);
|
||||
|
||||
currentPeriod = _currentPeriod;
|
||||
}
|
||||
|
||||
/************************
|
||||
* Restricted Functions *
|
||||
************************/
|
||||
|
||||
/// @notice Update the total token amount limit.
|
||||
/// @param _newTotalLimit The new total limit.
|
||||
function updateTotalLimit(uint104 _newTotalLimit) external onlyOwner {
|
||||
if (_newTotalLimit == 0) {
|
||||
revert TotalLimitIsZero();
|
||||
}
|
||||
|
||||
uint256 _oldTotalLimit = currentPeriod.limit;
|
||||
currentPeriod.limit = _newTotalLimit;
|
||||
|
||||
emit UpdateTotalLimit(_oldTotalLimit, _newTotalLimit);
|
||||
}
|
||||
}
|
||||
38
contracts/src/rate-limiter/IETHRateLimiter.sol
Normal file
38
contracts/src/rate-limiter/IETHRateLimiter.sol
Normal file
@@ -0,0 +1,38 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.16;
|
||||
|
||||
interface IETHRateLimiter {
|
||||
/**********
|
||||
* Events *
|
||||
**********/
|
||||
|
||||
/// @notice Emitted when the total limit is updated.
|
||||
/// @param oldTotalLimit The previous value of total limit before updating.
|
||||
/// @param newTotalLimit The current value of total limit after updating.
|
||||
event UpdateTotalLimit(uint256 oldTotalLimit, uint256 newTotalLimit);
|
||||
|
||||
/**********
|
||||
* Errors *
|
||||
**********/
|
||||
|
||||
/// @dev Thrown when the `periodDuration` is initialized to zero.
|
||||
error PeriodIsZero();
|
||||
|
||||
/// @dev Thrown when the `totalAmount` is initialized to zero.
|
||||
error TotalLimitIsZero();
|
||||
|
||||
/// @dev Thrown when an amount breaches the total limit in the period.
|
||||
error ExceedTotalLimit();
|
||||
|
||||
/// @dev Thrown when the call is not spender.
|
||||
error CallerNotSpender();
|
||||
|
||||
/*****************************
|
||||
* Public Mutating Functions *
|
||||
*****************************/
|
||||
|
||||
/// @notice Request some ETH usage for `sender`.
|
||||
/// @param _amount The amount of ETH to use.
|
||||
function addUsedAmount(uint256 _amount) external;
|
||||
}
|
||||
38
contracts/src/rate-limiter/ITokenRateLimiter.sol
Normal file
38
contracts/src/rate-limiter/ITokenRateLimiter.sol
Normal file
@@ -0,0 +1,38 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.16;
|
||||
|
||||
interface ITokenRateLimiter {
|
||||
/**********
|
||||
* Events *
|
||||
**********/
|
||||
|
||||
/// @notice Emitted when the total limit is updated.
|
||||
/// @param oldTotalLimit The previous value of total limit before updating.
|
||||
/// @param newTotalLimit The current value of total limit after updating.
|
||||
event UpdateTotalLimit(address indexed token, uint256 oldTotalLimit, uint256 newTotalLimit);
|
||||
|
||||
/**********
|
||||
* Errors *
|
||||
**********/
|
||||
|
||||
/// @dev Thrown when the `periodDuration` is initialized to zero.
|
||||
error PeriodIsZero();
|
||||
|
||||
/// @dev Thrown when the `totalAmount` is initialized to zero.
|
||||
/// @param token The address of the token.
|
||||
error TotalLimitIsZero(address token);
|
||||
|
||||
/// @dev Thrown when an amount breaches the total limit in the period.
|
||||
/// @param token The address of the token.
|
||||
error ExceedTotalLimit(address token);
|
||||
|
||||
/*****************************
|
||||
* Public Mutating Functions *
|
||||
*****************************/
|
||||
|
||||
/// @notice Request some token usage for `sender`.
|
||||
/// @param token The address of the token.
|
||||
/// @param amount The amount of token to use.
|
||||
function addUsedAmount(address token, uint256 amount) external;
|
||||
}
|
||||
106
contracts/src/rate-limiter/TokenRateLimiter.sol
Normal file
106
contracts/src/rate-limiter/TokenRateLimiter.sol
Normal file
@@ -0,0 +1,106 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity =0.8.16;
|
||||
|
||||
import {AccessControlEnumerable} from "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
|
||||
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
|
||||
|
||||
import {ITokenRateLimiter} from "./ITokenRateLimiter.sol";
|
||||
|
||||
// solhint-disable func-name-mixedcase
|
||||
// solhint-disable not-rely-on-time
|
||||
|
||||
contract TokenRateLimiter is AccessControlEnumerable, ITokenRateLimiter {
|
||||
/***********
|
||||
* Structs *
|
||||
***********/
|
||||
|
||||
struct TokenAmount {
|
||||
// The timestamp when the amount is updated.
|
||||
uint48 lastUpdateTs;
|
||||
// The token limit.
|
||||
uint104 limit;
|
||||
// The amount of token in current period.
|
||||
uint104 amount;
|
||||
}
|
||||
|
||||
/*************
|
||||
* Constants *
|
||||
*************/
|
||||
|
||||
/// @notice The role for token spender.
|
||||
bytes32 public constant TOKEN_SPENDER_ROLE = keccak256("TOKEN_SPENDER_ROLE");
|
||||
|
||||
/// @notice The period length in seconds.
|
||||
/// @dev The time frame for the `k`-th period is `[periodDuration * k, periodDuration * (k + 1))`.
|
||||
uint256 public immutable periodDuration;
|
||||
|
||||
/*************
|
||||
* Variables *
|
||||
*************/
|
||||
|
||||
/// @notice Mapping from token address to the total amounts used in current period and total token amount limit.
|
||||
mapping(address => TokenAmount) public currentPeriod;
|
||||
|
||||
/// @dev The storage slots for future usage.
|
||||
uint256[49] private __gap;
|
||||
|
||||
/***************
|
||||
* Constructor *
|
||||
***************/
|
||||
|
||||
constructor(uint256 _periodDuration) {
|
||||
if (_periodDuration == 0) {
|
||||
revert PeriodIsZero();
|
||||
}
|
||||
|
||||
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
|
||||
|
||||
periodDuration = _periodDuration;
|
||||
}
|
||||
|
||||
/*****************************
|
||||
* Public Mutating Functions *
|
||||
*****************************/
|
||||
|
||||
/// @inheritdoc ITokenRateLimiter
|
||||
function addUsedAmount(address _token, uint256 _amount) external override onlyRole(TOKEN_SPENDER_ROLE) {
|
||||
if (_amount == 0) return;
|
||||
|
||||
uint256 _currentPeriodStart = (block.timestamp / periodDuration) * periodDuration;
|
||||
|
||||
// check total limit, `0` means no limit at all.
|
||||
uint256 _currentTotalAmount;
|
||||
TokenAmount memory _currentPeriod = currentPeriod[_token];
|
||||
if (_currentPeriod.lastUpdateTs < _currentPeriodStart) {
|
||||
_currentTotalAmount = _amount;
|
||||
} else {
|
||||
_currentTotalAmount = _currentPeriod.amount + _amount;
|
||||
}
|
||||
if (_currentPeriod.limit != 0 && _currentTotalAmount > _currentPeriod.limit) {
|
||||
revert ExceedTotalLimit(_token);
|
||||
}
|
||||
|
||||
_currentPeriod.lastUpdateTs = uint48(block.timestamp);
|
||||
_currentPeriod.amount = SafeCast.toUint104(_currentTotalAmount);
|
||||
|
||||
currentPeriod[_token] = _currentPeriod;
|
||||
}
|
||||
|
||||
/************************
|
||||
* Restricted Functions *
|
||||
************************/
|
||||
|
||||
/// @notice Update the total token amount limit.
|
||||
/// @param _newTotalLimit The new total limit.
|
||||
function updateTotalLimit(address _token, uint104 _newTotalLimit) external onlyRole(DEFAULT_ADMIN_ROLE) {
|
||||
if (_newTotalLimit == 0) {
|
||||
revert TotalLimitIsZero(_token);
|
||||
}
|
||||
|
||||
uint256 _oldTotalLimit = currentPeriod[_token].limit;
|
||||
currentPeriod[_token].limit = _newTotalLimit;
|
||||
|
||||
emit UpdateTotalLimit(_token, _oldTotalLimit, _newTotalLimit);
|
||||
}
|
||||
}
|
||||
75
contracts/src/test/ETHRateLimiter.t.sol
Normal file
75
contracts/src/test/ETHRateLimiter.t.sol
Normal file
@@ -0,0 +1,75 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity =0.8.16;
|
||||
|
||||
import {DSTestPlus} from "solmate/test/utils/DSTestPlus.sol";
|
||||
|
||||
import {ETHRateLimiter} from "../rate-limiter/ETHRateLimiter.sol";
|
||||
import {IETHRateLimiter} from "../rate-limiter/IETHRateLimiter.sol";
|
||||
|
||||
contract ETHRateLimiterTest is DSTestPlus {
|
||||
event UpdateTotalLimit(uint256 oldTotalLimit, uint256 newTotalLimit);
|
||||
|
||||
ETHRateLimiter private limiter;
|
||||
|
||||
function setUp() public {
|
||||
hevm.warp(86400);
|
||||
limiter = new ETHRateLimiter(86400, address(this), 100 ether);
|
||||
}
|
||||
|
||||
function testUpdateTotalLimit(uint104 _newTotalLimit) external {
|
||||
hevm.assume(_newTotalLimit > 0);
|
||||
|
||||
// not owner, revert
|
||||
hevm.startPrank(address(1));
|
||||
hevm.expectRevert("Ownable: caller is not the owner");
|
||||
limiter.updateTotalLimit(_newTotalLimit);
|
||||
hevm.stopPrank();
|
||||
|
||||
// zero revert
|
||||
hevm.expectRevert(IETHRateLimiter.TotalLimitIsZero.selector);
|
||||
limiter.updateTotalLimit(0);
|
||||
|
||||
// success
|
||||
hevm.expectEmit(false, false, false, true);
|
||||
emit UpdateTotalLimit(100 ether, _newTotalLimit);
|
||||
limiter.updateTotalLimit(_newTotalLimit);
|
||||
(, uint104 _totalLimit, ) = limiter.currentPeriod();
|
||||
assertEq(_totalLimit, _newTotalLimit);
|
||||
}
|
||||
|
||||
function testAddUsedAmount() external {
|
||||
// non-spender, revert
|
||||
hevm.startPrank(address(1));
|
||||
hevm.expectRevert(IETHRateLimiter.CallerNotSpender.selector);
|
||||
limiter.addUsedAmount(0);
|
||||
hevm.stopPrank();
|
||||
|
||||
// exceed total limit on first call
|
||||
hevm.expectRevert(IETHRateLimiter.ExceedTotalLimit.selector);
|
||||
limiter.addUsedAmount(100 ether + 1);
|
||||
_checkTotalCurrentPeriodAmountAmount(0);
|
||||
|
||||
// exceed total limit on second call
|
||||
limiter.addUsedAmount(50 ether);
|
||||
_checkTotalCurrentPeriodAmountAmount(50 ether);
|
||||
hevm.expectRevert(IETHRateLimiter.ExceedTotalLimit.selector);
|
||||
limiter.addUsedAmount(50 ether + 1);
|
||||
_checkTotalCurrentPeriodAmountAmount(50 ether);
|
||||
|
||||
// one period passed
|
||||
hevm.warp(86400 * 2);
|
||||
limiter.addUsedAmount(1 ether);
|
||||
_checkTotalCurrentPeriodAmountAmount(1 ether);
|
||||
|
||||
// exceed
|
||||
hevm.expectRevert(IETHRateLimiter.ExceedTotalLimit.selector);
|
||||
limiter.addUsedAmount(99 ether + 1);
|
||||
_checkTotalCurrentPeriodAmountAmount(1 ether);
|
||||
}
|
||||
|
||||
function _checkTotalCurrentPeriodAmountAmount(uint256 expected) internal {
|
||||
(, , uint256 totalAmount) = limiter.currentPeriod();
|
||||
assertEq(totalAmount, expected);
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,26 @@ contract L1ScrollMessengerTest is L1GatewayTestBase {
|
||||
l1Messenger.relayMessageWithProof(address(this), address(messageQueue), 0, 0, new bytes(0), proof);
|
||||
}
|
||||
|
||||
function testForbidCallRateLimiterFromL2() external {
|
||||
l1Messenger.updateRateLimiter(address(1));
|
||||
bytes32 _xDomainCalldataHash = keccak256(
|
||||
abi.encodeWithSignature(
|
||||
"relayMessage(address,address,uint256,uint256,bytes)",
|
||||
address(this),
|
||||
address(1),
|
||||
0,
|
||||
0,
|
||||
new bytes(0)
|
||||
)
|
||||
);
|
||||
prepareL2MessageRoot(_xDomainCalldataHash);
|
||||
IL1ScrollMessenger.L2MessageProof memory proof;
|
||||
proof.batchIndex = rollup.lastFinalizedBatchIndex();
|
||||
|
||||
hevm.expectRevert("Forbid to call rate limiter");
|
||||
l1Messenger.relayMessageWithProof(address(this), address(1), 0, 0, new bytes(0), proof);
|
||||
}
|
||||
|
||||
function testForbidCallSelfFromL2() external {
|
||||
bytes32 _xDomainCalldataHash = keccak256(
|
||||
abi.encodeWithSignature(
|
||||
@@ -86,6 +106,8 @@ contract L1ScrollMessengerTest is L1GatewayTestBase {
|
||||
|
||||
exceedValue = bound(exceedValue, 1, address(this).balance / 2);
|
||||
|
||||
l1Messenger.updateMaxReplayTimes(0);
|
||||
|
||||
// append a message
|
||||
l1Messenger.sendMessage{value: 100}(address(0), 100, new bytes(0), 0, refundAddress);
|
||||
|
||||
@@ -159,9 +181,9 @@ contract L1ScrollMessengerTest is L1GatewayTestBase {
|
||||
hevm.stopPrank();
|
||||
|
||||
hevm.expectEmit(false, false, false, true);
|
||||
emit UpdateMaxReplayTimes(0, _maxReplayTimes);
|
||||
emit UpdateMaxReplayTimes(3, _maxReplayTimes);
|
||||
|
||||
assertEq(l1Messenger.maxReplayTimes(), 0);
|
||||
assertEq(l1Messenger.maxReplayTimes(), 3);
|
||||
l1Messenger.updateMaxReplayTimes(_maxReplayTimes);
|
||||
assertEq(l1Messenger.maxReplayTimes(), _maxReplayTimes);
|
||||
}
|
||||
|
||||
@@ -51,10 +51,15 @@ contract L2ScrollMessengerTest is DSTestPlus {
|
||||
}
|
||||
|
||||
function testForbidCallFromL1() external {
|
||||
l2Messenger.updateRateLimiter(address(1));
|
||||
|
||||
hevm.startPrank(AddressAliasHelper.applyL1ToL2Alias(address(l1Messenger)));
|
||||
hevm.expectRevert("Forbid to call message queue");
|
||||
l2Messenger.relayMessage(address(this), address(l2MessageQueue), 0, 0, new bytes(0));
|
||||
|
||||
hevm.expectRevert("Forbid to call rate limiter");
|
||||
l2Messenger.relayMessage(address(this), address(1), 0, 0, new bytes(0));
|
||||
|
||||
hevm.expectRevert("Forbid to call self");
|
||||
l2Messenger.relayMessage(address(this), address(l2Messenger), 0, 0, new bytes(0));
|
||||
hevm.stopPrank();
|
||||
|
||||
@@ -7,6 +7,7 @@ import {DSTestPlus} from "solmate/test/utils/DSTestPlus.sol";
|
||||
import {L1MessageQueue} from "../L1/rollup/L1MessageQueue.sol";
|
||||
import {MultipleVersionRollupVerifier} from "../L1/rollup/MultipleVersionRollupVerifier.sol";
|
||||
|
||||
import {MockScrollChain} from "./mocks/MockScrollChain.sol";
|
||||
import {MockZkEvmVerifier} from "./mocks/MockZkEvmVerifier.sol";
|
||||
|
||||
contract MultipleVersionRollupVerifierTest is DSTestPlus {
|
||||
@@ -17,27 +18,54 @@ contract MultipleVersionRollupVerifierTest is DSTestPlus {
|
||||
MockZkEvmVerifier private v0;
|
||||
MockZkEvmVerifier private v1;
|
||||
MockZkEvmVerifier private v2;
|
||||
MockScrollChain private chain;
|
||||
|
||||
function setUp() external {
|
||||
v0 = new MockZkEvmVerifier();
|
||||
v1 = new MockZkEvmVerifier();
|
||||
v2 = new MockZkEvmVerifier();
|
||||
chain = new MockScrollChain();
|
||||
|
||||
verifier = new MultipleVersionRollupVerifier(address(v0));
|
||||
}
|
||||
|
||||
function testInitialize(address _chain) external {
|
||||
hevm.assume(_chain != address(0));
|
||||
|
||||
// set by non-owner, should revert
|
||||
hevm.startPrank(address(1));
|
||||
hevm.expectRevert("Ownable: caller is not the owner");
|
||||
verifier.initialize(_chain);
|
||||
hevm.stopPrank();
|
||||
|
||||
// succeed
|
||||
assertEq(verifier.scrollChain(), address(0));
|
||||
verifier.initialize(_chain);
|
||||
assertEq(verifier.scrollChain(), _chain);
|
||||
|
||||
// initialized, revert
|
||||
hevm.expectRevert("initialized");
|
||||
verifier.initialize(_chain);
|
||||
}
|
||||
|
||||
function testUpdateVerifier(address _newVerifier) external {
|
||||
hevm.assume(_newVerifier != address(0));
|
||||
|
||||
verifier.initialize(address(chain));
|
||||
|
||||
// set by non-owner, should revert
|
||||
hevm.startPrank(address(1));
|
||||
hevm.expectRevert("Ownable: caller is not the owner");
|
||||
verifier.updateVerifier(0, address(0));
|
||||
hevm.stopPrank();
|
||||
|
||||
// start batch index finalized, revert
|
||||
hevm.expectRevert("start batch index finalized");
|
||||
verifier.updateVerifier(0, address(1));
|
||||
|
||||
// zero verifier address, revert
|
||||
hevm.expectRevert("zero verifier address");
|
||||
verifier.updateVerifier(0, address(0));
|
||||
verifier.updateVerifier(1, address(0));
|
||||
|
||||
// change to random operator
|
||||
assertEq(verifier.legacyVerifiersLength(), 0);
|
||||
@@ -65,6 +93,8 @@ contract MultipleVersionRollupVerifierTest is DSTestPlus {
|
||||
}
|
||||
|
||||
function testGetVerifier() external {
|
||||
verifier.initialize(address(chain));
|
||||
|
||||
verifier.updateVerifier(100, address(v1));
|
||||
verifier.updateVerifier(300, address(v2));
|
||||
|
||||
@@ -80,6 +110,8 @@ contract MultipleVersionRollupVerifierTest is DSTestPlus {
|
||||
}
|
||||
|
||||
function testVerifyAggregateProof() external {
|
||||
verifier.initialize(address(chain));
|
||||
|
||||
verifier.updateVerifier(100, address(v1));
|
||||
verifier.updateVerifier(300, address(v2));
|
||||
|
||||
|
||||
82
contracts/src/test/TokenRateLimiter.t.sol
Normal file
82
contracts/src/test/TokenRateLimiter.t.sol
Normal file
@@ -0,0 +1,82 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity =0.8.16;
|
||||
|
||||
import {DSTestPlus} from "solmate/test/utils/DSTestPlus.sol";
|
||||
|
||||
import {TokenRateLimiter} from "../rate-limiter/TokenRateLimiter.sol";
|
||||
import {ITokenRateLimiter} from "../rate-limiter/ITokenRateLimiter.sol";
|
||||
|
||||
contract TokenRateLimiterTest is DSTestPlus {
|
||||
event UpdateTotalLimit(address indexed token, uint256 oldTotalLimit, uint256 newTotalLimit);
|
||||
|
||||
TokenRateLimiter private limiter;
|
||||
|
||||
function setUp() public {
|
||||
hevm.warp(86400);
|
||||
limiter = new TokenRateLimiter(86400);
|
||||
}
|
||||
|
||||
function testUpdateTotalLimit(address _token, uint104 _newTotalLimit) external {
|
||||
hevm.assume(_newTotalLimit > 0);
|
||||
|
||||
// not admin, revert
|
||||
hevm.startPrank(address(1));
|
||||
hevm.expectRevert(
|
||||
"AccessControl: account 0x0000000000000000000000000000000000000001 is missing role 0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
);
|
||||
limiter.updateTotalLimit(_token, _newTotalLimit);
|
||||
hevm.stopPrank();
|
||||
|
||||
// zero revert
|
||||
hevm.expectRevert(abi.encodeWithSelector(ITokenRateLimiter.TotalLimitIsZero.selector, _token));
|
||||
limiter.updateTotalLimit(_token, 0);
|
||||
|
||||
// success
|
||||
hevm.expectEmit(true, false, false, true);
|
||||
emit UpdateTotalLimit(_token, 0 ether, _newTotalLimit);
|
||||
limiter.updateTotalLimit(_token, _newTotalLimit);
|
||||
(, uint104 _totalLimit, ) = limiter.currentPeriod(_token);
|
||||
assertEq(_totalLimit, _newTotalLimit);
|
||||
}
|
||||
|
||||
function testAddUsedAmount(address _token) external {
|
||||
// non-spender, revert
|
||||
hevm.startPrank(address(1));
|
||||
hevm.expectRevert(
|
||||
"AccessControl: account 0x0000000000000000000000000000000000000001 is missing role 0x267f05081a073059ae452e6ac77ec189636e43e41051d4c3ec760734b3d173cb"
|
||||
);
|
||||
limiter.addUsedAmount(_token, 0);
|
||||
hevm.stopPrank();
|
||||
|
||||
limiter.grantRole(bytes32(0x267f05081a073059ae452e6ac77ec189636e43e41051d4c3ec760734b3d173cb), address(this));
|
||||
limiter.updateTotalLimit(_token, 100 ether);
|
||||
|
||||
// exceed total limit on first call
|
||||
hevm.expectRevert(abi.encodeWithSelector(ITokenRateLimiter.ExceedTotalLimit.selector, _token));
|
||||
limiter.addUsedAmount(_token, 100 ether + 1);
|
||||
_checkTotalCurrentPeriodAmountAmount(_token, 0);
|
||||
|
||||
// exceed total limit on second call
|
||||
limiter.addUsedAmount(_token, 50 ether);
|
||||
_checkTotalCurrentPeriodAmountAmount(_token, 50 ether);
|
||||
hevm.expectRevert(abi.encodeWithSelector(ITokenRateLimiter.ExceedTotalLimit.selector, _token));
|
||||
limiter.addUsedAmount(_token, 50 ether + 1);
|
||||
_checkTotalCurrentPeriodAmountAmount(_token, 50 ether);
|
||||
|
||||
// one period passed
|
||||
hevm.warp(86400 * 2);
|
||||
limiter.addUsedAmount(_token, 1 ether);
|
||||
_checkTotalCurrentPeriodAmountAmount(_token, 1 ether);
|
||||
|
||||
// exceed
|
||||
hevm.expectRevert(abi.encodeWithSelector(ITokenRateLimiter.ExceedTotalLimit.selector, _token));
|
||||
limiter.addUsedAmount(_token, 99 ether + 1);
|
||||
_checkTotalCurrentPeriodAmountAmount(_token, 1 ether);
|
||||
}
|
||||
|
||||
function _checkTotalCurrentPeriodAmountAmount(address token, uint256 expected) internal {
|
||||
(, , uint256 totalAmount) = limiter.currentPeriod(token);
|
||||
assertEq(totalAmount, expected);
|
||||
}
|
||||
}
|
||||
@@ -7,18 +7,7 @@ import {ScrollChain} from "../../L1/rollup/ScrollChain.sol";
|
||||
contract MockScrollChain is ScrollChain {
|
||||
constructor() ScrollChain(0) {}
|
||||
|
||||
/*
|
||||
function computePublicInputHash(uint64 accTotalL1Messages, Batch memory batch)
|
||||
external
|
||||
view
|
||||
returns (
|
||||
bytes32,
|
||||
uint64,
|
||||
uint64,
|
||||
uint64
|
||||
)
|
||||
{
|
||||
return _computePublicInputHash(accTotalL1Messages, batch);
|
||||
function setLastFinalizedBatchIndex(uint256 _lastFinalizedBatchIndex) external {
|
||||
lastFinalizedBatchIndex = _lastFinalizedBatchIndex;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"gorm.io/gorm"
|
||||
|
||||
"scroll-tech/coordinator/internal/config"
|
||||
"scroll-tech/coordinator/internal/logic/verifier"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -25,9 +26,14 @@ var (
|
||||
// InitController inits Controller with database
|
||||
func InitController(cfg *config.Config, db *gorm.DB, reg prometheus.Registerer) {
|
||||
initControllerOnce.Do(func() {
|
||||
vf, err := verifier.NewVerifier(cfg.ProverManager.Verifier)
|
||||
if err != nil {
|
||||
panic("proof receiver new verifier failure")
|
||||
}
|
||||
|
||||
Auth = NewAuthController(db)
|
||||
HealthCheck = NewHealthCheckController()
|
||||
GetTask = NewGetTaskController(cfg, db, reg)
|
||||
SubmitProof = NewSubmitProofController(cfg, db, reg)
|
||||
GetTask = NewGetTaskController(cfg, db, vf, reg)
|
||||
SubmitProof = NewSubmitProofController(cfg, db, vf, reg)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
"scroll-tech/coordinator/internal/config"
|
||||
"scroll-tech/coordinator/internal/logic/provertask"
|
||||
"scroll-tech/coordinator/internal/logic/verifier"
|
||||
coordinatorType "scroll-tech/coordinator/internal/types"
|
||||
)
|
||||
|
||||
@@ -22,9 +23,9 @@ type GetTaskController struct {
|
||||
}
|
||||
|
||||
// NewGetTaskController create a get prover task controller
|
||||
func NewGetTaskController(cfg *config.Config, db *gorm.DB, reg prometheus.Registerer) *GetTaskController {
|
||||
chunkProverTask := provertask.NewChunkProverTask(cfg, db, reg)
|
||||
batchProverTask := provertask.NewBatchProverTask(cfg, db, reg)
|
||||
func NewGetTaskController(cfg *config.Config, db *gorm.DB, vf *verifier.Verifier, reg prometheus.Registerer) *GetTaskController {
|
||||
chunkProverTask := provertask.NewChunkProverTask(cfg, db, vf.ChunkVK, reg)
|
||||
batchProverTask := provertask.NewBatchProverTask(cfg, db, vf.BatchVK, reg)
|
||||
|
||||
ptc := &GetTaskController{
|
||||
proverTasks: make(map[message.ProofType]provertask.ProverTask),
|
||||
@@ -40,7 +41,7 @@ func NewGetTaskController(cfg *config.Config, db *gorm.DB, reg prometheus.Regist
|
||||
func (ptc *GetTaskController) GetTasks(ctx *gin.Context) {
|
||||
var getTaskParameter coordinatorType.GetTaskParameter
|
||||
if err := ctx.ShouldBind(&getTaskParameter); err != nil {
|
||||
nerr := fmt.Errorf("prover tasks parameter invalid, err:%w", err)
|
||||
nerr := fmt.Errorf("prover task parameter invalid, err:%w", err)
|
||||
coordinatorType.RenderJSON(ctx, types.ErrCoordinatorParameterInvalidNo, nerr, nil)
|
||||
return
|
||||
}
|
||||
@@ -48,7 +49,7 @@ func (ptc *GetTaskController) GetTasks(ctx *gin.Context) {
|
||||
proofType := ptc.proofType(&getTaskParameter)
|
||||
proverTask, isExist := ptc.proverTasks[proofType]
|
||||
if !isExist {
|
||||
nerr := fmt.Errorf("parameter wrong proof type")
|
||||
nerr := fmt.Errorf("parameter wrong proof type:%v", proofType)
|
||||
coordinatorType.RenderJSON(ctx, types.ErrCoordinatorParameterInvalidNo, nerr, nil)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -13,7 +13,8 @@ import (
|
||||
|
||||
"scroll-tech/coordinator/internal/config"
|
||||
"scroll-tech/coordinator/internal/logic/submitproof"
|
||||
coodinatorType "scroll-tech/coordinator/internal/types"
|
||||
"scroll-tech/coordinator/internal/logic/verifier"
|
||||
coordinatorType "scroll-tech/coordinator/internal/types"
|
||||
)
|
||||
|
||||
// SubmitProofController the submit proof api controller
|
||||
@@ -22,18 +23,18 @@ type SubmitProofController struct {
|
||||
}
|
||||
|
||||
// NewSubmitProofController create the submit proof api controller instance
|
||||
func NewSubmitProofController(cfg *config.Config, db *gorm.DB, reg prometheus.Registerer) *SubmitProofController {
|
||||
func NewSubmitProofController(cfg *config.Config, db *gorm.DB, vf *verifier.Verifier, reg prometheus.Registerer) *SubmitProofController {
|
||||
return &SubmitProofController{
|
||||
submitProofReceiverLogic: submitproof.NewSubmitProofReceiverLogic(cfg.ProverManager, db, reg),
|
||||
submitProofReceiverLogic: submitproof.NewSubmitProofReceiverLogic(cfg.ProverManager, db, vf, reg),
|
||||
}
|
||||
}
|
||||
|
||||
// SubmitProof prover submit the proof to coordinator
|
||||
func (spc *SubmitProofController) SubmitProof(ctx *gin.Context) {
|
||||
var spp coodinatorType.SubmitProofParameter
|
||||
var spp coordinatorType.SubmitProofParameter
|
||||
if err := ctx.ShouldBind(&spp); err != nil {
|
||||
nerr := fmt.Errorf("parameter invalid, err:%w", err)
|
||||
coodinatorType.RenderJSON(ctx, types.ErrCoordinatorParameterInvalidNo, nerr, nil)
|
||||
coordinatorType.RenderJSON(ctx, types.ErrCoordinatorParameterInvalidNo, nerr, nil)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -51,7 +52,7 @@ func (spc *SubmitProofController) SubmitProof(ctx *gin.Context) {
|
||||
var tmpChunkProof message.ChunkProof
|
||||
if err := json.Unmarshal([]byte(spp.Proof), &tmpChunkProof); err != nil {
|
||||
nerr := fmt.Errorf("unmarshal parameter chunk proof invalid, err:%w", err)
|
||||
coodinatorType.RenderJSON(ctx, types.ErrCoordinatorParameterInvalidNo, nerr, nil)
|
||||
coordinatorType.RenderJSON(ctx, types.ErrCoordinatorParameterInvalidNo, nerr, nil)
|
||||
return
|
||||
}
|
||||
proofMsg.ChunkProof = &tmpChunkProof
|
||||
@@ -59,7 +60,7 @@ func (spc *SubmitProofController) SubmitProof(ctx *gin.Context) {
|
||||
var tmpBatchProof message.BatchProof
|
||||
if err := json.Unmarshal([]byte(spp.Proof), &tmpBatchProof); err != nil {
|
||||
nerr := fmt.Errorf("unmarshal parameter batch proof invalid, err:%w", err)
|
||||
coodinatorType.RenderJSON(ctx, types.ErrCoordinatorParameterInvalidNo, nerr, nil)
|
||||
coordinatorType.RenderJSON(ctx, types.ErrCoordinatorParameterInvalidNo, nerr, nil)
|
||||
return
|
||||
}
|
||||
proofMsg.BatchProof = &tmpBatchProof
|
||||
@@ -68,8 +69,8 @@ func (spc *SubmitProofController) SubmitProof(ctx *gin.Context) {
|
||||
|
||||
if err := spc.submitProofReceiverLogic.HandleZkProof(ctx, &proofMsg, spp); err != nil {
|
||||
nerr := fmt.Errorf("handle zk proof failure, err:%w", err)
|
||||
coodinatorType.RenderJSON(ctx, types.ErrCoordinatorHandleZkProofFailure, nerr, nil)
|
||||
coordinatorType.RenderJSON(ctx, types.ErrCoordinatorHandleZkProofFailure, nerr, nil)
|
||||
return
|
||||
}
|
||||
coodinatorType.RenderJSON(ctx, types.Success, nil, nil)
|
||||
coordinatorType.RenderJSON(ctx, types.Success, nil, nil)
|
||||
}
|
||||
|
||||
@@ -29,10 +29,11 @@ type Collector struct {
|
||||
chunkOrm *orm.Chunk
|
||||
batchOrm *orm.Batch
|
||||
|
||||
timeoutBatchCheckerRunTotal prometheus.Counter
|
||||
batchProverTaskTimeoutTotal prometheus.Counter
|
||||
timeoutChunkCheckerRunTotal prometheus.Counter
|
||||
chunkProverTaskTimeoutTotal prometheus.Counter
|
||||
timeoutBatchCheckerRunTotal prometheus.Counter
|
||||
batchProverTaskTimeoutTotal prometheus.Counter
|
||||
timeoutChunkCheckerRunTotal prometheus.Counter
|
||||
chunkProverTaskTimeoutTotal prometheus.Counter
|
||||
checkBatchAllChunkReadyRunTotal prometheus.Counter
|
||||
}
|
||||
|
||||
// NewCollector create a collector to cron collect the data to send to prover
|
||||
@@ -62,10 +63,15 @@ func NewCollector(ctx context.Context, db *gorm.DB, cfg *config.Config, reg prom
|
||||
Name: "coordinator_chunk_prover_task_timeout_total",
|
||||
Help: "Total number of chunk timeout prover task.",
|
||||
}),
|
||||
checkBatchAllChunkReadyRunTotal: promauto.With(reg).NewCounter(prometheus.CounterOpts{
|
||||
Name: "coordinator_check_batch_all_chunk_ready_run_total",
|
||||
Help: "Total number of check batch all chunks ready total",
|
||||
}),
|
||||
}
|
||||
|
||||
go c.timeoutBatchProofTask()
|
||||
go c.timeoutChunkProofTask()
|
||||
go c.checkBatchAllChunkReady()
|
||||
|
||||
log.Info("Start coordinator successfully.")
|
||||
|
||||
@@ -79,7 +85,6 @@ func (c *Collector) Stop() {
|
||||
|
||||
// timeoutTask cron check the send task is timeout. if timeout reached, restore the
|
||||
// chunk/batch task to unassigned. then the batch/chunk collector can retry it.
|
||||
|
||||
func (c *Collector) timeoutBatchProofTask() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
@@ -149,7 +154,7 @@ func (c *Collector) check(assignedProverTasks []orm.ProverTask, timeout promethe
|
||||
// here not update the block batch proving status failed, because the collector loop will check
|
||||
// the attempt times. if reach the times, the collector will set the block batch proving status.
|
||||
for _, assignedProverTask := range assignedProverTasks {
|
||||
if c.proverTaskOrm.TaskTimeoutMoreThanOnce(c.ctx, assignedProverTask.TaskID) {
|
||||
if c.proverTaskOrm.TaskTimeoutMoreThanOnce(c.ctx, message.ProofType(assignedProverTask.TaskType), assignedProverTask.TaskID) {
|
||||
log.Warn("Task timeout more than once", "taskType", message.ProofType(assignedProverTask.TaskType).String(), "hash", assignedProverTask.TaskID)
|
||||
}
|
||||
|
||||
@@ -189,3 +194,60 @@ func (c *Collector) check(assignedProverTasks []orm.ProverTask, timeout promethe
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Collector) checkBatchAllChunkReady() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
nerr := fmt.Errorf("check batch all chunk ready panic error:%v", err)
|
||||
log.Warn(nerr.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
ticker := time.NewTicker(time.Second * 10)
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
c.checkBatchAllChunkReadyRunTotal.Inc()
|
||||
page := 1
|
||||
pageSize := 50
|
||||
for {
|
||||
offset := (page - 1) * pageSize
|
||||
batches, err := c.batchOrm.GetUnassignedAndChunksUnreadyBatches(c.ctx, offset, pageSize)
|
||||
if err != nil {
|
||||
log.Warn("checkBatchAllChunkReady GetUnassignedAndChunksUnreadyBatches", "error", err)
|
||||
break
|
||||
}
|
||||
|
||||
for _, batch := range batches {
|
||||
allReady, checkErr := c.chunkOrm.CheckIfBatchChunkProofsAreReady(c.ctx, batch.Hash)
|
||||
if checkErr != nil {
|
||||
log.Warn("checkBatchAllChunkReady CheckIfBatchChunkProofsAreReady failure", "error", checkErr, "hash", batch.Hash)
|
||||
continue
|
||||
}
|
||||
|
||||
if !allReady {
|
||||
continue
|
||||
}
|
||||
|
||||
if updateErr := c.batchOrm.UpdateChunkProofsStatusByBatchHash(c.ctx, batch.Hash, types.ChunkProofsStatusReady); updateErr != nil {
|
||||
log.Warn("checkBatchAllChunkReady UpdateChunkProofsStatusByBatchHash failure", "error", checkErr, "hash", batch.Hash)
|
||||
}
|
||||
}
|
||||
|
||||
if len(batches) < pageSize {
|
||||
break
|
||||
}
|
||||
page++
|
||||
}
|
||||
|
||||
case <-c.ctx.Done():
|
||||
if c.ctx.Err() != nil {
|
||||
log.Error("manager context canceled with error", "error", c.ctx.Err())
|
||||
}
|
||||
return
|
||||
case <-c.stopTimeoutChan:
|
||||
log.Info("the coordinator run loop exit")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,13 +25,14 @@ import (
|
||||
// BatchProverTask is prover task implement for batch proof
|
||||
type BatchProverTask struct {
|
||||
BaseProverTask
|
||||
vk string
|
||||
|
||||
batchAttemptsExceedTotal prometheus.Counter
|
||||
batchTaskGetTaskTotal prometheus.Counter
|
||||
}
|
||||
|
||||
// NewBatchProverTask new a batch collector
|
||||
func NewBatchProverTask(cfg *config.Config, db *gorm.DB, reg prometheus.Registerer) *BatchProverTask {
|
||||
func NewBatchProverTask(cfg *config.Config, db *gorm.DB, vk string, reg prometheus.Registerer) *BatchProverTask {
|
||||
bp := &BatchProverTask{
|
||||
BaseProverTask: BaseProverTask{
|
||||
db: db,
|
||||
@@ -40,6 +41,7 @@ func NewBatchProverTask(cfg *config.Config, db *gorm.DB, reg prometheus.Register
|
||||
batchOrm: orm.NewBatch(db),
|
||||
proverTaskOrm: orm.NewProverTask(db),
|
||||
},
|
||||
vk: vk,
|
||||
batchAttemptsExceedTotal: promauto.With(reg).NewCounter(prometheus.CounterOpts{
|
||||
Name: "coordinator_batch_attempts_exceed_total",
|
||||
Help: "Total number of batch attempts exceed.",
|
||||
@@ -68,8 +70,16 @@ func (bp *BatchProverTask) Assign(ctx *gin.Context, getTaskParameter *coordinato
|
||||
if !proverVersionExist {
|
||||
return nil, fmt.Errorf("get prover version from context failed")
|
||||
}
|
||||
if !version.CheckScrollProverVersion(proverVersion.(string)) {
|
||||
return nil, fmt.Errorf("incompatible prover version. please upgrade your prover, expect version: %s, actual version: %s", proverVersion.(string), version.Version)
|
||||
if getTaskParameter.VK == "" { // allow vk being empty, because for the first time the prover may not know its vk
|
||||
if !version.CheckScrollProverVersionTag(proverVersion.(string)) { // but reject too-old provers
|
||||
return nil, fmt.Errorf("incompatible prover version. please upgrade your prover, expect version: %s, actual version: %s", version.Version, proverVersion.(string))
|
||||
}
|
||||
} else if getTaskParameter.VK != bp.vk { // non-empty vk but different
|
||||
if version.CheckScrollProverVersion(proverVersion.(string)) { // same prover version but different vks
|
||||
return nil, fmt.Errorf("incompatible vk. please check your params files or config files")
|
||||
}
|
||||
// different prover versions and different vks
|
||||
return nil, fmt.Errorf("incompatible prover version. please upgrade your prover, expect version: %s, actual version: %s", version.Version, proverVersion.(string))
|
||||
}
|
||||
|
||||
isAssigned, err := bp.proverTaskOrm.IsProverAssigned(ctx, publicKey.(string))
|
||||
@@ -91,7 +101,8 @@ func (bp *BatchProverTask) Assign(ctx *gin.Context, getTaskParameter *coordinato
|
||||
}
|
||||
|
||||
if len(batchTasks) != 1 {
|
||||
return nil, fmt.Errorf("get unassigned batch proving task len not 1, batch tasks:%v", batchTasks)
|
||||
log.Error("get unassigned batch proving task len not 1", "length", len(batchTasks), "batch tasks", batchTasks)
|
||||
return nil, ErrCoordinatorInternalFailure
|
||||
}
|
||||
|
||||
batchTask := batchTasks[0]
|
||||
@@ -99,7 +110,9 @@ func (bp *BatchProverTask) Assign(ctx *gin.Context, getTaskParameter *coordinato
|
||||
|
||||
if !bp.checkAttemptsExceeded(batchTask.Hash, message.ProofTypeBatch) {
|
||||
bp.batchAttemptsExceedTotal.Inc()
|
||||
return nil, fmt.Errorf("the batch task id:%s check attempts have reach the maximum", batchTask.Hash)
|
||||
// TODO: retry fetching unassigned batch proving task
|
||||
log.Error("batch task proving attempts reach the maximum", "hash", batchTask.Hash)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
proverTask := orm.ProverTask{
|
||||
@@ -117,13 +130,15 @@ func (bp *BatchProverTask) Assign(ctx *gin.Context, getTaskParameter *coordinato
|
||||
// Store session info.
|
||||
if err = bp.proverTaskOrm.SetProverTask(ctx, &proverTask); err != nil {
|
||||
bp.recoverProvingStatus(ctx, batchTask)
|
||||
return nil, fmt.Errorf("db set session info fail, session id:%s, error:%w", proverTask.TaskID, err)
|
||||
log.Error("db set session info fail", "task hash", batchTask.Hash, "prover name", proverName.(string), "prover pubKey", publicKey.(string), "err", err)
|
||||
return nil, ErrCoordinatorInternalFailure
|
||||
}
|
||||
|
||||
taskMsg, err := bp.formatProverTask(ctx, batchTask.Hash)
|
||||
if err != nil {
|
||||
bp.recoverProvingStatus(ctx, batchTask)
|
||||
return nil, fmt.Errorf("format prover failure, id:%s error:%w", batchTask.Hash, err)
|
||||
log.Error("format prover task failure", "hash", batchTask.Hash, "err", err)
|
||||
return nil, ErrCoordinatorInternalFailure
|
||||
}
|
||||
|
||||
bp.batchTaskGetTaskTotal.Inc()
|
||||
|
||||
@@ -22,16 +22,20 @@ import (
|
||||
coordinatorType "scroll-tech/coordinator/internal/types"
|
||||
)
|
||||
|
||||
// ErrCoordinatorInternalFailure coordinator internal db failure
|
||||
var ErrCoordinatorInternalFailure = fmt.Errorf("coordinator internal error")
|
||||
|
||||
// ChunkProverTask the chunk prover task
|
||||
type ChunkProverTask struct {
|
||||
BaseProverTask
|
||||
vk string
|
||||
|
||||
chunkAttemptsExceedTotal prometheus.Counter
|
||||
chunkTaskGetTaskTotal prometheus.Counter
|
||||
}
|
||||
|
||||
// NewChunkProverTask new a chunk prover task
|
||||
func NewChunkProverTask(cfg *config.Config, db *gorm.DB, reg prometheus.Registerer) *ChunkProverTask {
|
||||
func NewChunkProverTask(cfg *config.Config, db *gorm.DB, vk string, reg prometheus.Registerer) *ChunkProverTask {
|
||||
cp := &ChunkProverTask{
|
||||
BaseProverTask: BaseProverTask{
|
||||
db: db,
|
||||
@@ -40,7 +44,7 @@ func NewChunkProverTask(cfg *config.Config, db *gorm.DB, reg prometheus.Register
|
||||
blockOrm: orm.NewL2Block(db),
|
||||
proverTaskOrm: orm.NewProverTask(db),
|
||||
},
|
||||
|
||||
vk: vk,
|
||||
chunkAttemptsExceedTotal: promauto.With(reg).NewCounter(prometheus.CounterOpts{
|
||||
Name: "coordinator_chunk_attempts_exceed_total",
|
||||
Help: "Total number of chunk attempts exceed.",
|
||||
@@ -69,7 +73,15 @@ func (cp *ChunkProverTask) Assign(ctx *gin.Context, getTaskParameter *coordinato
|
||||
if !proverVersionExist {
|
||||
return nil, fmt.Errorf("get prover version from context failed")
|
||||
}
|
||||
if !version.CheckScrollProverVersion(proverVersion.(string)) {
|
||||
if getTaskParameter.VK == "" { // allow vk being empty, because for the first time the prover may not know its vk
|
||||
if !version.CheckScrollProverVersionTag(proverVersion.(string)) { // but reject too-old provers
|
||||
return nil, fmt.Errorf("incompatible prover version. please upgrade your prover, expect version: %s, actual version: %s", version.Version, proverVersion.(string))
|
||||
}
|
||||
} else if getTaskParameter.VK != cp.vk { // non-empty vk but different
|
||||
if version.CheckScrollProverVersion(proverVersion.(string)) { // same prover version but different vks
|
||||
return nil, fmt.Errorf("incompatible vk. please check your params files or config files")
|
||||
}
|
||||
// different prover versions and different vks
|
||||
return nil, fmt.Errorf("incompatible prover version. please upgrade your prover, expect version: %s, actual version: %s", version.Version, proverVersion.(string))
|
||||
}
|
||||
|
||||
@@ -85,7 +97,8 @@ func (cp *ChunkProverTask) Assign(ctx *gin.Context, getTaskParameter *coordinato
|
||||
// load and send chunk tasks
|
||||
chunkTasks, err := cp.chunkOrm.UpdateUnassignedChunkReturning(ctx, getTaskParameter.ProverHeight, 1)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get unassigned chunk proving tasks, error:%w", err)
|
||||
log.Error("failed to get unassigned chunk proving tasks", "height", getTaskParameter.ProverHeight, "err", err)
|
||||
return nil, ErrCoordinatorInternalFailure
|
||||
}
|
||||
|
||||
if len(chunkTasks) == 0 {
|
||||
@@ -93,7 +106,8 @@ func (cp *ChunkProverTask) Assign(ctx *gin.Context, getTaskParameter *coordinato
|
||||
}
|
||||
|
||||
if len(chunkTasks) != 1 {
|
||||
return nil, fmt.Errorf("get unassigned chunk proving task len not 1, chunk tasks:%v", chunkTasks)
|
||||
log.Error("get unassigned chunk proving task len not 1", "length", len(chunkTasks), "chunk tasks", chunkTasks)
|
||||
return nil, ErrCoordinatorInternalFailure
|
||||
}
|
||||
|
||||
chunkTask := chunkTasks[0]
|
||||
@@ -102,7 +116,9 @@ func (cp *ChunkProverTask) Assign(ctx *gin.Context, getTaskParameter *coordinato
|
||||
|
||||
if !cp.checkAttemptsExceeded(chunkTask.Hash, message.ProofTypeChunk) {
|
||||
cp.chunkAttemptsExceedTotal.Inc()
|
||||
return nil, fmt.Errorf("chunk proof hash id:%s check attempts have reach the maximum", chunkTask.Hash)
|
||||
// TODO: retry fetching unassigned chunk proving task
|
||||
log.Error("chunk task proving attempts reach the maximum", "hash", chunkTask.Hash)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
proverTask := orm.ProverTask{
|
||||
@@ -118,13 +134,15 @@ func (cp *ChunkProverTask) Assign(ctx *gin.Context, getTaskParameter *coordinato
|
||||
}
|
||||
if err = cp.proverTaskOrm.SetProverTask(ctx, &proverTask); err != nil {
|
||||
cp.recoverProvingStatus(ctx, chunkTask)
|
||||
return nil, fmt.Errorf("db set session info fail, session id:%s , public key:%s, err:%w", chunkTask.Hash, publicKey, err)
|
||||
log.Error("db set session info fail", "task hash", chunkTask.Hash, "prover name", proverName.(string), "prover pubKey", publicKey.(string), "err", err)
|
||||
return nil, ErrCoordinatorInternalFailure
|
||||
}
|
||||
|
||||
taskMsg, err := cp.formatProverTask(ctx, chunkTask.Hash)
|
||||
if err != nil {
|
||||
cp.recoverProvingStatus(ctx, chunkTask)
|
||||
return nil, fmt.Errorf("format prover task failure, id:%s error:%w", chunkTask.Hash, err)
|
||||
log.Error("format prover task failure", "hash", chunkTask.Hash, "err", err)
|
||||
return nil, ErrCoordinatorInternalFailure
|
||||
}
|
||||
|
||||
cp.chunkTaskGetTaskTotal.Inc()
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -33,6 +34,12 @@ var (
|
||||
ErrValidatorFailureProofTimeout = errors.New("validator failure submit proof timeout")
|
||||
// ErrValidatorFailureTaskHaveVerifiedSuccess have proved success and verified success
|
||||
ErrValidatorFailureTaskHaveVerifiedSuccess = errors.New("validator failure chunk/batch have proved and verified success")
|
||||
// ErrValidatorFailureVerifiedFailed failed to verify and the verifier returns error
|
||||
ErrValidatorFailureVerifiedFailed = fmt.Errorf("verification failed, verifier returns error")
|
||||
// ErrValidatorSuccessInvalidProof successful verified and the proof is invalid
|
||||
ErrValidatorSuccessInvalidProof = fmt.Errorf("verification succeeded, it's an invalid proof")
|
||||
// ErrCoordinatorInternalFailure coordinator internal db failure
|
||||
ErrCoordinatorInternalFailure = fmt.Errorf("coordinator internal error")
|
||||
)
|
||||
|
||||
// ProofReceiverLogic the proof receiver logic
|
||||
@@ -59,11 +66,7 @@ type ProofReceiverLogic struct {
|
||||
}
|
||||
|
||||
// NewSubmitProofReceiverLogic create a proof receiver logic
|
||||
func NewSubmitProofReceiverLogic(cfg *config.ProverManager, db *gorm.DB, reg prometheus.Registerer) *ProofReceiverLogic {
|
||||
vf, err := verifier.NewVerifier(cfg.Verifier)
|
||||
if err != nil {
|
||||
panic("proof receiver new verifier failure")
|
||||
}
|
||||
func NewSubmitProofReceiverLogic(cfg *config.ProverManager, db *gorm.DB, vf *verifier.Verifier, reg prometheus.Registerer) *ProofReceiverLogic {
|
||||
return &ProofReceiverLogic{
|
||||
chunkOrm: orm.NewChunk(db),
|
||||
batchOrm: orm.NewBatch(db),
|
||||
@@ -128,11 +131,11 @@ func (m *ProofReceiverLogic) HandleZkProof(ctx *gin.Context, proofMsg *message.P
|
||||
return fmt.Errorf("get public key from context failed")
|
||||
}
|
||||
pv := ctx.GetString(coordinatorType.ProverVersion)
|
||||
if len(pk) == 0 {
|
||||
if len(pv) == 0 {
|
||||
return fmt.Errorf("get ProverVersion from context failed")
|
||||
}
|
||||
|
||||
proverTask, err := m.proverTaskOrm.GetProverTaskByTaskIDAndProver(ctx, proofMsg.ID, pk, pv)
|
||||
proverTask, err := m.proverTaskOrm.GetProverTaskByTaskIDAndProver(ctx, proofMsg.Type, proofMsg.ID, pk, pv)
|
||||
if proverTask == nil || err != nil {
|
||||
log.Error("get none prover task for the proof", "key", pk, "taskID", proofMsg.ID, "error", err)
|
||||
return ErrValidatorFailureProverTaskEmpty
|
||||
@@ -148,8 +151,7 @@ func (m *ProofReceiverLogic) HandleZkProof(ctx *gin.Context, proofMsg *message.P
|
||||
return err
|
||||
}
|
||||
|
||||
proverVersion := ctx.GetString(coordinatorType.ProverVersion)
|
||||
m.verifierTotal.WithLabelValues(proverVersion).Inc()
|
||||
m.verifierTotal.WithLabelValues(pv).Inc()
|
||||
|
||||
var success bool
|
||||
var verifyErr error
|
||||
@@ -160,16 +162,16 @@ func (m *ProofReceiverLogic) HandleZkProof(ctx *gin.Context, proofMsg *message.P
|
||||
}
|
||||
|
||||
if verifyErr != nil || !success {
|
||||
m.verifierFailureTotal.WithLabelValues(proverVersion).Inc()
|
||||
m.verifierFailureTotal.WithLabelValues(pv).Inc()
|
||||
m.proofRecover(ctx, proofMsg.ID, pk, proofMsg)
|
||||
|
||||
log.Info("proof verified by coordinator failed", "proof id", proofMsg.ID, "prover name", proverTask.ProverName,
|
||||
"prover pk", pk, "prove type", proofMsg.Type, "proof time", proofTimeSec, "error", verifyErr)
|
||||
|
||||
if verifyErr == nil {
|
||||
verifyErr = fmt.Errorf("verification succeeded and it's an invalid proof")
|
||||
if verifyErr != nil {
|
||||
return ErrValidatorFailureVerifiedFailed
|
||||
}
|
||||
return verifyErr
|
||||
return ErrValidatorSuccessInvalidProof
|
||||
}
|
||||
|
||||
m.proverTaskProveDuration.Observe(time.Since(proverTask.CreatedAt).Seconds())
|
||||
@@ -180,7 +182,7 @@ func (m *ProofReceiverLogic) HandleZkProof(ctx *gin.Context, proofMsg *message.P
|
||||
if err := m.closeProofTask(ctx, proofMsg.ID, pk, proofMsg, proofTimeSec); err != nil {
|
||||
m.proofSubmitFailure.Inc()
|
||||
m.proofRecover(ctx, proofMsg.ID, pk, proofMsg)
|
||||
return err
|
||||
return ErrCoordinatorInternalFailure
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -232,12 +234,19 @@ func (m *ProofReceiverLogic) validator(ctx context.Context, proverTask *orm.Prov
|
||||
proofTimeSec := uint64(proofTime.Seconds())
|
||||
|
||||
if proofMsg.Status != message.StatusOk {
|
||||
m.proofRecover(ctx, proofMsg.ID, pk, proofMsg)
|
||||
// Temporarily replace "panic" with "pa-nic" to prevent triggering the alert based on logs.
|
||||
failureMsg := strings.Replace(proofParameter.FailureMsg, "panic", "pa-nic", -1)
|
||||
// Verify if the proving task has already been assigned to another prover.
|
||||
// Upon receiving an error message, it's possible the proving status has been reset by another prover
|
||||
// and the task has been reassigned. In this case, the coordinator should avoid resetting the proving status.
|
||||
m.processProverErr(ctx, proofMsg.ID, pk, proofMsg.Type)
|
||||
|
||||
m.validateFailureProverTaskStatusNotOk.Inc()
|
||||
|
||||
log.Info("proof generated by prover failed",
|
||||
"taskType", proofMsg.Type, "hash", proofMsg.ID,
|
||||
"proverName", proverTask.ProverName, "proverVersion", proverTask.ProverVersion,
|
||||
"proverPublicKey", pk, "failureType", proofParameter.FailureType, "failureMessage", proofParameter.FailureMsg)
|
||||
"taskType", proofMsg.Type, "hash", proofMsg.ID, "proverName", proverTask.ProverName,
|
||||
"proverVersion", proverTask.ProverVersion, "proverPublicKey", pk, "failureType", proofParameter.FailureType,
|
||||
"failureMessage", "failureMessage", failureMsg)
|
||||
return ErrValidatorFailureProofMsgStatusNotOk
|
||||
}
|
||||
|
||||
@@ -265,15 +274,6 @@ func (m *ProofReceiverLogic) validator(ctx context.Context, proverTask *orm.Prov
|
||||
return nil
|
||||
}
|
||||
|
||||
//func (m *ProofReceiverLogic) proofFailure(ctx context.Context, hash string, pubKey string, proofMsg *message.ProofMsg) {
|
||||
// log.Info("proof failure update proof status", "hash", hash, "public key", pubKey,
|
||||
// "proof type", proofMsg.Type.String(), "status", types.ProvingTaskFailed.String())
|
||||
//
|
||||
// if err := m.updateProofStatus(ctx, hash, pubKey, proofMsg, types.ProvingTaskFailed, 0); err != nil {
|
||||
// log.Error("failed to updated proof status ProvingTaskFailed", "hash", hash, "pubKey", pubKey, "error", err)
|
||||
// }
|
||||
//}
|
||||
|
||||
func (m *ProofReceiverLogic) proofRecover(ctx context.Context, hash string, pubKey string, proofMsg *message.ProofMsg) {
|
||||
log.Info("proof recover update proof status", "hash", hash, "proverPublicKey", pubKey,
|
||||
"taskType", proofMsg.Type.String(), "status", types.ProvingTaskUnassigned.String())
|
||||
@@ -310,8 +310,8 @@ func (m *ProofReceiverLogic) updateProofStatus(ctx context.Context, hash string,
|
||||
}
|
||||
|
||||
// if the block batch has proof verified, so the failed status not update block batch proving status
|
||||
if status == types.ProvingTaskFailed && m.checkIsTaskSuccess(ctx, hash, proofMsg.Type) {
|
||||
log.Info("update proof status ProvingTaskFailed skip because other prover have prove success", "hash", hash, "public key", proverPublicKey)
|
||||
if m.checkIsTaskSuccess(ctx, hash, proofMsg.Type) {
|
||||
log.Info("update proof status skip because this chunk / batch has been verified", "hash", hash, "public key", proverPublicKey)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -378,6 +378,33 @@ func (m *ProofReceiverLogic) checkIsTaskSuccess(ctx context.Context, hash string
|
||||
return provingStatus == types.ProvingTaskVerified
|
||||
}
|
||||
|
||||
func (m *ProofReceiverLogic) processProverErr(ctx context.Context, taskID, pk string, taskType message.ProofType) {
|
||||
if updateErr := m.proverTaskOrm.UpdateProverTaskProvingStatus(ctx, taskType, taskID, pk, types.ProverProofInvalid); updateErr != nil {
|
||||
log.Error("update prover task proving status failure", "taskID", taskID, "proverPublicKey", pk, "taskType", taskType, "error", updateErr)
|
||||
}
|
||||
|
||||
proverTasks, err := m.proverTaskOrm.GetAssignedTaskOfOtherProvers(ctx, taskType, taskID, pk)
|
||||
if err != nil {
|
||||
log.Warn("checkIsAssignedToOtherProver failure", "taskID", taskID, "proverPublicKey", pk, "taskType", taskType, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(proverTasks) > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
switch taskType {
|
||||
case message.ProofTypeChunk:
|
||||
if err := m.chunkOrm.UpdateProvingStatusFromProverError(ctx, taskID, types.ProvingTaskUnassigned); err != nil {
|
||||
log.Error("failed to update chunk proving_status as failed", taskID, "proverPublicKey", pk, "taskType", taskType, "error", err)
|
||||
}
|
||||
case message.ProofTypeBatch:
|
||||
if err := m.batchOrm.UpdateProvingStatusFromProverError(ctx, taskID, types.ProvingTaskUnassigned); err != nil {
|
||||
log.Error("failed to update batch proving_status as failed", taskID, "proverPublicKey", pk, "taskType", taskType, "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *ProofReceiverLogic) updateProverTaskProof(ctx context.Context, pk string, proofMsg *message.ProofMsg) error {
|
||||
// store the proof to prover task
|
||||
var proofBytes []byte
|
||||
|
||||
@@ -8,11 +8,6 @@ import (
|
||||
"scroll-tech/coordinator/internal/config"
|
||||
)
|
||||
|
||||
const InvalidTestProof = "this is a invalid proof"
|
||||
|
||||
// Verifier represents a mock halo2 verifier.
|
||||
type Verifier struct{}
|
||||
|
||||
// NewVerifier Sets up a mock verifier.
|
||||
func NewVerifier(_ *config.VerifierConfig) (*Verifier, error) {
|
||||
return &Verifier{}, nil
|
||||
|
||||
15
coordinator/internal/logic/verifier/types.go
Normal file
15
coordinator/internal/logic/verifier/types.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package verifier
|
||||
|
||||
import (
|
||||
"scroll-tech/coordinator/internal/config"
|
||||
)
|
||||
|
||||
// InvalidTestProof invalid proof used in tests
|
||||
const InvalidTestProof = "this is a invalid proof"
|
||||
|
||||
// Verifier represents a rust ffi to a halo2 verifier.
|
||||
type Verifier struct {
|
||||
cfg *config.VerifierConfig
|
||||
BatchVK string
|
||||
ChunkVK string
|
||||
}
|
||||
@@ -11,7 +11,11 @@ package verifier
|
||||
import "C" //nolint:typecheck
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"unsafe"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/log"
|
||||
@@ -21,14 +25,6 @@ import (
|
||||
"scroll-tech/common/types/message"
|
||||
)
|
||||
|
||||
// InvalidTestProof invalid proof used in tests
|
||||
const InvalidTestProof = "this is a invalid proof"
|
||||
|
||||
// Verifier represents a rust ffi to a halo2 verifier.
|
||||
type Verifier struct {
|
||||
cfg *config.VerifierConfig
|
||||
}
|
||||
|
||||
// NewVerifier Sets up a rust ffi to call verify.
|
||||
func NewVerifier(cfg *config.VerifierConfig) (*Verifier, error) {
|
||||
if cfg.MockMode {
|
||||
@@ -44,7 +40,21 @@ func NewVerifier(cfg *config.VerifierConfig) (*Verifier, error) {
|
||||
C.init_batch_verifier(paramsPathStr, assetsPathStr)
|
||||
C.init_chunk_verifier(paramsPathStr, assetsPathStr)
|
||||
|
||||
return &Verifier{cfg: cfg}, nil
|
||||
batchVK, err := readVK(path.Join(cfg.AssetsPath, "agg_vk.vkey"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
chunkVK, err := readVK(path.Join(cfg.AssetsPath, "chunk_vk.vkey"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Verifier{
|
||||
cfg: cfg,
|
||||
BatchVK: batchVK,
|
||||
ChunkVK: chunkVK,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// VerifyBatchProof Verify a ZkProof by marshaling it and sending it to the Halo2 Verifier.
|
||||
@@ -96,3 +106,15 @@ func (v *Verifier) VerifyChunkProof(proof *message.ChunkProof) (bool, error) {
|
||||
verified := C.verify_chunk_proof(proofStr)
|
||||
return verified != 0, nil
|
||||
}
|
||||
|
||||
func readVK(filePat string) (string, error) {
|
||||
f, err := os.Open(filePat)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
byt, err := io.ReadAll(f)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(byt), nil
|
||||
}
|
||||
|
||||
@@ -91,11 +91,31 @@ func (o *Batch) GetUnassignedBatches(ctx context.Context, limit int) ([]*Batch,
|
||||
return batches, nil
|
||||
}
|
||||
|
||||
// GetAssignedBatches retrieves all batches whose proving_status is either types.ProvingTaskAssigned or types.ProvingTaskProved.
|
||||
// GetUnassignedAndChunksUnreadyBatches get the batches which is unassigned and chunks is not ready
|
||||
func (o *Batch) GetUnassignedAndChunksUnreadyBatches(ctx context.Context, offset, limit int) ([]*Batch, error) {
|
||||
if offset < 0 || limit < 0 {
|
||||
return nil, errors.New("limit and offset must not be smaller than 0")
|
||||
}
|
||||
|
||||
db := o.db.WithContext(ctx)
|
||||
db = db.Where("proving_status = ?", types.ProvingTaskUnassigned)
|
||||
db = db.Where("chunk_proofs_status = ?", types.ChunkProofsStatusPending)
|
||||
db = db.Order("index ASC")
|
||||
db = db.Offset(offset)
|
||||
db = db.Limit(limit)
|
||||
|
||||
var batches []*Batch
|
||||
if err := db.Find(&batches).Error; err != nil {
|
||||
return nil, fmt.Errorf("Batch.GetUnassignedAndChunksUnreadyBatches error: %w", err)
|
||||
}
|
||||
return batches, nil
|
||||
}
|
||||
|
||||
// GetAssignedBatches retrieves all batches whose proving_status is either types.ProvingTaskAssigned.
|
||||
func (o *Batch) GetAssignedBatches(ctx context.Context) ([]*Batch, error) {
|
||||
db := o.db.WithContext(ctx)
|
||||
db = db.Model(&Batch{})
|
||||
db = db.Where("proving_status IN (?)", []int{int(types.ProvingTaskAssigned), int(types.ProvingTaskProved)})
|
||||
db = db.Where("proving_status = ?", int(types.ProvingTaskAssigned))
|
||||
|
||||
var assignedBatches []*Batch
|
||||
if err := db.Find(&assignedBatches).Error; err != nil {
|
||||
@@ -236,7 +256,7 @@ func (o *Batch) UpdateProvingStatus(ctx context.Context, hash string, status typ
|
||||
updateFields["prover_assigned_at"] = time.Now()
|
||||
case types.ProvingTaskUnassigned:
|
||||
updateFields["prover_assigned_at"] = nil
|
||||
case types.ProvingTaskProved, types.ProvingTaskVerified:
|
||||
case types.ProvingTaskVerified:
|
||||
updateFields["proved_at"] = time.Now()
|
||||
}
|
||||
|
||||
@@ -250,6 +270,30 @@ func (o *Batch) UpdateProvingStatus(ctx context.Context, hash string, status typ
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateProvingStatusFromProverError updates batch proving status when prover prove failed
|
||||
func (o *Batch) UpdateProvingStatusFromProverError(ctx context.Context, 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.ProvingTaskVerified:
|
||||
updateFields["proved_at"] = time.Now()
|
||||
}
|
||||
|
||||
db := o.db.WithContext(ctx)
|
||||
db = db.Model(&Batch{})
|
||||
db = db.Where("hash", hash).Where("proving_status", types.ProvingTaskAssigned)
|
||||
|
||||
if err := db.Updates(updateFields).Error; err != nil {
|
||||
return fmt.Errorf("Batch.UpdateProvingStatusOptimistic error: %w, batch hash: %v, status: %v", err, hash, status.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateProofByHash updates the batch proof by hash.
|
||||
func (o *Batch) UpdateProofByHash(ctx context.Context, hash string, proof *message.BatchProof, proofTimeSec uint64, dbTX ...*gorm.DB) error {
|
||||
db := o.db
|
||||
|
||||
@@ -155,11 +155,11 @@ func (o *Chunk) GetProvingStatusByHash(ctx context.Context, hash string) (types.
|
||||
return types.ProvingStatus(chunk.ProvingStatus), nil
|
||||
}
|
||||
|
||||
// GetAssignedChunks retrieves all chunks whose proving_status is either types.ProvingTaskAssigned or types.ProvingTaskProved.
|
||||
// GetAssignedChunks retrieves all chunks whose proving_status is either types.ProvingTaskAssigned.
|
||||
func (o *Chunk) GetAssignedChunks(ctx context.Context) ([]*Chunk, error) {
|
||||
db := o.db.WithContext(ctx)
|
||||
db = db.Model(&Chunk{})
|
||||
db = db.Where("proving_status IN (?)", []int{int(types.ProvingTaskAssigned), int(types.ProvingTaskProved)})
|
||||
db = db.Where("proving_status = ?", int(types.ProvingTaskAssigned))
|
||||
|
||||
var chunks []*Chunk
|
||||
if err := db.Find(&chunks).Error; err != nil {
|
||||
@@ -285,7 +285,7 @@ func (o *Chunk) UpdateProvingStatus(ctx context.Context, hash string, status typ
|
||||
updateFields["prover_assigned_at"] = time.Now()
|
||||
case types.ProvingTaskUnassigned:
|
||||
updateFields["prover_assigned_at"] = nil
|
||||
case types.ProvingTaskProved, types.ProvingTaskVerified:
|
||||
case types.ProvingTaskVerified:
|
||||
updateFields["proved_at"] = time.Now()
|
||||
}
|
||||
db := o.db
|
||||
@@ -302,6 +302,29 @@ func (o *Chunk) UpdateProvingStatus(ctx context.Context, hash string, status typ
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateProvingStatusFromProverError updates chunk proving status when prover prove failed
|
||||
func (o *Chunk) UpdateProvingStatusFromProverError(ctx context.Context, 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.ProvingTaskVerified:
|
||||
updateFields["proved_at"] = time.Now()
|
||||
}
|
||||
db := o.db.WithContext(ctx)
|
||||
db = db.Model(&Chunk{})
|
||||
db = db.Where("hash", hash).Where("proving_status", types.ProvingTaskAssigned)
|
||||
|
||||
if err := db.Updates(updateFields).Error; err != nil {
|
||||
return fmt.Errorf("Chunk.UpdateProvingStatusOptimistic error: %w, chunk hash: %v, status: %v", err, hash, status.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateProofByHash updates the chunk proof by hash.
|
||||
func (o *Chunk) UpdateProofByHash(ctx context.Context, hash string, proof *message.ChunkProof, proofTimeSec uint64, dbTX ...*gorm.DB) error {
|
||||
db := o.db
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"scroll-tech/common/database"
|
||||
"scroll-tech/common/docker"
|
||||
"scroll-tech/common/types"
|
||||
"scroll-tech/common/types/message"
|
||||
"scroll-tech/common/utils"
|
||||
|
||||
"scroll-tech/database/migrate"
|
||||
@@ -67,6 +68,7 @@ func TestProverTaskOrm(t *testing.T) {
|
||||
reward.SetString("18446744073709551616", 10) // 1 << 64, uint64 maximum 1<<64 -1
|
||||
|
||||
proverTask := ProverTask{
|
||||
TaskType: int16(message.ProofTypeChunk),
|
||||
TaskID: "test-hash",
|
||||
ProverName: "prover-0",
|
||||
ProverPublicKey: "0",
|
||||
@@ -77,7 +79,7 @@ func TestProverTaskOrm(t *testing.T) {
|
||||
|
||||
err = proverTaskOrm.SetProverTask(context.Background(), &proverTask)
|
||||
assert.NoError(t, err)
|
||||
proverTasks, err := proverTaskOrm.GetProverTasksByHashes(context.Background(), []string{"test-hash"})
|
||||
proverTasks, err := proverTaskOrm.GetProverTasksByHashes(context.Background(), message.ProofTypeChunk, []string{"test-hash"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(proverTasks))
|
||||
assert.Equal(t, proverTask.ProverName, proverTasks[0].ProverName)
|
||||
@@ -91,7 +93,7 @@ func TestProverTaskOrm(t *testing.T) {
|
||||
proverTask.AssignedAt = utils.NowUTC()
|
||||
err = proverTaskOrm.SetProverTask(context.Background(), &proverTask)
|
||||
assert.NoError(t, err)
|
||||
proverTasks, err = proverTaskOrm.GetProverTasksByHashes(context.Background(), []string{"test-hash"})
|
||||
proverTasks, err = proverTaskOrm.GetProverTasksByHashes(context.Background(), message.ProofTypeChunk, []string{"test-hash"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(proverTasks))
|
||||
assert.Equal(t, proverTask.ProvingStatus, proverTasks[0].ProvingStatus)
|
||||
@@ -106,6 +108,7 @@ func TestProverTaskOrmUint256(t *testing.T) {
|
||||
rewardUint256 := big.NewInt(0)
|
||||
rewardUint256.SetString("115792089237316195423570985008687907853269984665640564039457584007913129639935", 10)
|
||||
proverTask := ProverTask{
|
||||
TaskType: int16(message.ProofTypeChunk),
|
||||
TaskID: "test-hash",
|
||||
ProverName: "prover-0",
|
||||
ProverPublicKey: "0",
|
||||
@@ -116,7 +119,7 @@ func TestProverTaskOrmUint256(t *testing.T) {
|
||||
|
||||
err = proverTaskOrm.SetProverTask(context.Background(), &proverTask)
|
||||
assert.NoError(t, err)
|
||||
proverTasksUint256, err := proverTaskOrm.GetProverTasksByHashes(context.Background(), []string{"test-hash"})
|
||||
proverTasksUint256, err := proverTaskOrm.GetProverTasksByHashes(context.Background(), message.ProofTypeChunk, []string{"test-hash"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(proverTasksUint256))
|
||||
resultRewardUint256 := proverTasksUint256[0].Reward.BigInt()
|
||||
|
||||
@@ -96,13 +96,14 @@ func (o *ProverTask) GetProverTasks(ctx context.Context, fields map[string]inter
|
||||
|
||||
// GetProverTasksByHashes retrieves the ProverTask records associated with the specified hashes.
|
||||
// The returned prover task objects are sorted in ascending order by their ids.
|
||||
func (o *ProverTask) GetProverTasksByHashes(ctx context.Context, hashes []string) ([]*ProverTask, error) {
|
||||
func (o *ProverTask) GetProverTasksByHashes(ctx context.Context, taskType message.ProofType, hashes []string) ([]*ProverTask, error) {
|
||||
if len(hashes) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
db := o.db.WithContext(ctx)
|
||||
db = db.Model(&ProverTask{})
|
||||
db = db.Where("task_type", int(taskType))
|
||||
db = db.Where("task_id IN ?", hashes)
|
||||
db = db.Order("id asc")
|
||||
|
||||
@@ -114,9 +115,10 @@ func (o *ProverTask) GetProverTasksByHashes(ctx context.Context, hashes []string
|
||||
}
|
||||
|
||||
// GetProverTaskByTaskIDAndProver get prover task taskID and public key
|
||||
func (o *ProverTask) GetProverTaskByTaskIDAndProver(ctx context.Context, taskID, proverPublicKey, proverVersion string) (*ProverTask, error) {
|
||||
func (o *ProverTask) GetProverTaskByTaskIDAndProver(ctx context.Context, taskType message.ProofType, taskID, proverPublicKey, proverVersion string) (*ProverTask, error) {
|
||||
db := o.db.WithContext(ctx)
|
||||
db = db.Model(&ProverTask{})
|
||||
db = db.Where("task_type", int(taskType))
|
||||
db = db.Where("task_id", taskID)
|
||||
db = db.Where("prover_public_key", proverPublicKey)
|
||||
db = db.Where("prover_version", proverVersion)
|
||||
@@ -129,11 +131,28 @@ func (o *ProverTask) GetProverTaskByTaskIDAndProver(ctx context.Context, taskID,
|
||||
return &proverTask, nil
|
||||
}
|
||||
|
||||
// GetAssignedTaskOfOtherProvers get the chunk/batch task assigned other provers
|
||||
func (o *ProverTask) GetAssignedTaskOfOtherProvers(ctx context.Context, taskType message.ProofType, taskID, proverPublicKey string) ([]ProverTask, error) {
|
||||
db := o.db.WithContext(ctx)
|
||||
db = db.Model(&ProverTask{})
|
||||
db = db.Where("task_type", int(taskType))
|
||||
db = db.Where("task_id", taskID)
|
||||
db = db.Where("prover_public_key != ?", proverPublicKey)
|
||||
db = db.Where("proving_status = ?", int(types.ProverAssigned))
|
||||
|
||||
var proverTasks []ProverTask
|
||||
if err := db.Find(&proverTasks).Error; err != nil {
|
||||
return nil, fmt.Errorf("ProverTask.GetAssignedProverTask error: %w, taskID: %v", err, taskID)
|
||||
}
|
||||
return proverTasks, nil
|
||||
}
|
||||
|
||||
// GetProvingStatusByTaskID retrieves the proving status of a prover task
|
||||
func (o *ProverTask) GetProvingStatusByTaskID(ctx context.Context, taskID string) (types.ProverProveStatus, error) {
|
||||
func (o *ProverTask) GetProvingStatusByTaskID(ctx context.Context, taskType message.ProofType, taskID string) (types.ProverProveStatus, error) {
|
||||
db := o.db.WithContext(ctx)
|
||||
db = db.Model(&ProverTask{})
|
||||
db = db.Select("proving_status")
|
||||
db = db.Where("task_type", int(taskType))
|
||||
db = db.Where("task_id = ?", taskID)
|
||||
|
||||
var proverTask ProverTask
|
||||
@@ -161,9 +180,10 @@ func (o *ProverTask) GetTimeoutAssignedProverTasks(ctx context.Context, limit in
|
||||
}
|
||||
|
||||
// TaskTimeoutMoreThanOnce get the timeout twice task. a temp design
|
||||
func (o *ProverTask) TaskTimeoutMoreThanOnce(ctx context.Context, taskID string) bool {
|
||||
func (o *ProverTask) TaskTimeoutMoreThanOnce(ctx context.Context, taskType message.ProofType, taskID string) bool {
|
||||
db := o.db.WithContext(ctx)
|
||||
db = db.Model(&ProverTask{})
|
||||
db = db.Where("task_type", int(taskType))
|
||||
db = db.Where("task_id", taskID)
|
||||
db = db.Where("proving_status", int(types.ProverProofInvalid))
|
||||
|
||||
|
||||
@@ -2,8 +2,9 @@ package types
|
||||
|
||||
// GetTaskParameter for ProverTasks request parameter
|
||||
type GetTaskParameter struct {
|
||||
ProverHeight int `form:"prover_height" json:"prover_height"`
|
||||
TaskType int `form:"task_type" json:"task_type"`
|
||||
ProverHeight int `form:"prover_height" json:"prover_height"`
|
||||
TaskType int `form:"task_type" json:"task_type"`
|
||||
VK string `form:"vk" json:"vk"`
|
||||
}
|
||||
|
||||
// GetTaskSchema the schema data return to prover for get prover task
|
||||
|
||||
@@ -110,7 +110,7 @@ func setupCoordinator(t *testing.T, proversPerSession uint8, coordinatorURL stri
|
||||
}
|
||||
|
||||
func setEnv(t *testing.T) {
|
||||
version.Version = "v1.2.3-aaa-bbb-ccc"
|
||||
version.Version = "v4.1.98-aaa-bbb-ccc"
|
||||
|
||||
base = docker.NewDockerApp()
|
||||
base.RunDBImage(t)
|
||||
@@ -386,9 +386,9 @@ func testProofGeneratedFailed(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
chunkProverTaskProvingStatus, err = proverTaskOrm.GetProvingStatusByTaskID(context.Background(), dbChunk.Hash)
|
||||
chunkProverTaskProvingStatus, err = proverTaskOrm.GetProvingStatusByTaskID(context.Background(), message.ProofTypeChunk, dbChunk.Hash)
|
||||
assert.NoError(t, err)
|
||||
batchProverTaskProvingStatus, err = proverTaskOrm.GetProvingStatusByTaskID(context.Background(), batch.Hash)
|
||||
batchProverTaskProvingStatus, err = proverTaskOrm.GetProvingStatusByTaskID(context.Background(), message.ProofTypeBatch, batch.Hash)
|
||||
assert.NoError(t, err)
|
||||
if chunkProverTaskProvingStatus == types.ProverProofInvalid && batchProverTaskProvingStatus == types.ProverProofInvalid {
|
||||
return
|
||||
|
||||
@@ -63,7 +63,7 @@ func testResetDB(t *testing.T) {
|
||||
cur, err := Current(pgDB.DB)
|
||||
assert.NoError(t, err)
|
||||
// total number of tables.
|
||||
assert.Equal(t, 7, int(cur))
|
||||
assert.Equal(t, 8, int(cur))
|
||||
}
|
||||
|
||||
func testMigrate(t *testing.T) {
|
||||
|
||||
@@ -39,7 +39,7 @@ create table chunk
|
||||
);
|
||||
|
||||
comment
|
||||
on column chunk.proving_status is 'undefined, unassigned, assigned, proved, verified, failed';
|
||||
on column chunk.proving_status is 'undefined, unassigned, assigned, proved (deprecated), verified, failed';
|
||||
|
||||
create unique index chunk_index_uindex
|
||||
on chunk (index) where deleted_at IS NULL;
|
||||
|
||||
@@ -50,7 +50,7 @@ comment
|
||||
on column batch.chunk_proofs_status is 'undefined, pending, ready';
|
||||
|
||||
comment
|
||||
on column batch.proving_status is 'undefined, unassigned, assigned, proved, verified, failed';
|
||||
on column batch.proving_status is 'undefined, unassigned, assigned, proved (deprecated), verified, failed';
|
||||
|
||||
comment
|
||||
on column batch.rollup_status is 'undefined, pending, committing, committed, finalizing, finalized, commit_failed, finalize_failed';
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
drop index l1_message_hash_uindex;
|
||||
drop index if exists l1_message_hash_uindex;
|
||||
|
||||
create index l1_message_hash_index
|
||||
create index if not exists l1_message_hash_index
|
||||
on l1_message (msg_hash) where deleted_at IS NULL;
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
drop index l1_message_hash_index;
|
||||
drop index if exists l1_message_hash_index;
|
||||
|
||||
create unique index l1_message_hash_uindex
|
||||
create unique index if not exists l1_message_hash_uindex
|
||||
on l1_message (msg_hash) where deleted_at IS NULL;
|
||||
-- +goose StatementEnd
|
||||
|
||||
@@ -37,8 +37,11 @@ func NewCoordinatorClient(cfg *config.CoordinatorConfig, proverName string, priv
|
||||
SetBaseURL(cfg.BaseURL).
|
||||
AddRetryCondition(func(r *resty.Response, _ error) bool {
|
||||
// Check for HTTP 5xx errors, e.g., coordinator is restarting.
|
||||
log.Warn("Received unexpected HTTP response. Retrying...", "status code", r.StatusCode())
|
||||
return r.StatusCode() >= http.StatusInternalServerError
|
||||
if r.StatusCode() >= http.StatusInternalServerError {
|
||||
log.Warn("Received unexpected HTTP response. Retrying...", "status code", r.StatusCode())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
log.Info("successfully initialized prover client",
|
||||
@@ -176,23 +179,24 @@ func (c *CoordinatorClient) SubmitProof(ctx context.Context, req *SubmitProofReq
|
||||
|
||||
if err != nil {
|
||||
log.Error("submit proof request failed: %v", err)
|
||||
return fmt.Errorf("submit proof request failed: %w", ConnectErr)
|
||||
return fmt.Errorf("submit proof request failed: %w", ErrCoordinatorConnect)
|
||||
}
|
||||
|
||||
if resp.StatusCode() != 200 {
|
||||
log.Error("failed to submit proof, status code: %v", resp.StatusCode())
|
||||
return fmt.Errorf("failed to submit proof, status code not 200: %w", ConnectErr)
|
||||
return fmt.Errorf("failed to submit proof, status code not 200: %w", ErrCoordinatorConnect)
|
||||
}
|
||||
|
||||
if result.ErrCode == types.ErrJWTTokenExpired {
|
||||
log.Info("JWT expired, attempting to re-login")
|
||||
if err := c.Login(ctx); err != nil {
|
||||
log.Error("JWT expired, re-login failed: %v", err)
|
||||
return fmt.Errorf("JWT expired, re-login failed: %w", ConnectErr)
|
||||
return fmt.Errorf("JWT expired, re-login failed: %w", ErrCoordinatorConnect)
|
||||
}
|
||||
log.Info("re-login success")
|
||||
return c.SubmitProof(ctx, req)
|
||||
}
|
||||
|
||||
if result.ErrCode != types.Success {
|
||||
return fmt.Errorf("error code: %v, error message: %v", result.ErrCode, result.ErrMsg)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@ import (
|
||||
"scroll-tech/common/types/message"
|
||||
)
|
||||
|
||||
var ConnectErr = errors.New("connect coordinator error")
|
||||
// ErrCoordinatorConnect connect to coordinator error
|
||||
var ErrCoordinatorConnect = errors.New("connect coordinator error")
|
||||
|
||||
// ChallengeResponse defines the response structure for random API
|
||||
type ChallengeResponse struct {
|
||||
@@ -42,6 +43,7 @@ type LoginResponse struct {
|
||||
type GetTaskRequest struct {
|
||||
TaskType message.ProofType `json:"task_type"`
|
||||
ProverHeight uint64 `json:"prover_height,omitempty"`
|
||||
VK string `json:"vk"`
|
||||
}
|
||||
|
||||
// GetTaskResponse defines the response structure for GetTask API
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"db_path": "unique-db-path-for-prover-1",
|
||||
"core": {
|
||||
"params_path": "params",
|
||||
"assets_path": "assets",
|
||||
"proof_type": 2
|
||||
},
|
||||
"coordinator": {
|
||||
|
||||
@@ -25,6 +25,7 @@ type Config struct {
|
||||
// ProverCoreConfig load zk prover config.
|
||||
type ProverCoreConfig struct {
|
||||
ParamsPath string `json:"params_path"`
|
||||
AssetsPath string `json:"assets_path"`
|
||||
ProofType message.ProofType `json:"proof_type,omitempty"` // 1: chunk prover (default type), 2: batch prover
|
||||
DumpDir string `json:"dump_dir,omitempty"`
|
||||
}
|
||||
|
||||
@@ -42,3 +42,7 @@ func (p *ProverCore) ProveBatch(taskID string, chunkInfos []*message.ChunkInfo,
|
||||
Vk: _empty[:],
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *ProverCore) GetVk() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -28,19 +28,22 @@ import (
|
||||
// ProverCore sends block-traces to rust-prover through ffi and get back the zk-proof.
|
||||
type ProverCore struct {
|
||||
cfg *config.ProverCoreConfig
|
||||
vk string
|
||||
}
|
||||
|
||||
// NewProverCore inits a ProverCore object.
|
||||
func NewProverCore(cfg *config.ProverCoreConfig) (*ProverCore, error) {
|
||||
paramsPathStr := C.CString(cfg.ParamsPath)
|
||||
assetsPathStr := C.CString(cfg.AssetsPath)
|
||||
defer func() {
|
||||
C.free(unsafe.Pointer(paramsPathStr))
|
||||
C.free(unsafe.Pointer(assetsPathStr))
|
||||
}()
|
||||
|
||||
if cfg.ProofType == message.ProofTypeBatch {
|
||||
C.init_batch_prover(paramsPathStr)
|
||||
C.init_batch_prover(paramsPathStr, assetsPathStr)
|
||||
} else if cfg.ProofType == message.ProofTypeChunk {
|
||||
C.init_chunk_prover(paramsPathStr)
|
||||
C.init_chunk_prover(paramsPathStr, assetsPathStr)
|
||||
}
|
||||
|
||||
if cfg.DumpDir != "" {
|
||||
@@ -54,6 +57,26 @@ func NewProverCore(cfg *config.ProverCoreConfig) (*ProverCore, error) {
|
||||
return &ProverCore{cfg: cfg}, nil
|
||||
}
|
||||
|
||||
// GetVk get Base64 format of vk.
|
||||
func (p *ProverCore) GetVk() string {
|
||||
if p.vk != "" { // cached
|
||||
return p.vk
|
||||
}
|
||||
|
||||
var raw *C.char
|
||||
if p.cfg.ProofType == message.ProofTypeBatch {
|
||||
raw = C.get_batch_vk()
|
||||
} else if p.cfg.ProofType == message.ProofTypeChunk {
|
||||
raw = C.get_chunk_vk()
|
||||
}
|
||||
|
||||
if raw != nil {
|
||||
p.vk = C.GoString(raw) // cache it
|
||||
}
|
||||
|
||||
return p.vk
|
||||
}
|
||||
|
||||
// ProveBatch call rust ffi to generate batch proof.
|
||||
func (p *ProverCore) ProveBatch(taskID string, chunkInfos []*message.ChunkInfo, chunkProofs []*message.ChunkProof) (*message.BatchProof, error) {
|
||||
if p.cfg.ProofType != message.ProofTypeBatch {
|
||||
@@ -68,6 +91,11 @@ func (p *ProverCore) ProveBatch(taskID string, chunkInfos []*message.ChunkInfo,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !p.checkChunkProofs(chunkProofsByt) {
|
||||
return nil, fmt.Errorf("Non-match chunk protocol: task-id = %s", taskID)
|
||||
}
|
||||
|
||||
proofByt := p.proveBatch(chunkInfosByt, chunkProofsByt)
|
||||
|
||||
err = p.mayDumpProof(taskID, proofByt)
|
||||
@@ -112,6 +140,20 @@ func (p *ProverCore) TracesToChunkInfo(traces []*types.BlockTrace) (*message.Chu
|
||||
return chunkInfo, json.Unmarshal(chunkInfoByt, chunkInfo)
|
||||
}
|
||||
|
||||
func (p *ProverCore) checkChunkProofs(chunkProofsByt []byte) bool {
|
||||
chunkProofsStr := C.CString(string(chunkProofsByt))
|
||||
|
||||
defer func() {
|
||||
C.free(unsafe.Pointer(chunkProofsStr))
|
||||
}()
|
||||
|
||||
log.Info("Start to check chunk proofs ...")
|
||||
valid := C.check_chunk_proofs(chunkProofsStr)
|
||||
log.Info("Finish checking chunk proofs!")
|
||||
|
||||
return valid != 0
|
||||
}
|
||||
|
||||
func (p *ProverCore) proveBatch(chunkInfosByt []byte, chunkProofsByt []byte) []byte {
|
||||
chunkInfosStr := C.CString(string(chunkInfosByt))
|
||||
chunkProofsStr := C.CString(string(chunkProofsByt))
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package core_test
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"io"
|
||||
@@ -22,9 +23,12 @@ import (
|
||||
|
||||
var (
|
||||
paramsPath = flag.String("params", "/assets/test_params", "params dir")
|
||||
assetsPath = flag.String("assets", "/assets/test_assets", "assets dir")
|
||||
proofDumpPath = flag.String("dump", "/assets/proof_data", "the path proofs dump to")
|
||||
tracePath1 = flag.String("trace1", "/assets/traces/1_transfer.json", "chunk trace 1")
|
||||
tracePath2 = flag.String("trace2", "/assets/traces/10_transfer.json", "chunk trace 2")
|
||||
batchVkPath = flag.String("batch-vk", "/assets/test_assets/agg_vk.vkey", "batch vk")
|
||||
chunkVkPath = flag.String("chunk-vk", "/assets/test_assets/chunk_vk.vkey", "chunk vk")
|
||||
)
|
||||
|
||||
func TestFFI(t *testing.T) {
|
||||
@@ -33,6 +37,7 @@ func TestFFI(t *testing.T) {
|
||||
chunkProverConfig := &config.ProverCoreConfig{
|
||||
DumpDir: *proofDumpPath,
|
||||
ParamsPath: *paramsPath,
|
||||
AssetsPath: *assetsPath,
|
||||
ProofType: message.ProofTypeChunk,
|
||||
}
|
||||
chunkProverCore, err := core.NewProverCore(chunkProverConfig)
|
||||
@@ -83,9 +88,13 @@ func TestFFI(t *testing.T) {
|
||||
as.NoError(err)
|
||||
t.Log("Generated and dumped chunk proof 2")
|
||||
|
||||
as.Equal(chunkProverCore.GetVk(), readVk(*chunkVkPath, as))
|
||||
t.Log("Chunk VKs are equal")
|
||||
|
||||
batchProverConfig := &config.ProverCoreConfig{
|
||||
DumpDir: *proofDumpPath,
|
||||
ParamsPath: *paramsPath,
|
||||
AssetsPath: *assetsPath,
|
||||
ProofType: message.ProofTypeBatch,
|
||||
}
|
||||
batchProverCore, err := core.NewProverCore(batchProverConfig)
|
||||
@@ -96,6 +105,9 @@ func TestFFI(t *testing.T) {
|
||||
_, err = batchProverCore.ProveBatch("batch_proof", chunkInfos, chunkProofs)
|
||||
as.NoError(err)
|
||||
t.Log("Generated and dumped batch proof")
|
||||
|
||||
as.Equal(batchProverCore.GetVk(), readVk(*batchVkPath, as))
|
||||
t.Log("Batch VKs are equal")
|
||||
}
|
||||
|
||||
func readChunkTrace(filePat string, as *assert.Assertions) []*types.BlockTrace {
|
||||
@@ -109,3 +121,12 @@ func readChunkTrace(filePat string, as *assert.Assertions) []*types.BlockTrace {
|
||||
|
||||
return []*types.BlockTrace{trace}
|
||||
}
|
||||
|
||||
func readVk(filePat string, as *assert.Assertions) string {
|
||||
f, err := os.Open(filePat)
|
||||
as.NoError(err)
|
||||
byt, err := io.ReadAll(f)
|
||||
as.NoError(err)
|
||||
|
||||
return base64.StdEncoding.EncodeToString(byt)
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ func (r *Prover) proveAndSubmit() error {
|
||||
proofMsg, err = r.prove(task)
|
||||
if err != nil { // handling error from prove
|
||||
log.Error("failed to prove task", "task_type", task.Task.Type, "task-id", task.Task.ID, "err", err)
|
||||
return r.submitErr(task, true, message.ProofFailureNoPanic, err)
|
||||
return r.submitErr(task, message.ProofFailureNoPanic, err)
|
||||
}
|
||||
return r.submitProof(proofMsg)
|
||||
}
|
||||
@@ -177,6 +177,9 @@ func (r *Prover) fetchTaskFromCoordinator() (*store.ProvingTask, error) {
|
||||
// prepare the request
|
||||
req := &client.GetTaskRequest{
|
||||
TaskType: r.Type(),
|
||||
// we may not be able to get the vk at the first time, so we should pass vk to the coordinator every time we getTask
|
||||
// instead of passing vk when we login
|
||||
VK: r.proverCore.GetVk(),
|
||||
}
|
||||
|
||||
if req.TaskType == message.ProofTypeChunk {
|
||||
@@ -322,7 +325,7 @@ func (r *Prover) submitProof(msg *message.ProofDetail) error {
|
||||
|
||||
// send the submit request
|
||||
if err := r.coordinatorClient.SubmitProof(r.ctx, req); err != nil {
|
||||
if !errors.Is(errors.Unwrap(err), client.ConnectErr) {
|
||||
if !errors.Is(errors.Unwrap(err), client.ErrCoordinatorConnect) {
|
||||
if deleteErr := r.stack.Delete(msg.ID); deleteErr != nil {
|
||||
log.Error("prover stack pop failed", "task_type", msg.Type, "task_id", msg.ID, "err", deleteErr)
|
||||
}
|
||||
@@ -330,12 +333,15 @@ func (r *Prover) submitProof(msg *message.ProofDetail) error {
|
||||
return fmt.Errorf("error submitting proof: %v", err)
|
||||
}
|
||||
|
||||
if deleteErr := r.stack.Delete(msg.ID); deleteErr != nil {
|
||||
log.Error("prover stack pop failed", "task_type", msg.Type, "task_id", msg.ID, "err", deleteErr)
|
||||
}
|
||||
log.Info("proof submitted successfully", "task-id", msg.ID, "task-type", msg.Type, "task-status", msg.Status, "err", msg.Error)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Prover) submitErr(task *store.ProvingTask, isRetry bool, proofFailureType message.ProofFailureType, err error) error {
|
||||
func (r *Prover) submitErr(task *store.ProvingTask, proofFailureType message.ProofFailureType, err error) error {
|
||||
// prepare the submit request
|
||||
req := &client.SubmitProofRequest{
|
||||
TaskID: task.Task.ID,
|
||||
@@ -348,13 +354,16 @@ func (r *Prover) submitErr(task *store.ProvingTask, isRetry bool, proofFailureTy
|
||||
|
||||
// send the submit request
|
||||
if submitErr := r.coordinatorClient.SubmitProof(r.ctx, req); submitErr != nil {
|
||||
if !errors.Is(errors.Unwrap(err), client.ConnectErr) {
|
||||
if !errors.Is(errors.Unwrap(err), client.ErrCoordinatorConnect) {
|
||||
if deleteErr := r.stack.Delete(task.Task.ID); deleteErr != nil {
|
||||
log.Error("prover stack pop failed", "task_type", task.Task.Type, "task_id", task.Task.ID, "err", deleteErr)
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("error submitting proof: %v", submitErr)
|
||||
}
|
||||
if deleteErr := r.stack.Delete(task.Task.ID); deleteErr != nil {
|
||||
log.Error("prover stack pop failed", "task_type", task.Task.Type, "task_id", task.Task.ID, "err", deleteErr)
|
||||
}
|
||||
|
||||
log.Info("proof submitted report failure successfully",
|
||||
"task-id", task.Task.ID, "task-type", task.Task.Type,
|
||||
|
||||
@@ -51,7 +51,7 @@ func TestMain(m *testing.M) {
|
||||
}
|
||||
|
||||
func TestCoordinatorProverInteraction(t *testing.T) {
|
||||
// Start postgres docker containers.
|
||||
// Start postgres docker containers
|
||||
base.RunL2Geth(t)
|
||||
base.RunDBImage(t)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user