jam_pvm_common/
result.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
use jam_types::{InvokeOutcomeCode, SimpleResult, SimpleResultCode, LOWEST_ERROR};

/// Error type for host-calls.
#[derive(Debug)]
pub enum ApiError {
	/// `OOB` Invalid memory access.
	OutOfBounds,
	/// `WHO` Target service is unknown.
	IndexUnknown,
	/// `FULL` Too much storage is used by the service for its holdings.
	StorageFull,
	/// `CORE` Bad core index given.
	BadCore,
	/// `CASH` The caller has too little funding.
	NoCash,
	/// `LOW` The gas limit provided is too low (lower than the amount of gas required for the
	/// transfer).
	GasLimitTooLow,
	/// `HIGH` The gas limit provided is too high (higher than the amount of gas remaining).
	GasLimitTooHigh,
	/// `HUH` The hash is already solicited or forgotten.
	ActionInvalid,
}

impl From<u64> for ApiError {
	fn from(code: SimpleResult) -> Self {
		match code {
			c if c == SimpleResultCode::OutOfBounds as u64 => ApiError::OutOfBounds,
			c if c == SimpleResultCode::IndexUnknown as u64 => ApiError::IndexUnknown,
			c if c == SimpleResultCode::StorageFull as u64 => ApiError::StorageFull,
			c if c == SimpleResultCode::BadCore as u64 => ApiError::BadCore,
			c if c == SimpleResultCode::NoCash as u64 => ApiError::NoCash,
			c if c == SimpleResultCode::GasLimitTooLow as u64 => ApiError::GasLimitTooLow,
			c if c == SimpleResultCode::GasLimitTooHigh as u64 => ApiError::GasLimitTooHigh,
			c if c == SimpleResultCode::ActionInvalid as u64 => ApiError::ActionInvalid,
			_ => panic!("unknown error code: {}", code),
		}
	}
}

/// Result type for host-calls, parameterized by the type of the successful result.
pub type ApiResult<T> = Result<T, ApiError>;

/// Simple conversion trait for types which can be converted to a regular host-call API result.
pub trait IntoApiResult<T> {
	fn into_api_result(self) -> ApiResult<T>;
}

impl IntoApiResult<()> for SimpleResult {
	fn into_api_result(self) -> ApiResult<()> {
		if self == SimpleResultCode::Ok as u64 {
			Ok(())
		} else {
			Err(self.into())
		}
	}
}
impl IntoApiResult<u64> for SimpleResult {
	fn into_api_result(self) -> ApiResult<u64> {
		if self < LOWEST_ERROR {
			Ok(self)
		} else {
			Err(self.into())
		}
	}
}
impl IntoApiResult<u32> for SimpleResult {
	fn into_api_result(self) -> ApiResult<u32> {
		if self <= u32::MAX as _ {
			Ok(self as u32)
		} else if self < LOWEST_ERROR {
			panic!("Our own API impl has resulted in success value which is out of range.");
		} else {
			Err(self.into())
		}
	}
}
impl IntoApiResult<Option<()>> for SimpleResult {
	fn into_api_result(self) -> ApiResult<Option<()>> {
		if self < LOWEST_ERROR {
			Ok(Some(()))
		} else if self == SimpleResultCode::Nothing as u64 {
			Ok(None)
		} else {
			Err(self.into())
		}
	}
}
impl IntoApiResult<Option<u64>> for SimpleResult {
	fn into_api_result(self) -> ApiResult<Option<u64>> {
		if self < LOWEST_ERROR {
			Ok(Some(self))
		} else if self == SimpleResultCode::Nothing as u64 {
			Ok(None)
		} else {
			Err(self.into())
		}
	}
}

/// The successful result of inner PVM invocations.
pub enum InvokeOutcome {
	/// `HALT` Completed normally.
	Halt,
	/// `FAULT` Completed with a page fault.
	PageFault(u64),
	/// `HOST` Completed with a host-call fault.
	HostCallFault(u64),
	/// `PANIC` Completed with a panic.
	Panic,
	/// `OOG` Completed by running out of gas.
	OutOfGas,
}

/// The result of the [crate::refine::invoke] host-call.
pub type InvokeResult = ApiResult<InvokeOutcome>;

/// Simple trait to convert to `InvokeResult`.
pub trait IntoInvokeResult {
	/// Convert `self` to `InvokeResult`.
	fn into_invoke_result(self) -> InvokeResult;
}

impl IntoInvokeResult for (u64, u64) {
	fn into_invoke_result(self) -> InvokeResult {
		const STATUS_HALT: u64 = InvokeOutcomeCode::Halt as u64;
		const STATUS_PANIC: u64 = InvokeOutcomeCode::Panic as u64;
		const STATUS_FAULT: u64 = InvokeOutcomeCode::PageFault as u64;
		const STATUS_HOST: u64 = InvokeOutcomeCode::HostCallFault as u64;
		const STATUS_OOG: u64 = InvokeOutcomeCode::OutOfGas as u64;
		// Convert `invoke` return value to `Result`.
		match self {
			(STATUS_HALT, _) => Ok(InvokeOutcome::Halt),
			(STATUS_FAULT, address) => Ok(InvokeOutcome::PageFault(address)),
			(STATUS_HOST, index) => Ok(InvokeOutcome::HostCallFault(index)),
			(STATUS_PANIC, _) => Ok(InvokeOutcome::Panic),
			(STATUS_OOG, _) => Ok(InvokeOutcome::OutOfGas),
			(code, _) => Err(code.into()),
		}
	}
}