-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: expose storage to tracers #77
Conversation
There is no huge performance impact at least. I cannot reliable measure any difference but I did not inspect the assembly. |
The new interface looks weird. I guess it could be caused by performance, but ideally I'd like to see the pub trait StorageInterface {
/// Gets value of the specified storage slot.
fn get_storage(
&mut self,
address: H160,
slot: U256,
) -> U256;
}
/// Public interface of the VM state. Encompasses both read and write methods.
pub trait StateInterface {
/// Gets a storage handle.
fn storage(&mut self) -> impl StorageInterface;
// ...
} ... or even pub trait StateInterface: StorageInterface { /* ... */ } In order to do this, pub(crate) struct VmState<'a, T, W> {
vm: &'a mut VirtualMachine<T, W>,
world: &'a mut W,
}
impl<T: Tracer, W: World<T>> StateInterface for VmState<'_, T, W> {
// ...
} ...but I don't like it because it means that |
@slowli Take a look at the commit history. I initially had the adapter but it turned out very unergonomic because the state interface is used for non-tracer purposes as well. |
I see. How about this alternative: pub trait StateInterface {
// left as-is
}
// Name TBD
pub trait TracedStateInterface: StateInterface {
fn get_storage(
&mut self,
address: H160,
slot: U256,
) -> U256;
}
pub trait Tracer {
/// Executes logic before an instruction handler.
///
/// The default implementation does nothing.
fn before_instruction<OP: OpcodeType, S: TracedStateInterface>(
&mut self,
state: &mut S,
) {
// ...
}
} Then, pub trait StateInterface {
// left as-is
}
pub trait TracedStateInterface {
fn base(&self) -> &impl StateInterface;
fn base_mut(&mut self) -> &mut impl StateInterface;
// a potential alternative is having an associated type:
// type Base: StateInterface;
// and requiring `TracedStateInterface: AsRef<Self::Base> + AsMut<Self::Base>`
fn get_storage(
&mut self,
address: H160,
slot: U256,
) -> U256;
} Then, the wrapper would be as simple as pub(crate) struct VmState<'a, T, W> {
vm: &'a mut VirtualMachine<T, W>,
world: &'a mut W,
}
impl<T: Tracer, W: World<T>> TracedStateInterface for VmState<'_, T, W> {
fn base(&self) -> &impl StateInterface {
self.vm
}
fn base_mut(&mut self) -> &mut impl StateInterface {
self.vm
}
fn get_storage(&mut self, address: H160, slot: U256) -> U256 {
self.vm.world_diff.just_read_storage(self.world, address, slot)
}
} |
The extension trait is the best suggestion IMO because using the I also have an issue with the wrapper: It seems wrong to me to pass |
AFAICT, there are no tricks for such trait delegation in the language itself. There are some crates implementing such delegation, such as
I do think that the compiler should simplify this away; logically, there's no reason not to. In general, re-borrowing references in Rust is somewhat common, and it doesn't look like an anti-pattern. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
Tracers can now read storage slots. This is required for the validation tracer.
TODO