arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/db/historydb/historydb.go
db/historydb/historydb.goRaw
  1package historydb
  2
  3import (
  4	"database/sql"
  5	"errors"
  6	"fmt"
  7	"math/big"
  8
  9	ethCommon "github.com/ethereum/go-ethereum/common"
 10	"github.com/hermeznetwork/hermez-node/common"
 11	"github.com/hermeznetwork/hermez-node/db"
 12	"github.com/hermeznetwork/hermez-node/log"
 13	"github.com/iden3/go-iden3-crypto/babyjub"
 14	"github.com/jmoiron/sqlx"
 15
 16	//nolint:errcheck // driver for postgres DB
 17	_ "github.com/lib/pq"
 18	"github.com/russross/meddler"
 19)
 20
 21// TODO(Edu): Document here how HistoryDB is kept consistent
 22
 23// HistoryDB persist the historic of the rollup
 24type HistoryDB struct {
 25	db *sqlx.DB
 26}
 27
 28// BlockData contains the information of a Block
 29type BlockData struct {
 30	Block *common.Block
 31	// Rollup
 32	// L1UserTxs that were submitted in the block
 33	L1UserTxs        []common.L1Tx
 34	Batches          []BatchData
 35	RegisteredTokens []common.Token
 36	RollupVars       *common.RollupVars
 37	// Auction
 38	Bids                []common.Bid
 39	Coordinators        []common.Coordinator
 40	AuctionVars         *common.AuctionVars
 41	WithdrawDelayerVars *common.WithdrawDelayerVars
 42	// TODO: enable when common.WithdrawalDelayerVars is Merged from Synchronizer PR
 43	// WithdrawalDelayerVars *common.WithdrawalDelayerVars
 44}
 45
 46// BatchData contains the information of a Batch
 47type BatchData struct {
 48	// L1UserTxs that were forged in the batch
 49	L1Batch          bool // TODO: Remove once Batch.ForgeL1TxsNum is a pointer
 50	L1UserTxs        []common.L1Tx
 51	L1CoordinatorTxs []common.L1Tx
 52	L2Txs            []common.L2Tx
 53	CreatedAccounts  []common.Account
 54	ExitTree         []common.ExitInfo
 55	Batch            *common.Batch
 56}
 57
 58// NewBatchData creates an empty BatchData with the slices initialized.
 59func NewBatchData() *BatchData {
 60	return &BatchData{
 61		L1Batch:          false,
 62		L1UserTxs:        make([]common.L1Tx, 0),
 63		L1CoordinatorTxs: make([]common.L1Tx, 0),
 64		L2Txs:            make([]common.L2Tx, 0),
 65		CreatedAccounts:  make([]common.Account, 0),
 66		ExitTree:         make([]common.ExitInfo, 0),
 67		Batch:            &common.Batch{},
 68	}
 69}
 70
 71// NewHistoryDB initialize the DB
 72func NewHistoryDB(db *sqlx.DB) *HistoryDB {
 73	return &HistoryDB{db: db}
 74}
 75
 76// AddBlock insert a block into the DB
 77func (hdb *HistoryDB) AddBlock(block *common.Block) error { return hdb.addBlock(hdb.db, block) }
 78func (hdb *HistoryDB) addBlock(d meddler.DB, block *common.Block) error {
 79	return meddler.Insert(d, "block", block)
 80}
 81
 82// AddBlocks inserts blocks into the DB
 83func (hdb *HistoryDB) AddBlocks(blocks []common.Block) error {
 84	return hdb.addBlocks(hdb.db, blocks)
 85}
 86
 87func (hdb *HistoryDB) addBlocks(d meddler.DB, blocks []common.Block) error {
 88	return db.BulkInsert(
 89		d,
 90		`INSERT INTO block (
 91			eth_block_num,
 92			timestamp,
 93			hash
 94		) VALUES %s;`,
 95		blocks[:],
 96	)
 97}
 98
 99// GetBlock retrieve a block from the DB, given a block number
100func (hdb *HistoryDB) GetBlock(blockNum int64) (*common.Block, error) {
101	block := &common.Block{}
102	err := meddler.QueryRow(
103		hdb.db, block,
104		"SELECT * FROM block WHERE eth_block_num = $1;", blockNum,
105	)
106	return block, err
107}
108
109// GetBlocks retrieve blocks from the DB, given a range of block numbers defined by from and to
110func (hdb *HistoryDB) GetBlocks(from, to int64) ([]common.Block, error) {
111	var blocks []*common.Block
112	err := meddler.QueryAll(
113		hdb.db, &blocks,
114		"SELECT * FROM block WHERE $1 <= eth_block_num AND eth_block_num < $2;",
115		from, to,
116	)
117	return db.SlicePtrsToSlice(blocks).([]common.Block), err
118}
119
120// GetLastBlock retrieve the block with the highest block number from the DB
121func (hdb *HistoryDB) GetLastBlock() (*common.Block, error) {
122	block := &common.Block{}
123	err := meddler.QueryRow(
124		hdb.db, block, "SELECT * FROM block ORDER BY eth_block_num DESC LIMIT 1;",
125	)
126	return block, err
127}
128
129// AddBatch insert a Batch into the DB
130func (hdb *HistoryDB) AddBatch(batch *common.Batch) error { return hdb.addBatch(hdb.db, batch) }
131func (hdb *HistoryDB) addBatch(d meddler.DB, batch *common.Batch) error {
132	return meddler.Insert(d, "batch", batch)
133}
134
135// AddBatches insert Bids into the DB
136func (hdb *HistoryDB) AddBatches(batches []common.Batch) error {
137	return hdb.addBatches(hdb.db, batches)
138}
139func (hdb *HistoryDB) addBatches(d meddler.DB, batches []common.Batch) error {
140	// TODO: Calculate and insert total_fees_usd
141	return db.BulkInsert(
142		d,
143		`INSERT INTO batch (
144			batch_num,
145			eth_block_num,
146			forger_addr,
147			fees_collected,
148			state_root,
149			num_accounts,
150			exit_root,
151			forge_l1_txs_num,
152			slot_num
153		) VALUES %s;`,
154		batches[:],
155	)
156}
157
158// GetBatches retrieve batches from the DB, given a range of batch numbers defined by from and to
159func (hdb *HistoryDB) GetBatches(from, to common.BatchNum) ([]common.Batch, error) {
160	var batches []*common.Batch
161	err := meddler.QueryAll(
162		hdb.db, &batches,
163		"SELECT * FROM batch WHERE $1 <= batch_num AND batch_num < $2;",
164		from, to,
165	)
166	return db.SlicePtrsToSlice(batches).([]common.Batch), err
167}
168
169// GetLastBatchNum returns the BatchNum of the latest forged batch
170func (hdb *HistoryDB) GetLastBatchNum() (common.BatchNum, error) {
171	row := hdb.db.QueryRow("SELECT batch_num FROM batch ORDER BY batch_num DESC LIMIT 1;")
172	var batchNum common.BatchNum
173	return batchNum, row.Scan(&batchNum)
174}
175
176// GetLastL1TxsNum returns the greatest ForgeL1TxsNum in the DB.  If there's no
177// batch in the DB (nil, nil) is returned.
178func (hdb *HistoryDB) GetLastL1TxsNum() (*int64, error) {
179	row := hdb.db.QueryRow("SELECT MAX(forge_l1_txs_num) FROM batch;")
180	lastL1TxsNum := new(int64)
181	return lastL1TxsNum, row.Scan(&lastL1TxsNum)
182}
183
184// Reorg deletes all the information that was added into the DB after the
185// lastValidBlock.  If lastValidBlock is negative, all block information is
186// deleted.
187func (hdb *HistoryDB) Reorg(lastValidBlock int64) error {
188	var err error
189	if lastValidBlock < 0 {
190		_, err = hdb.db.Exec("DELETE FROM block;")
191	} else {
192		_, err = hdb.db.Exec("DELETE FROM block WHERE eth_block_num > $1;", lastValidBlock)
193	}
194	return err
195}
196
197// SyncPoD stores all the data that can be changed / added on a block in the PoD SC
198func (hdb *HistoryDB) SyncPoD(
199	blockNum uint64,
200	bids []common.Bid,
201	coordinators []common.Coordinator,
202	vars *common.AuctionVars,
203) error {
204	return nil
205}
206
207// AddBids insert Bids into the DB
208func (hdb *HistoryDB) AddBids(bids []common.Bid) error { return hdb.addBids(hdb.db, bids) }
209func (hdb *HistoryDB) addBids(d meddler.DB, bids []common.Bid) error {
210	// TODO: check the coordinator info
211	return db.BulkInsert(
212		d,
213		"INSERT INTO bid (slot_num, bid_value, eth_block_num, bidder_addr) VALUES %s;",
214		bids[:],
215	)
216}
217
218// GetBids return the bids
219func (hdb *HistoryDB) GetBids() ([]common.Bid, error) {
220	var bids []*common.Bid
221	err := meddler.QueryAll(
222		hdb.db, &bids,
223		"SELECT * FROM bid;",
224	)
225	return db.SlicePtrsToSlice(bids).([]common.Bid), err
226}
227
228// AddCoordinators insert Coordinators into the DB
229func (hdb *HistoryDB) AddCoordinators(coordinators []common.Coordinator) error {
230	return hdb.addCoordinators(hdb.db, coordinators)
231}
232func (hdb *HistoryDB) addCoordinators(d meddler.DB, coordinators []common.Coordinator) error {
233	return db.BulkInsert(
234		d,
235		"INSERT INTO coordinator (bidder_addr, forger_addr, eth_block_num, url) VALUES %s;",
236		coordinators[:],
237	)
238}
239
240// AddExitTree insert Exit tree into the DB
241func (hdb *HistoryDB) AddExitTree(exitTree []common.ExitInfo) error {
242	return hdb.addExitTree(hdb.db, exitTree)
243}
244func (hdb *HistoryDB) addExitTree(d meddler.DB, exitTree []common.ExitInfo) error {
245	return db.BulkInsert(
246		d,
247		"INSERT INTO exit_tree (batch_num, account_idx, merkle_proof, balance, "+
248			"instant_withdrawn, delayed_withdraw_request, delayed_withdrawn) VALUES %s;",
249		exitTree[:],
250	)
251}
252
253// AddToken insert a token into the DB
254func (hdb *HistoryDB) AddToken(token *common.Token) error {
255	return meddler.Insert(hdb.db, "token", token)
256}
257
258// AddTokens insert tokens into the DB
259func (hdb *HistoryDB) AddTokens(tokens []common.Token) error { return hdb.addTokens(hdb.db, tokens) }
260func (hdb *HistoryDB) addTokens(d meddler.DB, tokens []common.Token) error {
261	return db.BulkInsert(
262		d,
263		`INSERT INTO token (
264			token_id,
265			eth_block_num,
266			eth_addr,
267			name,
268			symbol,
269			decimals
270		) VALUES %s;`,
271		tokens[:],
272	)
273}
274
275// UpdateTokenValue updates the USD value of a token
276func (hdb *HistoryDB) UpdateTokenValue(tokenSymbol string, value float64) error {
277	_, err := hdb.db.Exec(
278		"UPDATE token SET usd = $1 WHERE symbol = $2;",
279		value, tokenSymbol,
280	)
281	return err
282}
283
284// GetTokens returns a list of tokens from the DB
285func (hdb *HistoryDB) GetTokens() ([]TokenRead, error) {
286	var tokens []*TokenRead
287	err := meddler.QueryAll(
288		hdb.db, &tokens,
289		"SELECT * FROM token ORDER BY token_id;",
290	)
291	return db.SlicePtrsToSlice(tokens).([]TokenRead), err
292}
293
294// GetTokenSymbols returns all the token symbols from the DB
295func (hdb *HistoryDB) GetTokenSymbols() ([]string, error) {
296	var tokenSymbols []string
297	rows, err := hdb.db.Query("SELECT symbol FROM token;")
298	if err != nil {
299		return nil, err
300	}
301	sym := new(string)
302	for rows.Next() {
303		err = rows.Scan(sym)
304		if err != nil {
305			return nil, err
306		}
307		tokenSymbols = append(tokenSymbols, *sym)
308	}
309	return tokenSymbols, nil
310}
311
312// AddAccounts insert accounts into the DB
313func (hdb *HistoryDB) AddAccounts(accounts []common.Account) error {
314	return hdb.addAccounts(hdb.db, accounts)
315}
316func (hdb *HistoryDB) addAccounts(d meddler.DB, accounts []common.Account) error {
317	return db.BulkInsert(
318		d,
319		`INSERT INTO account (
320			idx,
321			token_id,
322			batch_num,
323			bjj,
324			eth_addr
325		) VALUES %s;`,
326		accounts[:],
327	)
328}
329
330// GetAccounts returns a list of accounts from the DB
331func (hdb *HistoryDB) GetAccounts() ([]common.Account, error) {
332	var accs []*common.Account
333	err := meddler.QueryAll(
334		hdb.db, &accs,
335		"SELECT * FROM account ORDER BY idx;",
336	)
337	return db.SlicePtrsToSlice(accs).([]common.Account), err
338}
339
340// AddL1Txs inserts L1 txs to the DB. USD and LoadAmountUSD will be set automatically before storing the tx.
341// If the tx is originated by a coordinator, BatchNum must be provided. If it's originated by a user,
342// BatchNum should be null, and the value will be setted by a trigger when a batch forges the tx.
343func (hdb *HistoryDB) AddL1Txs(l1txs []common.L1Tx) error { return hdb.addL1Txs(hdb.db, l1txs) }
344
345// addL1Txs inserts L1 txs to the DB. USD and LoadAmountUSD will be set automatically before storing the tx.
346// If the tx is originated by a coordinator, BatchNum must be provided. If it's originated by a user,
347// BatchNum should be null, and the value will be setted by a trigger when a batch forges the tx.
348func (hdb *HistoryDB) addL1Txs(d meddler.DB, l1txs []common.L1Tx) error {
349	txs := []txWrite{}
350	for i := 0; i < len(l1txs); i++ {
351		af := new(big.Float).SetInt(l1txs[i].Amount)
352		amountFloat, _ := af.Float64()
353		laf := new(big.Float).SetInt(l1txs[i].LoadAmount)
354		loadAmountFloat, _ := laf.Float64()
355		txs = append(txs, txWrite{
356			// Generic
357			IsL1:        true,
358			TxID:        l1txs[i].TxID,
359			Type:        l1txs[i].Type,
360			Position:    l1txs[i].Position,
361			FromIdx:     &l1txs[i].FromIdx,
362			ToIdx:       l1txs[i].ToIdx,
363			Amount:      l1txs[i].Amount,
364			AmountFloat: amountFloat,
365			TokenID:     l1txs[i].TokenID,
366			BatchNum:    l1txs[i].BatchNum,
367			EthBlockNum: l1txs[i].EthBlockNum,
368			// L1
369			ToForgeL1TxsNum: l1txs[i].ToForgeL1TxsNum,
370			UserOrigin:      &l1txs[i].UserOrigin,
371			FromEthAddr:     &l1txs[i].FromEthAddr,
372			FromBJJ:         l1txs[i].FromBJJ,
373			LoadAmount:      l1txs[i].LoadAmount,
374			LoadAmountFloat: &loadAmountFloat,
375		})
376	}
377	return hdb.addTxs(d, txs)
378}
379
380// AddL2Txs inserts L2 txs to the DB. TokenID, USD and FeeUSD will be set automatically before storing the tx.
381func (hdb *HistoryDB) AddL2Txs(l2txs []common.L2Tx) error { return hdb.addL2Txs(hdb.db, l2txs) }
382
383// addL2Txs inserts L2 txs to the DB. TokenID, USD and FeeUSD will be set automatically before storing the tx.
384func (hdb *HistoryDB) addL2Txs(d meddler.DB, l2txs []common.L2Tx) error {
385	txs := []txWrite{}
386	for i := 0; i < len(l2txs); i++ {
387		f := new(big.Float).SetInt(l2txs[i].Amount)
388		amountFloat, _ := f.Float64()
389		txs = append(txs, txWrite{
390			// Generic
391			IsL1:        false,
392			TxID:        l2txs[i].TxID,
393			Type:        l2txs[i].Type,
394			Position:    l2txs[i].Position,
395			FromIdx:     &l2txs[i].FromIdx,
396			ToIdx:       l2txs[i].ToIdx,
397			Amount:      l2txs[i].Amount,
398			AmountFloat: amountFloat,
399			BatchNum:    &l2txs[i].BatchNum,
400			EthBlockNum: l2txs[i].EthBlockNum,
401			// L2
402			Fee:   &l2txs[i].Fee,
403			Nonce: &l2txs[i].Nonce,
404		})
405	}
406	return hdb.addTxs(d, txs)
407}
408
409func (hdb *HistoryDB) addTxs(d meddler.DB, txs []txWrite) error {
410	return db.BulkInsert(
411		d,
412		`INSERT INTO tx (
413			is_l1,
414			id,
415			type,
416			position,
417			from_idx,
418			to_idx,
419			amount,
420			amount_f,
421			token_id,
422			batch_num,
423			eth_block_num,
424			to_forge_l1_txs_num,
425			user_origin,
426			from_eth_addr,
427			from_bjj,
428			load_amount,
429			load_amount_f,
430			fee,
431			nonce
432		) VALUES %s;`,
433		txs[:],
434	)
435}
436
437// // GetTxs returns a list of txs from the DB
438// func (hdb *HistoryDB) GetTxs() ([]common.Tx, error) {
439// 	var txs []*common.Tx
440// 	err := meddler.QueryAll(
441// 		hdb.db, &txs,
442// 		`SELECT * FROM tx
443// 		ORDER BY (batch_num, position) ASC`,
444// 	)
445// 	return db.SlicePtrsToSlice(txs).([]common.Tx), err
446// }
447
448// GetHistoryTxs returns a list of txs from the DB using the HistoryTx struct
449func (hdb *HistoryDB) GetHistoryTxs(
450	ethAddr *ethCommon.Address, bjj *babyjub.PublicKey,
451	tokenID, idx, batchNum *uint, txType *common.TxType,
452	offset, limit *uint, last bool,
453) ([]HistoryTx, int, error) {
454	if ethAddr != nil && bjj != nil {
455		return nil, 0, errors.New("ethAddr and bjj are incompatible")
456	}
457	var query string
458	var args []interface{}
459	queryStr := `SELECT tx.is_l1, tx.id, tx.type, tx.position, tx.from_idx, tx.to_idx, 
460	tx.amount, tx.token_id, tx.batch_num, tx.eth_block_num, tx.to_forge_l1_txs_num, 
461	tx.user_origin, tx.from_eth_addr, tx.from_bjj, tx.load_amount, tx.fee, tx.nonce,
462	token.token_id, token.eth_block_num AS token_block,
463	token.eth_addr, token.name, token.symbol, token.decimals, token.usd,
464	token.usd_update, block.timestamp, count(*) OVER() AS total_items 
465	FROM tx 
466	INNER JOIN token ON tx.token_id = token.token_id 
467	INNER JOIN block ON tx.eth_block_num = block.eth_block_num `
468	// Apply filters
469	nextIsAnd := false
470	// ethAddr filter
471	if ethAddr != nil {
472		queryStr = `WITH acc AS 
473		(select idx from account where eth_addr = ?) ` + queryStr
474		queryStr += ", acc WHERE (tx.from_idx IN(acc.idx) OR tx.to_idx IN(acc.idx)) "
475		nextIsAnd = true
476		args = append(args, ethAddr)
477	} else if bjj != nil { // bjj filter
478		queryStr = `WITH acc AS 
479		(select idx from account where bjj = ?) ` + queryStr
480		queryStr += ", acc WHERE (tx.from_idx IN(acc.idx) OR tx.to_idx IN(acc.idx)) "
481		nextIsAnd = true
482		args = append(args, bjj)
483	}
484	// tokenID filter
485	if tokenID != nil {
486		if nextIsAnd {
487			queryStr += "AND "
488		} else {
489			queryStr += "WHERE "
490		}
491		queryStr += "tx.token_id = ? "
492		args = append(args, tokenID)
493		nextIsAnd = true
494	}
495	// idx filter
496	if idx != nil {
497		if nextIsAnd {
498			queryStr += "AND "
499		} else {
500			queryStr += "WHERE "
501		}
502		queryStr += "(tx.from_idx = ? OR tx.to_idx = ?) "
503		args = append(args, idx, idx)
504		nextIsAnd = true
505	}
506	// batchNum filter
507	if batchNum != nil {
508		if nextIsAnd {
509			queryStr += "AND "
510		} else {
511			queryStr += "WHERE "
512		}
513		queryStr += "tx.batch_num = ? "
514		args = append(args, batchNum)
515		nextIsAnd = true
516	}
517	// txType filter
518	if txType != nil {
519		if nextIsAnd {
520			queryStr += "AND "
521		} else {
522			queryStr += "WHERE "
523		}
524		queryStr += "tx.type = ? "
525		args = append(args, txType)
526		// nextIsAnd = true
527	}
528	// pagination
529	if last {
530		queryStr += "ORDER BY (batch_num, position) DESC NULLS FIRST "
531	} else {
532		queryStr += "ORDER BY (batch_num, position) ASC NULLS LAST "
533		queryStr += fmt.Sprintf("OFFSET %d ", *offset)
534	}
535	queryStr += fmt.Sprintf("LIMIT %d;", *limit)
536	query = hdb.db.Rebind(queryStr)
537	// log.Debug(query)
538	txsPtrs := []*HistoryTx{}
539	if err := meddler.QueryAll(hdb.db, &txsPtrs, query, args...); err != nil {
540		return nil, 0, err
541	}
542	txs := db.SlicePtrsToSlice(txsPtrs).([]HistoryTx)
543	if len(txs) == 0 {
544		return nil, 0, sql.ErrNoRows
545	} else if last {
546		tmp := []HistoryTx{}
547		for i := len(txs) - 1; i >= 0; i-- {
548			tmp = append(tmp, txs[i])
549		}
550		txs = tmp
551	}
552	return txs, txs[0].TotalItems, nil
553}
554
555// // GetTx returns a tx from the DB
556// func (hdb *HistoryDB) GetTx(txID common.TxID) (*common.Tx, error) {
557// 	tx := new(common.Tx)
558// 	return tx, meddler.QueryRow(
559// 		hdb.db, tx,
560// 		"SELECT * FROM tx WHERE id = $1;",
561// 		txID,
562// 	)
563// }
564
565// // GetL1UserTxs gets L1 User Txs to be forged in a batch that will create an account
566// // TODO: This is currently not used.  Figure out if it should be used somewhere or removed.
567// func (hdb *HistoryDB) GetL1UserTxs(toForgeL1TxsNum int64) ([]*common.Tx, error) {
568// 	var txs []*common.Tx
569// 	err := meddler.QueryAll(
570// 		hdb.db, &txs,
571// 		"SELECT * FROM tx WHERE to_forge_l1_txs_num = $1 AND is_l1 = TRUE AND user_origin = TRUE;",
572// 		toForgeL1TxsNum,
573// 	)
574// 	return txs, err
575// }
576
577// TODO: Think about chaning all the queries that return a last value, to queries that return the next valid value.
578
579// GetLastTxsPosition for a given to_forge_l1_txs_num
580func (hdb *HistoryDB) GetLastTxsPosition(toForgeL1TxsNum int64) (int, error) {
581	row := hdb.db.QueryRow("SELECT MAX(position) FROM tx WHERE to_forge_l1_txs_num = $1;", toForgeL1TxsNum)
582	var lastL1TxsPosition int
583	return lastL1TxsPosition, row.Scan(&lastL1TxsPosition)
584}
585
586// AddBlockSCData stores all the information of a block retrieved by the Synchronizer
587func (hdb *HistoryDB) AddBlockSCData(blockData *BlockData) (err error) {
588	txn, err := hdb.db.Begin()
589	if err != nil {
590		return err
591	}
592	defer func() {
593		if err != nil {
594			errRollback := txn.Rollback()
595			if errRollback != nil {
596				log.Errorw("Rollback", "err", errRollback)
597			}
598		}
599	}()
600
601	// Add block
602	err = hdb.addBlock(txn, blockData.Block)
603	if err != nil {
604		return err
605	}
606
607	// Add Coordinators
608	if len(blockData.Coordinators) > 0 {
609		err = hdb.addCoordinators(txn, blockData.Coordinators)
610		if err != nil {
611			return err
612		}
613	}
614
615	// Add Bids
616	if len(blockData.Bids) > 0 {
617		err = hdb.addBids(txn, blockData.Bids)
618		if err != nil {
619			return err
620		}
621	}
622
623	// Add Tokens
624	if len(blockData.RegisteredTokens) > 0 {
625		err = hdb.addTokens(txn, blockData.RegisteredTokens)
626		if err != nil {
627			return err
628		}
629	}
630
631	// Add l1 Txs
632	if len(blockData.L1UserTxs) > 0 {
633		err = hdb.addL1Txs(txn, blockData.L1UserTxs)
634		if err != nil {
635			return err
636		}
637	}
638
639	// Add Batches
640	for _, batch := range blockData.Batches {
641		// Add Batch: this will trigger an update on the DB
642		// that will set the batch num of forged L1 txs in this batch
643		err = hdb.addBatch(txn, batch.Batch)
644		if err != nil {
645			return err
646		}
647
648		// Add unforged l1 Txs
649		if batch.L1Batch {
650			if len(batch.L1CoordinatorTxs) > 0 {
651				err = hdb.addL1Txs(txn, batch.L1CoordinatorTxs)
652				if err != nil {
653					return err
654				}
655			}
656		}
657
658		// Add l2 Txs
659		if len(batch.L2Txs) > 0 {
660			err = hdb.addL2Txs(txn, batch.L2Txs)
661			if err != nil {
662				return err
663			}
664		}
665
666		// Add accounts
667		if len(batch.CreatedAccounts) > 0 {
668			err = hdb.addAccounts(txn, batch.CreatedAccounts)
669			if err != nil {
670				return err
671			}
672		}
673
674		// Add exit tree
675		if len(batch.ExitTree) > 0 {
676			err = hdb.addExitTree(txn, batch.ExitTree)
677			if err != nil {
678				return err
679			}
680		}
681
682		// TODO: INSERT CONTRACTS VARS
683	}
684
685	return txn.Commit()
686}