1use crate::{types::AssetId, Issuance, LwkError, Script, Transaction, Txid};
2use elements::pset::{Input, PartiallySignedTransaction};
3use elements::{hashes::Hash, BlockHash};
4use lwk_wollet::elements_miniscript::psbt::finalize;
5use lwk_wollet::EC;
6use std::{fmt::Display, sync::Arc};
7
8#[derive(uniffi::Object, PartialEq, Debug, Clone)]
10#[uniffi::export(Display)]
11pub struct Pset {
12 inner: PartiallySignedTransaction,
13}
14
15impl From<PartiallySignedTransaction> for Pset {
16 fn from(inner: PartiallySignedTransaction) -> Self {
17 Self { inner }
18 }
19}
20
21impl Display for Pset {
22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23 write!(f, "{}", self.inner)
24 }
25}
26
27impl AsRef<PartiallySignedTransaction> for Pset {
28 fn as_ref(&self) -> &PartiallySignedTransaction {
29 &self.inner
30 }
31}
32
33#[uniffi::export]
34impl Pset {
35 #[uniffi::constructor]
37 pub fn new(base64: &str) -> Result<Arc<Self>, LwkError> {
38 let inner: PartiallySignedTransaction = base64.trim().parse()?;
39 Ok(Arc::new(Pset { inner }))
40 }
41
42 pub fn finalize(&self) -> Result<Arc<Transaction>, LwkError> {
44 let mut pset = self.inner.clone();
45 finalize(&mut pset, &EC, BlockHash::all_zeros())?;
46 let tx: Transaction = pset.extract_tx()?.into();
47 Ok(Arc::new(tx))
48 }
49
50 pub fn extract_tx(&self) -> Result<Arc<Transaction>, LwkError> {
53 let tx: Transaction = self.inner.extract_tx()?.into();
54 Ok(Arc::new(tx))
55 }
56
57 pub fn combine(&self, other: &Pset) -> Result<Pset, LwkError> {
59 let mut pset = self.inner.clone();
60 pset.merge(other.inner.clone())?;
61 Ok(pset.into())
62 }
63
64 pub fn unique_id(&self) -> Result<Txid, LwkError> {
68 let txid = self.inner.unique_id()?;
69 Ok(txid.into())
70 }
71
72 pub fn inputs(&self) -> Vec<Arc<PsetInput>> {
74 self.inner
75 .inputs()
76 .iter()
77 .map(|i| Arc::new(i.clone().into()))
78 .collect()
79 }
80}
81
82impl Pset {
83 pub(crate) fn inner(&self) -> PartiallySignedTransaction {
84 self.inner.clone()
85 }
86}
87
88#[derive(uniffi::Object, Debug)]
90pub struct PsetInput {
91 inner: Input,
92}
93
94impl From<Input> for PsetInput {
95 fn from(inner: Input) -> Self {
96 Self { inner }
97 }
98}
99
100#[uniffi::export]
101impl PsetInput {
102 pub fn previous_txid(&self) -> Arc<Txid> {
104 Arc::new(self.inner.previous_txid.into())
105 }
106
107 pub fn previous_vout(&self) -> u32 {
109 self.inner.previous_output_index
110 }
111
112 pub fn previous_script_pubkey(&self) -> Option<Arc<Script>> {
114 self.inner
115 .witness_utxo
116 .as_ref()
117 .map(|txout| Arc::new(txout.script_pubkey.clone().into()))
118 }
119
120 pub fn redeem_script(&self) -> Option<Arc<Script>> {
122 self.inner
123 .redeem_script
124 .as_ref()
125 .map(|s| Arc::new(s.clone().into()))
126 }
127
128 pub fn issuance_asset(&self) -> Option<AssetId> {
130 self.inner
131 .has_issuance()
132 .then(|| self.inner.issuance_ids().0.into())
133 }
134
135 pub fn issuance_token(&self) -> Option<AssetId> {
137 self.inner
138 .has_issuance()
139 .then(|| self.inner.issuance_ids().1.into())
140 }
141
142 pub fn issuance(&self) -> Option<Arc<Issuance>> {
144 self.inner
145 .has_issuance()
146 .then(|| Arc::new(lwk_common::Issuance::new(&self.inner).into()))
147 }
148
149 pub fn sighash(&self) -> u32 {
151 self.inner.sighash_type.map(|s| s.to_u32()).unwrap_or(1)
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::Pset;
158
159 #[test]
160 fn pset_roundtrip() {
161 let pset_string =
162 include_str!("../../lwk_jade/test_data/pset_to_be_signed.base64").to_string();
163 let pset = Pset::new(&pset_string).unwrap();
164
165 let tx_expected =
166 include_str!("../../lwk_jade/test_data/pset_to_be_signed_transaction.hex").to_string();
167 let tx = pset.extract_tx().unwrap();
168 assert_eq!(tx_expected, tx.to_string());
169
170 assert_eq!(pset_string, pset.to_string());
171
172 assert_eq!(pset.inputs().len(), tx.inputs().len());
173 let pset_in = &pset.inputs()[0];
174 let tx_in = &tx.inputs()[0];
175 assert_eq!(pset_in.previous_txid(), tx_in.outpoint().txid());
176 assert_eq!(pset_in.previous_vout(), tx_in.outpoint().vout());
177 assert!(pset_in.previous_script_pubkey().is_some());
178 assert!(pset_in.redeem_script().is_none());
179
180 assert!(pset_in.issuance_asset().is_none());
181 assert!(pset_in.issuance_token().is_none());
182 }
183
184 #[test]
185 fn pset_combine() {
186 let psets = lwk_test_util::psets_to_combine().1;
187 let psets: Vec<Pset> = psets.into_iter().map(Into::into).collect();
188 psets[0].finalize().unwrap_err(); let pset01 = psets[0].combine(&psets[1]).unwrap();
191 pset01.finalize().unwrap_err(); let pset012 = pset01.combine(&psets[2]).unwrap();
193 pset012.finalize().unwrap(); }
195
196 #[test]
197 fn pset_unique_id() {
198 let psets = lwk_test_util::psets_to_combine().1;
199 let psets: Vec<Pset> = psets.into_iter().map(Into::into).collect();
200
201 let unique_id = psets[0].unique_id().unwrap();
202 for pset in psets.iter().skip(1) {
203 assert_eq!(unique_id, pset.unique_id().unwrap());
204
205 assert_ne!(unique_id, *pset.extract_tx().unwrap().txid());
207 }
208 }
209}