Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions execution_chain/db/core_db/base.nim
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,16 @@ proc hasKey*(kvt: CoreDbTxRef; key: openArray[byte]): bool =
##
kvt.kTx.hasKeyRc(key).valueOr(false)

proc getCodeSize*(kvt: CoreDbTxRef; codeHash: Hash32): CoreDbRc[int] =
## This function returns the size of the code associated with `codeHash`.
let rc = kvt.kTx.getCodeSize(codeHash)
if rc.isOk:
ok(rc.value)
elif rc.error == GetNotFound:
err(rc.error.toError("", KvtNotFound))
else:
err(rc.error.toError(""))

# ------------------------------------------------------------------------------
# Public methods for accounts
# ------------------------------------------------------------------------------
Expand Down
9 changes: 8 additions & 1 deletion execution_chain/db/kvt/kvt_desc.nim
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@
import
std/[hashes, tables],
results,
minilru,
eth/common/hashes,
./kvt_init/init_common,
./kvt_constants,
./kvt_desc/desc_error

# Not auto-exporting backend
export hashes, tables, kvt_constants, desc_error
export hashes, tables, minilru, kvt_constants, desc_error

const CODE_SIZE_LRU_SIZE* = 1_000_000

type
GetKvpFn* =
Expand Down Expand Up @@ -95,6 +99,9 @@ type
## Tx holding data scheduled to be written to disk during the next
## `persist` call

codeSizeCache*: LruCache[Hash32, int]
## LRU cache for contract code sizes by code hash.

# ------------------------------------------------------------------------------
# Public helpers
# ------------------------------------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions execution_chain/db/kvt/kvt_init/memory_db.nim
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ proc memoryBackend*: KvtDbRef =

db.closeFn = closeFn be
db.getBackendFn = getBackendFn be
db.codeSizeCache = typeof(db.codeSizeCache).init(CODE_SIZE_LRU_SIZE)
db

# ------------------------------------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions execution_chain/db/kvt/kvt_init/rocks_db.nim
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ proc rocksDbKvtBackend*(baseDb: RocksDbInstanceRef, cf: static[KvtCFs]): KvtDbRe

db.closeFn = closeFn be
db.getBackendFn = getBackendFn be
db.codeSizeCache = typeof(db.codeSizeCache).init(CODE_SIZE_LRU_SIZE)
db

proc getBaseDb*(db: RdbBackendRef): RocksDbInstanceRef =
Expand Down
54 changes: 35 additions & 19 deletions execution_chain/db/kvt/kvt_utils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import
results,
../storage_types,
"."/[kvt_desc, kvt_layers]

export results
Expand Down Expand Up @@ -67,7 +68,7 @@ proc delRangeBe*(
# ------------

proc put*(
db: KvtTxRef; # Database
txRef: KvtTxRef; # Database
key: openArray[byte]; # Key of database record to store
data: openArray[byte]; # Value of database record to store
): Result[void,KvtError] =
Expand All @@ -78,26 +79,26 @@ proc put*(
if data.len == 0:
return err(DataInvalid)

db.layersPut(key, data)
txRef.layersPut(key, data)
ok()


proc del*(
db: KvtTxRef; # Database
txRef: KvtTxRef; # Database
key: openArray[byte]; # Key of database record to delete
): Result[void,KvtError] =
## For the argument `key` delete the associated value (which will be marked
## in the top layer cache.)
if key.len == 0:
return err(KeyInvalid)

db.layersPut(key, EmptyBlob)
txRef.layersPut(key, EmptyBlob)
ok()

# ------------

proc get*(
db: KvtTxRef; # Database
txRef: KvtTxRef; # Database
key: openArray[byte]; # Key of database record
): Result[seq[byte],KvtError] =
## For the argument `key` return the associated value preferably from the
Expand All @@ -106,27 +107,42 @@ proc get*(
if key.len == 0:
return err(KeyInvalid)

var data = db.layersGet(key).valueOr:
return db.db.getBe key
var data = txRef.layersGet(key).valueOr:
return txRef.db.getBe key

return ok(move(data))

proc len*(
db: KvtTxRef; # Database
txRef: KvtTxRef; # Database
key: openArray[byte]; # Key of database record
): Result[int,KvtError] =
## For the argument `key` return the length of the associated value,
## preferably from the top layer, or the database otherwise.
##
if key.len == 0:
return err(KeyInvalid)

let len = db.layersLen(key).valueOr:
return db.db.getBeLen key
let len = txRef.layersLen(key).valueOr:
return txRef.db.getBeLen key
ok(len)

proc getCodeSize*(
txRef: KvtTxRef;
codeHash: Hash32;
): Result[int, KvtError] =
let key = contractHashKey(codeHash)

txRef.layersLen(key.toOpenArray).isErrOr:
return ok(value)
txRef.db.codeSizeCache.get(codeHash).isErrOr:
return ok(value)

let size = ?txRef.db.getBeLen(key.toOpenArray)
txRef.db.codeSizeCache.put(codeHash, size)

ok(size)

proc multiGet*(
db: KvtTxRef,
txRef: KvtTxRef,
keys: openArray[seq[byte]],
values: var openArray[Opt[seq[byte]]],
sortedInput = false,
Expand All @@ -138,7 +154,7 @@ proc multiGet*(

# First fetch each key from the in memory layers
for i, k in keys:
let value = db.layersGet(k)
let value = txRef.layersGet(k)
if value.isSome():
values[i] = value
else:
Expand All @@ -148,7 +164,7 @@ proc multiGet*(
# Fetch the remaining keys from the db backend
if remainingKeys.len() > 0:
var remainingValues = newSeq[Opt[seq[byte]]](remainingKeys.len())
?db.db.multiGetBe(remainingKeys, remainingValues)
?txRef.db.multiGetBe(remainingKeys, remainingValues)

for i, v in remainingValues:
let index = keyIndexes[i]
Expand All @@ -157,7 +173,7 @@ proc multiGet*(
ok()

proc hasKeyRc*(
db: KvtTxRef; # Database
txRef: KvtTxRef; # Database
key: openArray[byte]; # Key of database record
): Result[bool,KvtError] =
## For the argument `key` return `true` if `get()` returned a value on
Expand All @@ -167,23 +183,23 @@ proc hasKeyRc*(
if key.len == 0:
return err(KeyInvalid)

if db.layersHasKey key:
if txRef.layersHasKey key:
return ok(true)

let rc = db.db.getBe key
let rc = txRef.db.getBe key
if rc.isOk:
return ok(true)
if rc.error == GetNotFound:
return ok(false)
err(rc.error)

proc hasKey*(
db: KvtTxRef; # Database
txRef: KvtTxRef; # Database
key: openArray[byte]; # Key of database record
): bool =
## Simplified version of `hasKeyRc` where `false` is returned instead of
## an error.
db.hasKeyRc(key).valueOr: false
txRef.hasKeyRc(key).valueOr: false

proc close*(db: KvtDbRef; wipe = false) =
## Backend destructor. The argument `wipe` indicates that a full
Expand Down
2 changes: 1 addition & 1 deletion execution_chain/db/ledger.nim
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ proc getCodeSize*(ledger: LedgerRef, address: Address): int =
# cached and easily accessible in the database layer - this is to prevent
# EXTCODESIZE calls from messing up the code cache and thus causing
# recomputation of the jump destination table
var rc = ledger.txFrame.len(contractHashKey(acc.statement.codeHash).toOpenArray)
var rc = ledger.txFrame.getCodeSize(acc.statement.codeHash)

return rc.valueOr:
warn logTxt "getCodeSize()", codeHash=acc.statement.codeHash, error=($$rc.error)
Expand Down
46 changes: 46 additions & 0 deletions tests/test_ledger.nim
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import
../execution_chain/core/chain,
../execution_chain/core/tx_pool,
../execution_chain/db/core_db/memory_only,
../execution_chain/db/kvt/kvt_desc,
../execution_chain/transaction,
../execution_chain/constants,
../execution_chain/db/ledger {.all.}, # import all private symbols
Expand Down Expand Up @@ -496,6 +497,51 @@ proc runLedgerBasicOperationsTests() =
val = memDB.baseTxFrame().get(key.toOpenArray).valueOr: EmptyBlob
check val == code

test "getCodeSize - no account":
var ledger = LedgerRef.init(memDB.baseTxFrame())
let addrNoAcc = initAddr(100)

check ledger.getCodeSize(addrNoAcc) == 0

test "getCodeSize - empty code":
var ledger = LedgerRef.init(memDB.baseTxFrame())
let addrEmpty = initAddr(101)
ledger.setBalance(addrEmpty, 1.u256)

check ledger.getCodeSize(addrEmpty) == 0

test "getCodeSize - cache miss then cache hit":
var ledger1 = LedgerRef.init(memDB.baseTxFrame())
let addrCode = initAddr(102)
ledger1.setCode(addrCode, code)
ledger1.persist()
ledger1.txFrame.checkpoint(0, skipSnapshot = true)
memDB.persist(ledger1.txFrame)

let codeHash = keccak256(code)

var ledger2 = LedgerRef.init(memDB.baseTxFrame())

check:
ledger2.txFrame.kTx.db.codeSizeCache.get(codeHash).isNone()
ledger2.getCodeSize(addrCode) == code.len

let cached = ledger2.txFrame.kTx.db.codeSizeCache.get(codeHash)
check:
cached.isSome()
cached.get() == code.len
ledger2.getCodeSize(addrCode) == code.len

test "getCodeSize - code loaded in account cache":
var ledger = LedgerRef.init(memDB.baseTxFrame())
let addrCode = initAddr(103)
ledger.setCode(addrCode, code)
ledger.persist()

discard ledger.getCode(addrCode)

check ledger.getCodeSize(addrCode) == code.len

test "accessList operations":
proc verifyAddrs(ledger: LedgerRef, addrs: varargs[int]): bool =
for c in addrs:
Expand Down
Loading