1use elements::encode::serialize;
2use elements::secp256k1_zkp::{RangeProof, SurjectionProof};
3use elements::{
4 bitcoin, confidential, Address, AssetIssuance, PeginData, PegoutData, Script, Transaction,
5 TxIn, TxInWitness, TxOut, TxOutWitness, Txid, Wtxid,
6};
7
8use serde::{Deserialize, Serialize};
9
10use crate::{GetInfo, HexBytes, Network};
11
12use crate::confidential::{ConfidentialAssetInfo, ConfidentialNonceInfo, ConfidentialValueInfo};
13
14const BTCNET: elements::bitcoin::Network = elements::bitcoin::Network::Bitcoin;
15
16#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
17pub struct AssetIssuanceInfo {
18 pub asset_blinding_nonce: Option<HexBytes>,
19 pub asset_entropy: Option<HexBytes>,
20 pub amount: Option<ConfidentialValueInfo>,
21 pub inflation_keys: Option<ConfidentialValueInfo>,
22}
23
24impl GetInfo<AssetIssuanceInfo> for AssetIssuance {
25 fn get_info(&self, network: Network) -> AssetIssuanceInfo {
26 AssetIssuanceInfo {
27 asset_blinding_nonce: Some(self.asset_blinding_nonce[..].into()),
28 asset_entropy: Some(self.asset_entropy[..].into()),
29 amount: Some(self.amount.get_info(network)),
30 inflation_keys: Some(self.inflation_keys.get_info(network)),
31 }
32 }
33}
34
35#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
36pub struct PeginDataInfo {
37 pub outpoint: String,
38 pub value: u64,
39 pub asset: ConfidentialAssetInfo,
40 pub genesis_hash: bitcoin::BlockHash,
41 pub claim_script: HexBytes,
42 pub mainchain_tx_hex: HexBytes,
43 pub mainchain_tx: Option<hal::tx::TransactionInfo>,
44 pub merkle_proof: HexBytes,
45 pub referenced_block: bitcoin::BlockHash,
46}
47
48impl<'tx> GetInfo<PeginDataInfo> for PeginData<'tx> {
49 fn get_info(&self, network: Network) -> PeginDataInfo {
50 PeginDataInfo {
51 outpoint: self.outpoint.to_string(),
52 value: self.value,
53 asset: self.asset.get_info(network),
54 genesis_hash: self.genesis_hash,
55 claim_script: self.claim_script.into(),
56 mainchain_tx_hex: self.tx.into(),
57 mainchain_tx: match bitcoin::consensus::encode::deserialize::<bitcoin::Transaction>(
58 self.tx,
59 ) {
60 Ok(tx) => Some(hal::GetInfo::get_info(&tx, BTCNET)),
61 Err(_) => None,
62 },
63 merkle_proof: self.merkle_proof.into(),
64 referenced_block: self.referenced_block,
65 }
66 }
67}
68
69#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
70pub struct InputWitnessInfo {
71 pub amount_rangeproof: Option<HexBytes>,
72 pub inflation_keys_rangeproof: Option<HexBytes>,
73 #[serde(skip_serializing_if = "Option::is_none")]
74 pub script_witness: Option<Vec<HexBytes>>,
75 #[serde(skip_serializing_if = "Option::is_none")]
76 pub pegin_witness: Option<Vec<HexBytes>>,
77}
78
79impl GetInfo<InputWitnessInfo> for TxInWitness {
80 fn get_info(&self, _network: Network) -> InputWitnessInfo {
81 InputWitnessInfo {
82 amount_rangeproof: self
83 .amount_rangeproof
84 .as_ref()
85 .map(|r| RangeProof::serialize(r).into()),
86 inflation_keys_rangeproof: self
87 .inflation_keys_rangeproof
88 .as_ref()
89 .map(|r| RangeProof::serialize(r).into()),
90 script_witness: if !self.script_witness.is_empty() {
91 Some(self.script_witness.iter().map(|w| w.clone().into()).collect())
92 } else {
93 None
94 },
95 pegin_witness: if !self.pegin_witness.is_empty() {
96 Some(self.pegin_witness.iter().map(|w| w.clone().into()).collect())
97 } else {
98 None
99 },
100 }
101 }
102}
103
104#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
105pub struct InputScriptInfo {
106 pub hex: Option<HexBytes>,
107 pub asm: Option<String>,
108}
109
110pub struct InputScript<'a>(pub &'a Script);
111
112impl<'a> GetInfo<InputScriptInfo> for InputScript<'a> {
113 fn get_info(&self, _network: Network) -> InputScriptInfo {
114 InputScriptInfo {
115 hex: Some(self.0.to_bytes().into()),
116 asm: Some(self.0.asm()),
117 }
118 }
119}
120
121#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
122pub struct InputInfo {
123 pub prevout: Option<String>,
124 pub txid: Option<Txid>,
125 pub vout: Option<u32>,
126 pub script_sig: Option<InputScriptInfo>,
127 pub sequence: Option<u32>,
128
129 pub is_pegin: Option<bool>,
130 pub has_issuance: Option<bool>,
131 #[serde(skip_serializing_if = "Option::is_none")]
132 pub asset_issuance: Option<AssetIssuanceInfo>,
133 #[serde(skip_serializing_if = "Option::is_none")]
134 pub witness: Option<InputWitnessInfo>,
135
136 #[serde(skip_serializing_if = "Option::is_none")]
137 pub pegin_data: Option<PeginDataInfo>,
138}
139
140impl GetInfo<InputInfo> for TxIn {
141 fn get_info(&self, network: Network) -> InputInfo {
142 InputInfo {
143 prevout: Some(format!("{}:{}", self.previous_output.txid, self.previous_output.vout)),
145 txid: Some(self.previous_output.txid),
146 vout: Some(self.previous_output.vout),
147 sequence: Some(self.sequence.to_consensus_u32()),
148 script_sig: Some(GetInfo::get_info(&InputScript(&self.script_sig), network)),
149
150 is_pegin: Some(self.is_pegin),
151 has_issuance: Some(self.has_issuance()),
152 asset_issuance: if self.has_issuance() {
153 Some(self.asset_issuance.get_info(network))
154 } else {
155 None
156 },
157 witness: if !self.witness.is_empty() {
158 Some(self.witness.get_info(network))
159 } else {
160 None
161 },
162 pegin_data: self.pegin_data().map(|p| p.get_info(network)),
163 }
164 }
165}
166
167#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
168pub struct PegoutDataInfo {
169 pub value: u64,
170 pub asset: ConfidentialAssetInfo,
171 pub genesis_hash: bitcoin::BlockHash,
172 pub script_pub_key: hal::tx::OutputScriptInfo,
173 pub extra_data: Vec<HexBytes>,
174}
175
176impl<'tx> GetInfo<PegoutDataInfo> for PegoutData<'tx> {
177 fn get_info(&self, network: Network) -> PegoutDataInfo {
178 PegoutDataInfo {
179 value: self.value,
180 asset: self.asset.get_info(network),
181 genesis_hash: self.genesis_hash,
182 script_pub_key: hal::GetInfo::get_info(
183 &hal::tx::OutputScript(&self.script_pubkey),
184 BTCNET,
185 ),
186 extra_data: self.extra_data.iter().map(|w| HexBytes::from(*w)).collect(),
187 }
188 }
189}
190
191#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
192pub struct OutputWitnessInfo {
193 pub surjection_proof: Option<HexBytes>,
194 pub rangeproof: Option<HexBytes>,
195}
196
197impl GetInfo<OutputWitnessInfo> for TxOutWitness {
198 fn get_info(&self, _network: Network) -> OutputWitnessInfo {
199 OutputWitnessInfo {
200 surjection_proof: self
201 .surjection_proof
202 .as_ref()
203 .map(|p| SurjectionProof::serialize(p).into()),
204 rangeproof: self.rangeproof.as_ref().map(|p| RangeProof::serialize(p).into()),
205 }
206 }
207}
208
209#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
210pub struct OutputScriptInfo {
211 pub hex: Option<HexBytes>,
212 pub asm: Option<String>,
213 #[serde(skip_serializing_if = "Option::is_none", rename = "type")]
214 pub type_: Option<String>,
215 #[serde(skip_serializing_if = "Option::is_none")]
216 pub address: Option<Address>,
217}
218
219pub struct OutputScript<'a>(pub &'a Script);
220
221impl<'a> GetInfo<OutputScriptInfo> for OutputScript<'a> {
222 fn get_info(&self, network: Network) -> OutputScriptInfo {
223 OutputScriptInfo {
224 hex: Some(self.0.to_bytes().into()),
225 asm: Some(self.0.asm()),
226 type_: Some(
227 if self.0.is_p2pk() {
228 "p2pk"
229 } else if self.0.is_p2pkh() {
230 "p2pkh"
231 } else if self.0.is_op_return() {
232 "opreturn"
233 } else if self.0.is_p2sh() {
234 "p2sh"
235 } else if self.0.is_v0_p2wpkh() {
236 "p2wpkh"
237 } else if self.0.is_v0_p2wsh() {
238 "p2wsh"
239 } else {
240 "unknown"
241 }
242 .to_owned(),
243 ),
244 address: Address::from_script(self.0, None, network.address_params()),
245 }
246 }
247}
248
249#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
250pub struct OutputInfo {
251 pub script_pub_key: Option<OutputScriptInfo>,
252
253 pub asset: Option<ConfidentialAssetInfo>,
254 pub value: Option<ConfidentialValueInfo>,
255 pub nonce: Option<ConfidentialNonceInfo>,
256 pub witness: Option<OutputWitnessInfo>,
257 pub is_fee: Option<bool>,
258
259 #[serde(skip_serializing_if = "Option::is_none")]
260 pub pegout_data: Option<PegoutDataInfo>,
261}
262
263impl GetInfo<OutputInfo> for TxOut {
264 fn get_info(&self, network: Network) -> OutputInfo {
265 let is_fee = {
266 let exp_ass = matches!(self.asset, confidential::Asset::Explicit(_));
269 let exp_val = matches!(self.value, confidential::Value::Explicit(_));
270
271 exp_ass && exp_val && self.script_pubkey.is_empty()
272 };
273
274 OutputInfo {
275 script_pub_key: Some(GetInfo::get_info(&OutputScript(&self.script_pubkey), network)),
276 asset: Some(self.asset.get_info(network)),
277 value: Some(self.value.get_info(network)),
278 nonce: Some(self.nonce.get_info(network)),
279 witness: Some(self.witness.get_info(network)),
280 is_fee: Some(is_fee),
281 pegout_data: self.pegout_data().map(|p| p.get_info(network)),
282 }
283 }
284}
285
286#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
287pub struct TransactionInfo {
288 pub txid: Option<Txid>,
289 pub wtxid: Option<Wtxid>,
290 pub hash: Option<Wtxid>,
291 pub size: Option<usize>,
292 pub weight: Option<usize>,
293 pub vsize: Option<usize>,
294 pub version: Option<u32>,
295 pub locktime: Option<elements::LockTime>,
296 pub inputs: Option<Vec<InputInfo>>,
297 pub outputs: Option<Vec<OutputInfo>>,
298}
299
300impl GetInfo<TransactionInfo> for Transaction {
301 fn get_info(&self, network: Network) -> TransactionInfo {
302 TransactionInfo {
303 txid: Some(self.txid()),
304 wtxid: Some(self.wtxid()),
305 hash: Some(self.wtxid()),
306 version: Some(self.version),
307 locktime: Some(self.lock_time),
308 size: Some(serialize(self).len()),
309 weight: Some(self.weight()),
310 vsize: Some(self.weight() / 4),
311 inputs: Some(self.input.iter().map(|i| i.get_info(network)).collect()),
312 outputs: Some(self.output.iter().map(|o| o.get_info(network)).collect()),
313 }
314 }
315}