Browse Source

Helper methods for Synchronizer and TX refactor

feature/sql-semaphore1
ToniRamirezM 4 years ago
committed by Eduard S
parent
commit
ac677694fe
15 changed files with 389 additions and 102 deletions
  1. +1
    -1
      .github/workflows/test.yml
  2. +5
    -1
      README.md
  3. +6
    -0
      api/api_test.go
  4. +6
    -6
      common/batch.go
  5. +4
    -4
      common/coordinator.go
  6. +15
    -4
      common/exittree.go
  7. +21
    -0
      common/tx.go
  8. +240
    -43
      db/historydb/historydb.go
  9. +33
    -2
      db/historydb/historydb_test.go
  10. +5
    -4
      db/historydb/migrations/001_init.sql
  11. +3
    -3
      db/l2db/l2db.go
  12. +1
    -16
      db/statedb/txprocessors.go
  13. +6
    -6
      synchronizer/synchronizer.go
  14. +41
    -10
      test/historydb.go
  15. +2
    -2
      test/l2db.go

+ 1
- 1
.github/workflows/test.yml

@ -23,4 +23,4 @@ jobs:
POSTGRES_PASS: ${{ secrets.POSTGRES_PASS }} POSTGRES_PASS: ${{ secrets.POSTGRES_PASS }}
ETHCLIENT_DIAL_URL: ${{ secrets.ETHCLIENT_DIAL_URL }} ETHCLIENT_DIAL_URL: ${{ secrets.ETHCLIENT_DIAL_URL }}
GOARCH: ${{ matrix.goarch }} GOARCH: ${{ matrix.goarch }}
run: go test ./... -v
run: go test -p 1 ./...

+ 5
- 1
README.md

@ -13,9 +13,13 @@ POSTGRES_PASS=yourpasswordhere; sudo docker run --rm --name hermez-db-test -p 54
- Then, run the tests with the password as env var - Then, run the tests with the password as env var
``` ```
POSTGRES_PASS=yourpasswordhere ETHCLIENT_DIAL_URL=yourethereumurlhere go test ./...
POSTGRES_PASS=yourpasswordhere ETHCLIENT_DIAL_URL=yourethereumurlhere go test -p 1 ./...
``` ```
NOTE: `-p 1` forces execution of package test in serial. Otherwise they may be
executed in paralel and the test may find unexpected entries in the SQL
databse because it's shared among all tests.
## Lint ## Lint
- Install [golangci-lint](https://golangci-lint.run) - Install [golangci-lint](https://golangci-lint.run)

+ 6
- 0
api/api_test.go

@ -82,6 +82,11 @@ func TestMain(m *testing.M) {
if err != nil { if err != nil {
panic(err) panic(err)
} }
// Reset DB
err = hdb.Reorg(-1)
if err != nil {
panic(err)
}
dir, err := ioutil.TempDir("", "tmpdb") dir, err := ioutil.TempDir("", "tmpdb")
if err != nil { if err != nil {
panic(err) panic(err)
@ -94,6 +99,7 @@ func TestMain(m *testing.M) {
if err != nil { if err != nil {
panic(err) panic(err)
} }
test.CleanL2DB(l2db.DB())
// Init API // Init API
api := gin.Default() api := gin.Default()
if err := SetAPIEndpoints( if err := SetAPIEndpoints(

+ 6
- 6
common/batch.go

@ -8,7 +8,7 @@ import (
ethCommon "github.com/ethereum/go-ethereum/common" ethCommon "github.com/ethereum/go-ethereum/common"
) )
const batchNumBytesLen = 4
const batchNumBytesLen = 8
// Batch is a struct that represents Hermez network batch // Batch is a struct that represents Hermez network batch
type Batch struct { type Batch struct {
@ -24,20 +24,20 @@ type Batch struct {
} }
// BatchNum identifies a batch // BatchNum identifies a batch
type BatchNum uint32
type BatchNum int64
// Bytes returns a byte array of length 4 representing the BatchNum // Bytes returns a byte array of length 4 representing the BatchNum
func (bn BatchNum) Bytes() []byte { func (bn BatchNum) Bytes() []byte {
var batchNumBytes [4]byte
binary.LittleEndian.PutUint32(batchNumBytes[:], uint32(bn))
var batchNumBytes [batchNumBytesLen]byte
binary.BigEndian.PutUint64(batchNumBytes[:], uint64(bn))
return batchNumBytes[:] return batchNumBytes[:]
} }
// BatchNumFromBytes returns BatchNum from a []byte // BatchNumFromBytes returns BatchNum from a []byte
func BatchNumFromBytes(b []byte) (BatchNum, error) { func BatchNumFromBytes(b []byte) (BatchNum, error) {
if len(b) != batchNumBytesLen { if len(b) != batchNumBytesLen {
return 0, fmt.Errorf("can not parse BatchNumFromBytes, bytes len %d, expected 4", len(b))
return 0, fmt.Errorf("can not parse BatchNumFromBytes, bytes len %d, expected %d", len(b), batchNumBytesLen)
} }
batchNum := binary.LittleEndian.Uint32(b[:4])
batchNum := binary.BigEndian.Uint64(b[:batchNumBytesLen])
return BatchNum(batchNum), nil return BatchNum(batchNum), nil
} }

+ 4
- 4
common/coordinator.go

@ -7,8 +7,8 @@ import (
// Coordinator represents a Hermez network coordinator who wins an auction for an specific slot // Coordinator represents a Hermez network coordinator who wins an auction for an specific slot
// WARNING: this is strongly based on the previous implementation, once the new spec is done, this may change a lot. // WARNING: this is strongly based on the previous implementation, once the new spec is done, this may change a lot.
type Coordinator struct { type Coordinator struct {
EthBlockNum int64 // block in which the coordinator was registered
Forger ethCommon.Address // address of the forger
Withdraw ethCommon.Address // address of the withdraw
URL string // URL of the coordinators API
Forger ethCommon.Address `meddler:"forger_addr"` // address of the forger
EthBlockNum int64 `meddler:"eth_block_num"` // block in which the coordinator was registered
WithdrawAddr ethCommon.Address `meddler:"withdraw_addr"` // address of the withdraw
URL string `meddler:"url"` // URL of the coordinators API
} }

+ 15
- 4
common/exittree.go

@ -8,8 +8,19 @@ import (
// ExitInfo represents the ExitTree Leaf data // ExitInfo represents the ExitTree Leaf data
type ExitInfo struct { type ExitInfo struct {
AccountIdx Idx
MerkleProof *merkletree.CircomVerifierProof
Balance *big.Int
Nullifier *big.Int
BatchNum BatchNum `meddler:"batch_num"`
AccountIdx Idx `meddler:"account_idx"`
MerkleProof *merkletree.CircomVerifierProof `meddler:"merkle_proof,json"`
Balance *big.Int `meddler:"balance,bigint"`
// InstantWithdrawn is the ethBlockNum in which the exit is withdrawn
// instantly. nil means this hasn't happened.
InstantWithdrawn *int64 `meddler:"instant_withdrawn"`
// DelayedWithdrawRequest is the ethBlockNum in which the exit is
// requested to be withdrawn from the delayedWithdrawn smart contract.
// nil means this hasn't happened.
DelayedWithdrawRequest *int64 `meddler:"delayed_withdraw_request"`
// DelayedWithdrawn is the ethBlockNum in which the exit is withdrawn
// from the delayedWithdrawn smart contract. nil means this hasn't
// happened.
DelayedWithdrawn *int64 `meddler:"delayed_withdrawn"`
} }

+ 21
- 0
common/tx.go

@ -66,3 +66,24 @@ type Tx struct {
FeeUSD float64 `meddler:"fee_usd,zeroisnull"` FeeUSD float64 `meddler:"fee_usd,zeroisnull"`
Nonce Nonce `meddler:"nonce,zeroisnull"` Nonce Nonce `meddler:"nonce,zeroisnull"`
} }
// L1Tx returns a *L1Tx from the Tx
func (tx *Tx) L1Tx() *L1Tx {
l1Tx := &L1Tx{
TxID: tx.TxID,
ToForgeL1TxsNum: tx.ToForgeL1TxsNum,
Position: tx.Position,
UserOrigin: tx.UserOrigin,
FromIdx: tx.FromIdx,
FromEthAddr: tx.FromEthAddr,
FromBJJ: tx.FromBJJ,
ToIdx: tx.ToIdx,
TokenID: tx.TokenID,
Amount: tx.Amount,
LoadAmount: tx.LoadAmount,
EthBlockNum: tx.EthBlockNum,
Type: tx.Type,
BatchNum: tx.BatchNum,
}
return l1Tx
}

+ 240
- 43
db/historydb/historydb.go

@ -26,6 +26,36 @@ type HistoryDB struct {
db *sqlx.DB db *sqlx.DB
} }
// BlockData contains the information of a Block
type BlockData struct {
block *common.Block
// Rollup
// L1UserTxs that were submitted in the block
L1UserTxs []common.L1Tx
Batches []BatchData
RegisteredTokens []common.Token
RollupVars *common.RollupVars
// Auction
Bids []common.Bid
Coordinators []common.Coordinator
AuctionVars *common.AuctionVars
// WithdrawalDelayer
// TODO: enable when common.WithdrawalDelayerVars is Merged from Synchronizer PR
// WithdrawalDelayerVars *common.WithdrawalDelayerVars
}
// BatchData contains the information of a Batch
type BatchData struct {
// L1UserTxs that were forged in the batch
L1Batch bool // TODO: Remove once Batch.ForgeL1TxsNum is a pointer
L1UserTxs []common.L1Tx
L1CoordinatorTxs []common.L1Tx
L2Txs []common.L2Tx
CreatedAccounts []common.Account
ExitTree []common.ExitInfo
Batch *common.Batch
}
// NewHistoryDB initialize the DB // NewHistoryDB initialize the DB
func NewHistoryDB(port int, host, user, password, dbname string) (*HistoryDB, error) { func NewHistoryDB(port int, host, user, password, dbname string) (*HistoryDB, error) {
// Connect to DB // Connect to DB
@ -52,14 +82,19 @@ func NewHistoryDB(port int, host, user, password, dbname string) (*HistoryDB, er
} }
// AddBlock insert a block into the DB // AddBlock insert a block into the DB
func (hdb *HistoryDB) AddBlock(block *common.Block) error {
return meddler.Insert(hdb.db, "block", block)
func (hdb *HistoryDB) AddBlock(block *common.Block) error { return hdb.addBlock(hdb.db, block) }
func (hdb *HistoryDB) addBlock(d meddler.DB, block *common.Block) error {
return meddler.Insert(d, "block", block)
} }
// AddBlocks inserts blocks into the DB // AddBlocks inserts blocks into the DB
func (hdb *HistoryDB) AddBlocks(blocks []common.Block) error { func (hdb *HistoryDB) AddBlocks(blocks []common.Block) error {
return hdb.addBlocks(hdb.db, blocks)
}
func (hdb *HistoryDB) addBlocks(d meddler.DB, blocks []common.Block) error {
return db.BulkInsert( return db.BulkInsert(
hdb.db,
d,
`INSERT INTO block ( `INSERT INTO block (
eth_block_num, eth_block_num,
timestamp, timestamp,
@ -84,7 +119,7 @@ func (hdb *HistoryDB) GetBlocks(from, to int64) ([]*common.Block, error) {
var blocks []*common.Block var blocks []*common.Block
err := meddler.QueryAll( err := meddler.QueryAll(
hdb.db, &blocks, hdb.db, &blocks,
"SELECT * FROM block WHERE $1 <= eth_block_num AND eth_block_num < $2",
"SELECT * FROM block WHERE $1 <= eth_block_num AND eth_block_num < $2;",
from, to, from, to,
) )
return blocks, err return blocks, err
@ -99,10 +134,19 @@ func (hdb *HistoryDB) GetLastBlock() (*common.Block, error) {
return block, err return block, err
} }
// AddBatch insert a Batch into the DB
func (hdb *HistoryDB) AddBatch(batch *common.Batch) error { return hdb.addBatch(hdb.db, batch) }
func (hdb *HistoryDB) addBatch(d meddler.DB, batch *common.Batch) error {
return meddler.Insert(d, "batch", batch)
}
// AddBatches insert Bids into the DB // AddBatches insert Bids into the DB
func (hdb *HistoryDB) AddBatches(batches []common.Batch) error { func (hdb *HistoryDB) AddBatches(batches []common.Batch) error {
return hdb.addBatches(hdb.db, batches)
}
func (hdb *HistoryDB) addBatches(d meddler.DB, batches []common.Batch) error {
return db.BulkInsert( return db.BulkInsert(
hdb.db,
d,
`INSERT INTO batch ( `INSERT INTO batch (
batch_num, batch_num,
eth_block_num, eth_block_num,
@ -123,7 +167,7 @@ func (hdb *HistoryDB) GetBatches(from, to common.BatchNum) ([]*common.Batch, err
var batches []*common.Batch var batches []*common.Batch
err := meddler.QueryAll( err := meddler.QueryAll(
hdb.db, &batches, hdb.db, &batches,
"SELECT * FROM batch WHERE $1 <= batch_num AND batch_num < $2",
"SELECT * FROM batch WHERE $1 <= batch_num AND batch_num < $2;",
from, to, from, to,
) )
return batches, err return batches, err
@ -144,29 +188,17 @@ func (hdb *HistoryDB) GetLastL1TxsNum() (*int64, error) {
return lastL1TxsNum, row.Scan(&lastL1TxsNum) return lastL1TxsNum, row.Scan(&lastL1TxsNum)
} }
// Reorg deletes all the information that was added into the DB after the lastValidBlock
// Reorg deletes all the information that was added into the DB after the
// lastValidBlock. If lastValidBlock is negative, all block information is
// deleted.
func (hdb *HistoryDB) Reorg(lastValidBlock int64) error { func (hdb *HistoryDB) Reorg(lastValidBlock int64) error {
_, err := hdb.db.Exec("DELETE FROM block WHERE eth_block_num > $1;", lastValidBlock)
return err
}
// SyncRollup stores all the data that can be changed / added on a block in the Rollup SC
func (hdb *HistoryDB) SyncRollup(
blockNum uint64,
l1txs []common.L1Tx,
l2txs []common.L2Tx,
registeredAccounts []common.Account,
exitTree common.ExitInfo,
withdrawals common.ExitInfo,
registeredTokens []common.Token,
batches []common.Batch,
vars *common.RollupVars,
) error {
// TODO: make all in a single DB commit
if err := hdb.AddBatches(batches); err != nil {
return err
var err error
if lastValidBlock < 0 {
_, err = hdb.db.Exec("DELETE FROM block;")
} else {
_, err = hdb.db.Exec("DELETE FROM block WHERE eth_block_num > $1;", lastValidBlock)
} }
return nil
return err
} }
// SyncPoD stores all the data that can be changed / added on a block in the PoD SC // SyncPoD stores all the data that can be changed / added on a block in the PoD SC
@ -179,12 +211,13 @@ func (hdb *HistoryDB) SyncPoD(
return nil return nil
} }
// addBids insert Bids into the DB
func (hdb *HistoryDB) addBids(bids []common.Bid) error {
// AddBids insert Bids into the DB
func (hdb *HistoryDB) AddBids(bids []common.Bid) error { return hdb.addBids(hdb.db, bids) }
func (hdb *HistoryDB) addBids(d meddler.DB, bids []common.Bid) error {
// TODO: check the coordinator info // TODO: check the coordinator info
return db.BulkInsert( return db.BulkInsert(
hdb.db,
"INSERT INTO bid (slot_num, forger_addr, bid_value, eth_block_num) VALUES %s",
d,
"INSERT INTO bid (slot_num, forger_addr, bid_value, eth_block_num) VALUES %s;",
bids[:], bids[:],
) )
} }
@ -199,15 +232,41 @@ func (hdb *HistoryDB) GetBids() ([]*common.Bid, error) {
return bids, err return bids, err
} }
// AddCoordinators insert Coordinators into the DB
func (hdb *HistoryDB) AddCoordinators(coordinators []common.Coordinator) error {
return hdb.addCoordinators(hdb.db, coordinators)
}
func (hdb *HistoryDB) addCoordinators(d meddler.DB, coordinators []common.Coordinator) error {
return db.BulkInsert(
d,
"INSERT INTO coordinator (forger_addr, eth_block_num, withdraw_addr, url) VALUES %s;",
coordinators[:],
)
}
// AddExitTree insert Exit tree into the DB
func (hdb *HistoryDB) AddExitTree(exitTree []common.ExitInfo) error {
return hdb.addExitTree(hdb.db, exitTree)
}
func (hdb *HistoryDB) addExitTree(d meddler.DB, exitTree []common.ExitInfo) error {
return db.BulkInsert(
d,
"INSERT INTO exit_tree (batch_num, account_idx, merkle_proof, balance, "+
"instant_withdrawn, delayed_withdraw_request, delayed_withdrawn) VALUES %s;",
exitTree[:],
)
}
// AddToken insert a token into the DB // AddToken insert a token into the DB
func (hdb *HistoryDB) AddToken(token *common.Token) error { func (hdb *HistoryDB) AddToken(token *common.Token) error {
return meddler.Insert(hdb.db, "token", token) return meddler.Insert(hdb.db, "token", token)
} }
// AddTokens insert tokens into the DB // AddTokens insert tokens into the DB
func (hdb *HistoryDB) AddTokens(tokens []common.Token) error {
func (hdb *HistoryDB) AddTokens(tokens []common.Token) error { return hdb.addTokens(hdb.db, tokens) }
func (hdb *HistoryDB) addTokens(d meddler.DB, tokens []common.Token) error {
return db.BulkInsert( return db.BulkInsert(
hdb.db,
d,
`INSERT INTO token ( `INSERT INTO token (
token_id, token_id,
eth_block_num, eth_block_num,
@ -243,8 +302,11 @@ func (hdb *HistoryDB) GetTokens() ([]*common.Token, error) {
// AddAccounts insert accounts into the DB // AddAccounts insert accounts into the DB
func (hdb *HistoryDB) AddAccounts(accounts []common.Account) error { func (hdb *HistoryDB) AddAccounts(accounts []common.Account) error {
return hdb.addAccounts(hdb.db, accounts)
}
func (hdb *HistoryDB) addAccounts(d meddler.DB, accounts []common.Account) error {
return db.BulkInsert( return db.BulkInsert(
hdb.db,
d,
`INSERT INTO account ( `INSERT INTO account (
idx, idx,
token_id, token_id,
@ -267,27 +329,30 @@ func (hdb *HistoryDB) GetAccounts() ([]*common.Account, error) {
} }
// AddL1Txs inserts L1 txs to the DB // AddL1Txs inserts L1 txs to the DB
func (hdb *HistoryDB) AddL1Txs(l1txs []common.L1Tx) error {
func (hdb *HistoryDB) AddL1Txs(l1txs []common.L1Tx) error { return hdb.addL1Txs(hdb.db, l1txs) }
func (hdb *HistoryDB) addL1Txs(d meddler.DB, l1txs []common.L1Tx) error {
txs := []common.Tx{} txs := []common.Tx{}
for _, tx := range l1txs { for _, tx := range l1txs {
txs = append(txs, *tx.Tx())
txs = append(txs, *(tx.Tx()))
} }
return hdb.AddTxs(txs)
return hdb.addTxs(d, txs)
} }
// AddL2Txs inserts L2 txs to the DB // AddL2Txs inserts L2 txs to the DB
func (hdb *HistoryDB) AddL2Txs(l2txs []common.L2Tx) error {
func (hdb *HistoryDB) AddL2Txs(l2txs []common.L2Tx) error { return hdb.addL2Txs(hdb.db, l2txs) }
func (hdb *HistoryDB) addL2Txs(d meddler.DB, l2txs []common.L2Tx) error {
txs := []common.Tx{} txs := []common.Tx{}
for _, tx := range l2txs { for _, tx := range l2txs {
txs = append(txs, *tx.Tx())
txs = append(txs, *(tx.Tx()))
} }
return hdb.AddTxs(txs)
return hdb.addTxs(d, txs)
} }
// AddTxs insert L1 txs into the DB // AddTxs insert L1 txs into the DB
func (hdb *HistoryDB) AddTxs(txs []common.Tx) error {
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( return db.BulkInsert(
hdb.db,
d,
`INSERT INTO tx ( `INSERT INTO tx (
is_l1, is_l1,
id, id,
@ -316,6 +381,16 @@ func (hdb *HistoryDB) AddTxs(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 // GetTxs returns a list of txs from the DB
func (hdb *HistoryDB) GetTxs() ([]*common.Tx, error) { func (hdb *HistoryDB) GetTxs() ([]*common.Tx, error) {
var txs []*common.Tx var txs []*common.Tx
@ -409,7 +484,7 @@ func (hdb *HistoryDB) GetHistoryTxs(
queryStr += "ORDER BY (batch_num, position) ASC NULLS LAST " queryStr += "ORDER BY (batch_num, position) ASC NULLS LAST "
queryStr += fmt.Sprintf("OFFSET %d ", *offset) queryStr += fmt.Sprintf("OFFSET %d ", *offset)
} }
queryStr += fmt.Sprintf("LIMIT %d ", *limit)
queryStr += fmt.Sprintf("LIMIT %d;", *limit)
query = hdb.db.Rebind(queryStr) query = hdb.db.Rebind(queryStr)
// log.Debug(query) // log.Debug(query)
txs := []*HistoryTx{} txs := []*HistoryTx{}
@ -438,6 +513,128 @@ 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
}
// TODO: Think about chaning all the queries that return a last value, to queries that return the next valid value.
// GetLastTxsPosition for a given to_forge_l1_txs_num
func (hdb *HistoryDB) GetLastTxsPosition(toForgeL1TxsNum int64) (int, error) {
row := hdb.db.QueryRow("SELECT MAX(position) FROM tx WHERE to_forge_l1_txs_num = $1;", toForgeL1TxsNum)
var lastL1TxsPosition int
return lastL1TxsPosition, row.Scan(&lastL1TxsPosition)
}
// AddBlockSCData stores all the information of a block retrieved by the Synchronizer
func (hdb *HistoryDB) AddBlockSCData(blockData *BlockData) (err error) {
txn, err := hdb.db.Begin()
if err != nil {
return err
}
defer func() {
if err != nil {
err = txn.Rollback()
}
}()
// Add block
err = hdb.addBlock(txn, blockData.block)
if err != nil {
return err
}
// Add l1 Txs
if len(blockData.L1UserTxs) > 0 {
err = hdb.addL1Txs(txn, blockData.L1UserTxs)
if err != nil {
return err
}
}
// Add Tokens
if len(blockData.RegisteredTokens) > 0 {
err = hdb.addTokens(txn, blockData.RegisteredTokens)
if err != nil {
return err
}
}
// Add Bids
if len(blockData.Bids) > 0 {
err = hdb.addBids(txn, blockData.Bids)
if err != nil {
return err
}
}
// Add Coordinators
if len(blockData.Coordinators) > 0 {
err = hdb.addCoordinators(txn, blockData.Coordinators)
if err != nil {
return err
}
}
// Add Batches
for _, batch := range blockData.Batches {
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 {
return err
}
}
}
// Add l2 Txs
if len(batch.L2Txs) > 0 {
err = hdb.addL2Txs(txn, batch.L2Txs)
if err != nil {
return err
}
}
// Add accounts
if len(batch.CreatedAccounts) > 0 {
err = hdb.addAccounts(txn, batch.CreatedAccounts)
if err != nil {
return err
}
}
// Add exit tree
if len(batch.ExitTree) > 0 {
err = hdb.addExitTree(txn, batch.ExitTree)
if err != nil {
return err
}
}
// 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 // Close frees the resources used by HistoryDB
func (hdb *HistoryDB) Close() error { func (hdb *HistoryDB) Close() error {
return hdb.db.Close() return hdb.db.Close()

+ 33
- 2
db/historydb/historydb_test.go

@ -119,10 +119,12 @@ func TestBids(t *testing.T) {
// Generate fake coordinators // Generate fake coordinators
const nCoords = 5 const nCoords = 5
coords := test.GenCoordinators(nCoords, blocks) coords := test.GenCoordinators(nCoords, blocks)
err := historyDB.AddCoordinators(coords)
assert.NoError(t, err)
// Generate fake bids // Generate fake bids
const nBids = 20 const nBids = 20
bids := test.GenBids(nBids, blocks, coords) bids := test.GenBids(nBids, blocks, coords)
err := historyDB.addBids(bids)
err = historyDB.AddBids(bids)
assert.NoError(t, err) assert.NoError(t, err)
// Fetch bids // Fetch bids
fetchedBids, err := historyDB.GetBids() fetchedBids, err := historyDB.GetBids()
@ -302,6 +304,35 @@ func TestTxs(t *testing.T) {
l2txs[0].Nonce = 0 l2txs[0].Nonce = 0
err = historyDB.AddL2Txs(l2txs) err = historyDB.AddL2Txs(l2txs)
assert.Error(t, err) assert.Error(t, err)
// Test helper functions for Synchronizer
txs, err := historyDB.GetL1UserTxs(2)
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)
}
func TestExitTree(t *testing.T) {
nBatches := 17
blocks := setTestBlocks(0, 10)
batches := test.GenBatches(nBatches, blocks)
err := historyDB.AddBatches(batches)
assert.NoError(t, err)
exitTree := test.GenExitTree(nBatches)
err = historyDB.AddExitTree(exitTree)
assert.NoError(t, err)
} }
// setTestBlocks WARNING: this will delete the blocks and recreate them // setTestBlocks WARNING: this will delete the blocks and recreate them
@ -317,5 +348,5 @@ func setTestBlocks(from, to int64) []common.Block {
} }
func cleanHistoryDB() error { func cleanHistoryDB() error {
return historyDB.Reorg(0)
return historyDB.Reorg(-1)
} }

+ 5
- 4
db/historydb/migrations/001_init.sql

@ -27,11 +27,12 @@ CREATE TABLE batch (
CREATE TABLE exit_tree ( CREATE TABLE exit_tree (
batch_num BIGINT REFERENCES batch (batch_num) ON DELETE CASCADE, batch_num BIGINT REFERENCES batch (batch_num) ON DELETE CASCADE,
withdrawn BIGINT REFERENCES batch (batch_num) ON DELETE SET NULL,
account_idx BIGINT, account_idx BIGINT,
merkle_proof BYTEA NOT NULL, merkle_proof BYTEA NOT NULL,
balance NUMERIC NOT NULL,
nullifier 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) PRIMARY KEY (batch_num, account_idx)
); );
@ -205,4 +206,4 @@ DROP TABLE bid;
DROP TABLE exit_tree; DROP TABLE exit_tree;
DROP TABLE batch; DROP TABLE batch;
DROP TABLE coordinator; DROP TABLE coordinator;
DROP TABLE block;
DROP TABLE block;

+ 3
- 3
db/l2db/l2db.go

@ -173,7 +173,7 @@ func (l2db *L2DB) InvalidateTxs(txIDs []common.TxID, batchNum common.BatchNum) e
// CheckNonces invalidate txs with nonces that are smaller or equal than their respective accounts nonces. // CheckNonces invalidate txs with nonces that are smaller or equal than their respective accounts nonces.
// The state of the affected txs will be changed from Pending -> Invalid // The state of the affected txs will be changed from Pending -> Invalid
func (l2db *L2DB) CheckNonces(updatedAccounts []common.Account, batchNum common.BatchNum) error {
func (l2db *L2DB) CheckNonces(updatedAccounts []common.Account, batchNum common.BatchNum) (err error) {
txn, err := l2db.db.Begin() txn, err := l2db.db.Begin()
if err != nil { if err != nil {
return err return err
@ -203,7 +203,7 @@ func (l2db *L2DB) CheckNonces(updatedAccounts []common.Account, batchNum common.
} }
// UpdateTxValue updates the absolute fee and value of txs given a token list that include their price in USD // UpdateTxValue updates the absolute fee and value of txs given a token list that include their price in USD
func (l2db *L2DB) UpdateTxValue(tokens []common.Token) error {
func (l2db *L2DB) UpdateTxValue(tokens []common.Token) (err error) {
// WARNING: this is very slow and should be optimized // WARNING: this is very slow and should be optimized
txn, err := l2db.db.Begin() txn, err := l2db.db.Begin()
if err != nil { if err != nil {
@ -252,7 +252,7 @@ func (l2db *L2DB) Reorg(lastValidBatch common.BatchNum) error {
// Purge deletes transactions that have been forged or marked as invalid for longer than the safety period // Purge deletes transactions that have been forged or marked as invalid for longer than the safety period
// it also deletes txs that has been in the L2DB for longer than the ttl if maxTxs has been exceeded // it also deletes txs that has been in the L2DB for longer than the ttl if maxTxs has been exceeded
func (l2db *L2DB) Purge(currentBatchNum common.BatchNum) error {
func (l2db *L2DB) Purge(currentBatchNum common.BatchNum) (err error) {
txn, err := l2db.db.Begin() txn, err := l2db.db.Begin()
if err != nil { if err != nil {
return err return err

+ 1
- 16
db/statedb/txprocessors.go

@ -8,7 +8,6 @@ import (
"github.com/hermeznetwork/hermez-node/common" "github.com/hermeznetwork/hermez-node/common"
"github.com/hermeznetwork/hermez-node/log" "github.com/hermeznetwork/hermez-node/log"
"github.com/iden3/go-iden3-crypto/babyjub" "github.com/iden3/go-iden3-crypto/babyjub"
"github.com/iden3/go-iden3-crypto/poseidon"
"github.com/iden3/go-merkletree" "github.com/iden3/go-merkletree"
"github.com/iden3/go-merkletree/db" "github.com/iden3/go-merkletree/db"
"github.com/iden3/go-merkletree/db/memory" "github.com/iden3/go-merkletree/db/memory"
@ -142,24 +141,10 @@ func (s *StateDB) ProcessTxs(cmpExitTree, cmpZKInputs bool, l1usertxs, l1coordin
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
// 1. compute nullifier
exitAccStateValue, err := exitAccount.HashValue()
if err != nil {
return nil, nil, err
}
nullifier, err := poseidon.Hash([]*big.Int{
exitAccStateValue,
big.NewInt(int64(s.currentBatch)),
exitTree.Root().BigInt(),
})
if err != nil {
return nil, nil, err
}
// 2. generate common.ExitInfo
// 1. generate common.ExitInfo
ei := &common.ExitInfo{ ei := &common.ExitInfo{
AccountIdx: exitIdx, AccountIdx: exitIdx,
MerkleProof: p, MerkleProof: p,
Nullifier: nullifier,
Balance: exitAccount.Balance, Balance: exitAccount.Balance,
} }
exitInfos = append(exitInfos, ei) exitInfos = append(exitInfos, ei)

+ 6
- 6
synchronizer/synchronizer.go

@ -501,9 +501,9 @@ func (s *Synchronizer) auctionSync(blockNum int64) (*auctionData, error) {
// Get Coordinators // Get Coordinators
for _, eNewCoordinator := range auctionEvents.NewCoordinator { for _, eNewCoordinator := range auctionEvents.NewCoordinator {
coordinator := &common.Coordinator{ coordinator := &common.Coordinator{
Forger: eNewCoordinator.ForgerAddress,
Withdraw: eNewCoordinator.WithdrawalAddress,
URL: eNewCoordinator.URL,
Forger: eNewCoordinator.ForgerAddress,
WithdrawAddr: eNewCoordinator.WithdrawalAddress,
URL: eNewCoordinator.URL,
} }
auctionData.coordinators = append(auctionData.coordinators, coordinator) auctionData.coordinators = append(auctionData.coordinators, coordinator)
} }
@ -525,9 +525,9 @@ func (s *Synchronizer) auctionSync(blockNum int64) (*auctionData, error) {
// Get Coordinators from updates // Get Coordinators from updates
for _, eCoordinatorUpdated := range auctionEvents.CoordinatorUpdated { for _, eCoordinatorUpdated := range auctionEvents.CoordinatorUpdated {
coordinator := &common.Coordinator{ coordinator := &common.Coordinator{
Forger: eCoordinatorUpdated.ForgerAddress,
Withdraw: eCoordinatorUpdated.WithdrawalAddress,
URL: eCoordinatorUpdated.URL,
Forger: eCoordinatorUpdated.ForgerAddress,
WithdrawAddr: eCoordinatorUpdated.WithdrawalAddress,
URL: eCoordinatorUpdated.URL,
} }
auctionData.coordinators = append(auctionData.coordinators, coordinator) auctionData.coordinators = append(auctionData.coordinators, coordinator)
} }

+ 41
- 10
test/historydb.go

@ -10,6 +10,7 @@ import (
ethCommon "github.com/ethereum/go-ethereum/common" ethCommon "github.com/ethereum/go-ethereum/common"
"github.com/hermeznetwork/hermez-node/common" "github.com/hermeznetwork/hermez-node/common"
"github.com/iden3/go-iden3-crypto/babyjub" "github.com/iden3/go-iden3-crypto/babyjub"
"github.com/iden3/go-merkletree"
) )
// WARNING: the generators in this file doesn't necessary follow the protocol // WARNING: the generators in this file doesn't necessary follow the protocol
@ -142,7 +143,7 @@ func GenL1Txs(
continue continue
} }
if i < nUserTxs { if i < nUserTxs {
var from, to common.Account
var from, to *common.Account
var err error var err error
if i%2 == 0 { if i%2 == 0 {
from, err = randomAccount(i, true, userAddr, accounts) from, err = randomAccount(i, true, userAddr, accounts)
@ -216,7 +217,7 @@ func GenL2Txs(
Type: randomTxType(i), Type: randomTxType(i),
} }
if i < nUserTxs { if i < nUserTxs {
var from, to common.Account
var from, to *common.Account
var err error var err error
if i%2 == 0 { if i%2 == 0 {
from, err = randomAccount(i, true, userAddr, accounts) from, err = randomAccount(i, true, userAddr, accounts)
@ -262,10 +263,10 @@ func GenCoordinators(nCoords int, blocks []common.Block) []common.Coordinator {
coords := []common.Coordinator{} coords := []common.Coordinator{}
for i := 0; i < nCoords; i++ { for i := 0; i < nCoords; i++ {
coords = append(coords, common.Coordinator{ coords = append(coords, common.Coordinator{
EthBlockNum: blocks[i%len(blocks)].EthBlockNum,
Forger: ethCommon.BigToAddress(big.NewInt(int64(i))),
Withdraw: ethCommon.BigToAddress(big.NewInt(int64(i))),
URL: "https://foo.bar",
EthBlockNum: blocks[i%len(blocks)].EthBlockNum,
Forger: ethCommon.BigToAddress(big.NewInt(int64(i))),
WithdrawAddr: ethCommon.BigToAddress(big.NewInt(int64(i))),
URL: "https://foo.bar",
}) })
} }
return coords return coords
@ -285,21 +286,51 @@ func GenBids(nBids int, blocks []common.Block, coords []common.Coordinator) []co
return bids return bids
} }
func randomAccount(seed int, userAccount bool, userAddr *ethCommon.Address, accs []common.Account) (common.Account, error) {
// GenExitTree generates an exitTree (as an array of Exits)
//nolint:gomnd
func GenExitTree(n int) []common.ExitInfo {
exitTree := make([]common.ExitInfo, n)
for i := 0; i < n; i++ {
exitTree[i] = common.ExitInfo{
BatchNum: common.BatchNum(i + 1),
InstantWithdrawn: nil,
DelayedWithdrawRequest: nil,
DelayedWithdrawn: nil,
AccountIdx: common.Idx(i * 10),
MerkleProof: &merkletree.CircomVerifierProof{
Root: &merkletree.Hash{byte(i), byte(i + 1)},
Siblings: []*big.Int{
big.NewInt(int64(i) * 10),
big.NewInt(int64(i)*100 + 1),
big.NewInt(int64(i)*1000 + 2)},
OldKey: &merkletree.Hash{byte(i * 1), byte(i*1 + 1)},
OldValue: &merkletree.Hash{byte(i * 2), byte(i*2 + 1)},
IsOld0: i%2 == 0,
Key: &merkletree.Hash{byte(i * 3), byte(i*3 + 1)},
Value: &merkletree.Hash{byte(i * 4), byte(i*4 + 1)},
Fnc: i % 2,
},
Balance: big.NewInt(int64(i) * 1000),
}
}
return exitTree
}
func randomAccount(seed int, userAccount bool, userAddr *ethCommon.Address, accs []common.Account) (*common.Account, error) {
i := seed % len(accs) i := seed % len(accs)
firstI := i firstI := i
for { for {
acc := accs[i] acc := accs[i]
if userAccount && *userAddr == acc.EthAddr { if userAccount && *userAddr == acc.EthAddr {
return acc, nil
return &acc, nil
} }
if !userAccount && (userAddr == nil || *userAddr != acc.EthAddr) { if !userAccount && (userAddr == nil || *userAddr != acc.EthAddr) {
return acc, nil
return &acc, nil
} }
i++ i++
i = i % len(accs) i = i % len(accs)
if i == firstI { if i == firstI {
return acc, errors.New("Didnt found any account matchinng the criteria")
return &acc, errors.New("Didnt found any account matchinng the criteria")
} }
} }
} }

+ 2
- 2
test/l2db.go

@ -13,10 +13,10 @@ import (
// CleanL2DB deletes 'tx_pool' and 'account_creation_auth' from the given DB // CleanL2DB deletes 'tx_pool' and 'account_creation_auth' from the given DB
func CleanL2DB(db *sqlx.DB) { func CleanL2DB(db *sqlx.DB) {
if _, err := db.Exec("DELETE FROM tx_pool"); err != nil {
if _, err := db.Exec("DELETE FROM tx_pool;"); err != nil {
panic(err) panic(err)
} }
if _, err := db.Exec("DELETE FROM account_creation_auth"); err != nil {
if _, err := db.Exec("DELETE FROM account_creation_auth;"); err != nil {
panic(err) panic(err)
} }
} }

Loading…
Cancel
Save