mirror of
https://github.com/arnaucube/hermez-node.git
synced 2026-02-07 19:36:44 +01:00
Implement L2DB
This commit is contained in:
@@ -2,32 +2,24 @@ package l2db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
eth "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/hermeznetwork/hermez-node/common"
|
||||
"github.com/iden3/go-iden3-crypto/babyjub"
|
||||
"github.com/hermeznetwork/hermez-node/log"
|
||||
"github.com/hermeznetwork/hermez-node/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var l2DB *L2DB
|
||||
|
||||
// In order to run the test you need to run a Posgres DB with
|
||||
// a database named "l2" that is accessible by
|
||||
// user: "hermez"
|
||||
// pass: set it using the env var POSTGRES_PASS
|
||||
// This can be achieved by running: POSTGRES_PASS=your_strong_pass && sudo docker run --rm --name hermez-db-test -p 5432:5432 -e POSTGRES_DB=history -e POSTGRES_USER=hermez -e POSTGRES_PASSWORD=$POSTGRES_PASS -d postgres && sleep 2s && sudo docker exec -it hermez-db-test psql -a history -U hermez -c "CREATE DATABASE l2;"
|
||||
// After running the test you can stop the container by running: sudo docker kill hermez-ydb-test
|
||||
// If you already did that for the HistoryDB you don't have to do it again
|
||||
func TestMain(m *testing.M) {
|
||||
// init DB
|
||||
var err error
|
||||
pass := os.Getenv("POSTGRES_PASS")
|
||||
l2DB, err = NewL2DB(5432, "localhost", "hermez", pass, "l2", 10, 512, 24*time.Hour)
|
||||
l2DB, err = NewL2DB(5432, "localhost", "hermez", pass, "l2", 10, 100, 24*time.Hour)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -43,7 +35,7 @@ func TestMain(m *testing.M) {
|
||||
func TestAddTx(t *testing.T) {
|
||||
const nInserts = 20
|
||||
cleanDB()
|
||||
txs := genTxs(nInserts)
|
||||
txs := test.GenPoolTxs(nInserts)
|
||||
for _, tx := range txs {
|
||||
err := l2DB.AddTx(tx)
|
||||
assert.NoError(t, err)
|
||||
@@ -61,7 +53,7 @@ func TestAddTx(t *testing.T) {
|
||||
func BenchmarkAddTx(b *testing.B) {
|
||||
const nInserts = 20
|
||||
cleanDB()
|
||||
txs := genTxs(nInserts)
|
||||
txs := test.GenPoolTxs(nInserts)
|
||||
now := time.Now()
|
||||
for _, tx := range txs {
|
||||
_ = l2DB.AddTx(tx)
|
||||
@@ -73,7 +65,7 @@ func BenchmarkAddTx(b *testing.B) {
|
||||
func TestGetPending(t *testing.T) {
|
||||
const nInserts = 20
|
||||
cleanDB()
|
||||
txs := genTxs(nInserts)
|
||||
txs := test.GenPoolTxs(nInserts)
|
||||
var pendingTxs []*common.PoolL2Tx
|
||||
for _, tx := range txs {
|
||||
err := l2DB.AddTx(tx)
|
||||
@@ -95,81 +87,296 @@ func TestGetPending(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestStartForging(t *testing.T) {
|
||||
const nInserts = 24
|
||||
const fakeBlockNum = 33
|
||||
// Generate txs
|
||||
const nInserts = 60
|
||||
const fakeBatchNum common.BatchNum = 33
|
||||
cleanDB()
|
||||
txs := genTxs(nInserts)
|
||||
var startForgingTxs []*common.PoolL2Tx
|
||||
txs := test.GenPoolTxs(nInserts)
|
||||
var startForgingTxIDs []common.TxID
|
||||
randomizer := 0
|
||||
// Add txs to DB
|
||||
for _, tx := range txs {
|
||||
err := l2DB.AddTx(tx)
|
||||
assert.NoError(t, err)
|
||||
if tx.State == common.PoolL2TxStatePending && randomizer%2 == 0 {
|
||||
randomizer++
|
||||
startForgingTxs = append(startForgingTxs, tx)
|
||||
startForgingTxIDs = append(startForgingTxIDs, tx.TxID)
|
||||
}
|
||||
if tx.State == common.PoolL2TxStateForging {
|
||||
startForgingTxs = append(startForgingTxs, tx)
|
||||
}
|
||||
}
|
||||
fmt.Println(startForgingTxs) // TODO added print here to avoid lint complaining about startForgingTxs not being used
|
||||
err := l2DB.StartForging(startForgingTxIDs, fakeBlockNum)
|
||||
// Start forging txs
|
||||
err := l2DB.StartForging(startForgingTxIDs, fakeBatchNum)
|
||||
assert.NoError(t, err)
|
||||
// TODO: Fetch txs and check that they've been updated correctly
|
||||
// Fetch txs and check that they've been updated correctly
|
||||
for _, id := range startForgingTxIDs {
|
||||
fetchedTx, err := l2DB.GetTx(id)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, common.PoolL2TxStateForging, fetchedTx.State)
|
||||
assert.Equal(t, fakeBatchNum, fetchedTx.BatchNum)
|
||||
}
|
||||
}
|
||||
|
||||
func genTxs(n int) []*common.PoolL2Tx {
|
||||
// WARNING: This tx doesn't follow the protocol (signature, txID, ...)
|
||||
// it's just to test getting/setting from/to the DB.
|
||||
// Type and RqTxCompressedData: not initialized because it's not stored
|
||||
// on the DB and add noise when checking results.
|
||||
txs := make([]*common.PoolL2Tx, 0, n)
|
||||
privK := babyjub.NewRandPrivKey()
|
||||
for i := 0; i < n; i++ {
|
||||
var state common.PoolL2TxState
|
||||
if i%4 == 0 {
|
||||
state = common.PoolL2TxStatePending
|
||||
} else if i%4 == 1 {
|
||||
state = common.PoolL2TxStateInvalid
|
||||
} else if i%4 == 2 {
|
||||
state = common.PoolL2TxStateForging
|
||||
} else if i%4 == 3 {
|
||||
state = common.PoolL2TxStateForged
|
||||
func TestDoneForging(t *testing.T) {
|
||||
// Generate txs
|
||||
const nInserts = 60
|
||||
const fakeBatchNum common.BatchNum = 33
|
||||
cleanDB()
|
||||
txs := test.GenPoolTxs(nInserts)
|
||||
var doneForgingTxIDs []common.TxID
|
||||
randomizer := 0
|
||||
// Add txs to DB
|
||||
for _, tx := range txs {
|
||||
err := l2DB.AddTx(tx)
|
||||
assert.NoError(t, err)
|
||||
if tx.State == common.PoolL2TxStateForging && randomizer%2 == 0 {
|
||||
randomizer++
|
||||
doneForgingTxIDs = append(doneForgingTxIDs, tx.TxID)
|
||||
}
|
||||
tx := &common.PoolL2Tx{
|
||||
TxID: common.TxID(common.Hash([]byte(strconv.Itoa(i)))),
|
||||
FromIdx: 47,
|
||||
ToIdx: 96,
|
||||
ToEthAddr: eth.BigToAddress(big.NewInt(234523534)),
|
||||
ToBJJ: privK.Public(),
|
||||
TokenID: 73,
|
||||
Amount: big.NewInt(3487762374627846747),
|
||||
Fee: 99,
|
||||
Nonce: 28,
|
||||
State: state,
|
||||
Signature: privK.SignPoseidon(big.NewInt(674238462)),
|
||||
Timestamp: time.Now().UTC(),
|
||||
}
|
||||
if i%2 == 0 { // Optional parameters: rq
|
||||
tx.RqFromIdx = 893
|
||||
tx.RqToIdx = 334
|
||||
tx.RqToEthAddr = eth.BigToAddress(big.NewInt(239457111187))
|
||||
tx.RqToBJJ = privK.Public()
|
||||
tx.RqTokenID = 222
|
||||
tx.RqAmount = big.NewInt(3487762374627846747)
|
||||
tx.RqFee = 11
|
||||
tx.RqNonce = 78
|
||||
}
|
||||
if i%3 == 0 { // Optional parameters: things that get updated "a posteriori"
|
||||
tx.BatchNum = 489
|
||||
tx.AbsoluteFee = 39.12345
|
||||
tx.AbsoluteFeeUpdate = time.Now().UTC()
|
||||
}
|
||||
txs = append(txs, tx)
|
||||
}
|
||||
return txs
|
||||
// Start forging txs
|
||||
err := l2DB.DoneForging(doneForgingTxIDs, fakeBatchNum)
|
||||
assert.NoError(t, err)
|
||||
// Fetch txs and check that they've been updated correctly
|
||||
for _, id := range doneForgingTxIDs {
|
||||
fetchedTx, err := l2DB.GetTx(id)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, common.PoolL2TxStateForged, fetchedTx.State)
|
||||
assert.Equal(t, fakeBatchNum, fetchedTx.BatchNum)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidate(t *testing.T) {
|
||||
// Generate txs
|
||||
const nInserts = 60
|
||||
const fakeBatchNum common.BatchNum = 33
|
||||
cleanDB()
|
||||
txs := test.GenPoolTxs(nInserts)
|
||||
var invalidTxIDs []common.TxID
|
||||
randomizer := 0
|
||||
// Add txs to DB
|
||||
for _, tx := range txs {
|
||||
err := l2DB.AddTx(tx)
|
||||
assert.NoError(t, err)
|
||||
if tx.State != common.PoolL2TxStateInvalid && randomizer%2 == 0 {
|
||||
randomizer++
|
||||
invalidTxIDs = append(invalidTxIDs, tx.TxID)
|
||||
}
|
||||
}
|
||||
// Start forging txs
|
||||
err := l2DB.InvalidateTxs(invalidTxIDs, fakeBatchNum)
|
||||
assert.NoError(t, err)
|
||||
// Fetch txs and check that they've been updated correctly
|
||||
for _, id := range invalidTxIDs {
|
||||
fetchedTx, err := l2DB.GetTx(id)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, common.PoolL2TxStateInvalid, fetchedTx.State)
|
||||
assert.Equal(t, fakeBatchNum, fetchedTx.BatchNum)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckNonces(t *testing.T) {
|
||||
// Generate txs
|
||||
const nInserts = 60
|
||||
const fakeBatchNum common.BatchNum = 33
|
||||
cleanDB()
|
||||
txs := test.GenPoolTxs(nInserts)
|
||||
var invalidTxIDs []common.TxID
|
||||
// Generate accounts
|
||||
const nAccoutns = 2
|
||||
const currentNonce = 2
|
||||
accs := []common.Account{}
|
||||
for i := 0; i < nAccoutns; i++ {
|
||||
accs = append(accs, common.Account{
|
||||
Idx: common.Idx(i),
|
||||
Nonce: currentNonce,
|
||||
})
|
||||
}
|
||||
// Add txs to DB
|
||||
for i := 0; i < len(txs); i++ {
|
||||
if txs[i].State != common.PoolL2TxStateInvalid {
|
||||
if i%2 == 0 { // Ensure transaction will be marked as invalid due to old nonce
|
||||
txs[i].Nonce = accs[i%len(accs)].Nonce
|
||||
txs[i].FromIdx = accs[i%len(accs)].Idx
|
||||
invalidTxIDs = append(invalidTxIDs, txs[i].TxID)
|
||||
} else { // Ensure transaction will NOT be marked as invalid due to old nonce
|
||||
txs[i].Nonce = currentNonce + 1
|
||||
}
|
||||
}
|
||||
err := l2DB.AddTx(txs[i])
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
// Start forging txs
|
||||
err := l2DB.InvalidateTxs(invalidTxIDs, fakeBatchNum)
|
||||
assert.NoError(t, err)
|
||||
// Fetch txs and check that they've been updated correctly
|
||||
for _, id := range invalidTxIDs {
|
||||
fetchedTx, err := l2DB.GetTx(id)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, common.PoolL2TxStateInvalid, fetchedTx.State)
|
||||
assert.Equal(t, fakeBatchNum, fetchedTx.BatchNum)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateTxValue(t *testing.T) {
|
||||
// Generate txs
|
||||
const nInserts = 255 // Force all possible fee selector values
|
||||
cleanDB()
|
||||
txs := test.GenPoolTxs(nInserts)
|
||||
// Generate tokens
|
||||
const nTokens = 2
|
||||
tokens := []common.Token{}
|
||||
for i := 0; i < nTokens; i++ {
|
||||
tokens = append(tokens, common.Token{
|
||||
TokenID: common.TokenID(i),
|
||||
USD: float64(i) * 1.3,
|
||||
})
|
||||
}
|
||||
// Add txs to DB
|
||||
for i := 0; i < len(txs); i++ {
|
||||
// Set Token
|
||||
token := tokens[i%len(tokens)]
|
||||
txs[i].TokenID = token.TokenID
|
||||
// Insert to DB
|
||||
err := l2DB.AddTx(txs[i])
|
||||
assert.NoError(t, err)
|
||||
// Set USD values (for comparing results when fetching from DB)
|
||||
txs[i].USD = txs[i].AmountFloat * token.USD
|
||||
if txs[i].Fee == 0 {
|
||||
txs[i].AbsoluteFee = 0
|
||||
} else if txs[i].Fee <= 32 {
|
||||
txs[i].AbsoluteFee = txs[i].USD * math.Pow(10, -24+(float64(txs[i].Fee)/2))
|
||||
} else if txs[i].Fee <= 223 {
|
||||
txs[i].AbsoluteFee = txs[i].USD * math.Pow(10, -8+(0.041666666666667*(float64(txs[i].Fee)-32)))
|
||||
} else {
|
||||
txs[i].AbsoluteFee = txs[i].USD * math.Pow(10, float64(txs[i].Fee)-224)
|
||||
}
|
||||
}
|
||||
// Update token value
|
||||
err := l2DB.UpdateTxValue(tokens)
|
||||
assert.NoError(t, err)
|
||||
// Fetch txs and check that they've been updated correctly
|
||||
for _, tx := range txs {
|
||||
fetchedTx, err := l2DB.GetTx(tx.TxID)
|
||||
assert.NoError(t, err)
|
||||
if fetchedTx.USD > tx.USD {
|
||||
assert.Less(t, 0.999, tx.USD/fetchedTx.USD)
|
||||
} else if fetchedTx.USD < tx.USD {
|
||||
assert.Less(t, 0.999, fetchedTx.USD/tx.USD)
|
||||
}
|
||||
if fetchedTx.AbsoluteFee > tx.AbsoluteFee {
|
||||
assert.Less(t, 0.999, tx.AbsoluteFee/fetchedTx.AbsoluteFee)
|
||||
} else if fetchedTx.AbsoluteFee < tx.AbsoluteFee {
|
||||
assert.Less(t, 0.999, fetchedTx.AbsoluteFee/tx.AbsoluteFee)
|
||||
}
|
||||
// Time is set in the DB, so it cannot be compared exactly
|
||||
assert.Greater(t, float64(15*time.Second), time.Since(fetchedTx.AbsoluteFeeUpdate).Seconds())
|
||||
}
|
||||
}
|
||||
|
||||
func TestReorg(t *testing.T) {
|
||||
// Generate txs
|
||||
const nInserts = 20
|
||||
const lastValidBatch common.BatchNum = 20
|
||||
const reorgBatch common.BatchNum = lastValidBatch + 1
|
||||
cleanDB()
|
||||
txs := test.GenPoolTxs(nInserts)
|
||||
// Add txs to the DB
|
||||
reorgedTxIDs := []common.TxID{}
|
||||
nonReorgedTxIDs := []common.TxID{}
|
||||
for i := 0; i < len(txs); i++ {
|
||||
if txs[i].State == common.PoolL2TxStateForged || txs[i].State == common.PoolL2TxStateInvalid {
|
||||
txs[i].BatchNum = reorgBatch
|
||||
reorgedTxIDs = append(reorgedTxIDs, txs[i].TxID)
|
||||
} else {
|
||||
txs[i].BatchNum = lastValidBatch
|
||||
nonReorgedTxIDs = append(nonReorgedTxIDs, txs[i].TxID)
|
||||
}
|
||||
err := l2DB.AddTx(txs[i])
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
err := l2DB.Reorg(lastValidBatch)
|
||||
assert.NoError(t, err)
|
||||
var nullBatchNum common.BatchNum
|
||||
for _, id := range reorgedTxIDs {
|
||||
tx, err := l2DB.GetTx(id)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, nullBatchNum, tx.BatchNum)
|
||||
assert.Equal(t, common.PoolL2TxStatePending, tx.State)
|
||||
}
|
||||
for _, id := range nonReorgedTxIDs {
|
||||
tx, err := l2DB.GetTx(id)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, lastValidBatch, tx.BatchNum)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPurge(t *testing.T) {
|
||||
// Generate txs
|
||||
nInserts := l2DB.maxTxs + 20
|
||||
cleanDB()
|
||||
txs := test.GenPoolTxs(int(nInserts))
|
||||
deletedIDs := []common.TxID{}
|
||||
keepedIDs := []common.TxID{}
|
||||
const toDeleteBatchNum common.BatchNum = 30
|
||||
safeBatchNum := toDeleteBatchNum + l2DB.safetyPeriod + 1
|
||||
// Add txs to the DB
|
||||
for i := 0; i < int(l2DB.maxTxs); i++ {
|
||||
if i%1 == 0 { // keep tx
|
||||
txs[i].BatchNum = safeBatchNum
|
||||
keepedIDs = append(keepedIDs, txs[i].TxID)
|
||||
} else if i%2 == 0 { // delete after safety period
|
||||
txs[i].BatchNum = toDeleteBatchNum
|
||||
if i%3 == 0 {
|
||||
txs[i].State = common.PoolL2TxStateForged
|
||||
} else {
|
||||
txs[i].State = common.PoolL2TxStateInvalid
|
||||
}
|
||||
deletedIDs = append(deletedIDs, txs[i].TxID)
|
||||
}
|
||||
err := l2DB.AddTx(txs[i])
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
for i := int(l2DB.maxTxs); i < len(txs); i++ {
|
||||
// Delete after TTL
|
||||
txs[i].Timestamp = time.Unix(time.Now().UTC().Unix()-int64(l2DB.ttl.Seconds()+float64(4*time.Second)), 0)
|
||||
deletedIDs = append(deletedIDs, txs[i].TxID)
|
||||
err := l2DB.AddTx(txs[i])
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
// Purge txs
|
||||
err := l2DB.Purge(safeBatchNum - 1)
|
||||
assert.NoError(t, err)
|
||||
// Check results
|
||||
for _, id := range deletedIDs {
|
||||
tx, err := l2DB.GetTx(id)
|
||||
if err == nil {
|
||||
log.Debug(tx)
|
||||
}
|
||||
assert.Error(t, err)
|
||||
}
|
||||
for _, id := range keepedIDs {
|
||||
_, err := l2DB.GetTx(id)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuth(t *testing.T) {
|
||||
cleanDB()
|
||||
const nAuths = 5
|
||||
// Generate authorizations
|
||||
auths := test.GenAuths(nAuths)
|
||||
for i := 0; i < len(auths); i++ {
|
||||
// Add to the DB
|
||||
err := l2DB.AddAccountCreationAuth(auths[i])
|
||||
assert.NoError(t, err)
|
||||
// Fetch from DB
|
||||
auth, err := l2DB.GetAccountCreationAuth(auths[i].EthAddr)
|
||||
assert.NoError(t, err)
|
||||
// Check fetched vs generated
|
||||
assert.Equal(t, auths[i].EthAddr, auth.EthAddr)
|
||||
assert.Equal(t, auths[i].BJJ, auth.BJJ)
|
||||
assert.Equal(t, auths[i].Signature, auth.Signature)
|
||||
assert.Equal(t, auths[i].Timestamp.Unix(), auths[i].Timestamp.Unix())
|
||||
}
|
||||
}
|
||||
|
||||
func cleanDB() {
|
||||
|
||||
Reference in New Issue
Block a user