neo3/neo_builder/transaction/
transaction_attribute.rs1use 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 pub fn get_height(&self) -> Option<&u32> {
101 match self {
102 TransactionAttribute::NotValidBefore { height } => Some(height),
103 _ => None,
104 }
105 }
106
107 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, TransactionAttribute::Conflicts { hash: _ } => 1 + 32, }
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}