|
package l2db
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
ethCommon "github.com/ethereum/go-ethereum/common"
|
|
"github.com/hermeznetwork/hermez-node/common"
|
|
"github.com/hermeznetwork/tracerr"
|
|
"github.com/russross/meddler"
|
|
)
|
|
|
|
var (
|
|
errPoolFull = fmt.Errorf("the pool is at full capacity. More transactions are not accepted currently")
|
|
)
|
|
|
|
// AddAccountCreationAuthAPI inserts an account creation authorization into the DB
|
|
func (l2db *L2DB) AddAccountCreationAuthAPI(auth *common.AccountCreationAuth) error {
|
|
cancel, err := l2db.apiConnCon.Acquire()
|
|
defer cancel()
|
|
if err != nil {
|
|
return tracerr.Wrap(err)
|
|
}
|
|
defer l2db.apiConnCon.Release()
|
|
return l2db.AddAccountCreationAuth(auth)
|
|
}
|
|
|
|
// GetAccountCreationAuthAPI returns an account creation authorization from the DB
|
|
func (l2db *L2DB) GetAccountCreationAuthAPI(addr ethCommon.Address) (*AccountCreationAuthAPI, error) {
|
|
cancel, err := l2db.apiConnCon.Acquire()
|
|
defer cancel()
|
|
if err != nil {
|
|
return nil, tracerr.Wrap(err)
|
|
}
|
|
defer l2db.apiConnCon.Release()
|
|
auth := new(AccountCreationAuthAPI)
|
|
return auth, tracerr.Wrap(meddler.QueryRow(
|
|
l2db.dbRead, auth,
|
|
"SELECT * FROM account_creation_auth WHERE eth_addr = $1;",
|
|
addr,
|
|
))
|
|
}
|
|
|
|
// AddTxAPI inserts a tx to the pool
|
|
func (l2db *L2DB) AddTxAPI(tx *PoolL2TxWrite) error {
|
|
cancel, err := l2db.apiConnCon.Acquire()
|
|
defer cancel()
|
|
if err != nil {
|
|
return tracerr.Wrap(err)
|
|
}
|
|
defer l2db.apiConnCon.Release()
|
|
|
|
row := l2db.dbRead.QueryRow(`SELECT
|
|
($1::NUMERIC * COALESCE(token.usd, 0) * fee_percentage($2::NUMERIC)) /
|
|
(10.0 ^ token.decimals::NUMERIC)
|
|
FROM token WHERE token.token_id = $3;`,
|
|
tx.AmountFloat, tx.Fee, tx.TokenID)
|
|
var feeUSD float64
|
|
if err := row.Scan(&feeUSD); err != nil {
|
|
return tracerr.Wrap(err)
|
|
}
|
|
if feeUSD < l2db.minFeeUSD {
|
|
return tracerr.Wrap(fmt.Errorf("tx.feeUSD (%v) < minFeeUSD (%v)",
|
|
feeUSD, l2db.minFeeUSD))
|
|
}
|
|
if feeUSD > l2db.maxFeeUSD {
|
|
return tracerr.Wrap(fmt.Errorf("tx.feeUSD (%v) > maxFeeUSD (%v)",
|
|
feeUSD, l2db.maxFeeUSD))
|
|
}
|
|
|
|
// Prepare insert SQL query argument parameters
|
|
namesPart, err := meddler.Default.ColumnsQuoted(tx, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
valuesPart, err := meddler.Default.PlaceholdersString(tx, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
values, err := meddler.Default.Values(tx, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
q := fmt.Sprintf(
|
|
`INSERT INTO tx_pool (%s)
|
|
SELECT %s
|
|
WHERE (SELECT COUNT(*) FROM tx_pool WHERE state = $%v AND NOT external_delete) < $%v;`,
|
|
namesPart, valuesPart,
|
|
len(values)+1, len(values)+2) //nolint:gomnd
|
|
values = append(values, common.PoolL2TxStatePending, l2db.maxTxs)
|
|
res, err := l2db.dbWrite.Exec(q, values...)
|
|
if err != nil {
|
|
return tracerr.Wrap(err)
|
|
}
|
|
rowsAffected, err := res.RowsAffected()
|
|
if err != nil {
|
|
return tracerr.Wrap(err)
|
|
}
|
|
if rowsAffected == 0 {
|
|
return tracerr.Wrap(errPoolFull)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// selectPoolTxAPI select part of queries to get PoolL2TxRead
|
|
const selectPoolTxAPI = `SELECT tx_pool.tx_id, hez_idx(tx_pool.from_idx, token.symbol) AS from_idx, tx_pool.effective_from_eth_addr,
|
|
tx_pool.effective_from_bjj, hez_idx(tx_pool.to_idx, token.symbol) AS to_idx, tx_pool.effective_to_eth_addr,
|
|
tx_pool.effective_to_bjj, tx_pool.token_id, tx_pool.amount, tx_pool.fee, tx_pool.nonce,
|
|
tx_pool.state, tx_pool.info, tx_pool.signature, tx_pool.timestamp, tx_pool.batch_num, hez_idx(tx_pool.rq_from_idx, token.symbol) AS rq_from_idx,
|
|
hez_idx(tx_pool.rq_to_idx, token.symbol) AS rq_to_idx, tx_pool.rq_to_eth_addr, tx_pool.rq_to_bjj, tx_pool.rq_token_id, tx_pool.rq_amount,
|
|
tx_pool.rq_fee, tx_pool.rq_nonce, tx_pool.tx_type,
|
|
token.item_id AS token_item_id, token.eth_block_num, token.eth_addr, token.name, token.symbol, token.decimals, token.usd, token.usd_update
|
|
FROM tx_pool INNER JOIN token ON tx_pool.token_id = token.token_id `
|
|
|
|
// GetTxAPI return the specified Tx in PoolTxAPI format
|
|
func (l2db *L2DB) GetTxAPI(txID common.TxID) (*PoolTxAPI, error) {
|
|
cancel, err := l2db.apiConnCon.Acquire()
|
|
defer cancel()
|
|
if err != nil {
|
|
return nil, tracerr.Wrap(err)
|
|
}
|
|
defer l2db.apiConnCon.Release()
|
|
tx := new(PoolTxAPI)
|
|
return tx, tracerr.Wrap(meddler.QueryRow(
|
|
l2db.dbRead, tx,
|
|
selectPoolTxAPI+"WHERE tx_id = $1;",
|
|
txID,
|
|
))
|
|
}
|
|
|
|
// GetPoolTxs return Txs from the pool
|
|
func (l2db *L2DB) GetPoolTxs(fromIdx, toIdx *common.Idx, state *common.PoolL2TxState) ([]*PoolTxAPI, error) {
|
|
cancel, err := l2db.apiConnCon.Acquire()
|
|
defer cancel()
|
|
if err != nil {
|
|
return nil, tracerr.Wrap(err)
|
|
}
|
|
defer l2db.apiConnCon.Release()
|
|
// Apply filters
|
|
nextIsAnd := false
|
|
queryStr := selectPoolTxAPI
|
|
var args []interface{}
|
|
if state != nil {
|
|
queryStr += "WHERE state = ? "
|
|
args = append(args, state)
|
|
nextIsAnd = true
|
|
}
|
|
|
|
if fromIdx != nil && toIdx != nil {
|
|
if nextIsAnd {
|
|
queryStr += "AND ("
|
|
} else {
|
|
queryStr += "WHERE ("
|
|
}
|
|
queryStr += "tx_pool.from_idx = ? "
|
|
queryStr += "OR tx_pool.to_idx = ?) "
|
|
args = append(args, fromIdx)
|
|
args = append(args, toIdx)
|
|
} else if fromIdx != nil {
|
|
if nextIsAnd {
|
|
queryStr += "AND "
|
|
} else {
|
|
queryStr += "WHERE "
|
|
}
|
|
queryStr += "tx_pool.from_idx = ?"
|
|
args = append(args, fromIdx)
|
|
} else if toIdx != nil {
|
|
if nextIsAnd {
|
|
queryStr += "AND "
|
|
} else {
|
|
queryStr += "WHERE "
|
|
}
|
|
queryStr += "tx_pool.to_idx = ?"
|
|
args = append(args, toIdx)
|
|
}
|
|
queryStr += "AND NOT external_delete;"
|
|
query := l2db.dbRead.Rebind(queryStr)
|
|
txs := []*PoolTxAPI{}
|
|
err = meddler.QueryAll(
|
|
l2db.dbRead, &txs,
|
|
query,
|
|
args...)
|
|
return txs, tracerr.Wrap(err)
|
|
}
|