github.com/0xPolygon/supernets2-node@v0.0.0-20230711153321-2fe574524eaa/pool/pgpoolstorage/pgpoolstorage.go (about)

     1  package pgpoolstorage
     2  
     3  import (
     4  	"context"
     5  	"database/sql"
     6  	"errors"
     7  	"time"
     8  
     9  	"github.com/0xPolygon/supernets2-node/db"
    10  	"github.com/0xPolygon/supernets2-node/hex"
    11  	"github.com/0xPolygon/supernets2-node/pool"
    12  	"github.com/0xPolygon/supernets2-node/state"
    13  	"github.com/ethereum/go-ethereum/common"
    14  	"github.com/ethereum/go-ethereum/core/types"
    15  	"github.com/jackc/pgx/v4"
    16  	"github.com/jackc/pgx/v4/pgxpool"
    17  )
    18  
    19  // PostgresPoolStorage is an implementation of the Pool interface
    20  // that uses a postgres database to store the data
    21  type PostgresPoolStorage struct {
    22  	db *pgxpool.Pool
    23  }
    24  
    25  // NewPostgresPoolStorage creates and initializes an instance of PostgresPoolStorage
    26  func NewPostgresPoolStorage(cfg db.Config) (*PostgresPoolStorage, error) {
    27  	poolDB, err := db.NewSQLDB(cfg)
    28  	if err != nil {
    29  		return nil, err
    30  	}
    31  
    32  	return &PostgresPoolStorage{
    33  		db: poolDB,
    34  	}, nil
    35  }
    36  
    37  // AddTx adds a transaction to the pool table with the provided status
    38  func (p *PostgresPoolStorage) AddTx(ctx context.Context, tx pool.Transaction) error {
    39  	hash := tx.Hash().Hex()
    40  
    41  	b, err := tx.MarshalBinary()
    42  	if err != nil {
    43  		return err
    44  	}
    45  	encoded := hex.EncodeToHex(b)
    46  
    47  	b, err = tx.MarshalJSON()
    48  	if err != nil {
    49  		return err
    50  	}
    51  	decoded := string(b)
    52  
    53  	gasPrice := tx.GasPrice().Uint64()
    54  	nonce := tx.Nonce()
    55  
    56  	sql := `
    57  		INSERT INTO pool.transaction 
    58  		(
    59  			hash,
    60  			encoded,
    61  			decoded,
    62  			status,
    63  			gas_price,
    64  			nonce,
    65  			cumulative_gas_used,
    66  			used_keccak_hashes,
    67  			used_poseidon_hashes,
    68  			used_poseidon_paddings,
    69  			used_mem_aligns,
    70  			used_arithmetics,
    71  			used_binaries,
    72  			used_steps,
    73  			received_at,
    74  			from_address,
    75  			is_wip,
    76  			ip
    77  		) 
    78  		VALUES 
    79  			($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)
    80  			ON CONFLICT (hash) DO UPDATE SET 
    81  			encoded = $2,
    82  			decoded = $3,
    83  			status = $4,
    84  			gas_price = $5,
    85  			nonce = $6,
    86  			cumulative_gas_used = $7,
    87  			used_keccak_hashes = $8, 
    88  			used_poseidon_hashes = $9,
    89  			used_poseidon_paddings = $10,
    90  			used_mem_aligns = $11,
    91  			used_arithmetics = $12,
    92  			used_binaries = $13,
    93  			used_steps = $14,
    94  			received_at = $15,
    95  			from_address = $16,
    96  			is_wip = $17,
    97  			ip = $18
    98  	`
    99  
   100  	// Get FromAddress from the JSON data
   101  	data, err := state.GetSender(tx.Transaction)
   102  	if err != nil {
   103  		return err
   104  	}
   105  	fromAddress := data.String()
   106  
   107  	if _, err := p.db.Exec(ctx, sql,
   108  		hash,
   109  		encoded,
   110  		decoded,
   111  		tx.Status,
   112  		gasPrice,
   113  		nonce,
   114  		tx.CumulativeGasUsed,
   115  		tx.UsedKeccakHashes,
   116  		tx.UsedPoseidonHashes,
   117  		tx.UsedPoseidonPaddings,
   118  		tx.UsedMemAligns,
   119  		tx.UsedArithmetics,
   120  		tx.UsedBinaries,
   121  		tx.UsedSteps,
   122  		tx.ReceivedAt,
   123  		fromAddress,
   124  		tx.IsWIP,
   125  		tx.IP); err != nil {
   126  		return err
   127  	}
   128  	return nil
   129  }
   130  
   131  // GetTxsByStatus returns an array of transactions filtered by status
   132  // limit parameter is used to limit amount txs from the db,
   133  // if limit = 0, then there is no limit
   134  func (p *PostgresPoolStorage) GetTxsByStatus(ctx context.Context, status pool.TxStatus, limit uint64) ([]pool.Transaction, error) {
   135  	var (
   136  		rows pgx.Rows
   137  		err  error
   138  		sql  string
   139  	)
   140  	if limit == 0 {
   141  		sql = "SELECT encoded, status, received_at, is_wip, ip, failed_reason FROM pool.transaction WHERE status = $1 ORDER BY gas_price DESC"
   142  		rows, err = p.db.Query(ctx, sql, status.String())
   143  	} else {
   144  		sql = "SELECT encoded, status, received_at, is_wip, ip, failed_reason FROM pool.transaction WHERE status = $1 ORDER BY gas_price DESC LIMIT $2"
   145  		rows, err = p.db.Query(ctx, sql, status.String(), limit)
   146  	}
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  	defer rows.Close()
   151  
   152  	txs := make([]pool.Transaction, 0, len(rows.RawValues()))
   153  	for rows.Next() {
   154  		tx, err := scanTx(rows)
   155  		if err != nil {
   156  			return nil, err
   157  		}
   158  		txs = append(txs, *tx)
   159  	}
   160  
   161  	return txs, nil
   162  }
   163  
   164  // GetNonWIPTxsByStatus returns an array of transactions filtered by status
   165  // limit parameter is used to limit amount txs from the db,
   166  // if limit = 0, then there is no limit
   167  func (p *PostgresPoolStorage) GetNonWIPTxsByStatus(ctx context.Context, status pool.TxStatus, limit uint64) ([]pool.Transaction, error) {
   168  	var (
   169  		rows pgx.Rows
   170  		err  error
   171  		sql  string
   172  	)
   173  	if limit == 0 {
   174  		sql = "SELECT encoded, status, received_at, is_wip, ip, failed_reason FROM pool.transaction WHERE is_wip IS FALSE and status = $1 ORDER BY gas_price DESC"
   175  		rows, err = p.db.Query(ctx, sql, status.String())
   176  	} else {
   177  		sql = "SELECT encoded, status, received_at, is_wip, ip, failed_reason FROM pool.transaction WHERE is_wip IS FALSE and status = $1 ORDER BY gas_price DESC LIMIT $2"
   178  		rows, err = p.db.Query(ctx, sql, status.String(), limit)
   179  	}
   180  	if err != nil {
   181  		return nil, err
   182  	}
   183  	defer rows.Close()
   184  
   185  	txs := make([]pool.Transaction, 0, len(rows.RawValues()))
   186  	for rows.Next() {
   187  		tx, err := scanTx(rows)
   188  		if err != nil {
   189  			return nil, err
   190  		}
   191  		txs = append(txs, *tx)
   192  	}
   193  
   194  	return txs, nil
   195  }
   196  
   197  // GetPendingTxHashesSince returns the pending tx since the given time.
   198  func (p *PostgresPoolStorage) GetPendingTxHashesSince(ctx context.Context, since time.Time) ([]common.Hash, error) {
   199  	sql := "SELECT hash FROM pool.transaction WHERE status = $1 AND received_at >= $2"
   200  	rows, err := p.db.Query(ctx, sql, pool.TxStatusPending, since)
   201  	if err != nil {
   202  		return nil, err
   203  	}
   204  	defer rows.Close()
   205  
   206  	hashes := make([]common.Hash, 0, len(rows.RawValues()))
   207  	for rows.Next() {
   208  		var hash string
   209  		if err := rows.Scan(&hash); err != nil {
   210  			return nil, err
   211  		}
   212  		hashes = append(hashes, common.HexToHash(hash))
   213  	}
   214  
   215  	return hashes, nil
   216  }
   217  
   218  // GetTxs gets txs with the lowest nonce
   219  func (p *PostgresPoolStorage) GetTxs(ctx context.Context, filterStatus pool.TxStatus, minGasPrice, limit uint64) ([]*pool.Transaction, error) {
   220  	query := `
   221  		SELECT
   222  			encoded,
   223  			status,
   224  			cumulative_gas_used,
   225  			used_keccak_hashes,
   226  			used_poseidon_hashes,
   227  			used_poseidon_paddings,
   228  			used_mem_aligns,
   229  			used_arithmetics,
   230  			used_binaries,
   231  			used_steps,
   232  			received_at,
   233  			nonce,
   234  			is_wip,
   235  			ip
   236  		FROM
   237  			pool.transaction p1
   238  		WHERE 
   239  			status = $1 AND
   240  			gas_price >= $2
   241  		ORDER BY 
   242  			nonce ASC
   243  		LIMIT $3
   244  	`
   245  
   246  	if filterStatus == pool.TxStatusFailed {
   247  		query = `
   248  		SELECT * FROM (
   249  			SELECT
   250  				encoded,
   251  				status,
   252  				cumulative_gas_used,
   253  				used_keccak_hashes,
   254  				used_poseidon_hashes,
   255  				used_poseidon_paddings,
   256  				used_mem_aligns,
   257  				used_arithmetics,
   258  				used_binaries,
   259  				used_steps,
   260  				received_at,
   261  				nonce,
   262  				is_wip,
   263  				ip
   264  			FROM
   265  				pool.transaction p1
   266  			WHERE
   267  				status = $1 AND
   268  				gas_price >= $2 
   269  			ORDER BY 
   270  				nonce ASC
   271  			LIMIT $3
   272  			) as tmp
   273  		ORDER BY nonce ASC
   274  		`
   275  	}
   276  
   277  	var (
   278  		encoded, status, ip string
   279  		receivedAt          time.Time
   280  		cumulativeGasUsed   uint64
   281  
   282  		usedKeccakHashes, usedPoseidonHashes, usedPoseidonPaddings,
   283  		usedMemAligns, usedArithmetics, usedBinaries, usedSteps uint32
   284  		nonce uint64
   285  		isWIP bool
   286  	)
   287  
   288  	args := []interface{}{filterStatus, minGasPrice, limit}
   289  
   290  	rows, err := p.db.Query(ctx, query, args...)
   291  	if errors.Is(err, pgx.ErrNoRows) {
   292  		return nil, pool.ErrNotFound
   293  	} else if err != nil {
   294  		return nil, err
   295  	}
   296  
   297  	txs := make([]*pool.Transaction, 0, len(rows.RawValues()))
   298  	for rows.Next() {
   299  		err := rows.Scan(
   300  			&encoded,
   301  			&status,
   302  			&cumulativeGasUsed,
   303  			&usedKeccakHashes,
   304  			&usedPoseidonHashes,
   305  			&usedPoseidonPaddings,
   306  			&usedMemAligns,
   307  			&usedArithmetics,
   308  			&usedBinaries,
   309  			&usedSteps,
   310  			&receivedAt,
   311  			&nonce,
   312  			&isWIP,
   313  			&ip,
   314  		)
   315  
   316  		if err != nil {
   317  			return nil, err
   318  		}
   319  		tx := new(pool.Transaction)
   320  		b, err := hex.DecodeHex(encoded)
   321  		if err != nil {
   322  			return nil, err
   323  		}
   324  		if err := tx.UnmarshalBinary(b); err != nil {
   325  			return nil, err
   326  		}
   327  
   328  		tx.Status = pool.TxStatus(status)
   329  		tx.ReceivedAt = receivedAt
   330  		tx.ZKCounters = state.ZKCounters{
   331  			CumulativeGasUsed:    cumulativeGasUsed,
   332  			UsedKeccakHashes:     usedKeccakHashes,
   333  			UsedPoseidonHashes:   usedPoseidonHashes,
   334  			UsedPoseidonPaddings: usedPoseidonPaddings,
   335  			UsedMemAligns:        usedMemAligns,
   336  			UsedArithmetics:      usedArithmetics,
   337  			UsedBinaries:         usedBinaries,
   338  			UsedSteps:            usedSteps,
   339  		}
   340  		tx.IsWIP = isWIP
   341  		tx.IP = ip
   342  
   343  		txs = append(txs, tx)
   344  	}
   345  
   346  	return txs, nil
   347  }
   348  
   349  // CountTransactionsByStatus get number of transactions
   350  // accordingly to the provided status
   351  func (p *PostgresPoolStorage) CountTransactionsByStatus(ctx context.Context, status pool.TxStatus) (uint64, error) {
   352  	sql := "SELECT COUNT(*) FROM pool.transaction WHERE status = $1"
   353  	var counter uint64
   354  	err := p.db.QueryRow(ctx, sql, status.String()).Scan(&counter)
   355  	if err != nil {
   356  		return 0, err
   357  	}
   358  	return counter, nil
   359  }
   360  
   361  // UpdateTxStatus updates a transaction status accordingly to the
   362  // provided status and hash
   363  func (p *PostgresPoolStorage) UpdateTxStatus(ctx context.Context, updateInfo pool.TxStatusUpdateInfo) error {
   364  	sql := "UPDATE pool.transaction SET status = $1, is_wip = $2"
   365  	args := []interface{}{updateInfo.NewStatus, updateInfo.IsWIP}
   366  
   367  	if updateInfo.FailedReason != nil {
   368  		sql += ", failed_reason = $3"
   369  		args = append(args, *updateInfo.FailedReason)
   370  		sql += " WHERE hash = $4"
   371  	} else {
   372  		sql += " WHERE hash = $3"
   373  	}
   374  
   375  	args = append(args, updateInfo.Hash.Hex())
   376  
   377  	if _, err := p.db.Exec(ctx, sql, args...); err != nil {
   378  		return err
   379  	}
   380  
   381  	return nil
   382  }
   383  
   384  // UpdateTxsStatus updates transactions status accordingly to the provided status and hashes
   385  func (p *PostgresPoolStorage) UpdateTxsStatus(ctx context.Context, updateInfos []pool.TxStatusUpdateInfo) error {
   386  	for _, updateInfo := range updateInfos {
   387  		if err := p.UpdateTxStatus(ctx, updateInfo); err != nil {
   388  			return err
   389  		}
   390  	}
   391  	return nil
   392  }
   393  
   394  // DeleteTransactionsByHashes deletes txs by their hashes
   395  func (p *PostgresPoolStorage) DeleteTransactionsByHashes(ctx context.Context, hashes []common.Hash) error {
   396  	hh := make([]string, 0, len(hashes))
   397  	for _, h := range hashes {
   398  		hh = append(hh, h.Hex())
   399  	}
   400  
   401  	query := "DELETE FROM pool.transaction WHERE hash = ANY ($1)"
   402  	if _, err := p.db.Exec(ctx, query, hh); err != nil {
   403  		return err
   404  	}
   405  	return nil
   406  }
   407  
   408  // SetGasPrice allows an external component to define the gas price
   409  func (p *PostgresPoolStorage) SetGasPrice(ctx context.Context, gasPrice uint64) error {
   410  	sql := "INSERT INTO pool.gas_price (price, timestamp) VALUES ($1, $2)"
   411  	if _, err := p.db.Exec(ctx, sql, gasPrice, time.Now().UTC()); err != nil {
   412  		return err
   413  	}
   414  	return nil
   415  }
   416  
   417  // GetGasPrice returns the current gas price
   418  func (p *PostgresPoolStorage) GetGasPrice(ctx context.Context) (uint64, error) {
   419  	sql := "SELECT price FROM pool.gas_price ORDER BY item_id DESC LIMIT 1"
   420  	rows, err := p.db.Query(ctx, sql)
   421  	if errors.Is(err, pgx.ErrNoRows) {
   422  		return 0, state.ErrNotFound
   423  	} else if err != nil {
   424  		return 0, err
   425  	}
   426  
   427  	defer rows.Close()
   428  
   429  	gasPrice := uint64(0)
   430  
   431  	for rows.Next() {
   432  		err := rows.Scan(&gasPrice)
   433  		if err != nil {
   434  			return 0, err
   435  		}
   436  	}
   437  
   438  	return gasPrice, nil
   439  }
   440  
   441  // MinGasPriceSince returns the min gas price after given timestamp
   442  func (p *PostgresPoolStorage) MinGasPriceSince(ctx context.Context, timestamp time.Time) (uint64, error) {
   443  	sql := "SELECT COALESCE(MIN(price), 0) FROM pool.gas_price WHERE \"timestamp\" >= $1 LIMIT 1"
   444  	var gasPrice uint64
   445  	err := p.db.QueryRow(ctx, sql, timestamp).Scan(&gasPrice)
   446  	if gasPrice == 0 || errors.Is(err, pgx.ErrNoRows) {
   447  		return 0, state.ErrNotFound
   448  	} else if err != nil {
   449  		return 0, err
   450  	}
   451  
   452  	return gasPrice, nil
   453  }
   454  
   455  // IsTxPending determines if the tx associated to the given hash is pending or
   456  // not.
   457  func (p *PostgresPoolStorage) IsTxPending(ctx context.Context, hash common.Hash) (bool, error) {
   458  	var exists bool
   459  	req := "SELECT exists (SELECT 1 FROM pool.transaction WHERE hash = $1 AND status = $2)"
   460  	err := p.db.QueryRow(ctx, req, hash.Hex(), pool.TxStatusPending).Scan(&exists)
   461  	if err != nil && err != sql.ErrNoRows {
   462  		return false, err
   463  	}
   464  
   465  	return exists, nil
   466  }
   467  
   468  // GetTxsByFromAndNonce get all the transactions from the pool with the same from and nonce
   469  func (p *PostgresPoolStorage) GetTxsByFromAndNonce(ctx context.Context, from common.Address, nonce uint64) ([]pool.Transaction, error) {
   470  	sql := `SELECT encoded, status, received_at, is_wip, ip, failed_reason
   471  	          FROM pool.transaction
   472  			 WHERE from_address = $1
   473  			   AND nonce = $2`
   474  	rows, err := p.db.Query(ctx, sql, from.String(), nonce)
   475  	if errors.Is(err, pgx.ErrNoRows) {
   476  		return nil, nil
   477  	} else if err != nil {
   478  		return nil, err
   479  	}
   480  	defer rows.Close()
   481  
   482  	txs := make([]pool.Transaction, 0, len(rows.RawValues()))
   483  	for rows.Next() {
   484  		tx, err := scanTx(rows)
   485  		if err != nil {
   486  			return nil, err
   487  		}
   488  		txs = append(txs, *tx)
   489  	}
   490  
   491  	return txs, nil
   492  }
   493  
   494  // GetTxFromAddressFromByHash gets tx from address by hash
   495  func (p *PostgresPoolStorage) GetTxFromAddressFromByHash(ctx context.Context, hash common.Hash) (common.Address, uint64, error) {
   496  	query := `SELECT from_address, nonce
   497  			  FROM pool.transaction
   498  			  WHERE hash = $1
   499  	`
   500  
   501  	var (
   502  		fromAddr string
   503  		nonce    uint64
   504  	)
   505  	err := p.db.QueryRow(ctx, query, hash.String()).Scan(&fromAddr, &nonce)
   506  	if err != nil {
   507  		return common.Address{}, 0, err
   508  	}
   509  
   510  	return common.HexToAddress(fromAddr), nonce, nil
   511  }
   512  
   513  // GetNonce gets the nonce to the provided address accordingly to the txs in the pool
   514  func (p *PostgresPoolStorage) GetNonce(ctx context.Context, address common.Address) (uint64, error) {
   515  	sql := `SELECT MAX(nonce)
   516                FROM pool.transaction
   517               WHERE from_address = $1
   518                 AND status IN  ($2, $3)`
   519  	rows, err := p.db.Query(ctx, sql, address.String(), pool.TxStatusPending, pool.TxStatusSelected)
   520  	if errors.Is(err, pgx.ErrNoRows) {
   521  		return 0, nil
   522  	} else if err != nil {
   523  		return 0, err
   524  	}
   525  	defer rows.Close()
   526  
   527  	var nonce *uint64
   528  	for rows.Next() {
   529  		err := rows.Scan(&nonce)
   530  		if err != nil {
   531  			return 0, err
   532  		} else if rows.Err() != nil {
   533  			return 0, rows.Err()
   534  		}
   535  	}
   536  
   537  	if nonce == nil {
   538  		n := uint64(0)
   539  		nonce = &n
   540  	} else {
   541  		n := *nonce + 1
   542  		nonce = &n
   543  	}
   544  
   545  	return *nonce, nil
   546  }
   547  
   548  // GetTxByHash gets a transaction in the pool by its hash
   549  func (p *PostgresPoolStorage) GetTxByHash(ctx context.Context, hash common.Hash) (*pool.Transaction, error) {
   550  	var (
   551  		encoded, status, ip string
   552  		receivedAt          time.Time
   553  		isWIP               bool
   554  	)
   555  
   556  	sql := `SELECT encoded, status, received_at, is_wip, ip
   557  	          FROM pool.transaction
   558  			 WHERE hash = $1`
   559  	err := p.db.QueryRow(ctx, sql, hash.String()).Scan(&encoded, &status, &receivedAt, &isWIP, &ip)
   560  	if errors.Is(err, pgx.ErrNoRows) {
   561  		return nil, pool.ErrNotFound
   562  	} else if err != nil {
   563  		return nil, err
   564  	}
   565  
   566  	b, err := hex.DecodeHex(encoded)
   567  	if err != nil {
   568  		return nil, err
   569  	}
   570  
   571  	tx := new(types.Transaction)
   572  	if err := tx.UnmarshalBinary(b); err != nil {
   573  		return nil, err
   574  	}
   575  
   576  	poolTx := &pool.Transaction{
   577  		ReceivedAt:  receivedAt,
   578  		Status:      pool.TxStatus(status),
   579  		Transaction: *tx,
   580  		IsWIP:       isWIP,
   581  		IP:          ip,
   582  	}
   583  
   584  	return poolTx, nil
   585  }
   586  
   587  func scanTx(rows pgx.Rows) (*pool.Transaction, error) {
   588  	var (
   589  		encoded, status, ip string
   590  		receivedAt          time.Time
   591  		isWIP               bool
   592  		failedReason        *string
   593  	)
   594  
   595  	if err := rows.Scan(&encoded, &status, &receivedAt, &isWIP, &ip, &failedReason); err != nil {
   596  		return nil, err
   597  	}
   598  
   599  	tx := new(pool.Transaction)
   600  
   601  	b, err := hex.DecodeHex(encoded)
   602  	if err != nil {
   603  		return nil, err
   604  	}
   605  
   606  	if err := tx.UnmarshalBinary(b); err != nil {
   607  		return nil, err
   608  	}
   609  
   610  	tx.Status = pool.TxStatus(status)
   611  	tx.ReceivedAt = receivedAt
   612  	tx.IsWIP = isWIP
   613  	tx.IP = ip
   614  	tx.FailedReason = failedReason
   615  
   616  	return tx, nil
   617  }
   618  
   619  // DeleteTransactionByHash deletes tx by its hash
   620  func (p *PostgresPoolStorage) DeleteTransactionByHash(ctx context.Context, hash common.Hash) error {
   621  	query := "DELETE FROM pool.transaction WHERE hash = $1"
   622  	if _, err := p.db.Exec(ctx, query, hash); err != nil {
   623  		return err
   624  	}
   625  	return nil
   626  }
   627  
   628  // GetTxZkCountersByHash gets a transaction zkcounters by its hash
   629  func (p *PostgresPoolStorage) GetTxZkCountersByHash(ctx context.Context, hash common.Hash) (*state.ZKCounters, error) {
   630  	var zkCounters state.ZKCounters
   631  
   632  	sql := `SELECT cumulative_gas_used, used_keccak_hashes, used_poseidon_hashes, used_poseidon_paddings, used_mem_aligns,
   633  			used_arithmetics, used_binaries, used_steps FROM pool.transaction WHERE hash = $1`
   634  	err := p.db.QueryRow(ctx, sql, hash.String()).Scan(&zkCounters.CumulativeGasUsed, &zkCounters.UsedKeccakHashes,
   635  		&zkCounters.UsedPoseidonHashes, &zkCounters.UsedPoseidonPaddings,
   636  		&zkCounters.UsedMemAligns, &zkCounters.UsedArithmetics, &zkCounters.UsedBinaries, &zkCounters.UsedSteps)
   637  	if errors.Is(err, pgx.ErrNoRows) {
   638  		return nil, pool.ErrNotFound
   639  	} else if err != nil {
   640  		return nil, err
   641  	}
   642  
   643  	return &zkCounters, nil
   644  }
   645  
   646  // MarkWIPTxsAsPending updates WIP status to non WIP
   647  func (p *PostgresPoolStorage) MarkWIPTxsAsPending(ctx context.Context) error {
   648  	const query = `UPDATE pool.transaction SET is_wip = false WHERE is_wip = true`
   649  	if _, err := p.db.Exec(ctx, query); err != nil {
   650  		return err
   651  	}
   652  	return nil
   653  }
   654  
   655  // UpdateTxWIPStatus updates a transaction wip status accordingly to the
   656  // provided WIP status and hash
   657  func (p *PostgresPoolStorage) UpdateTxWIPStatus(ctx context.Context, hash common.Hash, isWIP bool) error {
   658  	sql := "UPDATE pool.transaction SET is_wip = $1 WHERE hash = $2"
   659  	if _, err := p.db.Exec(ctx, sql, isWIP, hash.Hex()); err != nil {
   660  		return err
   661  	}
   662  	return nil
   663  }
   664  
   665  // GetAllAddressesBlocked get all addresses blocked
   666  func (p *PostgresPoolStorage) GetAllAddressesBlocked(ctx context.Context) ([]common.Address, error) {
   667  	sql := `SELECT addr FROM pool.blocked`
   668  
   669  	rows, err := p.db.Query(ctx, sql)
   670  	if err != nil {
   671  		if errors.Is(err, pgx.ErrNoRows) {
   672  			return nil, nil
   673  		} else {
   674  			return nil, err
   675  		}
   676  	}
   677  	defer rows.Close()
   678  
   679  	var addrs []common.Address
   680  	for rows.Next() {
   681  		var addr string
   682  		err := rows.Scan(&addr)
   683  		if err != nil {
   684  			return nil, err
   685  		}
   686  		addrs = append(addrs, common.HexToAddress(addr))
   687  	}
   688  
   689  	return addrs, nil
   690  }