hal/
tx.rs

1use bitcoin::consensus::encode::serialize;
2use bitcoin::{address, Address, Amount, Network, Script, Transaction, TxIn, TxOut, Txid, Wtxid};
3use serde::{Deserialize, Serialize};
4
5use crate::{GetInfo, HexBytes};
6
7#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
8pub struct InputScriptInfo {
9	pub hex: Option<HexBytes>,
10	pub asm: Option<String>,
11}
12
13pub struct InputScript<'a>(pub &'a Script);
14
15impl<'a> GetInfo<InputScriptInfo> for InputScript<'a> {
16	fn get_info(&self, _network: Network) -> InputScriptInfo {
17		InputScriptInfo {
18			hex: Some(self.0.to_bytes().into()),
19			asm: Some(self.0.to_asm_string()),
20		}
21	}
22}
23
24#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
25pub struct InputInfo {
26	pub prevout: Option<String>,
27	pub txid: Option<Txid>,
28	pub vout: Option<u32>,
29	pub script_sig: Option<InputScriptInfo>,
30	pub sequence: Option<u32>,
31	pub witness: Option<Vec<HexBytes>>,
32}
33
34impl GetInfo<InputInfo> for TxIn {
35	fn get_info(&self, network: Network) -> InputInfo {
36		InputInfo {
37			prevout: Some(self.previous_output.to_string()),
38			txid: Some(self.previous_output.txid),
39			vout: Some(self.previous_output.vout),
40			sequence: Some(self.sequence.to_consensus_u32()),
41			script_sig: Some(InputScript(&self.script_sig).get_info(network)),
42			witness: if self.witness.len() > 0 {
43				Some(self.witness.iter().map(|h| h.into()).collect())
44			} else {
45				None
46			},
47		}
48	}
49}
50
51#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
52pub struct OutputScriptInfo {
53	pub hex: Option<HexBytes>,
54	pub asm: Option<String>,
55	#[serde(skip_serializing_if = "Option::is_none", rename = "type")]
56	pub type_: Option<String>,
57	#[serde(skip_serializing_if = "Option::is_none")]
58	pub address: Option<Address<address::NetworkUnchecked>>,
59}
60
61pub struct OutputScript<'a>(pub &'a Script);
62
63impl<'a> GetInfo<OutputScriptInfo> for OutputScript<'a> {
64	fn get_info(&self, network: Network) -> OutputScriptInfo {
65		OutputScriptInfo {
66			hex: Some(self.0.to_bytes().into()),
67			asm: Some(self.0.to_asm_string()),
68			type_: Some(
69				if self.0.is_p2pk() {
70					"p2pk"
71				} else if self.0.is_p2pkh() {
72					"p2pkh"
73				} else if self.0.is_op_return() {
74					"opreturn"
75				} else if self.0.is_p2sh() {
76					"p2sh"
77				} else if self.0.is_p2wpkh() {
78					"p2wpkh"
79				} else if self.0.is_p2wsh() {
80					"p2wsh"
81				} else if self.0.is_p2tr() {
82					"p2tr"
83				} else {
84					"unknown"
85				}
86				.to_owned(),
87			),
88			address: Address::from_script(&self.0, network).ok().map(|a| a.as_unchecked().clone()),
89		}
90	}
91}
92
93#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
94pub struct OutputInfo {
95	#[serde(with = "bitcoin::amount::serde::as_sat::opt")]
96	pub value: Option<Amount>,
97	pub script_pub_key: Option<OutputScriptInfo>,
98}
99
100impl GetInfo<OutputInfo> for TxOut {
101	fn get_info(&self, network: Network) -> OutputInfo {
102		OutputInfo {
103			value: Some(self.value),
104			script_pub_key: Some(OutputScript(&self.script_pubkey).get_info(network)),
105		}
106	}
107}
108
109#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
110pub struct TransactionInfo {
111	pub txid: Option<Txid>,
112	pub wtxid: Option<Wtxid>,
113	pub size: Option<usize>,
114	pub weight: Option<usize>,
115	pub vsize: Option<usize>,
116	pub version: Option<i32>,
117	pub locktime: Option<u32>,
118	pub inputs: Option<Vec<InputInfo>>,
119	pub outputs: Option<Vec<OutputInfo>>,
120	pub total_output_value: Option<u64>,
121}
122
123impl GetInfo<TransactionInfo> for Transaction {
124	fn get_info(&self, network: Network) -> TransactionInfo {
125		let weight = self.weight().to_wu() as usize;
126		TransactionInfo {
127			txid: Some(self.compute_txid()),
128			wtxid: Some(self.compute_wtxid()),
129			version: Some(self.version.0),
130			locktime: Some(self.lock_time.to_consensus_u32()),
131			size: Some(serialize(self).len()),
132			weight: Some(weight),
133			vsize: Some(weight / 4),
134			inputs: Some(self.input.iter().map(|i| i.get_info(network)).collect()),
135			outputs: Some(self.output.iter().map(|o| o.get_info(network)).collect()),
136			total_output_value: Some(self.output.iter().map(|o| o.value.to_sat()).sum()),
137		}
138	}
139}