@@ -134,6 +134,68 @@ void CoherentStateCache::on_new_block(const api::StateChangeSet& state_changes_s
134
134
root->ready_cond_var .notify_all ();
135
135
}
136
136
137
+ Task<StateCache::ValidationResult> CoherentStateCache::validate_current_root (Transaction& tx) {
138
+ StateCache::ValidationResult validation_result{.enabled = true };
139
+
140
+ const StateVersionId current_state_version_id = co_await get_db_state_version (tx);
141
+ validation_result.latest_state_version_id = current_state_version_id;
142
+ // If the latest version id in the cache is not the same as the db or one below it
143
+ // then the cache will be a new one for the next call so return early
144
+ if (current_state_version_id > latest_state_version_id_) {
145
+ validation_result.latest_state_behind = true ;
146
+ co_return validation_result;
147
+ }
148
+ const auto root = co_await wait_for_root_ready (latest_state_version_id_);
149
+
150
+ bool clear_cache{false };
151
+ const auto get_address_domain = [](const auto & key) {
152
+ return key.size () == kAddressLength ? db::table::kAccountDomain : db::table::kStorageDomain ;
153
+ };
154
+ const auto compare_cache = [&](auto & cache, bool is_code) -> Task<std::pair<bool , std::vector<Bytes>>> {
155
+ bool cancelled{false };
156
+ std::vector<Bytes> keys;
157
+ if (cache.empty ()) {
158
+ co_return std::make_pair (cancelled, keys);
159
+ }
160
+ auto kv_node = cache.extract (cache.begin ());
161
+ if (!kv_node.empty ()) {
162
+ co_return std::make_pair (cancelled, keys);
163
+ }
164
+ KeyValue kv{kv_node.value ()};
165
+ const auto domain = is_code ? db::table::kCodeDomain : get_address_domain (kv.key );
166
+ const GetLatestResult result = co_await tx.get_latest ({.table = std::string{domain}, .key = kv.key });
167
+ if (!result.success ) {
168
+ co_return std::make_pair (cancelled, keys);
169
+ }
170
+ if (result.value != kv.key ) {
171
+ keys.push_back (kv.key );
172
+ clear_cache = true ;
173
+ }
174
+ co_return std::make_pair (cancelled, keys);
175
+ };
176
+
177
+ auto [cache, code_cache] = clone_caches (root);
178
+
179
+ auto [cancelled_1, keys] = co_await compare_cache (cache, /* is_code=*/ false );
180
+ if (cancelled_1) {
181
+ validation_result.request_canceled = true ;
182
+ co_return validation_result;
183
+ }
184
+ validation_result.state_keys_out_of_sync = std::move (keys);
185
+ auto [cancelled_2, code_keys] = co_await compare_cache (code_cache, /* is_code=*/ true );
186
+ if (cancelled_2) {
187
+ validation_result.request_canceled = true ;
188
+ co_return validation_result;
189
+ }
190
+ validation_result.code_keys_out_of_sync = std::move (code_keys);
191
+
192
+ if (clear_cache) {
193
+ clear_caches (root);
194
+ }
195
+ validation_result.cache_cleared = true ;
196
+ co_return validation_result;
197
+ }
198
+
137
199
void CoherentStateCache::process_upsert_change (CoherentStateRoot* root, StateVersionId version_id, const AccountChange& change) {
138
200
const auto & address = change.address ;
139
201
const auto & data_bytes = change.data ;
@@ -413,4 +475,17 @@ Task<StateVersionId> CoherentStateCache::get_db_state_version(Transaction& tx) c
413
475
co_return endian::load_big_u64 (version.data ());
414
476
}
415
477
478
+ std::pair<KeyValueSet, KeyValueSet> CoherentStateCache::clone_caches (CoherentStateRoot* root) {
479
+ std::scoped_lock write_lock{rw_mutex_};
480
+ KeyValueSet cache = root->cache ;
481
+ KeyValueSet code_cache = root->code_cache ;
482
+ return {cache, code_cache};
483
+ }
484
+
485
+ void CoherentStateCache::clear_caches (CoherentStateRoot* root) {
486
+ std::scoped_lock write_lock{rw_mutex_};
487
+ root->cache .clear ();
488
+ root->code_cache .clear ();
489
+ }
490
+
416
491
} // namespace silkworm::db::kv::api
0 commit comments