snarkos_cli/commands/developer/
decrypt.rs1use snarkvm::{
17 console::{
18 network::{CanaryV0, MainnetV0, Network, TestnetV0},
19 program::Ciphertext,
20 },
21 prelude::{Record, ViewKey},
22};
23
24use anyhow::{Result, bail};
25use clap::Parser;
26use std::str::FromStr;
27use zeroize::Zeroize;
28
29#[derive(Debug, Parser, Zeroize)]
31pub struct Decrypt {
32 #[clap(default_value = "0", long = "network")]
34 pub network: u16,
35 #[clap(short, long)]
37 pub ciphertext: String,
38 #[clap(short, long)]
40 pub view_key: String,
41}
42
43impl Decrypt {
44 pub fn parse(self) -> Result<String> {
45 match self.network {
47 MainnetV0::ID => Self::decrypt_ciphertext::<MainnetV0>(&self.ciphertext, &self.view_key),
48 TestnetV0::ID => Self::decrypt_ciphertext::<TestnetV0>(&self.ciphertext, &self.view_key),
49 CanaryV0::ID => Self::decrypt_ciphertext::<CanaryV0>(&self.ciphertext, &self.view_key),
50 unknown_id => bail!("Unknown network ID ({unknown_id})"),
51 }
52 }
53
54 fn decrypt_ciphertext<N: Network>(ciphertext: &str, view_key: &str) -> Result<String> {
56 let ciphertext_record = Record::<N, Ciphertext<N>>::from_str(ciphertext)?;
58
59 let view_key = ViewKey::<N>::from_str(view_key)?;
61
62 match ciphertext_record.decrypt(&view_key) {
63 Ok(plaintext_record) => Ok(plaintext_record.to_string()),
64 Err(_) => bail!("Invalid view key for the provided record ciphertext"),
65 }
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 use indexmap::IndexMap;
74 use snarkvm::prelude::{
75 Address,
76 Entry,
77 Field,
78 Identifier,
79 Literal,
80 Network,
81 Owner,
82 Plaintext,
83 PrivateKey,
84 Scalar,
85 TestRng,
86 Uniform,
87 ViewKey,
88 };
89
90 type CurrentNetwork = MainnetV0;
91
92 const ITERATIONS: usize = 1000;
93
94 fn construct_ciphertext<N: Network>(
95 view_key: ViewKey<N>,
96 owner: Owner<N, Plaintext<N>>,
97 rng: &mut TestRng,
98 ) -> Result<Record<N, Ciphertext<N>>> {
99 let randomizer = Scalar::rand(rng);
101 let record = Record::<N, Plaintext<N>>::from_plaintext(
102 owner,
103 IndexMap::from_iter(
104 vec![
105 (Identifier::from_str("a")?, Entry::Private(Plaintext::from(Literal::Field(Field::rand(rng))))),
106 (Identifier::from_str("b")?, Entry::Private(Plaintext::from(Literal::Scalar(Scalar::rand(rng))))),
107 ]
108 .into_iter(),
109 ),
110 N::g_scalar_multiply(&randomizer),
111 )?;
112 let ciphertext = record.encrypt(randomizer)?;
114 assert_eq!(record, ciphertext.decrypt(&view_key)?);
116
117 Ok(ciphertext)
118 }
119
120 #[test]
121 fn test_decryption() {
122 let mut rng = TestRng::default();
123
124 for _ in 0..ITERATIONS {
125 let private_key = PrivateKey::<CurrentNetwork>::new(&mut rng).unwrap();
126 let view_key = ViewKey::try_from(private_key).unwrap();
127 let address = Address::try_from(private_key).unwrap();
128
129 let owner = Owner::Private(Plaintext::from(Literal::Address(address)));
131 let ciphertext = construct_ciphertext(view_key, owner, &mut rng).unwrap();
132
133 let expected_plaintext = ciphertext.decrypt(&view_key).unwrap();
135
136 let decrypt = Decrypt { network: 0, ciphertext: ciphertext.to_string(), view_key: view_key.to_string() };
137 let plaintext = decrypt.parse().unwrap();
138
139 assert_eq!(plaintext, expected_plaintext.to_string());
141 }
142 }
143
144 #[test]
145 fn test_failed_decryption() {
146 let mut rng = TestRng::default();
147
148 let incorrect_private_key = PrivateKey::<CurrentNetwork>::new(&mut rng).unwrap();
150 let incorrect_view_key = ViewKey::try_from(incorrect_private_key).unwrap();
151
152 for _ in 0..ITERATIONS {
153 let private_key = PrivateKey::<CurrentNetwork>::new(&mut rng).unwrap();
154 let view_key = ViewKey::try_from(private_key).unwrap();
155 let address = Address::try_from(private_key).unwrap();
156
157 let owner = Owner::Private(Plaintext::from(Literal::Address(address)));
159 let ciphertext = construct_ciphertext::<CurrentNetwork>(view_key, owner, &mut rng).unwrap();
160
161 let decrypt =
163 Decrypt { network: 0, ciphertext: ciphertext.to_string(), view_key: incorrect_view_key.to_string() };
164 assert!(decrypt.parse().is_err());
165 }
166 }
167}