-
Notifications
You must be signed in to change notification settings - Fork 635
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: rework contract runtime error processing #4181
Changes from all commits
b2a6d8b
2641d36
60e6a2a
549f582
b67513d
28c2d52
bdc1bce
5656615
1541dcf
882eb2e
1273bf2
0b350e6
55d6801
9bca1f0
5afaca4
a7b3a6d
378b864
27541bf
4d4033a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,11 +1,11 @@ | ||||||
use std::fmt::{self, Error, Formatter}; | ||||||
|
||||||
use borsh::{BorshDeserialize, BorshSerialize}; | ||||||
use serde::{Deserialize, Serialize}; | ||||||
|
||||||
use near_rpc_error_macro::RpcError; | ||||||
use serde::{Deserialize, Serialize}; | ||||||
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, BorshDeserialize, BorshSerialize, Deserialize, Serialize)] | ||||||
// TODO: remove serialization derives, once fix compilation caching. | ||||||
#[derive(Debug, Clone, PartialEq, Eq, BorshDeserialize, BorshSerialize, Serialize, Deserialize)] | ||||||
pub enum VMError { | ||||||
FunctionCallError(FunctionCallError), | ||||||
/// Serialized external error from External trait implementation. | ||||||
|
@@ -17,10 +17,34 @@ pub enum VMError { | |||||
CacheError(CacheError), | ||||||
} | ||||||
|
||||||
#[derive( | ||||||
Debug, Clone, PartialEq, Eq, BorshDeserialize, BorshSerialize, Deserialize, Serialize, RpcError, | ||||||
)] | ||||||
// TODO(4217): remove serialization derives, once fix compilation caching. | ||||||
#[derive(Debug, Clone, PartialEq, Eq, BorshDeserialize, BorshSerialize, Serialize, Deserialize)] | ||||||
pub enum FunctionCallError { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this enum still needed given the presence of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it's used inside the contract runtime and have precise execution problem description. We don't want it to leak beyond contract runtime. |
||||||
/// Wasm compilation error | ||||||
CompilationError(CompilationError), | ||||||
/// Wasm binary env link error | ||||||
LinkError { | ||||||
msg: String, | ||||||
}, | ||||||
/// Import/export resolve error | ||||||
MethodResolveError(MethodResolveError), | ||||||
/// A trap happened during execution of a binary | ||||||
WasmTrap(WasmTrap), | ||||||
WasmUnknownError { | ||||||
debug_message: String, | ||||||
}, | ||||||
HostError(HostError), | ||||||
EvmError(EvmError), | ||||||
/// Non-deterministic error. | ||||||
Nondeterministic(String), | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this error deterministic, i.e, is it guaranteed that a nondeterministic error will be wrapped in this variant? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, now it is. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I doubt of still keep it WasmUnkownError is a good for wasmer 1. The internal of it is really unknown in wasmer 0.x (UnknownTrap etc.) but it's indeed known in wasmer 1. I suggest name it as WasmerRuntimeError or WasmerInternalError There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reworked, so that |
||||||
} | ||||||
|
||||||
/// Serializable version of `FunctionCallError`. Must never reorder/remove elements, can only | ||||||
/// add new variants at the end (but do that very carefully). This type must be never used | ||||||
/// directly, and must be converted to `ContractCallError` instead using `into()` converter. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One missing in doc: since it's never used directly, when it should be used? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, described that it is used in serialization. |
||||||
/// It describes stable serialization format, and only used by serialization logic. | ||||||
#[derive(Debug, Clone, PartialEq, Eq, BorshDeserialize, BorshSerialize, Serialize, Deserialize)] | ||||||
pub enum FunctionCallErrorSer { | ||||||
/// Wasm compilation error | ||||||
CompilationError(CompilationError), | ||||||
/// Wasm binary env link error | ||||||
|
@@ -34,13 +58,9 @@ pub enum FunctionCallError { | |||||
WasmUnknownError, | ||||||
HostError(HostError), | ||||||
EvmError(EvmError), | ||||||
/// An error message when wasmer 1.0 returns a wasmer::RuntimeError | ||||||
WasmerRuntimeError(String), | ||||||
/// A trap in Wasmer 1.0, not same as WasmTrap above, String is a machine readable form like "stk_ovf" | ||||||
/// String is used instead of wasmer internal enum is because of BorshSerializable. | ||||||
/// It can be convert back by wasmer_vm::TrapCode::from_str | ||||||
Wasmer1Trap(String), | ||||||
ExecutionError(String), | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd do
Suggested change
just in case. This thing is serializable to JSON, and, tuple variants are not extensible: #[derive(serde::Serialize, serde::Deserialize)]
enum E1 {
V(String),
}
#[derive(serde::Serialize, serde::Deserialize)]
enum E2 {
V { msg: String },
}
#[derive(serde::Serialize, serde::Deserialize)]
enum E3 {
V { msg: String, code: u32 },
}
fn main() {
println!("{}", serde_json::to_string(&E1::V("hello".into())).unwrap());
println!("{}", serde_json::to_string(&E2::V { msg: "hello".into() }).unwrap());
println!("{}", serde_json::to_string(&E3::V { msg: "hello".into(), code: 92 }).unwrap());
}
// OUTPUT
{"V":"hello"}
{"V":{"msg":"hello"}}
{"V":{"msg":"hello","code":92}} Migrating Now, because this is also borsh serializable, this might not be super-relevant, as adding a field to a borsh struct is not compatible, but it's better to be on the safe side here (and consistent with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, done. |
||||||
} | ||||||
|
||||||
#[derive( | ||||||
Debug, Clone, PartialEq, Eq, BorshDeserialize, BorshSerialize, Deserialize, Serialize, RpcError, | ||||||
)] | ||||||
|
@@ -67,8 +87,8 @@ pub enum WasmTrap { | |||||
IllegalArithmetic, | ||||||
/// Misaligned atomic access trap. | ||||||
MisalignedAtomicAccess, | ||||||
/// Breakpoint trap. | ||||||
BreakpointTrap, | ||||||
/// Indirect call to null. | ||||||
IndirectCallToNull, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a test case to trigger this error? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense, will do. |
||||||
/// Stack overflow. | ||||||
StackOverflow, | ||||||
/// Generic trap. | ||||||
|
@@ -363,12 +383,13 @@ impl fmt::Display for FunctionCallError { | |||||
FunctionCallError::HostError(e) => e.fmt(f), | ||||||
FunctionCallError::LinkError { msg } => write!(f, "{}", msg), | ||||||
FunctionCallError::WasmTrap(trap) => write!(f, "WebAssembly trap: {}", trap), | ||||||
FunctionCallError::WasmUnknownError => { | ||||||
write!(f, "Unknown error during Wasm contract execution") | ||||||
FunctionCallError::WasmUnknownError { debug_message } => { | ||||||
write!(f, "Unknown error during Wasm contract execution: {}", debug_message) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Still feel we should rename this to WasmInternalError, it's a paradox that it's unknown then error detail (debug_message) is given There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any renames like that would lead to serialization issues again. Think is general we shall go in direction of separatimg runtime and serialized data structures. |
||||||
} | ||||||
FunctionCallError::EvmError(e) => write!(f, "EVM: {:?}", e), | ||||||
FunctionCallError::WasmerRuntimeError(e) => write!(f, "Wasmer Runtime: {}", e), | ||||||
FunctionCallError::Wasmer1Trap(e) => write!(f, "Wasmer 1.0 trap: {}", e), | ||||||
FunctionCallError::Nondeterministic(msg) => { | ||||||
write!(f, "Nondeterministic error during contract execution: {}", msg) | ||||||
} | ||||||
} | ||||||
} | ||||||
} | ||||||
|
@@ -387,8 +408,8 @@ impl fmt::Display for WasmTrap { | |||||
} | ||||||
WasmTrap::MisalignedAtomicAccess => write!(f, "Misaligned atomic access trap."), | ||||||
WasmTrap::GenericTrap => write!(f, "Generic trap."), | ||||||
WasmTrap::BreakpointTrap => write!(f, "Breakpoint trap."), | ||||||
WasmTrap::StackOverflow => write!(f, "Stack overflow."), | ||||||
WasmTrap::IndirectCallToNull => write!(f, "Indirect call to null."), | ||||||
} | ||||||
} | ||||||
} | ||||||
|
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 is really good structure, self documented of what could be wrong in contract call :+!