1#![allow(non_snake_case)]
8
9mod script_public_key;
10
11use borsh::{BorshDeserialize, BorshSerialize};
12use kaspa_utils::hex::ToHex;
13use kaspa_utils::mem_size::MemSizeEstimator;
14use kaspa_utils::{serde_bytes, serde_bytes_fixed_ref};
15pub use script_public_key::{
16 scriptvec, ScriptPublicKey, ScriptPublicKeyT, ScriptPublicKeyVersion, ScriptPublicKeys, ScriptVec, SCRIPT_VECTOR_SIZE,
17};
18use serde::{Deserialize, Serialize};
19use std::collections::HashSet;
20use std::sync::atomic::AtomicU64;
21use std::sync::atomic::Ordering::SeqCst;
22use std::{
23 fmt::Display,
24 ops::Range,
25 str::{self},
26};
27use wasm_bindgen::prelude::*;
28
29use crate::{
30 hashing,
31 subnets::{self, SubnetworkId},
32};
33
34pub const COINBASE_TRANSACTION_INDEX: usize = 0;
36pub type TransactionId = kaspa_hashes::Hash;
38
39#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
45#[serde(rename_all = "camelCase")]
46#[wasm_bindgen(inspectable, js_name = TransactionUtxoEntry)]
47pub struct UtxoEntry {
48 pub amount: u64,
49 #[wasm_bindgen(js_name = scriptPublicKey, getter_with_clone)]
50 pub script_public_key: ScriptPublicKey,
51 #[wasm_bindgen(js_name = blockDaaScore)]
52 pub block_daa_score: u64,
53 #[wasm_bindgen(js_name = isCoinbase)]
54 pub is_coinbase: bool,
55}
56
57impl UtxoEntry {
58 pub fn new(amount: u64, script_public_key: ScriptPublicKey, block_daa_score: u64, is_coinbase: bool) -> Self {
59 Self { amount, script_public_key, block_daa_score, is_coinbase }
60 }
61}
62
63impl MemSizeEstimator for UtxoEntry {}
64
65pub type TransactionIndexType = u32;
66
67#[derive(Eq, Default, Hash, PartialEq, Debug, Copy, Clone, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
69#[serde(rename_all = "camelCase")]
70pub struct TransactionOutpoint {
71 #[serde(with = "serde_bytes_fixed_ref")]
72 pub transaction_id: TransactionId,
73 pub index: TransactionIndexType,
74}
75
76impl TransactionOutpoint {
77 pub fn new(transaction_id: TransactionId, index: u32) -> Self {
78 Self { transaction_id, index }
79 }
80}
81
82impl Display for TransactionOutpoint {
83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 write!(f, "({}, {})", self.transaction_id, self.index)
85 }
86}
87
88#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
90#[serde(rename_all = "camelCase")]
91pub struct TransactionInput {
92 pub previous_outpoint: TransactionOutpoint,
93 #[serde(with = "serde_bytes")]
94 pub signature_script: Vec<u8>, pub sequence: u64,
96
97 pub sig_op_count: u8,
101}
102
103impl TransactionInput {
104 pub fn new(previous_outpoint: TransactionOutpoint, signature_script: Vec<u8>, sequence: u64, sig_op_count: u8) -> Self {
105 Self { previous_outpoint, signature_script, sequence, sig_op_count }
106 }
107}
108
109impl std::fmt::Debug for TransactionInput {
110 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111 f.debug_struct("TransactionInput")
112 .field("previous_outpoint", &self.previous_outpoint)
113 .field("signature_script", &self.signature_script.to_hex())
114 .field("sequence", &self.sequence)
115 .field("sig_op_count", &self.sig_op_count)
116 .finish()
117 }
118}
119
120#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
122#[serde(rename_all = "camelCase")]
123pub struct TransactionOutput {
124 pub value: u64,
125 pub script_public_key: ScriptPublicKey,
126}
127
128impl TransactionOutput {
129 pub fn new(value: u64, script_public_key: ScriptPublicKey) -> Self {
130 Self { value, script_public_key }
131 }
132}
133
134#[derive(Debug, Default, Serialize, Deserialize)]
135pub struct TransactionMass(AtomicU64); impl Eq for TransactionMass {}
138
139impl PartialEq for TransactionMass {
140 fn eq(&self, other: &Self) -> bool {
141 self.0.load(SeqCst) == other.0.load(SeqCst)
142 }
143}
144
145impl Clone for TransactionMass {
146 fn clone(&self) -> Self {
147 Self(AtomicU64::new(self.0.load(SeqCst)))
148 }
149}
150
151impl BorshDeserialize for TransactionMass {
152 fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
153 let mass: u64 = borsh::BorshDeserialize::deserialize_reader(reader)?;
154 Ok(Self(AtomicU64::new(mass)))
155 }
156}
157
158impl BorshSerialize for TransactionMass {
159 fn serialize<W: std::io::prelude::Write>(&self, writer: &mut W) -> std::io::Result<()> {
160 borsh::BorshSerialize::serialize(&self.0.load(SeqCst), writer)
161 }
162}
163
164#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
166#[serde(rename_all = "camelCase")]
167pub struct Transaction {
168 pub version: u16,
169 pub inputs: Vec<TransactionInput>,
170 pub outputs: Vec<TransactionOutput>,
171 pub lock_time: u64,
172 pub subnetwork_id: SubnetworkId,
173 pub gas: u64,
174 #[serde(with = "serde_bytes")]
175 pub payload: Vec<u8>,
176
177 #[serde(default)]
178 mass: TransactionMass,
179
180 #[serde(with = "serde_bytes_fixed_ref")]
183 id: TransactionId,
184}
185
186impl Transaction {
187 pub fn new(
188 version: u16,
189 inputs: Vec<TransactionInput>,
190 outputs: Vec<TransactionOutput>,
191 lock_time: u64,
192 subnetwork_id: SubnetworkId,
193 gas: u64,
194 payload: Vec<u8>,
195 ) -> Self {
196 let mut tx = Self::new_non_finalized(version, inputs, outputs, lock_time, subnetwork_id, gas, payload);
197 tx.finalize();
198 tx
199 }
200
201 pub fn new_non_finalized(
202 version: u16,
203 inputs: Vec<TransactionInput>,
204 outputs: Vec<TransactionOutput>,
205 lock_time: u64,
206 subnetwork_id: SubnetworkId,
207 gas: u64,
208 payload: Vec<u8>,
209 ) -> Self {
210 Self { version, inputs, outputs, lock_time, subnetwork_id, gas, payload, mass: Default::default(), id: Default::default() }
211 }
212}
213
214impl Transaction {
215 pub fn is_coinbase(&self) -> bool {
220 self.subnetwork_id == subnets::SUBNETWORK_ID_COINBASE
221 }
222
223 pub fn finalize(&mut self) {
225 self.id = hashing::tx::id(self);
226 }
227
228 pub fn id(&self) -> TransactionId {
230 self.id
231 }
232
233 pub fn set_mass(&self, mass: u64) {
236 self.mass.0.store(mass, SeqCst)
237 }
238
239 pub fn mass(&self) -> u64 {
240 self.mass.0.load(SeqCst)
241 }
242
243 pub fn with_mass(self, mass: u64) -> Self {
244 self.set_mass(mass);
245 self
246 }
247}
248
249impl MemSizeEstimator for Transaction {
250 fn estimate_mem_bytes(&self) -> usize {
251 size_of::<Self>()
253 + self.payload.len()
254 + self
255 .inputs
256 .iter()
257 .map(|i| i.signature_script.len() + size_of::<TransactionInput>())
258 .chain(self.outputs.iter().map(|o| {
259 o.script_public_key.script().len().saturating_sub(SCRIPT_VECTOR_SIZE) + size_of::<TransactionOutput>()
261 }))
262 .sum::<usize>()
263 }
264}
265
266pub trait VerifiableTransaction {
268 fn tx(&self) -> &Transaction;
269
270 fn populated_input(&self, index: usize) -> (&TransactionInput, &UtxoEntry);
272
273 fn populated_inputs(&self) -> PopulatedInputIterator<'_, Self>
275 where
276 Self: Sized,
277 {
278 PopulatedInputIterator::new(self)
279 }
280
281 fn inputs(&self) -> &[TransactionInput] {
282 &self.tx().inputs
283 }
284
285 fn outputs(&self) -> &[TransactionOutput] {
286 &self.tx().outputs
287 }
288
289 fn is_coinbase(&self) -> bool {
290 self.tx().is_coinbase()
291 }
292
293 fn id(&self) -> TransactionId {
294 self.tx().id()
295 }
296}
297
298pub struct PopulatedInputIterator<'a, T: VerifiableTransaction> {
300 tx: &'a T,
301 r: Range<usize>,
302}
303
304impl<'a, T: VerifiableTransaction> PopulatedInputIterator<'a, T> {
305 pub fn new(tx: &'a T) -> Self {
306 Self { tx, r: (0..tx.inputs().len()) }
307 }
308}
309
310impl<'a, T: VerifiableTransaction> Iterator for PopulatedInputIterator<'a, T> {
311 type Item = (&'a TransactionInput, &'a UtxoEntry);
312
313 fn next(&mut self) -> Option<Self::Item> {
314 self.r.next().map(|i| self.tx.populated_input(i))
315 }
316
317 fn size_hint(&self) -> (usize, Option<usize>) {
318 self.r.size_hint()
319 }
320}
321
322impl<'a, T: VerifiableTransaction> ExactSizeIterator for PopulatedInputIterator<'a, T> {}
323
324pub struct PopulatedTransaction<'a> {
326 pub tx: &'a Transaction,
327 pub entries: Vec<UtxoEntry>,
328}
329
330impl<'a> PopulatedTransaction<'a> {
331 pub fn new(tx: &'a Transaction, entries: Vec<UtxoEntry>) -> Self {
332 assert_eq!(tx.inputs.len(), entries.len());
333 Self { tx, entries }
334 }
335}
336
337impl<'a> VerifiableTransaction for PopulatedTransaction<'a> {
338 fn tx(&self) -> &Transaction {
339 self.tx
340 }
341
342 fn populated_input(&self, index: usize) -> (&TransactionInput, &UtxoEntry) {
343 (&self.tx.inputs[index], &self.entries[index])
344 }
345}
346
347pub struct ValidatedTransaction<'a> {
349 pub tx: &'a Transaction,
350 pub entries: Vec<UtxoEntry>,
351 pub calculated_fee: u64,
352}
353
354impl<'a> ValidatedTransaction<'a> {
355 pub fn new(populated_tx: PopulatedTransaction<'a>, calculated_fee: u64) -> Self {
356 Self { tx: populated_tx.tx, entries: populated_tx.entries, calculated_fee }
357 }
358
359 pub fn new_coinbase(tx: &'a Transaction) -> Self {
360 assert!(tx.is_coinbase());
361 Self { tx, entries: Vec::new(), calculated_fee: 0 }
362 }
363}
364
365impl<'a> VerifiableTransaction for ValidatedTransaction<'a> {
366 fn tx(&self) -> &Transaction {
367 self.tx
368 }
369
370 fn populated_input(&self, index: usize) -> (&TransactionInput, &UtxoEntry) {
371 (&self.tx.inputs[index], &self.entries[index])
372 }
373}
374
375impl AsRef<Transaction> for Transaction {
376 fn as_ref(&self) -> &Transaction {
377 self
378 }
379}
380
381#[derive(Clone, Debug, PartialEq, Eq)]
384pub struct MutableTransaction<T: AsRef<Transaction> = std::sync::Arc<Transaction>> {
385 pub tx: T,
387 pub entries: Vec<Option<UtxoEntry>>,
389 pub calculated_fee: Option<u64>,
391 pub calculated_compute_mass: Option<u64>,
393}
394
395impl<T: AsRef<Transaction>> MutableTransaction<T> {
396 pub fn new(tx: T) -> Self {
397 let num_inputs = tx.as_ref().inputs.len();
398 Self { tx, entries: vec![None; num_inputs], calculated_fee: None, calculated_compute_mass: None }
399 }
400
401 pub fn id(&self) -> TransactionId {
402 self.tx.as_ref().id()
403 }
404
405 pub fn with_entries(tx: T, entries: Vec<UtxoEntry>) -> Self {
406 assert_eq!(tx.as_ref().inputs.len(), entries.len());
407 Self { tx, entries: entries.into_iter().map(Some).collect(), calculated_fee: None, calculated_compute_mass: None }
408 }
409
410 pub fn as_verifiable(&self) -> impl VerifiableTransaction + '_ {
413 assert!(self.is_verifiable());
414 MutableTransactionVerifiableWrapper { inner: self }
415 }
416
417 pub fn is_verifiable(&self) -> bool {
418 assert_eq!(self.entries.len(), self.tx.as_ref().inputs.len());
419 self.entries.iter().all(|e| e.is_some())
420 }
421
422 pub fn is_fully_populated(&self) -> bool {
423 self.is_verifiable() && self.calculated_fee.is_some() && self.calculated_compute_mass.is_some()
424 }
425
426 pub fn missing_outpoints(&self) -> impl Iterator<Item = TransactionOutpoint> + '_ {
427 assert_eq!(self.entries.len(), self.tx.as_ref().inputs.len());
428 self.entries.iter().enumerate().filter_map(|(i, entry)| {
429 if entry.is_none() {
430 Some(self.tx.as_ref().inputs[i].previous_outpoint)
431 } else {
432 None
433 }
434 })
435 }
436
437 pub fn clear_entries(&mut self) {
438 for entry in self.entries.iter_mut() {
439 *entry = None;
440 }
441 }
442
443 pub fn calculated_feerate(&self) -> Option<f64> {
448 let contextual_mass = self.tx.as_ref().mass();
449 if contextual_mass > 0 {
450 self.calculated_fee.map(|fee| fee as f64 / contextual_mass as f64)
451 } else {
452 None
453 }
454 }
455
456 pub fn mempool_estimated_bytes(&self) -> usize {
460 self.estimate_mem_bytes()
461 }
462
463 pub fn has_parent(&self, possible_parent: TransactionId) -> bool {
464 self.tx.as_ref().inputs.iter().any(|x| x.previous_outpoint.transaction_id == possible_parent)
465 }
466
467 pub fn has_parent_in_set(&self, possible_parents: &HashSet<TransactionId>) -> bool {
468 self.tx.as_ref().inputs.iter().any(|x| possible_parents.contains(&x.previous_outpoint.transaction_id))
469 }
470}
471
472impl<T: AsRef<Transaction>> MemSizeEstimator for MutableTransaction<T> {
473 fn estimate_mem_bytes(&self) -> usize {
474 size_of::<Self>()
475 + self
476 .entries
477 .iter()
478 .map(|op| {
479 size_of::<Option<UtxoEntry>>()
481 + op.as_ref().map_or(0, |e| e.script_public_key.script().len().saturating_sub(SCRIPT_VECTOR_SIZE))
482 })
483 .sum::<usize>()
484 + self.tx.as_ref().estimate_mem_bytes()
485 }
486}
487
488impl<T: AsRef<Transaction>> AsRef<Transaction> for MutableTransaction<T> {
489 fn as_ref(&self) -> &Transaction {
490 self.tx.as_ref()
491 }
492}
493
494struct MutableTransactionVerifiableWrapper<'a, T: AsRef<Transaction>> {
496 inner: &'a MutableTransaction<T>,
497}
498
499impl<T: AsRef<Transaction>> VerifiableTransaction for MutableTransactionVerifiableWrapper<'_, T> {
500 fn tx(&self) -> &Transaction {
501 self.inner.tx.as_ref()
502 }
503
504 fn populated_input(&self, index: usize) -> (&TransactionInput, &UtxoEntry) {
505 (
506 &self.inner.tx.as_ref().inputs[index],
507 self.inner.entries[index].as_ref().expect("expected to be called only following full UTXO population"),
508 )
509 }
510}
511
512impl MutableTransaction {
514 pub fn from_tx(tx: Transaction) -> Self {
515 Self::new(std::sync::Arc::new(tx))
516 }
517}
518
519pub type SignableTransaction = MutableTransaction<Transaction>;
522
523#[cfg(test)]
524mod tests {
525 use super::*;
526 use consensus_core::subnets::SUBNETWORK_ID_COINBASE;
527 use smallvec::smallvec;
528
529 fn test_transaction() -> Transaction {
530 let script_public_key = ScriptPublicKey::new(
531 0,
532 smallvec![
533 0x76, 0xa9, 0x21, 0x03, 0x2f, 0x7e, 0x43, 0x0a, 0xa4, 0xc9, 0xd1, 0x59, 0x43, 0x7e, 0x84, 0xb9, 0x75, 0xdc, 0x76,
534 0xd9, 0x00, 0x3b, 0xf0, 0x92, 0x2c, 0xf3, 0xaa, 0x45, 0x28, 0x46, 0x4b, 0xab, 0x78, 0x0d, 0xba, 0x5e
535 ],
536 );
537 Transaction::new(
538 1,
539 vec![
540 TransactionInput {
541 previous_outpoint: TransactionOutpoint {
542 transaction_id: TransactionId::from_slice(&[
543 0x16, 0x5e, 0x38, 0xe8, 0xb3, 0x91, 0x45, 0x95, 0xd9, 0xc6, 0x41, 0xf3, 0xb8, 0xee, 0xc2, 0xf3, 0x46,
544 0x11, 0x89, 0x6b, 0x82, 0x1a, 0x68, 0x3b, 0x7a, 0x4e, 0xde, 0xfe, 0x2c, 0x00, 0x00, 0x00,
545 ]),
546 index: 0xfffffffa,
547 },
548 signature_script: vec![
549 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
550 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
551 ],
552 sequence: 2,
553 sig_op_count: 3,
554 },
555 TransactionInput {
556 previous_outpoint: TransactionOutpoint {
557 transaction_id: TransactionId::from_slice(&[
558 0x4b, 0xb0, 0x75, 0x35, 0xdf, 0xd5, 0x8e, 0x0b, 0x3c, 0xd6, 0x4f, 0xd7, 0x15, 0x52, 0x80, 0x87, 0x2a,
559 0x04, 0x71, 0xbc, 0xf8, 0x30, 0x95, 0x52, 0x6a, 0xce, 0x0e, 0x38, 0xc6, 0x00, 0x00, 0x00,
560 ]),
561 index: 0xfffffffb,
562 },
563 signature_script: vec![
564 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31,
565 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
566 ],
567 sequence: 4,
568 sig_op_count: 5,
569 },
570 ],
571 vec![
572 TransactionOutput { value: 6, script_public_key: script_public_key.clone() },
573 TransactionOutput { value: 7, script_public_key },
574 ],
575 8,
576 SUBNETWORK_ID_COINBASE,
577 9,
578 vec![
579 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
580 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
581 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
582 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b,
583 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e,
584 0x5f, 0x60, 0x61, 0x62, 0x63,
585 ],
586 )
587 }
588
589 #[test]
590 fn test_transaction_bincode() {
591 let tx = test_transaction();
592 let bts = bincode::serialize(&tx).unwrap();
593
594 let expected_bts = vec![
596 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 22, 94, 56, 232, 179, 145, 69, 149, 217, 198, 65, 243, 184, 238, 194, 243, 70, 17, 137, 107,
597 130, 26, 104, 59, 122, 78, 222, 254, 44, 0, 0, 0, 250, 255, 255, 255, 32, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8,
598 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 2, 0, 0, 0, 0, 0, 0, 0, 3, 75,
599 176, 117, 53, 223, 213, 142, 11, 60, 214, 79, 215, 21, 82, 128, 135, 42, 4, 113, 188, 248, 48, 149, 82, 106, 206, 14, 56,
600 198, 0, 0, 0, 251, 255, 255, 255, 32, 0, 0, 0, 0, 0, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
601 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 4, 0, 0, 0, 0, 0, 0, 0, 5, 2, 0, 0, 0, 0, 0, 0, 0, 6, 0,
602 0, 0, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0, 118, 169, 33, 3, 47, 126, 67, 10, 164, 201, 209, 89, 67, 126, 132, 185,
603 117, 220, 118, 217, 0, 59, 240, 146, 44, 243, 170, 69, 40, 70, 75, 171, 120, 13, 186, 94, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0,
604 36, 0, 0, 0, 0, 0, 0, 0, 118, 169, 33, 3, 47, 126, 67, 10, 164, 201, 209, 89, 67, 126, 132, 185, 117, 220, 118, 217, 0,
605 59, 240, 146, 44, 243, 170, 69, 40, 70, 75, 171, 120, 13, 186, 94, 8, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
606 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
607 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
608 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
609 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 0, 0, 0, 0, 0,
610 0, 0, 0, 69, 146, 193, 64, 98, 49, 45, 0, 77, 32, 25, 122, 77, 15, 211, 252, 61, 210, 82, 177, 39, 153, 127, 33, 188, 172,
611 138, 38, 67, 75, 241, 176,
612 ];
613 assert_eq!(expected_bts, bts);
614 assert_eq!(tx, bincode::deserialize(&bts).unwrap());
615 }
616
617 #[test]
618 fn test_transaction_json() {
619 let tx = test_transaction();
620 let str = serde_json::to_string_pretty(&tx).unwrap();
621 let expected_str = r#"{
622 "version": 1,
623 "inputs": [
624 {
625 "previousOutpoint": {
626 "transactionId": "165e38e8b3914595d9c641f3b8eec2f34611896b821a683b7a4edefe2c000000",
627 "index": 4294967290
628 },
629 "signatureScript": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
630 "sequence": 2,
631 "sigOpCount": 3
632 },
633 {
634 "previousOutpoint": {
635 "transactionId": "4bb07535dfd58e0b3cd64fd7155280872a0471bcf83095526ace0e38c6000000",
636 "index": 4294967291
637 },
638 "signatureScript": "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
639 "sequence": 4,
640 "sigOpCount": 5
641 }
642 ],
643 "outputs": [
644 {
645 "value": 6,
646 "scriptPublicKey": "000076a921032f7e430aa4c9d159437e84b975dc76d9003bf0922cf3aa4528464bab780dba5e"
647 },
648 {
649 "value": 7,
650 "scriptPublicKey": "000076a921032f7e430aa4c9d159437e84b975dc76d9003bf0922cf3aa4528464bab780dba5e"
651 }
652 ],
653 "lockTime": 8,
654 "subnetworkId": "0100000000000000000000000000000000000000",
655 "gas": 9,
656 "payload": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f60616263",
657 "mass": 0,
658 "id": "4592c14062312d004d20197a4d0fd3fc3dd252b127997f21bcac8a26434bf1b0"
659}"#;
660 assert_eq!(expected_str, str);
661 assert_eq!(tx, serde_json::from_str(&str).unwrap());
662 }
663
664 #[test]
665 fn test_spk_serde_json() {
666 let vec = (0..SCRIPT_VECTOR_SIZE as u8).collect::<Vec<_>>();
667 let spk = ScriptPublicKey::from_vec(0xc0de, vec.clone());
668 let hex: String = serde_json::to_string(&spk).unwrap();
669 assert_eq!("\"c0de000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223\"", hex);
670 let spk = serde_json::from_str::<ScriptPublicKey>(&hex).unwrap();
671 assert_eq!(spk.version, 0xc0de);
672 assert_eq!(spk.script.as_slice(), vec.as_slice());
673 let result = "00".parse::<ScriptPublicKey>();
674 assert!(matches!(result, Err(faster_hex::Error::InvalidLength(2))));
675 let result = "0000".parse::<ScriptPublicKey>();
676 let _empty = ScriptPublicKey { version: 0, script: ScriptVec::new() };
677 assert!(matches!(result, Ok(_empty)));
678 }
679
680 #[test]
681 fn test_spk_borsh() {
682 let spk = ScriptPublicKey::from_vec(12, vec![32; 20]);
684 let bin = borsh::to_vec(&spk).unwrap();
685 let spk2: ScriptPublicKey = BorshDeserialize::try_from_slice(&bin).unwrap();
686 assert_eq!(spk, spk2);
687
688 let spk = ScriptPublicKey::from_vec(55455, vec![11; 200]);
689 let bin = borsh::to_vec(&spk).unwrap();
690 let spk2: ScriptPublicKey = BorshDeserialize::try_from_slice(&bin).unwrap();
691 assert_eq!(spk, spk2);
692 }
693
694 }