@@ -646,6 +646,8 @@ impl Bouncer {
646646 self . visited_storage_entries . extend ( & tx_execution_summary. visited_storage_entries ) ;
647647 // Note: cancelling writes (0 -> 1 -> 0) will not be removed, but it's fine since fee was
648648 // charged for them.
649+ // Also, `get_patricia_update_resources` relies on this property - each cell must
650+ // be counted at most once as modified.
649651 self . state_changes_keys . extend ( state_changes_keys) ;
650652 self . accumulated_weights . class_hashes_to_migrate . extend ( tx_weights. class_hashes_to_migrate ) ;
651653 }
@@ -846,7 +848,11 @@ pub fn get_tx_weights<S: StateReader>(
846848 map_class_hash_to_casm_hash_computation_resources ( state_reader, executed_class_hashes) ?;
847849
848850 // Patricia update + transaction resources.
849- let patrticia_update_resources = get_particia_update_resources ( n_visited_storage_entries) ;
851+ let patrticia_update_resources = get_patricia_update_resources (
852+ n_visited_storage_entries,
853+ // TODO(Yoni): consider counting here the global contract tree and the aliases as well.
854+ state_changes_keys. storage_keys . len ( ) ,
855+ ) ;
850856 let vm_resources = & patrticia_update_resources + & tx_resources. computation . total_vm_resources ( ) ;
851857
852858 // Builtin gas costs for stone and for stwo.
@@ -939,12 +945,23 @@ pub fn map_class_hash_to_casm_hash_computation_resources<S: StateReader>(
939945 . collect ( )
940946}
941947
942- /// Returns the estimated Cairo resources for Patricia tree updates, or hash invocations
943- /// (done by the OS), required for accessing (read/write) the given storage entries.
944- // For each tree: n_visited_leaves * log(n_initialized_leaves)
945- // as the height of a Patricia tree with N uniformly distributed leaves is ~log(N),
946- // and number of visited leaves includes reads and writes.
947- pub fn get_particia_update_resources ( n_visited_storage_entries : usize ) -> ExecutionResources {
948+ /// Returns the estimated Cairo resources for Patricia tree updates given the accessed and
949+ /// modified storage entries.
950+ ///
951+ /// Each access (read or write) requires a traversal of the previous tree, and a write access
952+ /// requires an additional traversal of the new tree.
953+ ///
954+ /// Note:
955+ /// 1. n_visited_storage_entries includes both read and write accesses, and may overlap with
956+ /// n_first_time_modified_storage_entries (if the first access to a cell was write) and may not
957+ /// (if a cell was read by a previous transaction and is now modified for the first time).
958+ /// 2. In practice, the OS performs a multi-update, which is more efficient than performing
959+ /// separate updates. However, we use this conservative estimate for simplicity.
960+ pub fn get_patricia_update_resources (
961+ n_visited_storage_entries : usize ,
962+ n_first_time_modified_storage_entries : usize ,
963+ ) -> ExecutionResources {
964+ // The height of a Patricia tree with N uniformly distributed leaves is ~log(N).
948965 const TREE_HEIGHT_UPPER_BOUND : usize = 24 ;
949966 // TODO(Yoni, 1/5/2024): re-estimate this.
950967 const STEPS_IN_TREE_PER_HEIGHT : usize = 16 ;
@@ -959,8 +976,8 @@ pub fn get_particia_update_resources(n_visited_storage_entries: usize) -> Execut
959976 n_memory_holes : 0 ,
960977 } ;
961978
962- // Multiply by 2 since each storage entry is accessed in both the old and new tree .
963- & resources_per_tree_access * ( n_visited_storage_entries * 2 )
979+ // One traversal per access (read or write), and an additional one per write access .
980+ & resources_per_tree_access * ( n_visited_storage_entries + n_first_time_modified_storage_entries )
964981}
965982
966983pub fn verify_tx_weights_within_max_capacity < S : StateReader > (
0 commit comments