pcli/command/view/
address.rs

1use anyhow::Result;
2use base64::Engine;
3use rand_core::OsRng;
4use std::str::FromStr;
5
6use penumbra_sdk_keys::{keys::AddressIndex, Address, FullViewingKey};
7
8#[derive(Debug, clap::Parser)]
9pub struct AddressCmd {
10    /// The address to provide information about
11    #[clap(default_value = "0")]
12    address_or_index: String,
13    /// Generate an ephemeral address instead of an indexed one.
14    #[clap(short, long)]
15    ephemeral: bool,
16    /// Output in base64 format, instead of the default bech32.
17    #[clap(long)]
18    base64: bool,
19    /// Use transparent (bech32, 32-byte) address encoding, for compatibility with some IBC chains.
20    #[clap(long)]
21    transparent: bool,
22    /// Print the current FVK
23    #[clap(long)]
24    fvk: bool,
25    /// Generate a payment address from a provided full viewing key
26    #[clap(long)]
27    from_fvk: Option<String>,
28}
29
30impl AddressCmd {
31    /// Determine if this command requires a network sync before it executes.
32    pub fn offline(&self) -> bool {
33        true
34    }
35
36    pub fn exec(&self, fvk: &FullViewingKey) -> Result<()> {
37        let index: Result<u32, _> = self.address_or_index.parse();
38
39        if let Ok(index) = index {
40            //index provided
41
42            let (address, _dtk) = match self.ephemeral {
43                false => fvk.incoming().payment_address(index.into()),
44                true => fvk.incoming().ephemeral_address(OsRng, index.into()),
45            };
46
47            if self.base64 {
48                println!(
49                    "{}",
50                    base64::engine::general_purpose::STANDARD.encode(address.to_vec()),
51                );
52            } else if self.transparent {
53                if index != 0 {
54                    return Err(anyhow::anyhow!(
55                        "warning: index must be 0 to use transparent address encoding"
56                    ));
57                }
58                println!("{}", fvk.incoming().transparent_address());
59            } else {
60                if self.fvk {
61                    eprintln!("🔥 CAUTION: POSSESSION OF THE FOLLOWING FULL VIEWING KEY WILL");
62                    eprintln!("🔥 PROVIDE VISIBILITY TO ALL ACTIVITY ON ITS ASSOCIATED ACCOUNTS.");
63                    eprintln!("🔥 DISTRIBUTE WITH CARE!");
64                    eprintln!("");
65                    println!("{}", fvk);
66                } else if let Some(fvk) = &self.from_fvk {
67                    let (address, _) = FullViewingKey::payment_address(
68                        &FullViewingKey::from_str(&fvk[..])?,
69                        AddressIndex::new(0),
70                    );
71
72                    println!("{}", address);
73                } else {
74                    println!("{}", address);
75                }
76            };
77        } else {
78            //address or nothing provided
79
80            let address: Address = self
81                .address_or_index
82                .parse()
83                .map_err(|_| anyhow::anyhow!("Provided address is invalid."))?;
84
85            match fvk.address_index(&address) {
86                Some(address_index) => println!(
87                    "Address is viewable with this full viewing key. Account index is {0}. {1}",
88                    address_index.account,
89                    match address_index.randomizer != [0u8; 12] {
90                        true => "Address is an IBC deposit address.",
91                        false => "",
92                    }
93                ),
94                None => println!("Address is not viewable with this full viewing key."),
95            }
96        }
97
98        Ok(())
99    }
100}