-
Notifications
You must be signed in to change notification settings - Fork 79
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
EVM runtime: implement GetBytecode and GetStorageAt getters. #631
Conversation
a18021d
to
f73b364
Compare
GetBytecode returns the CID of the bytecode block. In the future, the FVM's reachability analysis should add this CID to the reachable set of the caller. GetBytecode is publicly callable. GetStorageAt returns the value stored in the supplied EVM storage key. This method is private and only intended for calls from instrumentation and client functionality, such as the eth_getStorageAt JSON-RPC API operation. In order to make it private, it validates that the caller is the zero address. It needs to be private to preserve EVM guarantees: no contract is able to arbitrarily access storage keys from another contract. The client must form a message from the 0x0 sender address for this method to work.
f73b364
to
46314db
Compare
let output_usize = (output_size.bits() < 32) | ||
.then(|| output_size.as_usize()) | ||
.ok_or(StatusCode::InvalidMemoryAccess)?; | ||
|
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.
This was a nit from a previous review that stuck in my local copy. This is more idiomatic.
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.
Putting on my pedantic hat...
then
is usually used in place of:
if foo {
Some(1+2)
} else {
None
}
// easier to read as `foo.then(||1+2)`
In cases like this, I'd usually write:
if output_size.bits() >= 32 {
return Err(StatusCode::InvalidMemoryAccess);
}
... taking off my pedantic hat, this code is perfectly fine any which way.
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.
i think the original code was cleaner here, you ar going overboard with scala-like code!
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.
I am gonna rewrite this to not be like scale, but like steven.
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.
fixed in 69462dd, the scala-like incantation hurt my eyes. Let's not declare war on if!
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.
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.
Very clean (with a test!).
let output_usize = (output_size.bits() < 32) | ||
.then(|| output_size.as_usize()) | ||
.ok_or(StatusCode::InvalidMemoryAccess)?; | ||
|
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.
Putting on my pedantic hat...
then
is usually used in place of:
if foo {
Some(1+2)
} else {
None
}
// easier to read as `foo.then(||1+2)`
In cases like this, I'd usually write:
if output_size.bits() >= 32 {
return Err(StatusCode::InvalidMemoryAccess);
}
... taking off my pedantic hat, this code is perfectly fine any which way.
// load the storage HAMT | ||
let mut hamt = | ||
Hamt::<_, _, U256>::load(&state.contract_state, blockstore).map_err(|e| { | ||
ActorError::illegal_state(format!( | ||
"failed to load storage HAMT on invoke: {e:?}, e" | ||
)) | ||
})?; | ||
|
||
let mut system = System::new(rt, &mut hamt).map_err(|e| { | ||
ActorError::unspecified(format!("failed to create execution abstraction layer: {e:?}")) | ||
})?; |
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.
Looks like we need to do this a lot. Helper?
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.
we should probably make one, but that's out of scope here.
I would like to do a general cleanup/beatify pass at some point.
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.
btw, feel free to go wild and apply bigclaw magic in the code when there is a lull.
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 tests!
{ | ||
// This method cannot be called on-chain; other on-chain logic should not be able to | ||
// access arbitrary storage keys from a contract. | ||
rt.validate_immediate_caller_is([&fvm_shared::address::Address::new_id(0)])?; |
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.
This very funky, we have an effectively uncallable method.
Thats one extra reason to expect the client to directly read the state, or it should be a non method entry point.
Having said that, it is totally fine for now, but we should think about this general pattern.
BS: Blockstore + Clone, | ||
RT: Runtime<BS>, | ||
{ | ||
// Any caller can fetch the bytecode of a contract; this is now EXT* opcodes work. |
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.
nit: this is now
-> this is how
…n-project#631) * EVM runtime: implement GetBytecode and GetStorageAt getters. GetBytecode returns the CID of the bytecode block. In the future, the FVM's reachability analysis should add this CID to the reachable set of the caller. GetBytecode is publicly callable. GetStorageAt returns the value stored in the supplied EVM storage key. This method is private and only intended for calls from instrumentation and client functionality, such as the eth_getStorageAt JSON-RPC API operation. In order to make it private, it validates that the caller is the zero address. It needs to be private to preserve EVM guarantees: no contract is able to arbitrarily access storage keys from another contract. The client must form a message from the 0x0 sender address for this method to work. * it's not scala! Co-authored-by: vyzo <vyzo@hackzen.org>
GetBytecode
returns the CID of the bytecode block. In the future, the FVM's reachability analysis should add this CID to the reachable set of the caller.GetBytecode
is publicly callable.GetStorageAt
returns the value stored in the supplied EVM storage key. This method is private and only intended for calls from instrumentation and client functionality, such as theeth_getStorageAt
JSON-RPC API operation. In order to make it private, it validates that the caller is the zero address. It needs to be private to preserve EVM guarantees: no contract is able to arbitrarily access storage keys from another contract. The client must form a message from the 0x0 sender address for this method to work.Supports filecoin-project/ref-fvm#838
Supports filecoin-project/ref-fvm#854