浏览代码

Add new API `create_account` and `delete_account`

	Also, update an access key representation.
	Added an error option for it.
develop
Silvestr Predko 2 年前
父节点
当前提交
69fb2f78c3
共有 3 个文件被更改,包括 342 次插入29 次删除
  1. 81
    15
      near-client/src/client.rs
  2. 123
    6
      near-client/src/types/near.rs
  3. 138
    8
      near-client/tests/rpc.rs

+ 81
- 15
near-client/src/client.rs 查看文件

@@ -1,5 +1,5 @@
1 1
 use near_primitives::{
2
-    account::id::AccountId,
2
+    account::{id::AccountId, AccessKey, AccessKeyPermission},
3 3
     hash::CryptoHash,
4 4
     types::{Balance, Gas, Nonce},
5 5
 };
@@ -7,10 +7,12 @@ use near_primitives::{
7 7
 use crate::{
8 8
     rpc::client::RpcClient,
9 9
     types::{
10
-        crypto::{ED25519PublicKey, ED25519Signature, Keypair},
10
+        crypto::{ED25519PublicKey, ED25519SecretKey, ED25519Signature, Key, Keypair},
11 11
         near::{
12
-            Action, Chunks, DeployContractAction, FinalExecutionOutcomeView, FinalExecutionStatus,
13
-            FunctionCallAction, SignedTransaction, Transaction, ViewAccessKeyResult, ViewResult,
12
+            Action, AddKeyAction, Chunks, CreateAccountAction, DeleteAccountAction,
13
+            DeployContractAction, FinalExecutionOutcomeView, FinalExecutionStatus,
14
+            FunctionCallAction, SignedTransaction, Transaction, TransferAction, ViewAccessKey,
15
+            ViewResult,
14 16
         },
15 17
     },
16 18
     Error,
@@ -25,7 +27,7 @@ type Result<T> = std::result::Result<T, Error>;
25 27
 pub struct Signer {
26 28
     keypair: Keypair,
27 29
     signer_acc: AccountId,
28
-    key_nonce: Nonce,
30
+    pub key_nonce: Nonce,
29 31
 }
30 32
 
31 33
 impl Signer {
@@ -37,6 +39,21 @@ impl Signer {
37 39
         })
38 40
     }
39 41
 
42
+    // Temporary implementation
43
+    // TODO: refactor [`Signer`]
44
+    pub fn new_v1(
45
+        secret_key: &ED25519SecretKey,
46
+        signer_acc: AccountId,
47
+        key_nonce: Nonce,
48
+    ) -> Result<Self> {
49
+        let sk = ED25519SecretKey::from_bytes(&secret_key.to_bytes())?;
50
+        Ok(Self {
51
+            keypair: Keypair::new(sk),
52
+            signer_acc,
53
+            key_nonce,
54
+        })
55
+    }
56
+
40 57
     pub fn sign(&self, data: &[u8]) -> ED25519Signature {
41 58
         self.keypair.sign(data)
42 59
     }
@@ -45,6 +62,10 @@ impl Signer {
45 62
         self.keypair.public_key
46 63
     }
47 64
 
65
+    pub fn secret_key(&self) -> &ED25519SecretKey {
66
+        &self.keypair.secret_key
67
+    }
68
+
48 69
     pub fn account(&self) -> &AccountId {
49 70
         &self.signer_acc
50 71
     }
@@ -122,10 +143,10 @@ impl NearClient {
122 143
         &'a self,
123 144
         account_id: &'a AccountId,
124 145
         public_key: &'a ED25519PublicKey,
125
-    ) -> Result<ViewAccessKeyResult> {
146
+    ) -> Result<ViewAccessKey> {
126 147
         let result = self
127 148
             .rpc_client
128
-            .request::<ViewAccessKeyResult>(
149
+            .request::<ViewAccessKey>(
129 150
                 "query",
130 151
                 Some(json!({
131 152
                     "request_type": "view_access_key",
@@ -163,19 +184,64 @@ impl NearClient {
163 184
 
164 185
         Call {
165 186
             transaction_info,
166
-            action: Action::from(DeployContractAction { code: wasm }),
187
+            actions: vec![Action::from(DeployContractAction { code: wasm })],
188
+        }
189
+    }
190
+
191
+    pub fn create_account<'a>(
192
+        &'a self,
193
+        signer: &'a mut Signer,
194
+        new_account_id: &'a AccountId,
195
+        new_account_pk: ED25519PublicKey,
196
+        amount: Balance,
197
+    ) -> Call {
198
+        let transaction_info = TransactionInfo::new(self, signer, new_account_id);
199
+        let actions = vec![
200
+            CreateAccountAction {}.into(),
201
+            AddKeyAction {
202
+                public_key: new_account_pk,
203
+                access_key: AccessKey {
204
+                    nonce: 0,
205
+                    permission: AccessKeyPermission::FullAccess,
206
+                },
207
+            }
208
+            .into(),
209
+            TransferAction { deposit: amount }.into(),
210
+        ];
211
+
212
+        Call {
213
+            transaction_info,
214
+            actions,
215
+        }
216
+    }
217
+
218
+    pub fn delete_account<'a>(
219
+        &'a self,
220
+        signer: &'a mut Signer,
221
+        account_id: &'a AccountId,
222
+        beneficiary_acc_id: &'a AccountId,
223
+    ) -> Call {
224
+        let transaction_info = TransactionInfo::new(self, signer, account_id);
225
+        let actions = vec![DeleteAccountAction {
226
+            beneficiary_id: beneficiary_acc_id.clone(),
227
+        }
228
+        .into()];
229
+
230
+        Call {
231
+            transaction_info,
232
+            actions,
167 233
         }
168 234
     }
169 235
 }
170 236
 
171 237
 pub struct Call<'a> {
172 238
     transaction_info: TransactionInfo<'a>,
173
-    action: Action,
239
+    actions: Vec<Action>,
174 240
 }
175 241
 
176 242
 impl<'a> Call<'a> {
177 243
     pub async fn commit<T: DeserializeOwned>(self) -> Result<T> {
178
-        let transaction_bytes = serialize_transaction(&self.transaction_info, self.action).await?;
244
+        let transaction_bytes = serialize_transaction(&self.transaction_info, self.actions).await?;
179 245
 
180 246
         let execution_outcome = self
181 247
             .transaction_info
@@ -206,7 +272,7 @@ impl<'a> Call<'a> {
206 272
     }
207 273
 
208 274
     pub async fn commit_empty(self) -> Result<()> {
209
-        let transaction_bytes = serialize_transaction(&self.transaction_info, self.action).await?;
275
+        let transaction_bytes = serialize_transaction(&self.transaction_info, self.actions).await?;
210 276
         let execution_outcome = self
211 277
             .transaction_info
212 278
             .client
@@ -232,7 +298,7 @@ impl<'a> Call<'a> {
232 298
     }
233 299
 
234 300
     pub async fn commit_async(self) -> Result<String> {
235
-        let transaction_bytes = serialize_transaction(&self.transaction_info, self.action).await?;
301
+        let transaction_bytes = serialize_transaction(&self.transaction_info, self.actions).await?;
236 302
         self.transaction_info
237 303
             .client
238 304
             .rpc_client
@@ -265,7 +331,7 @@ impl<'a> TransactionInfo<'a> {
265 331
 
266 332
 async fn serialize_transaction<'a>(
267 333
     transaction_info: &'a TransactionInfo<'_>,
268
-    action: Action,
334
+    actions: Vec<Action>,
269 335
 ) -> Result<Vec<u8>> {
270 336
     let block_hash = transaction_info.client.block().await?;
271 337
 
@@ -275,7 +341,7 @@ async fn serialize_transaction<'a>(
275 341
         nonce: transaction_info.signer.nonce() + 1,
276 342
         receiver_id: transaction_info.contract_id.clone(),
277 343
         block_hash,
278
-        actions: vec![action],
344
+        actions,
279 345
     };
280 346
 
281 347
     let signed_transaction = sign_transaction(transaction_info.signer, transaction);
@@ -340,7 +406,7 @@ impl<'a> FunctionCallBuilder<'a> {
340 406
 
341 407
         Ok(Call {
342 408
             transaction_info: self.transaction_info,
343
-            action,
409
+            actions: vec![action],
344 410
         })
345 411
     }
346 412
 }

+ 123
- 6
near-client/src/types/near.rs 查看文件

@@ -4,13 +4,16 @@ use borsh::{BorshDeserialize, BorshSerialize};
4 4
 use near_account_id::AccountId;
5 5
 
6 6
 use near_primitives::{
7
-    account::AccessKey,
7
+    account::{AccessKey, AccessKeyPermission},
8 8
     hash::{hash, CryptoHash},
9 9
     logging,
10 10
     serialize::{base64_format, u128_dec_format_compatible},
11
-    types::{Balance, Gas, Nonce},
11
+    types::{Balance, BlockHeight, Gas, Nonce},
12
+};
13
+use serde::{
14
+    de::{self, Visitor},
15
+    Deserialize, Serialize,
12 16
 };
13
-use serde::{Deserialize, Serialize};
14 17
 use serde_json::Value;
15 18
 use std::{
16 19
     borrow::Borrow,
@@ -460,7 +463,121 @@ pub struct ViewResult {
460 463
     pub logs: Vec<String>,
461 464
 }
462 465
 
463
-#[derive(Debug, Clone, Serialize, Deserialize)]
464
-pub struct ViewAccessKeyResult {
465
-    pub nonce: u64,
466
+#[derive(Debug, Clone)]
467
+pub struct ViewAccessKey {
468
+    pub block_hash: CryptoHash,
469
+    pub block_height: BlockHeight,
470
+    pub result: ViewAccessKeyResult,
471
+}
472
+
473
+#[derive(Debug, Clone)]
474
+pub enum ViewAccessKeyResult {
475
+    Ok {
476
+        nonce: Nonce,
477
+        permission: AccessKeyPermissionView,
478
+    },
479
+    Err {
480
+        error: String,
481
+        logs: Vec<String>,
482
+    },
483
+}
484
+
485
+impl From<ViewAccessKey> for Result<Nonce, String> {
486
+    fn from(view: ViewAccessKey) -> Self {
487
+        match view.result {
488
+            ViewAccessKeyResult::Ok { nonce, .. } => Ok(nonce),
489
+            ViewAccessKeyResult::Err { error, .. } => Err(error),
490
+        }
491
+    }
492
+}
493
+
494
+impl<'de> Deserialize<'de> for ViewAccessKey {
495
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
496
+    where
497
+        D: serde::Deserializer<'de>,
498
+    {
499
+        struct Visit;
500
+
501
+        impl<'de> Visitor<'de> for Visit {
502
+            type Value = ViewAccessKey;
503
+
504
+            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
505
+                write!(formatter, "Expecting an key-value map")
506
+            }
507
+
508
+            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
509
+            where
510
+                A: serde::de::MapAccess<'de>,
511
+            {
512
+                let block_hash = map
513
+                    .next_entry::<String, CryptoHash>()?
514
+                    .ok_or_else(|| de::Error::missing_field("block_hash"))
515
+                    .and_then(|(key, block_hash)| {
516
+                        if key != "block_hash" {
517
+                            Err(serde::de::Error::unknown_field(&key, &["block_hash"]))
518
+                        } else {
519
+                            Ok(block_hash)
520
+                        }
521
+                    })?;
522
+
523
+                let block_height = map
524
+                    .next_entry::<String, BlockHeight>()?
525
+                    .ok_or_else(|| de::Error::missing_field("block_height"))
526
+                    .and_then(|(key, block_hash)| {
527
+                        if key != "block_height" {
528
+                            Err(serde::de::Error::unknown_field(&key, &["block_height"]))
529
+                        } else {
530
+                            Ok(block_hash)
531
+                        }
532
+                    })?;
533
+
534
+                let next_key = map.next_key::<String>()?;
535
+
536
+                match next_key.as_deref() {
537
+                    Some("nonce") => {
538
+                        let nonce = map.next_value::<Nonce>()?;
539
+                        let permission = map
540
+                            .next_entry::<String, AccessKeyPermissionView>()?
541
+                            .ok_or_else(|| de::Error::missing_field("permission"))
542
+                            .and_then(|(key, permission)| {
543
+                                if key != "permission" {
544
+                                    Err(serde::de::Error::unknown_field(&key, &["permission"]))
545
+                                } else {
546
+                                    Ok(permission)
547
+                                }
548
+                            })?;
549
+
550
+                        Ok(ViewAccessKey {
551
+                            block_hash,
552
+                            block_height,
553
+                            result: ViewAccessKeyResult::Ok { nonce, permission },
554
+                        })
555
+                    }
556
+                    Some("error") => {
557
+                        let error = map.next_value::<String>()?;
558
+                        let logs = map
559
+                            .next_entry::<String, Vec<String>>()?
560
+                            .ok_or_else(|| serde::de::Error::missing_field("logs"))
561
+                            .and_then(|(key, logs)| {
562
+                                if key != "logs" {
563
+                                    Err(serde::de::Error::unknown_field(&key, &["logs"]))
564
+                                } else {
565
+                                    Ok(logs)
566
+                                }
567
+                            })?;
568
+
569
+                        Ok(ViewAccessKey {
570
+                            block_hash,
571
+                            block_height,
572
+                            result: ViewAccessKeyResult::Err { error, logs },
573
+                        })
574
+                    }
575
+                    Some(field) => Err(serde::de::Error::unknown_field(field, &["nonce", "error"])),
576
+                    None => Err(serde::de::Error::missing_field("nonce or error")),
577
+                }
578
+            }
579
+        }
580
+
581
+        deserializer.deserialize_map(Visit)
582
+    }
466 583
 }

+ 138
- 8
near-client/tests/rpc.rs 查看文件

@@ -1,9 +1,11 @@
1 1
 use git2::{Cred, RemoteCallbacks};
2 2
 use itertools::Itertools;
3
-use near_account_id::AccountId;
4 3
 use near_client::{
5 4
     client::{NearClient, Signer},
6
-    types::crypto::{ED25519PublicKey, ED25519SecretKey, Key},
5
+    types::{
6
+        crypto::{ED25519PublicKey, ED25519SecretKey, Key},
7
+        near::{ViewAccessKey, ViewAccessKeyResult},
8
+    },
7 9
 };
8 10
 use rand::{RngCore, SeedableRng};
9 11
 use rand_chacha::ChaChaRng;
@@ -14,7 +16,7 @@ use std::{
14 16
     path::Path,
15 17
     str::FromStr,
16 18
 };
17
-use workspaces::{network::Sandbox, types::SecretKey, Worker};
19
+use workspaces::{network::Sandbox, types::SecretKey, AccountId, Worker};
18 20
 
19 21
 // auxiliary structs and methods
20 22
 fn near_client(worker: &Worker<Sandbox>) -> anyhow::Result<NearClient> {
@@ -34,11 +36,16 @@ async fn create_signer(
34 36
     let ws_sk = SecretKey::from_str(&ws_secret_key_str)?;
35 37
     let _ = worker.create_tla(signer_acc_id.clone(), ws_sk).await?;
36 38
 
37
-    let access_key_nonce = client.view_access_key(signer_acc_id, &pk).await?.nonce;
38
-
39
-    let signer_acc = Signer::new(&ws_secret_key_str, signer_acc_id.clone(), access_key_nonce)?;
40
-
41
-    Ok(signer_acc)
39
+    client
40
+        .view_access_key(signer_acc_id, &pk)
41
+        .await
42
+        .map(|access_key_view| match access_key_view.result {
43
+            ViewAccessKeyResult::Ok { nonce, .. } => {
44
+                let signer_acc = Signer::new(&ws_secret_key_str, signer_acc_id.clone(), nonce)?;
45
+                Ok(signer_acc)
46
+            }
47
+            ViewAccessKeyResult::Err { error, .. } => Err(anyhow::anyhow!(error)),
48
+        })?
42 49
 }
43 50
 
44 51
 async fn download_contract() -> anyhow::Result<Vec<u8>> {
@@ -262,3 +269,126 @@ async fn fc_with_param_and_result(
262 269
 
263 270
     Ok(())
264 271
 }
272
+
273
+#[tokio::test]
274
+async fn view_access_key_success() -> anyhow::Result<()> {
275
+    let worker = workspaces::sandbox().await?;
276
+    let client = near_client(&worker)?;
277
+    let signer_account_id = AccountId::from_str("alice.test.near").unwrap();
278
+    let mut signer = create_signer(&worker, &client, &signer_account_id).await?;
279
+
280
+    let new_acc = AccountId::from_str("one.alice.test.near")?;
281
+    let secret_key = ED25519SecretKey::from_bytes(&random_bits())?;
282
+    let pk = ED25519PublicKey::from(&secret_key);
283
+
284
+    let _ = client
285
+        .create_account(&mut signer, &new_acc, pk, near_units::parse_near!("3 N"))
286
+        .commit::<serde_json::Value>()
287
+        .await?;
288
+
289
+    let access_key = client.view_access_key(&new_acc, &pk).await?;
290
+    assert!(matches!(
291
+        access_key,
292
+        ViewAccessKey {
293
+            result: ViewAccessKeyResult::Ok { .. },
294
+            ..
295
+        }
296
+    ));
297
+
298
+    Ok(())
299
+}
300
+
301
+#[tokio::test]
302
+async fn view_access_key_failure() -> anyhow::Result<()> {
303
+    let worker = workspaces::sandbox().await?;
304
+    let client = near_client(&worker)?;
305
+
306
+    let new_acc = AccountId::from_str("one.alice.test.near")?;
307
+    let secret_key = ED25519SecretKey::from_bytes(&random_bits())?;
308
+    let pk = ED25519PublicKey::from(&secret_key);
309
+
310
+    let access_key = client.view_access_key(&new_acc, &pk).await?;
311
+    assert!(matches!(
312
+        access_key,
313
+        ViewAccessKey {
314
+            result: ViewAccessKeyResult::Err { .. },
315
+            ..
316
+        }
317
+    ));
318
+
319
+    Ok(())
320
+}
321
+
322
+#[tokio::test]
323
+async fn create_account() -> anyhow::Result<()> {
324
+    let worker = workspaces::sandbox().await?;
325
+    let client = near_client(&worker)?;
326
+    let signer_account_id = AccountId::from_str("alice.test.near").unwrap();
327
+    let mut signer = create_signer(&worker, &client, &signer_account_id).await?;
328
+
329
+    let new_acc = AccountId::from_str("one.alice.test.near")?;
330
+    let secret_key = ED25519SecretKey::from_bytes(&random_bits())?;
331
+    let pk = ED25519PublicKey::from(&secret_key);
332
+
333
+    let _ = client
334
+        .create_account(&mut signer, &new_acc, pk, near_units::parse_near!("3 N"))
335
+        .commit::<serde_json::Value>()
336
+        .await?;
337
+
338
+    let access_key = client.view_access_key(&new_acc, &pk).await?;
339
+    assert!(matches!(
340
+        access_key,
341
+        ViewAccessKey {
342
+            result: ViewAccessKeyResult::Ok { .. },
343
+            ..
344
+        }
345
+    ));
346
+
347
+    Ok(())
348
+}
349
+
350
+#[tokio::test]
351
+async fn delete_account() -> anyhow::Result<()> {
352
+    let worker = workspaces::sandbox().await?;
353
+    let client = near_client(&worker)?;
354
+    let signer_account_id = AccountId::from_str("alice.test.near").unwrap();
355
+    let mut signer = create_signer(&worker, &client, &signer_account_id).await?;
356
+
357
+    let new_acc = AccountId::from_str("one.alice.test.near")?;
358
+    let secret_key = ED25519SecretKey::from_bytes(&random_bits())?;
359
+    let pk = ED25519PublicKey::from(&secret_key);
360
+
361
+    client
362
+        .create_account(&mut signer, &new_acc, pk, near_units::parse_near!("3 N"))
363
+        .commit_empty()
364
+        .await?;
365
+
366
+    let access_key = client.view_access_key(&new_acc, &pk).await?;
367
+
368
+    let nonce = if let ViewAccessKey {
369
+        result: ViewAccessKeyResult::Ok { nonce, .. },
370
+        ..
371
+    } = access_key
372
+    {
373
+        nonce
374
+    } else {
375
+        panic!("Can't view access key for just created account")
376
+    };
377
+
378
+    let mut acc_signer = Signer::new(&secret_key.to_string(), new_acc.clone(), nonce)?;
379
+    client
380
+        .delete_account(&mut acc_signer, &new_acc, &signer_account_id)
381
+        .commit_empty()
382
+        .await?;
383
+
384
+    let access_key = client.view_access_key(&new_acc, &pk).await?;
385
+    assert!(matches!(
386
+        access_key,
387
+        ViewAccessKey {
388
+            result: ViewAccessKeyResult::Err { .. },
389
+            ..
390
+        }
391
+    ));
392
+
393
+    Ok(())
394
+}

正在加载...
取消
保存