From 440f8ad0213bf03c04fb003fd535491b4c931e3f Mon Sep 17 00:00:00 2001 From: bhartnett Date: Wed, 6 May 2026 08:00:05 +0200 Subject: [PATCH 1/7] Add code size cache to the kvt backend. --- execution_chain/db/core_db/base.nim | 10 ++++ execution_chain/db/kvt/kvt_desc.nim | 9 +++- execution_chain/db/kvt/kvt_init/memory_db.nim | 1 + execution_chain/db/kvt/kvt_init/rocks_db.nim | 1 + execution_chain/db/kvt/kvt_utils.nim | 49 ++++++++++++------- execution_chain/db/ledger.nim | 2 +- 6 files changed, 52 insertions(+), 20 deletions(-) diff --git a/execution_chain/db/core_db/base.nim b/execution_chain/db/core_db/base.nim index 59729d7ae2..369c2c2f6f 100644 --- a/execution_chain/db/core_db/base.nim +++ b/execution_chain/db/core_db/base.nim @@ -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 # ------------------------------------------------------------------------------ diff --git a/execution_chain/db/kvt/kvt_desc.nim b/execution_chain/db/kvt/kvt_desc.nim index 9cb5800ff4..51cfa0202a 100644 --- a/execution_chain/db/kvt/kvt_desc.nim +++ b/execution_chain/db/kvt/kvt_desc.nim @@ -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 codeSizeLruSize* = 1_000_000 type GetKvpFn* = @@ -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 # ------------------------------------------------------------------------------ diff --git a/execution_chain/db/kvt/kvt_init/memory_db.nim b/execution_chain/db/kvt/kvt_init/memory_db.nim index d1fb0b98cc..3f46bd0ade 100644 --- a/execution_chain/db/kvt/kvt_init/memory_db.nim +++ b/execution_chain/db/kvt/kvt_init/memory_db.nim @@ -198,6 +198,7 @@ proc memoryBackend*: KvtDbRef = db.closeFn = closeFn be db.getBackendFn = getBackendFn be + db.codeSizeCache = typeof(db.codeSizeCache).init(codeSizeLruSize) db # ------------------------------------------------------------------------------ diff --git a/execution_chain/db/kvt/kvt_init/rocks_db.nim b/execution_chain/db/kvt/kvt_init/rocks_db.nim index 7f7a0a127b..132f6c8ef2 100644 --- a/execution_chain/db/kvt/kvt_init/rocks_db.nim +++ b/execution_chain/db/kvt/kvt_init/rocks_db.nim @@ -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(codeSizeLruSize) db proc getBaseDb*(db: RdbBackendRef): RocksDbInstanceRef = diff --git a/execution_chain/db/kvt/kvt_utils.nim b/execution_chain/db/kvt/kvt_utils.nim index ce35db652e..d6b3765469 100644 --- a/execution_chain/db/kvt/kvt_utils.nim +++ b/execution_chain/db/kvt/kvt_utils.nim @@ -15,6 +15,7 @@ import results, + ../storage_types, "."/[kvt_desc, kvt_layers] export results @@ -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] = @@ -78,12 +79,12 @@ 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 @@ -91,13 +92,13 @@ proc del*( 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 @@ -106,13 +107,13 @@ 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, @@ -121,12 +122,24 @@ proc len*( 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 size = txRef.db.codeSizeCache.get(codeHash).valueOr: + + let size = ?txRef.len(contractHashKey(codeHash).toOpenArray) + txRef.db.codeSizeCache.put(codeHash, size) + size + + ok(size) + proc multiGet*( - db: KvtTxRef, + txRef: KvtTxRef, keys: openArray[seq[byte]], values: var openArray[Opt[seq[byte]]], sortedInput = false, @@ -138,7 +151,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: @@ -148,7 +161,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] @@ -157,7 +170,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 @@ -167,10 +180,10 @@ 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: @@ -178,12 +191,12 @@ proc hasKeyRc*( 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 diff --git a/execution_chain/db/ledger.nim b/execution_chain/db/ledger.nim index 3d7695b2d5..863865c094 100644 --- a/execution_chain/db/ledger.nim +++ b/execution_chain/db/ledger.nim @@ -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) From 3d85f5cbe7c66dcc61de64e538f259d35cc74551 Mon Sep 17 00:00:00 2001 From: bhartnett Date: Wed, 6 May 2026 14:33:35 +0800 Subject: [PATCH 2/7] Fix comments. --- execution_chain/db/kvt/kvt_utils.nim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/execution_chain/db/kvt/kvt_utils.nim b/execution_chain/db/kvt/kvt_utils.nim index d6b3765469..dab8eb8af8 100644 --- a/execution_chain/db/kvt/kvt_utils.nim +++ b/execution_chain/db/kvt/kvt_utils.nim @@ -68,7 +68,7 @@ proc delRangeBe*( # ------------ proc put*( - txRef: 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] = @@ -84,7 +84,7 @@ proc put*( proc del*( - txRef: 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 @@ -98,7 +98,7 @@ proc del*( # ------------ proc get*( - txRef: 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 @@ -113,7 +113,7 @@ proc get*( return ok(move(data)) proc len*( - txRef: 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, @@ -170,7 +170,7 @@ proc multiGet*( ok() proc hasKeyRc*( - txRef: 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 @@ -191,7 +191,7 @@ proc hasKeyRc*( err(rc.error) proc hasKey*( - txRef: KvtTxRef; # Database + txRef: KvtTxRef; # Database key: openArray[byte]; # Key of database record ): bool = ## Simplified version of `hasKeyRc` where `false` is returned instead of From 1eb134030f9cef61894c4cfa781a6553e414a06d Mon Sep 17 00:00:00 2001 From: bhartnett Date: Wed, 6 May 2026 08:52:30 +0200 Subject: [PATCH 3/7] Add getCodeSize ledger tests. --- tests/test_ledger.nim | 44 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tests/test_ledger.nim b/tests/test_ledger.nim index 1079b45600..fc0a103609 100644 --- a/tests/test_ledger.nim +++ b/tests/test_ledger.nim @@ -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 @@ -496,6 +497,49 @@ 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() + + 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: From 00e12d76bdec5f38b8579a809c42ce6632172dd7 Mon Sep 17 00:00:00 2001 From: bhartnett Date: Wed, 6 May 2026 15:41:35 +0800 Subject: [PATCH 4/7] Fetch from layers before cache. --- execution_chain/db/kvt/kvt_utils.nim | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/execution_chain/db/kvt/kvt_utils.nim b/execution_chain/db/kvt/kvt_utils.nim index dab8eb8af8..44d352c6ef 100644 --- a/execution_chain/db/kvt/kvt_utils.nim +++ b/execution_chain/db/kvt/kvt_utils.nim @@ -118,7 +118,6 @@ proc len*( ): 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) @@ -130,11 +129,12 @@ proc getCodeSize*( txRef: KvtTxRef; codeHash: Hash32; ): Result[int, KvtError] = - let size = txRef.db.codeSizeCache.get(codeHash).valueOr: + let key = contractHashKey(codeHash) - let size = ?txRef.len(contractHashKey(codeHash).toOpenArray) - txRef.db.codeSizeCache.put(codeHash, size) - size + let size = txRef.layersLen(key.toOpenArray).valueOr: + let s = txRef.db.codeSizeCache.get(codeHash).valueOr: + return txRef.db.getBeLen(key.toOpenArray) + s ok(size) From 484af5c655f4e5440de6540882114236af175261 Mon Sep 17 00:00:00 2001 From: bhartnett Date: Wed, 6 May 2026 16:55:06 +0800 Subject: [PATCH 5/7] Fix introduced bug. --- execution_chain/db/kvt/kvt_utils.nim | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/execution_chain/db/kvt/kvt_utils.nim b/execution_chain/db/kvt/kvt_utils.nim index 44d352c6ef..92f0cfff65 100644 --- a/execution_chain/db/kvt/kvt_utils.nim +++ b/execution_chain/db/kvt/kvt_utils.nim @@ -131,10 +131,13 @@ proc getCodeSize*( ): Result[int, KvtError] = let key = contractHashKey(codeHash) - let size = txRef.layersLen(key.toOpenArray).valueOr: - let s = txRef.db.codeSizeCache.get(codeHash).valueOr: - return txRef.db.getBeLen(key.toOpenArray) - s + 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) From 63ae8a4cb03f28d7ac642881914e83ac61da0a4e Mon Sep 17 00:00:00 2001 From: bhartnett Date: Wed, 6 May 2026 14:47:31 +0200 Subject: [PATCH 6/7] Fix test. --- tests/test_ledger.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_ledger.nim b/tests/test_ledger.nim index fc0a103609..6691bff9f1 100644 --- a/tests/test_ledger.nim +++ b/tests/test_ledger.nim @@ -515,6 +515,8 @@ proc runLedgerBasicOperationsTests() = let addrCode = initAddr(102) ledger1.setCode(addrCode, code) ledger1.persist() + ledger1.txFrame.checkpoint(0, skipSnapshot = true) + memDB.persist(ledger1.txFrame) let codeHash = keccak256(code) From 021a5f0309a3bd329c5a8a7e0f68e1c43174fab5 Mon Sep 17 00:00:00 2001 From: bhartnett Date: Wed, 6 May 2026 21:47:03 +0800 Subject: [PATCH 7/7] Update const name. --- execution_chain/db/kvt/kvt_desc.nim | 2 +- execution_chain/db/kvt/kvt_init/memory_db.nim | 2 +- execution_chain/db/kvt/kvt_init/rocks_db.nim | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/execution_chain/db/kvt/kvt_desc.nim b/execution_chain/db/kvt/kvt_desc.nim index 51cfa0202a..7fc570ca5c 100644 --- a/execution_chain/db/kvt/kvt_desc.nim +++ b/execution_chain/db/kvt/kvt_desc.nim @@ -25,7 +25,7 @@ import # Not auto-exporting backend export hashes, tables, minilru, kvt_constants, desc_error -const codeSizeLruSize* = 1_000_000 +const CODE_SIZE_LRU_SIZE* = 1_000_000 type GetKvpFn* = diff --git a/execution_chain/db/kvt/kvt_init/memory_db.nim b/execution_chain/db/kvt/kvt_init/memory_db.nim index 3f46bd0ade..c9dfb9d425 100644 --- a/execution_chain/db/kvt/kvt_init/memory_db.nim +++ b/execution_chain/db/kvt/kvt_init/memory_db.nim @@ -198,7 +198,7 @@ proc memoryBackend*: KvtDbRef = db.closeFn = closeFn be db.getBackendFn = getBackendFn be - db.codeSizeCache = typeof(db.codeSizeCache).init(codeSizeLruSize) + db.codeSizeCache = typeof(db.codeSizeCache).init(CODE_SIZE_LRU_SIZE) db # ------------------------------------------------------------------------------ diff --git a/execution_chain/db/kvt/kvt_init/rocks_db.nim b/execution_chain/db/kvt/kvt_init/rocks_db.nim index 132f6c8ef2..c8462aed60 100644 --- a/execution_chain/db/kvt/kvt_init/rocks_db.nim +++ b/execution_chain/db/kvt/kvt_init/rocks_db.nim @@ -218,7 +218,7 @@ proc rocksDbKvtBackend*(baseDb: RocksDbInstanceRef, cf: static[KvtCFs]): KvtDbRe db.closeFn = closeFn be db.getBackendFn = getBackendFn be - db.codeSizeCache = typeof(db.codeSizeCache).init(codeSizeLruSize) + db.codeSizeCache = typeof(db.codeSizeCache).init(CODE_SIZE_LRU_SIZE) db proc getBaseDb*(db: RdbBackendRef): RocksDbInstanceRef =