-
Notifications
You must be signed in to change notification settings - Fork 541
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
Return more sane value that replaces Return
#309
Comments
|
I can have a look at this, if you'd like. Is your idea to add a generic to If so, would that generic need to implement |
That would be great! I am okay with |
Gotcha. I'll create a proof of concept and then we can continue discussing from there 🙂 |
I started working on it when I realised that this will be a breaking change, as we'll be replacing the |
It should be a breaking change, plan is to publish v3.0.0 version as there are a lot of breaking changes on |
I'll prioritise this then! |
You dont need to rush it, I am taking it slowly, the timeline is around the middle to end of January. |
Before delving too deep, I wanted to discuss the design, as it seems like the changes might be quite invasive to allow errors to bubble up all the way from the EVM implementation. I was thinking of using the following error types, inspired by the Python reference implementation of the execution spec [1, 2]: /// Indicates that the EVM has experienced an exceptional halt. This causes execution to
/// immediately end with all gas being consumed.
#[derive(Debug, thiserror::Error)]
pub enum ExceptionalHalt {
#[error("The new contract code starts with 0xEF")]
InvalidContractPrefix,
/// Occurs when the destination of a jump operation doesn't meet any of the following criteria:
///
/// - The jump destination is less than the length of the code.
/// - The jump destination should have the `JUMPDEST` opcode (0x5B).
/// - The jump destination shouldn't be part of the data corresponding to `PUSH-N` opcodes.
#[error("Invalid jump destination encountered")]
InvalidJumpDest,
#[error("Invalid opcode is encountered")]
InvalidOpcode,
#[error("Invalid parameters are passed")]
InvalidParameter,
#[error("Reading data beyond the boundaries of the buffer")]
OutOfBoundsRead,
#[error("The operation costs more than the amount of gas left in the frame")]
OutOfGas,
#[error("The message depth is greater than `1024`")]
StackDepthLimit,
#[error("A push is executed on a stack at max capacity")]
StackOverflow,
#[error("A pop is executed on an empty stack")]
StackUnderflow,
#[error("Modifying the state while operating inside of a STATICCALL context")]
WriteInStaticContext,
}
/// All Ethereum errors that might occur by the specification during normal operation.
#[derive(Debug, thiserror::Error)]
pub enum EthereumError {
#[error(transparent)]
ExceptionalHalt(#[from] ExceptionalHalt),
#[error("The block being processed is found to be invalid")]
InvalidBlock,
#[error("RLP decoding failed")]
InvalidRlpDecoding,
#[error("RLP encoding failed")]
InvalidRlpEncoding,
/// Raised by the `REVERT` opcode.
///
/// Unlike other EVM exceptions this does not result in the consumption of all gas.
#[error("Raised by the `REVERT` opcode")]
Revert,
}
#[derive(Debug, thiserror::Error)]
pub enum EvmError<DE: Debug> {
#[error(transparent)]
Database(#[from] DatabaseError<DE>),
#[error(transparent)]
EthereumError(#[from] EthereumError),
}
#[derive(Debug, thiserror::Error)]
pub enum DatabaseError<E: Debug> {
#[error("The database returned an error")]
Failure(E),
#[error("The database is missing code for a contract")]
MissingCode(B160),
} Things to note:
Open question(s):
|
I like
From the first post To be honest I am not sure what is the best way to pack all of that. Crazy idea, but wdyt about double Result as in btw I am doing some internal refactoring of revm, so I would like this change to be front facing, as in mostly in evm_impl: https://github.com/bluealloy/revm/blob/main/crates/revm/src/evm_impl.rs |
My two cents: I can't give an informed opinion on the Rust side of this (ergonomics, etc.), but conceptually this makes a lot of sense to me. |
They are an error at a lower-level. The way that the EVM handles it is by catching that error and consuming all gas. After that it's no longer an error. The
After some more implementation work, I landed on this design:
The
When do you think you'll finish - as there'll likely be conflicts? I can try and split up my work. What I've done so far was a lot of investigation, which will be valuable regardless, but the changeset is quite big: https://github.com/Wodann/revm/tree/improvement/return One thing I can definitely do that's front-facing is add a transaction validation step before we're running the EVM. That should ensure that we can't get My hope here would be that I can reduce the design to: pub enum Reason {
Success(Eval),
Failure(ExceptionalHalt),
}
I'm merely basing it on the official Eth execution spec: https://github.com/ethereum/execution-specs/blob/24de2192e02bba11e61746aa9dee04d4e7a8b62e/src/ethereum/exceptions.py#L16 Is RLP encoding & decoding infallible in |
The thing with this: pub enum Eval {
Continue = 0x00,
Stop = 0x01,
Return = 0x02,
SelfDestruct = 0x03,
/// Raised by the `REVERT` opcode.
///
/// Unlike other EVM exceptions this does not result in the consumption of all gas.
Revert = 0x20,
} is that the type doesn't encode if the transaction reverted or not, which I feel could be useful (otherwise you have to check the value to see if it's in a "revert" range). The nested |
That's what the Or do you mean, whether we experienced an |
Hey, really sorry that I am not that responsive, I am on vacation and have a lot of family stuff that I am doing.
This is useful, we can go with this!
This revert is more like opcode @Wodann most changes that I expect to happen would be located in this function. I see this as localized change and not full replacement of revm/crates/revm/src/evm_impl.rs Line 44 in 3a13c9c
|
@Wodann Maybe I'm not understanding your proposal correctly, but my point is that after sending a transaction to be executed these are the things I would like to know about the result:
It seems to me that in that design, doing 1 is easy but doing 2 and 3 not so much, because you have to know which enum values correspond to "succeeded" and which to "reverted". This is similar to what happens now with the |
I feel like the second case is covered by |
No worries at all. I had enough info to submit a first PR. If that's good, I can proceed with the |
Cherry-picked ideas from here and came to this: https://github.com/bluealloy/revm/blob/primitives/crates/primitives/src/result.rs#L58-L109 It is only used at the end of
|
Overall, looks good to me. I just have one question: Are there no logs in the case of an |
Should be okay, any |
For deployed contracts that makes most sense, yes. I thought it might be useful for in-development contracts for a developer to be able to observe the logs up until the |
This would override the default mechanism that evm has, reverting everything is what every call is doing, |
merged here: #334 |
@rakita would you be opposed to convert these types:
to not use the pub enum DatabaseComponentError<SE, BHE> {
State(SE),
BlockHash(BHE),
} The reason for this request is that an implementation of fn foo() -> DatabaseComponentError<S::Error, BH::Error> The example that I'm running into where this poses an issue is the usage: I see that you already applied this pattern to |
Make sense, didn't think about it and did first thing that came to my mind. Go for it! |
I'll submit a PR shortly |
Return
is a enum and it is internal structure, exposing it outside of revm creates not that great interface.There are four states that can be returns:
Success
: Transaction executed and it was successFailure
: Transaction executed but it failed: Check this: https://eips.ethereum.org/EIPS/eip-658Error
: Transaction not executed. Not enough balance in account to cover max gas consumes.ExternalError
: Transaction not executed, external database error.The text was updated successfully, but these errors were encountered: