1pub mod box_selector;
4pub mod derivation_path;
5pub mod ext_pub_key;
6pub mod ext_secret_key;
7pub mod miner_fee;
8pub mod mnemonic;
9#[cfg(feature = "mnemonic_gen")]
10pub mod mnemonic_generator;
11pub mod multi_sig;
12pub mod secret_key;
13pub mod signing;
14pub mod tx_builder;
15pub mod tx_context;
16
17use ergotree_interpreter::sigma_protocol::private_input::PrivateInput;
18use ergotree_interpreter::sigma_protocol::prover::Prover;
19use ergotree_interpreter::sigma_protocol::prover::ProverError;
20use ergotree_interpreter::sigma_protocol::prover::TestProver;
21use secret_key::SecretKey;
22use signing::{sign_transaction, TxSigningError};
23use thiserror::Error;
24
25use crate::chain::ergo_state_context::ErgoStateContext;
26use crate::chain::transaction::reduced::ReducedTransaction;
27use crate::chain::transaction::unsigned::UnsignedTransaction;
28use crate::chain::transaction::Input;
29use crate::chain::transaction::Transaction;
30use crate::ergotree_ir::sigma_protocol::sigma_boolean::SigmaBoolean;
31use crate::wallet::mnemonic::Mnemonic;
32use crate::wallet::multi_sig::{
33 generate_commitments, generate_commitments_for, TransactionHintsBag,
34};
35
36use self::ext_secret_key::ExtSecretKey;
37use self::ext_secret_key::ExtSecretKeyError;
38use self::signing::make_context;
39use self::signing::sign_message;
40use self::signing::sign_reduced_transaction;
41use self::signing::sign_tx_input;
42use self::signing::TransactionContext;
43
44pub struct Wallet {
46 prover: Box<dyn Prover>,
47}
48
49#[allow(missing_docs)]
51#[derive(Error, Debug)]
52pub enum WalletError {
53 #[error("Transaction signing error: {0}")]
54 TxSigningError(#[from] TxSigningError),
55
56 #[error("Prover error: {0}")]
57 ProverError(#[from] ProverError),
58
59 #[error("ExtSecretKeyError: {0}")]
60 ExtSecretKeyError(#[from] ExtSecretKeyError),
61
62 #[error("error parsing SecretKey from ExtSecretKey.bytes")]
63 SecretKeyParsingError,
64}
65
66impl Wallet {
67 pub fn from_mnemonic(
70 mnemonic_phrase: &str,
71 mnemonic_pass: &str,
72 ) -> Result<Wallet, WalletError> {
73 let seed = Mnemonic::to_seed(mnemonic_phrase, mnemonic_pass);
74 let ext_sk = ExtSecretKey::derive_master(seed)?;
75 Ok(Wallet::from_secrets(vec![ext_sk.secret_key()]))
76 }
77
78 pub fn from_secrets(secrets: Vec<SecretKey>) -> Wallet {
80 let prover = TestProver {
81 secrets: secrets.into_iter().map(PrivateInput::from).collect(),
82 };
83 Wallet {
84 prover: Box::new(prover),
85 }
86 }
87
88 pub fn add_secret(&mut self, secret: SecretKey) {
90 self.prover.append_secret(secret.into())
91 }
92
93 pub fn sign_transaction(
95 &self,
96 tx_context: TransactionContext<UnsignedTransaction>,
97 state_context: &ErgoStateContext,
98 tx_hints: Option<&TransactionHintsBag>,
99 ) -> Result<Transaction, WalletError> {
100 sign_transaction(self.prover.as_ref(), tx_context, state_context, tx_hints)
101 .map_err(WalletError::from)
102 }
103
104 pub fn sign_reduced_transaction(
106 &self,
107 reduced_tx: ReducedTransaction,
108 tx_hints: Option<&TransactionHintsBag>,
109 ) -> Result<Transaction, WalletError> {
110 sign_reduced_transaction(self.prover.as_ref(), reduced_tx, tx_hints)
111 .map_err(WalletError::from)
112 }
113
114 pub fn generate_commitments(
116 &self,
117 tx_context: TransactionContext<UnsignedTransaction>,
118 state_context: &ErgoStateContext,
119 ) -> Result<TransactionHintsBag, TxSigningError> {
120 let public_keys: Vec<SigmaBoolean> = self
121 .prover
122 .secrets()
123 .iter()
124 .map(|secret| secret.public_image())
125 .collect();
126 generate_commitments(tx_context, state_context, public_keys.as_slice())
127 }
128
129 pub fn generate_commitments_for_reduced_transaction(
131 &self,
132 reduced_tx: ReducedTransaction,
133 ) -> Result<TransactionHintsBag, TxSigningError> {
134 let mut tx_hints = TransactionHintsBag::empty();
135 let public_keys: Vec<SigmaBoolean> = self
136 .prover
137 .secrets()
138 .iter()
139 .map(|secret| secret.public_image())
140 .collect();
141 for (index, input) in reduced_tx.reduced_inputs().iter().enumerate() {
142 let sigma_prop = input.clone().sigma_prop;
143 let hints = generate_commitments_for(&sigma_prop, &public_keys);
144 tx_hints.add_hints_for_input(index, hints);
145 }
146 Ok(tx_hints)
147 }
148
149 pub fn sign_message(
151 &self,
152 sigma_tree: SigmaBoolean,
153 msg: &[u8],
154 ) -> Result<Vec<u8>, WalletError> {
155 sign_message(self.prover.as_ref(), sigma_tree, msg).map_err(WalletError::from)
156 }
157
158 pub fn sign_tx_input(
160 &self,
161 input_idx: usize,
162 tx_context: TransactionContext<UnsignedTransaction>,
163 state_context: &ErgoStateContext,
164 tx_hints: Option<&TransactionHintsBag>,
165 ) -> Result<Input, WalletError> {
166 let tx = tx_context.spending_tx.clone();
167 let message_to_sign = tx.bytes_to_sign().map_err(TxSigningError::from)?;
168 let mut context =
169 make_context(state_context, &tx_context, input_idx).map_err(TxSigningError::from)?;
170 Ok(sign_tx_input(
171 self.prover.as_ref(),
172 &tx_context,
173 state_context,
174 &mut context,
175 tx_hints,
176 input_idx,
177 message_to_sign.as_slice(),
178 )?)
179 }
180}