mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-01-28 13:05:18 +00:00
343cc4fa43
Also implements transactional block persistence. Two issues in the transaction processing code have been discovered that might affect other usages such as the CALL instruction. The main fix gets us past block 49000. You may need to clean up your database.
132 lines
3.5 KiB
Nim
132 lines
3.5 KiB
Nim
import
|
|
os, sqlite3, ranges, ranges/ptr_arith, eth_trie/[db_tracing, constants],
|
|
../storage_types
|
|
|
|
type
|
|
SqliteChainDB* = ref object of RootObj
|
|
store: PSqlite3
|
|
selectStmt, insertStmt, deleteStmt: PStmt
|
|
|
|
ChainDB* = SqliteChainDB
|
|
|
|
proc put*(db: ChainDB, key, value: openarray[byte])
|
|
|
|
proc newChainDB*(basePath: string, inMemory = false): ChainDB =
|
|
result.new()
|
|
let dbPath = if inMemory: ":memory:" else: basePath / "nimbus.db"
|
|
var s = sqlite3.open(dbPath, result.store)
|
|
if s != SQLITE_OK:
|
|
raiseStorageInitError()
|
|
|
|
template execQuery(q: string) =
|
|
var s: Pstmt
|
|
if prepare_v2(result.store, q, q.len.int32, s, nil) == SQLITE_OK:
|
|
if step(s) != SQLITE_DONE or finalize(s) != SQLITE_OK:
|
|
raiseStorageInitError()
|
|
else:
|
|
raiseStorageInitError()
|
|
|
|
# TODO: check current version and implement schema versioning
|
|
execQuery "PRAGMA user_version = 1;"
|
|
|
|
execQuery """
|
|
CREATE TABLE IF NOT EXISTS trie_nodes(
|
|
key BLOB PRIMARY KEY,
|
|
value BLOB
|
|
);
|
|
"""
|
|
|
|
template prepare(q: string): PStmt =
|
|
var s: Pstmt
|
|
if prepare_v2(result.store, q, q.len.int32, s, nil) != SQLITE_OK:
|
|
raiseStorageInitError()
|
|
s
|
|
|
|
result.selectStmt = prepare "SELECT value FROM trie_nodes WHERE key = ?;"
|
|
|
|
if sqlite3.libversion_number() < 3024000:
|
|
result.insertStmt = prepare """
|
|
INSERT OR REPLACE INTO trie_nodes(key, value) VALUES (?, ?);
|
|
"""
|
|
else:
|
|
result.insertStmt = prepare """
|
|
INSERT INTO trie_nodes(key, value) VALUES (?, ?)
|
|
ON CONFLICT(key) DO UPDATE SET value = excluded.value;
|
|
"""
|
|
|
|
result.deleteStmt = prepare "DELETE FROM trie_nodes WHERE key = ?;"
|
|
|
|
put(result, emptyRlpHash.data, emptyRlp)
|
|
|
|
proc bindBlob(s: Pstmt, n: int, blob: openarray[byte]): int32 =
|
|
sqlite3.bind_blob(s, n.int32, blob.baseAddr, blob.len.int32, nil)
|
|
|
|
proc get*(db: ChainDB, key: openarray[byte]): seq[byte] =
|
|
template check(op) =
|
|
let status = op
|
|
if status != SQLITE_OK: raiseKeyReadError(key)
|
|
|
|
check reset(db.selectStmt)
|
|
check clearBindings(db.selectStmt)
|
|
check bindBlob(db.selectStmt, 1, key)
|
|
|
|
case step(db.selectStmt)
|
|
of SQLITE_ROW:
|
|
var
|
|
resStart = columnBlob(db.selectStmt, 0)
|
|
resLen = columnBytes(db.selectStmt, 0)
|
|
result = newSeq[byte](resLen)
|
|
copyMem(result.baseAddr, resStart, resLen)
|
|
traceGet key, result
|
|
of SQLITE_DONE:
|
|
discard
|
|
else:
|
|
raiseKeyReadError(key)
|
|
|
|
proc put*(db: ChainDB, key, value: openarray[byte]) =
|
|
tracePut key, value
|
|
|
|
template check(op) =
|
|
let status = op
|
|
if status != SQLITE_OK: raiseKeyWriteError(key)
|
|
|
|
check reset(db.insertStmt)
|
|
check clearBindings(db.insertStmt)
|
|
check bindBlob(db.insertStmt, 1, key)
|
|
check bindBlob(db.insertStmt, 2, value)
|
|
|
|
if step(db.insertStmt) != SQLITE_DONE:
|
|
raiseKeyWriteError(key)
|
|
|
|
proc contains*(db: ChainDB, key: openarray[byte]): bool =
|
|
template check(op) =
|
|
let status = op
|
|
if status != SQLITE_OK: raiseKeySearchError(key)
|
|
|
|
check reset(db.selectStmt)
|
|
check clearBindings(db.selectStmt)
|
|
check bindBlob(db.selectStmt, 1, key)
|
|
|
|
case step(db.selectStmt)
|
|
of SQLITE_ROW: result = true
|
|
of SQLITE_DONE: result = false
|
|
else: raiseKeySearchError(key)
|
|
|
|
proc del*(db: ChainDB, key: openarray[byte]) =
|
|
traceDel key
|
|
|
|
template check(op) =
|
|
let status = op
|
|
if status != SQLITE_OK: raiseKeyDeletionError(key)
|
|
|
|
check reset(db.deleteStmt)
|
|
check clearBindings(db.deleteStmt)
|
|
check bindBlob(db.deleteStmt, 1, key)
|
|
|
|
if step(db.deleteStmt) != SQLITE_DONE:
|
|
raiseKeyDeletionError(key)
|
|
|
|
proc close*(db: ChainDB) =
|
|
discard sqlite3.close(db.store)
|
|
reset(db[])
|