hal_simplicity/
tx.rs

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			// fmt::Display on elements outpoints show the `[elements]` prefix
144			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			// An output is fee if both the asset and the value are explicit
267			// and if the output script is empty.
268			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}