hal_simplicity/actions/simplicity/
mod.rs

1pub mod info;
2pub mod pset;
3pub mod sighash;
4
5pub use info::*;
6pub use sighash::*;
7
8use crate::simplicity::bitcoin::{Amount, Denomination};
9use crate::simplicity::elements::confidential;
10use crate::simplicity::elements::hex::FromHex as _;
11use crate::simplicity::jet::elements::ElementsUtxo;
12
13#[derive(Debug, thiserror::Error)]
14pub enum ParseElementsUtxoError {
15	#[error("invalid format: expected <scriptPubKey>:<asset>:<value>")]
16	InvalidFormat,
17
18	#[error("invalid scriptPubKey hex: {0}")]
19	ScriptPubKeyParsing(elements::hex::Error),
20
21	#[error("invalid asset hex: {0}")]
22	AssetHexParsing(elements::hashes::hex::HexToArrayError),
23
24	#[error("invalid asset commitment hex: {0}")]
25	AssetCommitmentHexParsing(elements::hex::Error),
26
27	#[error("invalid asset commitment: {0}")]
28	AssetCommitmentDecoding(elements::encode::Error),
29
30	#[error("invalid value commitment hex: {0}")]
31	ValueCommitmentHexParsing(elements::hex::Error),
32
33	#[error("invalid value commitment: {0}")]
34	ValueCommitmentDecoding(elements::encode::Error),
35}
36
37pub fn parse_elements_utxo(s: &str) -> Result<ElementsUtxo, ParseElementsUtxoError> {
38	let parts: Vec<&str> = s.split(':').collect();
39	if parts.len() != 3 {
40		return Err(ParseElementsUtxoError::InvalidFormat);
41	}
42	// Parse scriptPubKey
43	let script_pubkey: elements::Script =
44		parts[0].parse().map_err(ParseElementsUtxoError::ScriptPubKeyParsing)?;
45
46	// Parse asset - try as explicit AssetId first, then as confidential commitment
47	let asset = if parts[1].len() == 64 {
48		// 32 bytes = explicit AssetId
49		let asset_id: elements::AssetId =
50			parts[1].parse().map_err(ParseElementsUtxoError::AssetHexParsing)?;
51		confidential::Asset::Explicit(asset_id)
52	} else {
53		// Parse anything except 32 bytes as a confidential commitment (which must be 33 bytes)
54		let commitment_bytes =
55			Vec::from_hex(parts[1]).map_err(ParseElementsUtxoError::AssetCommitmentHexParsing)?;
56		elements::confidential::Asset::from_commitment(&commitment_bytes)
57			.map_err(ParseElementsUtxoError::AssetCommitmentDecoding)?
58	};
59
60	// Parse value - try as BTC decimal first, then as confidential commitment
61	let value = if let Ok(btc_amount) = Amount::from_str_in(parts[2], Denomination::Bitcoin) {
62		// Explicit value in BTC
63		elements::confidential::Value::Explicit(btc_amount.to_sat())
64	} else {
65		// 33 bytes = confidential commitment
66		let commitment_bytes =
67			Vec::from_hex(parts[2]).map_err(ParseElementsUtxoError::ValueCommitmentHexParsing)?;
68		elements::confidential::Value::from_commitment(&commitment_bytes)
69			.map_err(ParseElementsUtxoError::ValueCommitmentDecoding)?
70	};
71
72	Ok(ElementsUtxo {
73		script_pubkey,
74		asset,
75		value,
76	})
77}