mirror of
https://github.com/arnaucube/hermez-node.git
synced 2026-02-07 03:16:45 +01:00
Add HistoryDB SQL triggers (#125)
This commit is contained in:
@@ -47,6 +47,19 @@ func (hdb *HistoryDB) AddBlock(block *common.Block) error {
|
||||
return meddler.Insert(hdb.db, "block", block)
|
||||
}
|
||||
|
||||
// AddBlocks inserts blocks into the DB
|
||||
func (hdb *HistoryDB) AddBlocks(blocks []common.Block) error {
|
||||
return db.BulkInsert(
|
||||
hdb.db,
|
||||
`INSERT INTO block (
|
||||
eth_block_num,
|
||||
timestamp,
|
||||
hash
|
||||
) VALUES %s;`,
|
||||
blocks[:],
|
||||
)
|
||||
}
|
||||
|
||||
// GetBlock retrieve a block from the DB, given a block number
|
||||
func (hdb *HistoryDB) GetBlock(blockNum int64) (*common.Block, error) {
|
||||
block := &common.Block{}
|
||||
@@ -77,8 +90,8 @@ func (hdb *HistoryDB) GetLastBlock() (*common.Block, error) {
|
||||
return block, err
|
||||
}
|
||||
|
||||
// addBatches insert Bids into the DB
|
||||
func (hdb *HistoryDB) addBatches(batches []common.Batch) error {
|
||||
// AddBatches insert Bids into the DB
|
||||
func (hdb *HistoryDB) AddBatches(batches []common.Batch) error {
|
||||
return db.BulkInsert(
|
||||
hdb.db,
|
||||
`INSERT INTO batch (
|
||||
@@ -129,7 +142,7 @@ func (hdb *HistoryDB) Reorg(lastValidBlock int64) error {
|
||||
|
||||
// SyncRollup stores all the data that can be changed / added on a block in the Rollup SC
|
||||
func (hdb *HistoryDB) SyncRollup(
|
||||
blockNum int64,
|
||||
blockNum uint64,
|
||||
l1txs []common.L1Tx,
|
||||
l2txs []common.L2Tx,
|
||||
registeredAccounts []common.Account,
|
||||
@@ -140,7 +153,7 @@ func (hdb *HistoryDB) SyncRollup(
|
||||
vars *common.RollupVars,
|
||||
) error {
|
||||
// TODO: make all in a single DB commit
|
||||
if err := hdb.addBatches(batches); err != nil {
|
||||
if err := hdb.AddBatches(batches); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -148,7 +161,7 @@ func (hdb *HistoryDB) SyncRollup(
|
||||
|
||||
// SyncPoD stores all the data that can be changed / added on a block in the PoD SC
|
||||
func (hdb *HistoryDB) SyncPoD(
|
||||
blockNum int64,
|
||||
blockNum uint64,
|
||||
bids []common.Bid,
|
||||
coordinators []common.Coordinator,
|
||||
vars *common.AuctionVars,
|
||||
@@ -166,17 +179,154 @@ func (hdb *HistoryDB) addBids(bids []common.Bid) error {
|
||||
)
|
||||
}
|
||||
|
||||
// GetBidsBySlot return the bids for a specific slot
|
||||
func (hdb *HistoryDB) GetBidsBySlot(slotNum common.SlotNum) ([]*common.Bid, error) {
|
||||
// GetBids return the bids
|
||||
func (hdb *HistoryDB) GetBids() ([]*common.Bid, error) {
|
||||
var bids []*common.Bid
|
||||
err := meddler.QueryAll(
|
||||
hdb.db, &bids,
|
||||
"SELECT * FROM bid WHERE $1 = slot_num;",
|
||||
slotNum,
|
||||
"SELECT * FROM bid;",
|
||||
)
|
||||
return bids, err
|
||||
}
|
||||
|
||||
// AddToken insert a token into the DB
|
||||
func (hdb *HistoryDB) AddToken(token *common.Token) error {
|
||||
return meddler.Insert(hdb.db, "token", token)
|
||||
}
|
||||
|
||||
// AddTokens insert tokens into the DB
|
||||
func (hdb *HistoryDB) AddTokens(tokens []common.Token) error {
|
||||
return db.BulkInsert(
|
||||
hdb.db,
|
||||
`INSERT INTO token (
|
||||
token_id,
|
||||
eth_block_num,
|
||||
eth_addr,
|
||||
name,
|
||||
symbol,
|
||||
decimals,
|
||||
usd,
|
||||
usd_update
|
||||
) VALUES %s;`,
|
||||
tokens[:],
|
||||
)
|
||||
}
|
||||
|
||||
// UpdateTokenValue updates the USD value of a token
|
||||
func (hdb *HistoryDB) UpdateTokenValue(tokenID common.TokenID, value float32) error {
|
||||
_, err := hdb.db.Exec(
|
||||
"UPDATE token SET usd = $1 WHERE token_id = $2;",
|
||||
value, tokenID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetTokens returns a list of tokens from the DB
|
||||
func (hdb *HistoryDB) GetTokens() ([]*common.Token, error) {
|
||||
var tokens []*common.Token
|
||||
err := meddler.QueryAll(
|
||||
hdb.db, &tokens,
|
||||
"SELECT * FROM token ORDER BY token_id;",
|
||||
)
|
||||
return tokens, err
|
||||
}
|
||||
|
||||
// AddAccounts insert accounts into the DB
|
||||
func (hdb *HistoryDB) AddAccounts(accounts []common.Account) error {
|
||||
return db.BulkInsert(
|
||||
hdb.db,
|
||||
`INSERT INTO account (
|
||||
idx,
|
||||
token_id,
|
||||
batch_num,
|
||||
bjj,
|
||||
eth_addr
|
||||
) VALUES %s;`,
|
||||
accounts[:],
|
||||
)
|
||||
}
|
||||
|
||||
// GetAccounts returns a list of accounts from the DB
|
||||
func (hdb *HistoryDB) GetAccounts() ([]*common.Account, error) {
|
||||
var accs []*common.Account
|
||||
err := meddler.QueryAll(
|
||||
hdb.db, &accs,
|
||||
"SELECT * FROM account ORDER BY idx;",
|
||||
)
|
||||
return accs, err
|
||||
}
|
||||
|
||||
// AddL1Txs inserts L1 txs to the DB
|
||||
func (hdb *HistoryDB) AddL1Txs(l1txs []common.L1Tx) error {
|
||||
txs := []common.Tx{}
|
||||
for _, tx := range l1txs {
|
||||
txs = append(txs, *tx.Tx())
|
||||
}
|
||||
return hdb.AddTxs(txs)
|
||||
}
|
||||
|
||||
// AddL2Txs inserts L2 txs to the DB
|
||||
func (hdb *HistoryDB) AddL2Txs(l2txs []common.L2Tx) error {
|
||||
txs := []common.Tx{}
|
||||
for _, tx := range l2txs {
|
||||
txs = append(txs, *tx.Tx())
|
||||
}
|
||||
return hdb.AddTxs(txs)
|
||||
}
|
||||
|
||||
// AddTxs insert L1 txs into the DB
|
||||
func (hdb *HistoryDB) AddTxs(txs []common.Tx) error {
|
||||
return db.BulkInsert(
|
||||
hdb.db,
|
||||
`INSERT INTO tx (
|
||||
is_l1,
|
||||
id,
|
||||
type,
|
||||
position,
|
||||
from_idx,
|
||||
to_idx,
|
||||
amount,
|
||||
amount_f,
|
||||
token_id,
|
||||
amount_usd,
|
||||
batch_num,
|
||||
eth_block_num,
|
||||
to_forge_l1_txs_num,
|
||||
user_origin,
|
||||
from_eth_addr,
|
||||
from_bjj,
|
||||
load_amount,
|
||||
load_amount_f,
|
||||
load_amount_usd,
|
||||
fee,
|
||||
fee_usd,
|
||||
nonce
|
||||
) VALUES %s;`,
|
||||
txs[:],
|
||||
)
|
||||
}
|
||||
|
||||
// GetTxs returns a list of txs from the DB
|
||||
func (hdb *HistoryDB) GetTxs() ([]*common.Tx, error) {
|
||||
var txs []*common.Tx
|
||||
err := meddler.QueryAll(
|
||||
hdb.db, &txs,
|
||||
`SELECT * FROM tx
|
||||
ORDER BY (batch_num, position) ASC`,
|
||||
)
|
||||
return txs, err
|
||||
}
|
||||
|
||||
// GetTx returns a tx from the DB
|
||||
func (hdb *HistoryDB) GetTx(txID common.TxID) (*common.Tx, error) {
|
||||
tx := new(common.Tx)
|
||||
return tx, meddler.QueryRow(
|
||||
hdb.db, tx,
|
||||
"SELECT * FROM tx WHERE id = $1;",
|
||||
txID,
|
||||
)
|
||||
}
|
||||
|
||||
// Close frees the resources used by HistoryDB
|
||||
func (hdb *HistoryDB) Close() error {
|
||||
return hdb.db.Close()
|
||||
|
||||
@@ -2,14 +2,16 @@ package historydb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
eth "github.com/ethereum/go-ethereum/common"
|
||||
ethCommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/hermeznetwork/hermez-node/common"
|
||||
"github.com/hermeznetwork/hermez-node/db"
|
||||
"github.com/hermeznetwork/hermez-node/test"
|
||||
"github.com/iden3/go-iden3-crypto/babyjub"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -47,7 +49,7 @@ func TestBlocks(t *testing.T) {
|
||||
// Delete peviously created rows (clean previous test execs)
|
||||
assert.NoError(t, historyDB.Reorg(fromBlock-1))
|
||||
// Generate fake blocks
|
||||
blocks := genBlocks(fromBlock, toBlock)
|
||||
blocks := test.GenBlocks(fromBlock, toBlock)
|
||||
// Insert blocks into DB
|
||||
for i := 0; i < len(blocks); i++ {
|
||||
err := historyDB.AddBlock(&blocks[i])
|
||||
@@ -82,38 +84,16 @@ func assertEqualBlock(t *testing.T, expected *common.Block, actual *common.Block
|
||||
func TestBatches(t *testing.T) {
|
||||
const fromBlock int64 = 1
|
||||
const toBlock int64 = 3
|
||||
const nBatchesPerBlock = 3
|
||||
// Prepare blocks in the DB
|
||||
setTestBlocks(fromBlock, toBlock)
|
||||
blocks := setTestBlocks(fromBlock, toBlock)
|
||||
// Generate fake batches
|
||||
var batches []common.Batch
|
||||
collectedFees := make(map[common.TokenID]*big.Int)
|
||||
for i := 0; i < 64; i++ {
|
||||
collectedFees[common.TokenID(i)] = big.NewInt(int64(i))
|
||||
}
|
||||
for i := fromBlock; i < toBlock; i++ {
|
||||
for j := 0; j < nBatchesPerBlock; j++ {
|
||||
batch := common.Batch{
|
||||
BatchNum: common.BatchNum(int(i-1)*nBatchesPerBlock + j),
|
||||
EthBlockNum: int64(i),
|
||||
ForgerAddr: eth.BigToAddress(big.NewInt(239457111187)),
|
||||
CollectedFees: collectedFees,
|
||||
StateRoot: common.Hash([]byte("duhdqlwiucgwqeiu")),
|
||||
NumAccounts: j,
|
||||
ExitRoot: common.Hash([]byte("tykertheuhtgenuer3iuw3b")),
|
||||
SlotNum: common.SlotNum(j),
|
||||
}
|
||||
if j%2 == 0 {
|
||||
batch.ForgeL1TxsNum = uint32(i)
|
||||
}
|
||||
batches = append(batches, batch)
|
||||
}
|
||||
}
|
||||
const nBatches = 9
|
||||
batches := test.GenBatches(nBatches, blocks)
|
||||
// Add batches to the DB
|
||||
err := historyDB.addBatches(batches)
|
||||
err := historyDB.AddBatches(batches)
|
||||
assert.NoError(t, err)
|
||||
// Get batches from the DB
|
||||
fetchedBatches, err := historyDB.GetBatches(0, common.BatchNum(int(toBlock-fromBlock)*nBatchesPerBlock))
|
||||
fetchedBatches, err := historyDB.GetBatches(0, common.BatchNum(nBatches))
|
||||
assert.NoError(t, err)
|
||||
for i, fetchedBatch := range fetchedBatches {
|
||||
assert.Equal(t, batches[i], *fetchedBatch)
|
||||
@@ -125,76 +105,222 @@ func TestBatches(t *testing.T) {
|
||||
// Test GetLastL1TxsNum
|
||||
fetchedLastL1TxsNum, err := historyDB.GetLastL1TxsNum()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, batches[len(batches)-1-(int(toBlock-fromBlock+1)%nBatchesPerBlock)].ForgeL1TxsNum, fetchedLastL1TxsNum)
|
||||
assert.Equal(t, batches[nBatches-1].ForgeL1TxsNum, fetchedLastL1TxsNum)
|
||||
}
|
||||
|
||||
func TestBids(t *testing.T) {
|
||||
const fromBlock int64 = 1
|
||||
const toBlock int64 = 5
|
||||
const bidsPerSlot = 5
|
||||
// Prepare blocks in the DB
|
||||
setTestBlocks(fromBlock, toBlock)
|
||||
blocks := setTestBlocks(fromBlock, toBlock)
|
||||
// Generate fake coordinators
|
||||
const nCoords = 5
|
||||
coords := test.GenCoordinators(nCoords, blocks)
|
||||
// Generate fake bids
|
||||
bids := make([]common.Bid, 0, (toBlock-fromBlock)*bidsPerSlot)
|
||||
for i := fromBlock; i < toBlock; i++ {
|
||||
for j := 0; j < bidsPerSlot; j++ {
|
||||
bids = append(bids, common.Bid{
|
||||
SlotNum: common.SlotNum(i),
|
||||
BidValue: big.NewInt(int64(j)),
|
||||
EthBlockNum: i,
|
||||
ForgerAddr: eth.BigToAddress(big.NewInt(int64(j))),
|
||||
})
|
||||
}
|
||||
}
|
||||
const nBids = 20
|
||||
bids := test.GenBids(nBids, blocks, coords)
|
||||
err := historyDB.addBids(bids)
|
||||
assert.NoError(t, err)
|
||||
// Fetch bids
|
||||
var fetchedBids []*common.Bid
|
||||
for i := fromBlock; i < toBlock; i++ {
|
||||
fetchedBidsSlot, err := historyDB.GetBidsBySlot(common.SlotNum(i))
|
||||
assert.NoError(t, err)
|
||||
fetchedBids = append(fetchedBids, fetchedBidsSlot...)
|
||||
}
|
||||
fetchedBids, err := historyDB.GetBids()
|
||||
assert.NoError(t, err)
|
||||
// Compare fetched bids vs generated bids
|
||||
for i, bid := range fetchedBids {
|
||||
assert.Equal(t, bids[i], *bid)
|
||||
}
|
||||
}
|
||||
|
||||
// setTestBlocks WARNING: this will delete the blocks and recreate them
|
||||
func setTestBlocks(from, to int64) {
|
||||
if from == 0 {
|
||||
if err := historyDB.Reorg(from); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
if err := historyDB.Reorg(from - 1); err != nil {
|
||||
panic(err)
|
||||
func TestTokens(t *testing.T) {
|
||||
const fromBlock int64 = 1
|
||||
const toBlock int64 = 5
|
||||
// Prepare blocks in the DB
|
||||
blocks := setTestBlocks(fromBlock, toBlock)
|
||||
// Generate fake tokens
|
||||
const nTokens = 5
|
||||
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 + float32(i)
|
||||
tokens[i].USD = value
|
||||
err := historyDB.UpdateTokenValue(tokens[i].TokenID, value)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
blocks := genBlocks(from, to)
|
||||
if err := addBlocks(blocks); err != nil {
|
||||
panic(err)
|
||||
// Fetch tokens
|
||||
fetchedTokens, err := historyDB.GetTokens()
|
||||
assert.NoError(t, err)
|
||||
// Compare fetched tokens vs generated tokens
|
||||
// All the tokens should have USDUpdate setted by the DB trigger
|
||||
for i, token := range fetchedTokens {
|
||||
assert.Equal(t, tokens[i].TokenID, token.TokenID)
|
||||
assert.Equal(t, tokens[i].EthBlockNum, token.EthBlockNum)
|
||||
assert.Equal(t, tokens[i].EthAddr, token.EthAddr)
|
||||
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)))
|
||||
}
|
||||
}
|
||||
|
||||
func genBlocks(from, to int64) []common.Block {
|
||||
var blocks []common.Block
|
||||
for i := from; i < to; i++ {
|
||||
blocks = append(blocks, common.Block{
|
||||
EthBlockNum: i,
|
||||
Timestamp: time.Now().Add(time.Second * 13).UTC(),
|
||||
Hash: eth.BigToHash(big.NewInt(int64(i))),
|
||||
})
|
||||
func TestAccounts(t *testing.T) {
|
||||
const fromBlock int64 = 1
|
||||
const toBlock int64 = 5
|
||||
// Prepare blocks in the DB
|
||||
blocks := setTestBlocks(fromBlock, toBlock)
|
||||
// Generate fake tokens
|
||||
const nTokens = 5
|
||||
tokens := test.GenTokens(nTokens, blocks)
|
||||
err := historyDB.AddTokens(tokens)
|
||||
assert.NoError(t, err)
|
||||
// Generate fake batches
|
||||
const nBatches = 10
|
||||
batches := test.GenBatches(nBatches, blocks)
|
||||
err = historyDB.AddBatches(batches)
|
||||
assert.NoError(t, err)
|
||||
// Generate fake accounts
|
||||
const nAccounts = 3
|
||||
accs := test.GenAccounts(nAccounts, 0, tokens, nil, batches)
|
||||
err = historyDB.AddAccounts(accs)
|
||||
assert.NoError(t, err)
|
||||
// Fetch accounts
|
||||
fetchedAccs, err := historyDB.GetAccounts()
|
||||
assert.NoError(t, err)
|
||||
// Compare fetched accounts vs generated accounts
|
||||
for i, acc := range fetchedAccs {
|
||||
assert.Equal(t, accs[i], *acc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTxs(t *testing.T) {
|
||||
const fromBlock int64 = 1
|
||||
const toBlock int64 = 5
|
||||
// Prepare blocks in the DB
|
||||
blocks := setTestBlocks(fromBlock, toBlock)
|
||||
// Generate fake tokens
|
||||
const nTokens = 5
|
||||
const tokenValue = 1.23456
|
||||
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
|
||||
const nBatches = 10
|
||||
batches := test.GenBatches(nBatches, blocks)
|
||||
err = historyDB.AddBatches(batches)
|
||||
assert.NoError(t, err)
|
||||
// Generate fake accounts
|
||||
const nAccounts = 3
|
||||
accs := test.GenAccounts(nAccounts, 0, tokens, nil, batches)
|
||||
err = historyDB.AddAccounts(accs)
|
||||
assert.NoError(t, err)
|
||||
// Generate fake L1 txs
|
||||
const nL1s = 30
|
||||
_, 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
|
||||
_, 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)
|
||||
}
|
||||
if tx.Fee == 0 {
|
||||
tx.FeeUSD = 0
|
||||
} else if tx.Fee <= 32 {
|
||||
tx.FeeUSD = tx.USD * math.Pow(10, -24+(float64(tx.Fee)/2))
|
||||
} else if tx.Fee <= 223 {
|
||||
tx.FeeUSD = tx.USD * math.Pow(10, -8+(0.041666666666667*(float64(tx.Fee)-32)))
|
||||
} else {
|
||||
tx.FeeUSD = tx.USD * math.Pow(10, float64(tx.Fee)-224)
|
||||
}
|
||||
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)
|
||||
}
|
||||
// Test trigger: L1 integrity
|
||||
// from_eth_addr can't be null
|
||||
l1txs[0].FromEthAddr = ethCommon.Address{}
|
||||
err = historyDB.AddL1Txs(l1txs)
|
||||
assert.Error(t, err)
|
||||
l1txs[0].FromEthAddr = ethCommon.BigToAddress(big.NewInt(int64(5)))
|
||||
// from_bjj can't be null
|
||||
l1txs[0].FromBJJ = nil
|
||||
err = historyDB.AddL1Txs(l1txs)
|
||||
assert.Error(t, err)
|
||||
privK := babyjub.NewRandPrivKey()
|
||||
l1txs[0].FromBJJ = privK.Public()
|
||||
// load_amount can't be null
|
||||
l1txs[0].LoadAmount = nil
|
||||
err = historyDB.AddL1Txs(l1txs)
|
||||
assert.Error(t, err)
|
||||
// Test trigger: L2 integrity
|
||||
// batch_num can't be null
|
||||
l2txs[0].BatchNum = 0
|
||||
err = historyDB.AddL2Txs(l2txs)
|
||||
assert.Error(t, err)
|
||||
l2txs[0].BatchNum = 1
|
||||
// nonce can't be null
|
||||
l2txs[0].Nonce = 0
|
||||
err = historyDB.AddL2Txs(l2txs)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// setTestBlocks WARNING: this will delete the blocks and recreate them
|
||||
func setTestBlocks(from, to int64) []common.Block {
|
||||
if err := cleanHistoryDB(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
blocks := test.GenBlocks(from, to)
|
||||
if err := historyDB.AddBlocks(blocks); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return blocks
|
||||
}
|
||||
|
||||
// addBlocks insert blocks into the DB. TODO: move method to test
|
||||
func addBlocks(blocks []common.Block) error {
|
||||
return db.BulkInsert(
|
||||
historyDB.db,
|
||||
"INSERT INTO block (eth_block_num, timestamp, hash) VALUES %s",
|
||||
blocks[:],
|
||||
)
|
||||
func cleanHistoryDB() error {
|
||||
return historyDB.Reorg(0)
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ CREATE TABLE batch (
|
||||
|
||||
CREATE TABLE exit_tree (
|
||||
batch_num BIGINT REFERENCES batch (batch_num) ON DELETE CASCADE,
|
||||
withdrawn BIGINT REFERENCES batch (batch_num) ON DELETE SET NULL,
|
||||
account_idx BIGINT,
|
||||
merkle_proof BYTEA NOT NULL,
|
||||
balance NUMERIC NOT NULL,
|
||||
@@ -34,17 +35,9 @@ CREATE TABLE exit_tree (
|
||||
PRIMARY KEY (batch_num, account_idx)
|
||||
);
|
||||
|
||||
CREATE TABLE withdrawal (
|
||||
batch_num BIGINT,
|
||||
account_idx BIGINT,
|
||||
eth_block_num BIGINT REFERENCES block (eth_block_num) ON DELETE CASCADE,
|
||||
FOREIGN KEY (batch_num, account_idx) REFERENCES exit_tree (batch_num, account_idx) ON DELETE CASCADE,
|
||||
PRIMARY KEY (batch_num, account_idx)
|
||||
);
|
||||
|
||||
CREATE TABLE bid (
|
||||
slot_num BIGINT NOT NULL,
|
||||
bid_value BYTEA NOT NULL, -- (check if we can do a max(), if not add float for order purposes)
|
||||
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)
|
||||
@@ -56,40 +49,124 @@ CREATE TABLE token (
|
||||
eth_addr BYTEA UNIQUE NOT NULL,
|
||||
name VARCHAR(20) NOT NULL,
|
||||
symbol VARCHAR(10) NOT NULL,
|
||||
decimals INT NOT NULL
|
||||
decimals INT NOT NULL,
|
||||
usd NUMERIC,
|
||||
usd_update TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE l1tx (
|
||||
tx_id BYTEA PRIMARY KEY,
|
||||
to_forge_l1_txs_num BIGINT NOT NULL,
|
||||
-- +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,
|
||||
user_origin BOOLEAN NOT NULL,
|
||||
from_idx BIGINT NOT NULL,
|
||||
from_eth_addr BYTEA NOT NULL,
|
||||
from_bjj BYTEA 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 NUMERIC NOT NULL,
|
||||
load_amount BYTEA NOT NULL,
|
||||
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,
|
||||
tx_type VARCHAR(40) NOT NULL
|
||||
-- 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 TABLE l2tx (
|
||||
tx_id BYTEA PRIMARY KEY,
|
||||
batch_num BIGINT NOT NULL REFERENCES batch (batch_num) ON DELETE CASCADE,
|
||||
position INT NOT NULL,
|
||||
from_idx BIGINT NOT NULL,
|
||||
to_idx BIGINT NOT NULL,
|
||||
amount NUMERIC NOT NULL,
|
||||
fee INT NOT NULL,
|
||||
nonce BIGINT NOT NULL,
|
||||
tx_type VARCHAR(40) NOT NULL
|
||||
);
|
||||
-- +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),
|
||||
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
|
||||
@@ -113,7 +190,7 @@ CREATE TABLE consensus_vars (
|
||||
outbidding INT NOT NULL,
|
||||
donation_address BYTEA NOT NULL,
|
||||
governance_address BYTEA NOT NULL,
|
||||
allocation_ratio vARCHAR(200)
|
||||
allocation_ratio VARCHAR(200)
|
||||
);
|
||||
|
||||
-- +migrate Down
|
||||
|
||||
Reference in New Issue
Block a user