mirror of
https://github.com/scroll-tech/scroll.git
synced 2026-04-23 03:00:50 -04:00
feat(bridge): confirm block based on "safe" and "finalized" tags (#265)
This commit is contained in:
committed by
GitHub
parent
d9bc0842cc
commit
5fdd2c609c
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"l1_config": {
|
||||
"confirmations": 6,
|
||||
"confirmations": "number=6",
|
||||
"endpoint": "https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161",
|
||||
"l1_messenger_address": "0x0000000000000000000000000000000000000000",
|
||||
"rollup_contract_address": "0x0000000000000000000000000000000000000000",
|
||||
@@ -11,7 +11,7 @@
|
||||
"endpoint": "/var/lib/jenkins/workspace/SequencerPipeline/MyPrivateNetwork/geth.ipc",
|
||||
"check_pending_time": 3,
|
||||
"escalate_blocks": 100,
|
||||
"confirmations": 1,
|
||||
"confirmations": "number=1",
|
||||
"escalate_multiple_num": 11,
|
||||
"escalate_multiple_den": 10,
|
||||
"max_gas_price": 10000000000,
|
||||
@@ -24,7 +24,7 @@
|
||||
}
|
||||
},
|
||||
"l2_config": {
|
||||
"confirmations": 1,
|
||||
"confirmations": "number=1",
|
||||
"endpoint": "/var/lib/jenkins/workspace/SequencerPipeline/MyPrivateNetwork/geth.ipc",
|
||||
"l2_messenger_address": "0x0000000000000000000000000000000000000000",
|
||||
"relayer_config": {
|
||||
@@ -34,7 +34,7 @@
|
||||
"endpoint": "https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161",
|
||||
"check_pending_time": 10,
|
||||
"escalate_blocks": 100,
|
||||
"confirmations": 6,
|
||||
"confirmations": "number=6",
|
||||
"escalate_multiple_num": 11,
|
||||
"escalate_multiple_den": 10,
|
||||
"max_gas_price": 10000000000,
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package config
|
||||
|
||||
import "github.com/scroll-tech/go-ethereum/common"
|
||||
import (
|
||||
"scroll-tech/bridge/utils"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
)
|
||||
|
||||
// L1Config loads l1eth configuration items.
|
||||
type L1Config struct {
|
||||
// Confirmations block height confirmations number.
|
||||
Confirmations uint64 `json:"confirmations"`
|
||||
Confirmations utils.ConfirmationParams `json:"confirmations"`
|
||||
// l1 eth node url.
|
||||
Endpoint string `json:"endpoint"`
|
||||
// The start height to sync event from layer 1
|
||||
|
||||
@@ -3,13 +3,15 @@ package config
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"scroll-tech/bridge/utils"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
)
|
||||
|
||||
// L2Config loads l2geth configuration items.
|
||||
type L2Config struct {
|
||||
// Confirmations block height confirmations number.
|
||||
Confirmations uint64 `json:"confirmations"`
|
||||
Confirmations utils.ConfirmationParams `json:"confirmations"`
|
||||
// l2geth node url.
|
||||
Endpoint string `json:"endpoint"`
|
||||
// The messenger contract address deployed on layer 2 chain.
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"scroll-tech/bridge/utils"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
"github.com/scroll-tech/go-ethereum/crypto"
|
||||
)
|
||||
@@ -19,7 +21,7 @@ type SenderConfig struct {
|
||||
// The number of blocks to wait to escalate increase gas price of the transaction.
|
||||
EscalateBlocks uint64 `json:"escalate_blocks"`
|
||||
// The gap number between a block be confirmed and the latest block.
|
||||
Confirmations uint64 `json:"confirmations"`
|
||||
Confirmations utils.ConfirmationParams `json:"confirmations"`
|
||||
// The numerator of gas price escalate multiple.
|
||||
EscalateMultipleNum uint64 `json:"escalate_multiple_num"`
|
||||
// The denominator of gas price escalate multiple.
|
||||
|
||||
@@ -5,7 +5,7 @@ go 1.18
|
||||
require (
|
||||
github.com/iden3/go-iden3-crypto v0.0.13
|
||||
github.com/orcaman/concurrent-map v1.0.0
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20230112091133-2891916a0f81
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20230127005331-08ba436d8bb3
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/urfave/cli/v2 v2.10.2
|
||||
golang.org/x/sync v0.1.0
|
||||
@@ -32,7 +32,7 @@ require (
|
||||
github.com/rjeczalik/notify v0.9.1 // indirect
|
||||
github.com/rogpeppe/go-internal v1.8.1 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/scroll-tech/zktrie v0.3.1 // indirect
|
||||
github.com/scroll-tech/zktrie v0.4.1 // indirect
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||
github.com/tklauser/numcpus v0.4.0 // indirect
|
||||
|
||||
@@ -350,11 +350,11 @@ github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20230112091133-2891916a0f81 h1:Gm18RZ9WTR2Dupumr60E2m1Noe+l9/lITt6iRyxxZoc=
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20230112091133-2891916a0f81/go.mod h1:jurIpDQ0hqtp9//xxeWzr8X9KMP/+TYn+vz3K1wZrv0=
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20230127005331-08ba436d8bb3 h1:kYPsjs9hr579hMFuHXrOy0zveCLHD/kC+PGv9wnadvM=
|
||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20230127005331-08ba436d8bb3/go.mod h1:jurIpDQ0hqtp9//xxeWzr8X9KMP/+TYn+vz3K1wZrv0=
|
||||
github.com/scroll-tech/zktrie v0.3.0/go.mod h1:CuJFlG1/soTJJBAySxCZgTF7oPvd5qF6utHOEciC43Q=
|
||||
github.com/scroll-tech/zktrie v0.3.1 h1:HlR+fMBdjXX1/7cUMqpUgGEhGy/3vN1JpwQ0ovg/Ys8=
|
||||
github.com/scroll-tech/zktrie v0.3.1/go.mod h1:CuJFlG1/soTJJBAySxCZgTF7oPvd5qF6utHOEciC43Q=
|
||||
github.com/scroll-tech/zktrie v0.4.1 h1:+enbK4g6/Pj76Do6Pz+ncaqJuYczua+yhP3Phs0pD3E=
|
||||
github.com/scroll-tech/zktrie v0.4.1/go.mod h1:XvNo7vAk8yxNyTjBDj5WIiFzYW4bx/gJ78+NK6Zn6Uk=
|
||||
github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
|
||||
github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
|
||||
@@ -26,7 +26,7 @@ func New(ctx context.Context, cfg *config.L1Config, orm database.OrmFactory) (*B
|
||||
return nil, err
|
||||
}
|
||||
|
||||
relayer, err := NewLayer1Relayer(ctx, int64(cfg.Confirmations), orm, cfg.RelayerConfig)
|
||||
relayer, err := NewLayer1Relayer(ctx, orm, cfg.RelayerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ type Layer1Relayer struct {
|
||||
}
|
||||
|
||||
// NewLayer1Relayer will return a new instance of Layer1RelayerClient
|
||||
func NewLayer1Relayer(ctx context.Context, l1ConfirmNum int64, db orm.L1MessageOrm, cfg *config.RelayerConfig) (*Layer1Relayer, error) {
|
||||
func NewLayer1Relayer(ctx context.Context, db orm.L1MessageOrm, cfg *config.RelayerConfig) (*Layer1Relayer, error) {
|
||||
l2MessengerABI, err := bridge_abi.L2MessengerMetaData.GetAbi()
|
||||
if err != nil {
|
||||
log.Warn("new L2MessengerABI failed", "err", err)
|
||||
|
||||
@@ -19,7 +19,7 @@ func testCreateNewL1Relayer(t *testing.T) {
|
||||
assert.NoError(t, migrate.ResetDB(db.GetDB().DB))
|
||||
defer db.Close()
|
||||
|
||||
relayer, err := NewLayer1Relayer(context.Background(), 1, db, cfg.L2Config.RelayerConfig)
|
||||
relayer, err := NewLayer1Relayer(context.Background(), db, cfg.L2Config.RelayerConfig)
|
||||
assert.NoError(t, err)
|
||||
defer relayer.Stop()
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ type Watcher struct {
|
||||
db database.OrmFactory
|
||||
|
||||
// The number of new blocks to wait for a block to be confirmed
|
||||
confirmations uint64
|
||||
confirmations utils.ConfirmationParams
|
||||
messengerAddress common.Address
|
||||
messengerABI *abi.ABI
|
||||
|
||||
@@ -58,7 +58,7 @@ type Watcher struct {
|
||||
|
||||
// NewWatcher returns a new instance of Watcher. The instance will be not fully prepared,
|
||||
// and still needs to be finalized and ran by calling `watcher.Start`.
|
||||
func NewWatcher(ctx context.Context, client *ethclient.Client, startHeight uint64, confirmations uint64, messengerAddress common.Address, rollupAddress common.Address, db database.OrmFactory) *Watcher {
|
||||
func NewWatcher(ctx context.Context, client *ethclient.Client, startHeight uint64, confirmations utils.ConfirmationParams, messengerAddress common.Address, rollupAddress common.Address, db database.OrmFactory) *Watcher {
|
||||
savedHeight, err := db.GetLayer1LatestWatchedHeight()
|
||||
if err != nil {
|
||||
log.Warn("Failed to fetch height from db", "err", err)
|
||||
@@ -96,12 +96,13 @@ func (w *Watcher) Start() {
|
||||
return
|
||||
|
||||
default:
|
||||
blockNumber, err := w.client.BlockNumber(w.ctx)
|
||||
number, err := utils.GetLatestConfirmedBlockNumber(w.ctx, w.client, w.confirmations)
|
||||
if err != nil {
|
||||
log.Error("Failed to get block number", "err", err)
|
||||
log.Error("failed to get block number", "err", err)
|
||||
continue
|
||||
}
|
||||
if err := w.FetchContractEvent(blockNumber); err != nil {
|
||||
|
||||
if err := w.FetchContractEvent(number); err != nil {
|
||||
log.Error("Failed to fetch bridge contract", "err", err)
|
||||
}
|
||||
}
|
||||
@@ -123,7 +124,7 @@ func (w *Watcher) FetchContractEvent(blockHeight uint64) error {
|
||||
}()
|
||||
|
||||
fromBlock := int64(w.processedMsgHeight) + 1
|
||||
toBlock := int64(blockHeight) - int64(w.confirmations)
|
||||
toBlock := int64(blockHeight)
|
||||
|
||||
for from := fromBlock; from <= toBlock; from += contractEventsBlocksFetchLimit {
|
||||
to := from + contractEventsBlocksFetchLimit - 1
|
||||
|
||||
@@ -45,7 +45,7 @@ type WatcherClient struct {
|
||||
|
||||
orm database.OrmFactory
|
||||
|
||||
confirmations uint64
|
||||
confirmations utils.ConfirmationParams
|
||||
messengerAddress common.Address
|
||||
messengerABI *abi.ABI
|
||||
|
||||
@@ -59,7 +59,7 @@ type WatcherClient struct {
|
||||
}
|
||||
|
||||
// NewL2WatcherClient take a l2geth instance to generate a l2watcherclient instance
|
||||
func NewL2WatcherClient(ctx context.Context, client *ethclient.Client, confirmations uint64, bpCfg *config.BatchProposerConfig, messengerAddress common.Address, orm database.OrmFactory) *WatcherClient {
|
||||
func NewL2WatcherClient(ctx context.Context, client *ethclient.Client, confirmations utils.ConfirmationParams, bpCfg *config.BatchProposerConfig, messengerAddress common.Address, orm database.OrmFactory) *WatcherClient {
|
||||
savedHeight, err := orm.GetLayer2LatestWatchedHeight()
|
||||
if err != nil {
|
||||
log.Warn("fetch height from db failed", "err", err)
|
||||
@@ -100,19 +100,12 @@ func (w *WatcherClient) Start() {
|
||||
return
|
||||
|
||||
case <-ticker.C:
|
||||
// get current height
|
||||
number, err := w.BlockNumber(ctx)
|
||||
number, err := utils.GetLatestConfirmedBlockNumber(ctx, w.Client, w.confirmations)
|
||||
if err != nil {
|
||||
log.Error("failed to get_BlockNumber", "err", err)
|
||||
log.Error("failed to get block number", "err", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if number >= w.confirmations {
|
||||
number = number - w.confirmations
|
||||
} else {
|
||||
number = 0
|
||||
}
|
||||
|
||||
w.tryFetchRunningMissingBlocks(ctx, number)
|
||||
}
|
||||
}
|
||||
@@ -129,19 +122,12 @@ func (w *WatcherClient) Start() {
|
||||
return
|
||||
|
||||
case <-ticker.C:
|
||||
// get current height
|
||||
number, err := w.BlockNumber(ctx)
|
||||
number, err := utils.GetLatestConfirmedBlockNumber(ctx, w.Client, w.confirmations)
|
||||
if err != nil {
|
||||
log.Error("failed to get_BlockNumber", "err", err)
|
||||
log.Error("failed to get block number", "err", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if number >= w.confirmations {
|
||||
number = number - w.confirmations
|
||||
} else {
|
||||
number = 0
|
||||
}
|
||||
|
||||
w.FetchContractEvent(number)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ import (
|
||||
"github.com/scroll-tech/go-ethereum/ethclient"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"scroll-tech/bridge/utils"
|
||||
|
||||
"scroll-tech/bridge/config"
|
||||
"scroll-tech/bridge/mock_bridge"
|
||||
"scroll-tech/bridge/sender"
|
||||
@@ -36,7 +38,7 @@ func testCreateNewWatcherAndStop(t *testing.T) {
|
||||
defer rc.Stop()
|
||||
|
||||
l1cfg := cfg.L1Config
|
||||
l1cfg.RelayerConfig.SenderConfig.Confirmations = 0
|
||||
l1cfg.RelayerConfig.SenderConfig.Confirmations = utils.ConfirmationParams{Type: utils.BlockNumberConfirmation, Number: 0}
|
||||
newSender, err := sender.NewSender(context.Background(), l1cfg.RelayerConfig.SenderConfig, l1cfg.RelayerConfig.MessageSenderPrivateKeys)
|
||||
assert.NoError(t, err)
|
||||
|
||||
@@ -190,7 +192,8 @@ func testFetchMultipleSentMessageInOneBlock(t *testing.T) {
|
||||
}
|
||||
|
||||
func prepareRelayerClient(l2Cli *ethclient.Client, bpCfg *config.BatchProposerConfig, db database.OrmFactory, contractAddr common.Address) *WatcherClient {
|
||||
return NewL2WatcherClient(context.Background(), l2Cli, 0, bpCfg, contractAddr, db)
|
||||
confirmations := utils.ConfirmationParams{Type: utils.BlockNumberConfirmation, Number: 0}
|
||||
return NewL2WatcherClient(context.Background(), l2Cli, confirmations, bpCfg, contractAddr, db)
|
||||
}
|
||||
|
||||
func prepareAuth(t *testing.T, l2Cli *ethclient.Client, privateKey *ecdsa.PrivateKey) *bind.TransactOpts {
|
||||
|
||||
@@ -20,6 +20,8 @@ import (
|
||||
"github.com/scroll-tech/go-ethereum/ethclient"
|
||||
"github.com/scroll-tech/go-ethereum/log"
|
||||
|
||||
"scroll-tech/bridge/utils"
|
||||
|
||||
"scroll-tech/bridge/config"
|
||||
)
|
||||
|
||||
@@ -361,10 +363,12 @@ func (s *Sender) resubmitTransaction(feeData *FeeData, auth *bind.TransactOpts,
|
||||
return s.createAndSendTx(auth, feeData, tx.To(), tx.Value(), tx.Data(), &nonce)
|
||||
}
|
||||
|
||||
// CheckPendingTransaction Check pending transaction given number of blocks to wait before confirmation.
|
||||
func (s *Sender) CheckPendingTransaction(header *types.Header) {
|
||||
// checkPendingTransaction checks the confirmation status of pending transactions against the latest confirmed block number.
|
||||
// If a transaction hasn't been confirmed after a certain number of blocks, it will be resubmitted with an increased gas price.
|
||||
func (s *Sender) checkPendingTransaction(header *types.Header, confirmed uint64) {
|
||||
number := header.Number.Uint64()
|
||||
atomic.StoreUint64(&s.blockNumber, number)
|
||||
|
||||
if s.config.TxType == DynamicFeeTxType {
|
||||
if header.BaseFee != nil {
|
||||
atomic.StoreUint64(&s.baseFeePerGas, header.BaseFee.Uint64())
|
||||
@@ -372,6 +376,7 @@ func (s *Sender) CheckPendingTransaction(header *types.Header) {
|
||||
log.Error("DynamicFeeTxType not supported, header.BaseFee nil")
|
||||
}
|
||||
}
|
||||
|
||||
s.pendingTxs.Range(func(key, value interface{}) bool {
|
||||
// ignore empty id, since we use empty id to occupy pending task
|
||||
if value == nil || reflect.ValueOf(value).IsNil() {
|
||||
@@ -381,7 +386,7 @@ func (s *Sender) CheckPendingTransaction(header *types.Header) {
|
||||
pending := value.(*PendingTransaction)
|
||||
receipt, err := s.client.TransactionReceipt(s.ctx, pending.tx.Hash())
|
||||
if (err == nil) && (receipt != nil) {
|
||||
if number >= receipt.BlockNumber.Uint64()+s.config.Confirmations {
|
||||
if receipt.BlockNumber.Uint64() <= confirmed {
|
||||
s.pendingTxs.Delete(key)
|
||||
// send confirm message
|
||||
s.confirmCh <- &Confirmation{
|
||||
@@ -455,7 +460,14 @@ func (s *Sender) loop(ctx context.Context) {
|
||||
log.Error("failed to get latest head", "err", err)
|
||||
continue
|
||||
}
|
||||
s.CheckPendingTransaction(header)
|
||||
|
||||
confirmed, err := utils.GetLatestConfirmedBlockNumber(s.ctx, s.client, s.config.Confirmations)
|
||||
if err != nil {
|
||||
log.Error("failed to get latest confirmed block number", "err", err)
|
||||
continue
|
||||
}
|
||||
|
||||
s.checkPendingTransaction(header, confirmed)
|
||||
case <-checkBalanceTicker.C:
|
||||
// Check and set balance.
|
||||
_ = s.auths.checkAndSetBalances(ctx)
|
||||
|
||||
@@ -18,6 +18,8 @@ import (
|
||||
|
||||
"scroll-tech/common/docker"
|
||||
|
||||
"scroll-tech/bridge/utils"
|
||||
|
||||
"scroll-tech/bridge/config"
|
||||
"scroll-tech/bridge/sender"
|
||||
)
|
||||
@@ -68,7 +70,7 @@ func testBatchSender(t *testing.T, batchSize int) {
|
||||
}
|
||||
|
||||
senderCfg := cfg.L1Config.RelayerConfig.SenderConfig
|
||||
senderCfg.Confirmations = 0
|
||||
senderCfg.Confirmations = utils.ConfirmationParams{Type: utils.BlockNumberConfirmation, Number: 0}
|
||||
newSender, err := sender.NewSender(context.Background(), senderCfg, privateKeys)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -4,9 +4,12 @@ import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"math/big"
|
||||
"scroll-tech/common/docker"
|
||||
"testing"
|
||||
|
||||
"scroll-tech/common/docker"
|
||||
|
||||
"scroll-tech/bridge/utils"
|
||||
|
||||
"scroll-tech/bridge/config"
|
||||
"scroll-tech/bridge/mock_bridge"
|
||||
|
||||
@@ -63,10 +66,10 @@ func setupEnv(t *testing.T) {
|
||||
// Load config.
|
||||
cfg, err = config.NewConfig("../config.json")
|
||||
assert.NoError(t, err)
|
||||
cfg.L1Config.Confirmations = 0
|
||||
cfg.L1Config.Confirmations = utils.ConfirmationParams{Type: utils.BlockNumberConfirmation, Number: 0}
|
||||
cfg.L1Config.RelayerConfig.MessageSenderPrivateKeys = []*ecdsa.PrivateKey{messagePrivateKey}
|
||||
cfg.L1Config.RelayerConfig.RollupSenderPrivateKeys = []*ecdsa.PrivateKey{rollupPrivateKey}
|
||||
cfg.L2Config.Confirmations = 0
|
||||
cfg.L2Config.Confirmations = utils.ConfirmationParams{Type: utils.BlockNumberConfirmation, Number: 0}
|
||||
cfg.L2Config.RelayerConfig.MessageSenderPrivateKeys = []*ecdsa.PrivateKey{messagePrivateKey}
|
||||
cfg.L2Config.RelayerConfig.RollupSenderPrivateKeys = []*ecdsa.PrivateKey{rollupPrivateKey}
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"scroll-tech/database/orm"
|
||||
"testing"
|
||||
|
||||
"scroll-tech/bridge/utils"
|
||||
|
||||
"scroll-tech/bridge/l1"
|
||||
"scroll-tech/bridge/l2"
|
||||
|
||||
@@ -33,11 +35,12 @@ func testRelayL2MessageSucceed(t *testing.T) {
|
||||
defer l2Relayer.Stop()
|
||||
|
||||
// Create L2Watcher
|
||||
l2Watcher := l2.NewL2WatcherClient(context.Background(), l2Client, 0, l2Cfg.BatchProposerConfig, l2Cfg.L2MessengerAddress, db)
|
||||
confirmations := utils.ConfirmationParams{Type: utils.BlockNumberConfirmation, Number: 0}
|
||||
l2Watcher := l2.NewL2WatcherClient(context.Background(), l2Client, confirmations, l2Cfg.BatchProposerConfig, l2Cfg.L2MessengerAddress, db)
|
||||
|
||||
// Create L1Watcher
|
||||
l1Cfg := cfg.L1Config
|
||||
l1Watcher := l1.NewWatcher(context.Background(), l1Client, 0, 0, l1Cfg.L1MessengerAddress, l1Cfg.RollupContractAddress, db)
|
||||
l1Watcher := l1.NewWatcher(context.Background(), l1Client, 0, confirmations, l1Cfg.L1MessengerAddress, l1Cfg.RollupContractAddress, db)
|
||||
|
||||
// send message through l2 messenger contract
|
||||
nonce, err := l2MessengerInstance.MessageNonce(&bind.CallOpts{})
|
||||
|
||||
@@ -34,7 +34,7 @@ func testCommitBatchAndFinalizeBatch(t *testing.T) {
|
||||
|
||||
// Create L1Watcher
|
||||
l1Cfg := cfg.L1Config
|
||||
l1Watcher := l1.NewWatcher(context.Background(), l1Client, 0, 0, l1Cfg.L1MessengerAddress, l1Cfg.RollupContractAddress, db)
|
||||
l1Watcher := l1.NewWatcher(context.Background(), l1Client, 0, l1Cfg.Confirmations, l1Cfg.L1MessengerAddress, l1Cfg.RollupContractAddress, db)
|
||||
|
||||
// add some blocks to db
|
||||
var traces []*types.BlockTrace
|
||||
|
||||
144
bridge/utils/confirmation.go
Normal file
144
bridge/utils/confirmation.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/core/types"
|
||||
"github.com/scroll-tech/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
var pattern = regexp.MustCompile(`^number=(\d{1,3})$`)
|
||||
|
||||
// ConfirmationType defines the type of confirmation logic used by the watcher or the relayer.
|
||||
type ConfirmationType int
|
||||
|
||||
const (
|
||||
// FinalizedTagConfirmation means that we consider a block confirmed based on the "finalized" Ethereum tag.
|
||||
FinalizedTagConfirmation ConfirmationType = iota
|
||||
|
||||
// SafeTagConfirmation means that we consider a block confirmed based on the "safe" Ethereum tag.
|
||||
SafeTagConfirmation
|
||||
|
||||
// BlockNumberConfirmation means that we consider a block confirmed after waiting for a certain number of blocks.
|
||||
BlockNumberConfirmation
|
||||
)
|
||||
|
||||
// ConfirmationParams defines the confirmation configuration parameters used by the watcher or the relayer.
|
||||
type ConfirmationParams struct {
|
||||
// Type shows whether we confirm by specific block tags or by block number.
|
||||
Type ConfirmationType
|
||||
|
||||
// Number specifies the number of blocks after which a block is considered confirmed.
|
||||
// This field can only be used when Type is set to Number.
|
||||
Number uint64
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements custom JSON decoding from JSON string to ConfirmationParams.
|
||||
func (c *ConfirmationParams) UnmarshalJSON(input []byte) error {
|
||||
var raw string
|
||||
|
||||
if err := json.Unmarshal(input, &raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if raw == "finalized" {
|
||||
c.Type = FinalizedTagConfirmation
|
||||
return nil
|
||||
}
|
||||
|
||||
if raw == "safe" {
|
||||
c.Type = SafeTagConfirmation
|
||||
return nil
|
||||
}
|
||||
|
||||
matches := pattern.FindStringSubmatch(raw)
|
||||
if len(matches) != 2 {
|
||||
return fmt.Errorf("invalid configuration value for confirmations: %v", raw)
|
||||
}
|
||||
|
||||
number, err := strconv.Atoi(matches[1])
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid configuration value for confirmations: %v", raw)
|
||||
}
|
||||
|
||||
c.Type = BlockNumberConfirmation
|
||||
c.Number = uint64(number)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements custom JSON encoding from ConfirmationParams to JSON string.
|
||||
func (c *ConfirmationParams) MarshalJSON() ([]byte, error) {
|
||||
var raw string
|
||||
|
||||
switch c.Type {
|
||||
case FinalizedTagConfirmation:
|
||||
raw = "finalized"
|
||||
|
||||
case SafeTagConfirmation:
|
||||
raw = "safe"
|
||||
|
||||
case BlockNumberConfirmation:
|
||||
raw = fmt.Sprintf("number=%d", c.Number)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unable to marshal unknown confirmation type: %v", c.Type)
|
||||
}
|
||||
|
||||
return json.Marshal(&raw)
|
||||
}
|
||||
|
||||
type ethClient interface {
|
||||
BlockNumber(ctx context.Context) (uint64, error)
|
||||
HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)
|
||||
}
|
||||
|
||||
// GetLatestConfirmedBlockNumber queries the RPC provider and returns the latest
|
||||
// confirmed block number according to the provided confirmation parameters.
|
||||
func GetLatestConfirmedBlockNumber(ctx context.Context, client ethClient, confirmations ConfirmationParams) (uint64, error) {
|
||||
switch confirmations.Type {
|
||||
// use eth_getBlockByNumber and a tag
|
||||
case FinalizedTagConfirmation:
|
||||
case SafeTagConfirmation:
|
||||
var tag *big.Int
|
||||
|
||||
if confirmations.Type == FinalizedTagConfirmation {
|
||||
tag = big.NewInt(int64(rpc.FinalizedBlockNumber))
|
||||
} else {
|
||||
tag = big.NewInt(int64(rpc.SafeBlockNumber))
|
||||
}
|
||||
|
||||
header, err := client.HeaderByNumber(ctx, tag)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if !header.Number.IsUint64() {
|
||||
return 0, fmt.Errorf("received invalid block number: %v", header.Number)
|
||||
}
|
||||
|
||||
return header.Number.Uint64(), nil
|
||||
|
||||
// use eth_blockNumber
|
||||
case BlockNumberConfirmation:
|
||||
number, err := client.BlockNumber(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if number >= confirmations.Number {
|
||||
return number - confirmations.Number, nil
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
|
||||
default:
|
||||
return 0, fmt.Errorf("unknown confirmation type: %v", confirmations.Type)
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
100
bridge/utils/confirmation_test.go
Normal file
100
bridge/utils/confirmation_test.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package utils_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"scroll-tech/bridge/utils"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/core/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestUnmarshalJSON(t *testing.T) {
|
||||
var params utils.ConfirmationParams
|
||||
|
||||
decoder := json.NewDecoder(strings.NewReader(`"finalized"`))
|
||||
decoder.DisallowUnknownFields()
|
||||
err := decoder.Decode(¶ms)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, utils.FinalizedTagConfirmation, params.Type)
|
||||
|
||||
decoder = json.NewDecoder(strings.NewReader(`"safe"`))
|
||||
decoder.DisallowUnknownFields()
|
||||
err = decoder.Decode(¶ms)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, utils.SafeTagConfirmation, params.Type)
|
||||
|
||||
decoder = json.NewDecoder(strings.NewReader(`"number=6"`))
|
||||
decoder.DisallowUnknownFields()
|
||||
err = decoder.Decode(¶ms)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, utils.BlockNumberConfirmation, params.Type)
|
||||
assert.Equal(t, uint64(6), params.Number)
|
||||
|
||||
decoder = json.NewDecoder(strings.NewReader(`"number=999"`))
|
||||
decoder.DisallowUnknownFields()
|
||||
err = decoder.Decode(¶ms)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, utils.BlockNumberConfirmation, params.Type)
|
||||
assert.Equal(t, uint64(999), params.Number)
|
||||
|
||||
decoder = json.NewDecoder(strings.NewReader(`"number=1000"`))
|
||||
decoder.DisallowUnknownFields()
|
||||
err = decoder.Decode(¶ms)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
decoder = json.NewDecoder(strings.NewReader(`"number=6x"`))
|
||||
decoder.DisallowUnknownFields()
|
||||
err = decoder.Decode(¶ms)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
decoder = json.NewDecoder(strings.NewReader(`"latest"`))
|
||||
decoder.DisallowUnknownFields()
|
||||
err = decoder.Decode(¶ms)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestMarshalJSON(t *testing.T) {
|
||||
bytes, err := json.Marshal(&utils.ConfirmationParams{Type: utils.FinalizedTagConfirmation, Number: 6})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, `"finalized"`, string(bytes))
|
||||
|
||||
bytes, err = json.Marshal(&utils.ConfirmationParams{Type: utils.SafeTagConfirmation, Number: 6})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, `"safe"`, string(bytes))
|
||||
|
||||
bytes, err = json.Marshal(&utils.ConfirmationParams{Type: utils.BlockNumberConfirmation, Number: 6})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, `"number=6"`, string(bytes))
|
||||
}
|
||||
|
||||
type MockEthClient struct {
|
||||
val uint64
|
||||
}
|
||||
|
||||
func (e MockEthClient) BlockNumber(ctx context.Context) (uint64, error) {
|
||||
return e.val, nil
|
||||
}
|
||||
|
||||
func (e MockEthClient) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
|
||||
return &types.Header{Number: new(big.Int).SetUint64(e.val)}, nil
|
||||
}
|
||||
|
||||
func TestGetLatestConfirmedBlockNumber(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
client := MockEthClient{}
|
||||
|
||||
client.val = 5
|
||||
confirmed, err := utils.GetLatestConfirmedBlockNumber(ctx, &client, utils.ConfirmationParams{Type: utils.BlockNumberConfirmation, Number: 6})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, uint64(0), confirmed)
|
||||
|
||||
client.val = 7
|
||||
confirmed, err = utils.GetLatestConfirmedBlockNumber(ctx, &client, utils.ConfirmationParams{Type: utils.BlockNumberConfirmation, Number: 6})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, uint64(1), confirmed)
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"runtime/debug"
|
||||
)
|
||||
|
||||
var tag = "prealpha-v11.20"
|
||||
var tag = "prealpha-v12.0"
|
||||
|
||||
var commit = func() string {
|
||||
if info, ok := debug.ReadBuildInfo(); ok {
|
||||
|
||||
@@ -28,6 +28,8 @@ import (
|
||||
"scroll-tech/common/cmd"
|
||||
"scroll-tech/common/docker"
|
||||
|
||||
"scroll-tech/bridge/utils"
|
||||
|
||||
_ "scroll-tech/coordinator/cmd/app"
|
||||
coordinatorConfig "scroll-tech/coordinator/config"
|
||||
)
|
||||
@@ -119,7 +121,7 @@ func runSender(t *testing.T, endpoint string) *sender.Sender {
|
||||
Endpoint: endpoint,
|
||||
CheckPendingTime: 3,
|
||||
EscalateBlocks: 100,
|
||||
Confirmations: 0,
|
||||
Confirmations: utils.ConfirmationParams{Type: utils.BlockNumberConfirmation, Number: 0},
|
||||
EscalateMultipleNum: 11,
|
||||
EscalateMultipleDen: 10,
|
||||
TxType: "LegacyTx",
|
||||
|
||||
Reference in New Issue
Block a user