mirror of
https://github.com/scroll-tech/scroll.git
synced 2026-04-23 03:00:50 -04:00
Compare commits
3 Commits
feat/valid
...
feat-rollu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35096f593d | ||
|
|
67d5a860bb | ||
|
|
023a233e28 |
@@ -5,7 +5,7 @@ import (
|
||||
"runtime/debug"
|
||||
)
|
||||
|
||||
var tag = "v4.3.28"
|
||||
var tag = "v4.3.29"
|
||||
|
||||
var commit = func() string {
|
||||
if info, ok := debug.ReadBuildInfo(); ok {
|
||||
|
||||
@@ -83,6 +83,25 @@ Return the batch hash of a committed batch.
|
||||
|---|---|---|
|
||||
| _0 | bytes32 | undefined |
|
||||
|
||||
### finalizeBatch
|
||||
|
||||
```solidity
|
||||
function finalizeBatch(bytes _batchHeader, bytes32 _prevStateRoot, bytes32 _postStateRoot, bytes32 _withdrawRoot) external nonpayable
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|---|---|---|
|
||||
| _batchHeader | bytes | undefined |
|
||||
| _prevStateRoot | bytes32 | undefined |
|
||||
| _postStateRoot | bytes32 | undefined |
|
||||
| _withdrawRoot | bytes32 | undefined |
|
||||
|
||||
### finalizeBatchWithProof
|
||||
|
||||
```solidity
|
||||
|
||||
@@ -83,4 +83,16 @@ interface IScrollChain {
|
||||
bytes32 withdrawRoot,
|
||||
bytes calldata aggrProof
|
||||
) external;
|
||||
|
||||
/// @notice Finalize a committed batch on layer 1 without providing proof.
|
||||
/// @param batchHeader The header of current batch, see the encoding in comments of `commitBatch.
|
||||
/// @param prevStateRoot The state root of parent batch.
|
||||
/// @param postStateRoot The state root of current batch.
|
||||
/// @param withdrawRoot The withdraw trie root of current batch.
|
||||
function finalizeBatch(
|
||||
bytes calldata batchHeader,
|
||||
bytes32 prevStateRoot,
|
||||
bytes32 postStateRoot,
|
||||
bytes32 withdrawRoot
|
||||
) external;
|
||||
}
|
||||
|
||||
@@ -353,6 +353,63 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain {
|
||||
emit FinalizeBatch(_batchIndex, _batchHash, _postStateRoot, _withdrawRoot);
|
||||
}
|
||||
|
||||
/// @inheritdoc IScrollChain
|
||||
function finalizeBatch(
|
||||
bytes calldata _batchHeader,
|
||||
bytes32 _prevStateRoot,
|
||||
bytes32 _postStateRoot,
|
||||
bytes32 _withdrawRoot
|
||||
) external override OnlyProver whenNotPaused {
|
||||
require(_prevStateRoot != bytes32(0), "previous state root is zero");
|
||||
require(_postStateRoot != bytes32(0), "new state root is zero");
|
||||
|
||||
// compute batch hash and verify
|
||||
(uint256 memPtr, bytes32 _batchHash) = _loadBatchHeader(_batchHeader);
|
||||
|
||||
uint256 _batchIndex = BatchHeaderV0Codec.batchIndex(memPtr);
|
||||
require(committedBatches[_batchIndex] == _batchHash, "incorrect batch hash");
|
||||
|
||||
// verify previous state root.
|
||||
require(finalizedStateRoots[_batchIndex - 1] == _prevStateRoot, "incorrect previous state root");
|
||||
|
||||
// avoid duplicated verification
|
||||
require(finalizedStateRoots[_batchIndex] == bytes32(0), "batch already verified");
|
||||
|
||||
// check and update lastFinalizedBatchIndex
|
||||
unchecked {
|
||||
require(lastFinalizedBatchIndex + 1 == _batchIndex, "incorrect batch index");
|
||||
lastFinalizedBatchIndex = _batchIndex;
|
||||
}
|
||||
|
||||
// record state root and withdraw root
|
||||
finalizedStateRoots[_batchIndex] = _postStateRoot;
|
||||
withdrawRoots[_batchIndex] = _withdrawRoot;
|
||||
|
||||
// Pop finalized and non-skipped message from L1MessageQueue.
|
||||
uint256 _l1MessagePopped = BatchHeaderV0Codec.l1MessagePopped(memPtr);
|
||||
if (_l1MessagePopped > 0) {
|
||||
IL1MessageQueue _queue = IL1MessageQueue(messageQueue);
|
||||
|
||||
unchecked {
|
||||
uint256 _startIndex = BatchHeaderV0Codec.totalL1MessagePopped(memPtr) - _l1MessagePopped;
|
||||
|
||||
for (uint256 i = 0; i < _l1MessagePopped; i += 256) {
|
||||
uint256 _count = 256;
|
||||
if (_l1MessagePopped - i < _count) {
|
||||
_count = _l1MessagePopped - i;
|
||||
}
|
||||
uint256 _skippedBitmap = BatchHeaderV0Codec.skippedBitmap(memPtr, i / 256);
|
||||
|
||||
_queue.popCrossDomainMessage(_startIndex, _count, _skippedBitmap);
|
||||
|
||||
_startIndex += 256;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emit FinalizeBatch(_batchIndex, _batchHash, _postStateRoot, _withdrawRoot);
|
||||
}
|
||||
|
||||
/************************
|
||||
* Restricted Functions *
|
||||
************************/
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
"min_gas_price": 0,
|
||||
"gas_price_diff": 50000
|
||||
},
|
||||
"finalize_batch_interval_sec": 0,
|
||||
"gas_oracle_sender_private_key": "1313131313131313131313131313131313131313131313131313131313131313"
|
||||
}
|
||||
},
|
||||
@@ -59,7 +58,8 @@
|
||||
"try_times": 5,
|
||||
"base_url": "http://localhost:8750"
|
||||
},
|
||||
"finalize_batch_interval_sec": 0,
|
||||
"is_test_env": true,
|
||||
"finalize_batch_without_proof_timeout_sec": 7200,
|
||||
"gas_oracle_sender_private_key": "1313131313131313131313131313131313131313131313131313131313131313",
|
||||
"commit_sender_private_key": "1414141414141414141414141414141414141414141414141414141414141414",
|
||||
"finalize_sender_private_key": "1515151515151515151515151515151515151515151515151515151515151515",
|
||||
|
||||
@@ -64,6 +64,11 @@ type RelayerConfig struct {
|
||||
GasOracleSenderPrivateKey *ecdsa.PrivateKey `json:"-"`
|
||||
CommitSenderPrivateKey *ecdsa.PrivateKey `json:"-"`
|
||||
FinalizeSenderPrivateKey *ecdsa.PrivateKey `json:"-"`
|
||||
|
||||
// Indicates if the environment is a test environment.
|
||||
IsTestEnv bool `json:"is_test_env"`
|
||||
// The timeout in seconds for finalizing a batch without proof, only used in test env.
|
||||
FinalizeBatchWithoutProofTimeoutSec uint64 `json:"finalize_batch_without_proof_timeout_sec"`
|
||||
}
|
||||
|
||||
// GasOracleConfig The config for updating gas price oracle.
|
||||
|
||||
@@ -50,6 +50,11 @@ func NewLayer1Relayer(ctx context.Context, db *gorm.DB, cfg *config.RelayerConfi
|
||||
return nil, fmt.Errorf("new gas oracle sender failed for address %s, err: %v", addr.Hex(), err)
|
||||
}
|
||||
|
||||
// Ensure test features aren't enabled on the mainnet.
|
||||
if gasOracleSender.GetChainID() == big.NewInt(1) && cfg.IsTestEnv {
|
||||
return nil, fmt.Errorf("cannot enable test env features in mainnet")
|
||||
}
|
||||
|
||||
var minGasPrice uint64
|
||||
var gasPriceDiff uint64
|
||||
if cfg.GasOracleConfig != nil {
|
||||
|
||||
@@ -88,6 +88,11 @@ func NewLayer2Relayer(ctx context.Context, l2Client *ethclient.Client, db *gorm.
|
||||
return nil, fmt.Errorf("new gas oracle sender failed for address %s, err: %w", addr.Hex(), err)
|
||||
}
|
||||
|
||||
// Ensure test features aren't enabled on the mainnet.
|
||||
if commitSender.GetChainID() == big.NewInt(1) && cfg.IsTestEnv {
|
||||
return nil, fmt.Errorf("cannot enable test env features in mainnet")
|
||||
}
|
||||
|
||||
var minGasPrice uint64
|
||||
var gasPriceDiff uint64
|
||||
if cfg.GasOracleConfig != nil {
|
||||
@@ -423,107 +428,25 @@ func (r *Layer2Relayer) ProcessCommittedBatches() {
|
||||
r.metrics.rollupL2RelayerProcessCommittedBatchesTotal.Inc()
|
||||
|
||||
batch := batches[0]
|
||||
hash := batch.Hash
|
||||
status := types.ProvingStatus(batch.ProvingStatus)
|
||||
switch status {
|
||||
case types.ProvingTaskUnassigned, types.ProvingTaskAssigned:
|
||||
// The proof for this block is not ready yet.
|
||||
return
|
||||
now := time.Now()
|
||||
elapsedTime := now.Sub(batch.CreatedAt)
|
||||
if r.cfg.IsTestEnv && elapsedTime.Seconds() > float64(r.cfg.FinalizeBatchWithoutProofTimeoutSec) {
|
||||
if err := r.finalizeBatch(batch, false); err != nil {
|
||||
log.Error("Failed to finalize timeout batch without proof", "index", batch.Index, "hash", batch.Hash, "err", err)
|
||||
}
|
||||
log.Info("Successfully finalized timeout batch without proof", "index", batch.Index, "hash", batch.Hash)
|
||||
}
|
||||
|
||||
case types.ProvingTaskVerified:
|
||||
log.Info("Start to roll up zk proof", "hash", hash)
|
||||
log.Info("Start to roll up zk proof", "hash", batch.Hash)
|
||||
r.metrics.rollupL2RelayerProcessCommittedBatchesFinalizedTotal.Inc()
|
||||
|
||||
// Check batch status before send `finalizeBatchWithProof` tx.
|
||||
if r.cfg.ChainMonitor.Enabled {
|
||||
var batchStatus bool
|
||||
batchStatus, err = r.getBatchStatusByIndex(batch.Index)
|
||||
if err != nil {
|
||||
r.metrics.rollupL2ChainMonitorLatestFailedCall.Inc()
|
||||
log.Warn("failed to get batch status, please check chain_monitor api server", "batch_index", batch.Index, "err", err)
|
||||
return
|
||||
}
|
||||
if !batchStatus {
|
||||
r.metrics.rollupL2ChainMonitorLatestFailedBatchStatus.Inc()
|
||||
log.Error("the batch status is not right, stop finalize batch and check the reason", "batch_index", batch.Index)
|
||||
return
|
||||
}
|
||||
if err := r.finalizeBatch(batch, true); err != nil {
|
||||
log.Error("Failed to finalize batch with proof", "index", batch.Index, "hash", batch.Hash, "err", err)
|
||||
}
|
||||
|
||||
var parentBatchStateRoot string
|
||||
if batch.Index > 0 {
|
||||
var parentBatch *orm.Batch
|
||||
parentBatch, err = r.batchOrm.GetBatchByIndex(r.ctx, batch.Index-1)
|
||||
// handle unexpected db error
|
||||
if err != nil {
|
||||
log.Error("Failed to get batch", "index", batch.Index-1, "err", err)
|
||||
return
|
||||
}
|
||||
parentBatchStateRoot = parentBatch.StateRoot
|
||||
}
|
||||
|
||||
aggProof, err := r.batchOrm.GetVerifiedProofByHash(r.ctx, hash)
|
||||
if err != nil {
|
||||
log.Error("get verified proof by hash failed", "hash", hash, "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = aggProof.SanityCheck(); err != nil {
|
||||
log.Error("agg_proof sanity check fails", "hash", hash, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
data, err := r.l1RollupABI.Pack(
|
||||
"finalizeBatchWithProof",
|
||||
batch.BatchHeader,
|
||||
common.HexToHash(parentBatchStateRoot),
|
||||
common.HexToHash(batch.StateRoot),
|
||||
common.HexToHash(batch.WithdrawRoot),
|
||||
aggProof.Proof,
|
||||
)
|
||||
if err != nil {
|
||||
log.Error("Pack finalizeBatchWithProof failed", "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
txID := hash + "-finalize"
|
||||
// add suffix `-finalize` to avoid duplication with commit tx in unit tests
|
||||
txHash, err := r.finalizeSender.SendTransaction(txID, &r.cfg.RollupContractAddress, big.NewInt(0), data, 0)
|
||||
finalizeTxHash := &txHash
|
||||
if err != nil {
|
||||
if !errors.Is(err, sender.ErrNoAvailableAccount) && !errors.Is(err, sender.ErrFullPending) {
|
||||
// This can happen normally if we try to finalize 2 or more
|
||||
// batches around the same time. The 2nd tx might fail since
|
||||
// the client does not see the 1st tx's updates at this point.
|
||||
// TODO: add more fine-grained error handling
|
||||
log.Error(
|
||||
"finalizeBatchWithProof in layer1 failed",
|
||||
"index", batch.Index,
|
||||
"hash", batch.Hash,
|
||||
"RollupContractAddress", r.cfg.RollupContractAddress,
|
||||
"err", err,
|
||||
)
|
||||
log.Debug(
|
||||
"finalizeBatchWithProof in layer1 failed",
|
||||
"index", batch.Index,
|
||||
"hash", batch.Hash,
|
||||
"RollupContractAddress", r.cfg.RollupContractAddress,
|
||||
"calldata", common.Bytes2Hex(data),
|
||||
"err", err,
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
log.Info("finalizeBatchWithProof in layer1", "index", batch.Index, "batch hash", batch.Hash, "tx hash", hash)
|
||||
|
||||
// record and sync with db, @todo handle db error
|
||||
err = r.batchOrm.UpdateFinalizeTxHashAndRollupStatus(r.ctx, hash, finalizeTxHash.String(), types.RollupFinalizing)
|
||||
if err != nil {
|
||||
log.Error("UpdateFinalizeTxHashAndRollupStatus failed",
|
||||
"index", batch.Index, "batch hash", batch.Hash,
|
||||
"tx hash", finalizeTxHash.String(), "err", err)
|
||||
}
|
||||
r.processingFinalization.Store(txID, hash)
|
||||
r.metrics.rollupL2RelayerProcessCommittedBatchesFinalizedSuccessTotal.Inc()
|
||||
log.Info("Successfully finalized batch with proof", "index", batch.Index, "hash", batch.Hash)
|
||||
|
||||
case types.ProvingTaskFailed:
|
||||
// We were unable to prove this batch. There are two possibilities:
|
||||
@@ -542,13 +465,121 @@ func (r *Layer2Relayer) ProcessCommittedBatches() {
|
||||
"ProvedAt", batch.ProvedAt,
|
||||
"ProofTimeSec", batch.ProofTimeSec,
|
||||
)
|
||||
return
|
||||
|
||||
default:
|
||||
log.Error("encounter unreachable case in ProcessCommittedBatches", "proving status", status)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Layer2Relayer) finalizeBatch(batch *orm.Batch, withProof bool) error {
|
||||
// Check batch status before send `finalizeBatchWithProof` tx.
|
||||
if r.cfg.ChainMonitor.Enabled {
|
||||
var batchStatus bool
|
||||
batchStatus, err := r.getBatchStatusByIndex(batch.Index)
|
||||
if err != nil {
|
||||
r.metrics.rollupL2ChainMonitorLatestFailedCall.Inc()
|
||||
log.Warn("failed to get batch status, please check chain_monitor api server", "batch_index", batch.Index, "err", err)
|
||||
return err
|
||||
}
|
||||
if !batchStatus {
|
||||
r.metrics.rollupL2ChainMonitorLatestFailedBatchStatus.Inc()
|
||||
log.Error("the batch status is not right, stop finalize batch and check the reason", "batch_index", batch.Index)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var parentBatchStateRoot string
|
||||
if batch.Index > 0 {
|
||||
var parentBatch *orm.Batch
|
||||
parentBatch, err := r.batchOrm.GetBatchByIndex(r.ctx, batch.Index-1)
|
||||
// handle unexpected db error
|
||||
if err != nil {
|
||||
log.Error("Failed to get batch", "index", batch.Index-1, "err", err)
|
||||
return err
|
||||
}
|
||||
parentBatchStateRoot = parentBatch.StateRoot
|
||||
}
|
||||
|
||||
var txCalldata []byte
|
||||
if withProof {
|
||||
aggProof, err := r.batchOrm.GetVerifiedProofByHash(r.ctx, batch.Hash)
|
||||
if err != nil {
|
||||
log.Error("get verified proof by hash failed", "hash", batch.Hash, "err", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err = aggProof.SanityCheck(); err != nil {
|
||||
log.Error("agg_proof sanity check fails", "hash", batch.Hash, "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
txCalldata, err = r.l1RollupABI.Pack(
|
||||
"finalizeBatchWithProof",
|
||||
batch.BatchHeader,
|
||||
common.HexToHash(parentBatchStateRoot),
|
||||
common.HexToHash(batch.StateRoot),
|
||||
common.HexToHash(batch.WithdrawRoot),
|
||||
aggProof.Proof,
|
||||
)
|
||||
if err != nil {
|
||||
log.Error("Pack finalizeBatchWithProof failed", "err", err)
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
var err error
|
||||
txCalldata, err = r.l1RollupABI.Pack(
|
||||
"finalizeBatch",
|
||||
batch.BatchHeader,
|
||||
common.HexToHash(parentBatchStateRoot),
|
||||
common.HexToHash(batch.StateRoot),
|
||||
common.HexToHash(batch.WithdrawRoot),
|
||||
)
|
||||
if err != nil {
|
||||
log.Error("Pack finalizeBatch failed", "err", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
txID := batch.Hash + "-finalize"
|
||||
// add suffix `-finalize` to avoid duplication with commit tx in unit tests
|
||||
txHash, err := r.finalizeSender.SendTransaction(txID, &r.cfg.RollupContractAddress, big.NewInt(0), txCalldata, 0)
|
||||
finalizeTxHash := &txHash
|
||||
if err != nil {
|
||||
if !errors.Is(err, sender.ErrNoAvailableAccount) && !errors.Is(err, sender.ErrFullPending) {
|
||||
// This can happen normally if we try to finalize 2 or more
|
||||
// batches around the same time. The 2nd tx might fail since
|
||||
// the client does not see the 1st tx's updates at this point.
|
||||
// TODO: add more fine-grained error handling
|
||||
log.Error(
|
||||
"finalizeBatchWithProof in layer1 failed",
|
||||
"index", batch.Index,
|
||||
"hash", batch.Hash,
|
||||
"RollupContractAddress", r.cfg.RollupContractAddress,
|
||||
"err", err,
|
||||
)
|
||||
log.Debug(
|
||||
"finalizeBatchWithProof in layer1 failed",
|
||||
"index", batch.Index,
|
||||
"hash", batch.Hash,
|
||||
"RollupContractAddress", r.cfg.RollupContractAddress,
|
||||
"calldata", common.Bytes2Hex(txCalldata),
|
||||
"err", err,
|
||||
)
|
||||
}
|
||||
return err
|
||||
}
|
||||
log.Info("finalizeBatchWithProof in layer1", "index", batch.Index, "batch hash", batch.Hash, "tx hash", batch.Hash)
|
||||
|
||||
// record and sync with db, @todo handle db error
|
||||
if err := r.batchOrm.UpdateFinalizeTxHashAndRollupStatus(r.ctx, batch.Hash, finalizeTxHash.String(), types.RollupFinalizing); err != nil {
|
||||
log.Error("UpdateFinalizeTxHashAndRollupStatus failed", "index", batch.Index, "batch hash", batch.Hash, "tx hash", finalizeTxHash.String(), "err", err)
|
||||
return err
|
||||
}
|
||||
r.processingFinalization.Store(txID, batch.Hash)
|
||||
r.metrics.rollupL2RelayerProcessCommittedBatchesFinalizedSuccessTotal.Inc()
|
||||
return nil
|
||||
}
|
||||
|
||||
// batchStatusResponse the response schema
|
||||
type batchStatusResponse struct {
|
||||
ErrCode int `json:"errcode"`
|
||||
|
||||
@@ -165,6 +165,11 @@ func (s *Sender) IsFull() bool {
|
||||
return s.pendingTxs.Count() >= s.config.PendingLimit
|
||||
}
|
||||
|
||||
// GetChainID returns the chain ID associated with the sender.
|
||||
func (s *Sender) GetChainID() *big.Int {
|
||||
return s.chainID
|
||||
}
|
||||
|
||||
// Stop stop the sender module.
|
||||
func (s *Sender) Stop() {
|
||||
close(s.stopCh)
|
||||
|
||||
Reference in New Issue
Block a user