precompile_utils/testing/
modifier.rs

1// This file is part of Frontier.
2
3// Copyright (c) Moonsong Labs.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: Apache-2.0
6
7// Licensed under the Apache License, Version 2.0 (the "License");
8// you may not use this file except in compliance with the License.
9// You may obtain a copy of the License at
10//
11// 	http://www.apache.org/licenses/LICENSE-2.0
12//
13// Unless required by applicable law or agreed to in writing, software
14// distributed under the License is distributed on an "AS IS" BASIS,
15// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16// See the License for the specific language governing permissions and
17// limitations under the License.
18
19use crate::{
20	solidity::codec::Writer,
21	testing::{decode_revert_message, MockHandle},
22};
23use fp_evm::{Context, PrecompileFailure, PrecompileSet};
24use sp_core::{H160, U256};
25
26pub struct PrecompilesModifierTester<P> {
27	precompiles: P,
28	handle: MockHandle,
29}
30
31impl<P: PrecompileSet> PrecompilesModifierTester<P> {
32	pub fn new(precompiles: P, from: impl Into<H160>, to: impl Into<H160>) -> Self {
33		let to = to.into();
34		let mut handle = MockHandle::new(
35			to,
36			Context {
37				address: to,
38				caller: from.into(),
39				apparent_value: U256::zero(),
40			},
41		);
42
43		handle.gas_limit = u64::MAX;
44
45		Self {
46			precompiles,
47			handle,
48		}
49	}
50
51	fn is_view(&mut self, selector: u32) -> bool {
52		// View: calling with static should not revert with static-related message.
53		let handle = &mut self.handle;
54		handle.is_static = true;
55		handle.context.apparent_value = U256::zero();
56		handle.input = Writer::new_with_selector(selector).build();
57
58		let res = self.precompiles.execute(handle);
59
60		match res {
61			Some(Err(PrecompileFailure::Revert { output, .. })) => {
62				let decoded = decode_revert_message(&output);
63
64				dbg!(decoded) != b"Can't call non-static function in static context"
65			}
66			Some(_) => true,
67			None => panic!("tried to check view modifier on unknown precompile"),
68		}
69	}
70
71	fn is_payable(&mut self, selector: u32) -> bool {
72		// Payable: calling with value should not revert with payable-related message.
73		let handle = &mut self.handle;
74		handle.is_static = false;
75		handle.context.apparent_value = U256::one();
76		handle.input = Writer::new_with_selector(selector).build();
77
78		let res = self.precompiles.execute(handle);
79
80		match res {
81			Some(Err(PrecompileFailure::Revert { output, .. })) => {
82				let decoded = decode_revert_message(&output);
83
84				decoded != b"Function is not payable"
85			}
86			Some(_) => true,
87			None => panic!("tried to check payable modifier on unknown precompile"),
88		}
89	}
90
91	pub fn test_view_modifier(&mut self, selectors: &[u32]) {
92		for &s in selectors {
93			assert!(
94				self.is_view(s),
95				"Function doesn't behave like a view function."
96			);
97			assert!(
98				!self.is_payable(s),
99				"Function doesn't behave like a non-payable function."
100			)
101		}
102	}
103
104	pub fn test_payable_modifier(&mut self, selectors: &[u32]) {
105		for &s in selectors {
106			assert!(
107				!self.is_view(s),
108				"Function doesn't behave like a non-view function."
109			);
110			assert!(
111				self.is_payable(s),
112				"Function doesn't behave like a payable function."
113			);
114		}
115	}
116
117	pub fn test_default_modifier(&mut self, selectors: &[u32]) {
118		for &s in selectors {
119			assert!(
120				!self.is_view(s),
121				"Function doesn't behave like a non-view function."
122			);
123			assert!(
124				!self.is_payable(s),
125				"Function doesn't behave like a non-payable function."
126			);
127		}
128	}
129}