Browse Source

Use init SC vars and start block from events

Previously the Synchronizer required the initial variables of the smart
contracts to be passed as a configuration parameter (that the node took from
the configuration file).  The same applied to the blockNumber.

The last update of the smart contracts introduced events for each smart
contract constructor (initializer), which allows querying the initial variables
as well as the initial block number for each smart contract.

Now the synchronizer uses this information, and thus the initial variables and
the starting block numbers have been removed from the configuration.
feature/sql-semaphore1
Eduard S 4 years ago
parent
commit
b59f790c04
10 changed files with 194 additions and 146 deletions
  1. +0
    -64
      cli/node/cfg.buidler.toml
  2. +2
    -5
      config/config.go
  3. +11
    -12
      coordinator/coordinator_test.go
  4. +21
    -0
      eth/auction.go
  5. +22
    -0
      eth/rollup.go
  6. +13
    -0
      eth/wdelayer.go
  7. +6
    -3
      node/node.go
  8. +69
    -50
      synchronizer/synchronizer.go
  9. +2
    -10
      synchronizer/synchronizer_test.go
  10. +48
    -2
      test/ethclient.go

+ 0
- 64
cli/node/cfg.buidler.toml

@ -24,11 +24,6 @@ URL = "http://localhost:8545"
SyncLoopInterval = "1s"
StatsRefreshPeriod = "1s"
[Synchronizer.StartBlockNum]
Rollup = 19
Auction = 17
WDelayer = 15
[SmartContracts]
Rollup = "0x8EEaea23686c319133a7cC110b840d1591d9AeE0"
Auction = "0x317113D2593e3efF1FfAE0ba2fF7A61861Df7ae5"
@ -36,65 +31,6 @@ WDelayer = "0x5E0816F0f8bC560cB2B9e9C87187BeCac8c2021F"
TokenHEZ = "0x5D94e3e7aeC542aB0F9129B9a7BAdeb5B3Ca0f77"
TokenHEZName = "Hermez Network Token"
[Synchronizer.InitialVariables.Auction]
DonationAddress = "0x0000000000000000000000000000000000000001"
BootCoordinator = "0xb4124cEB3451635DAcedd11767f004d8a28c6eE7"
BootCoordinatorURL = "https://boot.coordinator.io"
DefaultSlotSetBid = [
"10000000000000000000",
"10000000000000000000",
"10000000000000000000",
"10000000000000000000",
"10000000000000000000",
"10000000000000000000",
]
DefaultSlotSetBidSlotNum = 0
ClosedAuctionSlots = 2
OpenAuctionSlots = 4320
AllocationRatio = [4000, 4000, 2000]
Outbidding = 1000
SlotDeadline = 20
[Synchronizer.InitialVariables.WDelayer]
# HermezRollupAddress =
HermezGovernanceAddress = "0x0000000000000000000000000000000000000001"
EmergencyCouncilAddress = "0x0000000000000000000000000000000000000001"
WithdrawalDelay = 60
EmergencyModeStartingTime = 0
EmergencyMode = false
[Synchronizer.InitialVariables.Rollup]
FeeAddToken = "10"
ForgeL1L2BatchTimeout = 10
WithdrawalDelay = 1209600 # 60 * 60 * 24 * 7 * 2
SafeMode = false
[[Synchronizer.InitialVariables.Rollup.Buckets]]
CeilUSD = 0
Withdrawals = 0
BlockWithdrawalRate = 0
MaxWithdrawals = 0
[[Synchronizer.InitialVariables.Rollup.Buckets]]
CeilUSD = 0
Withdrawals = 0
BlockWithdrawalRate = 0
MaxWithdrawals = 0
[[Synchronizer.InitialVariables.Rollup.Buckets]]
CeilUSD = 0
Withdrawals = 0
BlockWithdrawalRate = 0
MaxWithdrawals = 0
[[Synchronizer.InitialVariables.Rollup.Buckets]]
CeilUSD = 0
Withdrawals = 0
BlockWithdrawalRate = 0
MaxWithdrawals = 0
[[Synchronizer.InitialVariables.Rollup.Buckets]]
CeilUSD = 0
Withdrawals = 0
BlockWithdrawalRate = 0
MaxWithdrawals = 0
[Coordinator]
ForgerAddress = "0x6BB84Cc84D4A34467aD12a2039A312f7029e2071"
ConfirmBlocks = 10

+ 2
- 5
config/config.go

@ -8,7 +8,6 @@ import (
"github.com/BurntSushi/toml"
ethCommon "github.com/ethereum/go-ethereum/common"
"github.com/hermeznetwork/hermez-node/common"
"github.com/hermeznetwork/hermez-node/synchronizer"
"github.com/hermeznetwork/tracerr"
"gopkg.in/go-playground/validator.v9"
)
@ -111,10 +110,8 @@ type Node struct {
URL string `validate:"required"`
} `validate:"required"`
Synchronizer struct {
SyncLoopInterval Duration `validate:"required"`
StatsRefreshPeriod Duration `validate:"required"`
StartBlockNum synchronizer.ConfigStartBlockNum `validate:"required"`
InitialVariables synchronizer.SCVariables `validate:"required"`
SyncLoopInterval Duration `validate:"required"`
StatsRefreshPeriod Duration `validate:"required"`
} `validate:"required"`
SmartContracts struct {
Rollup ethCommon.Address `validate:"required"`

+ 11
- 12
coordinator/coordinator_test.go

@ -185,16 +185,7 @@ func newTestSynchronizer(t *testing.T, ethClient *test.Client, ethClientSetup *t
modules modules) *synchronizer.Synchronizer {
sync, err := synchronizer.NewSynchronizer(ethClient, modules.historyDB, modules.stateDB,
synchronizer.Config{
StartBlockNum: synchronizer.ConfigStartBlockNum{
Rollup: 1,
Auction: 1,
WDelayer: 1,
},
InitialVariables: synchronizer.SCVariables{
Rollup: *ethClientSetup.RollupVariables,
Auction: *ethClientSetup.AuctionVariables,
WDelayer: *ethClientSetup.WDelayerVariables,
},
StatsRefreshPeriod: 0 * time.Second,
})
require.NoError(t, err)
return sync
@ -311,7 +302,11 @@ func TestCoordCanForge(t *testing.T) {
coord := newTestCoordinator(t, forger, ethClient, ethClientSetup, modules)
_, err := ethClient.AuctionSetCoordinator(forger, "https://foo.bar")
require.NoError(t, err)
_, err = ethClient.AuctionBidSimple(2, big.NewInt(9999))
bid, ok := new(big.Int).SetString("12000000000000000000", 10)
if !ok {
panic("bad bid")
}
_, err = ethClient.AuctionBidSimple(2, bid)
require.NoError(t, err)
modules2 := newTestModules(t)
@ -359,7 +354,11 @@ func TestCoordHandleMsgSyncBlock(t *testing.T) {
coord := newTestCoordinator(t, forger, ethClient, ethClientSetup, modules)
_, err := ethClient.AuctionSetCoordinator(forger, "https://foo.bar")
require.NoError(t, err)
_, err = ethClient.AuctionBidSimple(2, big.NewInt(9999))
bid, ok := new(big.Int).SetString("11000000000000000000", 10)
if !ok {
panic("bad bid")
}
_, err = ethClient.AuctionBidSimple(2, bid)
require.NoError(t, err)
var msg MsgSyncBlock

+ 21
- 0
eth/auction.go

@ -69,6 +69,26 @@ type AuctionEventInitialize struct {
AllocationRatio [3]uint16
}
// AuctionVariables returns the AuctionVariables from the initialize event
func (ei *AuctionEventInitialize) AuctionVariables(InitialMinimalBidding *big.Int) *common.AuctionVariables {
return &common.AuctionVariables{
EthBlockNum: 0,
DonationAddress: ei.DonationAddress,
BootCoordinator: ei.BootCoordinatorAddress,
BootCoordinatorURL: ei.BootCoordinatorURL,
DefaultSlotSetBid: [6]*big.Int{
InitialMinimalBidding, InitialMinimalBidding, InitialMinimalBidding,
InitialMinimalBidding, InitialMinimalBidding, InitialMinimalBidding,
},
DefaultSlotSetBidSlotNum: 0,
ClosedAuctionSlots: ei.ClosedAuctionSlots,
OpenAuctionSlots: ei.OpenAuctionSlots,
AllocationRatio: ei.AllocationRatio,
Outbidding: ei.Outbidding,
SlotDeadline: ei.SlotDeadline,
}
}
// AuctionEventNewBid is an event of the Auction Smart Contract
type AuctionEventNewBid struct {
Slot int64
@ -235,6 +255,7 @@ type AuctionInterface interface {
AuctionConstants() (*common.AuctionConstants, error)
AuctionEventsByBlock(blockNum int64) (*AuctionEvents, *ethCommon.Hash, error)
AuctionEventInit() (*AuctionEventInitialize, int64, error)
}
//

+ 22
- 0
eth/rollup.go

@ -59,6 +59,27 @@ type RollupEventInitialize struct {
WithdrawalDelay uint64
}
// RollupVariables returns the RollupVariables from the initialize event
func (ei *RollupEventInitialize) RollupVariables() *common.RollupVariables {
var buckets [common.RollupConstNumBuckets]common.BucketParams
for i := range buckets {
buckets[i] = common.BucketParams{
CeilUSD: big.NewInt(0),
Withdrawals: big.NewInt(0),
BlockWithdrawalRate: big.NewInt(0),
MaxWithdrawals: big.NewInt(0),
}
}
return &common.RollupVariables{
EthBlockNum: 0,
FeeAddToken: ei.FeeAddToken,
ForgeL1L2BatchTimeout: int64(ei.ForgeL1L2BatchTimeout),
WithdrawalDelay: ei.WithdrawalDelay,
Buckets: buckets,
SafeMode: false,
}
}
// RollupEventL1UserTx is an event of the Rollup Smart Contract
type RollupEventL1UserTx struct {
// ToForgeL1TxsNum int64 // QueueIndex *big.Int
@ -245,6 +266,7 @@ type RollupInterface interface {
RollupConstants() (*common.RollupConstants, error)
RollupEventsByBlock(blockNum int64) (*RollupEvents, *ethCommon.Hash, error)
RollupForgeBatchArgs(ethCommon.Hash, uint16) (*RollupForgeBatchArgs, *ethCommon.Address, error)
RollupEventInit() (*RollupEventInitialize, int64, error)
}
//

+ 13
- 0
eth/wdelayer.go

@ -33,6 +33,18 @@ type WDelayerEventInitialize struct {
InitialEmergencyCouncil ethCommon.Address
}
// WDelayerVariables returns the WDelayerVariables from the initialize event
func (ei *WDelayerEventInitialize) WDelayerVariables() *common.WDelayerVariables {
return &common.WDelayerVariables{
EthBlockNum: 0,
HermezGovernanceAddress: ei.InitialHermezGovernanceAddress,
EmergencyCouncilAddress: ei.InitialEmergencyCouncil,
WithdrawalDelay: ei.InitialWithdrawalDelay,
EmergencyModeStartingBlock: 0,
EmergencyMode: false,
}
}
// WDelayerEventDeposit is an event of the WithdrawalDelayer Smart Contract
type WDelayerEventDeposit struct {
Owner ethCommon.Address
@ -124,6 +136,7 @@ type WDelayerInterface interface {
WDelayerEventsByBlock(blockNum int64) (*WDelayerEvents, *ethCommon.Hash, error)
WDelayerConstants() (*common.WDelayerConstants, error)
WDelayerEventInit() (*WDelayerEventInitialize, int64, error)
}
//

+ 6
- 3
node/node.go

@ -4,6 +4,7 @@ import (
"context"
"fmt"
"net/http"
"strings"
"sync"
"time"
@ -120,8 +121,6 @@ func NewNode(mode Mode, cfg *config.Node) (*Node, error) {
}
sync, err := synchronizer.NewSynchronizer(client, historyDB, stateDB, synchronizer.Config{
StartBlockNum: cfg.Synchronizer.StartBlockNum,
InitialVariables: cfg.Synchronizer.InitialVariables,
StatsRefreshPeriod: cfg.Synchronizer.StatsRefreshPeriod.Duration,
})
if err != nil {
@ -390,7 +389,11 @@ func (n *Node) syncLoopFn(lastBlock *common.Block) (*common.Block, time.Duration
stats := n.sync.Stats()
if err != nil {
// case: error
log.Errorw("Synchronizer.Sync", "err", err)
if strings.Contains(err.Error(), "context canceled") {
log.Warnw("Synchronizer.Sync", "err", err)
} else {
log.Errorw("Synchronizer.Sync", "err", err)
}
return nil, n.cfg.Synchronizer.SyncLoopInterval.Duration
} else if discarded != nil {
// case: reorg

+ 69
- 50
synchronizer/synchronizer.go

@ -17,31 +17,6 @@ import (
"github.com/hermeznetwork/tracerr"
)
var (
// ErrNotAbleToSync is used when there is not possible to find a valid block to sync
// ErrNotAbleToSync = errors.New("it has not been possible to synchronize any block")
)
// // SyncronizerState describes the synchronization progress of the smart contracts
// type SyncronizerState struct {
// LastUpdate time.Time // last time this information was updated
// CurrentBatchNum BatchNum // Last batch that was forged on the blockchain
// CurrentBlockNum uint64 // Last block that was mined on Ethereum
// CurrentToForgeL1TxsNum uint32
// LastSyncedBatchNum BatchNum // last batch synchronized by the coordinator
// LastSyncedBlockNum uint64 // last Ethereum block synchronized by the coordinator
// LastSyncedToForgeL1TxsNum uint32
// }
// // SyncStatus is returned by the Status method of the Synchronizer
// type SyncStatus struct {
// CurrentBlock int64
// CurrentBatch BatchNum
// CurrentForgerAddr ethCommon.Address
// NextForgerAddr ethCommon.Address
// Synchronized bool
// }
// Stats of the syncrhonizer
type Stats struct {
Eth struct {
@ -170,12 +145,12 @@ func (s *StatsHolder) batchesPerc(batchNum int64) float64 {
float64(s.Eth.LastBatch)
}
// ConfigStartBlockNum sets the first block used to start tracking the smart
// StartBlockNums sets the first block used to start tracking the smart
// contracts
type ConfigStartBlockNum struct {
Rollup int64 `validate:"required"`
Auction int64 `validate:"required"`
WDelayer int64 `validate:"required"`
type StartBlockNums struct {
Rollup int64
Auction int64
WDelayer int64
}
// SCVariables joins all the smart contract variables in a single struct
@ -202,8 +177,8 @@ type SCConsts struct {
// Config is the Synchronizer configuration
type Config struct {
StartBlockNum ConfigStartBlockNum
InitialVariables SCVariables
// StartBlockNum StartBlockNum
// InitialVariables SCVariables
StatsRefreshPeriod time.Duration
}
@ -217,6 +192,7 @@ type Synchronizer struct {
historyDB *historydb.HistoryDB
stateDB *statedb.StateDB
cfg Config
initVars SCVariables
startBlockNum int64
vars SCVariables
stats *StatsHolder
@ -242,27 +218,38 @@ func NewSynchronizer(ethClient eth.ClientInterface, historyDB *historydb.History
return nil, tracerr.Wrap(fmt.Errorf("NewSynchronizer ethClient.WDelayerConstants(): %w",
err))
}
consts := SCConsts{
Rollup: *rollupConstants,
Auction: *auctionConstants,
WDelayer: *wDelayerConstants,
}
initVars, startBlockNums, err := getInitialVariables(ethClient, &consts)
if err != nil {
return nil, err
}
log.Infow("Synchronizer syncing from smart contract blocks",
"rollup", startBlockNums.Rollup,
"auction", startBlockNums.Auction,
"wdelayer", startBlockNums.WDelayer,
)
// Set startBlockNum to the minimum between Auction, Rollup and
// WDelayer StartBlockNum
startBlockNum := cfg.StartBlockNum.Auction
if cfg.StartBlockNum.Rollup < startBlockNum {
startBlockNum = cfg.StartBlockNum.Rollup
startBlockNum := startBlockNums.Auction
if startBlockNums.Rollup < startBlockNum {
startBlockNum = startBlockNums.Rollup
}
if cfg.StartBlockNum.WDelayer < startBlockNum {
startBlockNum = cfg.StartBlockNum.WDelayer
if startBlockNums.WDelayer < startBlockNum {
startBlockNum = startBlockNums.WDelayer
}
stats := NewStatsHolder(startBlockNum, cfg.StatsRefreshPeriod)
s := &Synchronizer{
ethClient: ethClient,
consts: SCConsts{
Rollup: *rollupConstants,
Auction: *auctionConstants,
WDelayer: *wDelayerConstants,
},
ethClient: ethClient,
consts: consts,
historyDB: historyDB,
stateDB: stateDB,
cfg: cfg,
initVars: *initVars,
startBlockNum: startBlockNum,
stats: stats,
}
@ -596,22 +583,54 @@ func (s *Synchronizer) reorg(uncleBlock *common.Block) (int64, error) {
return block.Num, nil
}
func getInitialVariables(ethClient eth.ClientInterface,
consts *SCConsts) (*SCVariables, *StartBlockNums, error) {
rollupInit, rollupInitBlock, err := ethClient.RollupEventInit()
if err != nil {
return nil, nil, err
}
auctionInit, auctionInitBlock, err := ethClient.AuctionEventInit()
if err != nil {
return nil, nil, err
}
wDelayerInit, wDelayerInitBlock, err := ethClient.WDelayerEventInit()
if err != nil {
return nil, nil, err
}
rollupVars := rollupInit.RollupVariables()
auctionVars := auctionInit.AuctionVariables(consts.Auction.InitialMinimalBidding)
wDelayerVars := wDelayerInit.WDelayerVariables()
return &SCVariables{
Rollup: *rollupVars,
Auction: *auctionVars,
WDelayer: *wDelayerVars,
}, &StartBlockNums{
Rollup: rollupInitBlock,
Auction: auctionInitBlock,
WDelayer: wDelayerInitBlock,
}, nil
}
func (s *Synchronizer) resetState(block *common.Block) error {
rollup, auction, wDelayer, err := s.historyDB.GetSCVars()
// If SCVars are not in the HistoryDB, this is probably the first run
// of the Synchronizer: store the initial vars taken from config
if tracerr.Unwrap(err) == sql.ErrNoRows {
rollup = &s.cfg.InitialVariables.Rollup
auction = &s.cfg.InitialVariables.Auction
wDelayer = &s.cfg.InitialVariables.WDelayer
vars := s.initVars
log.Info("Setting initial SCVars in HistoryDB")
if err = s.historyDB.SetInitialSCVars(rollup, auction, wDelayer); err != nil {
if err = s.historyDB.SetInitialSCVars(&vars.Rollup, &vars.Auction, &vars.WDelayer); err != nil {
return tracerr.Wrap(fmt.Errorf("historyDB.SetInitialSCVars: %w", err))
}
s.vars.Rollup = *vars.Rollup.Copy()
s.vars.Auction = *vars.Auction.Copy()
s.vars.WDelayer = *vars.WDelayer.Copy()
} else if err != nil {
return err
} else {
s.vars.Rollup = *rollup
s.vars.Auction = *auction
s.vars.WDelayer = *wDelayer
}
s.vars.Rollup = *rollup
s.vars.Auction = *auction
s.vars.WDelayer = *wDelayer
batchNum, err := s.historyDB.GetLastBatchNum()
if err != nil && tracerr.Unwrap(err) != sql.ErrNoRows {

+ 2
- 10
synchronizer/synchronizer_test.go

@ -9,6 +9,7 @@ import (
"os"
"sort"
"testing"
"time"
ethCommon "github.com/ethereum/go-ethereum/common"
"github.com/hermeznetwork/hermez-node/common"
@ -300,16 +301,7 @@ func TestSync(t *testing.T) {
// Create Synchronizer
s, err := NewSynchronizer(client, historyDB, stateDB, Config{
StartBlockNum: ConfigStartBlockNum{
Rollup: 1,
Auction: 1,
WDelayer: 1,
},
InitialVariables: SCVariables{
Rollup: *clientSetup.RollupVariables,
Auction: *clientSetup.AuctionVariables,
WDelayer: *clientSetup.WDelayerVariables,
},
StatsRefreshPeriod: 0 * time.Second,
})
require.NoError(t, err)

+ 48
- 2
test/ethclient.go

@ -301,10 +301,20 @@ func NewClientSetupExample() *ClientSetup {
HermezAuctionContract: ethCommon.HexToAddress("0x8E442975805fb1908f43050c9C1A522cB0e28D7b"),
WithdrawDelayerContract: ethCommon.HexToAddress("0x5CB7979cBdbf65719BEE92e4D15b7b7Ed3D79114"),
}
var buckets [common.RollupConstNumBuckets]common.BucketParams
for i := range buckets {
buckets[i] = common.BucketParams{
CeilUSD: big.NewInt(0),
Withdrawals: big.NewInt(0),
BlockWithdrawalRate: big.NewInt(0),
MaxWithdrawals: big.NewInt(0),
}
}
rollupVariables := &common.RollupVariables{
FeeAddToken: big.NewInt(11),
ForgeL1L2BatchTimeout: 9,
WithdrawalDelay: 80,
Buckets: buckets,
}
auctionConstants := &common.AuctionConstants{
BlocksPerSlot: 40,
@ -319,8 +329,9 @@ func NewClientSetupExample() *ClientSetup {
BootCoordinator: ethCommon.HexToAddress("0xE39fEc6224708f0772D2A74fd3f9055A90E0A9f2"),
BootCoordinatorURL: "https://boot.coordinator.com",
DefaultSlotSetBid: [6]*big.Int{
big.NewInt(1000), big.NewInt(1100), big.NewInt(1200),
big.NewInt(1300), big.NewInt(1400), big.NewInt(1500)},
initialMinimalBidding, initialMinimalBidding, initialMinimalBidding,
initialMinimalBidding, initialMinimalBidding, initialMinimalBidding,
},
ClosedAuctionSlots: 2,
OpenAuctionSlots: 4320,
AllocationRatio: [3]uint16{4000, 4000, 2000},
@ -1064,6 +1075,16 @@ func (c *Client) RollupEventsByBlock(blockNum int64) (*eth.RollupEvents, *ethCom
return &block.Rollup.Events, &block.Eth.Hash, nil
}
// RollupEventInit returns the initialize event with its corresponding block number
func (c *Client) RollupEventInit() (*eth.RollupEventInitialize, int64, error) {
vars := c.blocks[0].Rollup.Vars
return &eth.RollupEventInitialize{
ForgeL1L2BatchTimeout: uint8(vars.ForgeL1L2BatchTimeout),
FeeAddToken: vars.FeeAddToken,
WithdrawalDelay: vars.WithdrawalDelay,
}, 1, nil
}
// RollupForgeBatchArgs returns the arguments used in a ForgeBatch call in the Rollup Smart Contract in the given transaction
func (c *Client) RollupForgeBatchArgs(ethTxHash ethCommon.Hash, l1UserTxsLen uint16) (*eth.RollupForgeBatchArgs, *ethCommon.Address, error) {
c.rw.RLock()
@ -1511,6 +1532,21 @@ func (c *Client) AuctionEventsByBlock(blockNum int64) (*eth.AuctionEvents, *ethC
return &block.Auction.Events, &block.Eth.Hash, nil
}
// AuctionEventInit returns the initialize event with its corresponding block number
func (c *Client) AuctionEventInit() (*eth.AuctionEventInitialize, int64, error) {
vars := c.blocks[0].Auction.Vars
return &eth.AuctionEventInitialize{
DonationAddress: vars.DonationAddress,
BootCoordinatorAddress: vars.BootCoordinator,
BootCoordinatorURL: vars.BootCoordinatorURL,
Outbidding: vars.Outbidding,
SlotDeadline: vars.SlotDeadline,
ClosedAuctionSlots: vars.ClosedAuctionSlots,
OpenAuctionSlots: vars.OpenAuctionSlots,
AllocationRatio: vars.AllocationRatio,
}, 1, nil
}
//
// WDelayer
//
@ -1720,6 +1756,16 @@ func (c *Client) WDelayerConstants() (*common.WDelayerConstants, error) {
return c.wDelayerConstants, nil
}
// WDelayerEventInit returns the initialize event with its corresponding block number
func (c *Client) WDelayerEventInit() (*eth.WDelayerEventInitialize, int64, error) {
vars := c.blocks[0].WDelayer.Vars
return &eth.WDelayerEventInitialize{
InitialWithdrawalDelay: vars.WithdrawalDelay,
InitialHermezGovernanceAddress: vars.HermezGovernanceAddress,
InitialEmergencyCouncil: vars.EmergencyCouncilAddress,
}, 1, nil
}
// CtlAddBlocks adds block data to the smarts contracts. The added blocks will
// appear as mined. Not thread safe.
func (c *Client) CtlAddBlocks(blocks []common.BlockData) (err error) {

Loading…
Cancel
Save