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