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}