hermez-node/db/l2db/l2db.go
db/l2db/l2db.goRaw
1package l2db
2
3import (
4 "fmt"
5 "strconv"
6 "time"
7
8 eth "github.com/ethereum/go-ethereum/common"
9 "github.com/gobuffalo/packr/v2"
10 "github.com/hermeznetwork/hermez-node/common"
11 "github.com/hermeznetwork/hermez-node/db"
12 "github.com/jmoiron/sqlx"
13
14 //nolint:errcheck // driver for postgres DB
15 _ "github.com/lib/pq"
16 migrate "github.com/rubenv/sql-migrate"
17 "github.com/russross/meddler"
18)
19
20// TODO(Edu): Check DB consistency while there's concurrent use from Coordinator/TxSelector & API
21
22// L2DB stores L2 txs and authorization registers received by the coordinator and keeps them until they are no longer relevant
23// due to them being forged or invalid after a safety period
24type L2DB struct {
25 db *sqlx.DB
26 safetyPeriod uint16
27 ttl time.Duration
28 maxTxs uint32
29}
30
31// NewL2DB creates a L2DB.
32// More info on how to set dbDialect and dbArgs here: http://gorm.io/docs/connecting_to_the_database.html
33// safetyPeriod is the amount of blockchain blocks that must be waited before deleting anything (to avoid reorg problems).
34// maxTxs indicates the desired maximum amount of txs stored on the L2DB.
35// TTL indicates the maximum amount of time that a tx can be in the L2DB
36// (to prevent tx that won't ever be forged to stay there, will be used if maxTxs is exceeded).
37// autoPurgePeriod will be used as delay between calls to Purge. If the value is 0, it will be disabled.
38func NewL2DB(
39 port int, host, user, password, dbname string,
40 safetyPeriod uint16,
41 maxTxs uint32,
42 TTL time.Duration,
43) (*L2DB, error) {
44 // init meddler
45 db.InitMeddler()
46 meddler.Default = meddler.PostgreSQL
47 // Stablish DB connection
48 psqlconn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", host, port, user, password, dbname)
49 db, err := sqlx.Connect("postgres", psqlconn)
50 if err != nil {
51 return nil, err
52 }
53
54 // Run DB migrations
55 migrations := &migrate.PackrMigrationSource{
56 Box: packr.New("history-migrations", "./migrations"),
57 }
58 if _, err := migrate.Exec(db.DB, "postgres", migrations, migrate.Up); err != nil {
59 return nil, err
60 }
61
62 return &L2DB{
63 db: db,
64 safetyPeriod: safetyPeriod,
65 ttl: TTL,
66 maxTxs: maxTxs,
67 }, nil
68}
69
70// DB returns a pointer to the L2DB.db. This method should be used only for
71// internal testing purposes.
72func (l2db *L2DB) DB() *sqlx.DB {
73 return l2db.db
74}
75
76// AddTx inserts a tx into the L2DB
77func (l2db *L2DB) AddTx(tx *common.PoolL2Tx) error {
78 return meddler.Insert(l2db.db, "tx_pool", tx)
79}
80
81// AddAccountCreationAuth inserts an account creation authorization into the DB
82func (l2db *L2DB) AddAccountCreationAuth(auth *common.AccountCreationAuth) error {
83 // TODO: impl
84 return nil
85}
86
87// GetTx return the specified Tx
88func (l2db *L2DB) GetTx(txID common.TxID) (*common.PoolL2Tx, error) {
89 tx := new(common.PoolL2Tx)
90 return tx, meddler.QueryRow(
91 l2db.db, tx,
92 "SELECT * FROM tx_pool WHERE tx_id = $1;",
93 txID,
94 )
95}
96
97// GetPendingTxs return all the pending txs of the L2DB
98func (l2db *L2DB) GetPendingTxs() ([]*common.PoolL2Tx, error) {
99 var txs []*common.PoolL2Tx
100 err := meddler.QueryAll(
101 l2db.db, &txs,
102 "SELECT * FROM tx_pool WHERE state = $1",
103 common.PoolL2TxStatePending,
104 )
105 return txs, err
106}
107
108// GetAccountCreationAuth return the authorization to make registers of an Ethereum address
109func (l2db *L2DB) GetAccountCreationAuth(ethAddr eth.Address) (*common.AccountCreationAuth, error) {
110 // TODO: impl
111 return nil, nil
112}
113
114// StartForging updates the state of the transactions that will begin the forging process.
115// The state of the txs referenced by txIDs will be changed from Pending -> Forging
116func (l2db *L2DB) StartForging(txIDs []common.TxID, batchNum common.BatchNum) error {
117 query, args, err := sqlx.In(
118 `UPDATE tx_pool
119 SET state = ?, batch_num = ?
120 WHERE state = ? AND tx_id IN (?);`,
121 string(common.PoolL2TxStateForging),
122 strconv.Itoa(int(batchNum)),
123 string(common.PoolL2TxStatePending),
124 txIDs,
125 )
126 if err != nil {
127 return err
128 }
129 query = l2db.db.Rebind(query)
130 _, err = l2db.db.Exec(query, args...)
131 return err
132}
133
134// DoneForging updates the state of the transactions that have been forged
135// so the state of the txs referenced by txIDs will be changed from Forging -> Forged
136func (l2db *L2DB) DoneForging(txIDs []common.TxID) error {
137 // TODO: impl
138 return nil
139}
140
141// InvalidateTxs updates the state of the transactions that are invalid.
142// The state of the txs referenced by txIDs will be changed from * -> Invalid
143func (l2db *L2DB) InvalidateTxs(txIDs []common.TxID) error {
144 return nil
145}
146
147// CheckNonces invalidate txs with nonces that are smaller than their respective accounts nonces.
148// The state of the affected txs will be changed from Pending -> Invalid
149func (l2db *L2DB) CheckNonces(updatedAccounts []common.Account) error {
150 // TODO: impl
151 return nil
152}
153
154// GetTxsByAbsoluteFeeUpdate return the txs that have an AbsoluteFee updated before olderThan
155func (l2db *L2DB) GetTxsByAbsoluteFeeUpdate(olderThan time.Time) ([]*common.PoolL2Tx, error) {
156 // TODO: impl
157 return nil, nil
158}
159
160// UpdateTxs update existing txs from the pool (TxID must exist)
161func (l2db *L2DB) UpdateTxs(txs []*common.PoolL2Tx) error {
162 // TODO: impl
163 return nil
164}
165
166// Reorg updates the state of txs that were updated in a batch that has been discarted due to a blockchain reorg.
167// The state of the affected txs can change form Forged -> Pending or from Invalid -> Pending
168func (l2db *L2DB) Reorg(lastValidBatch common.BatchNum) error {
169 // TODO: impl
170 return nil
171}
172
173// Purge deletes transactions that have been forged or marked as invalid for longer than the safety period
174// it also deletes txs that has been in the L2DB for longer than the ttl if maxTxs has been exceeded
175func (l2db *L2DB) Purge() error {
176 // TODO: impl
177 return nil
178}
179
180// Close frees the resources used by the L2DB
181func (l2db *L2DB) Close() error {
182 return l2db.db.Close()
183}