Extend ethclient test, implement new TxID spec

- Implement new TxID spec that distinguishes L1UserTx and L1CoordinatorTx
- Replace some type []*Foo by []Foo
- Fix HistoryDB & L2DB bug: in case of error, a rollback was applied and the returned error was nil
- Reorder inserts in historydb.NewHistoryDB() to follow foreign key dependencies
- Add initial synchronizer test with test.Client (for now, only tested l1UserTxs, blocks, addToken)
- Update L1UserTx event in test.Client
This commit is contained in:
Eduard S
2020-10-02 13:11:23 +02:00
parent 3d7b71e1fd
commit 0277210c39
21 changed files with 380 additions and 240 deletions

View File

@@ -51,7 +51,7 @@ func (bb *BatchBuilder) Reset(batchNum common.BatchNum, fromSynchronizer bool) e
} }
// BuildBatch takes the transactions and returns the common.ZKInputs of the next batch // BuildBatch takes the transactions and returns the common.ZKInputs of the next batch
func (bb *BatchBuilder) BuildBatch(configBatch *ConfigBatch, l1usertxs, l1coordinatortxs []*common.L1Tx, pooll2txs []*common.PoolL2Tx, tokenIDs []common.TokenID) (*common.ZKInputs, error) { func (bb *BatchBuilder) BuildBatch(configBatch *ConfigBatch, l1usertxs, l1coordinatortxs []common.L1Tx, pooll2txs []common.PoolL2Tx, tokenIDs []common.TokenID) (*common.ZKInputs, error) {
zkInputs, _, err := bb.localStateDB.ProcessTxs(l1usertxs, l1coordinatortxs, pooll2txs) zkInputs, _, err := bb.localStateDB.ProcessTxs(l1usertxs, l1coordinatortxs, pooll2txs)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -81,26 +81,41 @@ func NewL1Tx(l1Tx *L1Tx) (*L1Tx, error) {
} }
l1Tx.Type = txType l1Tx.Type = txType
var txid [TxIDLen]byte txID, err := l1Tx.CalcTxID()
if !l1Tx.UserOrigin { if err != nil {
txid[0] = TxIDPrefixL1CoordTx return nil, err
} }
var toForgeL1TxsNumBytes [8]byte l1Tx.TxID = *txID
var toForge uint64 = 0
if l1Tx.ToForgeL1TxsNum != nil {
toForge = uint64(*l1Tx.ToForgeL1TxsNum)
}
binary.BigEndian.PutUint64(toForgeL1TxsNumBytes[:], toForge)
copy(txid[1:9], toForgeL1TxsNumBytes[:])
var positionBytes [2]byte
binary.BigEndian.PutUint16(positionBytes[:], uint16(l1Tx.Position))
copy(txid[9:11], positionBytes[:])
l1Tx.TxID = TxID(txid)
return l1Tx, nil return l1Tx, nil
} }
func (l1Tx *L1Tx) CalcTxID() (*TxID, error) {
var txID TxID
if l1Tx.UserOrigin {
if l1Tx.ToForgeL1TxsNum == nil {
return nil, fmt.Errorf("L1Tx.UserOrigin == true && L1Tx.ToForgeL1TxsNum == nil")
}
txID[0] = TxIDPrefixL1UserTx
var toForgeL1TxsNumBytes [8]byte
binary.BigEndian.PutUint64(toForgeL1TxsNumBytes[:], uint64(*l1Tx.ToForgeL1TxsNum))
copy(txID[1:9], toForgeL1TxsNumBytes[:])
} else {
if l1Tx.BatchNum == nil {
return nil, fmt.Errorf("L1Tx.UserOrigin == false && L1Tx.BatchNum == nil")
}
txID[0] = TxIDPrefixL1CoordTx
var batchNumBytes [8]byte
binary.BigEndian.PutUint64(batchNumBytes[:], uint64(*l1Tx.BatchNum))
copy(txID[1:9], batchNumBytes[:])
}
var positionBytes [2]byte
binary.BigEndian.PutUint16(positionBytes[:], uint16(l1Tx.Position))
copy(txID[9:11], positionBytes[:])
return &txID, nil
}
// Tx returns a *Tx from the L1Tx // Tx returns a *Tx from the L1Tx
func (tx *L1Tx) Tx() *Tx { func (tx *L1Tx) Tx() *Tx {
f := new(big.Float).SetInt(tx.Amount) f := new(big.Float).SetInt(tx.Amount)

View File

@@ -15,23 +15,40 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestNewL1Tx(t *testing.T) { func TestNewL1UserTx(t *testing.T) {
toForge := new(int64) toForge := int64(123456)
*toForge = 123456 fromIdx := Idx(300)
fromIdx := new(Idx)
*fromIdx = 300
l1Tx := &L1Tx{ l1Tx := &L1Tx{
ToForgeL1TxsNum: toForge, ToForgeL1TxsNum: &toForge,
Position: 71, Position: 71,
UserOrigin: true,
ToIdx: 301, ToIdx: 301,
TokenID: 5, TokenID: 5,
Amount: big.NewInt(1), Amount: big.NewInt(1),
LoadAmount: big.NewInt(2), LoadAmount: big.NewInt(2),
FromIdx: fromIdx, FromIdx: &fromIdx,
} }
l1Tx, err := NewL1Tx(l1Tx) l1Tx, err := NewL1Tx(l1Tx)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, "0x01000000000001e240004700", l1Tx.TxID.String()) assert.Equal(t, "0x00000000000001e240004700", l1Tx.TxID.String())
}
func TestNewL1CoordinatorTx(t *testing.T) {
fromIdx := Idx(300)
batchNum := BatchNum(51966)
l1Tx := &L1Tx{
Position: 88,
UserOrigin: false,
ToIdx: 301,
TokenID: 5,
Amount: big.NewInt(1),
LoadAmount: big.NewInt(2),
FromIdx: &fromIdx,
BatchNum: &batchNum,
}
l1Tx, err := NewL1Tx(l1Tx)
assert.Nil(t, err)
assert.Equal(t, "0x01000000000000cafe005800", l1Tx.TxID.String())
} }
func TestL1TxByteParsers(t *testing.T) { func TestL1TxByteParsers(t *testing.T) {

View File

@@ -111,10 +111,10 @@ func (tx *L2Tx) PoolL2Tx() *PoolL2Tx {
// L2TxsToPoolL2Txs returns an array of []*PoolL2Tx from an array of []*L2Tx, // L2TxsToPoolL2Txs returns an array of []*PoolL2Tx from an array of []*L2Tx,
// where the PoolL2Tx only have the parameters of a L2Tx filled. // where the PoolL2Tx only have the parameters of a L2Tx filled.
func L2TxsToPoolL2Txs(txs []*L2Tx) []*PoolL2Tx { func L2TxsToPoolL2Txs(txs []*L2Tx) []PoolL2Tx {
var r []*PoolL2Tx var r []PoolL2Tx
for _, tx := range txs { for _, tx := range txs {
r = append(r, tx.PoolL2Tx()) r = append(r, *tx.PoolL2Tx())
} }
return r return r
} }

View File

@@ -250,15 +250,15 @@ func (tx *PoolL2Tx) Tx() (*Tx, error) {
}, nil }, nil
} }
// PoolL2TxsToL2Txs returns an array of []*L2Tx from an array of []*PoolL2Tx // PoolL2TxsToL2Txs returns an array of []L2Tx from an array of []PoolL2Tx
func PoolL2TxsToL2Txs(txs []*PoolL2Tx) ([]*L2Tx, error) { func PoolL2TxsToL2Txs(txs []PoolL2Tx) ([]L2Tx, error) {
var r []*L2Tx var r []L2Tx
for _, poolTx := range txs { for _, poolTx := range txs {
tx, err := poolTx.L2Tx() tx, err := poolTx.L2Tx()
if err != nil { if err != nil {
return nil, err return nil, err
} }
r = append(r, tx) r = append(r, *tx)
} }
return r, nil return r, nil
} }

View File

@@ -31,8 +31,8 @@ type AuctionVars struct {
AllocationRatio AllocationRatio AllocationRatio AllocationRatio
} }
// WithdrawalDelayerVars contains the Withdrawal Delayer smart contract variables // WithdrawDelayerVars contains the Withdrawal Delayer smart contract variables
type WithdrawalDelayerVars struct { type WithdrawDelayerVars struct {
HermezRollupAddress eth.Address HermezRollupAddress eth.Address
HermezGovernanceDAOAddress eth.Address HermezGovernanceDAOAddress eth.Address
WhiteHackGroupAddress eth.Address WhiteHackGroupAddress eth.Address

View File

@@ -12,6 +12,11 @@ import (
) )
const ( const (
// TXIDPrefixL1UserTx is the prefix that determines that the TxID is
// for a L1UserTx
//nolinter:gomnd
TxIDPrefixL1UserTx = byte(0)
// TXIDPrefixL1CoordTx is the prefix that determines that the TxID is // TXIDPrefixL1CoordTx is the prefix that determines that the TxID is
// for a L1CoordinatorTx // for a L1CoordinatorTx
//nolinter:gomnd //nolinter:gomnd

View File

@@ -15,9 +15,9 @@ type BatchInfo struct {
serverProof ServerProofInterface serverProof ServerProofInterface
zkInputs *common.ZKInputs zkInputs *common.ZKInputs
proof *Proof proof *Proof
L1UserTxsExtra []*common.L1Tx L1UserTxsExtra []common.L1Tx
L1OperatorTxs []*common.L1Tx L1OperatorTxs []common.L1Tx
L2Txs []*common.PoolL2Tx L2Txs []common.PoolL2Tx
// FeesInfo // FeesInfo
ethTx *types.Transaction ethTx *types.Transaction
} }
@@ -33,7 +33,7 @@ func NewBatchInfo(batchNum common.BatchNum, serverProof ServerProofInterface) Ba
// SetTxsInfo sets the l1UserTxs, l1OperatorTxs and l2Txs to the BatchInfo data // SetTxsInfo sets the l1UserTxs, l1OperatorTxs and l2Txs to the BatchInfo data
// structure // structure
func (bi *BatchInfo) SetTxsInfo(l1UserTxsExtra, l1OperatorTxs []*common.L1Tx, l2Txs []*common.PoolL2Tx) { func (bi *BatchInfo) SetTxsInfo(l1UserTxsExtra, l1OperatorTxs []common.L1Tx, l2Txs []common.PoolL2Tx) {
// TBD parameter: feesInfo // TBD parameter: feesInfo
bi.L1UserTxsExtra = l1UserTxsExtra bi.L1UserTxsExtra = l1UserTxsExtra
bi.L1OperatorTxs = l1OperatorTxs bi.L1OperatorTxs = l1OperatorTxs

View File

@@ -206,14 +206,14 @@ func (c *Coordinator) forge(serverProof ServerProofInterface) (*BatchInfo, error
c.batchNum = c.batchNum + 1 c.batchNum = c.batchNum + 1
batchInfo := NewBatchInfo(c.batchNum, serverProof) // to accumulate metadata of the batch batchInfo := NewBatchInfo(c.batchNum, serverProof) // to accumulate metadata of the batch
var poolL2Txs []*common.PoolL2Tx var poolL2Txs []common.PoolL2Tx
// var feesInfo // var feesInfo
var l1UserTxsExtra, l1OperatorTxs []*common.L1Tx var l1UserTxsExtra, l1OperatorTxs []common.L1Tx
// 1. Decide if we forge L2Tx or L1+L2Tx // 1. Decide if we forge L2Tx or L1+L2Tx
if c.shouldL1L2Batch() { if c.shouldL1L2Batch() {
// 2a: L1+L2 txs // 2a: L1+L2 txs
// l1UserTxs, toForgeL1TxsNumber := c.hdb.GetNextL1UserTxs() // TODO once HistoryDB is ready, uncomment // l1UserTxs, toForgeL1TxsNumber := c.hdb.GetNextL1UserTxs() // TODO once HistoryDB is ready, uncomment
var l1UserTxs []*common.L1Tx = nil // tmp, depends on HistoryDB var l1UserTxs []common.L1Tx = nil // tmp, depends on HistoryDB
l1UserTxsExtra, l1OperatorTxs, poolL2Txs, err = c.txsel.GetL1L2TxSelection(c.batchNum, l1UserTxs) // TODO once feesInfo is added to method return, add the var l1UserTxsExtra, l1OperatorTxs, poolL2Txs, err = c.txsel.GetL1L2TxSelection(c.batchNum, l1UserTxs) // TODO once feesInfo is added to method return, add the var
if err != nil { if err != nil {
return nil, err return nil, err
@@ -358,7 +358,7 @@ func (c *Coordinator) purgeRemoveByTimeout() error {
return nil // TODO return nil // TODO
} }
func (c *Coordinator) purgeInvalidDueToL2TxsSelection(l2Txs []*common.PoolL2Tx) error { func (c *Coordinator) purgeInvalidDueToL2TxsSelection(l2Txs []common.PoolL2Tx) error {
return nil // TODO return nil // TODO
} }

View File

@@ -8,6 +8,7 @@ import (
ethCommon "github.com/ethereum/go-ethereum/common" ethCommon "github.com/ethereum/go-ethereum/common"
"github.com/hermeznetwork/hermez-node/common" "github.com/hermeznetwork/hermez-node/common"
"github.com/hermeznetwork/hermez-node/db" "github.com/hermeznetwork/hermez-node/db"
"github.com/hermeznetwork/hermez-node/log"
"github.com/iden3/go-iden3-crypto/babyjub" "github.com/iden3/go-iden3-crypto/babyjub"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
@@ -25,7 +26,7 @@ type HistoryDB struct {
// BlockData contains the information of a Block // BlockData contains the information of a Block
type BlockData struct { type BlockData struct {
block *common.Block Block *common.Block
// Rollup // Rollup
// L1UserTxs that were submitted in the block // L1UserTxs that were submitted in the block
L1UserTxs []common.L1Tx L1UserTxs []common.L1Tx
@@ -33,10 +34,10 @@ type BlockData struct {
RegisteredTokens []common.Token RegisteredTokens []common.Token
RollupVars *common.RollupVars RollupVars *common.RollupVars
// Auction // Auction
Bids []common.Bid Bids []common.Bid
Coordinators []common.Coordinator Coordinators []common.Coordinator
AuctionVars *common.AuctionVars AuctionVars *common.AuctionVars
// WithdrawalDelayer WithdrawDelayerVars *common.WithdrawDelayerVars
// TODO: enable when common.WithdrawalDelayerVars is Merged from Synchronizer PR // TODO: enable when common.WithdrawalDelayerVars is Merged from Synchronizer PR
// WithdrawalDelayerVars *common.WithdrawalDelayerVars // WithdrawalDelayerVars *common.WithdrawalDelayerVars
} }
@@ -53,6 +54,19 @@ type BatchData struct {
Batch *common.Batch Batch *common.Batch
} }
// NewBatchData creates an empty BatchData with the slices initialized.
func NewBatchData() *BatchData {
return &BatchData{
L1Batch: false,
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),
Batch: &common.Batch{},
}
}
// NewHistoryDB initialize the DB // NewHistoryDB initialize the DB
func NewHistoryDB(db *sqlx.DB) *HistoryDB { func NewHistoryDB(db *sqlx.DB) *HistoryDB {
return &HistoryDB{db: db} return &HistoryDB{db: db}
@@ -536,27 +550,22 @@ func (hdb *HistoryDB) AddBlockSCData(blockData *BlockData) (err error) {
} }
defer func() { defer func() {
if err != nil { if err != nil {
err = txn.Rollback() errRollback := txn.Rollback()
if errRollback != nil {
log.Errorw("Rollback", "err", errRollback)
}
} }
}() }()
// Add block // Add block
err = hdb.addBlock(txn, blockData.block) err = hdb.addBlock(txn, blockData.Block)
if err != nil { if err != nil {
return err return err
} }
// Add l1 Txs // Add Coordinators
if len(blockData.L1UserTxs) > 0 { if len(blockData.Coordinators) > 0 {
err = hdb.addL1Txs(txn, blockData.L1UserTxs) err = hdb.addCoordinators(txn, blockData.Coordinators)
if err != nil {
return err
}
}
// Add Tokens
if len(blockData.RegisteredTokens) > 0 {
err = hdb.addTokens(txn, blockData.RegisteredTokens)
if err != nil { if err != nil {
return err return err
} }
@@ -570,9 +579,17 @@ func (hdb *HistoryDB) AddBlockSCData(blockData *BlockData) (err error) {
} }
} }
// Add Coordinators // Add Tokens
if len(blockData.Coordinators) > 0 { if len(blockData.RegisteredTokens) > 0 {
err = hdb.addCoordinators(txn, blockData.Coordinators) err = hdb.addTokens(txn, blockData.RegisteredTokens)
if err != nil {
return err
}
}
// Add l1 Txs
if len(blockData.L1UserTxs) > 0 {
err = hdb.addL1Txs(txn, blockData.L1UserTxs)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -7,6 +7,7 @@ import (
ethCommon "github.com/ethereum/go-ethereum/common" ethCommon "github.com/ethereum/go-ethereum/common"
"github.com/hermeznetwork/hermez-node/common" "github.com/hermeznetwork/hermez-node/common"
"github.com/hermeznetwork/hermez-node/log"
"github.com/iden3/go-iden3-crypto/babyjub" "github.com/iden3/go-iden3-crypto/babyjub"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
@@ -212,7 +213,10 @@ func (l2db *L2DB) CheckNonces(updatedAccounts []common.Account, batchNum common.
defer func() { defer func() {
// Rollback the transaction if there was an error. // Rollback the transaction if there was an error.
if err != nil { if err != nil {
err = txn.Rollback() errRollback := txn.Rollback()
if errRollback != nil {
log.Errorw("Rollback", "err", errRollback)
}
} }
}() }()
for i := 0; i < len(updatedAccounts); i++ { for i := 0; i < len(updatedAccounts); i++ {
@@ -257,7 +261,10 @@ func (l2db *L2DB) Purge(currentBatchNum common.BatchNum) (err error) {
defer func() { defer func() {
// Rollback the transaction if there was an error. // Rollback the transaction if there was an error.
if err != nil { if err != nil {
err = txn.Rollback() errRollback := txn.Rollback()
if errRollback != nil {
log.Errorw("Rollback", "err", errRollback)
}
} }
}() }()
// Delete pending txs that have been in the pool after the TTL if maxTxs is reached // Delete pending txs that have been in the pool after the TTL if maxTxs is reached

View File

@@ -36,7 +36,7 @@ type processedExit struct {
// type==TypeSynchronizer, assumes that the call is done from the Synchronizer, // type==TypeSynchronizer, assumes that the call is done from the Synchronizer,
// returns common.ExitTreeLeaf that is later used by the Synchronizer to update // returns common.ExitTreeLeaf that is later used by the Synchronizer to update
// the HistoryDB, and adds Nonce & TokenID to the L2Txs. // the HistoryDB, and adds Nonce & TokenID to the L2Txs.
func (s *StateDB) ProcessTxs(l1usertxs, l1coordinatortxs []*common.L1Tx, l2txs []*common.PoolL2Tx) (*common.ZKInputs, []common.ExitInfo, error) { func (s *StateDB) ProcessTxs(l1usertxs, l1coordinatortxs []common.L1Tx, l2txs []common.PoolL2Tx) (*common.ZKInputs, []common.ExitInfo, error) {
var err error var err error
var exitTree *merkletree.MerkleTree var exitTree *merkletree.MerkleTree
@@ -69,7 +69,7 @@ func (s *StateDB) ProcessTxs(l1usertxs, l1coordinatortxs []*common.L1Tx, l2txs [
// assumption: l1usertx are sorted by L1Tx.Position // assumption: l1usertx are sorted by L1Tx.Position
for _, tx := range l1usertxs { for _, tx := range l1usertxs {
exitIdx, exitAccount, newExit, err := s.processL1Tx(exitTree, tx) exitIdx, exitAccount, newExit, err := s.processL1Tx(exitTree, &tx)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -86,7 +86,7 @@ func (s *StateDB) ProcessTxs(l1usertxs, l1coordinatortxs []*common.L1Tx, l2txs [
} }
} }
for _, tx := range l1coordinatortxs { for _, tx := range l1coordinatortxs {
exitIdx, exitAccount, newExit, err := s.processL1Tx(exitTree, tx) exitIdx, exitAccount, newExit, err := s.processL1Tx(exitTree, &tx)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -106,7 +106,7 @@ func (s *StateDB) ProcessTxs(l1usertxs, l1coordinatortxs []*common.L1Tx, l2txs [
} }
} }
for _, tx := range l2txs { for _, tx := range l2txs {
exitIdx, exitAccount, newExit, err := s.processL2Tx(exitTree, tx) exitIdx, exitAccount, newExit, err := s.processL2Tx(exitTree, &tx)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -194,7 +194,7 @@ func (s *StateDB) ProcessTxs(l1usertxs, l1coordinatortxs []*common.L1Tx, l2txs [
} }
// getTokenIDsBigInt returns the list of TokenIDs in *big.Int format // getTokenIDsBigInt returns the list of TokenIDs in *big.Int format
func (s *StateDB) getTokenIDsBigInt(l1usertxs, l1coordinatortxs []*common.L1Tx, l2txs []*common.PoolL2Tx) ([]*big.Int, error) { func (s *StateDB) getTokenIDsBigInt(l1usertxs, l1coordinatortxs []common.L1Tx, l2txs []common.PoolL2Tx) ([]*big.Int, error) {
tokenIDs := make(map[common.TokenID]bool) tokenIDs := make(map[common.TokenID]bool)
for i := 0; i < len(l1usertxs); i++ { for i := 0; i < len(l1usertxs); i++ {
tokenIDs[l1usertxs[i].TokenID] = true tokenIDs[l1usertxs[i].TokenID] = true

View File

@@ -40,7 +40,7 @@ func InitSQLDB(port int, host, user, password, name string) (*sqlx.DB, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
log.Info("successfully runt ", nMigrations, " migrations") log.Info("successfully ran ", nMigrations, " migrations")
return db, nil return db, nil
} }

View File

@@ -1,6 +1,7 @@
package node package node
import ( import (
"context"
"time" "time"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
@@ -83,7 +84,10 @@ func NewNode(mode Mode, cfg *config.Node, coordCfg *config.Coordinator) (*Node,
} }
client := eth.NewClient(ethClient, nil, nil, nil) client := eth.NewClient(ethClient, nil, nil, nil)
sync := synchronizer.NewSynchronizer(client, historyDB, stateDB) sync, err := synchronizer.NewSynchronizer(client, historyDB, stateDB)
if err != nil {
return nil, err
}
var coord *coordinator.Coordinator var coord *coordinator.Coordinator
if mode == ModeCoordinator { if mode == ModeCoordinator {
@@ -223,7 +227,7 @@ func (n *Node) StartSynchronizer() {
log.Info("Coordinator stopped") log.Info("Coordinator stopped")
return return
case <-time.After(n.cfg.Synchronizer.SyncLoopInterval.Duration): case <-time.After(n.cfg.Synchronizer.SyncLoopInterval.Duration):
if err := n.sync.Sync(); err != nil { if err := n.sync.Sync(context.TODO()); err != nil {
log.Errorw("Synchronizer.Sync", "error", err) log.Errorw("Synchronizer.Sync", "error", err)
} }
} }

View File

@@ -20,93 +20,102 @@ var (
// rollupData contains information returned by the Rollup SC // rollupData contains information returned by the Rollup SC
type rollupData struct { type rollupData struct {
l1Txs []*common.L1Tx l1UserTxs []common.L1Tx
batches []*BatchData batches []historydb.BatchData
// withdrawals []*common.ExitInfo // withdrawals []*common.ExitInfo
registeredTokens []*common.Token registeredTokens []common.Token
rollupVars *common.RollupVars vars *common.RollupVars
} }
// NewRollupData creates an empty rollupData with the slices initialized. // NewRollupData creates an empty rollupData with the slices initialized.
func newRollupData() rollupData { func newRollupData() rollupData {
return rollupData{ return rollupData{
l1Txs: make([]*common.L1Tx, 0), l1UserTxs: make([]common.L1Tx, 0),
batches: make([]*BatchData, 0), batches: make([]historydb.BatchData, 0),
// withdrawals: make([]*common.ExitInfo, 0), // withdrawals: make([]*common.ExitInfo, 0),
registeredTokens: make([]*common.Token, 0), registeredTokens: make([]common.Token, 0),
} }
} }
// auctionData contains information returned by the Action SC // auctionData contains information returned by the Action SC
type auctionData struct { type auctionData struct {
bids []*common.Bid bids []common.Bid
coordinators []*common.Coordinator coordinators []common.Coordinator
auctionVars *common.AuctionVars vars *common.AuctionVars
} }
// newAuctionData creates an empty auctionData with the slices initialized. // newAuctionData creates an empty auctionData with the slices initialized.
func newAuctionData() *auctionData { func newAuctionData() *auctionData {
return &auctionData{ return &auctionData{
bids: make([]*common.Bid, 0), bids: make([]common.Bid, 0),
coordinators: make([]*common.Coordinator, 0), coordinators: make([]common.Coordinator, 0),
} }
} }
type wdelayerData struct {
vars *common.WithdrawDelayerVars
}
// BatchData contains information about Batches from the contracts // BatchData contains information about Batches from the contracts
type BatchData struct { // type BatchData struct {
l1UserTxs []*common.L1Tx // l1UserTxs []*common.L1Tx
l1CoordinatorTxs []*common.L1Tx // l1CoordinatorTxs []*common.L1Tx
l2Txs []*common.L2Tx // l2Txs []*common.L2Tx
createdAccounts []*common.Account // createdAccounts []*common.Account
exitTree []common.ExitInfo // exitTree []*common.ExitInfo
batch *common.Batch // batch *common.Batch
} // }
// NewBatchData creates an empty BatchData with the slices initialized. // NewBatchData creates an empty BatchData with the slices initialized.
func NewBatchData() *BatchData { // func NewBatchData() *BatchData {
return &BatchData{ // return &BatchData{
l1UserTxs: make([]*common.L1Tx, 0), // l1UserTxs: make([]*common.L1Tx, 0),
l1CoordinatorTxs: make([]*common.L1Tx, 0), // l1CoordinatorTxs: make([]*common.L1Tx, 0),
l2Txs: make([]*common.L2Tx, 0), // l2Txs: make([]*common.L2Tx, 0),
createdAccounts: make([]*common.Account, 0), // createdAccounts: make([]*common.Account, 0),
exitTree: make([]common.ExitInfo, 0), // exitTree: make([]*common.ExitInfo, 0),
} // }
} // }
// BlockData contains information about Blocks from the contracts // BlockData contains information about Blocks from the contracts
type BlockData struct { // type blockData struct {
block *common.Block // Block *common.Block
// Rollup // // Rollup
l1Txs []*common.L1Tx // TODO: Answer: User? Coordinator? Both? // L1Txs []*common.L1Tx // TODO: Answer: User? Coordinator? Both?
batches []*BatchData // TODO: Also contains L1Txs! // Batches []*BatchData // TODO: Also contains L1Txs!
// withdrawals []*common.ExitInfo // TODO // // withdrawals []*common.ExitInfo // TODO
registeredTokens []*common.Token // RegisteredTokens []common.Token
rollupVars *common.RollupVars // RollupVars *common.RollupVars
// Auction // // Auction
bids []*common.Bid // Bids []*common.Bid
coordinators []*common.Coordinator // Coordinators []*common.Coordinator
auctionVars *common.AuctionVars // AuctionVars *common.AuctionVars
// WithdrawalDelayer // // WithdrawalDelayer
withdrawalDelayerVars *common.WithdrawalDelayerVars // WithdrawalDelayerVars *common.WithdrawalDelayerVars
} // }
// Synchronizer implements the Synchronizer type // Synchronizer implements the Synchronizer type
type Synchronizer struct { type Synchronizer struct {
ethClient *eth.Client ethClient eth.ClientInterface
historyDB *historydb.HistoryDB auctionConstants eth.AuctionConstants
stateDB *statedb.StateDB historyDB *historydb.HistoryDB
firstSavedBlock *common.Block stateDB *statedb.StateDB
mux sync.Mutex firstSavedBlock *common.Block
mux sync.Mutex
} }
// NewSynchronizer creates a new Synchronizer // NewSynchronizer creates a new Synchronizer
func NewSynchronizer(ethClient *eth.Client, historyDB *historydb.HistoryDB, stateDB *statedb.StateDB) *Synchronizer { func NewSynchronizer(ethClient eth.ClientInterface, historyDB *historydb.HistoryDB, stateDB *statedb.StateDB) (*Synchronizer, error) {
s := &Synchronizer{ auctionConstants, err := ethClient.AuctionConstants()
ethClient: ethClient, if err != nil {
historyDB: historyDB, return nil, err
stateDB: stateDB,
} }
return s return &Synchronizer{
ethClient: ethClient,
auctionConstants: *auctionConstants,
historyDB: historyDB,
stateDB: stateDB,
}, nil
} }
// TODO: Be smart about locking: only lock during the read/write operations // TODO: Be smart about locking: only lock during the read/write operations
@@ -116,7 +125,7 @@ func NewSynchronizer(ethClient *eth.Client, historyDB *historydb.HistoryDB, stat
// TODO: Add argument: maximum number of blocks to process // TODO: Add argument: maximum number of blocks to process
// TODO: Check reorgs in the middle of syncing a block. Probably make // TODO: Check reorgs in the middle of syncing a block. Probably make
// rollupSync, auctionSync and withdrawalSync return the block hash. // rollupSync, auctionSync and withdrawalSync return the block hash.
func (s *Synchronizer) Sync() error { func (s *Synchronizer) Sync(ctx context.Context) error {
// Avoid new sync while performing one // Avoid new sync while performing one
s.mux.Lock() s.mux.Lock()
defer s.mux.Unlock() defer s.mux.Unlock()
@@ -130,11 +139,10 @@ func (s *Synchronizer) Sync() error {
} }
// If we don't have any stored block, we must do a full sync starting from the rollup genesis block // If we don't have any stored block, we must do a full sync starting from the rollup genesis block
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
// TODO: Query rollup constants and genesis information, store them nextBlockNum = s.auctionConstants.GenesisBlockNum
nextBlockNum = 1234 // TODO: Replace this with genesisBlockNum
} else { } else {
// Get the latest block we have in History DB from blockchain to detect a reorg // Get the latest block we have in History DB from blockchain to detect a reorg
ethBlock, err := s.ethClient.EthBlockByNumber(context.Background(), lastSavedBlock.EthBlockNum) ethBlock, err := s.ethClient.EthBlockByNumber(ctx, lastSavedBlock.EthBlockNum)
if err != nil { if err != nil {
return err return err
} }
@@ -165,7 +173,7 @@ func (s *Synchronizer) Sync() error {
log.Debugf("Blocks to sync: %v (firstBlockToSync: %v, latestBlock: %v)", latestBlockNum-nextBlockNum+1, nextBlockNum, latestBlockNum) log.Debugf("Blocks to sync: %v (firstBlockToSync: %v, latestBlock: %v)", latestBlockNum-nextBlockNum+1, nextBlockNum, latestBlockNum)
for nextBlockNum < latestBlockNum { for nextBlockNum <= latestBlockNum {
ethBlock, err := s.ethClient.EthBlockByNumber(context.Background(), nextBlockNum) ethBlock, err := s.ethClient.EthBlockByNumber(context.Background(), nextBlockNum)
if err != nil { if err != nil {
return err return err
@@ -195,34 +203,40 @@ func (s *Synchronizer) Sync() error {
} }
// Group all the block data into the structs to save into HistoryDB // Group all the block data into the structs to save into HistoryDB
var blockData BlockData var blockData historydb.BlockData
blockData.block = ethBlock blockData.Block = ethBlock
if rollupData != nil { if rollupData != nil {
blockData.l1Txs = rollupData.l1Txs blockData.L1UserTxs = rollupData.l1UserTxs
blockData.batches = rollupData.batches blockData.Batches = rollupData.batches
// blockData.withdrawals = rollupData.withdrawals // TODO // blockData.withdrawals = rollupData.withdrawals // TODO
blockData.registeredTokens = rollupData.registeredTokens blockData.RegisteredTokens = rollupData.registeredTokens
blockData.rollupVars = rollupData.rollupVars blockData.RollupVars = rollupData.vars
} }
if auctionData != nil { if auctionData != nil {
blockData.bids = auctionData.bids blockData.Bids = auctionData.bids
blockData.coordinators = auctionData.coordinators blockData.Coordinators = auctionData.coordinators
blockData.auctionVars = auctionData.auctionVars blockData.AuctionVars = auctionData.vars
} }
if wdelayerData != nil { if wdelayerData != nil {
blockData.withdrawalDelayerVars = wdelayerData blockData.WithdrawDelayerVars = wdelayerData.vars
} }
// Add rollupData and auctionData once the method is updated // Add rollupData and auctionData once the method is updated
// TODO: Save Whole Struct -> AddBlockSCData(blockData) // TODO: Save Whole Struct -> AddBlockSCData(blockData)
err = s.historyDB.AddBlock(blockData.block) log.Debugw("Sync()", "block", blockData)
// err = s.historyDB.AddBlock(blockData.Block)
// if err != nil {
// return err
// }
err = s.historyDB.AddBlockSCData(&blockData)
if err != nil { if err != nil {
return err return err
} }
nextBlockNum++
} }
return nil return nil
@@ -358,18 +372,18 @@ func (s *Synchronizer) rollupSync(blockNum int64) (*rollupData, error) {
// } // }
// Get L1UserTX // Get L1UserTX
rollupData.l1Txs, err = getL1UserTx(rollupEvents.L1UserTx, blockNum) rollupData.l1UserTxs, err = getL1UserTx(rollupEvents.L1UserTx, blockNum)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Get ForgeBatch events to get the L1CoordinatorTxs // Get ForgeBatch events to get the L1CoordinatorTxs
for _, fbEvent := range rollupEvents.ForgeBatch { for _, evtForgeBatch := range rollupEvents.ForgeBatch {
batchData := NewBatchData() batchData := historydb.NewBatchData()
position := 0 position := 0
// Get the input for each Tx // Get the input for each Tx
forgeBatchArgs, err := s.ethClient.RollupForgeBatchArgs(fbEvent.EthTxHash) forgeBatchArgs, err := s.ethClient.RollupForgeBatchArgs(evtForgeBatch.EthTxHash)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -399,14 +413,14 @@ func (s *Synchronizer) rollupSync(blockNum int64) (*rollupData, error) {
l1CoordinatorTx.UserOrigin = false l1CoordinatorTx.UserOrigin = false
l1CoordinatorTx.EthBlockNum = blockNum l1CoordinatorTx.EthBlockNum = blockNum
bn := new(common.BatchNum) bn := new(common.BatchNum)
*bn = common.BatchNum(fbEvent.BatchNum) *bn = common.BatchNum(evtForgeBatch.BatchNum)
l1CoordinatorTx.BatchNum = bn l1CoordinatorTx.BatchNum = bn
l1CoordinatorTx, err = common.NewL1Tx(l1CoordinatorTx) l1Tx, err := common.NewL1Tx(l1CoordinatorTx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
batchData.l1CoordinatorTxs = append(batchData.l1CoordinatorTxs, l1CoordinatorTx) batchData.L1CoordinatorTxs = append(batchData.L1CoordinatorTxs, *l1Tx)
// Check if we have to register an account // Check if we have to register an account
// if l1CoordinatorTx.FromIdx == 0 { // if l1CoordinatorTx.FromIdx == 0 {
@@ -435,7 +449,7 @@ func (s *Synchronizer) rollupSync(blockNum int64) (*rollupData, error) {
// TODO: Get createdAccounts from ProcessTxs() // TODO: Get createdAccounts from ProcessTxs()
// TODO: Get CollectedFees from ProcessTxs() // TODO: Get CollectedFees from ProcessTxs()
// TODO: Pass forgeBatchArgs.FeeIdxCoordinator to ProcessTxs() // TODO: Pass forgeBatchArgs.FeeIdxCoordinator to ProcessTxs()
_, exitInfo, err := s.stateDB.ProcessTxs(batchData.l1UserTxs, batchData.l1CoordinatorTxs, poolL2Txs) _, exitInfo, err := s.stateDB.ProcessTxs(batchData.L1UserTxs, batchData.L1CoordinatorTxs, poolL2Txs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -444,13 +458,13 @@ func (s *Synchronizer) rollupSync(blockNum int64) (*rollupData, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
batchData.l2Txs = append(batchData.l2Txs, l2Txs...) batchData.L2Txs = append(batchData.L2Txs, l2Txs...)
batchData.exitTree = exitInfo batchData.ExitTree = exitInfo
// Get Batch information // Get Batch information
batch := &common.Batch{ batch := &common.Batch{
BatchNum: common.BatchNum(fbEvent.BatchNum), BatchNum: common.BatchNum(evtForgeBatch.BatchNum),
EthBlockNum: blockNum, EthBlockNum: blockNum,
// ForgerAddr: , TODO: Get it from ethClient -> Add ForgerAddr to RollupEventForgeBatch // ForgerAddr: , TODO: Get it from ethClient -> Add ForgerAddr to RollupEventForgeBatch
// CollectedFees: , TODO: Clarify where to get them if they are still needed // CollectedFees: , TODO: Clarify where to get them if they are still needed
@@ -460,19 +474,23 @@ func (s *Synchronizer) rollupSync(blockNum int64) (*rollupData, error) {
ForgeL1TxsNum: &forgeL1TxsNum, ForgeL1TxsNum: &forgeL1TxsNum,
// SlotNum: TODO: Calculate once ethClient provides the info // calculate from blockNum + ethClient Constants // SlotNum: TODO: Calculate once ethClient provides the info // calculate from blockNum + ethClient Constants
} }
batchData.batch = batch batchData.Batch = batch
rollupData.batches = append(rollupData.batches, batchData) rollupData.batches = append(rollupData.batches, *batchData)
} }
// Get Registered Tokens // Get Registered Tokens
for _, eAddToken := range rollupEvents.AddToken { for _, evtAddToken := range rollupEvents.AddToken {
var token *common.Token var token common.Token
token.TokenID = common.TokenID(eAddToken.TokenID) token.TokenID = common.TokenID(evtAddToken.TokenID)
token.EthAddr = eAddToken.Address token.EthAddr = evtAddToken.Address
token.EthBlockNum = blockNum token.EthBlockNum = blockNum
// TODO: Add external information consulting SC about it using Address // TODO: Add external information consulting SC about it using Address
token.Name = "TODO"
token.Symbol = "TODO"
token.Decimals = 8 // TODO
rollupData.registeredTokens = append(rollupData.registeredTokens, token) rollupData.registeredTokens = append(rollupData.registeredTokens, token)
} }
@@ -498,22 +516,22 @@ func (s *Synchronizer) auctionSync(blockNum int64) (*auctionData, error) {
} }
// Get bids // Get bids
for _, eNewBid := range auctionEvents.NewBid { for _, evtNewBid := range auctionEvents.NewBid {
bid := &common.Bid{ bid := common.Bid{
SlotNum: common.SlotNum(eNewBid.Slot), SlotNum: common.SlotNum(evtNewBid.Slot),
BidValue: eNewBid.BidAmount, BidValue: evtNewBid.BidAmount,
Bidder: eNewBid.Bidder, Bidder: evtNewBid.Bidder,
EthBlockNum: blockNum, EthBlockNum: blockNum,
} }
auctionData.bids = append(auctionData.bids, bid) auctionData.bids = append(auctionData.bids, bid)
} }
// Get Coordinators // Get Coordinators
for _, eNewCoordinator := range auctionEvents.SetCoordinator { for _, evtSetCoordinator := range auctionEvents.SetCoordinator {
coordinator := &common.Coordinator{ coordinator := common.Coordinator{
Bidder: eNewCoordinator.BidderAddress, Bidder: evtSetCoordinator.BidderAddress,
Forger: eNewCoordinator.ForgerAddress, Forger: evtSetCoordinator.ForgerAddress,
URL: eNewCoordinator.CoordinatorURL, URL: evtSetCoordinator.CoordinatorURL,
} }
auctionData.coordinators = append(auctionData.coordinators, coordinator) auctionData.coordinators = append(auctionData.coordinators, coordinator)
} }
@@ -537,7 +555,7 @@ func (s *Synchronizer) auctionSync(blockNum int64) (*auctionData, error) {
} }
// wdelayerSync gets information from the Withdrawal Delayer Contract // wdelayerSync gets information from the Withdrawal Delayer Contract
func (s *Synchronizer) wdelayerSync(blockNum int64) (*common.WithdrawalDelayerVars, error) { func (s *Synchronizer) wdelayerSync(blockNum int64) (*wdelayerData, error) {
// TODO: VARS // TODO: VARS
// TODO: CONSTANTS // TODO: CONSTANTS
@@ -559,24 +577,23 @@ func (s *Synchronizer) wdelayerSync(blockNum int64) (*common.WithdrawalDelayerVa
// return forgeBatchArgs.NewLastIdx + 1, nil // return forgeBatchArgs.NewLastIdx + 1, nil
// } // }
func getL1UserTx(l1UserTxEvents []eth.RollupEventL1UserTx, blockNum int64) ([]*common.L1Tx, error) { func getL1UserTx(eventsL1UserTx []eth.RollupEventL1UserTx, blockNum int64) ([]common.L1Tx, error) {
l1Txs := make([]*common.L1Tx, 0) l1Txs := make([]common.L1Tx, 0)
for _, eL1UserTx := range l1UserTxEvents { for _, evtL1UserTx := range eventsL1UserTx {
// Fill aditional Tx fields // Fill aditional Tx fields
toForge := new(int64) toForge := evtL1UserTx.ToForgeL1TxsNum
*toForge = eL1UserTx.ToForgeL1TxsNum evtL1UserTx.L1Tx.ToForgeL1TxsNum = &toForge
eL1UserTx.L1Tx.ToForgeL1TxsNum = toForge evtL1UserTx.L1Tx.Position = evtL1UserTx.Position
eL1UserTx.L1Tx.Position = eL1UserTx.Position evtL1UserTx.L1Tx.UserOrigin = true
eL1UserTx.L1Tx.UserOrigin = true evtL1UserTx.L1Tx.EthBlockNum = blockNum
eL1UserTx.L1Tx.EthBlockNum = blockNum nL1Tx, err := common.NewL1Tx(&evtL1UserTx.L1Tx)
nL1Tx, err := common.NewL1Tx(&eL1UserTx.L1Tx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
eL1UserTx.L1Tx = *nL1Tx evtL1UserTx.L1Tx = *nL1Tx
l1Txs = append(l1Txs, &eL1UserTx.L1Tx) l1Txs = append(l1Txs, evtL1UserTx.L1Tx)
} }
return l1Txs, nil return l1Txs, nil
} }

View File

@@ -1,25 +1,37 @@
package synchronizer package synchronizer
import ( import (
"context"
"io/ioutil" "io/ioutil"
"math/big"
"os" "os"
"testing" "testing"
"github.com/ethereum/go-ethereum/ethclient" ethCommon "github.com/ethereum/go-ethereum/common"
dbUtils "github.com/hermeznetwork/hermez-node/db" dbUtils "github.com/hermeznetwork/hermez-node/db"
"github.com/hermeznetwork/hermez-node/db/historydb" "github.com/hermeznetwork/hermez-node/db/historydb"
"github.com/hermeznetwork/hermez-node/db/statedb" "github.com/hermeznetwork/hermez-node/db/statedb"
"github.com/hermeznetwork/hermez-node/eth" "github.com/hermeznetwork/hermez-node/test"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func Test(t *testing.T) { type timer struct {
time int64
}
func (t *timer) Time() int64 {
currentTime := t.time
t.time++
return currentTime
}
func TestSync(t *testing.T) {
// Int State DB // Int State DB
dir, err := ioutil.TempDir("", "tmpdb") dir, err := ioutil.TempDir("", "tmpdb")
require.Nil(t, err) require.Nil(t, err)
sdb, err := statedb.NewStateDB(dir, statedb.TypeSynchronizer, 32) stateDB, err := statedb.NewStateDB(dir, statedb.TypeSynchronizer, 32)
assert.Nil(t, err) assert.Nil(t, err)
// Init History DB // Init History DB
@@ -27,23 +39,56 @@ func Test(t *testing.T) {
db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez") db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
require.Nil(t, err) require.Nil(t, err)
historyDB := historydb.NewHistoryDB(db) historyDB := historydb.NewHistoryDB(db)
err = historyDB.Reorg(0) // Clear DB
err = historyDB.Reorg(-1)
assert.Nil(t, err) assert.Nil(t, err)
// Init eth client // Init eth client
ehtClientDialURL := os.Getenv("ETHCLIENT_DIAL_URL") var timer timer
ethClient, err := ethclient.Dial(ehtClientDialURL) clientSetup := test.NewClientSetupExample()
require.Nil(t, err) client := test.NewClient(true, &timer, &ethCommon.Address{}, clientSetup)
client := eth.NewClient(ethClient, nil, nil, nil)
// Create Synchronizer // Create Synchronizer
s := NewSynchronizer(client, historyDB, sdb) s, err := NewSynchronizer(client, historyDB, stateDB)
require.NotNil(t, s) require.Nil(t, err)
// Test Sync // Test Sync for ethereum genesis block
// err = s.Sync() err = s.Sync(context.Background())
// require.Nil(t, err) require.Nil(t, err)
blocks, err := s.historyDB.GetBlocks(0, 9999)
require.Nil(t, err)
assert.Equal(t, int64(0), blocks[0].EthBlockNum)
// 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
C (1): 8
D (3): 15
> advance batch
`)
require.Greater(t, len(l1UserTxs[0]), 0)
// require.Greater(t, len(tokens), 0)
for i := 1; i <= 3; i++ {
_, err := client.RollupAddToken(ethCommon.BigToAddress(big.NewInt(int64(i * 10000))))
require.Nil(t, err)
}
for _, l1UserTx := range l1UserTxs[0] {
client.CtlAddL1TxUser(&l1UserTx)
}
client.CtlMineBlock()
err = s.Sync(context.Background())
require.Nil(t, err)
getTokens, err := s.historyDB.GetTokens()
require.Nil(t, err)
assert.Equal(t, 3, len(getTokens))
// TODO: Reorg will be properly tested once we have the mock ethClient implemented // TODO: Reorg will be properly tested once we have the mock ethClient implemented
/* /*

View File

@@ -329,10 +329,11 @@ func NewClient(l bool, timer Timer, addr *ethCommon.Address, setup *ClientSetup)
blockCurrent := Block{ blockCurrent := Block{
Rollup: &RollupBlock{ Rollup: &RollupBlock{
State: eth.RollupState{ State: eth.RollupState{
StateRoot: big.NewInt(0), StateRoot: big.NewInt(0),
ExitRoots: make([]*big.Int, 0), ExitRoots: make([]*big.Int, 0),
ExitNullifierMap: make(map[[256 / 8]byte]bool), ExitNullifierMap: make(map[[256 / 8]byte]bool),
TokenList: make([]ethCommon.Address, 0), // TokenID = 0 is ETH. Set first entry in TokenList with 0x0 address for ETH.
TokenList: []ethCommon.Address{ethCommon.Address{}},
TokenMap: make(map[ethCommon.Address]bool), TokenMap: make(map[ethCommon.Address]bool),
MapL1TxQueue: mapL1TxQueue, MapL1TxQueue: mapL1TxQueue,
LastL1L2Batch: 0, LastL1L2Batch: 0,
@@ -597,7 +598,11 @@ func (c *Client) CtlAddL1TxUser(l1Tx *common.L1Tx) {
panic("l1Tx.TokenID + 1 > len(r.State.TokenList)") panic("l1Tx.TokenID + 1 > len(r.State.TokenList)")
} }
queue.L1TxQueue = append(queue.L1TxQueue, *l1Tx) queue.L1TxQueue = append(queue.L1TxQueue, *l1Tx)
r.Events.L1UserTx = append(r.Events.L1UserTx, eth.RollupEventL1UserTx{L1Tx: *l1Tx}) r.Events.L1UserTx = append(r.Events.L1UserTx, eth.RollupEventL1UserTx{
L1Tx: *l1Tx,
ToForgeL1TxsNum: r.State.LastToForgeL1TxsNum,
Position: len(queue.L1TxQueue) - 1,
})
} }
type transactionData struct { type transactionData struct {

View File

@@ -152,6 +152,12 @@ func GenL1Txs(
LoadAmountUSD: lUSD, LoadAmountUSD: lUSD,
EthBlockNum: blocks[i%len(blocks)].EthBlockNum, EthBlockNum: blocks[i%len(blocks)].EthBlockNum,
} }
if tx.UserOrigin {
n := nextTxsNum
tx.ToForgeL1TxsNum = &n
} else {
tx.BatchNum = &batches[i%len(batches)].BatchNum
}
nTx, err := common.NewL1Tx(&tx) nTx, err := common.NewL1Tx(&tx)
if err != nil { if err != nil {
panic(err) panic(err)
@@ -163,7 +169,8 @@ func GenL1Txs(
setFromToAndAppend(fromIdx, tx, i, nUserTxs, userAddr, accounts, &userTxs, &othersTxs) setFromToAndAppend(fromIdx, tx, i, nUserTxs, userAddr, accounts, &userTxs, &othersTxs)
} else { } else {
// Add unforged txs // Add unforged txs
tx.ToForgeL1TxsNum = nextTxsNum n := nextTxsNum
tx.ToForgeL1TxsNum = &n
tx.UserOrigin = true tx.UserOrigin = true
setFromToAndAppend(fromIdx, tx, i, nUserTxs, userAddr, accounts, &userTxs, &othersTxs) setFromToAndAppend(fromIdx, tx, i, nUserTxs, userAddr, accounts, &userTxs, &othersTxs)
} }
@@ -172,13 +179,13 @@ func GenL1Txs(
} }
// GetNextToForgeNumAndBatch returns the next BatchNum and ForgeL1TxsNum to be added // GetNextToForgeNumAndBatch returns the next BatchNum and ForgeL1TxsNum to be added
func GetNextToForgeNumAndBatch(batches []common.Batch) (common.BatchNum, *int64) { func GetNextToForgeNumAndBatch(batches []common.Batch) (common.BatchNum, int64) {
batchNum := batches[len(batches)-1].BatchNum + 1 batchNum := batches[len(batches)-1].BatchNum + 1
toForgeL1TxsNum := new(int64) var toForgeL1TxsNum int64
found := false found := false
for i := len(batches) - 1; i >= 0; i-- { for i := len(batches) - 1; i >= 0; i-- {
if batches[i].ForgeL1TxsNum != nil { if batches[i].ForgeL1TxsNum != nil {
*toForgeL1TxsNum = *batches[i].ForgeL1TxsNum + 1 toForgeL1TxsNum = *batches[i].ForgeL1TxsNum + 1
found = true found = true
break break
} }

View File

@@ -52,16 +52,16 @@ func GenerateKeys(t *testing.T, accNames []string) map[string]*Account {
// GenerateTestTxs generates L1Tx & PoolL2Tx in a deterministic way for the // GenerateTestTxs generates L1Tx & PoolL2Tx in a deterministic way for the
// given Instructions. // given Instructions.
func GenerateTestTxs(t *testing.T, instructions Instructions) ([][]*common.L1Tx, [][]*common.L1Tx, [][]*common.PoolL2Tx, []common.Token) { func GenerateTestTxs(t *testing.T, instructions Instructions) ([][]common.L1Tx, [][]common.L1Tx, [][]common.PoolL2Tx, []common.Token) {
accounts := GenerateKeys(t, instructions.Accounts) accounts := GenerateKeys(t, instructions.Accounts)
l1CreatedAccounts := make(map[string]*Account) l1CreatedAccounts := make(map[string]*Account)
var batchL1Txs []*common.L1Tx var batchL1Txs []common.L1Tx
var batchCoordinatorL1Txs []*common.L1Tx var batchCoordinatorL1Txs []common.L1Tx
var batchPoolL2Txs []*common.PoolL2Tx var batchPoolL2Txs []common.PoolL2Tx
var l1Txs [][]*common.L1Tx var l1Txs [][]common.L1Tx
var coordinatorL1Txs [][]*common.L1Tx var coordinatorL1Txs [][]common.L1Tx
var poolL2Txs [][]*common.PoolL2Tx var poolL2Txs [][]common.PoolL2Tx
idx := 256 idx := 256
for _, inst := range instructions.Instructions { for _, inst := range instructions.Instructions {
switch inst.Type { switch inst.Type {
@@ -71,10 +71,11 @@ func GenerateTestTxs(t *testing.T, instructions Instructions) ([][]*common.L1Tx,
FromEthAddr: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Addr, FromEthAddr: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Addr,
FromBJJ: accounts[idxTokenIDToString(inst.From, inst.TokenID)].BJJ.Public(), FromBJJ: accounts[idxTokenIDToString(inst.From, inst.TokenID)].BJJ.Public(),
TokenID: inst.TokenID, TokenID: inst.TokenID,
Amount: big.NewInt(0),
LoadAmount: big.NewInt(int64(inst.Amount)), LoadAmount: big.NewInt(int64(inst.Amount)),
Type: common.TxTypeCreateAccountDeposit, Type: common.TxTypeCreateAccountDeposit,
} }
batchL1Txs = append(batchL1Txs, &tx) batchL1Txs = append(batchL1Txs, tx)
if accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx == common.Idx(0) { // if account.Idx is not set yet, set it and increment idx if accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx == common.Idx(0) { // if account.Idx is not set yet, set it and increment idx
accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx = common.Idx(idx) accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx = common.Idx(idx)
@@ -93,7 +94,7 @@ func GenerateTestTxs(t *testing.T, instructions Instructions) ([][]*common.L1Tx,
} }
accounts[idxTokenIDToString(inst.To, inst.TokenID)].Idx = common.Idx(idx) accounts[idxTokenIDToString(inst.To, inst.TokenID)].Idx = common.Idx(idx)
l1CreatedAccounts[idxTokenIDToString(inst.To, inst.TokenID)] = accounts[idxTokenIDToString(inst.To, inst.TokenID)] l1CreatedAccounts[idxTokenIDToString(inst.To, inst.TokenID)] = accounts[idxTokenIDToString(inst.To, inst.TokenID)]
batchCoordinatorL1Txs = append(batchCoordinatorL1Txs, &tx) batchCoordinatorL1Txs = append(batchCoordinatorL1Txs, tx)
idx++ idx++
} }
toIdx := new(common.Idx) toIdx := new(common.Idx)
@@ -132,7 +133,7 @@ func GenerateTestTxs(t *testing.T, instructions Instructions) ([][]*common.L1Tx,
tx.Signature = sig tx.Signature = sig
accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce++ accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce++
batchPoolL2Txs = append(batchPoolL2Txs, &tx) batchPoolL2Txs = append(batchPoolL2Txs, tx)
case common.TxTypeExit, common.TxTypeForceExit: case common.TxTypeExit, common.TxTypeForceExit:
fromIdx := new(common.Idx) fromIdx := new(common.Idx)
@@ -144,14 +145,14 @@ func GenerateTestTxs(t *testing.T, instructions Instructions) ([][]*common.L1Tx,
Amount: big.NewInt(int64(inst.Amount)), Amount: big.NewInt(int64(inst.Amount)),
Type: common.TxTypeExit, Type: common.TxTypeExit,
} }
batchL1Txs = append(batchL1Txs, &tx) batchL1Txs = append(batchL1Txs, tx)
case TypeNewBatch: case TypeNewBatch:
l1Txs = append(l1Txs, batchL1Txs) l1Txs = append(l1Txs, batchL1Txs)
coordinatorL1Txs = append(coordinatorL1Txs, batchCoordinatorL1Txs) coordinatorL1Txs = append(coordinatorL1Txs, batchCoordinatorL1Txs)
poolL2Txs = append(poolL2Txs, batchPoolL2Txs) poolL2Txs = append(poolL2Txs, batchPoolL2Txs)
batchL1Txs = []*common.L1Tx{} batchL1Txs = []common.L1Tx{}
batchCoordinatorL1Txs = []*common.L1Tx{} batchCoordinatorL1Txs = []common.L1Tx{}
batchPoolL2Txs = []*common.PoolL2Tx{} batchPoolL2Txs = []common.PoolL2Tx{}
default: default:
continue continue
} }
@@ -184,7 +185,7 @@ func GenerateTestTxs(t *testing.T, instructions Instructions) ([][]*common.L1Tx,
// GenerateTestTxsFromSet reurns the L1 & L2 transactions for a given Set of // GenerateTestTxsFromSet reurns the L1 & L2 transactions for a given Set of
// Instructions code // Instructions code
func GenerateTestTxsFromSet(t *testing.T, set string) ([][]*common.L1Tx, [][]*common.L1Tx, [][]*common.PoolL2Tx, []common.Token) { func GenerateTestTxsFromSet(t *testing.T, set string) ([][]common.L1Tx, [][]common.L1Tx, [][]common.PoolL2Tx, []common.Token) {
parser := NewParser(strings.NewReader(set)) parser := NewParser(strings.NewReader(set))
instructions, err := parser.Parse() instructions, err := parser.Parse()
require.Nil(t, err) require.Nil(t, err)

View File

@@ -16,7 +16,7 @@ import (
) )
// txs implements the interface Sort for an array of Tx // txs implements the interface Sort for an array of Tx
type txs []*common.PoolL2Tx type txs []common.PoolL2Tx
func (t txs) Len() int { func (t txs) Len() int {
return len(t) return len(t)
@@ -68,7 +68,7 @@ func (txsel *TxSelector) Reset(batchNum common.BatchNum) error {
} }
// GetL2TxSelection returns a selection of the L2Txs for the next batch, from the L2DB pool // GetL2TxSelection returns a selection of the L2Txs for the next batch, from the L2DB pool
func (txsel *TxSelector) GetL2TxSelection(batchNum common.BatchNum) ([]*common.PoolL2Tx, error) { func (txsel *TxSelector) GetL2TxSelection(batchNum common.BatchNum) ([]common.PoolL2Tx, error) {
// get pending l2-tx from tx-pool // get pending l2-tx from tx-pool
l2TxsRaw, err := txsel.l2db.GetPendingTxs() // once l2db ready, maybe use parameter 'batchNum' l2TxsRaw, err := txsel.l2db.GetPendingTxs() // once l2db ready, maybe use parameter 'batchNum'
if err != nil { if err != nil {
@@ -81,7 +81,7 @@ func (txsel *TxSelector) GetL2TxSelection(batchNum common.BatchNum) ([]*common.P
_, err = txsel.localAccountsDB.GetAccount(&tx.FromIdx) _, err = txsel.localAccountsDB.GetAccount(&tx.FromIdx)
if err == nil { if err == nil {
// if FromIdx has an account into the AccountsDB // if FromIdx has an account into the AccountsDB
validTxs = append(validTxs, tx) validTxs = append(validTxs, *tx)
} }
} }
@@ -98,7 +98,7 @@ func (txsel *TxSelector) GetL2TxSelection(batchNum common.BatchNum) ([]*common.P
} }
// GetL1L2TxSelection returns the selection of L1 + L2 txs // GetL1L2TxSelection returns the selection of L1 + L2 txs
func (txsel *TxSelector) GetL1L2TxSelection(batchNum common.BatchNum, l1Txs []*common.L1Tx) ([]*common.L1Tx, []*common.L1Tx, []*common.PoolL2Tx, error) { func (txsel *TxSelector) GetL1L2TxSelection(batchNum common.BatchNum, l1Txs []common.L1Tx) ([]common.L1Tx, []common.L1Tx, []common.PoolL2Tx, error) {
// apply l1-user-tx to localAccountDB // apply l1-user-tx to localAccountDB
// create new leaves // create new leaves
// update balances // update balances
@@ -111,7 +111,7 @@ func (txsel *TxSelector) GetL1L2TxSelection(batchNum common.BatchNum, l1Txs []*c
} }
var validTxs txs var validTxs txs
var l1CoordinatorTxs []*common.L1Tx var l1CoordinatorTxs []common.L1Tx
positionL1 := len(l1Txs) positionL1 := len(l1Txs)
// if tx.ToIdx>=256, tx.ToIdx should exist to localAccountsDB, if so, // if tx.ToIdx>=256, tx.ToIdx should exist to localAccountsDB, if so,
@@ -127,7 +127,7 @@ func (txsel *TxSelector) GetL1L2TxSelection(batchNum common.BatchNum, l1Txs []*c
// a L1CoordinatorTx of this type, in the DB there still seem // a L1CoordinatorTx of this type, in the DB there still seem
// that needs to create a new L1CoordinatorTx, but as is already // that needs to create a new L1CoordinatorTx, but as is already
// created, the tx is valid // created, the tx is valid
validTxs = append(validTxs, l2TxsRaw[i]) validTxs = append(validTxs, *l2TxsRaw[i])
continue continue
} }
@@ -143,7 +143,7 @@ func (txsel *TxSelector) GetL1L2TxSelection(batchNum common.BatchNum, l1Txs []*c
// account for ToEthAddr&ToBJJ already exist, // account for ToEthAddr&ToBJJ already exist,
// there is no need to create a new one. // there is no need to create a new one.
// tx valid, StateDB will use the ToIdx==0 to define the AuxToIdx // tx valid, StateDB will use the ToIdx==0 to define the AuxToIdx
validTxs = append(validTxs, l2TxsRaw[i]) validTxs = append(validTxs, *l2TxsRaw[i])
continue continue
} }
// if not, check if AccountCreationAuth exist for that ToEthAddr&BJJ // if not, check if AccountCreationAuth exist for that ToEthAddr&BJJ
@@ -159,7 +159,7 @@ func (txsel *TxSelector) GetL1L2TxSelection(batchNum common.BatchNum, l1Txs []*c
log.Debugw("invalid L2Tx: ToIdx not found in StateDB, neither ToEthAddr & ToBJJ found in AccountCreationAuths L2DB", "ToIdx", l2TxsRaw[i].ToIdx, "ToEthAddr", l2TxsRaw[i].ToEthAddr, "ToBJJ", l2TxsRaw[i].ToBJJ) log.Debugw("invalid L2Tx: ToIdx not found in StateDB, neither ToEthAddr & ToBJJ found in AccountCreationAuths L2DB", "ToIdx", l2TxsRaw[i].ToIdx, "ToEthAddr", l2TxsRaw[i].ToEthAddr, "ToBJJ", l2TxsRaw[i].ToBJJ)
continue continue
} }
validTxs = append(validTxs, l2TxsRaw[i]) validTxs = append(validTxs, *l2TxsRaw[i])
} else { } else {
// case: ToBJJ==0: // case: ToBJJ==0:
// if idx exist for EthAddr use it // if idx exist for EthAddr use it
@@ -168,7 +168,7 @@ func (txsel *TxSelector) GetL1L2TxSelection(batchNum common.BatchNum, l1Txs []*c
// account for ToEthAddr already exist, // account for ToEthAddr already exist,
// there is no need to create a new one. // there is no need to create a new one.
// tx valid, StateDB will use the ToIdx==0 to define the AuxToIdx // tx valid, StateDB will use the ToIdx==0 to define the AuxToIdx
validTxs = append(validTxs, l2TxsRaw[i]) validTxs = append(validTxs, *l2TxsRaw[i])
continue continue
} }
// if not, check if AccountCreationAuth exist for that ToEthAddr // if not, check if AccountCreationAuth exist for that ToEthAddr
@@ -178,10 +178,10 @@ func (txsel *TxSelector) GetL1L2TxSelection(batchNum common.BatchNum, l1Txs []*c
log.Debugw("invalid L2Tx: ToIdx not found in StateDB, neither ToEthAddr found in AccountCreationAuths L2DB", "ToIdx", l2TxsRaw[i].ToIdx, "ToEthAddr", l2TxsRaw[i].ToEthAddr) log.Debugw("invalid L2Tx: ToIdx not found in StateDB, neither ToEthAddr found in AccountCreationAuths L2DB", "ToIdx", l2TxsRaw[i].ToIdx, "ToEthAddr", l2TxsRaw[i].ToEthAddr)
continue continue
} }
validTxs = append(validTxs, l2TxsRaw[i]) validTxs = append(validTxs, *l2TxsRaw[i])
} }
// create L1CoordinatorTx for the accountCreation // create L1CoordinatorTx for the accountCreation
l1CoordinatorTx := &common.L1Tx{ l1CoordinatorTx := common.L1Tx{
Position: positionL1, Position: positionL1,
UserOrigin: false, UserOrigin: false,
FromEthAddr: accAuth.EthAddr, FromEthAddr: accAuth.EthAddr,
@@ -199,7 +199,7 @@ func (txsel *TxSelector) GetL1L2TxSelection(batchNum common.BatchNum, l1Txs []*c
// account for ToEthAddr&ToBJJ already exist, (where ToEthAddr==0xff) // account for ToEthAddr&ToBJJ already exist, (where ToEthAddr==0xff)
// there is no need to create a new one. // there is no need to create a new one.
// tx valid, StateDB will use the ToIdx==0 to define the AuxToIdx // tx valid, StateDB will use the ToIdx==0 to define the AuxToIdx
validTxs = append(validTxs, l2TxsRaw[i]) validTxs = append(validTxs, *l2TxsRaw[i])
continue continue
} }
// if idx don't exist for EthAddr&BJJ, // if idx don't exist for EthAddr&BJJ,
@@ -210,7 +210,7 @@ func (txsel *TxSelector) GetL1L2TxSelection(batchNum common.BatchNum, l1Txs []*c
log.Warn("l2TxsRaw[i].ToEthAddr should not be nil") log.Warn("l2TxsRaw[i].ToEthAddr should not be nil")
continue continue
} }
l1CoordinatorTx := &common.L1Tx{ l1CoordinatorTx := common.L1Tx{
Position: positionL1, Position: positionL1,
UserOrigin: false, UserOrigin: false,
FromEthAddr: *l2TxsRaw[i].ToEthAddr, FromEthAddr: *l2TxsRaw[i].ToEthAddr,
@@ -230,10 +230,10 @@ func (txsel *TxSelector) GetL1L2TxSelection(batchNum common.BatchNum, l1Txs []*c
continue continue
} }
// Account found in the DB, include the l2Tx in the selection // Account found in the DB, include the l2Tx in the selection
validTxs = append(validTxs, l2TxsRaw[i]) validTxs = append(validTxs, *l2TxsRaw[i])
} else if *l2TxsRaw[i].ToIdx == common.Idx(1) { // nil already checked before } else if *l2TxsRaw[i].ToIdx == common.Idx(1) { // nil already checked before
// valid txs (of Exit type) // valid txs (of Exit type)
validTxs = append(validTxs, l2TxsRaw[i]) validTxs = append(validTxs, *l2TxsRaw[i])
} }
} }
@@ -254,7 +254,7 @@ func (txsel *TxSelector) GetL1L2TxSelection(batchNum common.BatchNum, l1Txs []*c
return l1Txs, l1CoordinatorTxs, l2Txs, nil return l1Txs, l1CoordinatorTxs, l2Txs, nil
} }
func checkAlreadyPendingToCreate(l1CoordinatorTxs []*common.L1Tx, addr *ethCommon.Address, bjj *babyjub.PublicKey) bool { func checkAlreadyPendingToCreate(l1CoordinatorTxs []common.L1Tx, addr *ethCommon.Address, bjj *babyjub.PublicKey) bool {
if addr == nil { if addr == nil {
log.Warn("The provided addr is nil") log.Warn("The provided addr is nil")
return false return false

View File

@@ -35,9 +35,9 @@ func initTest(t *testing.T, testSet string) *TxSelector {
return txsel return txsel
} }
func addL2Txs(t *testing.T, txsel *TxSelector, poolL2Txs []*common.PoolL2Tx) { func addL2Txs(t *testing.T, txsel *TxSelector, poolL2Txs []common.PoolL2Tx) {
for i := 0; i < len(poolL2Txs); i++ { for i := 0; i < len(poolL2Txs); i++ {
err := txsel.l2db.AddTxTest(poolL2Txs[i]) err := txsel.l2db.AddTxTest(&poolL2Txs[i])
require.Nil(t, err) require.Nil(t, err)
} }
} }