2023-01-13 16:49:01 +00:00
|
|
|
package ethhashlookup
|
2023-01-04 13:22:41 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"database/sql"
|
2023-01-16 07:56:45 +00:00
|
|
|
"errors"
|
|
|
|
"strconv"
|
2023-01-04 13:22:41 +00:00
|
|
|
|
|
|
|
"github.com/ipfs/go-cid"
|
|
|
|
_ "github.com/mattn/go-sqlite3"
|
|
|
|
"golang.org/x/xerrors"
|
|
|
|
|
|
|
|
"github.com/filecoin-project/lotus/chain/types/ethtypes"
|
|
|
|
)
|
|
|
|
|
2023-01-16 07:56:45 +00:00
|
|
|
var ErrNotFound = errors.New("not found")
|
|
|
|
|
2023-01-04 13:22:41 +00:00
|
|
|
var pragmas = []string{
|
|
|
|
"PRAGMA synchronous = normal",
|
|
|
|
"PRAGMA temp_store = memory",
|
|
|
|
"PRAGMA mmap_size = 30000000000",
|
|
|
|
"PRAGMA page_size = 32768",
|
|
|
|
"PRAGMA auto_vacuum = NONE",
|
|
|
|
"PRAGMA automatic_index = OFF",
|
|
|
|
"PRAGMA journal_mode = WAL",
|
|
|
|
"PRAGMA read_uncommitted = ON",
|
|
|
|
}
|
|
|
|
|
|
|
|
var ddls = []string{
|
2023-01-13 16:49:01 +00:00
|
|
|
`CREATE TABLE IF NOT EXISTS eth_tx_hashes (
|
|
|
|
hash TEXT PRIMARY KEY NOT NULL,
|
|
|
|
cid TEXT NOT NULL UNIQUE,
|
2023-01-16 07:56:45 +00:00
|
|
|
insertion_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
|
2023-01-04 13:22:41 +00:00
|
|
|
)`,
|
|
|
|
|
2023-01-16 07:56:45 +00:00
|
|
|
`CREATE INDEX IF NOT EXISTS insertion_time_index ON eth_tx_hashes (insertion_time)`,
|
2023-01-13 16:49:01 +00:00
|
|
|
|
2023-01-04 13:22:41 +00:00
|
|
|
// metadata containing version of schema
|
|
|
|
`CREATE TABLE IF NOT EXISTS _meta (
|
|
|
|
version UINT64 NOT NULL UNIQUE
|
|
|
|
)`,
|
|
|
|
|
|
|
|
// version 1.
|
|
|
|
`INSERT OR IGNORE INTO _meta (version) VALUES (1)`,
|
|
|
|
}
|
|
|
|
|
|
|
|
const schemaVersion = 1
|
|
|
|
|
|
|
|
const (
|
2023-01-13 16:49:01 +00:00
|
|
|
insertTxHash = `INSERT INTO eth_tx_hashes
|
2023-01-16 07:56:45 +00:00
|
|
|
(hash, cid)
|
|
|
|
VALUES(?, ?)
|
|
|
|
ON CONFLICT (hash) DO UPDATE SET insertion_time = CURRENT_TIMESTAMP`
|
2023-01-04 13:22:41 +00:00
|
|
|
)
|
|
|
|
|
2023-01-16 07:56:45 +00:00
|
|
|
type EthTxHashLookup struct {
|
2023-01-04 13:22:41 +00:00
|
|
|
db *sql.DB
|
|
|
|
}
|
|
|
|
|
2023-01-16 07:56:45 +00:00
|
|
|
func (ei *EthTxHashLookup) UpsertHash(txHash ethtypes.EthHash, c cid.Cid) error {
|
2023-01-04 13:22:41 +00:00
|
|
|
hashEntry, err := ei.db.Prepare(insertTxHash)
|
|
|
|
if err != nil {
|
|
|
|
return xerrors.Errorf("prepare insert event: %w", err)
|
|
|
|
}
|
|
|
|
|
2023-01-16 07:56:45 +00:00
|
|
|
_, err = hashEntry.Exec(txHash.String(), c.String())
|
2023-01-04 13:22:41 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-01-16 07:56:45 +00:00
|
|
|
func (ei *EthTxHashLookup) GetCidFromHash(txHash ethtypes.EthHash) (cid.Cid, error) {
|
2023-01-30 15:38:20 +00:00
|
|
|
row := ei.db.QueryRow("SELECT cid FROM eth_tx_hashes WHERE hash = :hash;", sql.Named("hash", txHash.String()))
|
2023-01-04 13:22:41 +00:00
|
|
|
|
|
|
|
var c string
|
2023-01-30 15:38:20 +00:00
|
|
|
err := row.Scan(&c)
|
2023-01-04 13:22:41 +00:00
|
|
|
if err != nil {
|
2023-01-30 15:38:20 +00:00
|
|
|
if err == sql.ErrNoRows {
|
|
|
|
return cid.Undef, ErrNotFound
|
|
|
|
}
|
2023-01-04 13:22:41 +00:00
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
return cid.Decode(c)
|
|
|
|
}
|
|
|
|
|
2023-01-16 07:56:45 +00:00
|
|
|
func (ei *EthTxHashLookup) GetHashFromCid(c cid.Cid) (ethtypes.EthHash, error) {
|
2023-01-30 15:38:20 +00:00
|
|
|
row := ei.db.QueryRow("SELECT hash FROM eth_tx_hashes WHERE cid = :cid;", sql.Named("cid", c.String()))
|
2023-01-04 13:22:41 +00:00
|
|
|
|
|
|
|
var hashString string
|
2023-01-30 15:38:20 +00:00
|
|
|
err := row.Scan(&c)
|
2023-01-04 13:22:41 +00:00
|
|
|
if err != nil {
|
2023-01-30 15:38:20 +00:00
|
|
|
if err == sql.ErrNoRows {
|
|
|
|
return ethtypes.EmptyEthHash, ErrNotFound
|
|
|
|
}
|
2023-01-04 13:22:41 +00:00
|
|
|
return ethtypes.EmptyEthHash, err
|
|
|
|
}
|
|
|
|
return ethtypes.ParseEthHash(hashString)
|
|
|
|
}
|
|
|
|
|
2023-01-16 07:56:45 +00:00
|
|
|
func (ei *EthTxHashLookup) DeleteEntriesOlderThan(days int) (int64, error) {
|
|
|
|
res, err := ei.db.Exec("DELETE FROM eth_tx_hashes WHERE insertion_time < datetime('now', ?);", "-"+strconv.Itoa(days)+" day")
|
2023-01-04 13:22:41 +00:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return res.RowsAffected()
|
|
|
|
}
|
|
|
|
|
2023-01-16 07:56:45 +00:00
|
|
|
func NewTransactionHashLookup(path string) (*EthTxHashLookup, error) {
|
2023-01-04 13:22:41 +00:00
|
|
|
db, err := sql.Open("sqlite3", path+"?mode=rwc")
|
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("open sqlite3 database: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, pragma := range pragmas {
|
|
|
|
if _, err := db.Exec(pragma); err != nil {
|
|
|
|
_ = db.Close()
|
|
|
|
return nil, xerrors.Errorf("exec pragma %q: %w", pragma, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
q, err := db.Query("SELECT name FROM sqlite_master WHERE type='table' AND name='_meta';")
|
|
|
|
if err == sql.ErrNoRows || !q.Next() {
|
|
|
|
// empty database, create the schema
|
|
|
|
for _, ddl := range ddls {
|
|
|
|
if _, err := db.Exec(ddl); err != nil {
|
|
|
|
_ = db.Close()
|
|
|
|
return nil, xerrors.Errorf("exec ddl %q: %w", ddl, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if err != nil {
|
|
|
|
_ = db.Close()
|
|
|
|
return nil, xerrors.Errorf("looking for _meta table: %w", err)
|
|
|
|
} else {
|
|
|
|
// Ensure we don't open a database from a different schema version
|
|
|
|
|
|
|
|
row := db.QueryRow("SELECT max(version) FROM _meta")
|
|
|
|
var version int
|
|
|
|
err := row.Scan(&version)
|
|
|
|
if err != nil {
|
|
|
|
_ = db.Close()
|
|
|
|
return nil, xerrors.Errorf("invalid database version: no version found")
|
|
|
|
}
|
|
|
|
if version != schemaVersion {
|
|
|
|
_ = db.Close()
|
|
|
|
return nil, xerrors.Errorf("invalid database version: got %d, expected %d", version, schemaVersion)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-16 07:56:45 +00:00
|
|
|
return &EthTxHashLookup{
|
2023-01-04 13:22:41 +00:00
|
|
|
db: db,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2023-01-16 07:56:45 +00:00
|
|
|
func (ei *EthTxHashLookup) Close() error {
|
2023-01-04 13:22:41 +00:00
|
|
|
if ei.db == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return ei.db.Close()
|
|
|
|
}
|