avalanche_types/wallet/p/
mod.rs1pub mod add_permissionless_validator;
2pub mod add_subnet_validator;
3pub mod add_validator;
4pub mod create_chain;
5pub mod create_subnet;
6pub mod export;
7pub mod import;
8
9use std::{cmp, time::SystemTime};
10
11use crate::{
12 errors::{Error, Result},
13 ids::{self, node},
14 jsonrpc::client::p as client_p,
15 key, platformvm, txs, wallet,
16};
17
18impl<T> wallet::Wallet<T>
19where
20 T: key::secp256k1::ReadOnly + key::secp256k1::SignOnly + Clone,
21{
22 #[must_use]
23 pub fn p(&self) -> P<T> {
24 P {
25 inner: self.clone(),
26 }
27 }
28}
29
30#[derive(Clone, Debug)]
31pub struct P<T>
32where
33 T: key::secp256k1::ReadOnly + key::secp256k1::SignOnly + Clone,
34{
35 pub inner: crate::wallet::Wallet<T>,
36}
37
38impl<T> P<T>
39where
40 T: key::secp256k1::ReadOnly + key::secp256k1::SignOnly + Clone,
41{
42 pub async fn balance_with_endpoint(&self, http_rpc: &str) -> Result<u64> {
44 let resp = client_p::get_balance(http_rpc, &self.inner.p_address).await?;
45 let cur_balance = resp
46 .result
47 .expect("unexpected None GetBalanceResult")
48 .balance;
49 Ok(cur_balance)
50 }
51
52 pub async fn balances(&self) -> Result<Vec<u64>> {
55 let mut balances = Vec::new();
56 for http_rpc in self.inner.base_http_urls.iter() {
57 let balance = self.balance_with_endpoint(http_rpc).await?;
58 balances.push(balance);
59 }
60 Ok(balances)
61 }
62
63 pub async fn balance(&self) -> Result<u64> {
65 self.balance_with_endpoint(&self.inner.pick_base_http_url().1)
66 .await
67 }
68
69 pub async fn utxos(&self) -> Result<Vec<txs::utxo::Utxo>> {
72 let resp =
73 client_p::get_utxos(&self.inner.pick_base_http_url().1, &self.inner.p_address).await?;
74 let utxos = resp
75 .result
76 .expect("unexpected None GetUtxosResult")
77 .utxos
78 .expect("unexpected None Utxos");
79 Ok(utxos)
80 }
81
82 pub async fn is_primary_network_validator(&self, node_id: &node::Id) -> Result<bool> {
84 let resp =
85 client_p::get_primary_network_validators(&self.inner.pick_base_http_url().1).await?;
86 let resp = resp
87 .result
88 .expect("unexpected None GetCurrentValidatorResult");
89 let validators = resp.validators.expect("unexpected None vaidators");
90 for validator in validators.iter() {
91 log::info!("listing primary network validator {}", node_id);
92 if validator.node_id.eq(node_id) {
93 return Ok(true);
94 }
95 }
96 Ok(false)
97 }
98
99 pub async fn is_subnet_validator(
101 &self,
102 node_id: &node::Id,
103 subnet_id: &ids::Id,
104 ) -> Result<bool> {
105 let resp = client_p::get_subnet_validators(
106 &self.inner.pick_base_http_url().1,
107 &subnet_id.to_string(),
108 )
109 .await?;
110 let resp = resp
111 .result
112 .expect("unexpected None GetCurrentValidatorResult");
113 let validators = resp.validators.expect("unexpected None vaidators");
114 for validator in validators.iter() {
115 log::info!(
116 "listing subnet validator {} for subnet {}",
117 node_id,
118 subnet_id
119 );
120 if validator.node_id.eq(node_id) {
121 return Ok(true);
122 }
123 }
124 Ok(false)
125 }
126
127 async fn spend(
131 &self,
132 amount: u64,
133 fee: u64,
134 ) -> Result<(
135 Vec<txs::transferable::Input>,
136 Vec<txs::transferable::Output>,
137 Vec<txs::transferable::Output>,
138 Vec<Vec<T>>,
139 )> {
140 let utxos = self.utxos().await?;
141
142 let now_unix = SystemTime::now()
143 .duration_since(SystemTime::UNIX_EPOCH)
144 .expect("unexpected None duration_since")
145 .as_secs();
146
147 let mut ins: Vec<txs::transferable::Input> = Vec::new();
148 let mut returned_outputs: Vec<txs::transferable::Output> = Vec::new();
149 let mut staked_outputs: Vec<txs::transferable::Output> = Vec::new();
150 let mut signers: Vec<Vec<T>> = Vec::new();
151
152 let mut amount_staked: u64 = 0_u64;
154
155 for utxo in utxos.iter() {
157 if amount_staked >= amount {
160 break;
161 }
162
163 if utxo.asset_id != self.inner.avax_asset_id {
165 continue;
166 }
167
168 if utxo.stakeable_lock_out.is_none() {
170 continue;
172 }
173
174 let out = utxo.stakeable_lock_out.clone().unwrap();
176 if out.locktime <= now_unix {
177 continue;
179 }
180
181 let inner = out.clone().transfer_output;
183 let res = self.inner.keychain.spend(&inner, now_unix);
184 if res.is_none() {
185 continue;
187 }
188 let (transfer_input, in_signers) = res.unwrap();
189
190 let mut remaining_value = transfer_input.amount;
191 let amount_to_stake = cmp::min(
192 amount - amount_staked, remaining_value, );
195 amount_staked += amount_to_stake;
196 remaining_value -= amount_to_stake;
197
198 ins.push(txs::transferable::Input {
200 utxo_id: utxo.utxo_id.clone(),
201 asset_id: utxo.asset_id,
202 stakeable_lock_in: Some(platformvm::txs::StakeableLockIn {
203 locktime: out.locktime,
204 transfer_input,
205 }),
206 ..txs::transferable::Input::default()
207 });
208
209 staked_outputs.push(txs::transferable::Output {
211 asset_id: utxo.asset_id,
212 stakeable_lock_out: Some(platformvm::txs::StakeableLockOut {
213 locktime: out.clone().locktime,
214 transfer_output: key::secp256k1::txs::transfer::Output {
215 amount: remaining_value,
216 output_owners: out.clone().transfer_output.output_owners,
217 },
218 }),
219 ..txs::transferable::Output::default()
220 });
221
222 if remaining_value > 0 {
223 returned_outputs.push(txs::transferable::Output {
226 asset_id: utxo.asset_id,
227 stakeable_lock_out: Some(platformvm::txs::StakeableLockOut {
228 locktime: out.clone().locktime,
229 transfer_output: key::secp256k1::txs::transfer::Output {
230 amount: amount_to_stake,
231 output_owners: out.clone().transfer_output.output_owners,
232 },
233 }),
234 ..txs::transferable::Output::default()
235 });
236 }
237
238 signers.push(in_signers);
239 }
240
241 let mut amount_burned = 0_u64;
243
244 for utxo in utxos.iter() {
245 if amount_burned >= fee && amount_staked >= amount {
248 break;
249 }
250
251 if utxo.asset_id != self.inner.avax_asset_id {
253 continue;
254 }
255
256 let (skip, out) = {
257 if utxo.transfer_output.is_some() {
258 let out = utxo.transfer_output.clone().unwrap();
259 (false, out)
260 } else {
261 let inner = utxo.stakeable_lock_out.clone().unwrap();
262 (inner.locktime > now_unix, inner.transfer_output)
263 }
264 };
265 if skip {
268 continue;
269 }
270
271 let res = self.inner.keychain.spend(&out, now_unix);
272 if res.is_none() {
273 continue;
275 }
276 let (transfer_input, in_signers) = res.unwrap();
277
278 let mut remaining_value = transfer_input.amount;
280 let amount_to_burn = cmp::min(
281 fee - amount_burned, remaining_value, );
284 amount_burned += amount_to_burn;
285 remaining_value -= amount_to_burn;
286
287 let amount_to_stake = cmp::min(
288 amount - amount_staked, remaining_value, );
291 amount_staked += amount_to_stake;
292 remaining_value -= amount_to_stake;
293
294 ins.push(txs::transferable::Input {
296 utxo_id: utxo.utxo_id.clone(),
297 asset_id: utxo.asset_id,
298 transfer_input: Some(transfer_input),
299 ..txs::transferable::Input::default()
300 });
301
302 if amount_to_stake > 0 {
303 staked_outputs.push(txs::transferable::Output {
305 asset_id: utxo.asset_id,
306 transfer_output: Some(key::secp256k1::txs::transfer::Output {
307 amount: amount_to_stake,
308 output_owners: key::secp256k1::txs::OutputOwners {
309 locktime: 0,
310 threshold: 1,
311 addresses: vec![self.inner.short_address.clone()],
312 },
313 }),
314 ..txs::transferable::Output::default()
315 });
316 }
317
318 if remaining_value > 0 {
319 returned_outputs.push(txs::transferable::Output {
321 asset_id: utxo.asset_id,
322 transfer_output: Some(key::secp256k1::txs::transfer::Output {
323 amount: remaining_value,
324 output_owners: key::secp256k1::txs::OutputOwners {
325 locktime: 0,
326 threshold: 1,
327 addresses: vec![self.inner.short_address.clone()],
328 },
329 }),
330 ..txs::transferable::Output::default()
331 });
332 }
333
334 signers.push(in_signers);
335 }
336
337 log::info!(
338 "provided keys have balance (unlocked/burned amount so far, locked/staked amount so far) ({}, {}) and need ({}, {})",
339 amount_burned,
340 amount_staked,
341 fee,
342 amount
343 );
344 if amount_burned < fee || amount_staked < amount {
345 return Err(Error::Other {
346 message: format!(
347 "provided keys have balance (unlocked/burned amount so far, locked/staked amount so far) ({}, {}) but need ({}, {})",
348 amount_burned,
349 amount_staked,
350 fee,
351 amount
352 ),
353 retryable: false,
354 });
355 }
356
357 ins.sort();
360 returned_outputs.sort();
361 staked_outputs.sort();
362
363 Ok((ins, returned_outputs, staked_outputs, signers))
364 }
365
366 async fn authorize(
370 &self,
371 subnet_id: ids::Id,
372 ) -> Result<(key::secp256k1::txs::Input, Vec<Vec<T>>)> {
373 log::info!("authorizing subnet {}", subnet_id);
374
375 let tx =
376 client_p::get_tx(&self.inner.pick_base_http_url().1, &subnet_id.to_string()).await?;
377 if let Some(tx_result) = tx.result {
378 let output_owners = tx_result.tx.unsigned_tx.output_owners;
379
380 let now_unix = SystemTime::now()
381 .duration_since(SystemTime::UNIX_EPOCH)
382 .expect("unexpected None duration_since")
383 .as_secs();
384
385 let res = self
386 .inner
387 .keychain
388 .match_threshold(&output_owners, now_unix);
389 let threshold_met = res.is_some();
390 if !threshold_met {
391 return Err(Error::Other {
392 message: "no threshold met, can't sign".to_string(),
393 retryable: false,
394 });
395 }
396 let (sig_indices, keys) = res.unwrap();
397
398 return Ok((
399 key::secp256k1::txs::Input {
400 sig_indices,
402 },
403 vec![keys],
404 ));
405 }
406
407 Err(Error::Other {
408 message: "empty get tx result".to_string(),
409 retryable: false,
410 })
411 }
412
413 #[must_use]
415 pub fn add_validator(&self) -> add_validator::Tx<T> {
416 add_validator::Tx::new(self)
417 }
418
419 #[must_use]
420 pub fn add_permissionless_validator(&self) -> add_permissionless_validator::Tx<T> {
421 add_permissionless_validator::Tx::new(self)
422 }
423
424 #[must_use]
427 pub fn create_subnet(&self) -> create_subnet::Tx<T> {
428 create_subnet::Tx::new(self)
429 }
430
431 #[must_use]
434 pub fn add_subnet_validator(&self) -> add_subnet_validator::Tx<T> {
435 add_subnet_validator::Tx::new(self)
436 }
437
438 #[must_use]
441 pub fn create_chain(&self) -> create_chain::Tx<T> {
442 create_chain::Tx::new(self)
443 }
444
445 #[must_use]
446 pub fn export(&self) -> export::Tx<T> {
447 export::Tx::new(self)
448 }
449
450 #[must_use]
451 pub fn import(&self) -> import::Tx<T> {
452 import::Tx::new(self)
453 }
454}