xpx_chain_sdk/models/transaction/
transaction.rs1use sha3::{Digest, Sha3_256};
8
9use ::std::fmt;
10use std::any::Any;
11use serde_json::Value;
12use crate::account::{Account, PublicAccount};
13
14use crate::anyhow::Result;
15use crate::{AsUint64, GenerationHash};
16use crate::helpers::{hex_decode, hex_encode, TransactionHash};
17use crate::models::consts::{HALF_OF_SIGNATURE, SIGNATURE_SIZE, SIGNER_SIZE, SIZE_SIZE};
18use crate::transaction::{DEFAULT_FEE_CALCULATION_STRATEGY, SignedTransaction, TransactionType};
19
20use super::{
21 CommonTransaction, deadline::Deadline, TransactionStatus,
22};
23
24pub type Amount = u64;
25
26pub type Height = u64;
27
28pub type TransactionsStatus = Vec<TransactionStatus>;
29
30pub type Transactions = Vec<Box<dyn Transaction>>;
31
32pub(crate) struct AbsVector<'b> {
33 pub signature_vec: fb::WIPOffset<fb::Vector<'b, u8>>,
34 pub signer_vec: fb::WIPOffset<fb::Vector<'b, u8>>,
35 pub version_vec: fb::UOffsetT,
36 pub type_vec: u16,
37 pub max_fee_vec: fb::WIPOffset<fb::Vector<'b, u32>>,
38 pub deadline_vec: fb::WIPOffset<fb::Vector<'b, u32>>,
39}
40
41impl<'b> AbsVector<'b> {
42 pub fn build_vector(
43 common: &CommonTransaction,
44 size: usize,
45 builder: &mut fb::FlatBufferBuilder<'b>,
46 ) -> Self {
47 let max_fee = match common.max_fee {
48 Some(item) => item,
49 _ => DEFAULT_FEE_CALCULATION_STRATEGY * (size as u64),
50 };
51
52 let deadline = match common.deadline {
53 Some(item) => item,
54 _ => Deadline::default(),
55 };
56
57 let network_type: fb::UOffsetT = common.network_type.value() as u32;
58
59 let version_vec = (network_type << 24) + *common.version as fb::UOffsetT;
60 let signature_vec = builder.create_vector(&[0u8; SIGNATURE_SIZE]);
61 let signer_vec = builder.create_vector(&[0u8; SIGNER_SIZE]);
62 let deadline_vec = builder.create_vector(&deadline.to_dto());
63
64 let max_fee_vec = builder.create_vector(&max_fee.to_dto());
65
66 AbsVector {
67 signature_vec,
68 signer_vec,
69 version_vec,
70 type_vec: common.transaction_type.value(),
71 max_fee_vec,
72 deadline_vec,
73 }
74 }
75}
76
77#[typetag::serde]
80pub trait Transaction
81where
82 Self: fmt::Debug + 'static + Sync + Send,
83{
84 fn size(&self) -> usize;
85
86 fn as_value(&self) -> Value;
88
89 fn get_common_transaction(&self) -> CommonTransaction;
91
92 fn get_transaction_type(&self) -> TransactionType {
94 self.get_common_transaction().transaction_type
95 }
96
97 fn get_transaction_hash(&self) -> TransactionHash {
99 self.get_common_transaction().get_hash()
100 }
101
102 fn get_signing_bytes(&self, payload_bytes: &[u8], generation_hash_bytes: &[u8]) -> Vec<u8> {
113 let byte_buffer_without_header = &payload_bytes[SIZE_SIZE + SIGNATURE_SIZE + SIGNER_SIZE..];
114 if self.get_transaction_type() == TransactionType::AggregateBonded
115 || self.get_transaction_type() == TransactionType::AggregateComplete
116 {
117 [generation_hash_bytes[..32].as_ref(), byte_buffer_without_header].concat()
118 } else {
119 [generation_hash_bytes, byte_buffer_without_header].concat()
120 }
121 }
122
123 fn sign_with(
134 &self,
135 account: Account,
136 generation_hash: GenerationHash,
137 ) -> Result<SignedTransaction> {
138 let generation_hash_bytes = generation_hash.to_fixed_bytes();
139 let byte_buffer = self.to_serializer();
140
141 let signing_bytes =
142 self.get_signing_bytes(byte_buffer.as_ref(), generation_hash_bytes.as_ref());
143
144 let signature = account.key_pair.sign(signing_bytes.as_ref());
145
146 let mut signed_transaction_buffer = Vec::with_capacity(byte_buffer.len());
147 signed_transaction_buffer.extend_from_slice(byte_buffer[..4].as_ref());
148 signed_transaction_buffer.extend_from_slice(&signature.to_bytes());
149 signed_transaction_buffer.extend_from_slice(account.public_account.public_key.as_ref());
150 signed_transaction_buffer.extend_from_slice(
151 byte_buffer[SIZE_SIZE + SIGNATURE_SIZE + SIGNER_SIZE..byte_buffer.len()]
152 .as_ref(),
153 );
154
155 let payload = hex_encode(signed_transaction_buffer.as_ref());
156
157
158 let transaction_hash =
159 <dyn Transaction>::create_transaction_hash(&payload, &generation_hash_bytes);
160
161 Ok(SignedTransaction {
162 payload: payload.to_uppercase(),
163 hash: transaction_hash,
164 signer: account.public_key_to_hex(),
165 entity_type: self.get_transaction_type(),
166 network_type: self.get_common_transaction().network_type.value(),
167 })
168 }
169
170 fn to_serializer(&self) -> Vec<u8>;
172
173 fn set_aggregate(&mut self, signer: PublicAccount) {
174 self.get_common_transaction().set_aggregate(signer)
175 }
176
177 fn is_unconfirmed(&self) -> bool {
179 self.get_common_transaction().is_unconfirmed()
180 }
181
182 fn is_confirmed(&self) -> bool {
184 self.get_common_transaction().is_confirmed()
185 }
186
187 fn has_missing_signatures(&self) -> bool {
189 self.get_common_transaction().has_missing_signatures()
190 }
191
192 fn is_unannounced(&self) -> bool {
194 self.get_common_transaction().is_unannounced()
195 }
196
197 fn as_any(&self) -> &dyn Any;
198
199 fn into_any(self: Box<Self>) -> Box<dyn Any>;
200
201 fn box_clone(&self) -> Box<dyn Transaction>;
202}
203
204impl dyn Transaction {
205 const HEADER_SIZE: usize = 4 + 32 + 64;
210
211 const TYPE_INDEX: usize = Self::HEADER_SIZE + 2;
215
216 const BODY_INDEX: usize = Self::HEADER_SIZE + 4 + 2 + 8 + 8;
220
221 fn create_transaction_hash(
232 transaction_payload: &str,
233 generation_hash: &[u8],
234 ) -> TransactionHash {
235 let mut transaction_hash = TransactionHash::zero();
236
237 let transaction_bytes = hex_decode(transaction_payload);
238
239 static TYPE_IDX: usize = <dyn Transaction>::TYPE_INDEX;
241
242 let mut sb = vec![];
243
244 sb.extend_from_slice(&transaction_bytes[SIZE_SIZE..SIZE_SIZE + HALF_OF_SIGNATURE]);
245
246 sb.extend_from_slice(
247 &transaction_bytes
248 [SIZE_SIZE + SIGNATURE_SIZE..SIZE_SIZE + SIGNATURE_SIZE + SIGNER_SIZE],
249 );
250
251 sb.extend_from_slice(generation_hash);
252
253 sb.extend_from_slice(&transaction_bytes[100..]);
254
255 let sha3_hash = Sha3_256::digest(sb.as_slice());
256
257 transaction_hash.assign_from_slice(&sha3_hash.as_slice()[0..32]);
258 transaction_hash
259 }
260
261 pub fn downcast_ref<T: Transaction>(&self) -> &T {
269 self.try_downcast_ref::<T>().unwrap_or_else(|| {
270 panic!(
271 "downcast to wrong Transaction type; original transaction type: {}",
272 self.get_transaction_type()
273 )
274 })
275 }
276
277 #[inline]
279 pub fn try_downcast_ref<T: Transaction>(&self) -> Option<&T> {
280 self.as_any().downcast_ref::<T>()
281 }
282
283 pub fn downcast<T: Transaction>(self: Box<Self>) -> Box<T> {
291 self.try_downcast().unwrap_or_else(|err| panic!("{}", err))
292 }
293
294 #[inline]
296 pub fn try_downcast<T: Transaction>(self: Box<Self>) -> Result<Box<T>> {
297 if self.as_ref().as_any().is::<T>() {
298 Ok(self.into_any().downcast().unwrap())
299 } else {
300 Err(anyhow!(
301 "downcast to wrong Transaction type; original transaction type: {}",
302 self.get_transaction_type()
303 ))
304 }
305 }
306}
307
308impl Clone for Box<dyn Transaction + 'static> {
310 fn clone(&self) -> Box<dyn Transaction + 'static> {
311 self.box_clone()
312 }
313}
314
315impl<'a> PartialEq for &'a dyn Transaction {
316 fn eq(&self, other: &Self) -> bool {
317 self.to_serializer() == other.to_serializer()
318 }
319}
320
321impl fmt::Display for dyn Transaction {
322 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
323 write!(f, "{}", serde_json::to_string_pretty(&self).unwrap_or_default())
324 }
325}