jam_pvm_common/
result.rs

1use jam_types::{InvokeOutcomeCode, SimpleResult, SimpleResultCode, LOWEST_ERROR};
2
3/// Error type for host-calls.
4#[derive(Debug)]
5pub enum ApiError {
6	/// `WHAT` Host call invalid
7	HostCallInvalid,
8	/// `OOB` Invalid memory access.
9	OutOfBounds,
10	/// `WHO` Target service is unknown.
11	IndexUnknown,
12	/// `FULL` Too much storage is used by the service for its holdings.
13	StorageFull,
14	/// `CORE` Bad core index given.
15	BadCore,
16	/// `CASH` The caller has too little funding.
17	NoCash,
18	/// `LOW` The gas limit provided is too low (lower than the amount of gas required for the
19	/// transfer).
20	GasLimitTooLow,
21	/// `HUH` The hash is already solicited or forgotten.
22	ActionInvalid,
23}
24
25impl From<u64> for ApiError {
26	fn from(code: SimpleResult) -> Self {
27		match code {
28			c if c == SimpleResultCode::HostCallInvalid as u64 => ApiError::HostCallInvalid,
29			c if c == SimpleResultCode::OutOfBounds as u64 => ApiError::OutOfBounds,
30			c if c == SimpleResultCode::IndexUnknown as u64 => ApiError::IndexUnknown,
31			c if c == SimpleResultCode::StorageFull as u64 => ApiError::StorageFull,
32			c if c == SimpleResultCode::BadCore as u64 => ApiError::BadCore,
33			c if c == SimpleResultCode::NoCash as u64 => ApiError::NoCash,
34			c if c == SimpleResultCode::GasLimitTooLow as u64 => ApiError::GasLimitTooLow,
35			c if c == SimpleResultCode::ActionInvalid as u64 => ApiError::ActionInvalid,
36			_ => panic!("unknown error code: {}", code),
37		}
38	}
39}
40
41/// Result type for host-calls, parameterized by the type of the successful result.
42pub type ApiResult<T> = Result<T, ApiError>;
43
44/// Simple conversion trait for types which can be converted to a regular option.
45pub trait IntoApiOption<T> {
46	fn into_api_option(self) -> Option<T>;
47}
48
49impl IntoApiOption<()> for SimpleResult {
50	fn into_api_option(self) -> Option<()> {
51		if self == SimpleResultCode::Ok as u64 {
52			Some(())
53		} else if self == SimpleResultCode::Nothing as u64 {
54			None
55		} else {
56			panic!("Our own API impl has resulted in success value which is out of range.");
57		}
58	}
59}
60impl IntoApiOption<u64> for SimpleResult {
61	fn into_api_option(self) -> Option<u64> {
62		if self < LOWEST_ERROR {
63			Some(self)
64		} else if self == SimpleResultCode::Nothing as u64 {
65			None
66		} else {
67			panic!("Our own API impl has resulted in success value which is out of range.");
68		}
69	}
70}
71impl IntoApiOption<u32> for SimpleResult {
72	fn into_api_option(self) -> Option<u32> {
73		if self <= u32::MAX as _ {
74			Some(self as _)
75		} else if self == SimpleResultCode::Nothing as u64 {
76			None
77		} else {
78			panic!("Our own API impl has resulted in success value which is out of range.");
79		}
80	}
81}
82impl IntoApiOption<usize> for SimpleResult {
83	fn into_api_option(self) -> Option<usize> {
84		if self < LOWEST_ERROR && self <= usize::MAX as _ {
85			Some(self as _)
86		} else if self == SimpleResultCode::Nothing as u64 {
87			None
88		} else {
89			panic!("Our own API impl has resulted in success value which is out of range.");
90		}
91	}
92}
93
94/// Simple conversion trait for types which can be converted to a regular host-call API result.
95pub trait IntoApiResult<T> {
96	fn into_api_result(self) -> ApiResult<T>;
97}
98
99impl IntoApiResult<()> for SimpleResult {
100	fn into_api_result(self) -> ApiResult<()> {
101		if self == SimpleResultCode::Ok as u64 {
102			Ok(())
103		} else {
104			Err(self.into())
105		}
106	}
107}
108impl IntoApiResult<u64> for SimpleResult {
109	fn into_api_result(self) -> ApiResult<u64> {
110		if self < LOWEST_ERROR {
111			Ok(self)
112		} else {
113			Err(self.into())
114		}
115	}
116}
117impl IntoApiResult<usize> for SimpleResult {
118	fn into_api_result(self) -> ApiResult<usize> {
119		if self <= usize::MAX as _ && self < LOWEST_ERROR {
120			Ok(self as usize)
121		} else if self < LOWEST_ERROR {
122			panic!("Our own API impl has resulted in success value which is out of range.");
123		} else {
124			Err(self.into())
125		}
126	}
127}
128impl IntoApiResult<u32> for SimpleResult {
129	fn into_api_result(self) -> ApiResult<u32> {
130		if self <= u32::MAX as _ {
131			Ok(self as u32)
132		} else if self < LOWEST_ERROR {
133			panic!("Our own API impl has resulted in success value which is out of range.");
134		} else {
135			Err(self.into())
136		}
137	}
138}
139impl IntoApiResult<Option<()>> for SimpleResult {
140	fn into_api_result(self) -> ApiResult<Option<()>> {
141		if self < LOWEST_ERROR {
142			Ok(Some(()))
143		} else if self == SimpleResultCode::Nothing as u64 {
144			Ok(None)
145		} else {
146			Err(self.into())
147		}
148	}
149}
150impl IntoApiResult<Option<u64>> for SimpleResult {
151	fn into_api_result(self) -> ApiResult<Option<u64>> {
152		if self < LOWEST_ERROR {
153			Ok(Some(self))
154		} else if self == SimpleResultCode::Nothing as u64 {
155			Ok(None)
156		} else {
157			Err(self.into())
158		}
159	}
160}
161impl IntoApiResult<Option<usize>> for SimpleResult {
162	fn into_api_result(self) -> ApiResult<Option<usize>> {
163		if self <= usize::MAX as _ && self < LOWEST_ERROR {
164			Ok(Some(self as usize))
165		} else if self < LOWEST_ERROR {
166			panic!("Our own API impl has resulted in success value which is out of range.");
167		} else if self == SimpleResultCode::Nothing as u64 {
168			Ok(None)
169		} else {
170			Err(self.into())
171		}
172	}
173}
174
175/// The successful result of inner PVM invocations.
176#[derive(Clone, Copy)]
177pub enum InvokeOutcome {
178	/// `HALT` Completed normally.
179	Halt,
180	/// `FAULT` Completed with a page fault.
181	PageFault(u64),
182	/// `HOST` Completed with a host-call fault.
183	HostCallFault(u64),
184	/// `PANIC` Completed with a panic.
185	Panic,
186	/// `OOG` Completed by running out of gas.
187	OutOfGas,
188}
189
190/// The result of the [crate::refine::invoke] host-call.
191pub type InvokeResult = ApiResult<InvokeOutcome>;
192
193/// Simple trait to convert to `InvokeResult`.
194pub trait IntoInvokeResult {
195	/// Convert `self` to `InvokeResult`.
196	fn into_invoke_result(self) -> InvokeResult;
197}
198
199impl IntoInvokeResult for (u64, u64) {
200	fn into_invoke_result(self) -> InvokeResult {
201		const STATUS_HALT: u64 = InvokeOutcomeCode::Halt as u64;
202		const STATUS_PANIC: u64 = InvokeOutcomeCode::Panic as u64;
203		const STATUS_FAULT: u64 = InvokeOutcomeCode::PageFault as u64;
204		const STATUS_HOST: u64 = InvokeOutcomeCode::HostCallFault as u64;
205		const STATUS_OOG: u64 = InvokeOutcomeCode::OutOfGas as u64;
206		// Convert `invoke` return value to `Result`.
207		match self {
208			(STATUS_HALT, _) => Ok(InvokeOutcome::Halt),
209			(STATUS_FAULT, address) => Ok(InvokeOutcome::PageFault(address)),
210			(STATUS_HOST, index) => Ok(InvokeOutcome::HostCallFault(index)),
211			(STATUS_PANIC, _) => Ok(InvokeOutcome::Panic),
212			(STATUS_OOG, _) => Ok(InvokeOutcome::OutOfGas),
213			(code, _) => Err(code.into()),
214		}
215	}
216}