1use std::any::Any;
5
6use crate::key_management::{Key, KeyInfo};
7use crate::message::SignedMessage;
8use crate::rpc::{ApiPaths, Ctx, Permission, RpcMethod, ServerError};
9use crate::shim::{
10 address::Address,
11 crypto::{Signature, SignatureType},
12 econ::TokenAmount,
13 message::Message,
14 state_tree::StateTree,
15};
16use enumflags2::BitFlags;
17use fvm_ipld_blockstore::Blockstore;
18
19pub enum WalletBalance {}
20impl RpcMethod<1> for WalletBalance {
21 const NAME: &'static str = "Filecoin.WalletBalance";
22 const PARAM_NAMES: [&'static str; 1] = ["address"];
23 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
24 const PERMISSION: Permission = Permission::Read;
25 const DESCRIPTION: Option<&'static str> = Some("Returns the balance of a wallet.");
26
27 type Params = (Address,);
28 type Ok = TokenAmount;
29
30 async fn handle(
31 ctx: Ctx<impl Blockstore>,
32 (address,): Self::Params,
33 ) -> Result<Self::Ok, ServerError> {
34 let heaviest_ts = ctx.chain_store().heaviest_tipset();
35 let cid = heaviest_ts.parent_state();
36
37 Ok(StateTree::new_from_root(ctx.store_owned(), cid)?
38 .get_actor(&address)?
39 .map(|it| it.balance.clone().into())
40 .unwrap_or_default())
41 }
42}
43
44pub enum WalletDefaultAddress {}
45impl RpcMethod<0> for WalletDefaultAddress {
46 const NAME: &'static str = "Filecoin.WalletDefaultAddress";
47 const PARAM_NAMES: [&'static str; 0] = [];
48 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
49 const PERMISSION: Permission = Permission::Read;
50
51 type Params = ();
52 type Ok = Option<Address>;
53
54 async fn handle(ctx: Ctx<impl Blockstore>, (): Self::Params) -> Result<Self::Ok, ServerError> {
55 let keystore = ctx.keystore.read();
56 Ok(crate::key_management::get_default(&keystore)?)
57 }
58}
59
60pub enum WalletExport {}
61impl RpcMethod<1> for WalletExport {
62 const NAME: &'static str = "Filecoin.WalletExport";
63 const PARAM_NAMES: [&'static str; 1] = ["address"];
64 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
65 const PERMISSION: Permission = Permission::Admin;
66
67 type Params = (Address,);
68 type Ok = KeyInfo;
69
70 async fn handle(
71 ctx: Ctx<impl Blockstore>,
72 (address,): Self::Params,
73 ) -> Result<Self::Ok, ServerError> {
74 let keystore = ctx.keystore.read();
75 let key_info = crate::key_management::export_key_info(&address, &keystore)?;
76 Ok(key_info)
77 }
78}
79
80pub enum WalletHas {}
81impl RpcMethod<1> for WalletHas {
82 const NAME: &'static str = "Filecoin.WalletHas";
83 const PARAM_NAMES: [&'static str; 1] = ["address"];
84 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
85 const PERMISSION: Permission = Permission::Write;
86 const DESCRIPTION: Option<&'static str> =
87 Some("Indicates whether the given address exists in the wallet.");
88
89 type Params = (Address,);
90 type Ok = bool;
91
92 async fn handle(
93 ctx: Ctx<impl Blockstore>,
94 (address,): Self::Params,
95 ) -> Result<Self::Ok, ServerError> {
96 let keystore = ctx.keystore.read();
97 Ok(crate::key_management::find_key(&address, &keystore).is_ok())
98 }
99}
100
101pub enum WalletImport {}
102impl RpcMethod<1> for WalletImport {
103 const NAME: &'static str = "Filecoin.WalletImport";
104 const PARAM_NAMES: [&'static str; 1] = ["key"];
105 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
106 const PERMISSION: Permission = Permission::Admin;
107
108 type Params = (KeyInfo,);
109 type Ok = Address;
110
111 async fn handle(
112 ctx: Ctx<impl Blockstore>,
113 (key_info,): Self::Params,
114 ) -> Result<Self::Ok, ServerError> {
115 let key = Key::try_from(key_info)?;
116
117 let addr = format!("wallet-{}", key.address);
118
119 let mut keystore = ctx.keystore.write();
120 keystore.put(&addr, key.key_info)?;
121 Ok(key.address)
122 }
123}
124
125pub enum WalletList {}
126impl RpcMethod<0> for WalletList {
127 const NAME: &'static str = "Filecoin.WalletList";
128 const PARAM_NAMES: [&'static str; 0] = [];
129 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
130 const PERMISSION: Permission = Permission::Write;
131 const DESCRIPTION: Option<&'static str> =
132 Some("Returns a list of all addresses in the wallet.");
133
134 type Params = ();
135 type Ok = Vec<Address>;
136
137 async fn handle(ctx: Ctx<impl Blockstore>, (): Self::Params) -> Result<Self::Ok, ServerError> {
138 let keystore = ctx.keystore.read();
139 Ok(crate::key_management::list_addrs(&keystore)?)
140 }
141}
142
143pub enum WalletNew {}
144impl RpcMethod<1> for WalletNew {
145 const NAME: &'static str = "Filecoin.WalletNew";
146 const PARAM_NAMES: [&'static str; 1] = ["signature_type"];
147 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
148 const PERMISSION: Permission = Permission::Write;
149
150 type Params = (SignatureType,);
151 type Ok = Address;
152
153 async fn handle(
154 ctx: Ctx<impl Blockstore>,
155 (signature_type,): Self::Params,
156 ) -> Result<Self::Ok, ServerError> {
157 let mut keystore = ctx.keystore.write();
158 let key = crate::key_management::generate_key(signature_type)?;
159
160 let addr = format!("wallet-{}", key.address);
161 keystore.put(&addr, key.key_info.clone())?;
162 let value = keystore.get("default");
163 if value.is_err() {
164 keystore.put("default", key.key_info)?
165 }
166
167 Ok(key.address)
168 }
169}
170
171pub enum WalletSetDefault {}
172impl RpcMethod<1> for WalletSetDefault {
173 const NAME: &'static str = "Filecoin.WalletSetDefault";
174 const PARAM_NAMES: [&'static str; 1] = ["address"];
175 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
176 const PERMISSION: Permission = Permission::Write;
177
178 type Params = (Address,);
179 type Ok = ();
180
181 async fn handle(
182 ctx: Ctx<impl Blockstore>,
183 (address,): Self::Params,
184 ) -> Result<Self::Ok, ServerError> {
185 let mut keystore = ctx.keystore.write();
186 let addr_string = format!("wallet-{address}");
187 let key_info = keystore.get(&addr_string)?;
188 keystore.remove("default")?; keystore.put("default", key_info)?;
190 Ok(())
191 }
192}
193
194pub enum WalletSign {}
195impl RpcMethod<2> for WalletSign {
196 const NAME: &'static str = "Filecoin.WalletSign";
197 const PARAM_NAMES: [&'static str; 2] = ["address", "message"];
198 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
199 const PERMISSION: Permission = Permission::Sign;
200 const DESCRIPTION: Option<&'static str> =
201 Some("Signs the given bytes using the specified address.");
202
203 type Params = (Address, Vec<u8>);
204 type Ok = Signature;
205
206 async fn handle(
207 ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
208 (address, message): Self::Params,
209 ) -> Result<Self::Ok, ServerError> {
210 let heaviest_tipset = ctx.chain_store().heaviest_tipset();
211 let key_addr = ctx
212 .state_manager
213 .resolve_to_key_addr(&address, &heaviest_tipset)
214 .await?;
215 let keystore = &mut *ctx.keystore.write();
216 let key = match crate::key_management::find_key(&key_addr, keystore) {
217 Ok(key) => key,
218 Err(_) => {
219 let key_info = crate::key_management::try_find(&key_addr, keystore)?;
220 Key::try_from(key_info)?
221 }
222 };
223
224 let sig = crate::key_management::sign(
225 *key.key_info.key_type(),
226 key.key_info.private_key(),
227 &message,
228 )?;
229
230 Ok(sig)
231 }
232}
233
234pub enum WalletSignMessage {}
235impl RpcMethod<2> for WalletSignMessage {
236 const NAME: &'static str = "Filecoin.WalletSignMessage";
237 const PARAM_NAMES: [&'static str; 2] = ["address", "message"];
238 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
239 const PERMISSION: Permission = Permission::Sign;
240 const DESCRIPTION: Option<&'static str> =
241 Some("Signs the given message using the specified address.");
242
243 type Params = (Address, Message);
244 type Ok = SignedMessage;
245
246 async fn handle(
247 ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
248 (address, message): Self::Params,
249 ) -> Result<Self::Ok, ServerError> {
250 let ts = ctx.chain_store().heaviest_tipset();
251 let key_addr = ctx
252 .state_manager
253 .resolve_to_deterministic_address(address, ts)
254 .await?;
255
256 let keystore = &mut *ctx.keystore.write();
257 let key = match crate::key_management::find_key(&key_addr, keystore) {
258 Ok(key) => key,
259 Err(_) => {
260 let key_info = crate::key_management::try_find(&key_addr, keystore)?;
261 Key::try_from(key_info)?
262 }
263 };
264
265 let sig = crate::key_management::sign(
266 *key.key_info.key_type(),
267 key.key_info.private_key(),
268 message.cid().to_bytes().as_slice(),
269 )?;
270
271 let smsg = SignedMessage::new_from_parts(message, sig).expect(
274 "This is infallible. We just generated the signature, so it cannot be invalid.",
275 );
276
277 Ok(smsg)
278 }
279}
280
281pub enum WalletValidateAddress {}
282impl RpcMethod<1> for WalletValidateAddress {
283 const NAME: &'static str = "Filecoin.WalletValidateAddress";
284 const PARAM_NAMES: [&'static str; 1] = ["address"];
285 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
286 const PERMISSION: Permission = Permission::Read;
287
288 type Params = (String,);
289 type Ok = Address;
290
291 async fn handle(_: Ctx<impl Any>, (s,): Self::Params) -> Result<Self::Ok, ServerError> {
292 Ok(s.parse()?)
293 }
294}
295
296pub enum WalletVerify {}
297impl RpcMethod<3> for WalletVerify {
298 const NAME: &'static str = "Filecoin.WalletVerify";
299 const PARAM_NAMES: [&'static str; 3] = ["address", "message", "signature"];
300 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
301 const PERMISSION: Permission = Permission::Read;
302
303 type Params = (Address, Vec<u8>, Signature);
304 type Ok = bool;
305
306 async fn handle(
307 _: Ctx<impl Any>,
308 (address, message, signature): Self::Params,
309 ) -> Result<Self::Ok, ServerError> {
310 Ok(signature.verify(&message, &address).is_ok())
311 }
312}
313
314pub enum WalletDelete {}
315impl RpcMethod<1> for WalletDelete {
316 const NAME: &'static str = "Filecoin.WalletDelete";
317 const PARAM_NAMES: [&'static str; 1] = ["address"];
318 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
319 const PERMISSION: Permission = Permission::Write;
320
321 type Params = (Address,);
322 type Ok = ();
323
324 async fn handle(
325 ctx: Ctx<impl Blockstore>,
326 (address,): Self::Params,
327 ) -> Result<Self::Ok, ServerError> {
328 let mut keystore = ctx.keystore.write();
329 crate::key_management::remove_key(&address, &mut keystore)?;
330 Ok(())
331 }
332}
333
334#[cfg(test)]
335mod tests {
336 use crate::{KeyStore, shim::crypto::SignatureType};
337
338 #[tokio::test]
339 async fn wallet_delete_existing_key() {
340 let key = crate::key_management::generate_key(SignatureType::Secp256k1).unwrap();
341 let addr = format!("wallet-{}", key.address);
342 let mut keystore = KeyStore::new(crate::KeyStoreConfig::Memory).unwrap();
343 keystore.put(&addr, key.key_info.clone()).unwrap();
344 crate::key_management::remove_key(&key.address, &mut keystore).unwrap();
345 assert!(keystore.get(&addr).is_err());
346 }
347
348 #[tokio::test]
349 async fn wallet_delete_empty_keystore() {
350 let key = crate::key_management::generate_key(SignatureType::Secp256k1).unwrap();
351 let mut keystore = KeyStore::new(crate::KeyStoreConfig::Memory).unwrap();
352 assert!(crate::key_management::remove_key(&key.address, &mut keystore).is_err());
353 }
354
355 #[tokio::test]
356 async fn wallet_delete_non_existent_key() {
357 let key1 = crate::key_management::generate_key(SignatureType::Secp256k1).unwrap();
358 let key2 = crate::key_management::generate_key(SignatureType::Secp256k1).unwrap();
359 let addr1 = format!("wallet-{}", key1.address);
360 let mut keystore = KeyStore::new(crate::KeyStoreConfig::Memory).unwrap();
361 keystore.put(&addr1, key1.key_info.clone()).unwrap();
362 assert!(crate::key_management::remove_key(&key2.address, &mut keystore).is_err());
363 }
364
365 #[tokio::test]
366 async fn wallet_delete_default_key() {
367 let key1 = crate::key_management::generate_key(SignatureType::Secp256k1).unwrap();
368 let key2 = crate::key_management::generate_key(SignatureType::Secp256k1).unwrap();
369 let addr1 = format!("wallet-{}", key1.address);
370 let addr2 = format!("wallet-{}", key2.address);
371 let mut keystore = KeyStore::new(crate::KeyStoreConfig::Memory).unwrap();
372 keystore.put(&addr1, key1.key_info.clone()).unwrap();
373 keystore.put(&addr2, key2.key_info.clone()).unwrap();
374 keystore.put("default", key2.key_info.clone()).unwrap();
375 crate::key_management::remove_key(&key2.address, &mut keystore).unwrap();
376 assert!(
377 crate::key_management::get_default(&keystore)
378 .unwrap()
379 .is_none()
380 );
381 }
382}