Compare commits

..

9 Commits

Author SHA1 Message Date
zimpha
82af544cbb move Forwarder contract from PR 528 2023-07-18 12:16:53 +08:00
ChuhanJin
b4cb30e2a1 feat(bridge-history-api): add watch nft batch events watching (#643)
Co-authored-by: vincent <419436363@qq.com>
2023-07-17 21:10:03 +08:00
colin
d9b8891803 refactor(chunk & batch proposer): optimize logging (#653) 2023-07-17 15:28:25 +08:00
ChuhanJin
6db2c0a8cb fix(bridge-history-api): crossMsgs can be empty (#646)
Co-authored-by: vincent <419436363@qq.com>
2023-07-14 17:39:37 +08:00
ChuhanJin
0caf0d4052 fix(bridge-history-api): fix claimable api sql issue (#645)
Co-authored-by: vincent <419436363@qq.com>
2023-07-14 15:26:31 +08:00
vyzo
95f2f7da0f fix(bridge): adjust gas fee cap in resumbitTransaction for rising basefee (#625)
Co-authored-by: georgehao <haohongfan@gmail.com>
Co-authored-by: colin <102356659+colinlyguo@users.noreply.github.com>
2023-07-13 17:29:17 +08:00
Xi Lin
d2a1459768 fix(contracts): fix dropping message with nonce 0 (#640)
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
2023-07-13 16:48:45 +08:00
colin
f38dda8e02 feat(relayer): remove 'skipped' proving/rollup status and related code/tests (#642) 2023-07-13 16:39:02 +08:00
HAOYUatHZ
189ef09938 ci(github): only trigger build when merging (#641) 2023-07-13 09:07:26 +08:00
26 changed files with 441 additions and 192 deletions

View File

@@ -29,6 +29,25 @@ jobs:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
steps:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: 1.19.x
- name: Checkout code
uses: actions/checkout@v2
- name: Test
run: |
go test -tags="mock_prover" -v -coverprofile=coverage.txt ./...
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
flags: roller
compile:
if: github.event_name == 'push' # will only be triggered when pushing to main & staging & develop & alpha
runs-on: ubuntu-latest
steps:
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly-2022-12-10
@@ -47,13 +66,6 @@ jobs:
- name: Test
run: |
make roller
go test -tags="mock_prover" -v -coverprofile=coverage.txt ./...
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
flags: roller
check:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest

View File

@@ -39,6 +39,12 @@ var (
L2WithdrawERC721Sig common.Hash
L2WithdrawERC1155Sig common.Hash
// batch nft sigs
L1BatchDepositERC721Sig common.Hash
L1BatchDepositERC1155Sig common.Hash
L2BatchWithdrawERC721Sig common.Hash
L2BatchWithdrawERC1155Sig common.Hash
// scroll mono repo
// ScrollChainABI holds information about ScrollChain's context and available invokable methods.
@@ -116,6 +122,12 @@ func init() {
L2ERC1155GatewayABI, _ = L2ERC1155GatewayMetaData.GetAbi()
L2WithdrawERC1155Sig = L2ERC1155GatewayABI.Events["WithdrawERC1155"].ID
// batch nft events
L1BatchDepositERC721Sig = L1ERC721GatewayABI.Events["BatchDepositERC721"].ID
L1BatchDepositERC1155Sig = L1ERC1155GatewayABI.Events["BatchDepositERC1155"].ID
L2BatchWithdrawERC721Sig = L2ERC721GatewayABI.Events["BatchWithdrawERC721"].ID
L2BatchWithdrawERC1155Sig = L2ERC1155GatewayABI.Events["BatchWithdrawERC1155"].ID
// scroll monorepo
ScrollChainABI, _ = ScrollChainMetaData.GetAbi()
ScrollChainV2ABI, _ = ScrollChainV2MetaData.GetAbi()
@@ -277,6 +289,23 @@ type ERC1155MessageEvent struct {
Amount *big.Int
}
type BatchERC721MessageEvent struct {
L1Token common.Address
L2Token common.Address
From common.Address
To common.Address
TokenIDs []*big.Int
}
type BatchERC1155MessageEvent struct {
L1Token common.Address
L2Token common.Address
From common.Address
To common.Address
TokenIDs []*big.Int
TokenAmounts []*big.Int
}
// scroll monorepo
// L1SentMessageEvent represents a SentMessage event raised by the L1ScrollMessenger contract.

View File

@@ -9,6 +9,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/jmoiron/sqlx"
"github.com/lib/pq"
)
type l2CrossMsgOrm struct {
@@ -143,7 +144,7 @@ func (l *l2CrossMsgOrm) GetL2EarliestNoBlockTimestampHeight() (uint64, error) {
func (l *l2CrossMsgOrm) GetL2CrossMsgByMsgHashList(msgHashList []string) ([]*CrossMsg, error) {
var results []*CrossMsg
rows, err := l.db.Queryx(`SELECT * FROM cross_message WHERE msg_hash in ($1) AND msg_type = $2 AND deleted_at IS NULL;`, msgHashList, Layer2Msg)
rows, err := l.db.Queryx(`SELECT * FROM cross_message WHERE msg_hash = ANY($1) AND msg_type = $2 AND deleted_at IS NULL;`, pq.Array(msgHashList), Layer2Msg)
for rows.Next() {
msg := &CrossMsg{}
if err = rows.StructScan(msg); err != nil {

View File

@@ -122,7 +122,8 @@ func (h *historyBackend) GetClaimableTxsByAddress(address common.Address, offset
msgHashList = append(msgHashList, result.MsgHash)
}
crossMsgs, err := h.db.GetL2CrossMsgByMsgHashList(msgHashList)
if err != nil || len(crossMsgs) == 0 {
// crossMsgs can be empty, because they can be emitted by user directly call contract
if err != nil {
return txHistories, 0, err
}
crossMsgMap := make(map[string]*orm.CrossMsg)

View File

@@ -2,6 +2,8 @@ package utils
import (
"context"
"math/big"
"strings"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
@@ -108,7 +110,43 @@ func ParseBackendL1EventLogs(logs []types.Log) ([]*orm.CrossMsg, []*orm.RelayedM
}
// since every deposit event will emit after a sent event, so can use this msg_hash as next withdraw event's msg_hash
msgHash = ComputeMessageHash(event.Sender, event.Target, event.Value, event.MessageNonce, event.Message).Hex()
case backendabi.L1BatchDepositERC721Sig:
event := backendabi.BatchERC721MessageEvent{}
err := UnpackLog(backendabi.L1ERC721GatewayABI, &event, "BatchDepositERC721", vlog)
if err != nil {
log.Warn("Failed to unpack BatchDepositERC721 event", "err", err)
return l1CrossMsg, relayedMsgs, err
}
l1CrossMsg = append(l1CrossMsg, &orm.CrossMsg{
Height: vlog.BlockNumber,
Sender: event.From.String(),
Target: event.To.String(),
Asset: int(orm.ERC721),
Layer1Hash: vlog.TxHash.Hex(),
Layer1Token: event.L1Token.Hex(),
Layer2Token: event.L2Token.Hex(),
TokenIDs: convertBigIntArrayToString(event.TokenIDs),
MsgHash: msgHash,
})
case backendabi.L1BatchDepositERC1155Sig:
event := backendabi.BatchERC1155MessageEvent{}
err := UnpackLog(backendabi.L1ERC1155GatewayABI, &event, "BatchDepositERC1155", vlog)
if err != nil {
log.Warn("Failed to unpack BatchDepositERC1155 event", "err", err)
return l1CrossMsg, relayedMsgs, err
}
l1CrossMsg = append(l1CrossMsg, &orm.CrossMsg{
Height: vlog.BlockNumber,
Sender: event.From.String(),
Target: event.To.String(),
Asset: int(orm.ERC1155),
Layer1Hash: vlog.TxHash.Hex(),
Layer1Token: event.L1Token.Hex(),
Layer2Token: event.L2Token.Hex(),
TokenIDs: convertBigIntArrayToString(event.TokenIDs),
TokenAmounts: convertBigIntArrayToString(event.TokenAmounts),
MsgHash: msgHash,
})
case backendabi.L1RelayedMessageEventSignature:
event := backendabi.L1RelayedMessageEvent{}
err := UnpackLog(backendabi.L1ScrollMessengerABI, &event, "RelayedMessage", vlog)
@@ -172,6 +210,7 @@ func ParseBackendL2EventLogs(logs []types.Log) ([]*orm.CrossMsg, []*orm.RelayedM
Layer2Hash: vlog.TxHash.Hex(),
Layer1Token: event.L1Token.Hex(),
Layer2Token: event.L2Token.Hex(),
MsgHash: l2SentMsgs[len(l2SentMsgs)-1].MsgHash,
})
case backendabi.L2WithdrawERC721Sig:
event := backendabi.ERC721MessageEvent{}
@@ -190,6 +229,7 @@ func ParseBackendL2EventLogs(logs []types.Log) ([]*orm.CrossMsg, []*orm.RelayedM
Layer1Token: event.L1Token.Hex(),
Layer2Token: event.L2Token.Hex(),
TokenIDs: event.TokenID.String(),
MsgHash: l2SentMsgs[len(l2SentMsgs)-1].MsgHash,
})
case backendabi.L2WithdrawERC1155Sig:
event := backendabi.ERC1155MessageEvent{}
@@ -209,6 +249,46 @@ func ParseBackendL2EventLogs(logs []types.Log) ([]*orm.CrossMsg, []*orm.RelayedM
Layer2Token: event.L2Token.Hex(),
TokenIDs: event.TokenID.String(),
Amount: event.Amount.String(),
MsgHash: l2SentMsgs[len(l2SentMsgs)-1].MsgHash,
})
case backendabi.L2BatchWithdrawERC721Sig:
event := backendabi.BatchERC721MessageEvent{}
err := UnpackLog(backendabi.L2ERC721GatewayABI, &event, "BatchWithdrawERC721", vlog)
if err != nil {
log.Warn("Failed to unpack BatchWithdrawERC721 event", "err", err)
return l2CrossMsg, relayedMsgs, l2SentMsgs, err
}
l2SentMsgs[len(l2SentMsgs)-1].OriginalSender = event.From.Hex()
l2CrossMsg = append(l2CrossMsg, &orm.CrossMsg{
Height: vlog.BlockNumber,
Sender: event.From.String(),
Target: event.To.String(),
Asset: int(orm.ERC721),
Layer1Hash: vlog.TxHash.Hex(),
Layer1Token: event.L1Token.Hex(),
Layer2Token: event.L2Token.Hex(),
TokenIDs: convertBigIntArrayToString(event.TokenIDs),
MsgHash: l2SentMsgs[len(l2SentMsgs)-1].MsgHash,
})
case backendabi.L2BatchWithdrawERC1155Sig:
event := backendabi.BatchERC1155MessageEvent{}
err := UnpackLog(backendabi.L2ERC1155GatewayABI, &event, "BatchWithdrawERC1155", vlog)
if err != nil {
log.Warn("Failed to unpack BatchWithdrawERC1155 event", "err", err)
return l2CrossMsg, relayedMsgs, l2SentMsgs, err
}
l2SentMsgs[len(l2SentMsgs)-1].OriginalSender = event.From.Hex()
l2CrossMsg = append(l2CrossMsg, &orm.CrossMsg{
Height: vlog.BlockNumber,
Sender: event.From.String(),
Target: event.To.String(),
Asset: int(orm.ERC1155),
Layer1Hash: vlog.TxHash.Hex(),
Layer1Token: event.L1Token.Hex(),
Layer2Token: event.L2Token.Hex(),
TokenIDs: convertBigIntArrayToString(event.TokenIDs),
TokenAmounts: convertBigIntArrayToString(event.TokenAmounts),
MsgHash: l2SentMsgs[len(l2SentMsgs)-1].MsgHash,
})
case backendabi.L2SentMessageEventSignature:
event := backendabi.L2SentMessageEvent{}
@@ -304,3 +384,13 @@ func ParseBatchInfoFromScrollChain(ctx context.Context, client *ethclient.Client
}
return rollupBatches, nil
}
func convertBigIntArrayToString(array []*big.Int) string {
stringArray := make([]string, len(array))
for i, num := range array {
stringArray[i] = num.String()
}
result := strings.Join(stringArray, ", ")
return result
}

View File

@@ -29,7 +29,6 @@ var (
bridgeL2BatchesCommittedTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/batches/committed/total", metrics.ScrollRegistry)
bridgeL2BatchesFinalizedConfirmedTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/batches/finalized/confirmed/total", metrics.ScrollRegistry)
bridgeL2BatchesCommittedConfirmedTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/batches/committed/confirmed/total", metrics.ScrollRegistry)
bridgeL2BatchesSkippedTotalCounter = gethMetrics.NewRegisteredCounter("bridge/l2/batches/skipped/total", metrics.ScrollRegistry)
)
// Layer2Relayer is responsible for
@@ -394,15 +393,6 @@ func (r *Layer2Relayer) ProcessPendingBatches() {
// ProcessCommittedBatches submit proof to layer 1 rollup contract
func (r *Layer2Relayer) ProcessCommittedBatches() {
// set skipped batches in a single db operation
if count, err := r.batchOrm.UpdateSkippedBatches(r.ctx); err != nil {
log.Error("UpdateSkippedBatches failed", "err", err)
// continue anyway
} else if count > 0 {
bridgeL2BatchesSkippedTotalCounter.Inc(int64(count))
log.Info("Skipping batches", "count", count)
}
// retrieves the earliest batch whose rollup status is 'committed'
fields := map[string]interface{}{
"rollup_status": types.RollupCommitted,
@@ -430,11 +420,6 @@ func (r *Layer2Relayer) ProcessCommittedBatches() {
// It's an intermediate state. The roller 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.ProvingTaskFailed, types.ProvingTaskSkipped:
// note: this is covered by UpdateSkippedBatches, but we keep it for completeness's sake
if err = r.batchOrm.UpdateRollupStatus(r.ctx, hash, types.RollupFinalizationSkipped); err != nil {
log.Warn("UpdateRollupStatus failed", "hash", hash, "err", err)
}
case types.ProvingTaskVerified:
log.Info("Start to roll up zk proof", "hash", hash)
success := false
@@ -454,8 +439,8 @@ func (r *Layer2Relayer) ProcessCommittedBatches() {
defer func() {
// TODO: need to revisit this and have a more fine-grained error handling
if !success {
log.Info("Failed to upload the proof, change rollup status to FinalizationSkipped", "hash", hash)
if err = r.batchOrm.UpdateRollupStatus(r.ctx, hash, types.RollupFinalizationSkipped); err != nil {
log.Info("Failed to upload the proof, change rollup status to RollupFinalizeFailed", "hash", hash)
if err = r.batchOrm.UpdateRollupStatus(r.ctx, hash, types.RollupFinalizeFailed); err != nil {
log.Warn("UpdateRollupStatus failed", "hash", hash, "err", err)
}
}

View File

@@ -90,7 +90,7 @@ func testL2RelayerProcessCommittedBatches(t *testing.T) {
statuses, err := batchOrm.GetRollupStatusByHashList(context.Background(), []string{batch.Hash})
assert.NoError(t, err)
assert.Equal(t, 1, len(statuses))
assert.Equal(t, types.RollupFinalizationSkipped, statuses[0])
assert.Equal(t, types.RollupFinalizeFailed, statuses[0])
err = batchOrm.UpdateRollupStatus(context.Background(), batch.Hash, types.RollupCommitted)
assert.NoError(t, err)
@@ -108,67 +108,6 @@ func testL2RelayerProcessCommittedBatches(t *testing.T) {
assert.Equal(t, types.RollupFinalizing, statuses[0])
}
func testL2RelayerSkipBatches(t *testing.T) {
db := setupL2RelayerDB(t)
defer database.CloseDB(db)
l2Cfg := cfg.L2Config
relayer, err := NewLayer2Relayer(context.Background(), l2Cli, db, l2Cfg.RelayerConfig, false)
assert.NoError(t, err)
batchOrm := orm.NewBatch(db)
createBatch := func(rollupStatus types.RollupStatus, provingStatus types.ProvingStatus) string {
batch, err := batchOrm.InsertBatch(context.Background(), 0, 1, chunkHash1.Hex(), chunkHash2.Hex(), []*types.Chunk{chunk1, chunk2})
assert.NoError(t, err)
err = batchOrm.UpdateRollupStatus(context.Background(), batch.Hash, rollupStatus)
assert.NoError(t, err)
proof := &message.AggProof{
Proof: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31},
FinalPair: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31},
}
err = batchOrm.UpdateProofByHash(context.Background(), batch.Hash, proof, 100)
assert.NoError(t, err)
err = batchOrm.UpdateProvingStatus(context.Background(), batch.Hash, provingStatus)
assert.NoError(t, err)
return batch.Hash
}
skipped := []string{
createBatch(types.RollupCommitted, types.ProvingTaskSkipped),
createBatch(types.RollupCommitted, types.ProvingTaskFailed),
}
notSkipped := []string{
createBatch(types.RollupPending, types.ProvingTaskSkipped),
createBatch(types.RollupCommitting, types.ProvingTaskSkipped),
createBatch(types.RollupFinalizing, types.ProvingTaskSkipped),
createBatch(types.RollupFinalized, types.ProvingTaskSkipped),
createBatch(types.RollupPending, types.ProvingTaskFailed),
createBatch(types.RollupCommitting, types.ProvingTaskFailed),
createBatch(types.RollupFinalizing, types.ProvingTaskFailed),
createBatch(types.RollupFinalized, types.ProvingTaskFailed),
createBatch(types.RollupCommitted, types.ProvingTaskVerified),
}
relayer.ProcessCommittedBatches()
for _, id := range skipped {
statuses, err := batchOrm.GetRollupStatusByHashList(context.Background(), []string{id})
assert.NoError(t, err)
assert.Equal(t, 1, len(statuses))
assert.Equal(t, types.RollupFinalizationSkipped, statuses[0])
}
for _, id := range notSkipped {
statuses, err := batchOrm.GetRollupStatusByHashList(context.Background(), []string{id})
assert.NoError(t, err)
assert.Equal(t, 1, len(statuses))
assert.NotEqual(t, types.RollupFinalizationSkipped, statuses[0])
}
}
func testL2RelayerRollupConfirm(t *testing.T) {
db := setupL2RelayerDB(t)
defer database.CloseDB(db)

View File

@@ -97,7 +97,6 @@ func TestFunctions(t *testing.T) {
t.Run("TestCreateNewRelayer", testCreateNewRelayer)
t.Run("TestL2RelayerProcessPendingBatches", testL2RelayerProcessPendingBatches)
t.Run("TestL2RelayerProcessCommittedBatches", testL2RelayerProcessCommittedBatches)
t.Run("TestL2RelayerSkipBatches", testL2RelayerSkipBatches)
t.Run("TestL2RelayerRollupConfirm", testL2RelayerRollupConfirm)
t.Run("TestL2RelayerGasOracleConfirm", testL2RelayerGasOracleConfirm)
t.Run("TestLayer2RelayerProcessGasPriceOracle", testLayer2RelayerProcessGasPriceOracle)

View File

@@ -343,6 +343,20 @@ func (s *Sender) resubmitTransaction(feeData *FeeData, auth *bind.TransactOpts,
if gasTipCap.Cmp(feeData.gasTipCap) < 0 {
gasTipCap = feeData.gasTipCap
}
// adjust for rising basefee
adjBaseFee := big.NewInt(0)
if feeGas := atomic.LoadUint64(&s.baseFeePerGas); feeGas != 0 {
adjBaseFee.SetUint64(feeGas)
}
adjBaseFee = adjBaseFee.Mul(adjBaseFee, escalateMultipleNum)
adjBaseFee = adjBaseFee.Div(adjBaseFee, escalateMultipleDen)
currentGasFeeCap := new(big.Int).Add(gasTipCap, adjBaseFee)
if gasFeeCap.Cmp(currentGasFeeCap) < 0 {
gasFeeCap = currentGasFeeCap
}
// but don't exceed maxGasPrice
if gasFeeCap.Cmp(maxGasPrice) > 0 {
gasFeeCap = maxGasPrice
}

View File

@@ -65,6 +65,7 @@ func TestSender(t *testing.T) {
t.Run("test min gas limit", testMinGasLimit)
t.Run("test resubmit transaction", testResubmitTransaction)
t.Run("test resubmit transaction with rising base fee", testResubmitTransactionWithRisingBaseFee)
t.Run("test check pending transaction", testCheckPendingTransaction)
t.Run("test 1 account sender", func(t *testing.T) { testBatchSender(t, 1) })
@@ -154,6 +155,43 @@ func testResubmitTransaction(t *testing.T) {
}
}
func testResubmitTransactionWithRisingBaseFee(t *testing.T) {
txType := "DynamicFeeTx"
cfgCopy := *cfg.L1Config.RelayerConfig.SenderConfig
cfgCopy.TxType = txType
s, err := NewSender(context.Background(), &cfgCopy, privateKeys)
assert.NoError(t, err)
auth := s.auths.getAccount()
tx := types.NewTransaction(auth.Nonce.Uint64(), common.Address{}, big.NewInt(0), 0, big.NewInt(0), nil)
s.baseFeePerGas = 1000
feeData, err := s.getFeeData(auth, &common.Address{}, big.NewInt(0), nil, 0)
assert.NoError(t, err)
// bump the basefee by 10x
s.baseFeePerGas *= 10
// resubmit and check that the gas fee has been adjusted accordingly
newTx, err := s.resubmitTransaction(feeData, auth, tx)
assert.NoError(t, err)
escalateMultipleNum := new(big.Int).SetUint64(s.config.EscalateMultipleNum)
escalateMultipleDen := new(big.Int).SetUint64(s.config.EscalateMultipleDen)
maxGasPrice := new(big.Int).SetUint64(s.config.MaxGasPrice)
adjBaseFee := new(big.Int)
adjBaseFee.SetUint64(s.baseFeePerGas)
adjBaseFee = adjBaseFee.Mul(adjBaseFee, escalateMultipleNum)
adjBaseFee = adjBaseFee.Div(adjBaseFee, escalateMultipleDen)
expectedGasFeeCap := new(big.Int).Add(feeData.gasTipCap, adjBaseFee)
if expectedGasFeeCap.Cmp(maxGasPrice) > 0 {
expectedGasFeeCap = maxGasPrice
}
assert.Equal(t, expectedGasFeeCap.Int64(), newTx.GasFeeCap().Int64())
s.Stop()
}
func testCheckPendingTransaction(t *testing.T) {
for _, txType := range txTypes {
cfgCopy := *cfg.L1Config.RelayerConfig.SenderConfig

View File

@@ -93,7 +93,6 @@ func (p *BatchProposer) proposeBatchChunks() ([]*orm.Chunk, error) {
}
if len(dbChunks) == 0 {
log.Warn("No Unbatched Chunks")
return nil, nil
}
@@ -147,7 +146,7 @@ func (p *BatchProposer) proposeBatchChunks() ([]*orm.Chunk, error) {
}
if !hasChunkTimeout && uint64(len(dbChunks)) < p.minChunkNumPerBatch {
log.Warn("The payload size of the batch is less than the minimum limit",
log.Warn("The chunk number of the batch is less than the minimum limit",
"chunk num", len(dbChunks), "minChunkNumPerBatch", p.minChunkNumPerBatch,
)
return nil, nil

View File

@@ -61,7 +61,6 @@ func (p *ChunkProposer) TryProposeChunk() {
func (p *ChunkProposer) updateChunkInfoInDB(chunk *types.Chunk) error {
if chunk == nil {
log.Warn("proposed chunk is nil, cannot update in DB")
return nil
}
@@ -86,7 +85,6 @@ func (p *ChunkProposer) proposeChunk() (*types.Chunk, error) {
}
if len(blocks) == 0 {
log.Warn("no un-chunked blocks")
return nil, nil
}

View File

@@ -280,25 +280,6 @@ func (o *Batch) InsertBatch(ctx context.Context, startChunkIndex, endChunkIndex
return &newBatch, nil
}
// UpdateSkippedBatches updates the skipped batches in the database.
func (o *Batch) UpdateSkippedBatches(ctx context.Context) (uint64, error) {
provingStatusList := []interface{}{
int(types.ProvingTaskSkipped),
int(types.ProvingTaskFailed),
}
db := o.db.WithContext(ctx)
db = db.Model(&Batch{})
db = db.Where("rollup_status", int(types.RollupCommitted))
db = db.Where("proving_status IN (?)", provingStatusList)
result := db.Update("rollup_status", int(types.RollupFinalizationSkipped))
if result.Error != nil {
return 0, fmt.Errorf("Batch.UpdateSkippedBatches error: %w", result.Error)
}
return uint64(result.RowsAffected), nil
}
// UpdateL2GasOracleStatusAndOracleTxHash updates the L2 gas oracle status and transaction hash for a batch.
func (o *Batch) UpdateL2GasOracleStatusAndOracleTxHash(ctx context.Context, hash string, status types.GasOracleStatus, txHash string) error {
updateFields := make(map[string]interface{})

View File

@@ -202,27 +202,6 @@ func TestBatchOrm(t *testing.T) {
assert.Equal(t, types.RollupPending, rollupStatus[0])
assert.Equal(t, types.RollupPending, rollupStatus[1])
err = batchOrm.UpdateProvingStatus(context.Background(), batchHash1, types.ProvingTaskSkipped)
assert.NoError(t, err)
err = batchOrm.UpdateRollupStatus(context.Background(), batchHash1, types.RollupCommitted)
assert.NoError(t, err)
err = batchOrm.UpdateProvingStatus(context.Background(), batchHash2, types.ProvingTaskFailed)
assert.NoError(t, err)
err = batchOrm.UpdateRollupStatus(context.Background(), batchHash2, types.RollupCommitted)
assert.NoError(t, err)
count, err = batchOrm.UpdateSkippedBatches(context.Background())
assert.NoError(t, err)
assert.Equal(t, uint64(2), count)
count, err = batchOrm.UpdateSkippedBatches(context.Background())
assert.NoError(t, err)
assert.Equal(t, uint64(0), count)
batch, err := batchOrm.GetBatchByIndex(context.Background(), 1)
assert.NoError(t, err)
assert.Equal(t, types.RollupFinalizationSkipped, types.RollupStatus(batch.RollupStatus))
err = batchOrm.UpdateProvingStatus(context.Background(), batchHash2, types.ProvingTaskVerified)
assert.NoError(t, err)

View File

@@ -138,8 +138,6 @@ const (
ProvingStatusUndefined ProvingStatus = iota
// ProvingTaskUnassigned : proving_task is not assigned to be proved
ProvingTaskUnassigned
// ProvingTaskSkipped : proving_task is skipped for proof generation
ProvingTaskSkipped
// ProvingTaskAssigned : proving_task is assigned to be proved
ProvingTaskAssigned
// ProvingTaskProved : proof has been returned by prover
@@ -154,8 +152,6 @@ func (ps ProvingStatus) String() string {
switch ps {
case ProvingTaskUnassigned:
return "unassigned"
case ProvingTaskSkipped:
return "skipped"
case ProvingTaskAssigned:
return "assigned"
case ProvingTaskProved:
@@ -210,8 +206,6 @@ const (
RollupFinalizing
// RollupFinalized : finalize transaction is confirmed to layer1
RollupFinalized
// RollupFinalizationSkipped : batch finalization is skipped
RollupFinalizationSkipped
// RollupCommitFailed : rollup commit transaction confirmed but failed
RollupCommitFailed
// RollupFinalizeFailed : rollup finalize transaction is confirmed but failed
@@ -230,8 +224,6 @@ func (s RollupStatus) String() string {
return "RollupFinalizing"
case RollupFinalized:
return "RollupFinalized"
case RollupFinalizationSkipped:
return "RollupFinalizationSkipped"
case RollupCommitFailed:
return "RollupCommitFailed"
case RollupFinalizeFailed:

View File

@@ -52,11 +52,6 @@ func TestProvingStatus(t *testing.T) {
ProvingTaskUnassigned,
"unassigned",
},
{
"ProvingTaskSkipped",
ProvingTaskSkipped,
"skipped",
},
{
"ProvingTaskAssigned",
ProvingTaskAssigned,

View File

@@ -5,7 +5,7 @@ import (
"runtime/debug"
)
var tag = "v4.0.17"
var tag = "v4.0.23"
var commit = func() string {
if info, ok := debug.ReadBuildInfo(); ok {

View File

@@ -9,7 +9,7 @@ libraries = [] # a list of deploy
cache = true # whether to cache builds or not
force = true # whether to ignore the cache (clean build)
evm_version = 'london' # the evm version (by hardfork name)
solc_version = '0.8.10' # override for the solc version (setting this ignores `auto_detect_solc`)
solc_version = '0.8.16' # override for the solc version (setting this ignores `auto_detect_solc`)
optimizer = true # enable or disable the solc optimizer
optimizer_runs = 200 # the number of optimizer runs
verbosity = 2 # the verbosity of tests

View File

@@ -22,7 +22,7 @@ const RINKEBY_PRIVATE_KEY = process.env.RINKEBY_PRIVATE_KEY || "1".repeat(64);
const L1_DEPLOYER_PRIVATE_KEY = process.env.L1_DEPLOYER_PRIVATE_KEY || "1".repeat(64);
const L2_DEPLOYER_PRIVATE_KEY = process.env.L2_DEPLOYER_PRIVATE_KEY || "1".repeat(64);
const SOLC_DEFAULT = "0.8.10";
const SOLC_DEFAULT = "0.8.16";
// try use forge config
let foundry: any;

View File

@@ -66,10 +66,16 @@ contract L1ScrollMessenger is PausableUpgradeable, ScrollMessengerBase, IL1Scrol
mapping(bytes32 => ReplayState) public replayStates;
/// @notice Mapping from queue index to previous replay queue index.
///
/// @dev If a message `x` was replayed 3 times with index `q1`, `q2` and `q3`, the
/// value of `prevReplayIndex` and `replayStates` will be `replayStates[hash(x)].lastIndex = q3`,
/// `replayStates[hash(x)].times = 3`, `prevReplayIndex[q3] = q2`, `prevReplayIndex[q2] = q1`
/// and `prevReplayIndex[q1] = x`.
/// `replayStates[hash(x)].times = 3`, `prevReplayIndex[q3] = q2`, `prevReplayIndex[q2] = q1`,
/// `prevReplayIndex[q1] = x` and `prevReplayIndex[x]=nil`.
///
/// @dev The index `x` that `prevReplayIndex[x]=nil` is used as the termination of the list.
/// Usually we use `0` to represent `nil`, but we cannot distinguish it with the first message
/// with index zero. So a nonzero offset `1` is added to the value of `prevReplayIndex[x]` to
/// avoid such situation.
mapping(uint256 => uint256) public prevReplayIndex;
/***************
@@ -200,11 +206,13 @@ contract L1ScrollMessenger is PausableUpgradeable, ScrollMessengerBase, IL1Scrol
ReplayState memory _replayState = replayStates[_xDomainCalldataHash];
// update the replayed message chain.
if (_replayState.lastIndex == 0) {
// the message has not been replayed before.
prevReplayIndex[_nextQueueIndex] = _messageNonce;
} else {
prevReplayIndex[_nextQueueIndex] = _replayState.lastIndex;
unchecked {
if (_replayState.lastIndex == 0) {
// the message has not been replayed before.
prevReplayIndex[_nextQueueIndex] = _messageNonce + 1;
} else {
prevReplayIndex[_nextQueueIndex] = _replayState.lastIndex + 1;
}
}
_replayState.lastIndex = uint128(_nextQueueIndex);
@@ -265,6 +273,9 @@ contract L1ScrollMessenger is PausableUpgradeable, ScrollMessengerBase, IL1Scrol
IL1MessageQueue(_messageQueue).dropCrossDomainMessage(_lastIndex);
_lastIndex = prevReplayIndex[_lastIndex];
if (_lastIndex == 0) break;
unchecked {
_lastIndex = _lastIndex - 1;
}
}
isL1MessageDropped[_xDomainCalldataHash] = true;

View File

@@ -0,0 +1,78 @@
// SPDX-License-Identifier: MIT
pragma solidity =0.8.16;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
// solhint-disable no-inline-assembly
/// @notice This contract is designed as a proxy contract to forward contract call to some other
/// target contract. There are two roles in the contract: the `owner` is the super admin and the
/// `admin`. The `owner` can change the address of the `admin`. Both roles can forward contract call.
contract Forwarder is Ownable {
/**********
* Events *
**********/
/// @notice Emitted when forward call happens.
/// @param caller The address of the caller.
/// @param target The address of the target contract.
/// @param value The value passed to the target contract.
/// @param data The calldata passed to the target contract.
event Forwarded(address indexed caller, address indexed target, uint256 value, bytes data);
/// @notice Emitted when the address of admin is updated.
/// @param oldAdmin The address of the old admin.
/// @param newAdmin The address of the new admin.
event SetAdmin(address indexed oldAdmin, address indexed newAdmin);
/*************
* Variables *
*************/
/// @notice The address of contract admin.
address public admin;
/***************
* Constructor *
***************/
constructor(address _admin) {
admin = _admin;
emit SetAdmin(address(0), _admin);
}
/************************
* Restricted Functions *
************************/
/// @notice Update the address of admin.
/// @param _newAdmin The address of the new admin.
function setAdmin(address _newAdmin) external onlyOwner {
address _oldAdmin = admin;
admin = _newAdmin;
emit SetAdmin(_oldAdmin, _newAdmin);
}
/// @notice Forward calldata to some target contract.
/// @param _target The address of the target contract.
/// @param _data The data forwarded to the target contract.
function forward(address _target, bytes calldata _data) external payable {
require(msg.sender == owner() || msg.sender == admin, "only owner or admin");
(bool success, ) = _target.call{value: msg.value}(_data);
// bubble up revert reason
if (!success) {
assembly {
let ptr := mload(0x40)
let size := returndatasize()
returndatacopy(ptr, 0, size)
revert(ptr, size)
}
}
emit Forwarded(msg.sender, _target, msg.value, _data);
}
}

View File

@@ -0,0 +1,68 @@
// SPDX-License-Identifier: MIT
pragma solidity =0.8.16;
import {DSTestPlus} from "solmate/test/utils/DSTestPlus.sol";
import {WETH} from "solmate/tokens/WETH.sol";
import {MockTarget} from "./mocks/MockTarget.sol";
import {Forwarder} from "../misc/Forwarder.sol";
import {IL1ScrollMessenger, L1ScrollMessenger} from "../L1/L1ScrollMessenger.sol";
contract ForwarderTest is DSTestPlus {
MockTarget public target;
Forwarder public forwarder;
L1ScrollMessenger internal l1Messenger;
address public admin = address(2);
address public superAdmin = address(3);
function setUp() public {
target = new MockTarget();
forwarder = new Forwarder(admin);
forwarder.transferOwnership(superAdmin);
l1Messenger = new L1ScrollMessenger();
l1Messenger.initialize(address(0), address(0), address(0), address(0));
l1Messenger.transferOwnership(address(forwarder));
}
function testAdminFail() external {
hevm.expectRevert("only owner or admin");
forwarder.forward(address(l1Messenger), hex"00");
hevm.expectRevert("Ownable: caller is not the owner");
forwarder.setAdmin(address(0));
}
function testAdmin() external {
// cast calldata "transferOwnership(address)" 0x0000000000000000000000000000000000000005
// 0xf2fde38b0000000000000000000000000000000000000000000000000000000000000005
hevm.startPrank(admin);
forwarder.forward(
address(l1Messenger),
hex"f2fde38b0000000000000000000000000000000000000000000000000000000000000006"
);
assertEq(address(6), l1Messenger.owner());
hevm.stopPrank();
}
function testForwardSuperAdmin() external {
hevm.startPrank(superAdmin);
forwarder.forward(
address(l1Messenger),
hex"f2fde38b0000000000000000000000000000000000000000000000000000000000000006"
);
assertEq(address(6), l1Messenger.owner());
forwarder.setAdmin(address(0));
assertEq(forwarder.admin(), address(0));
}
function testNestedRevert() external {
hevm.startPrank(superAdmin);
hevm.expectRevert("test error");
forwarder.forward(address(target), hex"38df7677");
}
}

View File

@@ -144,9 +144,9 @@ contract L1ScrollMessengerTest is L1GatewayTestBase {
(_replayTimes, _lastIndex) = l1Messenger.replayStates(hash);
assertEq(_replayTimes, i + 1);
assertEq(_lastIndex, i + 3);
assertEq(l1Messenger.prevReplayIndex(i + 3), i + 2);
assertEq(l1Messenger.prevReplayIndex(i + 3), i + 2 + 1);
for (uint256 j = 0; j <= i; j++) {
assertEq(l1Messenger.prevReplayIndex(i + 3 - j), i + 2 - j);
assertEq(l1Messenger.prevReplayIndex(i + 3 - j), i + 2 - j + 1);
}
}
}
@@ -239,51 +239,76 @@ contract L1ScrollMessengerTest is L1GatewayTestBase {
l1Messenger.updateMaxReplayTimes(10);
// replay 3 times
for (uint256 i = 0; i < 3; i++) {
l1Messenger.replayMessage(address(this), address(0), 0, 0, new bytes(0), 0, address(0));
}
assertEq(messageQueue.nextCrossDomainMessageIndex(), 4);
// replay 1 time
l1Messenger.replayMessage(address(this), address(0), 0, 0, new bytes(0), 0, address(0));
assertEq(messageQueue.nextCrossDomainMessageIndex(), 2);
// only first 3 are skipped
// skip all 2 messages
hevm.startPrank(address(rollup));
messageQueue.popCrossDomainMessage(0, 4, 0x7);
assertEq(messageQueue.pendingQueueIndex(), 4);
messageQueue.popCrossDomainMessage(0, 2, 0x3);
assertEq(messageQueue.pendingQueueIndex(), 2);
hevm.stopPrank();
// message already dropped or executed, revert
hevm.expectRevert("message already dropped or executed");
l1Messenger.dropMessage(address(this), address(0), 0, 0, new bytes(0));
// send one message with nonce 4 and replay 4 times
l1Messenger.sendMessage(address(0), 0, new bytes(0), 0);
for (uint256 i = 0; i < 4; i++) {
l1Messenger.replayMessage(address(this), address(0), 0, 4, new bytes(0), 0, address(0));
}
assertEq(messageQueue.nextCrossDomainMessageIndex(), 9);
// skip all 5 messages
hevm.startPrank(address(rollup));
messageQueue.popCrossDomainMessage(4, 5, 0x1f);
assertEq(messageQueue.pendingQueueIndex(), 9);
hevm.stopPrank();
for (uint256 i = 4; i < 9; ++i) {
for (uint256 i = 0; i < 2; ++i) {
assertGt(uint256(messageQueue.getCrossDomainMessage(i)), 0);
}
hevm.expectEmit(false, false, false, true);
emit OnDropMessageCalled(new bytes(0));
l1Messenger.dropMessage(address(this), address(0), 0, 4, new bytes(0));
for (uint256 i = 4; i < 9; ++i) {
l1Messenger.dropMessage(address(this), address(0), 0, 0, new bytes(0));
for (uint256 i = 0; i < 2; ++i) {
assertEq(messageQueue.getCrossDomainMessage(i), bytes32(0));
}
// send one message with nonce 2 and replay 3 times
l1Messenger.sendMessage(address(0), 0, new bytes(0), 0);
assertEq(messageQueue.nextCrossDomainMessageIndex(), 3);
for (uint256 i = 0; i < 3; i++) {
l1Messenger.replayMessage(address(this), address(0), 0, 2, new bytes(0), 0, address(0));
}
assertEq(messageQueue.nextCrossDomainMessageIndex(), 6);
// only first 3 are skipped
hevm.startPrank(address(rollup));
messageQueue.popCrossDomainMessage(2, 4, 0x7);
assertEq(messageQueue.pendingQueueIndex(), 6);
hevm.stopPrank();
// message already dropped or executed, revert
hevm.expectRevert("message already dropped or executed");
l1Messenger.dropMessage(address(this), address(0), 0, 2, new bytes(0));
// send one message with nonce 6 and replay 4 times
l1Messenger.sendMessage(address(0), 0, new bytes(0), 0);
for (uint256 i = 0; i < 4; i++) {
l1Messenger.replayMessage(address(this), address(0), 0, 6, new bytes(0), 0, address(0));
}
assertEq(messageQueue.nextCrossDomainMessageIndex(), 11);
// skip all 5 messages
hevm.startPrank(address(rollup));
messageQueue.popCrossDomainMessage(6, 5, 0x1f);
assertEq(messageQueue.pendingQueueIndex(), 11);
hevm.stopPrank();
for (uint256 i = 6; i < 11; ++i) {
assertGt(uint256(messageQueue.getCrossDomainMessage(i)), 0);
}
hevm.expectEmit(false, false, false, true);
emit OnDropMessageCalled(new bytes(0));
l1Messenger.dropMessage(address(this), address(0), 0, 6, new bytes(0));
for (uint256 i = 6; i < 11; ++i) {
assertEq(messageQueue.getCrossDomainMessage(i), bytes32(0));
}
// Message already dropped, revert
hevm.expectRevert("Message already dropped");
l1Messenger.dropMessage(address(this), address(0), 0, 4, new bytes(0));
l1Messenger.dropMessage(address(this), address(0), 0, 0, new bytes(0));
hevm.expectRevert("Message already dropped");
l1Messenger.dropMessage(address(this), address(0), 0, 6, new bytes(0));
// replay dropped message, revert
hevm.expectRevert("Message already dropped");
l1Messenger.replayMessage(address(this), address(0), 0, 4, new bytes(0), 0, address(0));
l1Messenger.replayMessage(address(this), address(0), 0, 0, new bytes(0), 0, address(0));
hevm.expectRevert("Message already dropped");
l1Messenger.replayMessage(address(this), address(0), 0, 6, new bytes(0), 0, address(0));
}
function onDropMessage(bytes memory message) external payable {

View File

@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity =0.8.16;
contract MockTarget {
event ABC(uint256);
function err() external pure {
revert("test error");
}
function succeed() external {
emit ABC(1);
}
}

View File

@@ -35,7 +35,7 @@ create table chunk
);
comment
on column chunk.proving_status is 'undefined, unassigned, skipped, assigned, proved, verified, failed';
on column chunk.proving_status is 'undefined, unassigned, assigned, proved, verified, failed';
create unique index chunk_index_uindex
on chunk (index);

View File

@@ -49,10 +49,10 @@ comment
on column batch.chunk_proofs_status is 'undefined, pending, ready';
comment
on column batch.proving_status is 'undefined, unassigned, skipped, assigned, proved, verified, failed';
on column batch.proving_status is 'undefined, unassigned, assigned, proved, verified, failed';
comment
on column batch.rollup_status is 'undefined, pending, committing, committed, finalizing, finalized, finalization_skipped, commit_failed, finalize_failed';
on column batch.rollup_status is 'undefined, pending, committing, committed, finalizing, finalized, commit_failed, finalize_failed';
-- +goose StatementEnd