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	/// `OOB` Invalid memory access.
7	OutOfBounds,
8	/// `WHO` Target service is unknown.
9	IndexUnknown,
10	/// `FULL` Too much storage is used by the service for its holdings.
11	StorageFull,
12	/// `CORE` Bad core index given.
13	BadCore,
14	/// `CASH` The caller has too little funding.
15	NoCash,
16	/// `LOW` The gas limit provided is too low (lower than the amount of gas required for the
17	/// transfer).
18	GasLimitTooLow,
19	/// `HIGH` The gas limit provided is too high (higher than the amount of gas remaining).
20	GasLimitTooHigh,
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::OutOfBounds as u64 => ApiError::OutOfBounds,
29			c if c == SimpleResultCode::IndexUnknown as u64 => ApiError::IndexUnknown,
30			c if c == SimpleResultCode::StorageFull as u64 => ApiError::StorageFull,
31			c if c == SimpleResultCode::BadCore as u64 => ApiError::BadCore,
32			c if c == SimpleResultCode::NoCash as u64 => ApiError::NoCash,
33			c if c == SimpleResultCode::GasLimitTooLow as u64 => ApiError::GasLimitTooLow,
34			c if c == SimpleResultCode::GasLimitTooHigh as u64 => ApiError::GasLimitTooHigh,
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.
176pub enum InvokeOutcome {
177	/// `HALT` Completed normally.
178	Halt,
179	/// `FAULT` Completed with a page fault.
180	PageFault(u64),
181	/// `HOST` Completed with a host-call fault.
182	HostCallFault(u64),
183	/// `PANIC` Completed with a panic.
184	Panic,
185	/// `OOG` Completed by running out of gas.
186	OutOfGas,
187}
188
189/// The result of the [crate::refine::invoke] host-call.
190pub type InvokeResult = ApiResult<InvokeOutcome>;
191
192/// Simple trait to convert to `InvokeResult`.
193pub trait IntoInvokeResult {
194	/// Convert `self` to `InvokeResult`.
195	fn into_invoke_result(self) -> InvokeResult;
196}
197
198impl IntoInvokeResult for (u64, u64) {
199	fn into_invoke_result(self) -> InvokeResult {
200		const STATUS_HALT: u64 = InvokeOutcomeCode::Halt as u64;
201		const STATUS_PANIC: u64 = InvokeOutcomeCode::Panic as u64;
202		const STATUS_FAULT: u64 = InvokeOutcomeCode::PageFault as u64;
203		const STATUS_HOST: u64 = InvokeOutcomeCode::HostCallFault as u64;
204		const STATUS_OOG: u64 = InvokeOutcomeCode::OutOfGas as u64;
205		// Convert `invoke` return value to `Result`.
206		match self {
207			(STATUS_HALT, _) => Ok(InvokeOutcome::Halt),
208			(STATUS_FAULT, address) => Ok(InvokeOutcome::PageFault(address)),
209			(STATUS_HOST, index) => Ok(InvokeOutcome::HostCallFault(index)),
210			(STATUS_PANIC, _) => Ok(InvokeOutcome::Panic),
211			(STATUS_OOG, _) => Ok(InvokeOutcome::OutOfGas),
212			(code, _) => Err(code.into()),
213		}
214	}
215}