feat(bridge): confirm block based on "safe" and "finalized" tags (#265)

This commit is contained in:
Péter Garamvölgyi
2023-02-09 14:20:52 +01:00
committed by GitHub
parent d9bc0842cc
commit 5fdd2c609c
21 changed files with 322 additions and 58 deletions

View File

@@ -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,

View File

@@ -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

View File

@@ -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.

View File

@@ -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.

View File

@@ -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

View File

@@ -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=

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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()

View File

@@ -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

View File

@@ -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)
}
}

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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)

View File

@@ -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}

View File

@@ -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{})

View File

@@ -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

View 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
}

View 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(&params)
assert.Nil(t, err)
assert.Equal(t, utils.FinalizedTagConfirmation, params.Type)
decoder = json.NewDecoder(strings.NewReader(`"safe"`))
decoder.DisallowUnknownFields()
err = decoder.Decode(&params)
assert.Nil(t, err)
assert.Equal(t, utils.SafeTagConfirmation, params.Type)
decoder = json.NewDecoder(strings.NewReader(`"number=6"`))
decoder.DisallowUnknownFields()
err = decoder.Decode(&params)
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(&params)
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(&params)
assert.NotNil(t, err)
decoder = json.NewDecoder(strings.NewReader(`"number=6x"`))
decoder.DisallowUnknownFields()
err = decoder.Decode(&params)
assert.NotNil(t, err)
decoder = json.NewDecoder(strings.NewReader(`"latest"`))
decoder.DisallowUnknownFields()
err = decoder.Decode(&params)
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)
}

View File

@@ -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 {

View File

@@ -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",