mirror of
https://github.com/arnaucube/hermez-node.git
synced 2026-02-07 03:16:45 +01:00
Feature/merge history l2 tables (#156)
* WIP rebase * Combine both SQL DBs * API and DB refactor
This commit is contained in:
@@ -6,16 +6,13 @@ import (
|
||||
"fmt"
|
||||
|
||||
ethCommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
"github.com/hermeznetwork/hermez-node/common"
|
||||
"github.com/hermeznetwork/hermez-node/db"
|
||||
"github.com/hermeznetwork/hermez-node/log"
|
||||
"github.com/iden3/go-iden3-crypto/babyjub"
|
||||
"github.com/jmoiron/sqlx"
|
||||
|
||||
//nolint:errcheck // driver for postgres DB
|
||||
_ "github.com/lib/pq"
|
||||
migrate "github.com/rubenv/sql-migrate"
|
||||
"github.com/russross/meddler"
|
||||
)
|
||||
|
||||
@@ -57,28 +54,8 @@ type BatchData struct {
|
||||
}
|
||||
|
||||
// NewHistoryDB initialize the DB
|
||||
func NewHistoryDB(port int, host, user, password, dbname string) (*HistoryDB, error) {
|
||||
// Connect to DB
|
||||
psqlconn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", host, port, user, password, dbname)
|
||||
hdb, err := sqlx.Connect("postgres", psqlconn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Init meddler
|
||||
db.InitMeddler()
|
||||
meddler.Default = meddler.PostgreSQL
|
||||
|
||||
// Run DB migrations
|
||||
migrations := &migrate.PackrMigrationSource{
|
||||
Box: packr.New("history-migrations", "./migrations"),
|
||||
}
|
||||
nMigrations, err := migrate.Exec(hdb.DB, "postgres", migrations, migrate.Up)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debug("HistoryDB applied ", nMigrations, " migrations for ", dbname, " database")
|
||||
|
||||
return &HistoryDB{hdb}, nil
|
||||
func NewHistoryDB(db *sqlx.DB) *HistoryDB {
|
||||
return &HistoryDB{db: db}
|
||||
}
|
||||
|
||||
// AddBlock insert a block into the DB
|
||||
@@ -328,8 +305,14 @@ func (hdb *HistoryDB) GetAccounts() ([]*common.Account, error) {
|
||||
return accs, err
|
||||
}
|
||||
|
||||
// AddL1Txs inserts L1 txs to the DB
|
||||
// AddL1Txs inserts L1 txs to the DB. USD and LoadAmountUSD will be set automatically before storing the tx.
|
||||
// If the tx is originated by a coordinator, BatchNum must be provided. If it's originated by a user,
|
||||
// BatchNum should be null, and the value will be setted by a trigger when a batch forges the tx.
|
||||
func (hdb *HistoryDB) AddL1Txs(l1txs []common.L1Tx) error { return hdb.addL1Txs(hdb.db, l1txs) }
|
||||
|
||||
// addL1Txs inserts L1 txs to the DB. USD and LoadAmountUSD will be set automatically before storing the tx.
|
||||
// If the tx is originated by a coordinator, BatchNum must be provided. If it's originated by a user,
|
||||
// BatchNum should be null, and the value will be setted by a trigger when a batch forges the tx.
|
||||
func (hdb *HistoryDB) addL1Txs(d meddler.DB, l1txs []common.L1Tx) error {
|
||||
txs := []common.Tx{}
|
||||
for _, tx := range l1txs {
|
||||
@@ -338,8 +321,10 @@ func (hdb *HistoryDB) addL1Txs(d meddler.DB, l1txs []common.L1Tx) error {
|
||||
return hdb.addTxs(d, txs)
|
||||
}
|
||||
|
||||
// AddL2Txs inserts L2 txs to the DB
|
||||
// AddL2Txs inserts L2 txs to the DB. USD and FeeUSD will be set automatically before storing the tx.
|
||||
func (hdb *HistoryDB) AddL2Txs(l2txs []common.L2Tx) error { return hdb.addL2Txs(hdb.db, l2txs) }
|
||||
|
||||
// addL2Txs inserts L2 txs to the DB. USD and FeeUSD will be set automatically before storing the tx.
|
||||
func (hdb *HistoryDB) addL2Txs(d meddler.DB, l2txs []common.L2Tx) error {
|
||||
txs := []common.Tx{}
|
||||
for _, tx := range l2txs {
|
||||
@@ -348,8 +333,6 @@ func (hdb *HistoryDB) addL2Txs(d meddler.DB, l2txs []common.L2Tx) error {
|
||||
return hdb.addTxs(d, txs)
|
||||
}
|
||||
|
||||
// AddTxs insert L1 txs into the DB
|
||||
func (hdb *HistoryDB) AddTxs(txs []common.Tx) error { return hdb.addTxs(hdb.db, txs) }
|
||||
func (hdb *HistoryDB) addTxs(d meddler.DB, txs []common.Tx) error {
|
||||
return db.BulkInsert(
|
||||
d,
|
||||
@@ -381,16 +364,6 @@ func (hdb *HistoryDB) addTxs(d meddler.DB, txs []common.Tx) error {
|
||||
)
|
||||
}
|
||||
|
||||
// SetBatchNumL1UserTxs sets the batchNum in all the L1UserTxs with toForgeL1TxsNum.
|
||||
func (hdb *HistoryDB) SetBatchNumL1UserTxs(toForgeL1TxsNum, batchNum int64) error {
|
||||
return hdb.setBatchNumL1UserTxs(hdb.db, toForgeL1TxsNum, batchNum)
|
||||
}
|
||||
func (hdb *HistoryDB) setBatchNumL1UserTxs(d meddler.DB, toForgeL1TxsNum, batchNum int64) error {
|
||||
_, err := d.Exec("UPDATE tx SET batch_num = $1 WHERE to_forge_l1_txs_num = $2 AND is_l1 = TRUE AND user_origin = TRUE;",
|
||||
batchNum, toForgeL1TxsNum)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetTxs returns a list of txs from the DB
|
||||
func (hdb *HistoryDB) GetTxs() ([]*common.Tx, error) {
|
||||
var txs []*common.Tx
|
||||
@@ -413,8 +386,10 @@ func (hdb *HistoryDB) GetHistoryTxs(
|
||||
}
|
||||
var query string
|
||||
var args []interface{}
|
||||
queryStr := `SELECT tx.*, tx.amount_f * token.usd AS current_usd,
|
||||
token.symbol, token.usd_update, block.timestamp, count(*) OVER() AS total_items FROM tx
|
||||
queryStr := `SELECT tx.*, token.token_id, token.eth_block_num AS token_block,
|
||||
token.eth_addr, token.name, token.symbol, token.decimals, token.usd,
|
||||
token.usd_update, block.timestamp, count(*) OVER() AS total_items
|
||||
FROM tx
|
||||
INNER JOIN token ON tx.token_id = token.token_id
|
||||
INNER JOIN block ON tx.eth_block_num = block.eth_block_num `
|
||||
// Apply filters
|
||||
@@ -513,17 +488,17 @@ func (hdb *HistoryDB) GetTx(txID common.TxID) (*common.Tx, error) {
|
||||
)
|
||||
}
|
||||
|
||||
// GetL1UserTxs gets L1 User Txs to be forged in a batch that will create an account
|
||||
// TODO: This is currently not used. Figure out if it should be used somewhere or removed.
|
||||
func (hdb *HistoryDB) GetL1UserTxs(toForgeL1TxsNum int64) ([]*common.Tx, error) {
|
||||
var txs []*common.Tx
|
||||
err := meddler.QueryAll(
|
||||
hdb.db, &txs,
|
||||
"SELECT * FROM tx WHERE to_forge_l1_txs_num = $1 AND is_l1 = TRUE AND user_origin = TRUE;",
|
||||
toForgeL1TxsNum,
|
||||
)
|
||||
return txs, err
|
||||
}
|
||||
// // GetL1UserTxs gets L1 User Txs to be forged in a batch that will create an account
|
||||
// // TODO: This is currently not used. Figure out if it should be used somewhere or removed.
|
||||
// func (hdb *HistoryDB) GetL1UserTxs(toForgeL1TxsNum int64) ([]*common.Tx, error) {
|
||||
// var txs []*common.Tx
|
||||
// err := meddler.QueryAll(
|
||||
// hdb.db, &txs,
|
||||
// "SELECT * FROM tx WHERE to_forge_l1_txs_num = $1 AND is_l1 = TRUE AND user_origin = TRUE;",
|
||||
// toForgeL1TxsNum,
|
||||
// )
|
||||
// return txs, err
|
||||
// }
|
||||
|
||||
// TODO: Think about chaning all the queries that return a last value, to queries that return the next valid value.
|
||||
|
||||
@@ -586,11 +561,15 @@ func (hdb *HistoryDB) AddBlockSCData(blockData *BlockData) (err error) {
|
||||
|
||||
// Add Batches
|
||||
for _, batch := range blockData.Batches {
|
||||
// Add Batch: this will trigger an update on the DB
|
||||
// that will set the batch num of forged L1 txs in this batch
|
||||
err = hdb.addBatch(txn, batch.Batch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add unforged l1 Txs
|
||||
if batch.L1Batch {
|
||||
err = hdb.setBatchNumL1UserTxs(txn, batch.Batch.ForgeL1TxsNum, int64(batch.Batch.BatchNum))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(batch.L1CoordinatorTxs) > 0 {
|
||||
err = hdb.addL1Txs(txn, batch.L1CoordinatorTxs)
|
||||
if err != nil {
|
||||
@@ -623,19 +602,8 @@ func (hdb *HistoryDB) AddBlockSCData(blockData *BlockData) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Add Batch
|
||||
err = hdb.addBatch(txn, batch.Batch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: INSERT CONTRACTS VARS
|
||||
}
|
||||
|
||||
return txn.Commit()
|
||||
}
|
||||
|
||||
// Close frees the resources used by HistoryDB
|
||||
func (hdb *HistoryDB) Close() error {
|
||||
return hdb.db.Close()
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package historydb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"testing"
|
||||
@@ -9,6 +8,8 @@ import (
|
||||
|
||||
ethCommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/hermeznetwork/hermez-node/common"
|
||||
dbUtils "github.com/hermeznetwork/hermez-node/db"
|
||||
"github.com/hermeznetwork/hermez-node/log"
|
||||
"github.com/hermeznetwork/hermez-node/test"
|
||||
"github.com/iden3/go-iden3-crypto/babyjub"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -26,17 +27,20 @@ var historyDB *HistoryDB
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
// init DB
|
||||
var err error
|
||||
pass := os.Getenv("POSTGRES_PASS")
|
||||
historyDB, err = NewHistoryDB(5432, "localhost", "hermez", pass, "history")
|
||||
db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
historyDB = NewHistoryDB(db)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Run tests
|
||||
result := m.Run()
|
||||
// Close DB
|
||||
if err := historyDB.Close(); err != nil {
|
||||
fmt.Println("Error closing the history DB:", err)
|
||||
if err := db.Close(); err != nil {
|
||||
log.Error("Error closing the history DB:", err)
|
||||
}
|
||||
os.Exit(result)
|
||||
}
|
||||
@@ -145,15 +149,6 @@ func TestTokens(t *testing.T) {
|
||||
tokens := test.GenTokens(nTokens, blocks)
|
||||
err := historyDB.AddTokens(tokens)
|
||||
assert.NoError(t, err)
|
||||
// Update price of generated tokens without price
|
||||
for i := 0; i < len(tokens); i++ {
|
||||
if tokens[i].USD == 0 {
|
||||
value := 3.33 + float64(i)
|
||||
tokens[i].USD = value
|
||||
err := historyDB.UpdateTokenValue(tokens[i].TokenID, value)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
// Fetch tokens
|
||||
fetchedTokens, err := historyDB.GetTokens()
|
||||
assert.NoError(t, err)
|
||||
@@ -166,7 +161,11 @@ func TestTokens(t *testing.T) {
|
||||
assert.Equal(t, tokens[i].Name, token.Name)
|
||||
assert.Equal(t, tokens[i].Symbol, token.Symbol)
|
||||
assert.Equal(t, tokens[i].USD, token.USD)
|
||||
assert.Greater(t, int64(1*time.Second), int64(time.Since(token.USDUpdate)))
|
||||
if token.USDUpdate != nil {
|
||||
assert.Greater(t, int64(1*time.Second), int64(time.Since(*token.USDUpdate)))
|
||||
} else {
|
||||
assert.Equal(t, tokens[i].USDUpdate, token.USDUpdate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,12 +204,8 @@ func TestTxs(t *testing.T) {
|
||||
// Prepare blocks in the DB
|
||||
blocks := setTestBlocks(fromBlock, toBlock)
|
||||
// Generate fake tokens
|
||||
const nTokens = 5
|
||||
const tokenValue = 1.23456
|
||||
const nTokens = 500
|
||||
tokens := test.GenTokens(nTokens, blocks)
|
||||
for i := 0; i < len(tokens); i++ {
|
||||
tokens[i].USD = tokenValue
|
||||
}
|
||||
err := historyDB.AddTokens(tokens)
|
||||
assert.NoError(t, err)
|
||||
// Generate fake batches
|
||||
@@ -224,60 +219,17 @@ func TestTxs(t *testing.T) {
|
||||
err = historyDB.AddAccounts(accs)
|
||||
assert.NoError(t, err)
|
||||
// Generate fake L1 txs
|
||||
const nL1s = 30
|
||||
const nL1s = 64
|
||||
_, l1txs := test.GenL1Txs(0, nL1s, 0, nil, accs, tokens, blocks, batches)
|
||||
err = historyDB.AddL1Txs(l1txs)
|
||||
assert.NoError(t, err)
|
||||
// Generate fake L2 txs
|
||||
const nL2s = 20
|
||||
const nL2s = 2048 - nL1s
|
||||
_, l2txs := test.GenL2Txs(0, nL2s, 0, nil, accs, tokens, blocks, batches)
|
||||
err = historyDB.AddL2Txs(l2txs)
|
||||
assert.NoError(t, err)
|
||||
// Compare fetched txs vs generated txs.
|
||||
for i := 0; i < len(l1txs); i++ {
|
||||
tx := l1txs[i].Tx()
|
||||
fetchedTx, err := historyDB.GetTx(tx.TxID)
|
||||
assert.NoError(t, err)
|
||||
tx.USD = tokenValue * tx.AmountFloat
|
||||
if fetchedTx.USD > tx.USD {
|
||||
assert.Less(t, 0.999, tx.USD/fetchedTx.USD)
|
||||
} else {
|
||||
assert.Less(t, 0.999, fetchedTx.USD/tx.USD)
|
||||
}
|
||||
tx.LoadAmountUSD = tokenValue * tx.LoadAmountFloat
|
||||
if fetchedTx.LoadAmountUSD > tx.LoadAmountUSD {
|
||||
assert.Less(t, 0.999, tx.LoadAmountUSD/fetchedTx.LoadAmountUSD)
|
||||
} else {
|
||||
assert.Less(t, 0.999, fetchedTx.LoadAmountUSD/tx.LoadAmountUSD)
|
||||
}
|
||||
tx.LoadAmountUSD = 0
|
||||
tx.USD = 0
|
||||
fetchedTx.LoadAmountUSD = 0
|
||||
fetchedTx.USD = 0
|
||||
assert.Equal(t, tx, fetchedTx)
|
||||
}
|
||||
for i := 0; i < len(l2txs); i++ {
|
||||
tx := l2txs[i].Tx()
|
||||
fetchedTx, err := historyDB.GetTx(tx.TxID)
|
||||
assert.NoError(t, err)
|
||||
tx.USD = tokenValue * tx.AmountFloat
|
||||
if fetchedTx.USD > tx.USD {
|
||||
assert.Less(t, 0.999, tx.USD/fetchedTx.USD)
|
||||
} else {
|
||||
assert.Less(t, 0.999, fetchedTx.USD/tx.USD)
|
||||
}
|
||||
tx.FeeUSD = tx.USD * tx.Fee.Percentage()
|
||||
if fetchedTx.FeeUSD > tx.FeeUSD {
|
||||
assert.Less(t, 0.999, tx.FeeUSD/fetchedTx.FeeUSD)
|
||||
} else if fetchedTx.FeeUSD < tx.FeeUSD {
|
||||
assert.Less(t, 0.999, fetchedTx.FeeUSD/tx.FeeUSD)
|
||||
}
|
||||
tx.FeeUSD = 0
|
||||
tx.USD = 0
|
||||
fetchedTx.FeeUSD = 0
|
||||
fetchedTx.USD = 0
|
||||
assert.Equal(t, tx, fetchedTx)
|
||||
}
|
||||
fetchAndAssertTxs(t, l1txs, l2txs)
|
||||
// Test trigger: L1 integrity
|
||||
// from_eth_addr can't be null
|
||||
l1txs[0].FromEthAddr = ethCommon.Address{}
|
||||
@@ -304,23 +256,75 @@ func TestTxs(t *testing.T) {
|
||||
l2txs[0].Nonce = 0
|
||||
err = historyDB.AddL2Txs(l2txs)
|
||||
assert.Error(t, err)
|
||||
// Test trigger: forge L1 txs
|
||||
// add next batch to DB
|
||||
batchNum, toForgeL1TxsNum := test.GetNextToForgeNumAndBatch(batches)
|
||||
batch := batches[0]
|
||||
batch.BatchNum = batchNum
|
||||
batch.ForgeL1TxsNum = toForgeL1TxsNum
|
||||
assert.NoError(t, historyDB.AddBatch(&batch)) // This should update nL1s / 2 rows
|
||||
// Set batch num in txs that should have been marked as forged in the DB
|
||||
for i := 0; i < len(l1txs); i++ {
|
||||
fetchedTx, err := historyDB.GetTx(l1txs[i].TxID)
|
||||
assert.NoError(t, err)
|
||||
if l1txs[i].ToForgeL1TxsNum == toForgeL1TxsNum {
|
||||
assert.Equal(t, batchNum, *fetchedTx.BatchNum)
|
||||
} else {
|
||||
if fetchedTx.BatchNum != nil {
|
||||
assert.NotEqual(t, batchNum, *fetchedTx.BatchNum)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test helper functions for Synchronizer
|
||||
txs, err := historyDB.GetL1UserTxs(2)
|
||||
// GetLastTxsPosition
|
||||
expectedPosition := -1
|
||||
var choosenToForgeL1TxsNum int64 = -1
|
||||
for _, tx := range l1txs {
|
||||
if choosenToForgeL1TxsNum == -1 && tx.ToForgeL1TxsNum > 0 {
|
||||
choosenToForgeL1TxsNum = tx.ToForgeL1TxsNum
|
||||
expectedPosition = tx.Position
|
||||
} else if choosenToForgeL1TxsNum == tx.ToForgeL1TxsNum && expectedPosition < tx.Position {
|
||||
expectedPosition = tx.Position
|
||||
}
|
||||
}
|
||||
position, err := historyDB.GetLastTxsPosition(choosenToForgeL1TxsNum)
|
||||
assert.NoError(t, err)
|
||||
assert.NotZero(t, len(txs))
|
||||
position, err := historyDB.GetLastTxsPosition(2)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 22, position)
|
||||
// Test Update L1 TX Batch_num
|
||||
assert.Equal(t, common.BatchNum(0), txs[0].BatchNum)
|
||||
txs[0].BatchNum = common.BatchNum(1)
|
||||
// err = historyDB.UpdateTxsBatchNum(txs)
|
||||
err = historyDB.SetBatchNumL1UserTxs(2, 1)
|
||||
assert.NoError(t, err)
|
||||
txs, err = historyDB.GetL1UserTxs(2)
|
||||
assert.NoError(t, err)
|
||||
assert.NotZero(t, len(txs))
|
||||
assert.Equal(t, common.BatchNum(1), txs[0].BatchNum)
|
||||
assert.Equal(t, expectedPosition, position)
|
||||
|
||||
// GetL1UserTxs: not needed? tests were broken
|
||||
// txs, err := historyDB.GetL1UserTxs(2)
|
||||
// assert.NoError(t, err)
|
||||
// assert.NotZero(t, len(txs))
|
||||
// assert.NoError(t, err)
|
||||
// assert.Equal(t, 22, position)
|
||||
// // Test Update L1 TX Batch_num
|
||||
// assert.Equal(t, common.BatchNum(0), txs[0].BatchNum)
|
||||
// txs[0].BatchNum = common.BatchNum(1)
|
||||
// txs, err = historyDB.GetL1UserTxs(2)
|
||||
// assert.NoError(t, err)
|
||||
// assert.NotZero(t, len(txs))
|
||||
// assert.Equal(t, common.BatchNum(1), txs[0].BatchNum)
|
||||
}
|
||||
|
||||
func fetchAndAssertTxs(t *testing.T, l1txs []common.L1Tx, l2txs []common.L2Tx) {
|
||||
for i := 0; i < len(l1txs); i++ {
|
||||
tx := l1txs[i].Tx()
|
||||
fetchedTx, err := historyDB.GetTx(tx.TxID)
|
||||
assert.NoError(t, err)
|
||||
test.AssertUSD(t, tx.USD, fetchedTx.USD)
|
||||
test.AssertUSD(t, tx.LoadAmountUSD, fetchedTx.LoadAmountUSD)
|
||||
assert.Equal(t, tx, fetchedTx)
|
||||
}
|
||||
for i := 0; i < len(l2txs); i++ {
|
||||
tx := l2txs[i].Tx()
|
||||
fetchedTx, err := historyDB.GetTx(tx.TxID)
|
||||
tx.TokenID = fetchedTx.TokenID
|
||||
assert.NoError(t, err)
|
||||
test.AssertUSD(t, fetchedTx.USD, tx.USD)
|
||||
test.AssertUSD(t, fetchedTx.FeeUSD, tx.FeeUSD)
|
||||
assert.Equal(t, tx, fetchedTx)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExitTree(t *testing.T) {
|
||||
|
||||
@@ -1,209 +0,0 @@
|
||||
-- +migrate Up
|
||||
CREATE TABLE block (
|
||||
eth_block_num BIGINT PRIMARY KEY,
|
||||
timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL,
|
||||
hash BYTEA NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE coordinator (
|
||||
forger_addr BYTEA NOT NULL,
|
||||
eth_block_num BIGINT NOT NULL REFERENCES block (eth_block_num) ON DELETE CASCADE,
|
||||
withdraw_addr BYTEA NOT NULL,
|
||||
url VARCHAR(200) NOT NULL,
|
||||
PRIMARY KEY (forger_addr, eth_block_num)
|
||||
);
|
||||
|
||||
CREATE TABLE batch (
|
||||
batch_num BIGINT PRIMARY KEY,
|
||||
eth_block_num BIGINT REFERENCES block (eth_block_num) ON DELETE CASCADE,
|
||||
forger_addr BYTEA NOT NULL, -- fake foreign key for coordinator
|
||||
fees_collected BYTEA NOT NULL,
|
||||
state_root BYTEA NOT NULL,
|
||||
num_accounts BIGINT NOT NULL,
|
||||
exit_root BYTEA NOT NULL,
|
||||
forge_l1_txs_num BIGINT,
|
||||
slot_num BIGINT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE exit_tree (
|
||||
batch_num BIGINT REFERENCES batch (batch_num) ON DELETE CASCADE,
|
||||
account_idx BIGINT,
|
||||
merkle_proof BYTEA NOT NULL,
|
||||
balance BYTEA NOT NULL,
|
||||
instant_withdrawn BIGINT REFERENCES batch (batch_num) ON DELETE SET NULL,
|
||||
delayed_withdraw_request BIGINT REFERENCES batch (batch_num) ON DELETE SET NULL,
|
||||
delayed_withdrawn BIGINT REFERENCES batch (batch_num) ON DELETE SET NULL,
|
||||
PRIMARY KEY (batch_num, account_idx)
|
||||
);
|
||||
|
||||
CREATE TABLE bid (
|
||||
slot_num BIGINT NOT NULL,
|
||||
bid_value BYTEA NOT NULL,
|
||||
eth_block_num BIGINT NOT NULL REFERENCES block (eth_block_num) ON DELETE CASCADE,
|
||||
forger_addr BYTEA NOT NULL, -- fake foreign key for coordinator
|
||||
PRIMARY KEY (slot_num, bid_value)
|
||||
);
|
||||
|
||||
CREATE TABLE token (
|
||||
token_id INT PRIMARY KEY,
|
||||
eth_block_num BIGINT NOT NULL REFERENCES block (eth_block_num) ON DELETE CASCADE,
|
||||
eth_addr BYTEA UNIQUE NOT NULL,
|
||||
name VARCHAR(20) NOT NULL,
|
||||
symbol VARCHAR(10) NOT NULL,
|
||||
decimals INT NOT NULL,
|
||||
usd NUMERIC,
|
||||
usd_update TIMESTAMP
|
||||
);
|
||||
|
||||
-- +migrate StatementBegin
|
||||
CREATE FUNCTION set_token_usd_update()
|
||||
RETURNS TRIGGER
|
||||
AS
|
||||
$BODY$
|
||||
BEGIN
|
||||
IF NEW."usd" IS NOT NULL AND NEW."usd_update" IS NULL THEN
|
||||
NEW."usd_update" = timezone('utc', now());
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$BODY$
|
||||
LANGUAGE plpgsql;
|
||||
-- +migrate StatementEnd
|
||||
CREATE TRIGGER trigger_token_usd_update BEFORE UPDATE OR INSERT ON token
|
||||
FOR EACH ROW EXECUTE PROCEDURE set_token_usd_update();
|
||||
|
||||
CREATE TABLE tx (
|
||||
-- Generic TX
|
||||
is_l1 BOOLEAN NOT NULL,
|
||||
id BYTEA PRIMARY KEY,
|
||||
type VARCHAR(40) NOT NULL,
|
||||
position INT NOT NULL,
|
||||
from_idx BIGINT NOT NULL,
|
||||
to_idx BIGINT NOT NULL,
|
||||
amount BYTEA NOT NULL,
|
||||
amount_f NUMERIC NOT NULL,
|
||||
token_id INT NOT NULL REFERENCES token (token_id),
|
||||
amount_usd NUMERIC, -- Value of the amount in USD at the moment the tx was inserted in the DB
|
||||
batch_num BIGINT REFERENCES batch (batch_num) ON DELETE SET NULL, -- Can be NULL in the case of L1 txs that are on the queue but not forged yet.
|
||||
eth_block_num BIGINT NOT NULL REFERENCES block (eth_block_num) ON DELETE CASCADE,
|
||||
-- L1
|
||||
to_forge_l1_txs_num BIGINT,
|
||||
user_origin BOOLEAN,
|
||||
from_eth_addr BYTEA,
|
||||
from_bjj BYTEA,
|
||||
load_amount BYTEA,
|
||||
load_amount_f NUMERIC,
|
||||
load_amount_usd NUMERIC,
|
||||
-- L2
|
||||
fee INT,
|
||||
fee_usd NUMERIC,
|
||||
nonce BIGINT
|
||||
);
|
||||
|
||||
CREATE INDEX tx_order ON tx (batch_num, position);
|
||||
|
||||
-- +migrate StatementBegin
|
||||
CREATE FUNCTION set_tx()
|
||||
RETURNS TRIGGER
|
||||
AS
|
||||
$BODY$
|
||||
DECLARE token_value NUMERIC := (SELECT usd FROM token WHERE token_id = NEW.token_id);
|
||||
BEGIN
|
||||
-- Validate L1/L2 constrains
|
||||
IF NEW.is_l1 AND (( -- L1 mandatory fields
|
||||
NEW.user_origin IS NULL OR
|
||||
NEW.from_eth_addr IS NULL OR
|
||||
NEW.from_bjj IS NULL OR
|
||||
NEW.load_amount IS NULL OR
|
||||
NEW.load_amount_f IS NULL
|
||||
) OR (NOT NEW.user_origin AND NEW.batch_num IS NULL)) THEN -- If is Coordinator L1, must include batch_num
|
||||
RAISE EXCEPTION 'Invalid L1 tx.';
|
||||
ELSIF NOT NEW.is_l1 THEN
|
||||
IF NEW.fee IS NULL THEN
|
||||
NEW.fee = (SELECT 0);
|
||||
END IF;
|
||||
IF NEW.batch_num IS NULL OR NEW.nonce IS NULL THEN
|
||||
RAISE EXCEPTION 'Invalid L2 tx.';
|
||||
END IF;
|
||||
END IF;
|
||||
-- If is L2, add token_id
|
||||
IF NEW.token_id IS NULL THEN
|
||||
NEW."token_id" = (SELECT token_id FROM account WHERE idx = NEW."from_idx");
|
||||
END IF;
|
||||
-- Set value_usd
|
||||
NEW."amount_usd" = (SELECT token_value * NEW.amount_f);
|
||||
NEW."load_amount_usd" = (SELECT token_value * NEW.load_amount_f);
|
||||
IF NOT NEW.is_l1 THEN
|
||||
NEW."fee_usd" = (SELECT token_value * NEW.amount_f * CASE
|
||||
WHEN NEW.fee = 0 THEN 0
|
||||
WHEN NEW.fee >= 1 AND NEW.fee <= 32 THEN POWER(10,-24+(NEW.fee::float/2))
|
||||
WHEN NEW.fee >= 33 AND NEW.fee <= 223 THEN POWER(10,-8+(0.041666666666667*(NEW.fee::float-32)))
|
||||
WHEN NEW.fee >= 224 AND NEW.fee <= 255 THEN POWER(10,NEW.fee-224) END);
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$BODY$
|
||||
LANGUAGE plpgsql;
|
||||
-- +migrate StatementEnd
|
||||
CREATE TRIGGER trigger_set_tx BEFORE INSERT ON tx
|
||||
FOR EACH ROW EXECUTE PROCEDURE set_tx();
|
||||
|
||||
-- +migrate StatementBegin
|
||||
CREATE FUNCTION forge_l1_user_txs()
|
||||
RETURNS TRIGGER
|
||||
AS
|
||||
$BODY$
|
||||
BEGIN
|
||||
IF NEW.forge_l1_txs_num IS NOT NULL THEN
|
||||
UPDATE tx
|
||||
SET batch_num = NEW.batch_num
|
||||
WHERE user_origin AND NEW.forge_l1_txs_num = to_forge_l1_txs_num;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$BODY$
|
||||
LANGUAGE plpgsql;
|
||||
-- +migrate StatementEnd
|
||||
CREATE TRIGGER trigger_forge_l1_txs AFTER INSERT ON batch
|
||||
FOR EACH ROW EXECUTE PROCEDURE forge_l1_user_txs();
|
||||
|
||||
CREATE TABLE account (
|
||||
idx BIGINT PRIMARY KEY,
|
||||
token_id INT NOT NULL REFERENCES token (token_id) ON DELETE CASCADE,
|
||||
batch_num BIGINT NOT NULL REFERENCES batch (batch_num) ON DELETE CASCADE,
|
||||
bjj BYTEA NOT NULL,
|
||||
eth_addr BYTEA NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE rollup_vars (
|
||||
eth_block_num BIGINT PRIMARY KEY REFERENCES block (eth_block_num) ON DELETE CASCADE,
|
||||
forge_l1_timeout BYTEA NOT NULL,
|
||||
fee_l1_user_tx BYTEA NOT NULL,
|
||||
fee_add_token BYTEA NOT NULL,
|
||||
tokens_hez BYTEA NOT NULL,
|
||||
governance BYTEA NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE consensus_vars (
|
||||
eth_block_num BIGINT PRIMARY KEY REFERENCES block (eth_block_num) ON DELETE CASCADE,
|
||||
slot_deadline INT NOT NULL,
|
||||
close_auction_slots INT NOT NULL,
|
||||
open_auction_slots INT NOT NULL,
|
||||
min_bid_slots VARCHAR(200) NOT NULL,
|
||||
outbidding INT NOT NULL,
|
||||
donation_address BYTEA NOT NULL,
|
||||
governance_address BYTEA NOT NULL,
|
||||
allocation_ratio VARCHAR(200)
|
||||
);
|
||||
|
||||
-- +migrate Down
|
||||
DROP TABLE consensus_vars;
|
||||
DROP TABLE rollup_vars;
|
||||
DROP TABLE account;
|
||||
DROP TABLE tx;
|
||||
DROP TABLE token;
|
||||
DROP TABLE bid;
|
||||
DROP TABLE exit_tree;
|
||||
DROP TABLE batch;
|
||||
DROP TABLE coordinator;
|
||||
DROP TABLE block;
|
||||
@@ -13,34 +13,38 @@ import (
|
||||
// required by the API, and extracted by joining block and token tables
|
||||
type HistoryTx struct {
|
||||
// Generic
|
||||
IsL1 bool `meddler:"is_l1"`
|
||||
TxID common.TxID `meddler:"id"`
|
||||
Type common.TxType `meddler:"type"`
|
||||
Position int `meddler:"position"`
|
||||
FromIdx common.Idx `meddler:"from_idx"`
|
||||
ToIdx common.Idx `meddler:"to_idx"`
|
||||
Amount *big.Int `meddler:"amount,bigint"`
|
||||
AmountFloat float64 `meddler:"amount_f"`
|
||||
TokenID common.TokenID `meddler:"token_id"`
|
||||
USD float64 `meddler:"amount_usd,zeroisnull"`
|
||||
BatchNum common.BatchNum `meddler:"batch_num,zeroisnull"` // batchNum in which this tx was forged. If the tx is L2, this must be != 0
|
||||
EthBlockNum int64 `meddler:"eth_block_num"` // Ethereum Block Number in which this L1Tx was added to the queue
|
||||
IsL1 bool `meddler:"is_l1"`
|
||||
TxID common.TxID `meddler:"id"`
|
||||
Type common.TxType `meddler:"type"`
|
||||
Position int `meddler:"position"`
|
||||
FromIdx common.Idx `meddler:"from_idx"`
|
||||
ToIdx common.Idx `meddler:"to_idx"`
|
||||
Amount *big.Int `meddler:"amount,bigint"`
|
||||
AmountFloat float64 `meddler:"amount_f"`
|
||||
HistoricUSD *float64 `meddler:"amount_usd"`
|
||||
BatchNum *common.BatchNum `meddler:"batch_num"` // batchNum in which this tx was forged. If the tx is L2, this must be != 0
|
||||
EthBlockNum int64 `meddler:"eth_block_num"` // Ethereum Block Number in which this L1Tx was added to the queue
|
||||
// L1
|
||||
ToForgeL1TxsNum int64 `meddler:"to_forge_l1_txs_num"` // toForgeL1TxsNum in which the tx was forged / will be forged
|
||||
UserOrigin bool `meddler:"user_origin"` // true if the tx was originated by a user, false if it was aoriginated by a coordinator. Note that this differ from the spec for implementation simplification purpposes
|
||||
FromEthAddr ethCommon.Address `meddler:"from_eth_addr"`
|
||||
FromBJJ *babyjub.PublicKey `meddler:"from_bjj"`
|
||||
LoadAmount *big.Int `meddler:"load_amount,bigintnull"`
|
||||
LoadAmountFloat float64 `meddler:"load_amount_f"`
|
||||
LoadAmountUSD float64 `meddler:"load_amount_usd,zeroisnull"`
|
||||
ToForgeL1TxsNum int64 `meddler:"to_forge_l1_txs_num"` // toForgeL1TxsNum in which the tx was forged / will be forged
|
||||
UserOrigin bool `meddler:"user_origin"` // true if the tx was originated by a user, false if it was aoriginated by a coordinator. Note that this differ from the spec for implementation simplification purpposes
|
||||
FromEthAddr ethCommon.Address `meddler:"from_eth_addr"`
|
||||
FromBJJ *babyjub.PublicKey `meddler:"from_bjj"`
|
||||
LoadAmount *big.Int `meddler:"load_amount,bigintnull"`
|
||||
LoadAmountFloat *float64 `meddler:"load_amount_f"`
|
||||
HistoricLoadAmountUSD *float64 `meddler:"load_amount_usd"`
|
||||
// L2
|
||||
Fee common.FeeSelector `meddler:"fee,zeroisnull"`
|
||||
FeeUSD float64 `meddler:"fee_usd,zeroisnull"`
|
||||
Nonce common.Nonce `meddler:"nonce,zeroisnull"`
|
||||
Fee *common.FeeSelector `meddler:"fee"`
|
||||
HistoricFeeUSD *float64 `meddler:"fee_usd"`
|
||||
Nonce *common.Nonce `meddler:"nonce"`
|
||||
// API extras
|
||||
Timestamp time.Time `meddler:"timestamp,utctime"`
|
||||
TokenSymbol string `meddler:"symbol"`
|
||||
CurrentUSD float64 `meddler:"current_usd"`
|
||||
USDUpdate time.Time `meddler:"usd_update,utctime"`
|
||||
TotalItems int `meddler:"total_items"`
|
||||
Timestamp time.Time `meddler:"timestamp,utctime"`
|
||||
TotalItems int `meddler:"total_items"`
|
||||
TokenID common.TokenID `meddler:"token_id"`
|
||||
TokenEthBlockNum int64 `meddler:"token_block"`
|
||||
TokenEthAddr ethCommon.Address `meddler:"eth_addr"`
|
||||
TokenName string `meddler:"name"`
|
||||
TokenSymbol string `meddler:"symbol"`
|
||||
TokenDecimals uint64 `meddler:"decimals"`
|
||||
TokenUSD *float64 `meddler:"usd"`
|
||||
TokenUSDUpdate *time.Time `meddler:"usd_update"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user