ergo_lib_c_core/
wallet.rs

1//! Wallet-like features
2
3use ergo_lib::ergotree_ir::sigma_protocol::sigma_boolean::SigmaBoolean;
4
5use std::str::FromStr;
6
7use crate::{
8    address::{Address, ConstAddressPtr},
9    collections::ConstCollectionPtr,
10    ergo_box::ErgoBox,
11    ergo_state_ctx::ConstErgoStateContextPtr,
12    reduced::ConstReducedTransactionPtr,
13    secret_key::SecretKey,
14    transaction::{
15        ConstTransactionHintsBagPtr, ConstUnsignedTransactionPtr, Transaction, TransactionHintsBag,
16        TransactionHintsBagPtr, TransactionPtr,
17    },
18    util::{const_ptr_as_ref, mut_ptr_as_mut},
19    Error,
20};
21
22/// A collection of secret keys. This simplified signing by matching the secret keys to the correct inputs automatically.
23pub struct Wallet(ergo_lib::wallet::Wallet);
24pub type WalletPtr = *mut Wallet;
25pub type ConstWalletPtr = *const Wallet;
26
27pub struct MnemonicGenerator(ergo_lib::wallet::mnemonic_generator::MnemonicGenerator);
28pub type MnemonicGeneratorPtr = *mut MnemonicGenerator;
29
30/// Create `MnemonicGenerator` instance
31pub unsafe fn mnemonic_generator(
32    language: &str,
33    strength: u32,
34    mnemonic_generator_out: *mut MnemonicGeneratorPtr,
35) -> Result<(), Error> {
36    let lang = match ergo_lib::wallet::mnemonic_generator::Language::from_str(language) {
37        Ok(lang) => lang,
38        _ => return Err(Error::Misc("Invalid language string".into())),
39    };
40    let mnemonic_generator_inner =
41        ergo_lib::wallet::mnemonic_generator::MnemonicGenerator::new(lang, strength);
42    *mnemonic_generator_out = Box::into_raw(Box::new(MnemonicGenerator(mnemonic_generator_inner)));
43    Ok(())
44}
45
46/// Generate mnemonic sentence using random entropy
47pub unsafe fn mnemonic_generator_generate(
48    mnemonic_generator_ptr: MnemonicGeneratorPtr,
49) -> Result<String, Error> {
50    let mnemonic_generator = mut_ptr_as_mut(mnemonic_generator_ptr, "mnemonic_generator_ptr")?;
51    let mnemonic = match mnemonic_generator.0.generate() {
52        Ok(mnemonic) => mnemonic,
53        Err(error) => return Err(Error::Misc(Box::new(error))),
54    };
55    Ok(mnemonic)
56}
57
58/// Generate mnemonic sentence using provided entropy
59pub unsafe fn mnemonic_generator_generate_from_entropy(
60    mnemonic_generator_ptr: MnemonicGeneratorPtr,
61    entropy_bytes_ptr: *const u8,
62    len: usize,
63) -> Result<String, Error> {
64    let entrophy = std::slice::from_raw_parts(entropy_bytes_ptr, len);
65    let mnemonic_generator = mut_ptr_as_mut(mnemonic_generator_ptr, "mnemonic_generator_ptr")?;
66    let mnemonic = match mnemonic_generator.0.from_entrophy(entrophy.to_vec()) {
67        Ok(mnemonic) => mnemonic,
68        Err(error) => return Err(Error::Misc(Box::new(error))),
69    };
70    Ok(mnemonic)
71}
72
73/// Create `Wallet` instance loading secret key from mnemonic Returns Err if a DlogSecretKey cannot be
74/// parsed from the provided phrase
75pub unsafe fn wallet_from_mnemonic(
76    mnemonic_phrase: &str,
77    mnemonic_pass: &str,
78    wallet_out: *mut WalletPtr,
79) -> Result<(), Error> {
80    let wallet_out = mut_ptr_as_mut(wallet_out, "wallet_out")?;
81    if let Ok(wallet_inner) =
82        ergo_lib::wallet::Wallet::from_mnemonic(mnemonic_phrase, mnemonic_pass)
83    {
84        *wallet_out = Box::into_raw(Box::new(Wallet(wallet_inner)));
85        Ok(())
86    } else {
87        Err(Error::Misc(
88            "Wallet.from_mnemonic: DlogSecretKey can't be parsed from the provided phrase".into(),
89        ))
90    }
91}
92
93/// Create `Wallet` from secrets
94pub unsafe fn wallet_from_secrets(
95    secret_keys_ptr: ConstCollectionPtr<SecretKey>,
96    wallet_out: *mut WalletPtr,
97) -> Result<(), Error> {
98    let secret_keys = const_ptr_as_ref(secret_keys_ptr, "secret_keys_ptr")?;
99    let wallet_out = mut_ptr_as_mut(wallet_out, "wallet_out")?;
100    *wallet_out = Box::into_raw(Box::new(Wallet(ergo_lib::wallet::Wallet::from_secrets(
101        secret_keys.0.clone().into_iter().map(|s| s.0).collect(),
102    ))));
103    Ok(())
104}
105
106/// Add a new secret to the wallets prover
107pub unsafe fn wallet_add_secret(
108    wallet_ptr: WalletPtr,
109    secret_key_ptr: *mut SecretKey,
110) -> Result<(), Error> {
111    let wallet = mut_ptr_as_mut(wallet_ptr, "wallet_ptr")?;
112    let sk = mut_ptr_as_mut(secret_key_ptr, "secret_key_ptr")?;
113    wallet.0.add_secret(sk.0.clone());
114    Ok(())
115}
116
117/// Signs a transaction
118pub unsafe fn wallet_sign_transaction(
119    wallet_ptr: ConstWalletPtr,
120    state_context_ptr: ConstErgoStateContextPtr,
121    unsigned_tx_ptr: ConstUnsignedTransactionPtr,
122    boxes_to_spend_ptr: ConstCollectionPtr<ErgoBox>,
123    data_boxes_ptr: ConstCollectionPtr<ErgoBox>,
124    transaction_out: *mut TransactionPtr,
125) -> Result<(), Error> {
126    let wallet = const_ptr_as_ref(wallet_ptr, "wallet_ptr")?;
127    let state_context = const_ptr_as_ref(state_context_ptr, "state_context_ptr")?;
128    let unsigned_tx = const_ptr_as_ref(unsigned_tx_ptr, "unsigned_tx_ptr")?;
129    let boxes_to_spend = const_ptr_as_ref(boxes_to_spend_ptr, "boxes_to_spend_ptr")?;
130    let data_boxes = const_ptr_as_ref(data_boxes_ptr, "data_boxes_ptr")?;
131    let transaction_out = mut_ptr_as_mut(transaction_out, "transaction_out")?;
132    let boxes_to_spend = boxes_to_spend.0.clone().into_iter().map(|b| b.0).collect();
133    let data_boxes = data_boxes.0.clone().into_iter().map(|b| b.0).collect();
134    let tx_context = ergo_lib::wallet::signing::TransactionContext::new(
135        unsigned_tx.0.clone(),
136        boxes_to_spend,
137        data_boxes,
138    )?;
139    let tx = wallet
140        .0
141        .sign_transaction(tx_context, &state_context.0, None)?;
142    *transaction_out = Box::into_raw(Box::new(Transaction(tx)));
143    Ok(())
144}
145
146/// Signs a multi signature transaction
147pub unsafe fn wallet_sign_transaction_multi(
148    wallet_ptr: ConstWalletPtr,
149    state_context_ptr: ConstErgoStateContextPtr,
150    unsigned_tx_ptr: ConstUnsignedTransactionPtr,
151    boxes_to_spend_ptr: ConstCollectionPtr<ErgoBox>,
152    data_boxes_ptr: ConstCollectionPtr<ErgoBox>,
153    tx_hints_ptr: ConstTransactionHintsBagPtr,
154    transaction_out: *mut TransactionPtr,
155) -> Result<(), Error> {
156    let wallet = const_ptr_as_ref(wallet_ptr, "wallet_ptr")?;
157    let state_context = const_ptr_as_ref(state_context_ptr, "state_context_ptr")?;
158    let unsigned_tx = const_ptr_as_ref(unsigned_tx_ptr, "unsigned_tx_ptr")?;
159    let boxes_to_spend = const_ptr_as_ref(boxes_to_spend_ptr, "boxes_to_spend_ptr")?;
160    let data_boxes = const_ptr_as_ref(data_boxes_ptr, "data_boxes_ptr")?;
161    let tx_hints = const_ptr_as_ref(tx_hints_ptr, "tx_hints_ptr")?;
162    let transaction_out = mut_ptr_as_mut(transaction_out, "transaction_out")?;
163    let boxes_to_spend = boxes_to_spend.0.clone().into_iter().map(|b| b.0).collect();
164    let data_boxes = data_boxes.0.clone().into_iter().map(|b| b.0).collect();
165    let tx_context = ergo_lib::wallet::signing::TransactionContext::new(
166        unsigned_tx.0.clone(),
167        boxes_to_spend,
168        data_boxes,
169    )?;
170    let tx = wallet
171        .0
172        .sign_transaction(tx_context, &state_context.0, Some(&tx_hints.0))?;
173    *transaction_out = Box::into_raw(Box::new(Transaction(tx)));
174    Ok(())
175}
176
177/// Signs a reduced transaction (generating proofs for inputs)
178pub unsafe fn wallet_sign_reduced_transaction(
179    wallet_ptr: ConstWalletPtr,
180    reduced_tx_ptr: ConstReducedTransactionPtr,
181    transaction_out: *mut TransactionPtr,
182) -> Result<(), Error> {
183    let wallet = const_ptr_as_ref(wallet_ptr, "wallet_ptr")?;
184    let reduced_tx = const_ptr_as_ref(reduced_tx_ptr, "reduced_tx_ptr")?;
185    let transaction_out = mut_ptr_as_mut(transaction_out, "transaction_out")?;
186    let tx = wallet
187        .0
188        .sign_reduced_transaction(reduced_tx.0.clone(), None)
189        .map(Transaction)?;
190    *transaction_out = Box::into_raw(Box::new(tx));
191    Ok(())
192}
193
194/// Signs a multi signature reduced transaction
195pub unsafe fn wallet_sign_reduced_transaction_multi(
196    wallet_ptr: ConstWalletPtr,
197    reduced_tx_ptr: ConstReducedTransactionPtr,
198    tx_hints_ptr: ConstTransactionHintsBagPtr,
199    transaction_out: *mut TransactionPtr,
200) -> Result<(), Error> {
201    let wallet = const_ptr_as_ref(wallet_ptr, "wallet_ptr")?;
202    let reduced_tx = const_ptr_as_ref(reduced_tx_ptr, "reduced_tx_ptr")?;
203    let transaction_out = mut_ptr_as_mut(transaction_out, "transaction_out")?;
204    let tx_hints = const_ptr_as_ref(tx_hints_ptr, "tx_hints_ptr")?;
205    let tx = wallet
206        .0
207        .sign_reduced_transaction(reduced_tx.0.clone(), Some(&tx_hints.0))
208        .map(Transaction)?;
209    *transaction_out = Box::into_raw(Box::new(tx));
210    Ok(())
211}
212
213/// Generate Commitments for unsigned tx
214pub unsafe fn wallet_generate_commitments(
215    wallet_ptr: ConstWalletPtr,
216    state_context_ptr: ConstErgoStateContextPtr,
217    unsigned_tx_ptr: ConstUnsignedTransactionPtr,
218    boxes_to_spend_ptr: ConstCollectionPtr<ErgoBox>,
219    data_boxes_ptr: ConstCollectionPtr<ErgoBox>,
220    transaction_hints_bag_out: *mut TransactionHintsBagPtr,
221) -> Result<(), Error> {
222    let wallet = const_ptr_as_ref(wallet_ptr, "wallet_ptr")?;
223    let state_context = const_ptr_as_ref(state_context_ptr, "state_context_ptr")?;
224    let unsigned_tx = const_ptr_as_ref(unsigned_tx_ptr, "unsigned_tx_ptr")?;
225    let boxes_to_spend = const_ptr_as_ref(boxes_to_spend_ptr, "boxes_to_spend_ptr")?;
226    let data_boxes = const_ptr_as_ref(data_boxes_ptr, "data_boxes_ptr")?;
227    let transaction_hints_bag_out = mut_ptr_as_mut(transaction_hints_bag_out, "transaction_out")?;
228    let boxes_to_spend = boxes_to_spend.0.clone().into_iter().map(|b| b.0).collect();
229    let data_boxes = data_boxes.0.clone().into_iter().map(|b| b.0).collect();
230    let tx_context = ergo_lib::wallet::signing::TransactionContext::new(
231        unsigned_tx.0.clone(),
232        boxes_to_spend,
233        data_boxes,
234    )?;
235    *transaction_hints_bag_out = Box::into_raw(Box::new(TransactionHintsBag(
236        wallet
237            .0
238            .generate_commitments(tx_context, &state_context.0)?,
239    )));
240    Ok(())
241}
242
243/// Generate Commitments for reduced transaction
244pub unsafe fn wallet_generate_commitments_for_reduced_transaction(
245    wallet_ptr: ConstWalletPtr,
246    reduced_tx_ptr: ConstReducedTransactionPtr,
247    transaction_hints_bag_out: *mut TransactionHintsBagPtr,
248) -> Result<(), Error> {
249    let wallet = const_ptr_as_ref(wallet_ptr, "wallet_ptr")?;
250    let reduced_tx = const_ptr_as_ref(reduced_tx_ptr, "reduced_tx_ptr")?;
251    let transaction_hints_bag_out = mut_ptr_as_mut(transaction_hints_bag_out, "transaction_out")?;
252    *transaction_hints_bag_out = Box::into_raw(Box::new(TransactionHintsBag(
253        wallet
254            .0
255            .generate_commitments_for_reduced_transaction(reduced_tx.0.clone())?,
256    )));
257    Ok(())
258}
259
260/// Represents the signature of a signed message
261pub struct SignedMessage(Vec<u8>);
262pub type SignedMessagePtr = *mut SignedMessage;
263pub type ConstSignedMessagePtr = *const SignedMessage;
264
265/// Sign an arbitrary message using a P2PK address
266pub unsafe fn wallet_sign_message_using_p2pk(
267    wallet_ptr: ConstWalletPtr,
268    address_ptr: ConstAddressPtr,
269    message_ptr: *const u8,
270    message_length: usize,
271    signed_message_out: *mut SignedMessagePtr,
272) -> Result<(), Error> {
273    let wallet = const_ptr_as_ref(wallet_ptr, "wallet_ptr")?;
274    let address = const_ptr_as_ref(address_ptr, "address_ptr")?;
275    let msg = std::slice::from_raw_parts(message_ptr, message_length);
276    let signed_message_out = mut_ptr_as_mut(signed_message_out, "signed_message_out")?;
277    if let Address(ergo_lib::ergotree_ir::chain::address::Address::P2Pk(d)) = address {
278        let sb = SigmaBoolean::from(d.clone());
279        let sig = wallet.0.sign_message(sb, msg)?;
280        *signed_message_out = Box::into_raw(Box::new(SignedMessage(sig)));
281        Ok(())
282    } else {
283        Err(Error::Misc(
284            "wallet::sign_message_using_p2pk: Address:P2Pk expected".into(),
285        ))
286    }
287}
288
289/// Verify that the signature is presented to satisfy SigmaProp conditions.
290pub unsafe fn verify_signature(
291    address_ptr: ConstAddressPtr,
292    message_ptr: *const u8,
293    message_length: usize,
294    signed_message_ptr: ConstSignedMessagePtr,
295) -> Result<bool, Error> {
296    let address = const_ptr_as_ref(address_ptr, "address_ptr")?;
297    let msg = std::slice::from_raw_parts(message_ptr, message_length);
298    let signed_message = const_ptr_as_ref(signed_message_ptr, "signed_message_ptr")?;
299
300    if let Address(ergo_lib::ergotree_ir::chain::address::Address::P2Pk(d)) = address {
301        let sb = SigmaBoolean::from(d.clone());
302        let res = ergo_lib::ergotree_interpreter::sigma_protocol::verifier::verify_signature(
303            sb,
304            msg,
305            signed_message.0.as_slice(),
306        )?;
307        Ok(res)
308    } else {
309        Err(Error::Misc(
310            "wallet::verify_signature: Address:P2Pk expected".into(),
311        ))
312    }
313}