The current solution for fallback mechanism is implemented wrong, since we pass the same State from failed native execution to the VM, which could be corrupted (e.g the same value could be written twice).
But it should use the same state as input gets, like in the following diagram:
Reproduction
To reproduce it locally I've created mock contract that has two methods: set_value and get_value, but we invoke only set_value method, which checks if contract storage value set to 0 and if not - throw an error. Then it changes it to any value passed to the function arguments and calls send_message_to_l1 syscall (to throw an error on native side). The cairo code will be as follows:
fn set_value(ref self: ContractState, value: u32) {
assert!(self.value.read() == 0, "value must be 0");
self.value.write(value);
let caller = get_caller_address();
syscalls::send_message_to_l1_syscall(caller.into(), array![].span()).unwrap();
}
And on the blockifier side, there are some hard-coded things, that will force native executor to throw an error.
To test it out, use the following command:
FALLBACK_ENABLED=1 cargo test --test fallback_tests --features testing
Proposed solution
Proposed solution to fix this problem would be using something similar to TransactionalState (if fallback enabled) to pass it to the native and then either commit it if everything is OK or abort changes and fallback to the VM with initial state.
Description
The current solution for fallback mechanism is implemented wrong, since we pass the same
State
from failed native execution to theVM
, which could be corrupted (e.g the same value could be written twice).But it should use the same state as input gets, like in the following diagram:
Reproduction
To reproduce it locally I've created mock contract that has two methods:
set_value
andget_value
, but we invoke onlyset_value
method, which checks if contract storage value set to 0 and if not - throw an error. Then it changes it to any value passed to the function arguments and callssend_message_to_l1
syscall (to throw an error on native side). The cairo code will be as follows:And on the blockifier side, there are some hard-coded things, that will force native executor to throw an error.
To test it out, use the following command:
Proposed solution
Proposed solution to fix this problem would be using something similar to
TransactionalState
(if fallback enabled) to pass it to the native and then either commit it if everything is OK or abort changes and fallback to the VM with initial state.