arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/db/historydb/historydb.go
db/historydb/historydb.goRaw
   1/*
   2Package historydb is responsible for storing and retrieving the historic data of the Hermez network.
   3It's mostly but not exclusively used by the API and the synchronizer.
   4
   5Apart from the logic defined in this package, it's important to notice that there are some triggers defined in the
   6migration files that have to be taken into consideration to understanding the results of some queries. This is especially true
   7for reorgs: all the data is directly or indirectly related to a block, this makes handling reorgs as easy as deleting the
   8reorged blocks from the block table, and all related items will be dropped in cascade. This is not the only case, in general
   9functions defined in this package that get affected somehow by the SQL level defined logic has a special mention on the function description.
  10
  11Some of the database tooling used in this package such as meddler and migration tools is explained in the db package.
  12
  13This package is spitted in different files following these ideas:
  14- historydb.go: constructor and functions used by packages other than the api.
  15- apiqueries.go: functions used by the API, the queries implemented in this functions use a semaphore
  16to restrict the maximum concurrent connections to the database.
  17- views.go: structs used to retrieve/store data from/to the database. When possible, the common structs are used, however
  18most of the time there is no 1:1 relation between the struct fields and the tables of the schema, especially when joining tables.
  19In some cases, some of the structs defined in this file also include custom Marshallers to easily match the expected api formats.
  20- nodeinfo.go: used to handle the interfaces and structs that allow communication across running in different machines/process but sharing the same database.
  21*/
  22package historydb
  23
  24import (
  25	"math"
  26	"math/big"
  27	"strings"
  28
  29	ethCommon "github.com/ethereum/go-ethereum/common"
  30	"github.com/hermeznetwork/hermez-node/common"
  31	"github.com/hermeznetwork/hermez-node/db"
  32	"github.com/hermeznetwork/tracerr"
  33	"github.com/jmoiron/sqlx"
  34
  35	//nolint:errcheck // driver for postgres DB
  36	_ "github.com/lib/pq"
  37	"github.com/russross/meddler"
  38)
  39
  40const (
  41	// OrderAsc indicates ascending order when using pagination
  42	OrderAsc = "ASC"
  43	// OrderDesc indicates descending order when using pagination
  44	OrderDesc = "DESC"
  45)
  46
  47// TODO(Edu): Document here how HistoryDB is kept consistent
  48
  49// HistoryDB persist the historic of the rollup
  50type HistoryDB struct {
  51	dbRead     *sqlx.DB
  52	dbWrite    *sqlx.DB
  53	apiConnCon *db.APIConnectionController
  54}
  55
  56// NewHistoryDB initialize the DB
  57func NewHistoryDB(dbRead, dbWrite *sqlx.DB, apiConnCon *db.APIConnectionController) *HistoryDB {
  58	return &HistoryDB{
  59		dbRead:     dbRead,
  60		dbWrite:    dbWrite,
  61		apiConnCon: apiConnCon,
  62	}
  63}
  64
  65// DB returns a pointer to the L2DB.db. This method should be used only for
  66// internal testing purposes.
  67func (hdb *HistoryDB) DB() *sqlx.DB {
  68	return hdb.dbWrite
  69}
  70
  71// AddBlock insert a block into the DB
  72func (hdb *HistoryDB) AddBlock(block *common.Block) error { return hdb.addBlock(hdb.dbWrite, block) }
  73func (hdb *HistoryDB) addBlock(d meddler.DB, block *common.Block) error {
  74	return tracerr.Wrap(meddler.Insert(d, "block", block))
  75}
  76
  77// AddBlocks inserts blocks into the DB
  78func (hdb *HistoryDB) AddBlocks(blocks []common.Block) error {
  79	return tracerr.Wrap(hdb.addBlocks(hdb.dbWrite, blocks))
  80}
  81
  82func (hdb *HistoryDB) addBlocks(d meddler.DB, blocks []common.Block) error {
  83	return tracerr.Wrap(db.BulkInsert(
  84		d,
  85		`INSERT INTO block (
  86			eth_block_num,
  87			timestamp,
  88			hash
  89		) VALUES %s;`,
  90		blocks,
  91	))
  92}
  93
  94// GetBlock retrieve a block from the DB, given a block number
  95func (hdb *HistoryDB) GetBlock(blockNum int64) (*common.Block, error) {
  96	block := &common.Block{}
  97	err := meddler.QueryRow(
  98		hdb.dbRead, block,
  99		"SELECT * FROM block WHERE eth_block_num = $1;", blockNum,
 100	)
 101	return block, tracerr.Wrap(err)
 102}
 103
 104// GetAllBlocks retrieve all blocks from the DB
 105func (hdb *HistoryDB) GetAllBlocks() ([]common.Block, error) {
 106	var blocks []*common.Block
 107	err := meddler.QueryAll(
 108		hdb.dbRead, &blocks,
 109		"SELECT * FROM block ORDER BY eth_block_num;",
 110	)
 111	return db.SlicePtrsToSlice(blocks).([]common.Block), tracerr.Wrap(err)
 112}
 113
 114// getBlocks retrieve blocks from the DB, given a range of block numbers defined by from and to
 115func (hdb *HistoryDB) getBlocks(from, to int64) ([]common.Block, error) {
 116	var blocks []*common.Block
 117	err := meddler.QueryAll(
 118		hdb.dbRead, &blocks,
 119		"SELECT * FROM block WHERE $1 <= eth_block_num AND eth_block_num < $2 ORDER BY eth_block_num;",
 120		from, to,
 121	)
 122	return db.SlicePtrsToSlice(blocks).([]common.Block), tracerr.Wrap(err)
 123}
 124
 125// GetLastBlock retrieve the block with the highest block number from the DB
 126func (hdb *HistoryDB) GetLastBlock() (*common.Block, error) {
 127	block := &common.Block{}
 128	err := meddler.QueryRow(
 129		hdb.dbRead, block, "SELECT * FROM block ORDER BY eth_block_num DESC LIMIT 1;",
 130	)
 131	return block, tracerr.Wrap(err)
 132}
 133
 134// AddBatch insert a Batch into the DB
 135func (hdb *HistoryDB) AddBatch(batch *common.Batch) error { return hdb.addBatch(hdb.dbWrite, batch) }
 136func (hdb *HistoryDB) addBatch(d meddler.DB, batch *common.Batch) error {
 137	// Calculate total collected fees in USD
 138	// Get IDs of collected tokens for fees
 139	tokenIDs := []common.TokenID{}
 140	for id := range batch.CollectedFees {
 141		tokenIDs = append(tokenIDs, id)
 142	}
 143	// Get USD value of the tokens
 144	type tokenPrice struct {
 145		ID       common.TokenID `meddler:"token_id"`
 146		USD      *float64       `meddler:"usd"`
 147		Decimals int            `meddler:"decimals"`
 148	}
 149	var tokenPrices []*tokenPrice
 150	if len(tokenIDs) > 0 {
 151		query, args, err := sqlx.In(
 152			"SELECT token_id, usd, decimals FROM token WHERE token_id IN (?);",
 153			tokenIDs,
 154		)
 155		if err != nil {
 156			return tracerr.Wrap(err)
 157		}
 158		query = hdb.dbWrite.Rebind(query)
 159		if err := meddler.QueryAll(
 160			hdb.dbWrite, &tokenPrices, query, args...,
 161		); err != nil {
 162			return tracerr.Wrap(err)
 163		}
 164	}
 165	// Calculate total collected
 166	var total float64
 167	for _, tokenPrice := range tokenPrices {
 168		if tokenPrice.USD == nil {
 169			continue
 170		}
 171		f := new(big.Float).SetInt(batch.CollectedFees[tokenPrice.ID])
 172		amount, _ := f.Float64()
 173		total += *tokenPrice.USD * (amount / math.Pow(10, float64(tokenPrice.Decimals))) //nolint decimals have to be ^10
 174	}
 175	batch.TotalFeesUSD = &total
 176	// Insert to DB
 177	return tracerr.Wrap(meddler.Insert(d, "batch", batch))
 178}
 179
 180// AddBatches insert Bids into the DB
 181func (hdb *HistoryDB) AddBatches(batches []common.Batch) error {
 182	return tracerr.Wrap(hdb.addBatches(hdb.dbWrite, batches))
 183}
 184func (hdb *HistoryDB) addBatches(d meddler.DB, batches []common.Batch) error {
 185	for i := 0; i < len(batches); i++ {
 186		if err := hdb.addBatch(d, &batches[i]); err != nil {
 187			return tracerr.Wrap(err)
 188		}
 189	}
 190	return nil
 191}
 192
 193// GetBatch returns the batch with the given batchNum
 194func (hdb *HistoryDB) GetBatch(batchNum common.BatchNum) (*common.Batch, error) {
 195	var batch common.Batch
 196	err := meddler.QueryRow(
 197		hdb.dbRead, &batch, `SELECT batch.batch_num, batch.eth_block_num, batch.forger_addr,
 198		batch.fees_collected, batch.fee_idxs_coordinator, batch.state_root,
 199		batch.num_accounts, batch.last_idx, batch.exit_root, batch.forge_l1_txs_num,
 200		batch.slot_num, batch.total_fees_usd FROM batch WHERE batch_num = $1;`,
 201		batchNum,
 202	)
 203	return &batch, tracerr.Wrap(err)
 204}
 205
 206// GetAllBatches retrieve all batches from the DB
 207func (hdb *HistoryDB) GetAllBatches() ([]common.Batch, error) {
 208	var batches []*common.Batch
 209	err := meddler.QueryAll(
 210		hdb.dbRead, &batches,
 211		`SELECT batch.batch_num, batch.eth_block_num, batch.forger_addr, batch.fees_collected,
 212		 batch.fee_idxs_coordinator, batch.state_root, batch.num_accounts, batch.last_idx, batch.exit_root,
 213		 batch.forge_l1_txs_num, batch.slot_num, batch.total_fees_usd FROM batch
 214		 ORDER BY item_id;`,
 215	)
 216	return db.SlicePtrsToSlice(batches).([]common.Batch), tracerr.Wrap(err)
 217}
 218
 219// GetBatches retrieve batches from the DB, given a range of batch numbers defined by from and to
 220func (hdb *HistoryDB) GetBatches(from, to common.BatchNum) ([]common.Batch, error) {
 221	var batches []*common.Batch
 222	err := meddler.QueryAll(
 223		hdb.dbRead, &batches,
 224		`SELECT batch_num, eth_block_num, forger_addr, fees_collected, fee_idxs_coordinator, 
 225		state_root, num_accounts, last_idx, exit_root, forge_l1_txs_num, slot_num, total_fees_usd 
 226		FROM batch WHERE $1 <= batch_num AND batch_num < $2 ORDER BY batch_num;`,
 227		from, to,
 228	)
 229	return db.SlicePtrsToSlice(batches).([]common.Batch), tracerr.Wrap(err)
 230}
 231
 232// GetFirstBatchBlockNumBySlot returns the ethereum block number of the first
 233// batch within a slot
 234func (hdb *HistoryDB) GetFirstBatchBlockNumBySlot(slotNum int64) (int64, error) {
 235	row := hdb.dbRead.QueryRow(
 236		`SELECT eth_block_num FROM batch
 237		WHERE slot_num = $1 ORDER BY batch_num ASC LIMIT 1;`, slotNum,
 238	)
 239	var blockNum int64
 240	return blockNum, tracerr.Wrap(row.Scan(&blockNum))
 241}
 242
 243// GetLastBatchNum returns the BatchNum of the latest forged batch
 244func (hdb *HistoryDB) GetLastBatchNum() (common.BatchNum, error) {
 245	row := hdb.dbRead.QueryRow("SELECT batch_num FROM batch ORDER BY batch_num DESC LIMIT 1;")
 246	var batchNum common.BatchNum
 247	return batchNum, tracerr.Wrap(row.Scan(&batchNum))
 248}
 249
 250// GetLastBatch returns the last forged batch
 251func (hdb *HistoryDB) GetLastBatch() (*common.Batch, error) {
 252	var batch common.Batch
 253	err := meddler.QueryRow(
 254		hdb.dbRead, &batch, `SELECT batch.batch_num, batch.eth_block_num, batch.forger_addr,
 255		batch.fees_collected, batch.fee_idxs_coordinator, batch.state_root,
 256		batch.num_accounts, batch.last_idx, batch.exit_root, batch.forge_l1_txs_num,
 257		batch.slot_num, batch.total_fees_usd FROM batch ORDER BY batch_num DESC LIMIT 1;`,
 258	)
 259	return &batch, tracerr.Wrap(err)
 260}
 261
 262// GetLastL1BatchBlockNum returns the blockNum of the latest forged l1Batch
 263func (hdb *HistoryDB) GetLastL1BatchBlockNum() (int64, error) {
 264	row := hdb.dbRead.QueryRow(`SELECT eth_block_num FROM batch
 265		WHERE forge_l1_txs_num IS NOT NULL
 266		ORDER BY batch_num DESC LIMIT 1;`)
 267	var blockNum int64
 268	return blockNum, tracerr.Wrap(row.Scan(&blockNum))
 269}
 270
 271// GetLastL1TxsNum returns the greatest ForgeL1TxsNum in the DB from forged
 272// batches.  If there's no batch in the DB (nil, nil) is returned.
 273func (hdb *HistoryDB) GetLastL1TxsNum() (*int64, error) {
 274	row := hdb.dbRead.QueryRow("SELECT MAX(forge_l1_txs_num) FROM batch;")
 275	lastL1TxsNum := new(int64)
 276	return lastL1TxsNum, tracerr.Wrap(row.Scan(&lastL1TxsNum))
 277}
 278
 279// Reorg deletes all the information that was added into the DB after the
 280// lastValidBlock.  If lastValidBlock is negative, all block information is
 281// deleted.
 282func (hdb *HistoryDB) Reorg(lastValidBlock int64) error {
 283	var err error
 284	if lastValidBlock < 0 {
 285		_, err = hdb.dbWrite.Exec("DELETE FROM block;")
 286	} else {
 287		_, err = hdb.dbWrite.Exec("DELETE FROM block WHERE eth_block_num > $1;", lastValidBlock)
 288	}
 289	return tracerr.Wrap(err)
 290}
 291
 292// AddBids insert Bids into the DB
 293func (hdb *HistoryDB) AddBids(bids []common.Bid) error { return hdb.addBids(hdb.dbWrite, bids) }
 294func (hdb *HistoryDB) addBids(d meddler.DB, bids []common.Bid) error {
 295	if len(bids) == 0 {
 296		return nil
 297	}
 298	// TODO: check the coordinator info
 299	return tracerr.Wrap(db.BulkInsert(
 300		d,
 301		"INSERT INTO bid (slot_num, bid_value, eth_block_num, bidder_addr) VALUES %s;",
 302		bids,
 303	))
 304}
 305
 306// GetAllBids retrieve all bids from the DB
 307func (hdb *HistoryDB) GetAllBids() ([]common.Bid, error) {
 308	var bids []*common.Bid
 309	err := meddler.QueryAll(
 310		hdb.dbRead, &bids,
 311		`SELECT bid.slot_num, bid.bid_value, bid.eth_block_num, bid.bidder_addr FROM bid
 312		ORDER BY item_id;`,
 313	)
 314	return db.SlicePtrsToSlice(bids).([]common.Bid), tracerr.Wrap(err)
 315}
 316
 317// GetBestBidCoordinator returns the forger address of the highest bidder in a slot by slotNum
 318func (hdb *HistoryDB) GetBestBidCoordinator(slotNum int64) (*common.BidCoordinator, error) {
 319	bidCoord := &common.BidCoordinator{}
 320	err := meddler.QueryRow(
 321		hdb.dbRead, bidCoord,
 322		`SELECT (
 323			SELECT default_slot_set_bid
 324			FROM auction_vars
 325			WHERE default_slot_set_bid_slot_num <= $1
 326			ORDER BY eth_block_num DESC LIMIT 1
 327		),
 328		bid.slot_num, bid.bid_value, bid.bidder_addr,
 329		coordinator.forger_addr, coordinator.url
 330		FROM bid
 331		INNER JOIN (
 332			SELECT bidder_addr, MAX(item_id) AS item_id FROM coordinator
 333			GROUP BY bidder_addr
 334		) c ON bid.bidder_addr = c.bidder_addr 
 335		INNER JOIN coordinator ON c.item_id = coordinator.item_id
 336		WHERE bid.slot_num = $1 ORDER BY bid.item_id DESC LIMIT 1;`,
 337		slotNum)
 338
 339	return bidCoord, tracerr.Wrap(err)
 340}
 341
 342// AddCoordinators insert Coordinators into the DB
 343func (hdb *HistoryDB) AddCoordinators(coordinators []common.Coordinator) error {
 344	return tracerr.Wrap(hdb.addCoordinators(hdb.dbWrite, coordinators))
 345}
 346func (hdb *HistoryDB) addCoordinators(d meddler.DB, coordinators []common.Coordinator) error {
 347	if len(coordinators) == 0 {
 348		return nil
 349	}
 350	return tracerr.Wrap(db.BulkInsert(
 351		d,
 352		"INSERT INTO coordinator (bidder_addr, forger_addr, eth_block_num, url) VALUES %s;",
 353		coordinators,
 354	))
 355}
 356
 357// AddExitTree insert Exit tree into the DB
 358func (hdb *HistoryDB) AddExitTree(exitTree []common.ExitInfo) error {
 359	return tracerr.Wrap(hdb.addExitTree(hdb.dbWrite, exitTree))
 360}
 361func (hdb *HistoryDB) addExitTree(d meddler.DB, exitTree []common.ExitInfo) error {
 362	if len(exitTree) == 0 {
 363		return nil
 364	}
 365	return tracerr.Wrap(db.BulkInsert(
 366		d,
 367		"INSERT INTO exit_tree (batch_num, account_idx, merkle_proof, balance, "+
 368			"instant_withdrawn, delayed_withdraw_request, delayed_withdrawn) VALUES %s;",
 369		exitTree,
 370	))
 371}
 372
 373func (hdb *HistoryDB) updateExitTree(d sqlx.Ext, blockNum int64,
 374	rollupWithdrawals []common.WithdrawInfo, wDelayerWithdrawals []common.WDelayerTransfer) error {
 375	if len(rollupWithdrawals) == 0 && len(wDelayerWithdrawals) == 0 {
 376		return nil
 377	}
 378	type withdrawal struct {
 379		BatchNum               int64              `db:"batch_num"`
 380		AccountIdx             int64              `db:"account_idx"`
 381		InstantWithdrawn       *int64             `db:"instant_withdrawn"`
 382		DelayedWithdrawRequest *int64             `db:"delayed_withdraw_request"`
 383		DelayedWithdrawn       *int64             `db:"delayed_withdrawn"`
 384		Owner                  *ethCommon.Address `db:"owner"`
 385		Token                  *ethCommon.Address `db:"token"`
 386	}
 387	withdrawals := make([]withdrawal, len(rollupWithdrawals)+len(wDelayerWithdrawals))
 388	for i := range rollupWithdrawals {
 389		info := &rollupWithdrawals[i]
 390		withdrawals[i] = withdrawal{
 391			BatchNum:   int64(info.NumExitRoot),
 392			AccountIdx: int64(info.Idx),
 393		}
 394		if info.InstantWithdraw {
 395			withdrawals[i].InstantWithdrawn = &blockNum
 396		} else {
 397			withdrawals[i].DelayedWithdrawRequest = &blockNum
 398			withdrawals[i].Owner = &info.Owner
 399			withdrawals[i].Token = &info.Token
 400		}
 401	}
 402	for i := range wDelayerWithdrawals {
 403		info := &wDelayerWithdrawals[i]
 404		withdrawals[len(rollupWithdrawals)+i] = withdrawal{
 405			DelayedWithdrawn: &blockNum,
 406			Owner:            &info.Owner,
 407			Token:            &info.Token,
 408		}
 409	}
 410	// In VALUES we set an initial row of NULLs to set the types of each
 411	// variable passed as argument
 412	const query string = `
 413		UPDATE exit_tree e SET
 414			instant_withdrawn = d.instant_withdrawn,
 415			delayed_withdraw_request = CASE
 416				WHEN e.delayed_withdraw_request IS NOT NULL THEN e.delayed_withdraw_request
 417				ELSE d.delayed_withdraw_request
 418			END,
 419			delayed_withdrawn = d.delayed_withdrawn,
 420			owner = d.owner,
 421			token = d.token
 422		FROM (VALUES
 423			(NULL::::BIGINT, NULL::::BIGINT, NULL::::BIGINT, NULL::::BIGINT, NULL::::BIGINT, NULL::::BYTEA, NULL::::BYTEA),
 424			(:batch_num,
 425			 :account_idx,
 426			 :instant_withdrawn,
 427			 :delayed_withdraw_request,
 428			 :delayed_withdrawn,
 429			 :owner,
 430			 :token)
 431		) as d (batch_num, account_idx, instant_withdrawn, delayed_withdraw_request, delayed_withdrawn, owner, token)
 432		WHERE
 433			(d.batch_num IS NOT NULL AND e.batch_num = d.batch_num AND e.account_idx = d.account_idx) OR
 434			(d.delayed_withdrawn IS NOT NULL AND e.delayed_withdrawn IS NULL AND e.owner = d.owner AND e.token = d.token);
 435		`
 436	if len(withdrawals) > 0 {
 437		if _, err := sqlx.NamedExec(d, query, withdrawals); err != nil {
 438			return tracerr.Wrap(err)
 439		}
 440	}
 441
 442	return nil
 443}
 444
 445// AddToken insert a token into the DB
 446func (hdb *HistoryDB) AddToken(token *common.Token) error {
 447	return tracerr.Wrap(meddler.Insert(hdb.dbWrite, "token", token))
 448}
 449
 450// AddTokens insert tokens into the DB
 451func (hdb *HistoryDB) AddTokens(tokens []common.Token) error {
 452	return hdb.addTokens(hdb.dbWrite, tokens)
 453}
 454func (hdb *HistoryDB) addTokens(d meddler.DB, tokens []common.Token) error {
 455	if len(tokens) == 0 {
 456		return nil
 457	}
 458	// Sanitize name and symbol
 459	for i, token := range tokens {
 460		token.Name = strings.ToValidUTF8(token.Name, " ")
 461		token.Symbol = strings.ToValidUTF8(token.Symbol, " ")
 462		tokens[i] = token
 463	}
 464	return tracerr.Wrap(db.BulkInsert(
 465		d,
 466		`INSERT INTO token (
 467			token_id,
 468			eth_block_num,
 469			eth_addr,
 470			name,
 471			symbol,
 472			decimals
 473		) VALUES %s;`,
 474		tokens,
 475	))
 476}
 477
 478// UpdateTokenValue updates the USD value of a token.  Value is the price in
 479// USD of a normalized token (1 token = 10^decimals units)
 480func (hdb *HistoryDB) UpdateTokenValue(tokenAddr ethCommon.Address, value float64) error {
 481	_, err := hdb.dbWrite.Exec(
 482		"UPDATE token SET usd = $1 WHERE eth_addr = $2;",
 483		value, tokenAddr,
 484	)
 485	return tracerr.Wrap(err)
 486}
 487
 488// GetToken returns a token from the DB given a TokenID
 489func (hdb *HistoryDB) GetToken(tokenID common.TokenID) (*TokenWithUSD, error) {
 490	token := &TokenWithUSD{}
 491	err := meddler.QueryRow(
 492		hdb.dbRead, token, `SELECT * FROM token WHERE token_id = $1;`, tokenID,
 493	)
 494	return token, tracerr.Wrap(err)
 495}
 496
 497// GetAllTokens returns all tokens from the DB
 498func (hdb *HistoryDB) GetAllTokens() ([]TokenWithUSD, error) {
 499	var tokens []*TokenWithUSD
 500	err := meddler.QueryAll(
 501		hdb.dbRead, &tokens,
 502		"SELECT * FROM token ORDER BY token_id;",
 503	)
 504	return db.SlicePtrsToSlice(tokens).([]TokenWithUSD), tracerr.Wrap(err)
 505}
 506
 507// GetTokenSymbolsAndAddrs returns all the token symbols and addresses from the DB
 508func (hdb *HistoryDB) GetTokenSymbolsAndAddrs() ([]TokenSymbolAndAddr, error) {
 509	var tokens []*TokenSymbolAndAddr
 510	err := meddler.QueryAll(
 511		hdb.dbRead, &tokens,
 512		"SELECT symbol, eth_addr FROM token;",
 513	)
 514	return db.SlicePtrsToSlice(tokens).([]TokenSymbolAndAddr), tracerr.Wrap(err)
 515}
 516
 517// AddAccounts insert accounts into the DB
 518func (hdb *HistoryDB) AddAccounts(accounts []common.Account) error {
 519	return tracerr.Wrap(hdb.addAccounts(hdb.dbWrite, accounts))
 520}
 521func (hdb *HistoryDB) addAccounts(d meddler.DB, accounts []common.Account) error {
 522	if len(accounts) == 0 {
 523		return nil
 524	}
 525	return tracerr.Wrap(db.BulkInsert(
 526		d,
 527		`INSERT INTO account (
 528			idx,
 529			token_id,
 530			batch_num,
 531			bjj,
 532			eth_addr
 533		) VALUES %s;`,
 534		accounts,
 535	))
 536}
 537
 538// GetAllAccounts returns a list of accounts from the DB
 539func (hdb *HistoryDB) GetAllAccounts() ([]common.Account, error) {
 540	var accs []*common.Account
 541	err := meddler.QueryAll(
 542		hdb.dbRead, &accs,
 543		"SELECT idx, token_id, batch_num, bjj, eth_addr FROM account ORDER BY idx;",
 544	)
 545	return db.SlicePtrsToSlice(accs).([]common.Account), tracerr.Wrap(err)
 546}
 547
 548// AddAccountUpdates inserts accUpdates into the DB
 549func (hdb *HistoryDB) AddAccountUpdates(accUpdates []common.AccountUpdate) error {
 550	return tracerr.Wrap(hdb.addAccountUpdates(hdb.dbWrite, accUpdates))
 551}
 552func (hdb *HistoryDB) addAccountUpdates(d meddler.DB, accUpdates []common.AccountUpdate) error {
 553	if len(accUpdates) == 0 {
 554		return nil
 555	}
 556	return tracerr.Wrap(db.BulkInsert(
 557		d,
 558		`INSERT INTO account_update (
 559			eth_block_num,
 560			batch_num,
 561			idx,
 562			nonce,
 563			balance
 564		) VALUES %s;`,
 565		accUpdates,
 566	))
 567}
 568
 569// GetAllAccountUpdates returns all the AccountUpdate from the DB
 570func (hdb *HistoryDB) GetAllAccountUpdates() ([]common.AccountUpdate, error) {
 571	var accUpdates []*common.AccountUpdate
 572	err := meddler.QueryAll(
 573		hdb.dbRead, &accUpdates,
 574		"SELECT eth_block_num, batch_num, idx, nonce, balance FROM account_update ORDER BY idx;",
 575	)
 576	return db.SlicePtrsToSlice(accUpdates).([]common.AccountUpdate), tracerr.Wrap(err)
 577}
 578
 579// AddL1Txs inserts L1 txs to the DB. USD and DepositAmountUSD will be set automatically before storing the tx.
 580// If the tx is originated by a coordinator, BatchNum must be provided. If it's originated by a user,
 581// BatchNum should be null, and the value will be setted by a trigger when a batch forges the tx.
 582// EffectiveAmount and EffectiveDepositAmount are seted with default values by the DB.
 583func (hdb *HistoryDB) AddL1Txs(l1txs []common.L1Tx) error {
 584	return tracerr.Wrap(hdb.addL1Txs(hdb.dbWrite, l1txs))
 585}
 586
 587// addL1Txs inserts L1 txs to the DB. USD and DepositAmountUSD will be set automatically before storing the tx.
 588// If the tx is originated by a coordinator, BatchNum must be provided. If it's originated by a user,
 589// BatchNum should be null, and the value will be setted by a trigger when a batch forges the tx.
 590// EffectiveAmount and EffectiveDepositAmount are seted with default values by the DB.
 591func (hdb *HistoryDB) addL1Txs(d meddler.DB, l1txs []common.L1Tx) error {
 592	if len(l1txs) == 0 {
 593		return nil
 594	}
 595	txs := []txWrite{}
 596	for i := 0; i < len(l1txs); i++ {
 597		af := new(big.Float).SetInt(l1txs[i].Amount)
 598		amountFloat, _ := af.Float64()
 599		laf := new(big.Float).SetInt(l1txs[i].DepositAmount)
 600		depositAmountFloat, _ := laf.Float64()
 601		var effectiveFromIdx *common.Idx
 602		if l1txs[i].UserOrigin {
 603			if l1txs[i].Type != common.TxTypeCreateAccountDeposit &&
 604				l1txs[i].Type != common.TxTypeCreateAccountDepositTransfer {
 605				effectiveFromIdx = &l1txs[i].FromIdx
 606			}
 607		} else {
 608			effectiveFromIdx = &l1txs[i].EffectiveFromIdx
 609		}
 610		txs = append(txs, txWrite{
 611			// Generic
 612			IsL1:             true,
 613			TxID:             l1txs[i].TxID,
 614			Type:             l1txs[i].Type,
 615			Position:         l1txs[i].Position,
 616			FromIdx:          &l1txs[i].FromIdx,
 617			EffectiveFromIdx: effectiveFromIdx,
 618			ToIdx:            l1txs[i].ToIdx,
 619			Amount:           l1txs[i].Amount,
 620			AmountFloat:      amountFloat,
 621			TokenID:          l1txs[i].TokenID,
 622			BatchNum:         l1txs[i].BatchNum,
 623			EthBlockNum:      l1txs[i].EthBlockNum,
 624			// L1
 625			ToForgeL1TxsNum:    l1txs[i].ToForgeL1TxsNum,
 626			UserOrigin:         &l1txs[i].UserOrigin,
 627			FromEthAddr:        &l1txs[i].FromEthAddr,
 628			FromBJJ:            &l1txs[i].FromBJJ,
 629			DepositAmount:      l1txs[i].DepositAmount,
 630			DepositAmountFloat: &depositAmountFloat,
 631		})
 632	}
 633	return tracerr.Wrap(hdb.addTxs(d, txs))
 634}
 635
 636// AddL2Txs inserts L2 txs to the DB. TokenID, USD and FeeUSD will be set automatically before storing the tx.
 637func (hdb *HistoryDB) AddL2Txs(l2txs []common.L2Tx) error {
 638	return tracerr.Wrap(hdb.addL2Txs(hdb.dbWrite, l2txs))
 639}
 640
 641// addL2Txs inserts L2 txs to the DB. TokenID, USD and FeeUSD will be set automatically before storing the tx.
 642func (hdb *HistoryDB) addL2Txs(d meddler.DB, l2txs []common.L2Tx) error {
 643	txs := []txWrite{}
 644	for i := 0; i < len(l2txs); i++ {
 645		f := new(big.Float).SetInt(l2txs[i].Amount)
 646		amountFloat, _ := f.Float64()
 647		txs = append(txs, txWrite{
 648			// Generic
 649			IsL1:             false,
 650			TxID:             l2txs[i].TxID,
 651			Type:             l2txs[i].Type,
 652			Position:         l2txs[i].Position,
 653			FromIdx:          &l2txs[i].FromIdx,
 654			EffectiveFromIdx: &l2txs[i].FromIdx,
 655			ToIdx:            l2txs[i].ToIdx,
 656			TokenID:          l2txs[i].TokenID,
 657			Amount:           l2txs[i].Amount,
 658			AmountFloat:      amountFloat,
 659			BatchNum:         &l2txs[i].BatchNum,
 660			EthBlockNum:      l2txs[i].EthBlockNum,
 661			// L2
 662			Fee:   &l2txs[i].Fee,
 663			Nonce: &l2txs[i].Nonce,
 664		})
 665	}
 666	return tracerr.Wrap(hdb.addTxs(d, txs))
 667}
 668
 669func (hdb *HistoryDB) addTxs(d meddler.DB, txs []txWrite) error {
 670	if len(txs) == 0 {
 671		return nil
 672	}
 673	return tracerr.Wrap(db.BulkInsert(
 674		d,
 675		`INSERT INTO tx (
 676			is_l1,
 677			id,
 678			type,
 679			position,
 680			from_idx,
 681			effective_from_idx,
 682			to_idx,
 683			amount,
 684			amount_f,
 685			token_id,
 686			batch_num,
 687			eth_block_num,
 688			to_forge_l1_txs_num,
 689			user_origin,
 690			from_eth_addr,
 691			from_bjj,
 692			deposit_amount,
 693			deposit_amount_f,
 694			fee,
 695			nonce
 696		) VALUES %s;`,
 697		txs,
 698	))
 699}
 700
 701// GetAllExits returns all exit from the DB
 702func (hdb *HistoryDB) GetAllExits() ([]common.ExitInfo, error) {
 703	var exits []*common.ExitInfo
 704	err := meddler.QueryAll(
 705		hdb.dbRead, &exits,
 706		`SELECT exit_tree.batch_num, exit_tree.account_idx, exit_tree.merkle_proof,
 707		exit_tree.balance, exit_tree.instant_withdrawn, exit_tree.delayed_withdraw_request,
 708		exit_tree.delayed_withdrawn FROM exit_tree ORDER BY item_id;`,
 709	)
 710	return db.SlicePtrsToSlice(exits).([]common.ExitInfo), tracerr.Wrap(err)
 711}
 712
 713// GetAllL1UserTxs returns all L1UserTxs from the DB
 714func (hdb *HistoryDB) GetAllL1UserTxs() ([]common.L1Tx, error) {
 715	var txs []*common.L1Tx
 716	err := meddler.QueryAll(
 717		hdb.dbRead, &txs,
 718		`SELECT tx.id, tx.to_forge_l1_txs_num, tx.position, tx.user_origin,
 719		tx.from_idx, tx.effective_from_idx, tx.from_eth_addr, tx.from_bjj, tx.to_idx, tx.token_id,
 720		tx.amount, (CASE WHEN tx.batch_num IS NULL THEN NULL WHEN tx.amount_success THEN tx.amount ELSE 0 END) AS effective_amount,
 721		tx.deposit_amount, (CASE WHEN tx.batch_num IS NULL THEN NULL WHEN tx.deposit_amount_success THEN tx.deposit_amount ELSE 0 END) AS effective_deposit_amount,
 722		tx.eth_block_num, tx.type, tx.batch_num
 723		FROM tx WHERE is_l1 = TRUE AND user_origin = TRUE ORDER BY item_id;`,
 724	)
 725	return db.SlicePtrsToSlice(txs).([]common.L1Tx), tracerr.Wrap(err)
 726}
 727
 728// GetAllL1CoordinatorTxs returns all L1CoordinatorTxs from the DB
 729func (hdb *HistoryDB) GetAllL1CoordinatorTxs() ([]common.L1Tx, error) {
 730	var txs []*common.L1Tx
 731	// Since the query specifies that only coordinator txs are returned, it's safe to assume
 732	// that returned txs will always have effective amounts
 733	err := meddler.QueryAll(
 734		hdb.dbRead, &txs,
 735		`SELECT tx.id, tx.to_forge_l1_txs_num, tx.position, tx.user_origin,
 736		tx.from_idx, tx.effective_from_idx, tx.from_eth_addr, tx.from_bjj, tx.to_idx, tx.token_id,
 737		tx.amount, tx.amount AS effective_amount,
 738		tx.deposit_amount, tx.deposit_amount AS effective_deposit_amount,
 739		tx.eth_block_num, tx.type, tx.batch_num
 740		FROM tx WHERE is_l1 = TRUE AND user_origin = FALSE ORDER BY item_id;`,
 741	)
 742	return db.SlicePtrsToSlice(txs).([]common.L1Tx), tracerr.Wrap(err)
 743}
 744
 745// GetAllL2Txs returns all L2Txs from the DB
 746func (hdb *HistoryDB) GetAllL2Txs() ([]common.L2Tx, error) {
 747	var txs []*common.L2Tx
 748	err := meddler.QueryAll(
 749		hdb.dbRead, &txs,
 750		`SELECT tx.id, tx.batch_num, tx.position,
 751		tx.from_idx, tx.to_idx, tx.amount, tx.token_id,
 752		tx.fee, tx.nonce, tx.type, tx.eth_block_num
 753		FROM tx WHERE is_l1 = FALSE ORDER BY item_id;`,
 754	)
 755	return db.SlicePtrsToSlice(txs).([]common.L2Tx), tracerr.Wrap(err)
 756}
 757
 758// GetUnforgedL1UserTxs gets L1 User Txs to be forged in the L1Batch with toForgeL1TxsNum.
 759func (hdb *HistoryDB) GetUnforgedL1UserTxs(toForgeL1TxsNum int64) ([]common.L1Tx, error) {
 760	var txs []*common.L1Tx
 761	err := meddler.QueryAll(
 762		hdb.dbRead, &txs, // only L1 user txs can have batch_num set to null
 763		`SELECT tx.id, tx.to_forge_l1_txs_num, tx.position, tx.user_origin,
 764		tx.from_idx, tx.from_eth_addr, tx.from_bjj, tx.to_idx, tx.token_id,
 765		tx.amount, NULL AS effective_amount,
 766		tx.deposit_amount, NULL AS effective_deposit_amount,
 767		tx.eth_block_num, tx.type, tx.batch_num
 768		FROM tx WHERE batch_num IS NULL AND to_forge_l1_txs_num = $1
 769		ORDER BY position;`,
 770		toForgeL1TxsNum,
 771	)
 772	return db.SlicePtrsToSlice(txs).([]common.L1Tx), tracerr.Wrap(err)
 773}
 774
 775// GetUnforgedL1UserFutureTxs gets L1 User Txs to be forged after the L1Batch
 776// with toForgeL1TxsNum (in one of the future batches, not in the next one).
 777func (hdb *HistoryDB) GetUnforgedL1UserFutureTxs(toForgeL1TxsNum int64) ([]common.L1Tx, error) {
 778	var txs []*common.L1Tx
 779	err := meddler.QueryAll(
 780		hdb.dbRead, &txs, // only L1 user txs can have batch_num set to null
 781		`SELECT tx.id, tx.to_forge_l1_txs_num, tx.position, tx.user_origin,
 782		tx.from_idx, tx.from_eth_addr, tx.from_bjj, tx.to_idx, tx.token_id,
 783		tx.amount, NULL AS effective_amount,
 784		tx.deposit_amount, NULL AS effective_deposit_amount,
 785		tx.eth_block_num, tx.type, tx.batch_num
 786		FROM tx WHERE batch_num IS NULL AND to_forge_l1_txs_num > $1
 787		ORDER BY position;`,
 788		toForgeL1TxsNum,
 789	)
 790	return db.SlicePtrsToSlice(txs).([]common.L1Tx), tracerr.Wrap(err)
 791}
 792
 793// GetUnforgedL1UserTxsCount returns the count of unforged L1Txs (either in
 794// open or frozen queues that are not yet forged)
 795func (hdb *HistoryDB) GetUnforgedL1UserTxsCount() (int, error) {
 796	row := hdb.dbRead.QueryRow(
 797		`SELECT COUNT(*) FROM tx WHERE batch_num IS NULL;`,
 798	)
 799	var count int
 800	return count, tracerr.Wrap(row.Scan(&count))
 801}
 802
 803// TODO: Think about chaning all the queries that return a last value, to queries that return the next valid value.
 804
 805// GetLastTxsPosition for a given to_forge_l1_txs_num
 806func (hdb *HistoryDB) GetLastTxsPosition(toForgeL1TxsNum int64) (int, error) {
 807	row := hdb.dbRead.QueryRow(
 808		"SELECT position FROM tx WHERE to_forge_l1_txs_num = $1 ORDER BY position DESC;",
 809		toForgeL1TxsNum,
 810	)
 811	var lastL1TxsPosition int
 812	return lastL1TxsPosition, tracerr.Wrap(row.Scan(&lastL1TxsPosition))
 813}
 814
 815// GetSCVars returns the rollup, auction and wdelayer smart contracts variables at their last update.
 816func (hdb *HistoryDB) GetSCVars() (*common.RollupVariables, *common.AuctionVariables,
 817	*common.WDelayerVariables, error) {
 818	var rollup common.RollupVariables
 819	var auction common.AuctionVariables
 820	var wDelayer common.WDelayerVariables
 821	if err := meddler.QueryRow(hdb.dbRead, &rollup,
 822		"SELECT * FROM rollup_vars ORDER BY eth_block_num DESC LIMIT 1;"); err != nil {
 823		return nil, nil, nil, tracerr.Wrap(err)
 824	}
 825	if err := meddler.QueryRow(hdb.dbRead, &auction,
 826		"SELECT * FROM auction_vars ORDER BY eth_block_num DESC LIMIT 1;"); err != nil {
 827		return nil, nil, nil, tracerr.Wrap(err)
 828	}
 829	if err := meddler.QueryRow(hdb.dbRead, &wDelayer,
 830		"SELECT * FROM wdelayer_vars ORDER BY eth_block_num DESC LIMIT 1;"); err != nil {
 831		return nil, nil, nil, tracerr.Wrap(err)
 832	}
 833	return &rollup, &auction, &wDelayer, nil
 834}
 835
 836func (hdb *HistoryDB) setRollupVars(d meddler.DB, rollup *common.RollupVariables) error {
 837	return tracerr.Wrap(meddler.Insert(d, "rollup_vars", rollup))
 838}
 839
 840func (hdb *HistoryDB) setAuctionVars(d meddler.DB, auction *common.AuctionVariables) error {
 841	return tracerr.Wrap(meddler.Insert(d, "auction_vars", auction))
 842}
 843
 844func (hdb *HistoryDB) setWDelayerVars(d meddler.DB, wDelayer *common.WDelayerVariables) error {
 845	return tracerr.Wrap(meddler.Insert(d, "wdelayer_vars", wDelayer))
 846}
 847
 848func (hdb *HistoryDB) addBucketUpdates(d meddler.DB, bucketUpdates []common.BucketUpdate) error {
 849	if len(bucketUpdates) == 0 {
 850		return nil
 851	}
 852	return tracerr.Wrap(db.BulkInsert(
 853		d,
 854		`INSERT INTO bucket_update (
 855		 	eth_block_num,
 856		 	num_bucket,
 857		 	block_stamp,
 858		 	withdrawals
 859		) VALUES %s;`,
 860		bucketUpdates,
 861	))
 862}
 863
 864// AddBucketUpdatesTest allows call to unexported method
 865// only for internal testing purposes
 866func (hdb *HistoryDB) AddBucketUpdatesTest(d meddler.DB, bucketUpdates []common.BucketUpdate) error {
 867	return hdb.addBucketUpdates(d, bucketUpdates)
 868}
 869
 870// GetAllBucketUpdates retrieves all the bucket updates
 871func (hdb *HistoryDB) GetAllBucketUpdates() ([]common.BucketUpdate, error) {
 872	var bucketUpdates []*common.BucketUpdate
 873	err := meddler.QueryAll(
 874		hdb.dbRead, &bucketUpdates,
 875		`SELECT eth_block_num, num_bucket, block_stamp, withdrawals  
 876		FROM bucket_update ORDER BY item_id;`,
 877	)
 878	return db.SlicePtrsToSlice(bucketUpdates).([]common.BucketUpdate), tracerr.Wrap(err)
 879}
 880
 881func (hdb *HistoryDB) getMinBidInfo(d meddler.DB,
 882	currentSlot, lastClosedSlot int64) ([]MinBidInfo, error) {
 883	minBidInfo := []*MinBidInfo{}
 884	query := `
 885		SELECT DISTINCT default_slot_set_bid, default_slot_set_bid_slot_num FROM auction_vars
 886		WHERE default_slot_set_bid_slot_num < $1
 887		ORDER BY default_slot_set_bid_slot_num DESC
 888		LIMIT $2;`
 889	err := meddler.QueryAll(d, &minBidInfo, query, lastClosedSlot, int(lastClosedSlot-currentSlot)+1)
 890	return db.SlicePtrsToSlice(minBidInfo).([]MinBidInfo), tracerr.Wrap(err)
 891}
 892
 893func (hdb *HistoryDB) addTokenExchanges(d meddler.DB, tokenExchanges []common.TokenExchange) error {
 894	if len(tokenExchanges) == 0 {
 895		return nil
 896	}
 897	return tracerr.Wrap(db.BulkInsert(
 898		d,
 899		`INSERT INTO token_exchange (
 900			eth_block_num,
 901    			eth_addr,
 902    			value_usd
 903		) VALUES %s;`,
 904		tokenExchanges,
 905	))
 906}
 907
 908// GetAllTokenExchanges retrieves all the token exchanges
 909func (hdb *HistoryDB) GetAllTokenExchanges() ([]common.TokenExchange, error) {
 910	var tokenExchanges []*common.TokenExchange
 911	err := meddler.QueryAll(
 912		hdb.dbRead, &tokenExchanges,
 913		"SELECT eth_block_num, eth_addr, value_usd FROM token_exchange ORDER BY item_id;",
 914	)
 915	return db.SlicePtrsToSlice(tokenExchanges).([]common.TokenExchange), tracerr.Wrap(err)
 916}
 917
 918func (hdb *HistoryDB) addEscapeHatchWithdrawals(d meddler.DB,
 919	escapeHatchWithdrawals []common.WDelayerEscapeHatchWithdrawal) error {
 920	if len(escapeHatchWithdrawals) == 0 {
 921		return nil
 922	}
 923	return tracerr.Wrap(db.BulkInsert(
 924		d,
 925		`INSERT INTO escape_hatch_withdrawal (
 926			eth_block_num,
 927			who_addr,
 928			to_addr,
 929			token_addr,
 930			amount
 931		) VALUES %s;`,
 932		escapeHatchWithdrawals,
 933	))
 934}
 935
 936// GetAllEscapeHatchWithdrawals retrieves all the escape hatch withdrawals
 937func (hdb *HistoryDB) GetAllEscapeHatchWithdrawals() ([]common.WDelayerEscapeHatchWithdrawal, error) {
 938	var escapeHatchWithdrawals []*common.WDelayerEscapeHatchWithdrawal
 939	err := meddler.QueryAll(
 940		hdb.dbRead, &escapeHatchWithdrawals,
 941		"SELECT eth_block_num, who_addr, to_addr, token_addr, amount FROM escape_hatch_withdrawal ORDER BY item_id;",
 942	)
 943	return db.SlicePtrsToSlice(escapeHatchWithdrawals).([]common.WDelayerEscapeHatchWithdrawal),
 944		tracerr.Wrap(err)
 945}
 946
 947// SetInitialSCVars sets the initial state of rollup, auction, wdelayer smart
 948// contract variables.  This initial state is stored linked to block 0, which
 949// always exist in the DB and is used to store initialization data that always
 950// exist in the smart contracts.
 951func (hdb *HistoryDB) SetInitialSCVars(rollup *common.RollupVariables,
 952	auction *common.AuctionVariables, wDelayer *common.WDelayerVariables) error {
 953	txn, err := hdb.dbWrite.Beginx()
 954	if err != nil {
 955		return tracerr.Wrap(err)
 956	}
 957	defer func() {
 958		if err != nil {
 959			db.Rollback(txn)
 960		}
 961	}()
 962	// Force EthBlockNum to be 0 because it's the block used to link data
 963	// that belongs to the creation of the smart contracts
 964	rollup.EthBlockNum = 0
 965	auction.EthBlockNum = 0
 966	wDelayer.EthBlockNum = 0
 967	auction.DefaultSlotSetBidSlotNum = 0
 968	if err := hdb.setRollupVars(txn, rollup); err != nil {
 969		return tracerr.Wrap(err)
 970	}
 971	if err := hdb.setAuctionVars(txn, auction); err != nil {
 972		return tracerr.Wrap(err)
 973	}
 974	if err := hdb.setWDelayerVars(txn, wDelayer); err != nil {
 975		return tracerr.Wrap(err)
 976	}
 977
 978	return tracerr.Wrap(txn.Commit())
 979}
 980
 981// setExtraInfoForgedL1UserTxs sets the EffectiveAmount, EffectiveDepositAmount
 982// and EffectiveFromIdx of the given l1UserTxs (with an UPDATE)
 983func (hdb *HistoryDB) setExtraInfoForgedL1UserTxs(d sqlx.Ext, txs []common.L1Tx) error {
 984	if len(txs) == 0 {
 985		return nil
 986	}
 987	// Effective amounts are stored as success flags in the DB, with true value by default
 988	// to reduce the amount of updates. Therefore, only amounts that became uneffective should be
 989	// updated to become false.  At the same time, all the txs that contain
 990	// accounts (FromIdx == 0) are updated to set the EffectiveFromIdx.
 991	type txUpdate struct {
 992		ID                   common.TxID `db:"id"`
 993		AmountSuccess        bool        `db:"amount_success"`
 994		DepositAmountSuccess bool        `db:"deposit_amount_success"`
 995		EffectiveFromIdx     common.Idx  `db:"effective_from_idx"`
 996	}
 997	txUpdates := []txUpdate{}
 998	equal := func(a *big.Int, b *big.Int) bool {
 999		return a.Cmp(b) == 0
1000	}
1001	for i := range txs {
1002		amountSuccess := equal(txs[i].Amount, txs[i].EffectiveAmount)
1003		depositAmountSuccess := equal(txs[i].DepositAmount, txs[i].EffectiveDepositAmount)
1004		if !amountSuccess || !depositAmountSuccess || txs[i].FromIdx == 0 {
1005			txUpdates = append(txUpdates, txUpdate{
1006				ID:                   txs[i].TxID,
1007				AmountSuccess:        amountSuccess,
1008				DepositAmountSuccess: depositAmountSuccess,
1009				EffectiveFromIdx:     txs[i].EffectiveFromIdx,
1010			})
1011		}
1012	}
1013	const query string = `
1014		UPDATE tx SET
1015			amount_success = tx_update.amount_success,
1016			deposit_amount_success = tx_update.deposit_amount_success,
1017			effective_from_idx = tx_update.effective_from_idx
1018		FROM (VALUES
1019			(NULL::::BYTEA, NULL::::BOOL, NULL::::BOOL, NULL::::BIGINT),
1020			(:id, :amount_success, :deposit_amount_success, :effective_from_idx)
1021		) as tx_update (id, amount_success, deposit_amount_success, effective_from_idx)
1022		WHERE tx.id = tx_update.id;
1023	`
1024	if len(txUpdates) > 0 {
1025		if _, err := sqlx.NamedExec(d, query, txUpdates); err != nil {
1026			return tracerr.Wrap(err)
1027		}
1028	}
1029	return nil
1030}
1031
1032// AddBlockSCData stores all the information of a block retrieved by the
1033// Synchronizer.  Blocks should be inserted in order, leaving no gaps because
1034// the pagination system of the API/DB depends on this.  Within blocks, all
1035// items should also be in the correct order (Accounts, Tokens, Txs, etc.)
1036func (hdb *HistoryDB) AddBlockSCData(blockData *common.BlockData) (err error) {
1037	txn, err := hdb.dbWrite.Beginx()
1038	if err != nil {
1039		return tracerr.Wrap(err)
1040	}
1041	defer func() {
1042		if err != nil {
1043			db.Rollback(txn)
1044		}
1045	}()
1046
1047	// Add block
1048	if err := hdb.addBlock(txn, &blockData.Block); err != nil {
1049		return tracerr.Wrap(err)
1050	}
1051
1052	// Add Coordinators
1053	if err := hdb.addCoordinators(txn, blockData.Auction.Coordinators); err != nil {
1054		return tracerr.Wrap(err)
1055	}
1056
1057	// Add Bids
1058	if err := hdb.addBids(txn, blockData.Auction.Bids); err != nil {
1059		return tracerr.Wrap(err)
1060	}
1061
1062	// Add Tokens
1063	if err := hdb.addTokens(txn, blockData.Rollup.AddedTokens); err != nil {
1064		return tracerr.Wrap(err)
1065	}
1066
1067	// Prepare user L1 txs to be added.
1068	// They must be added before the batch that will forge them (which can be in the same block)
1069	// and after the account that will be sent to (also can be in the same block).
1070	// Note: insert order is not relevant since item_id will be updated by a DB trigger when
1071	// the batch that forges those txs is inserted
1072	userL1s := make(map[common.BatchNum][]common.L1Tx)
1073	for i := range blockData.Rollup.L1UserTxs {
1074		batchThatForgesIsInTheBlock := false
1075		for _, batch := range blockData.Rollup.Batches {
1076			if batch.Batch.ForgeL1TxsNum != nil &&
1077				*batch.Batch.ForgeL1TxsNum == *blockData.Rollup.L1UserTxs[i].ToForgeL1TxsNum {
1078				// Tx is forged in this block. It's guaranteed that:
1079				// * the first batch of the block won't forge user L1 txs that have been added in this block
1080				// * batch nums are sequential therefore it's safe to add the tx at batch.BatchNum -1
1081				batchThatForgesIsInTheBlock = true
1082				addAtBatchNum := batch.Batch.BatchNum - 1
1083				userL1s[addAtBatchNum] = append(userL1s[addAtBatchNum], blockData.Rollup.L1UserTxs[i])
1084				break
1085			}
1086		}
1087		if !batchThatForgesIsInTheBlock {
1088			// User artificial batchNum 0 to add txs that are not forge in this block
1089			// after all the accounts of the block have been added
1090			userL1s[0] = append(userL1s[0], blockData.Rollup.L1UserTxs[i])
1091		}
1092	}
1093
1094	// Add Batches
1095	for i := range blockData.Rollup.Batches {
1096		batch := &blockData.Rollup.Batches[i]
1097
1098		// Add Batch: this will trigger an update on the DB
1099		// that will set the batch num of forged L1 txs in this batch
1100		if err = hdb.addBatch(txn, &batch.Batch); err != nil {
1101			return tracerr.Wrap(err)
1102		}
1103
1104		// Add accounts
1105		if err := hdb.addAccounts(txn, batch.CreatedAccounts); err != nil {
1106			return tracerr.Wrap(err)
1107		}
1108
1109		// Add accountBalances if it exists
1110		if err := hdb.addAccountUpdates(txn, batch.UpdatedAccounts); err != nil {
1111			return tracerr.Wrap(err)
1112		}
1113
1114		// Set the EffectiveAmount and EffectiveDepositAmount of all the
1115		// L1UserTxs that have been forged in this batch
1116		if err = hdb.setExtraInfoForgedL1UserTxs(txn, batch.L1UserTxs); err != nil {
1117			return tracerr.Wrap(err)
1118		}
1119
1120		// Add forged l1 coordinator Txs
1121		if err := hdb.addL1Txs(txn, batch.L1CoordinatorTxs); err != nil {
1122			return tracerr.Wrap(err)
1123		}
1124
1125		// Add l2 Txs
1126		if err := hdb.addL2Txs(txn, batch.L2Txs); err != nil {
1127			return tracerr.Wrap(err)
1128		}
1129
1130		// Add user L1 txs that will be forged in next batch
1131		if userlL1s, ok := userL1s[batch.Batch.BatchNum]; ok {
1132			if err := hdb.addL1Txs(txn, userlL1s); err != nil {
1133				return tracerr.Wrap(err)
1134			}
1135		}
1136
1137		// Add exit tree
1138		if err := hdb.addExitTree(txn, batch.ExitTree); err != nil {
1139			return tracerr.Wrap(err)
1140		}
1141	}
1142	// Add user L1 txs that won't be forged in this block
1143	if userL1sNotForgedInThisBlock, ok := userL1s[0]; ok {
1144		if err := hdb.addL1Txs(txn, userL1sNotForgedInThisBlock); err != nil {
1145			return tracerr.Wrap(err)
1146		}
1147	}
1148
1149	// Set SC Vars if there was an update
1150	if blockData.Rollup.Vars != nil {
1151		if err := hdb.setRollupVars(txn, blockData.Rollup.Vars); err != nil {
1152			return tracerr.Wrap(err)
1153		}
1154	}
1155	if blockData.Auction.Vars != nil {
1156		if err := hdb.setAuctionVars(txn, blockData.Auction.Vars); err != nil {
1157			return tracerr.Wrap(err)
1158		}
1159	}
1160	if blockData.WDelayer.Vars != nil {
1161		if err := hdb.setWDelayerVars(txn, blockData.WDelayer.Vars); err != nil {
1162			return tracerr.Wrap(err)
1163		}
1164	}
1165
1166	// Update withdrawals in exit tree table
1167	if err := hdb.updateExitTree(txn, blockData.Block.Num,
1168		blockData.Rollup.Withdrawals, blockData.WDelayer.Withdrawals); err != nil {
1169		return tracerr.Wrap(err)
1170	}
1171
1172	// Add Escape Hatch Withdrawals
1173	if err := hdb.addEscapeHatchWithdrawals(txn,
1174		blockData.WDelayer.EscapeHatchWithdrawals); err != nil {
1175		return tracerr.Wrap(err)
1176	}
1177
1178	// Add Buckets withdrawals updates
1179	if err := hdb.addBucketUpdates(txn, blockData.Rollup.UpdateBucketWithdraw); err != nil {
1180		return tracerr.Wrap(err)
1181	}
1182
1183	// Add Token exchange updates
1184	if err := hdb.addTokenExchanges(txn, blockData.Rollup.TokenExchanges); err != nil {
1185		return tracerr.Wrap(err)
1186	}
1187
1188	return tracerr.Wrap(txn.Commit())
1189}
1190
1191// AddAuctionVars insert auction vars into the DB
1192func (hdb *HistoryDB) AddAuctionVars(auctionVars *common.AuctionVariables) error {
1193	return tracerr.Wrap(meddler.Insert(hdb.dbWrite, "auction_vars", auctionVars))
1194}
1195
1196// GetTokensTest used to get tokens in a testing context
1197func (hdb *HistoryDB) GetTokensTest() ([]TokenWithUSD, error) {
1198	tokens := []*TokenWithUSD{}
1199	if err := meddler.QueryAll(
1200		hdb.dbRead, &tokens,
1201		"SELECT * FROM token ORDER BY token_id ASC",
1202	); err != nil {
1203		return nil, tracerr.Wrap(err)
1204	}
1205	if len(tokens) == 0 {
1206		return []TokenWithUSD{}, nil
1207	}
1208	return db.SlicePtrsToSlice(tokens).([]TokenWithUSD), nil
1209}
1210
1211const (
1212	// CreateAccountExtraFeePercentage is the multiplication factor over
1213	// the average fee for CreateAccount that is applied to obtain the
1214	// recommended fee for CreateAccount
1215	CreateAccountExtraFeePercentage float64 = 2.5
1216	// CreateAccountInternalExtraFeePercentage is the multiplication factor
1217	// over the average fee for CreateAccountInternal that is applied to
1218	// obtain the recommended fee for CreateAccountInternal
1219	CreateAccountInternalExtraFeePercentage float64 = 2.0
1220)
1221
1222// GetRecommendedFee returns the RecommendedFee information
1223func (hdb *HistoryDB) GetRecommendedFee(minFeeUSD, maxFeeUSD float64) (*common.RecommendedFee, error) {
1224	var recommendedFee common.RecommendedFee
1225	// Get total txs and the batch of the first selected tx of the last hour
1226	type totalTxsSinceBatchNum struct {
1227		TotalTxs      int             `meddler:"total_txs"`
1228		FirstBatchNum common.BatchNum `meddler:"batch_num"`
1229	}
1230	ttsbn := &totalTxsSinceBatchNum{}
1231	if err := meddler.QueryRow(
1232		hdb.dbRead, ttsbn, `SELECT COUNT(tx.*) as total_txs, 
1233			COALESCE (MIN(tx.batch_num), 0) as batch_num 
1234			FROM tx INNER JOIN block ON tx.eth_block_num = block.eth_block_num
1235			WHERE block.timestamp >= NOW() - INTERVAL '1 HOURS';`,
1236	); err != nil {
1237		return nil, tracerr.Wrap(err)
1238	}
1239	// Get the amount of batches and acumulated fees for the last hour
1240	type totalBatchesAndFee struct {
1241		TotalBatches int     `meddler:"total_batches"`
1242		TotalFees    float64 `meddler:"total_fees"`
1243	}
1244	tbf := &totalBatchesAndFee{}
1245	if err := meddler.QueryRow(
1246		hdb.dbRead, tbf, `SELECT COUNT(*) AS total_batches, 
1247			COALESCE (SUM(total_fees_usd), 0) AS total_fees FROM batch 
1248			WHERE batch_num > $1;`, ttsbn.FirstBatchNum,
1249	); err != nil {
1250		return nil, tracerr.Wrap(err)
1251	}
1252	// Update NodeInfo struct
1253	var avgTransactionFee float64
1254	if ttsbn.TotalTxs > 0 {
1255		avgTransactionFee = tbf.TotalFees / float64(ttsbn.TotalTxs)
1256	} else {
1257		avgTransactionFee = 0
1258	}
1259	recommendedFee.ExistingAccount = math.Min(maxFeeUSD,
1260		math.Max(avgTransactionFee, minFeeUSD))
1261	recommendedFee.CreatesAccount = math.Min(maxFeeUSD,
1262		math.Max(CreateAccountExtraFeePercentage*avgTransactionFee, minFeeUSD))
1263	recommendedFee.CreatesAccountInternal = math.Min(maxFeeUSD,
1264		math.Max(CreateAccountInternalExtraFeePercentage*avgTransactionFee, minFeeUSD))
1265	return &recommendedFee, nil
1266}