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