neo3/neo_builder/transaction/witness_rule/
witness_rule.rs1use 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}