Skip to content

Commit 1c29310

Browse files
authored
fix: traceBlockTransactions shouldn't fail because of wrong class cache (#2644)
1 parent 4f0d355 commit 1c29310

File tree

4 files changed

+65
-57
lines changed

4 files changed

+65
-57
lines changed

vm/rust/src/juno_state_reader.rs

+39-12
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ use starknet_api::contract_class::{
2222
use starknet_api::core::{ClassHash, CompiledClassHash, ContractAddress, Nonce};
2323
use starknet_api::state::StorageKey;
2424
use starknet_types_core::felt::Felt;
25+
26+
use crate::BlockInfo;
27+
2528
type StarkFelt = Felt;
2629

2730
extern "C" {
@@ -55,13 +58,35 @@ struct CachedRunnableCompiledClass {
5558
static CLASS_CACHE: Lazy<Mutex<SizedCache<ClassHash, CachedRunnableCompiledClass>>> =
5659
Lazy::new(|| Mutex::new(SizedCache::with_size(128)));
5760

61+
pub enum BlockHeight {
62+
Height(u64),
63+
Pending,
64+
}
65+
66+
impl BlockHeight {
67+
pub fn is_after(&self, target: u64) -> bool {
68+
match self {
69+
BlockHeight::Height(height) => *height > target,
70+
BlockHeight::Pending => true,
71+
}
72+
}
73+
74+
pub fn from_block_info(block_info: &BlockInfo) -> Self {
75+
if block_info.is_pending == 1 {
76+
Self::Pending
77+
} else {
78+
Self::Height(block_info.block_number)
79+
}
80+
}
81+
}
82+
5883
pub struct JunoStateReader {
5984
pub handle: usize, // uintptr_t equivalent
60-
pub height: u64,
85+
pub height: BlockHeight,
6186
}
6287

6388
impl JunoStateReader {
64-
pub fn new(handle: usize, height: u64) -> Self {
89+
pub fn new(handle: usize, height: BlockHeight) -> Self {
6590
Self { handle, height }
6691
}
6792
}
@@ -145,7 +170,7 @@ impl StateReader for JunoStateReader {
145170
// with the same block number but with the state at the end of that block. That is why, we cannot use classes from cache
146171
// if they are cached on the same height that we are executing on. Because they might be cached using a state instance that
147172
// is in the future compared to the state that we are currently executing on, even tho they have the same height.
148-
if cached_class.cached_on_height < self.height {
173+
if self.height.is_after(cached_class.cached_on_height) {
149174
return Ok(cached_class.definition.clone());
150175
}
151176
}
@@ -164,15 +189,17 @@ impl StateReader for JunoStateReader {
164189
Ok(class) => {
165190
let runnable_compiled_class =
166191
RunnableCompiledClass::try_from(class.contract_class).unwrap();
167-
CLASS_CACHE.lock().unwrap().cache_set(
168-
class_hash,
169-
CachedRunnableCompiledClass {
170-
// This clone is cheap, it is just a reference copy in the underlying
171-
// RunnableCompiledClass implementation
172-
definition: runnable_compiled_class.clone(),
173-
cached_on_height: self.height,
174-
},
175-
);
192+
if let BlockHeight::Height(height) = self.height {
193+
CLASS_CACHE.lock().unwrap().cache_set(
194+
class_hash,
195+
CachedRunnableCompiledClass {
196+
// This clone is cheap, it is just a reference copy in the underlying
197+
// RunnableCompiledClass implementation
198+
definition: runnable_compiled_class.clone(),
199+
cached_on_height: height,
200+
},
201+
);
202+
}
176203
Ok(runnable_compiled_class)
177204
}
178205
Err(e) => Err(StateError::StateReadError(format!(

vm/rust/src/lib.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ pub mod execution;
44
pub mod jsonrpc;
55
mod juno_state_reader;
66

7-
use crate::juno_state_reader::{ptr_to_felt, JunoStateReader};
7+
use crate::juno_state_reader::{ptr_to_felt, BlockHeight, JunoStateReader};
88
use error::{CallError, ExecutionError};
99
use error_stack::{ErrorStack, Frame};
1010
use execution::process_transaction;
@@ -99,6 +99,7 @@ pub struct CallInfo {
9999
pub struct BlockInfo {
100100
pub block_number: c_ulonglong,
101101
pub block_timestamp: c_ulonglong,
102+
pub is_pending: c_uchar,
102103
pub sequencer_address: [c_uchar; 32],
103104
pub l1_gas_price_wei: [c_uchar; 32],
104105
pub l1_gas_price_fri: [c_uchar; 32],
@@ -126,7 +127,10 @@ pub extern "C" fn cairoVMCall(
126127
let block_info = unsafe { *block_info_ptr };
127128
let call_info = unsafe { *call_info_ptr };
128129

129-
let reader = JunoStateReader::new(reader_handle, block_info.block_number);
130+
let reader = JunoStateReader::new(
131+
reader_handle,
132+
BlockHeight::from_block_info(&block_info),
133+
);
130134
let contract_addr_felt = StarkFelt::from_bytes_be(&call_info.contract_address);
131135
let class_hash = if call_info.class_hash == [0; 32] {
132136
None
@@ -247,7 +251,10 @@ pub extern "C" fn cairoVMExecute(
247251
err_stack: c_uchar,
248252
) {
249253
let block_info = unsafe { *block_info_ptr };
250-
let reader = JunoStateReader::new(reader_handle, block_info.block_number);
254+
let reader = JunoStateReader::new(
255+
reader_handle,
256+
BlockHeight::from_block_info(&block_info),
257+
);
251258
let chain_id_str = unsafe { CStr::from_ptr(chain_id) }.to_str().unwrap();
252259
let txn_json_str = unsafe { CStr::from_ptr(txns_json) }.to_str().unwrap();
253260
let txns_and_query_bits: Result<Vec<TxnAndQueryBit>, serde_json::Error> =

vm/vm.go

+15-42
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ func makeCBlockInfo(blockInfo *BlockInfo) C.BlockInfo {
195195

196196
cBlockInfo.block_number = C.ulonglong(blockInfo.Header.Number)
197197
cBlockInfo.block_timestamp = C.ulonglong(blockInfo.Header.Timestamp)
198+
cBlockInfo.is_pending = toUchar(blockInfo.Header.Hash == nil)
198199
copyFeltIntoCArray(blockInfo.Header.SequencerAddress, &cBlockInfo.sequencer_address[0])
199200
copyFeltIntoCArray(blockInfo.Header.L1GasPriceETH, &cBlockInfo.l1_gas_price_wei[0])
200201
copyFeltIntoCArray(blockInfo.Header.L1GasPriceSTRK, &cBlockInfo.l1_gas_price_fri[0])
@@ -223,16 +224,6 @@ func (v *vm) Call(callInfo *CallInfo, blockInfo *BlockInfo, state core.StateRead
223224
handle := cgo.NewHandle(context)
224225
defer handle.Delete()
225226

226-
var concurrencyModeByte byte
227-
if v.concurrencyMode {
228-
concurrencyModeByte = 1
229-
}
230-
var structuredErrStackByte byte
231-
if structuredErrStack {
232-
structuredErrStackByte = 1
233-
}
234-
C.setVersionedConstants(C.CString("my_json"))
235-
236227
cCallInfo, callInfoPinner := makeCCallInfo(callInfo)
237228
cBlockInfo := makeCBlockInfo(blockInfo)
238229
chainID := C.CString(network.L2ChainID)
@@ -243,9 +234,9 @@ func (v *vm) Call(callInfo *CallInfo, blockInfo *BlockInfo, state core.StateRead
243234
C.uintptr_t(handle),
244235
chainID,
245236
C.ulonglong(maxSteps),
246-
C.uchar(concurrencyModeByte),
237+
toUchar(v.concurrencyMode),
247238
cSierraVersion,
248-
C.uchar(structuredErrStackByte), //nolint:gocritic // don't know why the linter is annoyed
239+
toUchar(structuredErrStack), //nolint:gocritic // See https://github.com/go-critic/go-critic/issues/897
249240
)
250241
callInfoPinner.Unpin()
251242
C.free(unsafe.Pointer(chainID))
@@ -284,31 +275,6 @@ func (v *vm) Execute(txns []core.Transaction, declaredClasses []core.Class, paid
284275
txnsJSONCstr := cstring(txnsJSON)
285276
classesJSONCStr := cstring(classesJSON)
286277

287-
var skipChargeFeeByte byte
288-
if skipChargeFee {
289-
skipChargeFeeByte = 1
290-
}
291-
292-
var skipValidateByte byte
293-
if skipValidate {
294-
skipValidateByte = 1
295-
}
296-
297-
var errOnRevertByte byte
298-
if errOnRevert {
299-
errOnRevertByte = 1
300-
}
301-
302-
var errorStackByte byte
303-
if errorStack {
304-
errorStackByte = 1
305-
}
306-
307-
var concurrencyModeByte byte
308-
if v.concurrencyMode {
309-
concurrencyModeByte = 1
310-
}
311-
312278
cBlockInfo := makeCBlockInfo(blockInfo)
313279
chainID := C.CString(network.L2ChainID)
314280
C.cairoVMExecute(txnsJSONCstr,
@@ -317,11 +283,11 @@ func (v *vm) Execute(txns []core.Transaction, declaredClasses []core.Class, paid
317283
&cBlockInfo,
318284
C.uintptr_t(handle),
319285
chainID,
320-
C.uchar(skipChargeFeeByte),
321-
C.uchar(skipValidateByte),
322-
C.uchar(errOnRevertByte), //nolint:gocritic
323-
C.uchar(concurrencyModeByte), //nolint:gocritic
324-
C.uchar(errorStackByte), //nolint:gocritic
286+
toUchar(skipChargeFee),
287+
toUchar(skipValidate),
288+
toUchar(errOnRevert),
289+
toUchar(v.concurrencyMode),
290+
toUchar(errorStack), //nolint:gocritic // See https://github.com/go-critic/go-critic/issues/897
325291
)
326292

327293
C.free(unsafe.Pointer(classesJSONCStr))
@@ -412,3 +378,10 @@ func SetVersionedConstants(filename string) error {
412378

413379
return err
414380
}
381+
382+
func toUchar(b bool) C.uchar {
383+
if b {
384+
return 1
385+
}
386+
return 0
387+
}

vm/vm_ffi.h

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ typedef struct CallInfo {
1818
typedef struct BlockInfo {
1919
unsigned long long block_number;
2020
unsigned long long block_timestamp;
21+
unsigned char is_pending;
2122
unsigned char sequencer_address[FELT_SIZE];
2223
unsigned char l1_gas_price_wei[FELT_SIZE];
2324
unsigned char l1_gas_price_fri[FELT_SIZE];

0 commit comments

Comments
 (0)