hal_elements/
tx.rs

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