-
Notifications
You must be signed in to change notification settings - Fork 772
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
Error Management Improvements / Add error codes #487
Comments
Hi @rumkin, thanks for this extensive write-up! Error handling can definitely improved within the VM, and your motivational points you outlined are all pretty valid from my perspective. So if you want to do some work here that would be highly appreciated! 😄 @s1na Any additional thoughts or comments? One thing that came to my mind: I think we should also preserve broader error categorization like One way here would probably to (also/alternatively) do numerical error codes like (made up the numbers) Is there actually some kind of standard - e.g. for error codes - in the (some of the) other clients? |
Thanks for the proposal, I'm already on-board :) I'd add that in addition to error classification, we could also improve error handling, in the sense that which errors are expected (e.g. OOG within the context of evm), and which ones are unexpected and should be propagated and possibly cause a crash. |
Great! I'm in.
Errors could be event nested like so
For text codes there are prefixes. Prefix could has some additional separation ( The main thing is to make it easy to restore error on the other side of network connection. And if there is a numeric codes in use, then there should be some package with translator. Because developers are prone to handle errors in-place and if it's not possible they will not manage it at all.
I need to get deeper in EVM to be useful in such cases. But I've got some case with expected and unexpected errors in deeply nested tree in test running tool. And solution for me become a wrapper for unexpected errors which is using like a flag that error is already handled. catch (err) {
if (err instanceof UnitError || err instanceof RuntimeError) {
throw err;
}
else {
throw new RuntimeError(err);
}
} RuntimeError could be a regular object. To avoid duplicated stack trace capturing cause it's pretty expensive. Maybe it could be useful somehow. |
Had a short look, some inspirations from other clients (feel free to directly add to this list in this post by editing if permissions available): Do you think you have still some capacity to work on this or did something change in the meantime (feel free to openly let us know, this write-up is already pretty helpful)? And if so, would you want to do a small getting-started PR, maybe with the basic new structure handling one specific error case where classification is simple? |
I have problems with error management with the VM. When I get EvmError I can not debug where it is coming from. Any help here would be valuable. Is there a way to throw console messages? Or is there a way to have at least some debug message where the error stems from (maybe some require failed in solidity code - but where is the message from that require?) |
Hi @NikZak, could you post the However, I think you are looking into the "solidity error" which is being thrown. We do not throw this by default, and this is also how the EVM works. The EVM will run "calls", and these calls themselves might revert (error). To lookup these errors, you have to hook into the events of EVM:
The |
Thank you very much @jochem-brouwer! But I really need to know the exact error which solidity might have thrown (the "revert reason"). Would be amazing if you could point to steps that help get that |
Hi @NikZak, what you need to extract the reason here is to hook into the import { equalsBytes, bytesToBigInt, utf8ToBytes, bytesToUtf8, bytesToHex } from '@ethereumjs/util'
import { keccak256 } from 'ethereum-cryptography/keccak'
evm.events.on('afterMessage', (e) => {
if (e.execResult.exceptionError?.error === 'revert') {
const revertBytes = e.execResult.returnValue
// The revertBytes are the "raw bytes" which are thrown within the EVM
// If the contract is compiled by solidity, this is an ABI-encoded string
// See: https://gist.github.com/pcaversaccio/e1aebf9222424e9c1fbc82b8bf5fea8b
// To extract the reason:
// The first 4-bytes are the function selector of Error(string)
// The keccak256 hash of "Error(string)" is 08c379a0afcc32b1a39302f7cb8073359698411ab5fd6e3edb2c02c0b5fba8aa
// Take the first 4-bytes to get the function selector: 08c379a0
// Then, the next 32 bytes is the offset of the string in this memory, which points to the start of the revert string
// When reading this offset from memory, the first 32 bytes are interpreted as the string length
// The actual string follows after this
const fnSelector = keccak256(utf8ToBytes("Error(string)")).slice(0,4)
if (equalsBytes(fnSelector, revertBytes.slice(0, 4))) {
const relevantMemory = revertBytes.slice(4)
const offset = Number(bytesToBigInt(relevantMemory.slice(0, 32)))
const length = Number(bytesToBigInt(relevantMemory.slice(offset, offset+ 32)))
const stringStart = offset + 32
const stringBytes = relevantMemory.slice(stringStart, stringStart + length)
const revertString = bytesToUtf8(stringBytes)
console.log(`EVM Revert, reason: ${revertString} (length: ${length}))`)
} else {
console.log(`Non-solidity revert, raw revert bytes: ${bytesToHex(revertBytes)}`)
}
}
}) |
Oh, this is perfect! Helps a lot! Indeed informative messages now! Thanks a lot! |
Just for completeness - added panic messages too.
|
Ah, interesting, I was not aware of |
I will close this issue, the central error handling issue is now here: #3712 @NikZak if you need any further assistance feel free to open a new issue/discussion or ask on our discord: https://discord.gg/TNwARpR 😄 👍 |
Error management in long term couldn't rely on stringified error messages. For example Node.js decided to add error codes while ago because they met the issues with this. You can look at lib/internal/error.js to see how they have solved the problem.
In a few words node.js is using an error factory which receives error code, message formatter and base error class. And then use the factory to define list of errors.
I think they overengineered it: they simplify error creation but factory class creation make stack trace less informative. And this could be done using ES6 class inheritance mechanism.
I have some experience with such error handling. And can help to implement it in VM. Just decided to create an issue before making a PR.
Proposal
code
property to identify error.details
property to store error details as dictionary of primitives.format
method to produce error message from details object.Motivation
Example
Let's take upfront cost error in runTx method:
How it could be modified.
lib/errors.js
to hold EVM error constructors:tx_upfront_cost
:Use it in the runTx code:
The text was updated successfully, but these errors were encountered: