-
Notifications
You must be signed in to change notification settings - Fork 129
Expand file tree
/
Copy pathmod.rs
More file actions
294 lines (258 loc) · 8.3 KB
/
mod.rs
File metadata and controls
294 lines (258 loc) · 8.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
use serde::{Deserialize, Serialize};
use std::{
fs::{self, File},
io::Write,
};
use crate::{
print::Print,
signer::{self, Signer},
utils::deprecate_message,
xdr::{
self, FeeBumpTransaction, FeeBumpTransactionEnvelope, SequenceNumber, Transaction,
TransactionEnvelope, TransactionV1Envelope, VecM,
},
Pwd,
};
use network::Network;
pub mod address;
pub mod alias;
pub mod data;
pub mod key;
pub mod locator;
pub mod network;
pub mod sc_address;
pub mod secret;
pub mod sign_with;
pub mod upgrade_check;
pub mod utils;
use crate::config::locator::cli_config_file;
pub use address::UnresolvedMuxedAccount;
pub use alias::UnresolvedContract;
pub use sc_address::UnresolvedScAddress;
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error(transparent)]
Network(#[from] network::Error),
#[error(transparent)]
Secret(#[from] secret::Error),
#[error(transparent)]
Locator(#[from] locator::Error),
#[error(transparent)]
Rpc(#[from] soroban_rpc::Error),
#[error(transparent)]
Signer(#[from] signer::Error),
#[error(transparent)]
SignWith(#[from] sign_with::Error),
#[error(transparent)]
StellarStrkey(#[from] stellar_strkey::DecodeError),
#[error(transparent)]
Address(#[from] address::Error),
}
#[derive(Debug, clap::Args, Clone, Default)]
#[group(skip)]
pub struct Args {
#[command(flatten)]
pub network: network::Args,
#[arg(long, short = 's', visible_alias = "source", env = "STELLAR_ACCOUNT")]
/// Account that where transaction originates from. Alias `source`.
/// Can be an identity (--source alice), a public key (--source GDKW...),
/// a muxed account (--source MDA…), a secret key (--source SC36…),
/// or a seed phrase (--source "kite urban…").
/// If `--build-only` was NOT provided, this key will also be used to
/// sign the final transaction. In that case, trying to sign with public key will fail.
pub source_account: UnresolvedMuxedAccount,
#[command(flatten)]
pub locator: locator::Args,
#[command(flatten)]
pub sign_with: sign_with::Args,
/// ⚠️ Deprecated, use `--inclusion-fee`. Fee amount for transaction, in stroops. 1 stroop = 0.0000001 xlm
#[arg(long, env = "STELLAR_FEE")]
pub fee: Option<u32>,
/// Maximum fee amount for transaction inclusion, in stroops. 1 stroop = 0.0000001 xlm. Defaults to 100 if no arg, env, or config value is provided
#[arg(long, env = "STELLAR_INCLUSION_FEE")]
pub inclusion_fee: Option<u32>,
}
impl Args {
// TODO: Replace PublicKey with MuxedAccount once https://github.com/stellar/rs-stellar-xdr/pull/396 is merged.
pub async fn source_account(&self) -> Result<xdr::MuxedAccount, Error> {
Ok(self
.source_account
.resolve_muxed_account(&self.locator, self.hd_path())
.await?)
}
pub async fn source_signer(&self) -> Result<Signer, Error> {
let print = Print::new(true);
let secret = &self.source_account.resolve_secret(&self.locator)?;
Ok(secret.signer(self.hd_path(), print).await?)
}
pub fn key_pair(&self) -> Result<ed25519_dalek::SigningKey, Error> {
let key = &self.source_account.resolve_secret(&self.locator)?;
Ok(key.key_pair(self.hd_path())?)
}
pub async fn sign(&self, tx: Transaction, quiet: bool) -> Result<TransactionEnvelope, Error> {
let tx_env = TransactionEnvelope::Tx(TransactionV1Envelope {
tx,
signatures: VecM::default(),
});
Ok(self
.sign_with
.sign_tx_env(
&tx_env,
&self.locator,
&self.network.get(&self.locator)?,
quiet,
Some(&self.source_account),
)
.await?)
}
pub async fn sign_fee_bump(
&self,
tx: FeeBumpTransaction,
quiet: bool,
) -> Result<TransactionEnvelope, Error> {
let tx_env = TransactionEnvelope::TxFeeBump(FeeBumpTransactionEnvelope {
tx,
signatures: VecM::default(),
});
Ok(self
.sign_with
.sign_tx_env(
&tx_env,
&self.locator,
&self.network.get(&self.locator)?,
quiet,
Some(&self.source_account),
)
.await?)
}
pub async fn sign_soroban_authorizations(
&self,
tx: &Transaction,
signers: &[Signer],
) -> Result<Option<Transaction>, Error> {
let network = self.get_network()?;
let client = network.rpc_client()?;
let latest_ledger = client.get_latest_ledger().await?.sequence;
let seq_num = latest_ledger + 60; // ~ 5 min
Ok(signer::sign_soroban_authorizations(
tx,
signers,
seq_num,
&network.network_passphrase,
)?)
}
pub fn get_network(&self) -> Result<Network, Error> {
Ok(self.network.get(&self.locator)?)
}
/// Get the inclusion fee if available from args, otherwise fall back to fee,
/// and finally return 100 if nothing is set.
///
/// Precedence is:
/// 1. inclusion_fee (via clap, arg then env var)
/// 2. fee (via clap, arg then env var)
/// 3. default of 100 stroops
pub fn get_inclusion_fee(&self) -> Result<u32, Error> {
if self.fee.is_some() {
deprecate_message(
Print::new(false),
"--fee [env: STELLAR_FEE]",
"Use `--inclusion-fee [env: STELLAR_INCLUSION_FEE]` instead.",
);
}
Ok(self.inclusion_fee.or(self.fee).unwrap_or(100))
}
pub async fn next_sequence_number(
&self,
account: impl Into<xdr::AccountId>,
) -> Result<SequenceNumber, Error> {
let network = self.get_network()?;
let client = network.rpc_client()?;
Ok((client
.get_account(&account.into().to_string())
.await?
.seq_num
.0
+ 1)
.into())
}
pub fn hd_path(&self) -> Option<usize> {
self.sign_with.hd_path
}
}
impl Pwd for Args {
fn set_pwd(&mut self, pwd: &std::path::Path) {
self.locator.set_pwd(pwd);
}
}
#[derive(Debug, clap::Args, Clone, Default)]
#[group(skip)]
pub struct ArgsLocatorAndNetwork {
#[command(flatten)]
pub network: network::Args,
#[command(flatten)]
pub locator: locator::Args,
}
impl ArgsLocatorAndNetwork {
pub fn get_network(&self) -> Result<Network, Error> {
Ok(self.network.get(&self.locator)?)
}
}
#[derive(Serialize, Deserialize, Debug, Default)]
pub struct Config {
pub defaults: Defaults,
}
#[derive(Serialize, Deserialize, Debug, Default)]
pub struct Defaults {
pub network: Option<String>,
pub identity: Option<String>,
pub inclusion_fee: Option<u32>,
}
impl Config {
pub fn new() -> Result<Config, locator::Error> {
let path = cli_config_file()?;
if path.exists() {
let data = fs::read_to_string(&path).map_err(|_| locator::Error::FileRead { path })?;
Ok(toml::from_str(&data)?)
} else {
Ok(Config::default())
}
}
#[must_use]
pub fn set_network(mut self, s: &str) -> Self {
self.defaults.network = Some(s.to_string());
self
}
#[must_use]
pub fn set_identity(mut self, s: &str) -> Self {
self.defaults.identity = Some(s.to_string());
self
}
#[must_use]
pub fn set_inclusion_fee(mut self, uint: u32) -> Self {
self.defaults.inclusion_fee = Some(uint);
self
}
#[must_use]
pub fn unset_identity(mut self) -> Self {
self.defaults.identity = None;
self
}
#[must_use]
pub fn unset_network(mut self) -> Self {
self.defaults.network = None;
self
}
#[must_use]
pub fn unset_inclusion_fee(mut self) -> Self {
self.defaults.inclusion_fee = None;
self
}
pub fn save(&self) -> Result<(), locator::Error> {
let toml_string = toml::to_string(&self)?;
let path = cli_config_file()?;
// Depending on the platform, this function may fail if the full directory path does not exist
let mut file = File::create(locator::ensure_directory(path)?)?;
file.write_all(toml_string.as_bytes())?;
Ok(())
}
}