neo3/neo_builder/transaction/witness_rule/
witness_rule.rs

1use serde::{Deserialize, Serialize};
2
3use crate::{
4	builder::{TransactionError, WitnessAction, WitnessCondition},
5	codec::{Decoder, Encoder, NeoSerializable},
6};
7
8#[derive(Serialize, Deserialize, Debug, Hash, PartialEq, Clone)]
9pub struct WitnessRule {
10	#[serde(rename = "action")]
11	pub action: WitnessAction,
12	#[serde(rename = "condition")]
13	pub condition: WitnessCondition,
14}
15
16impl WitnessRule {
17	pub fn new(action: WitnessAction, condition: WitnessCondition) -> Self {
18		Self { action, condition }
19	}
20}
21
22impl NeoSerializable for WitnessRule {
23	type Error = TransactionError;
24
25	fn size(&self) -> usize {
26		1 + self.condition.size()
27	}
28
29	fn encode(&self, writer: &mut Encoder) {
30		writer.write_u8(self.action as u8);
31		writer.write_serializable_fixed(&self.condition);
32	}
33
34	fn decode(reader: &mut Decoder) -> Result<Self, Self::Error> {
35		let action = reader.read_u8();
36		let condition = WitnessCondition::decode(reader)?;
37		Ok(Self { action: WitnessAction::try_from(action).unwrap(), condition })
38	}
39	fn to_array(&self) -> Vec<u8> {
40		let mut writer = Encoder::new();
41		self.encode(&mut writer);
42		writer.to_bytes()
43	}
44}
45
46#[cfg(test)]
47mod tests {
48	use primitive_types::H160;
49
50	use crate::{
51		builder::{WitnessCondition, WitnessRule},
52		codec::{Encoder, NeoSerializable},
53		config::TestConstants,
54		crypto::Secp256r1PublicKey,
55		neo_types::ScriptHashExtension,
56	};
57
58	#[test]
59	fn test_decode_boolean_condition() {
60		let json = r#"{"action": "Allow","condition": {"type": "Boolean","expression": "false"}}"#;
61		let rule: WitnessRule = serde_json::from_str(json).unwrap();
62		assert!(matches!(rule.condition, WitnessCondition::Boolean(_)));
63		assert!(!rule.condition.boolean_expression().unwrap());
64	}
65
66	#[test]
67	fn test_script_hash_condition_serialize_deserialize() {
68		let hash = TestConstants::DEFAULT_ACCOUNT_SCRIPT_HASH;
69		let condition = WitnessCondition::ScriptHash(H160::from_hex(hash).unwrap());
70
71		let bytes = hex::decode(format!("18{}", hash)).unwrap();
72
73		let deserialized = WitnessCondition::from_bytes(&bytes).unwrap();
74		assert_eq!(condition, deserialized);
75
76		let mut writer = Encoder::new();
77		condition.encode(&mut writer);
78		assert_eq!(bytes, writer.to_bytes());
79	}
80
81	#[test]
82	fn test_decode_not_condition() {
83		let json = r#"{
84        "action": "Allow",
85        "condition": {
86            "type": "Not",
87            "expression": {
88                "type": "Not",
89                "expression": {
90                    "type": "CalledByEntry"
91                }
92            }
93        }
94    }"#;
95
96		let rule: WitnessRule = serde_json::from_str(json).unwrap();
97
98		assert!(matches!(
99			rule.condition,
100			WitnessCondition::Not(boxed) if matches!(*boxed, WitnessCondition::Not(_))
101		));
102	}
103
104	#[test]
105	fn test_and_condition_serialize_deserialize() {
106		let condition = WitnessCondition::And(vec![
107			WitnessCondition::Boolean(true),
108			WitnessCondition::Boolean(false),
109		]);
110
111		let bytes = hex::decode("020200010000").unwrap();
112
113		let deserialized = WitnessCondition::from_bytes(&bytes).unwrap();
114		assert_eq!(condition, deserialized);
115
116		let mut writer = Encoder::new();
117		condition.encode(&mut writer);
118
119		assert_eq!(bytes, writer.to_bytes());
120	}
121
122	#[test]
123	fn test_boolean_nil_values() {
124		let json = r#"{
125        "action": "Deny",
126        "condition": {
127            "type": "CalledByGroup",
128            "group": "035a1ced7ae274a881c3f479452c8bca774c89f653d54c5c5959a01371a8c696fd"
129        }
130    }"#;
131
132		let rule: WitnessRule = serde_json::from_str(json).unwrap();
133
134		assert!(rule.condition.boolean_expression().is_none());
135		assert!(rule.condition.expression().is_none());
136	}
137
138	#[test]
139	fn test_decode_or_condition() {
140		let json = r#"{
141        "action": "Deny",
142        "condition": {
143            "type": "Or",
144            "expressions": [
145                {"type": "Group", "group": "023be7b6742268f4faca4835718f3232ddc976855d5ef273524cea36f0e8d102f3"},
146                {"type": "CalledByEntry"}
147            ]
148        }
149    }"#;
150
151		let rule: WitnessRule = serde_json::from_str(json).unwrap();
152
153		assert!(matches!(
154			rule.condition,
155			WitnessCondition::Or(conditions) if conditions.len() == 2
156		));
157	}
158
159	#[test]
160	fn test_called_by_group_condition_serialize_deserialize() {
161		let key: &str = TestConstants::DEFAULT_ACCOUNT_PUBLIC_KEY;
162		let condition =
163			WitnessCondition::CalledByGroup(Secp256r1PublicKey::from_encoded(key).unwrap());
164
165		let bytes = hex::decode(format!("29{}", key)).unwrap();
166
167		let deserialized = WitnessCondition::from_bytes(&bytes).unwrap();
168		assert_eq!(condition, deserialized);
169
170		let mut writer = Encoder::new();
171		condition.encode(&mut writer);
172
173		assert_eq!(bytes, writer.to_bytes());
174	}
175
176	#[test]
177	fn test_called_by_entry_serialize_deserialize() {
178		let condition = WitnessCondition::CalledByEntry;
179
180		let bytes = hex::decode("20").unwrap();
181
182		let deserialized = WitnessCondition::from_bytes(&bytes).unwrap();
183		assert_eq!(condition, deserialized);
184
185		let mut writer = Encoder::new();
186		condition.encode(&mut writer);
187
188		assert_eq!(bytes, writer.to_bytes());
189	}
190
191	#[test]
192	fn test_called_by_contract_serialize_deserialize() {
193		let hash = TestConstants::DEFAULT_ACCOUNT_SCRIPT_HASH;
194		let condition = WitnessCondition::CalledByContract(H160::from_hex(&hash).unwrap());
195
196		let bytes = hex::decode(format!("28{}", hash)).unwrap();
197
198		let deserialized = WitnessCondition::from_bytes(&bytes).unwrap();
199		assert_eq!(condition, deserialized);
200
201		let mut writer = Encoder::new();
202		condition.encode(&mut writer);
203
204		assert_eq!(bytes, writer.to_bytes());
205	}
206
207	#[test]
208	fn test_decode_script_hash_condition() {
209		let json = r#"{
210        "action": "Allow",
211        "condition": {
212            "type": "ScriptHash",
213            "hash": "ef4073a0f2b305a38ec4050e4d3d28bc40ea63f5"
214        }
215    }"#;
216
217		let rule: WitnessRule = serde_json::from_str(json).unwrap();
218
219		assert!(matches!(rule.condition, WitnessCondition::ScriptHash(_)));
220	}
221
222	#[test]
223	fn test_decode_group_condition() {
224		let json = r#"{
225        "action": "Allow",
226        "condition": {
227            "type": "Group",
228            "group": "0352321377ac7b4e1c4c2ebfe28f4d82fa3c213f7ccfcc9dac62da37fb9b433f0c"
229        }
230    }"#;
231
232		let rule: WitnessRule = serde_json::from_str(json).unwrap();
233
234		assert!(matches!(rule.condition, WitnessCondition::Group(_),));
235	}
236
237	#[test]
238	fn test_decode_called_by_entry_condition() {
239		let json = r#"{
240        "action": "Deny",
241        "condition": {
242            "type": "CalledByEntry"
243        }
244    }"#;
245
246		let rule: WitnessRule = serde_json::from_str(json).unwrap();
247
248		assert_eq!(rule.condition, WitnessCondition::CalledByEntry,);
249	}
250
251	#[test]
252	fn test_decode_called_by_contract_condition() {
253		let json = r#"{
254        "action": "Allow",
255        "condition": {
256            "type": "CalledByContract",
257            "hash": "ef4073a0f2b305a38ec4050e4d3d28bc40ea63e4"
258        }
259    }"#;
260
261		let rule: WitnessRule = serde_json::from_str(json).unwrap();
262
263		assert!(matches!(rule.condition, WitnessCondition::CalledByContract(_),));
264	}
265
266	#[test]
267	fn test_and_condition_decode() {
268		let json = r#"{
269        "action": "Allow",
270        "condition": {
271            "type": "And",
272            "expressions": [
273                {"type": "CalledByEntry"},
274                {"type": "Group", "group": "021821807f923a3da004fb73871509d7635bcc05f41edef2a3ca5c941d8bbc1231"},
275                {"type": "Boolean", "expression": "true"}
276            ]
277        }
278    }"#;
279
280		let rule: WitnessRule = serde_json::from_str(json).unwrap();
281
282		assert!(matches!(
283			rule.condition,
284			WitnessCondition::And(expressions) if expressions.len() == 3
285		));
286	}
287
288	#[test]
289	fn test_not_condition_decode() {
290		let json = r#"{
291        "action": "Allow",
292        "condition": {
293            "type": "Not",
294            "expression": {
295                "type": "CalledByEntry"
296            }
297        }
298    }"#;
299
300		let rule: WitnessRule = serde_json::from_str(json).unwrap();
301
302		let bo = Box::new(WitnessCondition::CalledByEntry);
303		assert!(matches!(rule.condition, WitnessCondition::Not(bo)));
304	}
305
306	#[test]
307	fn boolean_expression() {
308		let json = r#"{
309        "condition": {
310            "type": "Boolean",
311            "expression": "false"
312        }
313    }"#;
314
315		let condition = parse_condition(json);
316		assert!(!condition.boolean_expression().unwrap());
317	}
318
319	fn parse_condition(_: &str) -> WitnessCondition {
320		WitnessCondition::Boolean(false)
321	}
322}