pallet_evm_test_vector_support/
lib.rs1#![warn(unused_crate_dependencies)]
19
20use std::fs;
21
22use evm::{Context, ExitError, ExitReason, ExitSucceed, Transfer};
23use fp_evm::{Precompile, PrecompileFailure, PrecompileHandle};
24use sp_core::{H160, H256};
25
26#[derive(Debug, serde::Deserialize)]
27#[serde(rename_all = "PascalCase")]
28struct EthConsensusTest {
29 input: String,
30 expected: String,
31 name: String,
32 gas: Option<u64>,
33}
34
35#[derive(Debug, serde::Deserialize)]
36#[serde(rename_all = "PascalCase")]
37struct EthConsensusFailureTest {
38 input: String,
39 expected_error: String,
40 name: String,
41}
42
43pub struct MockHandle {
44 pub input: Vec<u8>,
45 pub gas_limit: Option<u64>,
46 pub context: Context,
47 pub is_static: bool,
48 pub gas_used: u64,
49}
50
51impl MockHandle {
52 pub fn new(input: Vec<u8>, gas_limit: Option<u64>, context: Context) -> Self {
53 Self {
54 input,
55 gas_limit,
56 context,
57 is_static: false,
58 gas_used: 0,
59 }
60 }
61}
62
63impl PrecompileHandle for MockHandle {
64 fn call(
67 &mut self,
68 _: H160,
69 _: Option<Transfer>,
70 _: Vec<u8>,
71 _: Option<u64>,
72 _: bool,
73 _: &Context,
74 ) -> (ExitReason, Vec<u8>) {
75 unimplemented!()
76 }
77
78 fn record_cost(&mut self, cost: u64) -> Result<(), ExitError> {
79 self.gas_used += cost;
80 Ok(())
81 }
82
83 fn record_external_cost(
84 &mut self,
85 _: Option<u64>,
86 _: Option<u64>,
87 _: Option<u64>,
88 ) -> Result<(), ExitError> {
89 Ok(())
90 }
91
92 fn refund_external_cost(&mut self, _: Option<u64>, _: Option<u64>) {}
93
94 fn log(&mut self, _: H160, _: Vec<H256>, _: Vec<u8>) -> Result<(), ExitError> {
95 unimplemented!()
96 }
97
98 fn remaining_gas(&self) -> u64 {
99 unimplemented!()
100 }
101
102 fn code_address(&self) -> H160 {
103 unimplemented!()
104 }
105
106 fn input(&self) -> &[u8] {
107 &self.input
108 }
109
110 fn context(&self) -> &Context {
111 &self.context
112 }
113
114 fn origin(&self) -> H160 {
115 unimplemented!()
116 }
117
118 fn is_static(&self) -> bool {
119 self.is_static
120 }
121
122 fn gas_limit(&self) -> Option<u64> {
123 self.gas_limit
124 }
125
126 fn is_contract_being_constructed(&self, _address: H160) -> bool {
127 unimplemented!()
128 }
129}
130
131pub fn test_precompile_test_vectors<P: Precompile>(filepath: &str) -> Result<(), String> {
135 let data = fs::read_to_string(filepath).unwrap_or_else(|_| panic!("Failed to read {filepath}"));
136
137 let tests: Vec<EthConsensusTest> = serde_json::from_str(&data).expect("expected json array");
138
139 for test in tests {
140 let input: Vec<u8> = hex::decode(test.input).expect("Could not hex-decode test input data");
141
142 let cost: u64 = 10000000;
143
144 let context: Context = Context {
145 address: Default::default(),
146 caller: Default::default(),
147 apparent_value: From::from(0),
148 };
149
150 let mut handle = MockHandle::new(input, Some(cost), context);
151
152 match P::execute(&mut handle) {
153 Ok(result) => {
154 let as_hex: String = hex::encode(result.output);
155 assert_eq!(
156 result.exit_status,
157 ExitSucceed::Returned,
158 "test '{}' returned {:?} (expected 'Returned')",
159 test.name,
160 result.exit_status
161 );
162 assert_eq!(
163 as_hex, test.expected,
164 "test '{}' failed (different output)",
165 test.name
166 );
167 if let Some(expected_gas) = test.gas {
168 assert_eq!(
169 handle.gas_used, expected_gas,
170 "test '{}' failed (different gas cost)",
171 test.name
172 );
173 }
174 }
175 Err(err) => {
176 return Err(format!("Test '{}' returned error: {:?}", test.name, err));
177 }
178 }
179 }
180
181 Ok(())
182}
183
184pub fn test_precompile_failure_test_vectors<P: Precompile>(filepath: &str) -> Result<(), String> {
185 let data = fs::read_to_string(filepath).unwrap_or_else(|_| panic!("Failed to read {filepath}"));
186
187 let tests: Vec<EthConsensusFailureTest> =
188 serde_json::from_str(&data).expect("expected json array");
189
190 for test in tests {
191 let input: Vec<u8> = hex::decode(test.input).expect("Could not hex-decode test input data");
192
193 let cost: u64 = 10000000;
194
195 let context: Context = Context {
196 address: Default::default(),
197 caller: Default::default(),
198 apparent_value: From::from(0),
199 };
200
201 let mut handle = MockHandle::new(input, Some(cost), context);
202
203 match P::execute(&mut handle) {
204 Ok(..) => {
205 unreachable!("Test should be failed");
206 }
207 Err(err) => {
208 let expected_err = PrecompileFailure::Error {
209 exit_status: ExitError::Other(test.expected_error.into()),
210 };
211 assert_eq!(
212 expected_err, err,
213 "Test '{}' failed (different error)",
214 test.name
215 );
216 }
217 }
218 }
219
220 Ok(())
221}