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// GetUnforgedL1UserTxsCount returns the count of unforged L1Txs (either in
 776// open or frozen queues that are not yet forged)
 777func (hdb *HistoryDB) GetUnforgedL1UserTxsCount() (int, error) {
 778	row := hdb.dbRead.QueryRow(
 779		`SELECT COUNT(*) FROM tx WHERE batch_num IS NULL;`,
 780	)
 781	var count int
 782	return count, tracerr.Wrap(row.Scan(&count))
 783}
 784
 785// TODO: Think about chaning all the queries that return a last value, to queries that return the next valid value.
 786
 787// GetLastTxsPosition for a given to_forge_l1_txs_num
 788func (hdb *HistoryDB) GetLastTxsPosition(toForgeL1TxsNum int64) (int, error) {
 789	row := hdb.dbRead.QueryRow(
 790		"SELECT position FROM tx WHERE to_forge_l1_txs_num = $1 ORDER BY position DESC;",
 791		toForgeL1TxsNum,
 792	)
 793	var lastL1TxsPosition int
 794	return lastL1TxsPosition, tracerr.Wrap(row.Scan(&lastL1TxsPosition))
 795}
 796
 797// GetSCVars returns the rollup, auction and wdelayer smart contracts variables at their last update.
 798func (hdb *HistoryDB) GetSCVars() (*common.RollupVariables, *common.AuctionVariables,
 799	*common.WDelayerVariables, error) {
 800	var rollup common.RollupVariables
 801	var auction common.AuctionVariables
 802	var wDelayer common.WDelayerVariables
 803	if err := meddler.QueryRow(hdb.dbRead, &rollup,
 804		"SELECT * FROM rollup_vars ORDER BY eth_block_num DESC LIMIT 1;"); err != nil {
 805		return nil, nil, nil, tracerr.Wrap(err)
 806	}
 807	if err := meddler.QueryRow(hdb.dbRead, &auction,
 808		"SELECT * FROM auction_vars ORDER BY eth_block_num DESC LIMIT 1;"); err != nil {
 809		return nil, nil, nil, tracerr.Wrap(err)
 810	}
 811	if err := meddler.QueryRow(hdb.dbRead, &wDelayer,
 812		"SELECT * FROM wdelayer_vars ORDER BY eth_block_num DESC LIMIT 1;"); err != nil {
 813		return nil, nil, nil, tracerr.Wrap(err)
 814	}
 815	return &rollup, &auction, &wDelayer, nil
 816}
 817
 818func (hdb *HistoryDB) setRollupVars(d meddler.DB, rollup *common.RollupVariables) error {
 819	return tracerr.Wrap(meddler.Insert(d, "rollup_vars", rollup))
 820}
 821
 822func (hdb *HistoryDB) setAuctionVars(d meddler.DB, auction *common.AuctionVariables) error {
 823	return tracerr.Wrap(meddler.Insert(d, "auction_vars", auction))
 824}
 825
 826func (hdb *HistoryDB) setWDelayerVars(d meddler.DB, wDelayer *common.WDelayerVariables) error {
 827	return tracerr.Wrap(meddler.Insert(d, "wdelayer_vars", wDelayer))
 828}
 829
 830func (hdb *HistoryDB) addBucketUpdates(d meddler.DB, bucketUpdates []common.BucketUpdate) error {
 831	if len(bucketUpdates) == 0 {
 832		return nil
 833	}
 834	return tracerr.Wrap(db.BulkInsert(
 835		d,
 836		`INSERT INTO bucket_update (
 837		 	eth_block_num,
 838		 	num_bucket,
 839		 	block_stamp,
 840		 	withdrawals
 841		) VALUES %s;`,
 842		bucketUpdates,
 843	))
 844}
 845
 846// AddBucketUpdatesTest allows call to unexported method
 847// only for internal testing purposes
 848func (hdb *HistoryDB) AddBucketUpdatesTest(d meddler.DB, bucketUpdates []common.BucketUpdate) error {
 849	return hdb.addBucketUpdates(d, bucketUpdates)
 850}
 851
 852// GetAllBucketUpdates retrieves all the bucket updates
 853func (hdb *HistoryDB) GetAllBucketUpdates() ([]common.BucketUpdate, error) {
 854	var bucketUpdates []*common.BucketUpdate
 855	err := meddler.QueryAll(
 856		hdb.dbRead, &bucketUpdates,
 857		`SELECT eth_block_num, num_bucket, block_stamp, withdrawals  
 858		FROM bucket_update ORDER BY item_id;`,
 859	)
 860	return db.SlicePtrsToSlice(bucketUpdates).([]common.BucketUpdate), tracerr.Wrap(err)
 861}
 862
 863func (hdb *HistoryDB) getMinBidInfo(d meddler.DB,
 864	currentSlot, lastClosedSlot int64) ([]MinBidInfo, error) {
 865	minBidInfo := []*MinBidInfo{}
 866	query := `
 867		SELECT DISTINCT default_slot_set_bid, default_slot_set_bid_slot_num FROM auction_vars
 868		WHERE default_slot_set_bid_slot_num < $1
 869		ORDER BY default_slot_set_bid_slot_num DESC
 870		LIMIT $2;`
 871	err := meddler.QueryAll(d, &minBidInfo, query, lastClosedSlot, int(lastClosedSlot-currentSlot)+1)
 872	return db.SlicePtrsToSlice(minBidInfo).([]MinBidInfo), tracerr.Wrap(err)
 873}
 874
 875func (hdb *HistoryDB) addTokenExchanges(d meddler.DB, tokenExchanges []common.TokenExchange) error {
 876	if len(tokenExchanges) == 0 {
 877		return nil
 878	}
 879	return tracerr.Wrap(db.BulkInsert(
 880		d,
 881		`INSERT INTO token_exchange (
 882			eth_block_num,
 883    			eth_addr,
 884    			value_usd
 885		) VALUES %s;`,
 886		tokenExchanges,
 887	))
 888}
 889
 890// GetAllTokenExchanges retrieves all the token exchanges
 891func (hdb *HistoryDB) GetAllTokenExchanges() ([]common.TokenExchange, error) {
 892	var tokenExchanges []*common.TokenExchange
 893	err := meddler.QueryAll(
 894		hdb.dbRead, &tokenExchanges,
 895		"SELECT eth_block_num, eth_addr, value_usd FROM token_exchange ORDER BY item_id;",
 896	)
 897	return db.SlicePtrsToSlice(tokenExchanges).([]common.TokenExchange), tracerr.Wrap(err)
 898}
 899
 900func (hdb *HistoryDB) addEscapeHatchWithdrawals(d meddler.DB,
 901	escapeHatchWithdrawals []common.WDelayerEscapeHatchWithdrawal) error {
 902	if len(escapeHatchWithdrawals) == 0 {
 903		return nil
 904	}
 905	return tracerr.Wrap(db.BulkInsert(
 906		d,
 907		`INSERT INTO escape_hatch_withdrawal (
 908			eth_block_num,
 909			who_addr,
 910			to_addr,
 911			token_addr,
 912			amount
 913		) VALUES %s;`,
 914		escapeHatchWithdrawals,
 915	))
 916}
 917
 918// GetAllEscapeHatchWithdrawals retrieves all the escape hatch withdrawals
 919func (hdb *HistoryDB) GetAllEscapeHatchWithdrawals() ([]common.WDelayerEscapeHatchWithdrawal, error) {
 920	var escapeHatchWithdrawals []*common.WDelayerEscapeHatchWithdrawal
 921	err := meddler.QueryAll(
 922		hdb.dbRead, &escapeHatchWithdrawals,
 923		"SELECT eth_block_num, who_addr, to_addr, token_addr, amount FROM escape_hatch_withdrawal ORDER BY item_id;",
 924	)
 925	return db.SlicePtrsToSlice(escapeHatchWithdrawals).([]common.WDelayerEscapeHatchWithdrawal),
 926		tracerr.Wrap(err)
 927}
 928
 929// SetInitialSCVars sets the initial state of rollup, auction, wdelayer smart
 930// contract variables.  This initial state is stored linked to block 0, which
 931// always exist in the DB and is used to store initialization data that always
 932// exist in the smart contracts.
 933func (hdb *HistoryDB) SetInitialSCVars(rollup *common.RollupVariables,
 934	auction *common.AuctionVariables, wDelayer *common.WDelayerVariables) error {
 935	txn, err := hdb.dbWrite.Beginx()
 936	if err != nil {
 937		return tracerr.Wrap(err)
 938	}
 939	defer func() {
 940		if err != nil {
 941			db.Rollback(txn)
 942		}
 943	}()
 944	// Force EthBlockNum to be 0 because it's the block used to link data
 945	// that belongs to the creation of the smart contracts
 946	rollup.EthBlockNum = 0
 947	auction.EthBlockNum = 0
 948	wDelayer.EthBlockNum = 0
 949	auction.DefaultSlotSetBidSlotNum = 0
 950	if err := hdb.setRollupVars(txn, rollup); err != nil {
 951		return tracerr.Wrap(err)
 952	}
 953	if err := hdb.setAuctionVars(txn, auction); err != nil {
 954		return tracerr.Wrap(err)
 955	}
 956	if err := hdb.setWDelayerVars(txn, wDelayer); err != nil {
 957		return tracerr.Wrap(err)
 958	}
 959
 960	return tracerr.Wrap(txn.Commit())
 961}
 962
 963// setExtraInfoForgedL1UserTxs sets the EffectiveAmount, EffectiveDepositAmount
 964// and EffectiveFromIdx of the given l1UserTxs (with an UPDATE)
 965func (hdb *HistoryDB) setExtraInfoForgedL1UserTxs(d sqlx.Ext, txs []common.L1Tx) error {
 966	if len(txs) == 0 {
 967		return nil
 968	}
 969	// Effective amounts are stored as success flags in the DB, with true value by default
 970	// to reduce the amount of updates. Therefore, only amounts that became uneffective should be
 971	// updated to become false.  At the same time, all the txs that contain
 972	// accounts (FromIdx == 0) are updated to set the EffectiveFromIdx.
 973	type txUpdate struct {
 974		ID                   common.TxID `db:"id"`
 975		AmountSuccess        bool        `db:"amount_success"`
 976		DepositAmountSuccess bool        `db:"deposit_amount_success"`
 977		EffectiveFromIdx     common.Idx  `db:"effective_from_idx"`
 978	}
 979	txUpdates := []txUpdate{}
 980	equal := func(a *big.Int, b *big.Int) bool {
 981		return a.Cmp(b) == 0
 982	}
 983	for i := range txs {
 984		amountSuccess := equal(txs[i].Amount, txs[i].EffectiveAmount)
 985		depositAmountSuccess := equal(txs[i].DepositAmount, txs[i].EffectiveDepositAmount)
 986		if !amountSuccess || !depositAmountSuccess || txs[i].FromIdx == 0 {
 987			txUpdates = append(txUpdates, txUpdate{
 988				ID:                   txs[i].TxID,
 989				AmountSuccess:        amountSuccess,
 990				DepositAmountSuccess: depositAmountSuccess,
 991				EffectiveFromIdx:     txs[i].EffectiveFromIdx,
 992			})
 993		}
 994	}
 995	const query string = `
 996		UPDATE tx SET
 997			amount_success = tx_update.amount_success,
 998			deposit_amount_success = tx_update.deposit_amount_success,
 999			effective_from_idx = tx_update.effective_from_idx
1000		FROM (VALUES
1001			(NULL::::BYTEA, NULL::::BOOL, NULL::::BOOL, NULL::::BIGINT),
1002			(:id, :amount_success, :deposit_amount_success, :effective_from_idx)
1003		) as tx_update (id, amount_success, deposit_amount_success, effective_from_idx)
1004		WHERE tx.id = tx_update.id;
1005	`
1006	if len(txUpdates) > 0 {
1007		if _, err := sqlx.NamedExec(d, query, txUpdates); err != nil {
1008			return tracerr.Wrap(err)
1009		}
1010	}
1011	return nil
1012}
1013
1014// AddBlockSCData stores all the information of a block retrieved by the
1015// Synchronizer.  Blocks should be inserted in order, leaving no gaps because
1016// the pagination system of the API/DB depends on this.  Within blocks, all
1017// items should also be in the correct order (Accounts, Tokens, Txs, etc.)
1018func (hdb *HistoryDB) AddBlockSCData(blockData *common.BlockData) (err error) {
1019	txn, err := hdb.dbWrite.Beginx()
1020	if err != nil {
1021		return tracerr.Wrap(err)
1022	}
1023	defer func() {
1024		if err != nil {
1025			db.Rollback(txn)
1026		}
1027	}()
1028
1029	// Add block
1030	if err := hdb.addBlock(txn, &blockData.Block); err != nil {
1031		return tracerr.Wrap(err)
1032	}
1033
1034	// Add Coordinators
1035	if err := hdb.addCoordinators(txn, blockData.Auction.Coordinators); err != nil {
1036		return tracerr.Wrap(err)
1037	}
1038
1039	// Add Bids
1040	if err := hdb.addBids(txn, blockData.Auction.Bids); err != nil {
1041		return tracerr.Wrap(err)
1042	}
1043
1044	// Add Tokens
1045	if err := hdb.addTokens(txn, blockData.Rollup.AddedTokens); err != nil {
1046		return tracerr.Wrap(err)
1047	}
1048
1049	// Prepare user L1 txs to be added.
1050	// They must be added before the batch that will forge them (which can be in the same block)
1051	// and after the account that will be sent to (also can be in the same block).
1052	// Note: insert order is not relevant since item_id will be updated by a DB trigger when
1053	// the batch that forges those txs is inserted
1054	userL1s := make(map[common.BatchNum][]common.L1Tx)
1055	for i := range blockData.Rollup.L1UserTxs {
1056		batchThatForgesIsInTheBlock := false
1057		for _, batch := range blockData.Rollup.Batches {
1058			if batch.Batch.ForgeL1TxsNum != nil &&
1059				*batch.Batch.ForgeL1TxsNum == *blockData.Rollup.L1UserTxs[i].ToForgeL1TxsNum {
1060				// Tx is forged in this block. It's guaranteed that:
1061				// * the first batch of the block won't forge user L1 txs that have been added in this block
1062				// * batch nums are sequential therefore it's safe to add the tx at batch.BatchNum -1
1063				batchThatForgesIsInTheBlock = true
1064				addAtBatchNum := batch.Batch.BatchNum - 1
1065				userL1s[addAtBatchNum] = append(userL1s[addAtBatchNum], blockData.Rollup.L1UserTxs[i])
1066				break
1067			}
1068		}
1069		if !batchThatForgesIsInTheBlock {
1070			// User artificial batchNum 0 to add txs that are not forge in this block
1071			// after all the accounts of the block have been added
1072			userL1s[0] = append(userL1s[0], blockData.Rollup.L1UserTxs[i])
1073		}
1074	}
1075
1076	// Add Batches
1077	for i := range blockData.Rollup.Batches {
1078		batch := &blockData.Rollup.Batches[i]
1079
1080		// Add Batch: this will trigger an update on the DB
1081		// that will set the batch num of forged L1 txs in this batch
1082		if err = hdb.addBatch(txn, &batch.Batch); err != nil {
1083			return tracerr.Wrap(err)
1084		}
1085
1086		// Add accounts
1087		if err := hdb.addAccounts(txn, batch.CreatedAccounts); err != nil {
1088			return tracerr.Wrap(err)
1089		}
1090
1091		// Add accountBalances if it exists
1092		if err := hdb.addAccountUpdates(txn, batch.UpdatedAccounts); err != nil {
1093			return tracerr.Wrap(err)
1094		}
1095
1096		// Set the EffectiveAmount and EffectiveDepositAmount of all the
1097		// L1UserTxs that have been forged in this batch
1098		if err = hdb.setExtraInfoForgedL1UserTxs(txn, batch.L1UserTxs); err != nil {
1099			return tracerr.Wrap(err)
1100		}
1101
1102		// Add forged l1 coordinator Txs
1103		if err := hdb.addL1Txs(txn, batch.L1CoordinatorTxs); err != nil {
1104			return tracerr.Wrap(err)
1105		}
1106
1107		// Add l2 Txs
1108		if err := hdb.addL2Txs(txn, batch.L2Txs); err != nil {
1109			return tracerr.Wrap(err)
1110		}
1111
1112		// Add user L1 txs that will be forged in next batch
1113		if userlL1s, ok := userL1s[batch.Batch.BatchNum]; ok {
1114			if err := hdb.addL1Txs(txn, userlL1s); err != nil {
1115				return tracerr.Wrap(err)
1116			}
1117		}
1118
1119		// Add exit tree
1120		if err := hdb.addExitTree(txn, batch.ExitTree); err != nil {
1121			return tracerr.Wrap(err)
1122		}
1123	}
1124	// Add user L1 txs that won't be forged in this block
1125	if userL1sNotForgedInThisBlock, ok := userL1s[0]; ok {
1126		if err := hdb.addL1Txs(txn, userL1sNotForgedInThisBlock); err != nil {
1127			return tracerr.Wrap(err)
1128		}
1129	}
1130
1131	// Set SC Vars if there was an update
1132	if blockData.Rollup.Vars != nil {
1133		if err := hdb.setRollupVars(txn, blockData.Rollup.Vars); err != nil {
1134			return tracerr.Wrap(err)
1135		}
1136	}
1137	if blockData.Auction.Vars != nil {
1138		if err := hdb.setAuctionVars(txn, blockData.Auction.Vars); err != nil {
1139			return tracerr.Wrap(err)
1140		}
1141	}
1142	if blockData.WDelayer.Vars != nil {
1143		if err := hdb.setWDelayerVars(txn, blockData.WDelayer.Vars); err != nil {
1144			return tracerr.Wrap(err)
1145		}
1146	}
1147
1148	// Update withdrawals in exit tree table
1149	if err := hdb.updateExitTree(txn, blockData.Block.Num,
1150		blockData.Rollup.Withdrawals, blockData.WDelayer.Withdrawals); err != nil {
1151		return tracerr.Wrap(err)
1152	}
1153
1154	// Add Escape Hatch Withdrawals
1155	if err := hdb.addEscapeHatchWithdrawals(txn,
1156		blockData.WDelayer.EscapeHatchWithdrawals); err != nil {
1157		return tracerr.Wrap(err)
1158	}
1159
1160	// Add Buckets withdrawals updates
1161	if err := hdb.addBucketUpdates(txn, blockData.Rollup.UpdateBucketWithdraw); err != nil {
1162		return tracerr.Wrap(err)
1163	}
1164
1165	// Add Token exchange updates
1166	if err := hdb.addTokenExchanges(txn, blockData.Rollup.TokenExchanges); err != nil {
1167		return tracerr.Wrap(err)
1168	}
1169
1170	return tracerr.Wrap(txn.Commit())
1171}
1172
1173// AddAuctionVars insert auction vars into the DB
1174func (hdb *HistoryDB) AddAuctionVars(auctionVars *common.AuctionVariables) error {
1175	return tracerr.Wrap(meddler.Insert(hdb.dbWrite, "auction_vars", auctionVars))
1176}
1177
1178// GetTokensTest used to get tokens in a testing context
1179func (hdb *HistoryDB) GetTokensTest() ([]TokenWithUSD, error) {
1180	tokens := []*TokenWithUSD{}
1181	if err := meddler.QueryAll(
1182		hdb.dbRead, &tokens,
1183		"SELECT * FROM token ORDER BY token_id ASC",
1184	); err != nil {
1185		return nil, tracerr.Wrap(err)
1186	}
1187	if len(tokens) == 0 {
1188		return []TokenWithUSD{}, nil
1189	}
1190	return db.SlicePtrsToSlice(tokens).([]TokenWithUSD), nil
1191}
1192
1193const (
1194	// CreateAccountExtraFeePercentage is the multiplication factor over
1195	// the average fee for CreateAccount that is applied to obtain the
1196	// recommended fee for CreateAccount
1197	CreateAccountExtraFeePercentage float64 = 2.5
1198	// CreateAccountInternalExtraFeePercentage is the multiplication factor
1199	// over the average fee for CreateAccountInternal that is applied to
1200	// obtain the recommended fee for CreateAccountInternal
1201	CreateAccountInternalExtraFeePercentage float64 = 2.0
1202)
1203
1204// GetRecommendedFee returns the RecommendedFee information
1205func (hdb *HistoryDB) GetRecommendedFee(minFeeUSD, maxFeeUSD float64) (*common.RecommendedFee, error) {
1206	var recommendedFee common.RecommendedFee
1207	// Get total txs and the batch of the first selected tx of the last hour
1208	type totalTxsSinceBatchNum struct {
1209		TotalTxs      int             `meddler:"total_txs"`
1210		FirstBatchNum common.BatchNum `meddler:"batch_num"`
1211	}
1212	ttsbn := &totalTxsSinceBatchNum{}
1213	if err := meddler.QueryRow(
1214		hdb.dbRead, ttsbn, `SELECT COUNT(tx.*) as total_txs, 
1215			COALESCE (MIN(tx.batch_num), 0) as batch_num 
1216			FROM tx INNER JOIN block ON tx.eth_block_num = block.eth_block_num
1217			WHERE block.timestamp >= NOW() - INTERVAL '1 HOURS';`,
1218	); err != nil {
1219		return nil, tracerr.Wrap(err)
1220	}
1221	// Get the amount of batches and acumulated fees for the last hour
1222	type totalBatchesAndFee struct {
1223		TotalBatches int     `meddler:"total_batches"`
1224		TotalFees    float64 `meddler:"total_fees"`
1225	}
1226	tbf := &totalBatchesAndFee{}
1227	if err := meddler.QueryRow(
1228		hdb.dbRead, tbf, `SELECT COUNT(*) AS total_batches, 
1229			COALESCE (SUM(total_fees_usd), 0) AS total_fees FROM batch 
1230			WHERE batch_num > $1;`, ttsbn.FirstBatchNum,
1231	); err != nil {
1232		return nil, tracerr.Wrap(err)
1233	}
1234	// Update NodeInfo struct
1235	var avgTransactionFee float64
1236	if ttsbn.TotalTxs > 0 {
1237		avgTransactionFee = tbf.TotalFees / float64(ttsbn.TotalTxs)
1238	} else {
1239		avgTransactionFee = 0
1240	}
1241	recommendedFee.ExistingAccount = math.Min(maxFeeUSD,
1242		math.Max(avgTransactionFee, minFeeUSD))
1243	recommendedFee.CreatesAccount = math.Min(maxFeeUSD,
1244		math.Max(CreateAccountExtraFeePercentage*avgTransactionFee, minFeeUSD))
1245	recommendedFee.CreatesAccountInternal = math.Min(maxFeeUSD,
1246		math.Max(CreateAccountInternalExtraFeePercentage*avgTransactionFee, minFeeUSD))
1247	return &recommendedFee, nil
1248}