mirror of
https://github.com/arnaucube/hermez-node.git
synced 2026-02-07 03:16:45 +01:00
Update missing parts, improve til, and more
- Node - Updated configuration to initialize the interface to all the smart contracts - Common - Moved BlockData and BatchData types to common so that they can be shared among: historydb, til and synchronizer - Remove hash.go (it was never used) - Remove slot.go (it was never used) - Remove smartcontractparams.go (it was never used, and appropriate structs are defined in `eth/`) - Comment state / status method until requirements of this method are properly defined, and move it to Synchronizer - Synchronizer - Simplify `Sync` routine to only sync one block per call, and return useful information. - Use BlockData and BatchData from common - Check that events belong to the expected block hash - In L1Batch, query L1UserTxs from HistoryDB - Fill ERC20 token information - Test AddTokens with test.Client - HistryDB - Use BlockData and BatchData from common - Add `GetAllTokens` method - Uncomment and update GetL1UserTxs (with corresponding tests) - Til - Rename all instances of RegisterToken to AddToken (to follow the smart contract implementation naming) - Use BlockData and BatchData from common - Move testL1CoordinatorTxs and testL2Txs to a separate struct from BatchData in Context - Start Context with BatchNum = 1 (which the protocol defines to be the first batchNum) - In every Batch, set StateRoot and ExitRoot to a non-nil big.Int (zero). - In all L1Txs, if LoadAmount is not used, set it to 0; if Amount is not used, set it to 0; so that no *big.Int is nil. - In L1UserTx, don't set BatchNum, because when L1UserTxs are created and obtained by the synchronizer, the BatchNum is not known yet (it's a synchronizer job to set it) - In L1UserTxs, set `UserOrigin` and set `ToForgeL1TxsNum`.
This commit is contained in:
@@ -3,9 +3,8 @@ package synchronizer
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/hermeznetwork/hermez-node/common"
|
||||
"github.com/hermeznetwork/hermez-node/db/historydb"
|
||||
"github.com/hermeznetwork/hermez-node/db/statedb"
|
||||
@@ -14,26 +13,46 @@ import (
|
||||
)
|
||||
|
||||
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")
|
||||
// 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
|
||||
// }
|
||||
|
||||
// rollupData contains information returned by the Rollup SC
|
||||
type rollupData struct {
|
||||
l1UserTxs []common.L1Tx
|
||||
batches []historydb.BatchData
|
||||
batches []common.BatchData
|
||||
// withdrawals []*common.ExitInfo
|
||||
registeredTokens []common.Token
|
||||
vars *common.RollupVars
|
||||
addTokens []common.Token
|
||||
vars *common.RollupVars
|
||||
}
|
||||
|
||||
// NewRollupData creates an empty rollupData with the slices initialized.
|
||||
func newRollupData() rollupData {
|
||||
return rollupData{
|
||||
l1UserTxs: make([]common.L1Tx, 0),
|
||||
batches: make([]historydb.BatchData, 0),
|
||||
batches: make([]common.BatchData, 0),
|
||||
// withdrawals: make([]*common.ExitInfo, 0),
|
||||
registeredTokens: make([]common.Token, 0),
|
||||
addTokens: make([]common.Token, 0),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,58 +75,21 @@ type wdelayerData struct {
|
||||
vars *common.WithdrawDelayerVars
|
||||
}
|
||||
|
||||
// BatchData contains information about Batches from the contracts
|
||||
// type BatchData struct {
|
||||
// l1UserTxs []*common.L1Tx
|
||||
// l1CoordinatorTxs []*common.L1Tx
|
||||
// l2Txs []*common.L2Tx
|
||||
// createdAccounts []*common.Account
|
||||
// exitTree []*common.ExitInfo
|
||||
// batch *common.Batch
|
||||
// }
|
||||
|
||||
// NewBatchData creates an empty BatchData with the slices initialized.
|
||||
// func NewBatchData() *BatchData {
|
||||
// return &BatchData{
|
||||
// l1UserTxs: make([]*common.L1Tx, 0),
|
||||
// l1CoordinatorTxs: make([]*common.L1Tx, 0),
|
||||
// l2Txs: make([]*common.L2Tx, 0),
|
||||
// createdAccounts: make([]*common.Account, 0),
|
||||
// exitTree: make([]*common.ExitInfo, 0),
|
||||
// }
|
||||
// }
|
||||
|
||||
// BlockData contains information about Blocks from the contracts
|
||||
// type blockData struct {
|
||||
// Block *common.Block
|
||||
// // Rollup
|
||||
// L1Txs []*common.L1Tx // TODO: Answer: User? Coordinator? Both?
|
||||
// Batches []*BatchData // TODO: Also contains L1Txs!
|
||||
// // withdrawals []*common.ExitInfo // TODO
|
||||
// RegisteredTokens []common.Token
|
||||
// RollupVars *common.RollupVars
|
||||
// // Auction
|
||||
// Bids []*common.Bid
|
||||
// Coordinators []*common.Coordinator
|
||||
// AuctionVars *common.AuctionVars
|
||||
// // WithdrawalDelayer
|
||||
// WithdrawalDelayerVars *common.WithdrawalDelayerVars
|
||||
// }
|
||||
|
||||
// Synchronizer implements the Synchronizer type
|
||||
type Synchronizer struct {
|
||||
ethClient eth.ClientInterface
|
||||
auctionConstants eth.AuctionConstants
|
||||
historyDB *historydb.HistoryDB
|
||||
stateDB *statedb.StateDB
|
||||
firstSavedBlock *common.Block
|
||||
mux sync.Mutex
|
||||
// firstSavedBlock *common.Block
|
||||
// mux sync.Mutex
|
||||
}
|
||||
|
||||
// NewSynchronizer creates a new Synchronizer
|
||||
func NewSynchronizer(ethClient eth.ClientInterface, historyDB *historydb.HistoryDB, stateDB *statedb.StateDB) (*Synchronizer, error) {
|
||||
auctionConstants, err := ethClient.AuctionConstants()
|
||||
if err != nil {
|
||||
log.Errorw("NewSynchronizer", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
return &Synchronizer{
|
||||
@@ -118,128 +100,101 @@ func NewSynchronizer(ethClient eth.ClientInterface, historyDB *historydb.History
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Sync2 attems to synchronize an ethereum block starting from lastSavedBlock.
|
||||
// If lastSavedBlock is nil, the lastSavedBlock value is obtained from de DB.
|
||||
// If a block is synched, it will be returned and also stored in the DB. If a
|
||||
// reorg is detected, the number of discarded blocks will be returned and no
|
||||
// synchronization will be made.
|
||||
// TODO: Be smart about locking: only lock during the read/write operations
|
||||
|
||||
// Sync updates History and State DB with information from the blockchain
|
||||
// TODO: Return true if a new block was processed
|
||||
// TODO: Add argument: maximum number of blocks to process
|
||||
// TODO: Check reorgs in the middle of syncing a block. Probably make
|
||||
// rollupSync, auctionSync and withdrawalSync return the block hash.
|
||||
func (s *Synchronizer) Sync(ctx context.Context) error {
|
||||
// Avoid new sync while performing one
|
||||
s.mux.Lock()
|
||||
defer s.mux.Unlock()
|
||||
|
||||
func (s *Synchronizer) Sync2(ctx context.Context, lastSavedBlock *common.Block) (*common.BlockData, *int64, error) {
|
||||
var nextBlockNum int64 // next block number to sync
|
||||
|
||||
// Get lastSavedBlock from History DB
|
||||
lastSavedBlock, err := s.historyDB.GetLastBlock()
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return err
|
||||
if lastSavedBlock == nil {
|
||||
var err error
|
||||
// Get lastSavedBlock from History DB
|
||||
lastSavedBlock, err = s.historyDB.GetLastBlock()
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return nil, nil, err
|
||||
}
|
||||
// If we don't have any stored block, we must do a full sync starting from the rollup genesis block
|
||||
if err == sql.ErrNoRows {
|
||||
nextBlockNum = s.auctionConstants.GenesisBlockNum
|
||||
}
|
||||
}
|
||||
// If we don't have any stored block, we must do a full sync starting from the rollup genesis block
|
||||
if err == sql.ErrNoRows {
|
||||
nextBlockNum = s.auctionConstants.GenesisBlockNum
|
||||
} else {
|
||||
// Get the latest block we have in History DB from blockchain to detect a reorg
|
||||
ethBlock, err := s.ethClient.EthBlockByNumber(ctx, lastSavedBlock.EthBlockNum)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ethBlock.Hash != lastSavedBlock.Hash {
|
||||
// Reorg detected
|
||||
log.Debugf("Reorg Detected...")
|
||||
_, err := s.reorg(lastSavedBlock)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lastSavedBlock, err = s.historyDB.GetLastBlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if lastSavedBlock != nil {
|
||||
nextBlockNum = lastSavedBlock.EthBlockNum + 1
|
||||
}
|
||||
|
||||
log.Debugf("Syncing...")
|
||||
ethBlock, err := s.ethClient.EthBlockByNumber(ctx, nextBlockNum)
|
||||
if err == ethereum.NotFound {
|
||||
return nil, nil, nil
|
||||
} else if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Get latest blockNum in blockchain
|
||||
latestBlockNum, err := s.ethClient.EthCurrentBlock()
|
||||
log.Debugw("Syncing...", "block", nextBlockNum)
|
||||
|
||||
// Check that the obtianed ethBlock.ParentHash == prevEthBlock.Hash; if not, reorg!
|
||||
if lastSavedBlock != nil {
|
||||
if lastSavedBlock.Hash != ethBlock.ParentHash {
|
||||
// Reorg detected
|
||||
log.Debugw("Reorg Detected",
|
||||
"blockNum", ethBlock.EthBlockNum,
|
||||
"block.parent", ethBlock.ParentHash, "parent.hash", lastSavedBlock.Hash)
|
||||
lastDBBlockNum, err := s.reorg(lastSavedBlock)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
discarded := lastSavedBlock.EthBlockNum - lastDBBlockNum
|
||||
return nil, &discarded, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Get data from the rollup contract
|
||||
rollupData, err := s.rollupSync(ethBlock)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
log.Debugf("Blocks to sync: %v (firstBlockToSync: %v, latestBlock: %v)", latestBlockNum-nextBlockNum+1, nextBlockNum, latestBlockNum)
|
||||
|
||||
for nextBlockNum <= latestBlockNum {
|
||||
ethBlock, err := s.ethClient.EthBlockByNumber(context.Background(), nextBlockNum)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: Check that the obtianed ethBlock.ParentHash == prevEthBlock.Hash; if not, reorg!
|
||||
|
||||
// TODO: Send the ethHash in rollupSync(), auctionSync() and
|
||||
// wdelayerSync() and make sure they all use the same block
|
||||
// hash.
|
||||
|
||||
// Get data from the rollup contract
|
||||
rollupData, err := s.rollupSync(nextBlockNum)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get data from the auction contract
|
||||
auctionData, err := s.auctionSync(nextBlockNum)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get data from the WithdrawalDelayer contract
|
||||
wdelayerData, err := s.wdelayerSync(nextBlockNum)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Group all the block data into the structs to save into HistoryDB
|
||||
var blockData historydb.BlockData
|
||||
|
||||
blockData.Block = ethBlock
|
||||
|
||||
if rollupData != nil {
|
||||
blockData.L1UserTxs = rollupData.l1UserTxs
|
||||
blockData.Batches = rollupData.batches
|
||||
// blockData.withdrawals = rollupData.withdrawals // TODO
|
||||
blockData.RegisteredTokens = rollupData.registeredTokens
|
||||
blockData.RollupVars = rollupData.vars
|
||||
}
|
||||
|
||||
if auctionData != nil {
|
||||
blockData.Bids = auctionData.bids
|
||||
blockData.Coordinators = auctionData.coordinators
|
||||
blockData.AuctionVars = auctionData.vars
|
||||
}
|
||||
|
||||
if wdelayerData != nil {
|
||||
blockData.WithdrawDelayerVars = wdelayerData.vars
|
||||
}
|
||||
|
||||
// Add rollupData and auctionData once the method is updated
|
||||
// TODO: Save Whole Struct -> AddBlockSCData(blockData)
|
||||
log.Debugw("Sync()", "block", blockData)
|
||||
// err = s.historyDB.AddBlock(blockData.Block)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
err = s.historyDB.AddBlockSCData(&blockData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nextBlockNum++
|
||||
// Get data from the auction contract
|
||||
auctionData, err := s.auctionSync(ethBlock)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return nil
|
||||
// Get data from the WithdrawalDelayer contract
|
||||
wdelayerData, err := s.wdelayerSync(ethBlock)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Group all the block data into the structs to save into HistoryDB
|
||||
var blockData common.BlockData
|
||||
|
||||
blockData.Block = *ethBlock
|
||||
|
||||
blockData.L1UserTxs = rollupData.l1UserTxs
|
||||
blockData.Batches = rollupData.batches
|
||||
// blockData.withdrawals = rollupData.withdrawals // TODO
|
||||
blockData.AddedTokens = rollupData.addTokens
|
||||
blockData.RollupVars = rollupData.vars
|
||||
|
||||
blockData.Bids = auctionData.bids
|
||||
blockData.Coordinators = auctionData.coordinators
|
||||
blockData.AuctionVars = auctionData.vars
|
||||
|
||||
blockData.WithdrawDelayerVars = wdelayerData.vars
|
||||
|
||||
// log.Debugw("Sync()", "block", blockData)
|
||||
// err = s.historyDB.AddBlock(blockData.Block)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
err = s.historyDB.AddBlockSCData(&blockData)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return &blockData, nil, nil
|
||||
}
|
||||
|
||||
// reorg manages a reorg, updating History and State DB as needed. Keeps
|
||||
@@ -250,12 +205,8 @@ func (s *Synchronizer) Sync(ctx context.Context) error {
|
||||
func (s *Synchronizer) reorg(uncleBlock *common.Block) (int64, error) {
|
||||
var block *common.Block
|
||||
blockNum := uncleBlock.EthBlockNum
|
||||
found := false
|
||||
|
||||
log.Debugf("Reorg first uncle block: %v", blockNum)
|
||||
|
||||
// Iterate History DB and the blokchain looking for the latest valid block
|
||||
for !found && blockNum > s.firstSavedBlock.EthBlockNum {
|
||||
for blockNum >= s.auctionConstants.GenesisBlockNum {
|
||||
ethBlock, err := s.ethClient.EthBlockByNumber(context.Background(), blockNum)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@@ -266,39 +217,36 @@ func (s *Synchronizer) reorg(uncleBlock *common.Block) (int64, error) {
|
||||
return 0, err
|
||||
}
|
||||
if block.Hash == ethBlock.Hash {
|
||||
found = true
|
||||
log.Debugf("Found valid block: %v", blockNum)
|
||||
} else {
|
||||
log.Debugf("Discarding block: %v", blockNum)
|
||||
break
|
||||
}
|
||||
|
||||
blockNum--
|
||||
}
|
||||
total := uncleBlock.EthBlockNum - blockNum
|
||||
log.Debugw("Discarding blocks", "total", total, "from", uncleBlock.EthBlockNum, "to", blockNum+1)
|
||||
|
||||
if found {
|
||||
// Set History DB and State DB to the correct state
|
||||
err := s.historyDB.Reorg(block.EthBlockNum)
|
||||
// Set History DB and State DB to the correct state
|
||||
err := s.historyDB.Reorg(block.EthBlockNum)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
batchNum, err := s.historyDB.GetLastBatchNum()
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return 0, err
|
||||
}
|
||||
if batchNum != 0 {
|
||||
err = s.stateDB.Reset(batchNum)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
batchNum, err := s.historyDB.GetLastBatchNum()
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return 0, err
|
||||
}
|
||||
if batchNum != 0 {
|
||||
err = s.stateDB.Reset(batchNum)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return block.EthBlockNum, nil
|
||||
}
|
||||
|
||||
return 0, ErrNotAbleToSync
|
||||
return block.EthBlockNum, nil
|
||||
}
|
||||
|
||||
// TODO: Figure out who will use the Status output, and only return what's strictly need
|
||||
/*
|
||||
// Status returns current status values from the Synchronizer
|
||||
func (s *Synchronizer) Status() (*common.SyncStatus, error) {
|
||||
// Avoid possible inconsistencies
|
||||
@@ -340,21 +288,27 @@ func (s *Synchronizer) Status() (*common.SyncStatus, error) {
|
||||
status.Synchronized = status.CurrentBlock == latestBlockNum
|
||||
return status, nil
|
||||
}
|
||||
*/
|
||||
|
||||
// rollupSync gets information from the Rollup Contract
|
||||
func (s *Synchronizer) rollupSync(blockNum int64) (*rollupData, error) {
|
||||
// rollupSync retreives all the Rollup Smart Contract Data that happened at
|
||||
// ethBlock.blockNum with ethBlock.Hash.
|
||||
func (s *Synchronizer) rollupSync(ethBlock *common.Block) (*rollupData, error) {
|
||||
blockNum := ethBlock.EthBlockNum
|
||||
var rollupData = newRollupData()
|
||||
// var forgeL1TxsNum int64
|
||||
var numAccounts int
|
||||
|
||||
// Get rollup events in the block
|
||||
rollupEvents, _, err := s.ethClient.RollupEventsByBlock(blockNum)
|
||||
// Get rollup events in the block, and make sure the block hash matches
|
||||
// the expected one.
|
||||
rollupEvents, blockHash, err := s.ethClient.RollupEventsByBlock(blockNum)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if *blockHash != ethBlock.Hash {
|
||||
return nil, eth.ErrBlockHashMismatchEvent
|
||||
}
|
||||
|
||||
// TODO: Replace GetLastL1TxsNum by GetNextL1TxsNum
|
||||
var nextForgeL1TxsNum int64
|
||||
var nextForgeL1TxsNum int64 // forgeL1TxsNum for the next L1Batch
|
||||
nextForgeL1TxsNumPtr, err := s.historyDB.GetLastL1TxsNum()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -379,7 +333,7 @@ func (s *Synchronizer) rollupSync(blockNum int64) (*rollupData, error) {
|
||||
|
||||
// Get ForgeBatch events to get the L1CoordinatorTxs
|
||||
for _, evtForgeBatch := range rollupEvents.ForgeBatch {
|
||||
batchData := historydb.NewBatchData()
|
||||
batchData := common.NewBatchData()
|
||||
position := 0
|
||||
|
||||
// Get the input for each Tx
|
||||
@@ -387,70 +341,70 @@ func (s *Synchronizer) rollupSync(blockNum int64) (*rollupData, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
batchNum := common.BatchNum(evtForgeBatch.BatchNum)
|
||||
forgeL1TxsNum := nextForgeL1TxsNum
|
||||
var l1UserTxs []common.L1Tx
|
||||
// Check if this is a L1Batch to get L1 Tx from it
|
||||
if forgeBatchArgs.L1Batch {
|
||||
// Get L1 User Txs from History DB
|
||||
// TODO: Get L1TX from HistoryDB filtered by toforgeL1txNum & fromidx = 0 and
|
||||
// update batch number and add accounts to createdAccounts updating idx
|
||||
// Get L1UserTxs with toForgeL1TxsNum, which correspond
|
||||
// to the L1UserTxs that are forged in this batch, so
|
||||
// that stateDB can process them.
|
||||
|
||||
// First try to find them in HistoryDB.
|
||||
l1UserTxs, err := s.historyDB.GetL1UserTxs(forgeL1TxsNum)
|
||||
if len(l1UserTxs) == 0 {
|
||||
// If not found in the DB, try to find them in
|
||||
// this block. This could happen because in a
|
||||
// block there could be multiple batches with
|
||||
// L1Batch = true (although it's a very rare
|
||||
// case).
|
||||
// If not found in the DB and the block doesn't
|
||||
// contain the l1UserTxs, it means that the
|
||||
// L1UserTxs queue with toForgeL1TxsNum was
|
||||
// closed empty, so we leave `l1UserTxs` as an
|
||||
// empty slice.
|
||||
for _, l1UserTx := range rollupData.l1UserTxs {
|
||||
if *l1UserTx.ToForgeL1TxsNum == forgeL1TxsNum {
|
||||
l1UserTxs = append(l1UserTxs, l1UserTx)
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// l1UserTxs, err := s.historyDB.GetL1UserTxs(nextForgeL1TxsNum)
|
||||
// If HistoryDB doesn't have L1UserTxs at
|
||||
// nextForgeL1TxsNum, check if they exist in
|
||||
// rollupData.l1Txs. This could happen because in a
|
||||
// block there could be multiple batches with L1Batch =
|
||||
// true (although it's a very rare case). If the
|
||||
// L1UserTxs are not in rollupData.l1Txs, use an empty
|
||||
// array (this happens when the L1UserTxs queue is
|
||||
// frozen but didn't store any tx).
|
||||
l1UserTxs := []common.L1Tx{}
|
||||
position = len(l1UserTxs)
|
||||
|
||||
// Get L1 Coordinator Txs
|
||||
for i := 0; i < len(forgeBatchArgs.L1CoordinatorTxs); i++ {
|
||||
for i := range forgeBatchArgs.L1CoordinatorTxs {
|
||||
l1CoordinatorTx := forgeBatchArgs.L1CoordinatorTxs[i]
|
||||
l1CoordinatorTx.Position = position
|
||||
l1CoordinatorTx.ToForgeL1TxsNum = &forgeL1TxsNum
|
||||
l1CoordinatorTx.UserOrigin = false
|
||||
l1CoordinatorTx.EthBlockNum = blockNum
|
||||
bn := new(common.BatchNum)
|
||||
*bn = common.BatchNum(evtForgeBatch.BatchNum)
|
||||
l1CoordinatorTx.BatchNum = bn
|
||||
l1CoordinatorTx.BatchNum = &batchNum
|
||||
l1Tx, err := common.NewL1Tx(&l1CoordinatorTx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
batchData.L1CoordinatorTxs = append(batchData.L1CoordinatorTxs, *l1Tx)
|
||||
|
||||
// Check if we have to register an account
|
||||
// if l1CoordinatorTx.FromIdx == 0 {
|
||||
// account := common.Account{
|
||||
// // TODO: Uncommnent when common.account has IDx
|
||||
// // IDx: common.Idx(idx),
|
||||
// TokenID: l1CoordinatorTx.TokenID,
|
||||
// Nonce: 0,
|
||||
// Balance: l1CoordinatorTx.LoadAmount,
|
||||
// PublicKey: l1CoordinatorTx.FromBJJ,
|
||||
// EthAddr: l1CoordinatorTx.FromEthAddr,
|
||||
// }
|
||||
// idx++
|
||||
// batchData.createdAccounts = append(batchData.createdAccounts, &account)
|
||||
// numAccounts++
|
||||
// }
|
||||
position++
|
||||
}
|
||||
nextForgeL1TxsNum++
|
||||
}
|
||||
|
||||
// Get L2Txs
|
||||
poolL2Txs := common.L2TxsToPoolL2Txs(forgeBatchArgs.L2TxsData) // TODO: This is a big uggly, find a better way
|
||||
// Insert all the txs forged in this batch (l1UserTxs,
|
||||
// L1CoordinatorTxs, PoolL2Txs) into stateDB so that they are
|
||||
// processed.
|
||||
poolL2Txs := common.L2TxsToPoolL2Txs(forgeBatchArgs.L2TxsData) // TODO: This is a big ugly, find a better way
|
||||
|
||||
// Get exitTree
|
||||
// TODO: Get createdAccounts from ProcessTxs()
|
||||
// TODO: Get CollectedFees from ProcessTxs()
|
||||
// TODO: Pass forgeBatchArgs.FeeIdxCoordinator to ProcessTxs()
|
||||
_, exitInfo, err := s.stateDB.ProcessTxs(batchData.L1UserTxs, batchData.L1CoordinatorTxs, poolL2Txs)
|
||||
// ProcessTxs updates poolL2Txs adding: Nonce, TokenID
|
||||
_, exitInfo, err := s.stateDB.ProcessTxs(l1UserTxs, batchData.L1CoordinatorTxs, poolL2Txs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -459,21 +413,42 @@ func (s *Synchronizer) rollupSync(blockNum int64) (*rollupData, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
batchData.L2Txs = append(batchData.L2Txs, l2Txs...)
|
||||
|
||||
for i := range l2Txs {
|
||||
_l2Tx := l2Txs[i]
|
||||
_l2Tx.Position = position
|
||||
_l2Tx.EthBlockNum = blockNum
|
||||
_l2Tx.BatchNum = batchNum
|
||||
l2Tx, err := common.NewL2Tx(&_l2Tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
batchData.L2Txs = append(batchData.L2Txs, *l2Tx)
|
||||
position++
|
||||
}
|
||||
|
||||
batchData.ExitTree = exitInfo
|
||||
|
||||
slotNum := int64(0)
|
||||
if ethBlock.EthBlockNum >= s.auctionConstants.GenesisBlockNum {
|
||||
slotNum = (ethBlock.EthBlockNum - s.auctionConstants.GenesisBlockNum) /
|
||||
int64(s.auctionConstants.BlocksPerSlot)
|
||||
}
|
||||
|
||||
// Get Batch information
|
||||
batch := &common.Batch{
|
||||
BatchNum: common.BatchNum(evtForgeBatch.BatchNum),
|
||||
batch := common.Batch{
|
||||
BatchNum: batchNum,
|
||||
EthBlockNum: blockNum,
|
||||
ForgerAddr: *sender,
|
||||
// CollectedFees: , TODO: Clarify where to get them if they are still needed
|
||||
StateRoot: common.Hash(forgeBatchArgs.NewStRoot.Bytes()),
|
||||
NumAccounts: numAccounts,
|
||||
ExitRoot: common.Hash(forgeBatchArgs.NewExitRoot.Bytes()),
|
||||
ForgeL1TxsNum: &forgeL1TxsNum,
|
||||
// SlotNum: TODO: Calculate once ethClient provides the info // calculate from blockNum + ethClient Constants
|
||||
StateRoot: forgeBatchArgs.NewStRoot,
|
||||
NumAccounts: numAccounts,
|
||||
ExitRoot: forgeBatchArgs.NewExitRoot,
|
||||
SlotNum: slotNum,
|
||||
}
|
||||
if forgeBatchArgs.L1Batch {
|
||||
batch.ForgeL1TxsNum = &forgeL1TxsNum
|
||||
}
|
||||
batchData.Batch = batch
|
||||
rollupData.batches = append(rollupData.batches, *batchData)
|
||||
@@ -487,12 +462,19 @@ func (s *Synchronizer) rollupSync(blockNum int64) (*rollupData, error) {
|
||||
token.EthAddr = evtAddToken.TokenAddress
|
||||
token.EthBlockNum = blockNum
|
||||
|
||||
// TODO: Add external information consulting SC about it using Address
|
||||
token.Name = "TODO"
|
||||
token.Symbol = "TODO"
|
||||
token.Decimals = 8 // TODO
|
||||
if consts, err := s.ethClient.EthERC20Consts(evtAddToken.TokenAddress); err != nil {
|
||||
log.Warnw("Error retreiving ERC20 token constants", "addr", evtAddToken.TokenAddress)
|
||||
// TODO: Add external information consulting SC about it using Address
|
||||
token.Name = "ERC20_ETH_ERROR"
|
||||
token.Symbol = "ERROR"
|
||||
token.Decimals = 1
|
||||
} else {
|
||||
token.Name = cutStringMax(consts.Name, 20)
|
||||
token.Symbol = cutStringMax(consts.Symbol, 10)
|
||||
token.Decimals = consts.Decimals
|
||||
}
|
||||
|
||||
rollupData.registeredTokens = append(rollupData.registeredTokens, token)
|
||||
rollupData.addTokens = append(rollupData.addTokens, token)
|
||||
}
|
||||
|
||||
// TODO: rollupEvents.UpdateForgeL1L2BatchTimeout
|
||||
@@ -506,20 +488,31 @@ func (s *Synchronizer) rollupSync(blockNum int64) (*rollupData, error) {
|
||||
return &rollupData, nil
|
||||
}
|
||||
|
||||
func cutStringMax(s string, max int) string {
|
||||
if len(s) > max {
|
||||
return s[:max]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// auctionSync gets information from the Auction Contract
|
||||
func (s *Synchronizer) auctionSync(blockNum int64) (*auctionData, error) {
|
||||
func (s *Synchronizer) auctionSync(ethBlock *common.Block) (*auctionData, error) {
|
||||
blockNum := ethBlock.EthBlockNum
|
||||
var auctionData = newAuctionData()
|
||||
|
||||
// Get auction events in the block
|
||||
auctionEvents, _, err := s.ethClient.AuctionEventsByBlock(blockNum)
|
||||
auctionEvents, blockHash, err := s.ethClient.AuctionEventsByBlock(blockNum)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if *blockHash != ethBlock.Hash {
|
||||
return nil, eth.ErrBlockHashMismatchEvent
|
||||
}
|
||||
|
||||
// Get bids
|
||||
for _, evtNewBid := range auctionEvents.NewBid {
|
||||
bid := common.Bid{
|
||||
SlotNum: common.SlotNum(evtNewBid.Slot),
|
||||
SlotNum: evtNewBid.Slot,
|
||||
BidValue: evtNewBid.BidAmount,
|
||||
Bidder: evtNewBid.Bidder,
|
||||
EthBlockNum: blockNum,
|
||||
@@ -556,11 +549,14 @@ func (s *Synchronizer) auctionSync(blockNum int64) (*auctionData, error) {
|
||||
}
|
||||
|
||||
// wdelayerSync gets information from the Withdrawal Delayer Contract
|
||||
func (s *Synchronizer) wdelayerSync(blockNum int64) (*wdelayerData, error) {
|
||||
func (s *Synchronizer) wdelayerSync(ethBlock *common.Block) (*wdelayerData, error) {
|
||||
// blockNum := ethBlock.EthBlockNum
|
||||
// TODO: VARS
|
||||
// TODO: CONSTANTS
|
||||
|
||||
return nil, nil
|
||||
return &wdelayerData{
|
||||
vars: nil,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// func (s *Synchronizer) getIdx(rollupEvents *eth.RollupEvents) (int64, error) {
|
||||
@@ -579,17 +575,15 @@ func (s *Synchronizer) wdelayerSync(blockNum int64) (*wdelayerData, error) {
|
||||
// }
|
||||
|
||||
func getL1UserTx(eventsL1UserTx []eth.RollupEventL1UserTx, blockNum int64) ([]common.L1Tx, error) {
|
||||
l1Txs := make([]common.L1Tx, 0)
|
||||
|
||||
for _, evtL1UserTx := range eventsL1UserTx {
|
||||
evtL1UserTx.L1UserTx.EthBlockNum = blockNum
|
||||
nL1Tx, err := common.NewL1Tx(&evtL1UserTx.L1UserTx)
|
||||
l1Txs := make([]common.L1Tx, len(eventsL1UserTx))
|
||||
for i := range eventsL1UserTx {
|
||||
eventsL1UserTx[i].L1UserTx.EthBlockNum = blockNum
|
||||
// Check validity of L1UserTx
|
||||
l1Tx, err := common.NewL1Tx(&eventsL1UserTx[i].L1UserTx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
evtL1UserTx.L1UserTx = *nL1Tx
|
||||
|
||||
l1Txs = append(l1Txs, evtL1UserTx.L1UserTx)
|
||||
l1Txs[i] = *l1Tx
|
||||
}
|
||||
return l1Txs, nil
|
||||
}
|
||||
|
||||
@@ -2,15 +2,24 @@ package synchronizer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
ethCommon "github.com/ethereum/go-ethereum/common"
|
||||
ethCrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/hermeznetwork/hermez-node/common"
|
||||
dbUtils "github.com/hermeznetwork/hermez-node/db"
|
||||
"github.com/hermeznetwork/hermez-node/db/historydb"
|
||||
"github.com/hermeznetwork/hermez-node/db/statedb"
|
||||
"github.com/hermeznetwork/hermez-node/eth"
|
||||
"github.com/hermeznetwork/hermez-node/test"
|
||||
"github.com/iden3/go-iden3-crypto/babyjub"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -25,7 +34,14 @@ func (t *timer) Time() int64 {
|
||||
return currentTime
|
||||
}
|
||||
|
||||
type tokenData struct {
|
||||
TokenID common.TokenID
|
||||
Addr ethCommon.Address
|
||||
Consts eth.ERC20Consts
|
||||
}
|
||||
|
||||
func TestSync(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
// Int State DB
|
||||
dir, err := ioutil.TempDir("", "tmpdb")
|
||||
require.Nil(t, err)
|
||||
@@ -52,19 +68,20 @@ func TestSync(t *testing.T) {
|
||||
s, err := NewSynchronizer(client, historyDB, stateDB)
|
||||
require.Nil(t, err)
|
||||
|
||||
// Test Sync for ethereum genesis block
|
||||
err = s.Sync(context.Background())
|
||||
// Test Sync for rollup genesis block
|
||||
blockData, _, err := s.Sync2(ctx, nil)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.NotNil(t, blockData)
|
||||
assert.Equal(t, int64(1), blockData.Block.EthBlockNum)
|
||||
blocks, err := s.historyDB.GetBlocks(0, 9999)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, 1, len(blocks))
|
||||
assert.Equal(t, int64(1), blocks[0].EthBlockNum)
|
||||
|
||||
// TODO once Til is completed
|
||||
/*
|
||||
// Test Sync for a block with new Tokens and L1UserTxs
|
||||
// accounts := test.GenerateKeys(t, []string{"A", "B", "C", "D"})
|
||||
l1UserTxs, _, _, _ := test.GenerateTestTxsFromSet(t, `
|
||||
// Test Sync for a block with new Tokens and L1UserTxs
|
||||
// accounts := test.GenerateKeys(t, []string{"A", "B", "C", "D"})
|
||||
l1UserTxs, _, _, _ := test.GenerateTestTxsFromSet(t, `
|
||||
A (1): 10
|
||||
A (2): 20
|
||||
B (1): 5
|
||||
@@ -72,7 +89,7 @@ func TestSync(t *testing.T) {
|
||||
D (3): 15
|
||||
> advance batch
|
||||
`)
|
||||
require.Greater(t, len(l1UserTxs[0]), 0)
|
||||
require.Greater(t, len(l1UserTxs[0]), 0)
|
||||
// require.Greater(t, len(tokens), 0)
|
||||
|
||||
for i := 1; i <= 3; i++ {
|
||||
@@ -94,6 +111,86 @@ func TestSync(t *testing.T) {
|
||||
assert.Equal(t, 3, len(getTokens))
|
||||
*/
|
||||
|
||||
// Generate tokens vector
|
||||
numTokens := 3
|
||||
tokens := make([]tokenData, numTokens)
|
||||
for i := 1; i <= numTokens; i++ {
|
||||
addr := ethCommon.BigToAddress(big.NewInt(int64(i * 10000)))
|
||||
consts := eth.ERC20Consts{
|
||||
Name: fmt.Sprintf("Token %d", i),
|
||||
Symbol: fmt.Sprintf("TK%d", i),
|
||||
Decimals: uint64(i * 2),
|
||||
}
|
||||
tokens[i-1] = tokenData{common.TokenID(i), addr, consts}
|
||||
}
|
||||
|
||||
numUsers := 4
|
||||
keys := make([]*userKeys, numUsers)
|
||||
for i := range keys {
|
||||
keys[i] = genKeys(i)
|
||||
}
|
||||
|
||||
// Generate some L1UserTxs of type deposit
|
||||
l1UserTxs := make([]*common.L1Tx, 5)
|
||||
for i := range l1UserTxs {
|
||||
l1UserTxs[i] = &common.L1Tx{
|
||||
FromIdx: common.Idx(0),
|
||||
FromEthAddr: keys[i%numUsers].Addr,
|
||||
FromBJJ: keys[i%numUsers].BJJPK,
|
||||
Amount: big.NewInt(0),
|
||||
LoadAmount: big.NewInt((int64(i) + 1) * 1000),
|
||||
TokenID: common.TokenID(i%numTokens + 1),
|
||||
}
|
||||
}
|
||||
|
||||
// Add tokens to ethereum, and to rollup
|
||||
for _, token := range tokens {
|
||||
client.CtlAddERC20(token.Addr, token.Consts)
|
||||
_, err := client.RollupAddToken(token.Addr, clientSetup.RollupVariables.FeeAddToken)
|
||||
require.Nil(t, err)
|
||||
}
|
||||
|
||||
// Add L1Txs to rollup
|
||||
for i := range l1UserTxs {
|
||||
tx := l1UserTxs[i]
|
||||
_, err := client.RollupL1UserTxERC20ETH(tx.FromBJJ, int64(tx.FromIdx), tx.LoadAmount, tx.Amount,
|
||||
uint32(tx.TokenID), int64(tx.ToIdx))
|
||||
require.Nil(t, err)
|
||||
}
|
||||
|
||||
// Mine block and sync
|
||||
client.CtlMineBlock()
|
||||
|
||||
blockData, _, err = s.Sync2(ctx, nil)
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, blockData)
|
||||
assert.Equal(t, int64(2), blockData.Block.EthBlockNum)
|
||||
|
||||
// Check tokens in DB
|
||||
dbTokens, err := s.historyDB.GetAllTokens()
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, len(tokens), len(dbTokens))
|
||||
assert.Equal(t, len(tokens), len(blockData.AddedTokens))
|
||||
for i := range tokens {
|
||||
token := tokens[i]
|
||||
addToken := blockData.AddedTokens[i]
|
||||
dbToken := dbTokens[i]
|
||||
|
||||
assert.Equal(t, int64(2), addToken.EthBlockNum)
|
||||
assert.Equal(t, token.TokenID, addToken.TokenID)
|
||||
assert.Equal(t, token.Addr, addToken.EthAddr)
|
||||
assert.Equal(t, token.Consts.Name, addToken.Name)
|
||||
assert.Equal(t, token.Consts.Symbol, addToken.Symbol)
|
||||
assert.Equal(t, token.Consts.Decimals, addToken.Decimals)
|
||||
|
||||
var addTokenCpy historydb.TokenRead
|
||||
require.Nil(t, copier.Copy(&addTokenCpy, &addToken)) // copy common.Token to historydb.TokenRead
|
||||
addTokenCpy.ItemID = dbToken.ItemID // we don't care about ItemID
|
||||
assert.Equal(t, addTokenCpy, dbToken)
|
||||
}
|
||||
|
||||
// Check L1UserTxs in DB
|
||||
|
||||
// TODO: Reorg will be properly tested once we have the mock ethClient implemented
|
||||
/*
|
||||
// Force a Reorg
|
||||
@@ -116,3 +213,28 @@ func TestSync(t *testing.T) {
|
||||
require.Nil(t, err)
|
||||
*/
|
||||
}
|
||||
|
||||
type userKeys struct {
|
||||
BJJSK *babyjub.PrivateKey
|
||||
BJJPK *babyjub.PublicKey
|
||||
Addr ethCommon.Address
|
||||
}
|
||||
|
||||
func genKeys(i int) *userKeys {
|
||||
i++ // i = 0 doesn't work for the ecdsa key generation
|
||||
var sk babyjub.PrivateKey
|
||||
binary.LittleEndian.PutUint64(sk[:], uint64(i))
|
||||
|
||||
// eth address
|
||||
var key ecdsa.PrivateKey
|
||||
key.D = big.NewInt(int64(i)) // only for testing
|
||||
key.PublicKey.X, key.PublicKey.Y = ethCrypto.S256().ScalarBaseMult(key.D.Bytes())
|
||||
key.Curve = ethCrypto.S256()
|
||||
addr := ethCrypto.PubkeyToAddress(key.PublicKey)
|
||||
|
||||
return &userKeys{
|
||||
BJJSK: &sk,
|
||||
BJJPK: sk.Public(),
|
||||
Addr: addr,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user