1
// Copyright (C) Parity Technologies (UK) Ltd.
2
// This file is part of Polkadot.
3

            
4
// Substrate is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Substrate is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16

            
17
//! XCM matcher API, used primarily for writing barrier conditions.
18

            
19
use core::ops::ControlFlow;
20
use frame_support::traits::ProcessMessageError;
21
use xcm::latest::{Instruction, Location};
22

            
23
/// Creates an instruction matcher from an XCM. Since XCM versions differ, we need to make a trait
24
/// here to unify the interfaces among them.
25
pub trait CreateMatcher {
26
	/// The concrete matcher type.
27
	type Matcher;
28

            
29
	/// Method that creates and returns the matcher type from `Self`.
30
	fn matcher(self) -> Self::Matcher;
31
}
32

            
33
impl<'a, Call> CreateMatcher for &'a mut [Instruction<Call>] {
34
	type Matcher = Matcher<'a, Call>;
35

            
36
	fn matcher(self) -> Self::Matcher {
37
		let total_inst = self.len();
38

            
39
		Matcher { xcm: self, current_idx: 0, total_inst }
40
	}
41
}
42

            
43
/// API that allows to pattern-match against anything that is contained within an XCM.
44
///
45
/// The intended usage of the matcher API is to enable the ability to chain successive methods of
46
/// this trait together, along with the ? operator for the purpose of facilitating the writing,
47
/// maintenance and auditability of XCM barriers.
48
///
49
/// Example:
50
/// ```rust
51
/// use frame_support::traits::ProcessMessageError;
52
/// use xcm::latest::Instruction;
53
/// use staging_xcm_builder::{CreateMatcher, MatchXcm};
54
///
55
/// let mut msg = [Instruction::<()>::ClearOrigin];
56
/// let res = msg
57
/// 	.matcher()
58
/// 	.assert_remaining_insts(1)?
59
/// 	.match_next_inst(|inst| match inst {
60
/// 		Instruction::<()>::ClearOrigin => Ok(()),
61
/// 		_ => Err(ProcessMessageError::BadFormat),
62
/// 	});
63
/// assert!(res.is_ok());
64
///
65
/// Ok::<(), ProcessMessageError>(())
66
/// ```
67
pub trait MatchXcm {
68
	/// The concrete instruction type. Necessary to specify as it changes between XCM versions.
69
	type Inst;
70
	/// The `Location` type. Necessary to specify as it changes between XCM versions.
71
	type Loc;
72
	/// The error type to throw when errors happen during matching.
73
	type Error;
74

            
75
	/// Returns success if the number of instructions that still have not been iterated over
76
	/// equals `n`, otherwise returns an error.
77
	fn assert_remaining_insts(self, n: usize) -> Result<Self, Self::Error>
78
	where
79
		Self: Sized;
80

            
81
	/// Accepts a closure `f` that contains an argument signifying the next instruction to be
82
	/// iterated over. The closure can then be used to check whether the instruction matches a
83
	/// given condition, and can also be used to mutate the fields of an instruction.
84
	///
85
	/// The closure `f` returns success when the instruction passes the condition, otherwise it
86
	/// returns an error, which will ultimately be returned by this function.
87
	fn match_next_inst<F>(self, f: F) -> Result<Self, Self::Error>
88
	where
89
		Self: Sized,
90
		F: FnMut(&mut Self::Inst) -> Result<(), Self::Error>;
91

            
92
	/// Attempts to continuously iterate through the instructions while applying `f` to each of
93
	/// them, until either the last instruction or `cond` returns false.
94
	///
95
	/// If `f` returns an error, then iteration halts and the function returns that error.
96
	/// Otherwise, `f` returns a `ControlFlow` which signifies whether the iteration breaks or
97
	/// continues.
98
	fn match_next_inst_while<C, F>(self, cond: C, f: F) -> Result<Self, Self::Error>
99
	where
100
		Self: Sized,
101
		C: Fn(&Self::Inst) -> bool,
102
		F: FnMut(&mut Self::Inst) -> Result<ControlFlow<()>, Self::Error>;
103

            
104
	/// Iterate instructions forward until `cond` returns false. When there are no more instructions
105
	/// to be read, an error is returned.
106
	fn skip_inst_while<C>(self, cond: C) -> Result<Self, Self::Error>
107
	where
108
		Self: Sized,
109
		C: Fn(&Self::Inst) -> bool,
110
	{
111
		Self::match_next_inst_while(self, cond, |_| Ok(ControlFlow::Continue(())))
112
	}
113
}
114

            
115
/// Struct created from calling `fn matcher()` on a mutable slice of `Instruction`s.
116
///
117
/// Implements `MatchXcm` to allow an iterator-like API to match against each `Instruction`
118
/// contained within the slice, which facilitates the building of XCM barriers.
119
pub struct Matcher<'a, Call> {
120
	pub(crate) xcm: &'a mut [Instruction<Call>],
121
	pub(crate) current_idx: usize,
122
	pub(crate) total_inst: usize,
123
}
124

            
125
impl<'a, Call> MatchXcm for Matcher<'a, Call> {
126
	type Error = ProcessMessageError;
127
	type Inst = Instruction<Call>;
128
	type Loc = Location;
129

            
130
	fn assert_remaining_insts(self, n: usize) -> Result<Self, Self::Error>
131
	where
132
		Self: Sized,
133
	{
134
		if self.total_inst - self.current_idx != n {
135
			return Err(ProcessMessageError::BadFormat)
136
		}
137

            
138
		Ok(self)
139
	}
140

            
141
	fn match_next_inst<F>(mut self, mut f: F) -> Result<Self, Self::Error>
142
	where
143
		Self: Sized,
144
		F: FnMut(&mut Self::Inst) -> Result<(), Self::Error>,
145
	{
146
		if self.current_idx < self.total_inst {
147
			f(&mut self.xcm[self.current_idx])?;
148
			self.current_idx += 1;
149
			Ok(self)
150
		} else {
151
			Err(ProcessMessageError::BadFormat)
152
		}
153
	}
154

            
155
	fn match_next_inst_while<C, F>(mut self, cond: C, mut f: F) -> Result<Self, Self::Error>
156
	where
157
		Self: Sized,
158
		C: Fn(&Self::Inst) -> bool,
159
		F: FnMut(&mut Self::Inst) -> Result<ControlFlow<()>, Self::Error>,
160
	{
161
		if self.current_idx >= self.total_inst {
162
			return Err(ProcessMessageError::BadFormat)
163
		}
164

            
165
		while self.current_idx < self.total_inst && cond(&self.xcm[self.current_idx]) {
166
			if let ControlFlow::Break(()) = f(&mut self.xcm[self.current_idx])? {
167
				break
168
			}
169
			self.current_idx += 1;
170
		}
171

            
172
		Ok(self)
173
	}
174
}
175

            
176
#[cfg(test)]
177
mod tests {
178
	use super::*;
179
	use std::{vec, vec::Vec};
180
	use xcm::latest::prelude::*;
181

            
182
	#[test]
183
	fn match_next_inst_while_works() {
184
		let mut xcm: Vec<Instruction<()>> = vec![ClearOrigin];
185

            
186
		let _ = xcm
187
			.matcher()
188
			.match_next_inst_while(|_| true, |_| Ok(ControlFlow::Continue(())))
189
			.unwrap();
190
	}
191
}