neo3/neo_builder/transaction/
transaction.rs

1use std::hash::{Hash, Hasher};
2
3use futures_util::TryFutureExt;
4use getset::{CopyGetters, Getters, MutGetters, Setters};
5use primitive_types::U256;
6use serde::{Deserialize, Deserializer, Serialize};
7use serde_json::Value;
8use serde_with::__private__::DeError;
9use tracing::info;
10
11use crate::{
12	builder::{init_logger, Signer, TransactionAttribute, TransactionError, Witness},
13	codec::{Decoder, Encoder, NeoSerializable, VarSizeTrait},
14	config::NeoConstants,
15	crypto::HashableForVec,
16	neo_clients::{APITrait, HttpProvider, JsonRpcProvider, RpcClient},
17	neo_error::NeoError,
18	neo_protocol::{ApplicationLog, RawTransaction},
19	neo_types::NameOrAddress,
20	Bytes,
21};
22
23/// A Neo N3 blockchain transaction.
24///
25/// The `Transaction` struct represents a transaction on the Neo N3 blockchain. It contains
26/// all the necessary information for a transaction, including version, nonce, validity period,
27/// signers, fees, attributes, script, and witnesses.
28///
29/// # Fields
30///
31/// * `network` - An optional reference to an RPC client for network operations.
32/// * `version` - The transaction version.
33/// * `nonce` - A random number to prevent transaction duplication.
34/// * `valid_until_block` - The block height until which the transaction is valid.
35/// * `signers` - A list of transaction signers.
36/// * `size` - The size of the transaction in bytes.
37/// * `sys_fee` - The system fee for the transaction.
38/// * `net_fee` - The network fee for the transaction.
39/// * `attributes` - Transaction attributes.
40/// * `script` - The transaction script.
41/// * `witnesses` - Transaction witnesses (signatures).
42/// * `block_count_when_sent` - The block count when the transaction was sent.
43///
44/// # Examples
45///
46/// ```rust,no_run
47/// use neo3::neo_builder::{Transaction, TransactionBuilder};
48/// use neo3::neo_clients::{HttpProvider, RpcClient};
49///
50/// fn example() -> Result<(), Box<dyn std::error::Error>> {
51///     // Create a new transaction (placeholder)
52///     // let tx = Transaction::new();
53///
54///     // Transactions are typically created using the TransactionBuilder
55///     let provider = HttpProvider::new("https://testnet1.neo.org:443")?;
56///     let client = RpcClient::new(provider);
57///     // let mut tx_builder = TransactionBuilder::with_client(&client);
58///     Ok(())
59/// }
60/// ```
61#[derive(Serialize, Getters, Setters, MutGetters, CopyGetters, Debug, Clone)]
62pub struct Transaction<'a, P: JsonRpcProvider + 'static> {
63	#[serde(skip)]
64	#[getset(get = "pub", set = "pub")]
65	pub network: Option<&'a RpcClient<P>>,
66
67	#[serde(rename = "version")]
68	#[getset(get = "pub", set = "pub")]
69	pub version: u8,
70
71	#[serde(rename = "nonce")]
72	#[getset(get = "pub", set = "pub")]
73	pub nonce: u32,
74
75	#[serde(rename = "validuntilblock")]
76	#[getset(get = "pub", set = "pub")]
77	pub valid_until_block: u32,
78
79	#[serde(rename = "signers")]
80	#[getset(get = "pub", set = "pub")]
81	pub signers: Vec<Signer>,
82
83	#[serde(rename = "size")]
84	#[getset(get = "pub", set = "pub")]
85	pub size: i32,
86
87	#[serde(rename = "sysfee")]
88	#[getset(get = "pub", set = "pub")]
89	pub sys_fee: i64,
90
91	#[serde(rename = "netfee")]
92	#[getset(get = "pub", set = "pub")]
93	pub net_fee: i64,
94
95	#[serde(rename = "attributes")]
96	#[getset(get = "pub", set = "pub")]
97	pub attributes: Vec<TransactionAttribute>,
98
99	#[serde(rename = "script")]
100	#[getset(get = "pub", set = "pub")]
101	pub script: Bytes,
102
103	#[serde(rename = "witnesses")]
104	#[getset(get = "pub", set = "pub")]
105	pub witnesses: Vec<Witness>,
106
107	// #[serde(rename = "blocktime")]
108	// #[getset(get = "pub", set = "pub")]
109	// pub block_time: Option<i32>,
110	#[serde(skip)]
111	pub(crate) block_count_when_sent: Option<u32>,
112}
113
114impl<'a, P: JsonRpcProvider + 'static> Default for Transaction<'a, P> {
115	fn default() -> Self {
116		Transaction {
117			network: None,
118			version: Default::default(),
119			nonce: Default::default(),
120			valid_until_block: Default::default(),
121			signers: Default::default(),
122			size: Default::default(),
123			sys_fee: Default::default(),
124			net_fee: Default::default(),
125			attributes: Default::default(),
126			script: Default::default(),
127			witnesses: Default::default(),
128			// block_time: Default::default(),
129			block_count_when_sent: None,
130		}
131	}
132}
133
134impl<'de, 'a, P: JsonRpcProvider + 'static> Deserialize<'de> for Transaction<'a, P> {
135	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
136	where
137		D: Deserializer<'de>,
138	{
139		let value = Value::deserialize(deserializer)?;
140
141		// Example for version, apply similar logic for other fields
142		let version = value
143			.get("version")
144			.ok_or(DeError::missing_field("version"))?
145			.as_u64()
146			.ok_or(DeError::custom("invalid type for version"))? as u8;
147
148		// Production-ready field deserialization with proper error handling
149		let nonce = value
150			.get("nonce")
151			.and_then(|v| v.as_i64())
152			.ok_or(DeError::custom("Missing or invalid nonce field"))? as u32;
153
154		let valid_until_block = value
155			.get("validuntilblock")
156			.and_then(|v| v.as_i64())
157			.ok_or(DeError::custom("Missing or invalid validuntilblock field"))?
158			as u32;
159		// Continue for other fields...
160
161		// For Vec<T> fields like signers, attributes, witnesses, you might deserialize them like this:
162		// This assumes that Signer, TransactionAttribute, Witness can be deserialized directly from serde_json::Value
163		let signers: Vec<Signer> =
164			serde_json::from_value(value["signers"].clone()).map_err(DeError::custom)?;
165		let attributes: Vec<TransactionAttribute> =
166			serde_json::from_value(value["attributes"].clone()).map_err(DeError::custom)?;
167		let witnesses: Vec<Witness> =
168			serde_json::from_value(value["witnesses"].clone()).map_err(DeError::custom)?;
169
170		// For bytes, assuming it's a Vec<u8> and stored as a base64 string in JSON
171		let script: Bytes = base64::decode(value["script"].as_str().unwrap_or_default())
172			.map_err(DeError::custom)?;
173
174		// For optional fields
175		let block_time = value["blocktime"].as_i64().map(|v| v as i32);
176
177		// Complete field deserialization with comprehensive error handling
178		let size = value
179			.get("size")
180			.and_then(|v| v.as_i64())
181			.ok_or(DeError::custom("Missing or invalid size field"))? as i32;
182
183		let sys_fee = value
184			.get("sysfee")
185			.and_then(|v| v.as_i64())
186			.ok_or(DeError::custom("Missing or invalid sysfee field"))?;
187
188		let net_fee = value
189			.get("netfee")
190			.and_then(|v| v.as_i64())
191			.ok_or(DeError::custom("Missing or invalid netfee field"))?;
192
193		Ok(Transaction {
194			network: None,
195			version,
196			nonce,
197			valid_until_block,
198			signers,
199			size,
200			sys_fee,
201			net_fee,
202			attributes,
203			script,
204			witnesses,
205			block_count_when_sent: None,
206		})
207	}
208}
209
210// impl<P: JsonRpcClient + 'static> DeserializeOwned for Transaction<P> {}
211
212impl<'a, P: JsonRpcProvider + 'static> Hash for Transaction<'a, P> {
213	fn hash<H: Hasher>(&self, state: &mut H) {
214		self.to_array().hash(state);
215	}
216}
217
218impl<'a, T: JsonRpcProvider + 'static> Transaction<'a, T> {
219	const HEADER_SIZE: usize = 25;
220	pub fn new() -> Self {
221		Self::default()
222	}
223
224	/// Convenience function for sending a new payment transaction to the receiver.
225	pub fn pay<K: Into<NameOrAddress>, V: Into<U256>>(_to: K, _value: V) -> Self {
226		Transaction { ..Default::default() }
227	}
228
229	pub fn add_witness(&mut self, witness: Witness) {
230		self.witnesses.push(witness);
231	}
232
233	pub async fn get_hash_data(&self) -> Result<Bytes, TransactionError> {
234		if self.network.is_none() {
235			return Err(TransactionError::TransactionConfiguration(
236				"Transaction network magic is not set".to_string(),
237			));
238		}
239		let mut encoder = Encoder::new();
240		self.serialize_without_witnesses(&mut encoder);
241		let mut data = encoder.to_bytes().hash256();
242		let network_value = self.network.as_ref().unwrap().network().await?;
243		data.splice(0..0, network_value.to_be_bytes());
244
245		Ok(data)
246	}
247
248	fn get_tx_id(&self) -> Result<primitive_types::H256, TransactionError> {
249		let mut encoder = Encoder::new();
250		self.serialize_without_witnesses(&mut encoder);
251		let data = encoder.to_bytes().hash256();
252		let reversed_data = data.iter().rev().cloned().collect::<Vec<u8>>();
253		Ok(primitive_types::H256::from_slice(&reversed_data))
254	}
255
256	fn serialize_without_witnesses(&self, writer: &mut Encoder) {
257		writer.write_u8(self.version);
258		writer.write_u32(self.nonce);
259		writer.write_i64(self.sys_fee);
260		writer.write_i64(self.net_fee);
261		writer.write_u32(self.valid_until_block);
262		writer.write_serializable_variable_list(&self.signers);
263		writer.write_serializable_variable_list(&self.attributes);
264		writer.write_var_bytes(&self.script);
265	}
266
267	/// Sends the transaction to the Neo N3 network.
268	///
269	/// This method validates the transaction, converts it to a hexadecimal string,
270	/// and sends it to the network using the RPC client. It also records the current
271	/// block count for transaction tracking purposes.
272	///
273	/// # Returns
274	///
275	/// A `Result` containing the `RawTransaction` response if successful,
276	/// or a `TransactionError` if an error occurs.
277	///
278	/// # Errors
279	///
280	/// Returns an error if:
281	/// * The number of signers does not match the number of witnesses
282	/// * The transaction exceeds the maximum transaction size
283	/// * The network client encounters an error when sending the transaction
284	///
285	/// # Examples
286	///
287	/// ```rust,no_run
288	/// use neo3::neo_builder::{ScriptBuilder, AccountSigner, TransactionBuilder, CallFlags};
289	/// use neo3::neo_clients::{HttpProvider, RpcClient, APITrait};
290	/// use neo3::neo_protocol::{Account, AccountTrait};
291	/// use neo3::neo_types::{Address, ContractParameter, ScriptHash, AddressExtension};
292	/// use std::str::FromStr;
293	///
294	/// #[tokio::main]
295	/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
296	///     // Connect to Neo N3 TestNet
297	///     let provider = HttpProvider::new("https://testnet1.neo.org:443").unwrap();
298	///     let client = RpcClient::new(provider);
299	///     
300	///     // Load your account from WIF or keystore
301	///     let private_key = "your_private_key_here";
302	///     let account = Account::from_wif(private_key)?;
303	///     
304	///     // Create a token transfer transaction
305	///     let from_address = account.get_address();
306	///     let to_address = "NdUL5oDPD159KeFpD5A9zw5xNF1xLX6nLT";
307	///     let amount = 1_000_000; // 1 GAS (with 8 decimals)
308	///     
309	///     let mut script_builder = ScriptBuilder::new();
310	///     let script = script_builder.contract_call(
311	///         &ScriptHash::from_str("d2a4cff31913016155e38e474a2c06d08be276cf")?, // GAS token
312	///         "transfer",
313	///         &[
314	///             ContractParameter::from(account.get_script_hash()),
315	///             ContractParameter::from(Address::from_str(to_address)?.address_to_script_hash()?),
316	///             ContractParameter::integer(amount),
317	///             ContractParameter::any()
318	///         ],
319	///         Some(CallFlags::All)
320	///     )?;
321	///     
322	///     // Build the transaction with proper fee calculation
323	///     let mut tx_builder = TransactionBuilder::with_client(&client);
324	///     tx_builder.extend_script(script.to_bytes());
325	///     let account_signer = AccountSigner::called_by_entry(&account)?;
326	///     tx_builder.set_signers(vec![account_signer.into()])?;
327	///     tx_builder.valid_until_block(client.get_block_count().await? + 2400)?; // ~1 hour validity
328	///     
329	///     // Sign the transaction and get signed transaction
330	///     let signed_transaction = tx_builder.sign().await?;
331	///     
332	///     // Send the transaction to the network
333	///     let mut transaction = signed_transaction;
334	///     let response = transaction.send_tx().await?;
335	///     println!("✅ Transaction sent successfully!");
336	///     println!("Transaction ID: {}", response.hash);
337	///     println!("Transferred {} GAS to {}", amount as f64 / 100_000_000.0, to_address);
338	///     
339	///     Ok(())
340	/// }
341	/// ```
342	pub async fn send_tx(&mut self) -> Result<RawTransaction, TransactionError>
343// where
344	// 	P: APITrait,
345	{
346		if self.signers.len() != self.witnesses.len() {
347			return Err(TransactionError::TransactionConfiguration(
348				"The transaction does not have the same number of signers and witnesses."
349					.to_string(),
350			));
351		}
352		if self.size() > &(NeoConstants::MAX_TRANSACTION_SIZE as i32) {
353			return Err(TransactionError::TransactionConfiguration(
354				"The transaction exceeds the maximum transaction size.".to_string(),
355			));
356		}
357		let hex = hex::encode(self.to_array());
358		// self.throw()?;
359		self.block_count_when_sent = Some(self.network().unwrap().get_block_count().await?);
360		self.network()
361			.unwrap()
362			.send_raw_transaction(hex)
363			.await
364			.map_err(|e| TransactionError::IllegalState(e.to_string()))
365	}
366
367	/// Tracks a transaction until it appears in a block.
368	///
369	/// This method waits for the transaction to be included in a block by monitoring new blocks
370	/// as they are added to the blockchain. It returns when the transaction is found in a block.
371	///
372	/// # Arguments
373	///
374	/// * `max_blocks` - The maximum number of blocks to wait for the transaction to appear
375	///
376	/// # Returns
377	///
378	/// * `Ok(())` - If the transaction is found in a block
379	/// * `Err(TransactionError)` - If the transaction is not found after waiting for `max_blocks` blocks
380	///
381	/// # Errors
382	///
383	/// Returns an error if:
384	/// * The transaction has not been sent yet
385	/// * The maximum number of blocks is reached without finding the transaction
386	/// * There is an error communicating with the blockchain
387	///
388	/// # Examples
389	///
390	/// ```rust,no_run
391	/// use neo3::neo_builder::{ScriptBuilder, AccountSigner, TransactionBuilder, CallFlags};
392	/// use neo3::neo_clients::{HttpProvider, RpcClient, APITrait};
393	/// use neo3::neo_protocol::{Account, AccountTrait};
394	/// use neo3::neo_types::{ContractParameter, ScriptHash};
395	/// use std::str::FromStr;
396	///
397	/// #[tokio::main]
398	/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
399	///     // Initialize provider and client
400	///     let provider = HttpProvider::new("https://testnet1.neo.org:443").unwrap();
401	///     let client = RpcClient::new(provider);
402	///     
403	///     // Load account and create smart contract deployment transaction
404	///     let account = Account::from_wif("your_private_key_here")?;
405	///     
406	///     // Load contract NEF and manifest
407	///     let nef_bytes = std::fs::read("path/to/contract.nef")?;
408	///     let manifest_bytes = std::fs::read("path/to/contract.manifest.json")?;
409	///     
410	///     let mut script_builder = ScriptBuilder::new();
411	///     let deploy_script = script_builder.contract_call(
412	///         &ScriptHash::from_str("0xfffdc93764dbaddd97c48f252a53ea4643faa3fd")?, // ContractManagement
413	///         "deploy",
414	///         &[
415	///             ContractParameter::byte_array(nef_bytes),
416	///             ContractParameter::byte_array(manifest_bytes),
417	///             ContractParameter::any() // Optional data parameter
418	///         ],
419	///         Some(CallFlags::All)
420	///     )?;
421	///     
422	///     // Build transaction with appropriate settings for contract deployment
423	///     let mut tx_builder = TransactionBuilder::with_client(&client);
424	///     tx_builder.extend_script(deploy_script.to_bytes());
425	///     let account_signer = AccountSigner::called_by_entry(&account)?;
426	///     tx_builder.set_signers(vec![account_signer.into()])?;
427	///     tx_builder.valid_until_block(client.get_block_count().await? + 2400)?;
428	///     
429	///     // Sign the transaction and get signed transaction
430	///     let signed_transaction = tx_builder.sign().await?;
431	///     
432	///     // Send the transaction
433	///     let mut transaction = signed_transaction;
434	///     let response = transaction.send_tx().await?;
435	///     println!("✅ Contract deployment transaction sent!");
436	///     println!("Transaction ID: {}", response.hash);
437	///     
438	///     // Track the transaction until confirmation
439	///     println!("⏳ Waiting for transaction confirmation...");
440	///     transaction.track_tx(15).await?; // Wait up to 15 blocks (~15 seconds)
441	///     println!("🎉 Contract deployment confirmed!");
442	///     
443	///     Ok(())
444	/// }
445	/// ```
446	pub async fn track_tx(&self, max_blocks: u32) -> Result<(), TransactionError> {
447		let block_count_when_sent =
448			self.block_count_when_sent.ok_or(TransactionError::IllegalState(
449				"Cannot track transaction before it has been sent.".to_string(),
450			))?;
451
452		let tx_id = self.get_tx_id()?;
453		let mut current_block = block_count_when_sent;
454		let max_block = block_count_when_sent + max_blocks;
455
456		while current_block <= max_block {
457			// Get the current block count
458			let latest_block = self.network().unwrap().get_block_count().await?;
459
460			// If there are new blocks, check them for our transaction
461			if latest_block > current_block {
462				for block_index in current_block..latest_block {
463					// Get the block hash for this index
464					let block_hash = self.network().unwrap().get_block_hash(block_index).await?;
465
466					// Get the block with full transaction details
467					let block = self.network().unwrap().get_block(block_hash, true).await?;
468
469					// Check if our transaction is in this block
470					if let Some(transactions) = &block.transactions {
471						for tx in transactions.iter() {
472							if tx.hash == tx_id {
473								return Ok(());
474							}
475						}
476					}
477
478					current_block = block_index + 1;
479				}
480			}
481
482			// Wait a bit before checking again
483			tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
484		}
485
486		Err(TransactionError::IllegalState(format!(
487			"Transaction {} not found after waiting for {} blocks",
488			tx_id, max_blocks
489		)))
490	}
491
492	/// Retrieves the application log for this transaction.
493	///
494	/// Application logs contain detailed information about the execution of a transaction,
495	/// including notifications, stack items, and any exceptions that occurred during execution.
496	///
497	/// # Arguments
498	///
499	/// * `provider` - A provider implementing the `APITrait` to make the RPC call.
500	///
501	/// # Returns
502	///
503	/// A `Result` containing the `ApplicationLog` if successful,
504	/// or a `TransactionError` if an error occurs.
505	///
506	/// # Errors
507	///
508	/// Returns an error if:
509	/// * The transaction has not been sent yet
510	/// * The transaction ID cannot be calculated
511	/// * The provider encounters an error when retrieving the application log
512	///
513	/// # Examples
514	///
515	/// ```rust,no_run
516	/// use neo3::neo_builder::{ScriptBuilder, AccountSigner, TransactionBuilder, CallFlags};
517	/// use neo3::neo_clients::{HttpProvider, RpcClient, APITrait};
518	/// use neo3::neo_protocol::{Account, AccountTrait};
519	/// use neo3::neo_types::{ContractParameter, ScriptHash};
520	/// use std::str::FromStr;
521	///
522	/// #[tokio::main]
523	/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
524	///     // Setup client connection
525	///     let provider = HttpProvider::new("https://testnet1.neo.org:443").unwrap();
526	///     let client = RpcClient::new(provider);
527	///     
528	///     // Load account for contract interaction
529	///     let account = Account::from_wif("your_private_key_here")?;
530	///     let contract_hash = ScriptHash::from_str("your_contract_hash_here")?;
531	///     
532	///     // Create a contract invocation transaction
533	///     let mut script_builder = ScriptBuilder::new();
534	///     let invoke_script = script_builder.contract_call(
535	///         &contract_hash,
536	///         "setValue", // Contract method name
537	///         &[
538	///             ContractParameter::string("myKey".to_string()),
539	///             ContractParameter::string("myValue".to_string()),
540	///             ContractParameter::integer(42)
541	///         ],
542	///         Some(CallFlags::All)
543	///     )?;
544	///     
545	///     // Build and configure the transaction
546	///     let mut tx_builder = TransactionBuilder::with_client(&client);
547	///     tx_builder.extend_script(invoke_script.to_bytes());
548	///     let account_signer = AccountSigner::called_by_entry(&account)?;
549	///     tx_builder.set_signers(vec![account_signer.into()])?;
550	///     tx_builder.valid_until_block(client.get_block_count().await? + 1200)?; // 20 minutes validity
551	///     
552	///     // Sign the transaction and get signed transaction
553	///     let signed_transaction = tx_builder.sign().await?;
554	///     
555	///     // Send the transaction
556	///     let mut transaction = signed_transaction;
557	///     let response = transaction.send_tx().await?;
558	///     println!("📤 Smart contract invocation sent!");
559	///     println!("Transaction ID: {}", response.hash);
560	///     
561	///     // Wait for confirmation and get detailed execution results
562	///     println!("⏳ Waiting for transaction confirmation...");
563	///     transaction.track_tx(12).await?;
564	///     println!("✅ Transaction confirmed!");
565	///     
566	///     // Analyze the execution results
567	///     let app_log = transaction.get_application_log(&client).await?;
568	///     println!("🔍 Transaction execution analysis:");
569	///     if let Ok(execution) = app_log.get_first_execution() {
570	///         println!("  Execution state: {:?}", execution.state);
571	///         println!("  GAS consumed: {}", execution.gas_consumed);
572	///         
573	///         // Process contract notifications and events
574	///         if !execution.notifications.is_empty() {
575	///             println!("📋 Contract notifications:");
576	///             for (i, notification) in execution.notifications.iter().enumerate() {
577	///                 println!("  {}. Event: {} from contract {}",
578	///                     i + 1, notification.event_name, notification.contract);
579	///                 println!("     State: {:?}", notification.state);
580	///             }
581	///         }
582	///         
583	///         // Check execution stack for return values
584	///         if !execution.stack.is_empty() {
585	///             println!("📊 Return values: {:?}", execution.stack);
586	///         }
587	///         
588	///         if execution.state.to_string() == "HALT" {
589	///             println!("🎉 Smart contract executed successfully!");
590	///         } else {
591	///             println!("❌ Smart contract execution failed");
592	///             if let Some(exception) = &execution.exception {
593	///                 println!("   Exception: {}", exception);
594	///             }
595	///         }
596	///     }
597	///     
598	///     Ok(())
599	/// }
600	/// ```
601	pub async fn get_application_log<P>(
602		&self,
603		provider: &P,
604	) -> Result<ApplicationLog, TransactionError>
605	where
606		P: APITrait,
607	{
608		init_logger();
609		if self.block_count_when_sent.is_none() {
610			return Err(TransactionError::IllegalState(
611				"Cannot get the application log before transaction has been sent.".to_string(),
612			));
613		}
614
615		let hash = self.get_tx_id()?;
616		info!("hash: {:?}", hash);
617
618		// self.thro
619		provider
620			.get_application_log(hash)
621			.await
622			.map_err(|e| TransactionError::IllegalState(e.to_string()))
623	}
624}
625
626// This commented-out code has been replaced by the send_tx method above
627
628impl<'a, P: JsonRpcProvider + 'static> Eq for Transaction<'a, P> {}
629
630impl<'a, P: JsonRpcProvider + 'static> PartialEq for Transaction<'a, P> {
631	fn eq(&self, other: &Self) -> bool {
632		self.to_array() == other.to_array()
633	}
634}
635
636impl<'a, P: JsonRpcProvider + 'static> NeoSerializable for Transaction<'a, P> {
637	type Error = TransactionError;
638
639	fn size(&self) -> usize {
640		Transaction::<HttpProvider>::HEADER_SIZE
641			+ self.signers.var_size()
642			+ self.attributes.var_size()
643			+ self.script.var_size()
644			+ self.witnesses.var_size()
645	}
646
647	fn encode(&self, writer: &mut Encoder) {
648		self.serialize_without_witnesses(writer);
649		writer.write_serializable_variable_list(&self.witnesses);
650	}
651
652	fn decode(reader: &mut Decoder) -> Result<Self, Self::Error>
653	where
654		Self: Sized,
655	{
656		let version = reader.read_u8();
657		let nonce = reader.read_u32().map_err(|e| {
658			TransactionError::TransactionConfiguration(format!("Failed to read nonce: {}", e))
659		})?;
660		let system_fee = reader.read_i64().map_err(|e| {
661			TransactionError::TransactionConfiguration(format!("Failed to read system fee: {}", e))
662		})?;
663		let network_fee = reader.read_i64().map_err(|e| {
664			TransactionError::TransactionConfiguration(format!("Failed to read network fee: {}", e))
665		})?;
666		let valid_until_block = reader.read_u32().map_err(|e| {
667			TransactionError::TransactionConfiguration(format!(
668				"Failed to read valid until block: {}",
669				e
670			))
671		})?;
672
673		// Read signers
674		let signers: Vec<Signer> = reader.read_serializable_list::<Signer>()?;
675
676		// Read attributes
677		let attributes: Vec<TransactionAttribute> =
678			reader.read_serializable_list::<TransactionAttribute>()?;
679
680		let script = reader.read_var_bytes()?.to_vec();
681
682		let mut witnesses = vec![];
683		if reader.available() > 0 {
684			witnesses.append(&mut reader.read_serializable_list::<Witness>()?);
685		}
686
687		Ok(Self {
688			network: None,
689			version,
690			nonce,
691			valid_until_block,
692			size: 0,
693			sys_fee: system_fee,
694			net_fee: network_fee,
695			signers,
696			attributes,
697			script,
698			witnesses,
699			// block_time: None,
700			block_count_when_sent: None,
701		})
702	}
703
704	fn to_array(&self) -> Vec<u8> {
705		let mut writer = Encoder::new();
706		self.encode(&mut writer);
707		writer.to_bytes()
708	}
709}