-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Enrich detail of Seal contract trap errors #5239
Comments
This sounds very useful. First, I think we need to do the printing only opt-in ensuring that this functionality is not enabled by default on production nodes. Probably Second, there is complication that the formatting machineries are basically non-existent in substrate at the moment of writing, so we should need to workaround that or settle on more simpler formatting, e.g.:
Linking #2082 since it can help with this kind of separation of runtime logic and diagnostic logic. |
A reasonable demand. I also ran into the dreaded |
I did some investigation. It seems doable as there are ways of passing errors from wasmi host functions to the invoker. Currently,
We need to change the error type in order to be able to pass error information from a host function to the invoker. I suppose we should use something like failure in order to record the error causes:
The "called from" lines are just added by the contract module which walks through all the callers in case when the sandbox reports a failure. I am not sure if we can use |
Heh, this was my first idea when we discussed this with Robin and I am not sure how that is gonna work. When a contract calls another contract and then the callee fails, then the failure is not cascaded through the call chain. Instead, the caller gets a return status code. I also don't see contracts cooperatively passing an error up the stack. Instead, we could just print this information to the runtime's stdout. It seems this is the best we can do so far. Anyway, regardless however it is implemented, the production code path should not regress in performance. |
You mean just using |
Not quite, although similar: the |
So what turns out to be a pretty good solution for having at least some decent error reporting for supervisor triggered errors is the following approach that I already used when I adding a new error kind:
This will not allow for dynamic error messages (formatting a message). As a corollary to this stack traces are also not possible. On the upside it allows those messages to be printed to the UI (e.G polkadot JS apps). |
Currently, our tool for run contract in a sandbox Europa has support the similar feature like: 1: NestedRuntime {
ext_result: [success] ExecReturnValue { flags: 0, data: 7c000000 },
caller: 0000000000000000000000000000000000000000000000000000000000000000 (5C4hrfjw...),
self_account: 36b1ebfb6e6f31fa0a9b9d934db3033bc5766bc0a52ddd5656cb54e91903ad70 (5DJRJAXF...),
selector: 0x1e5ca456,
args: None,
value: 0,
gas_limit: 4999999999999,
gas_left: 4997774243082,
env_trace: [
seal_value_transferred(Some(0x00000000000000000000000000000000)),
seal_input(Some(0x1e5ca456)),
seal_get_storage((Some(0x0000000000000000000000000000000000000000000000000000000000000000), Some(0x829153a24020d6c6e3a66aaaa7e5235114553fc0a2e91b1f2bd49b50c2317696))),
seal_call((Some(0x829153a24020d6c6e3a66aaaa7e5235114553fc0a2e91b1f2bd49b50c2317696), 0, Some(0), Some(0x1e5ca456), Some(0x7c000000), Some(ReturnCode::Success))),
seal_return((0, Some(0x7c000000))),
],
trap_reason: TrapReason::Return(ReturnData { flags: 0, data: 7c000000 }),
nest: [
2: NestedRuntime {
ext_result: [success] ExecReturnValue { flags: 0, data: 7c000000 },
caller: 36b1ebfb6e6f31fa0a9b9d934db3033bc5766bc0a52ddd5656cb54e91903ad70 (5DJRJAXF...),
self_account: 829153a24020d6c6e3a66aaaa7e5235114553fc0a2e91b1f2bd49b50c2317696 (5F1uFe5U...),
selector: 0x1e5ca456,
args: None,
value: 0,
gas_limit: 4998256583394,
gas_left: 4997888971426,
env_trace: [
seal_value_transferred(Some(0x00000000000000000000000000000000)),
seal_input(Some(0x1e5ca456)),
seal_get_storage((Some(0x0000000000000000000000000000000000000000000000000000000000000000), Some(0x7c000000))),
seal_return((0, Some(0x7c000000))),
],
trap_reason: TrapReason::Return(ReturnData { flags: 0, data: 7c000000 }),
nest: [],
},
],
} The simple log: 1: NestedRuntime {
self_account: 36b1ebfb6e6f31fa0a9b9d934db3033bc5766bc0a52ddd5656cb54e91903ad70 (5DJRJAXF...),
nest: [
2: NestedRuntime {
self_account: 829153a24020d6c6e3a66aaaa7e5235114553fc0a2e91b1f2bd49b50c2317696 (5F1uFe5U...),
nest: [],
}
]
} is same with trap during contract execution.
reason: ext_get_storage failed
caused by: failed to decode key
called from: contract at 0x293843249
called from: contract at 0x873317821
called from: etc. And we have detailed wasm backtrace to locate the panic or error location, even for host_function error. More information could refer our doc And I think our implemention may provide some help for you guys. :p Our implementation is mainly in this pr patractlabs#16 |
Thanks for your great work creating the europa sandbox. The thing is that we cannot allow any overhead during on-chain execution. This prevents us from just doing the same changes you did. Additionally, I don't think that generating backtraces is appropriate for pallet contracts. With the existence of europa it can be the place to debug contracts before deployment. There is no need to have fully fledged back tracing in pallet contracts. We need to keep it simple and auditable. |
Yes, I very agree with you opinion. I just notice this issue in "Smart Contract" project TODO list, and notice this issue is posted long time ago. I just so you know if you guys need to do this feature again, Europa may provide a method to do it. All in all, I think this issue could be closed for this thing may not in you plan, for the final result you just log the error info in the chain event, do not need other information. |
I plan to tacke this issue with the following plan:
This will give users and novice developers good feedback from the get go. For more in-depth debugging patractlab's Europa can be used. |
This is mostly done by having the debug buffer. I think this issue is way to convoluted. A follow up issue should be opened in case the debug buffer isn't sufficient. |
Extended Trap Information
Problem
There are mainly two different types how a contract execution can trap:
For the second case the contracts pallet currently only prints
trap during contract execution
message to the console. This mostly doesn't help a contract writer a lot and they eventually have to path their way forward withprintln
-debugging which is not sustainable nor scalable.Proposal
But we can do better in the second case where the trap happens due to some preconditions that have not been met.
Example
In the case of a call to
ext_get_storage(key_ptr: u32, key_len: u32)
the contracts pallet traps if it cannot properly decode the(key_ptr, key_len)
representing slice of bytes into a properKey
. While currently the contracts pallet simply prints outtrap during contract execution
it actually could do better, e.g. by providing a reason:For a whole call stack we could even provide the call stack until the point of failure:
This would provide the contract writer with much more helpful additional information to actually get to the point of failure within their contract logic.
Also this should be fairly simple to add to the contracts pallet since it already maintains the call stack of every contract execution. Printing this information would simply access the already managed state.
Note that this is not comparable to stack traces and just a middle ground solution that should provide practical help quickly. It should be noted here that better error reporting is one of the most frequently asked for features in contracts. So we really need to push this forward!
Also note that for the implemented error reports to the console we would never guarantee their contents which means that once we can do better we could easily exchange the whole error reporting mechanism with a more mature one, e.g. proper stack traces upon a trap.
The text was updated successfully, but these errors were encountered: