neo3/neo_builder/transaction/
transaction_attribute.rs

1use std::{
2	fmt::{Debug, Formatter},
3	hash::{Hash, Hasher},
4};
5
6use crate::neo_crypto::utils::FromBase64String;
7use getset::{Getters, Setters};
8use primitive_types::{H160, H256};
9use serde::{Deserialize, Serialize};
10use serde_json::Value;
11
12use crate::{
13	builder::TransactionError,
14	codec::{Decoder, Encoder, NeoSerializable},
15	neo_types::{Bytes, TypeError},
16	prelude::Base64Encode,
17	var_size,
18};
19
20use super::oracle_response_code::OracleResponseCode;
21
22#[derive(Serialize, Deserialize, PartialEq, Hash, Debug, Clone)]
23#[serde(tag = "type")]
24pub enum TransactionAttribute {
25	#[serde(rename = "HighPriority")]
26	HighPriority,
27
28	#[serde(rename = "OracleResponse")]
29	OracleResponse(OracleResponse),
30
31	#[serde(rename = "NotValidBefore")]
32	NotValidBefore {
33		height: u32,
34	},
35
36	Conflicts {
37		hash: H256,
38	},
39}
40
41#[derive(Serialize, Deserialize, PartialEq, Hash, Debug, Clone)]
42struct OracleResponse {
43	pub(crate) id: u32,
44	pub(crate) response_code: OracleResponseCode,
45	pub(crate) result: String,
46}
47
48impl TransactionAttribute {
49	pub const MAX_RESULT_SIZE: usize = 0xffff;
50
51	pub fn to_bytes(&self) -> Vec<u8> {
52		let mut bytes = vec![];
53
54		match self {
55			TransactionAttribute::HighPriority => {
56				bytes.push(0x01);
57			},
58			TransactionAttribute::OracleResponse(OracleResponse { id, response_code, result }) => {
59				bytes.push(0x11);
60				bytes.extend(&id.to_be_bytes());
61				bytes.push(response_code.clone() as u8);
62				bytes.extend(result.as_bytes());
63			},
64			_ => {},
65		}
66
67		bytes
68	}
69
70	pub fn from_bytes(bytes: &[u8]) -> Result<Self, &'static str> {
71		match bytes[0] {
72			0x01 => Ok(TransactionAttribute::HighPriority),
73			0x11 => {
74				if bytes.len() < 9 {
75					return Err("Not enough bytes for OracleResponse");
76				}
77				let mut array = [0; 8];
78				let slice_len = bytes[1..9].len();
79				array[8 - slice_len..].copy_from_slice(&bytes[1..9]);
80				let id = u64::from_be_bytes(array);
81				let response_code = OracleResponseCode::try_from(bytes[9]).unwrap();
82				let result =
83					String::from_utf8(bytes[10..].to_vec()).map_err(|_| "Invalid UTF-8").unwrap();
84
85				Ok(TransactionAttribute::OracleResponse(OracleResponse {
86					id: id as u32,
87					response_code,
88					result,
89				}))
90			},
91			_ => Err("Invalid attribute type byte"),
92		}
93	}
94
95	pub fn to_json(&self) -> String {
96		serde_json::to_string(self).unwrap()
97	}
98
99	// Get the height for NotValidBefore attribute
100	pub fn get_height(&self) -> Option<&u32> {
101		match self {
102			TransactionAttribute::NotValidBefore { height } => Some(height),
103			_ => None,
104		}
105	}
106
107	// Get the height for NotValidBefore attribute
108	pub fn get_hash(&self) -> Option<&H256> {
109		match self {
110			TransactionAttribute::Conflicts { hash } => Some(hash),
111			_ => None,
112		}
113	}
114}
115
116impl NeoSerializable for TransactionAttribute {
117	type Error = TransactionError;
118
119	fn size(&self) -> usize {
120		match self {
121			TransactionAttribute::HighPriority => 1,
122			TransactionAttribute::OracleResponse(OracleResponse {
123				id: _,
124				response_code: _,
125				result,
126			}) => 1 + 9 + result.len(),
127			TransactionAttribute::NotValidBefore { height: _ } => 1 + 4, // 1 byte type + 4 bytes height
128			TransactionAttribute::Conflicts { hash: _ } => 1 + 32,       // 1 byte type + 32 bytes hash
129		}
130	}
131
132	fn encode(&self, writer: &mut Encoder) {
133		match self {
134			TransactionAttribute::HighPriority => {
135				writer.write_u8(0x01);
136			},
137			TransactionAttribute::OracleResponse(OracleResponse { id, response_code, result }) => {
138				writer.write_u8(0x11);
139				let mut v = id.to_be_bytes();
140				v.reverse();
141				writer.write(&v);
142				writer.write_u8(response_code.clone() as u8);
143				writer.write_var_bytes(result.from_base64_string().unwrap().as_slice());
144			},
145			_ => {},
146		}
147	}
148
149	fn decode(reader: &mut Decoder) -> Result<Self, Self::Error> {
150		match reader.read_u8() {
151			0x01 => Ok(TransactionAttribute::HighPriority),
152			0x11 => {
153				let id = reader.read_u32().map_err(|e| {
154					TransactionError::TransactionConfiguration(format!(
155						"Failed to read oracle response ID: {}",
156						e
157					))
158				})?;
159				let response_code =
160					OracleResponseCode::try_from(reader.read_u8()).map_err(|_| {
161						TransactionError::TransactionConfiguration(
162							"Invalid oracle response code".to_string(),
163						)
164					})?;
165				let result = reader
166					.read_var_bytes()
167					.map_err(|e| {
168						TransactionError::TransactionConfiguration(format!(
169							"Failed to read oracle response result: {}",
170							e
171						))
172					})?
173					.to_base64();
174
175				Ok(TransactionAttribute::OracleResponse(OracleResponse {
176					id,
177					response_code,
178					result,
179				}))
180			},
181			_ => Err(TransactionError::InvalidTransaction),
182		}
183	}
184
185	fn to_array(&self) -> Vec<u8> {
186		let mut writer = Encoder::new();
187		self.encode(&mut writer);
188		writer.to_bytes()
189	}
190}