浏览代码

Add initial source code

develop
Predko Silvestr 2 年前
父节点
当前提交
692fd20dae

+ 5
- 0
.gitignore 查看文件

@@ -0,0 +1,5 @@
1
+/pkg
2
+/target
3
+/Cargo.lock
4
+/js-client/node_modules/
5
+/js-client/dist/

+ 3
- 0
.vscode/settings.json 查看文件

@@ -0,0 +1,3 @@
1
+{
2
+    "rust-analyzer.cargo.target": "wasm32-unknown-unknown"
3
+}

+ 10
- 0
Cargo.toml 查看文件

@@ -0,0 +1,10 @@
1
+[workspace]
2
+members = ["wasm-client", "near-client"]
3
+
4
+[profile.release]
5
+codegen-units = 1
6
+opt-level = "z"
7
+lto = true
8
+debug = false
9
+panic = "abort"
10
+overflow-checks = true

+ 13
- 0
js-client/assets/index.html 查看文件

@@ -0,0 +1,13 @@
1
+<!DOCTYPE html>
2
+<html>
3
+
4
+<head>
5
+    <meta charset="utf-8" />
6
+    <title>Demo Application</title>
7
+</head>
8
+
9
+<body>
10
+    <script src="main.js"></script>
11
+</body>
12
+
13
+</html>

+ 8642
- 0
js-client/package-lock.json
文件差异内容过多而无法显示
查看文件


+ 29
- 0
js-client/package.json 查看文件

@@ -0,0 +1,29 @@
1
+{
2
+  "name": "js-client",
3
+  "version": "1.0.0",
4
+  "description": "",
5
+  "private": true,
6
+  "scripts": {
7
+    "test": "echo \"Error: no test specified\" && exit 1",
8
+    "build": "webpack",
9
+    "serve": "webpack serve"
10
+  },
11
+  "keywords": [],
12
+  "author": "",
13
+  "license": "ISC",
14
+  "devDependencies": {
15
+    "@wasm-tool/wasm-pack-plugin": "^1.6.0",
16
+    "copy-webpack-plugin": "^11.0.0",
17
+    "html-webpack-plugin": "^5.5.0",
18
+    "rimraf": "^3.0.2",
19
+    "webpack": "^5.74.0",
20
+    "webpack-cli": "^4.10.0",
21
+    "webpack-dev-server": "^4.10.0"
22
+  },
23
+  "dependencies": {
24
+    "assert": "^2.0.0",
25
+    "buffer": "^6.0.3",
26
+    "lodash": "^4.17.21",
27
+    "near-api-js": "^0.45.1"
28
+  }
29
+}

+ 74
- 0
js-client/src/index.js 查看文件

@@ -0,0 +1,74 @@
1
+import _ from 'lodash';
2
+import { connect, Contract, WalletConnection, utils, providers, keyStores } from 'near-api-js'
3
+// import init, { greet } from 'client/client.js';
4
+
5
+
6
+function component() {
7
+    const element = document.createElement('div');
8
+
9
+    // Lodash, currently included via a script, is required for this line to work
10
+    element.innerHTML = _.join(['Hello', 'webpack'], ' ');
11
+
12
+    return element;
13
+}
14
+
15
+document.body.appendChild(component());
16
+
17
+async function near_login() {
18
+    const { greet, send_transaction } = await import('client/wasm_client.js');
19
+    const keyStore = new keyStores.BrowserLocalStorageKeyStore();
20
+    const connectionConfig = {
21
+        networkId: "testnet",
22
+        keyStore,
23
+        nodeUrl: "https://rpc.testnet.near.org",
24
+        walletUrl: "https://wallet.testnet.near.org",
25
+        helperUrl: "https://helper.testnet.near.org",
26
+        explorerUrl: "https://explorer.testnet.near.org",
27
+    };
28
+
29
+    let nearConnection = await connect(connectionConfig);
30
+    let walletConnection = new WalletConnection(nearConnection);
31
+
32
+    if (!walletConnection.isSignedIn()) {
33
+        await walletConnection.requestSignIn("drxgheootp.testnet", "Sample App");
34
+    }
35
+
36
+    let keypair = await keyStore.getKey("testnet", "drxgheootp.testnet");
37
+    let account = await nearConnection.account("drxgheootp.testnet");
38
+
39
+    console.log("KeyPair: ", keypair.getPublicKey().toString());
40
+    console.log("Account: ", account);
41
+
42
+    let accessKeys = await account.getAccessKeys();
43
+    for (let key of accessKeys) {
44
+        console.log("Key: ", key.public_key);
45
+    }
46
+
47
+    let found = accessKeys.find(key => key.public_key == keypair.getPublicKey());
48
+    console.log("Found: ", found);
49
+
50
+    // if (keypair == null) {
51
+    //     console.log("Here");
52
+    //     let new_keypair = utils.KeyPairEd25519.fromRandom();
53
+    //     await keyStore.setKey("testnet", account.accountId, new_keypair);
54
+    //     let final_execution_outcome = await account.addKey(new_keypair.getPublicKey());
55
+    //     console.log("Execution Outcome: ", final_execution_outcome);
56
+    //     keypair = new_keypair;
57
+    // }
58
+
59
+    // let accessKeys = await account.getAccessKeys();
60
+    // let found = accessKeys.find(key => key.public_key == keypair.getPublicKey());
61
+
62
+    // console.log("Found: ", found);
63
+
64
+    // if (found == undefined) {
65
+    //     let final_execution_outcome = await account.addKey(keypair.getPublicKey());
66
+    //     console.log("Execution Outcome: ", final_execution_outcome);
67
+    // }
68
+
69
+    console.log("Keypair PubKey: ", keypair.getPublicKey().toString());
70
+    console.log("Keypair: ", keypair.toString());
71
+    await send_transaction(keypair.toString(), BigInt(found.access_key.nonce), "drxgheootp.testnet");
72
+}
73
+
74
+near_login();

+ 47
- 0
js-client/webpack.config.js 查看文件

@@ -0,0 +1,47 @@
1
+const path = require('path');
2
+const webpack = require('webpack');
3
+const HtmlWebpackPlugin = require('html-webpack-plugin');
4
+
5
+module.exports = {
6
+    entry: './src/index.js',
7
+    output: {
8
+        filename: 'main.js',
9
+        path: path.resolve(__dirname, 'dist'),
10
+        clean: true,
11
+    },
12
+    resolve: {
13
+        alias: {
14
+            'client': path.join(__dirname, '../pkg/')
15
+        },
16
+        fallback: {
17
+            "buffer": require.resolve("buffer")
18
+        }
19
+    },
20
+    devServer: {
21
+        static: {
22
+            directory: path.join(__dirname, 'dist'),
23
+        },
24
+        compress: true,
25
+        port: 9000,
26
+        client: {
27
+            logging: 'none',
28
+            progress: true,
29
+            overlay: {
30
+                errors: true,
31
+                warnings: false,
32
+            }
33
+        },
34
+    },
35
+    plugins: [
36
+        new webpack.ProvidePlugin({
37
+            Buffer: ['buffer', 'Buffer'],
38
+        }),
39
+        new HtmlWebpackPlugin({
40
+            title: 'My App',
41
+            template: 'assets/index.html'
42
+        })
43
+    ],
44
+    experiments: {
45
+        asyncWebAssembly: true
46
+    }
47
+};

+ 26
- 0
near-client/Cargo.toml 查看文件

@@ -0,0 +1,26 @@
1
+[package]
2
+name = "near-client"
3
+version = "0.1.0"
4
+edition = "2021"
5
+authors = ["silvestr@relayz.io"]
6
+description = """
7
+"""
8
+
9
+[lib]
10
+crate-type = ["cdylib", "rlib"]
11
+
12
+[dependencies]
13
+async-trait = "0.1"
14
+base64 = { version = "0.13" }
15
+bs58 = "0.4"
16
+borsh = "0.9"
17
+ed25519-dalek = "1"
18
+itertools = "0.10"
19
+near-account-id = "0.14"
20
+near-primitives = { version = "0.14", package = "near-primitives-core" }
21
+near-units = "0.2"
22
+reqwest = { version = "0.11", features = ["json"] }
23
+serde = { version = "1", default-features = false, features = ["derive"] }
24
+serde_json = { version = "1", default-features = false }
25
+thiserror = "1"
26
+url = "2"

+ 230
- 0
near-client/src/client.rs 查看文件

@@ -0,0 +1,230 @@
1
+use near_primitives::{
2
+    account::id::AccountId,
3
+    hash::CryptoHash,
4
+    types::{Balance, Gas, Nonce},
5
+};
6
+
7
+use crate::{
8
+    rpc::client::RpcClient,
9
+    types::{
10
+        crypto::{ED25519PublicKey, ED25519Signature, Keypair},
11
+        near::{
12
+            Action, Chunks, FinalExecutionOutcomeView, FinalExecutionStatus, FunctionCallAction,
13
+            SignedTransaction, Transaction,
14
+        },
15
+    },
16
+    Error,
17
+};
18
+
19
+use serde_json::{json, Value};
20
+use url::Url;
21
+
22
+type Result<T> = std::result::Result<T, Error>;
23
+
24
+pub struct Signer {
25
+    keypair: Keypair,
26
+    signer_acc: AccountId,
27
+    key_nonce: Nonce,
28
+}
29
+
30
+impl Signer {
31
+    pub fn new(secret_key: &str, signer_acc: AccountId, key_nonce: Nonce) -> Result<Self> {
32
+        Ok(Self {
33
+            keypair: Keypair::from_encoded(secret_key)?,
34
+            signer_acc,
35
+            key_nonce,
36
+        })
37
+    }
38
+
39
+    pub fn sign(&self, data: &[u8]) -> ED25519Signature {
40
+        self.keypair.sign(data)
41
+    }
42
+
43
+    pub fn public_key(&self) -> ED25519PublicKey {
44
+        self.keypair.public_key
45
+    }
46
+
47
+    pub fn account(&self) -> &AccountId {
48
+        &self.signer_acc
49
+    }
50
+
51
+    pub fn nonce(&self) -> Nonce {
52
+        self.key_nonce
53
+    }
54
+}
55
+
56
+pub struct NearClient {
57
+    rpc_client: RpcClient,
58
+}
59
+
60
+impl NearClient {
61
+    pub fn new(url: Url) -> Result<Self> {
62
+        Ok(Self {
63
+            rpc_client: RpcClient::new(url).map_err(|error| Error::Rpc {
64
+                error,
65
+                method: "create_new_near_client",
66
+            })?,
67
+        })
68
+    }
69
+
70
+    pub async fn block(&self) -> Result<CryptoHash> {
71
+        let chunks = self
72
+            .rpc_client
73
+            .request::<Chunks>("block", Some(json!({"finality": "optimistic"})))
74
+            .await
75
+            .map_err(|error| Error::Rpc {
76
+                error,
77
+                method: "block",
78
+            })?;
79
+
80
+        Ok(chunks
81
+            .chunks
82
+            .into_iter()
83
+            .next()
84
+            .ok_or(Error::EmptyBlock)?
85
+            .prev_block_hash)
86
+    }
87
+
88
+    pub fn call<'a>(
89
+        &'a self,
90
+        signer: &'a mut Signer,
91
+        contract_id: &'a AccountId,
92
+        method: &'static str,
93
+    ) -> TransactionBuilder {
94
+        TransactionBuilder::new(self, signer, method, contract_id)
95
+    }
96
+
97
+    pub fn view<'a>(&'a self, _contract_id: &'a AccountId, _method: &'static str) {}
98
+}
99
+
100
+pub struct Call<'a>(TransactionBuilder<'a>);
101
+
102
+impl<'a> Call<'a> {
103
+    pub async fn commit<T>(self) -> Result<T>
104
+    where
105
+        T: serde::de::DeserializeOwned,
106
+    {
107
+        let transaction_bytes = serialize_transaction(&self.0).await?;
108
+
109
+        let execution_outcome = self
110
+            .0
111
+            .client
112
+            .rpc_client
113
+            .request::<FinalExecutionOutcomeView>(
114
+                "broadcast_tx_commit",
115
+                Some(json!(vec![base64::encode(transaction_bytes)])),
116
+            )
117
+            .await
118
+            .map_err(|error| Error::Rpc {
119
+                error,
120
+                method: "broadcast_tx_commit",
121
+            })?;
122
+
123
+        match execution_outcome.status {
124
+            FinalExecutionStatus::Failure(err) => Err(Error::TransactionExec(err)),
125
+            FinalExecutionStatus::SuccessValue(data) => {
126
+                self.0.signer.key_nonce += 1;
127
+                serde_json::from_slice::<T>(&data).map_err(Error::DeserializeTxResp)
128
+            }
129
+            _ => Err(Error::TxNotStarted),
130
+        }
131
+    }
132
+
133
+    pub async fn commit_async(self) -> Result<String> {
134
+        let transaction_bytes = serialize_transaction(&self.0).await?;
135
+        self.0
136
+            .client
137
+            .rpc_client
138
+            .request::<String>(
139
+                "broadcast_tx_async",
140
+                Some(json!(vec![base64::encode(transaction_bytes)])),
141
+            )
142
+            .await
143
+            .map_err(|error| Error::Rpc {
144
+                error,
145
+                method: "broadcast_tx_async",
146
+            })
147
+    }
148
+}
149
+
150
+pub struct TransactionBuilder<'a> {
151
+    client: &'a NearClient,
152
+    signer: &'a mut Signer,
153
+    method: &'static str,
154
+    contract_id: &'a AccountId,
155
+    deposit: Balance,
156
+    gas: Gas,
157
+    args: Option<Value>,
158
+}
159
+
160
+impl<'a> TransactionBuilder<'a> {
161
+    fn new(
162
+        client: &'a NearClient,
163
+        signer: &'a mut Signer,
164
+        method: &'static str,
165
+        contract_id: &'a AccountId,
166
+    ) -> Self {
167
+        Self {
168
+            client,
169
+            signer,
170
+            contract_id,
171
+            method,
172
+            deposit: Default::default(),
173
+            gas: Default::default(),
174
+            args: Default::default(),
175
+        }
176
+    }
177
+
178
+    pub fn deposit(mut self, deposit: Balance) -> Self {
179
+        self.deposit = deposit;
180
+        self
181
+    }
182
+
183
+    pub fn gas(mut self, gas: Gas) -> Self {
184
+        self.gas = gas;
185
+        self
186
+    }
187
+
188
+    pub fn args(mut self, args: Value) -> Self {
189
+        self.args = Some(args);
190
+        self
191
+    }
192
+
193
+    pub fn build(self) -> Call<'a> {
194
+        Call(self)
195
+    }
196
+}
197
+
198
+async fn serialize_transaction<'a>(builder: &TransactionBuilder<'a>) -> Result<Vec<u8>> {
199
+    let block_hash = builder.client.block().await?;
200
+    let args_bytes = builder
201
+        .args
202
+        .as_ref()
203
+        .map(serde_json::to_vec)
204
+        .transpose()
205
+        .map_err(Error::ArgsSerialize)?
206
+        .unwrap_or_default();
207
+
208
+    let transaction = Transaction {
209
+        signer_id: builder.signer.account().clone(),
210
+        public_key: builder.signer.public_key(),
211
+        nonce: builder.signer.nonce() + 1,
212
+        receiver_id: builder.contract_id.clone(),
213
+        block_hash,
214
+        actions: vec![Action::FunctionCall(FunctionCallAction {
215
+            method_name: builder.method.to_owned(),
216
+            args: args_bytes,
217
+            gas: builder.gas,
218
+            deposit: builder.deposit,
219
+        })],
220
+    };
221
+
222
+    let (hash, ..) = transaction.get_hash_and_size();
223
+    let signature = builder.signer.keypair.sign(hash.0.as_ref());
224
+    let signed_transaction = SignedTransaction::new(signature, transaction);
225
+
226
+    borsh::to_vec(&signed_transaction).map_err(Error::TransactionSerialize)
227
+}
228
+
229
+#[cfg(test)]
230
+mod tests {}

+ 59
- 0
near-client/src/lib.rs 查看文件

@@ -0,0 +1,59 @@
1
+use prelude::TxExecutionError;
2
+
3
+pub mod client;
4
+pub mod rpc;
5
+pub mod types;
6
+
7
+pub mod prelude {
8
+    pub use super::types::{
9
+        self,
10
+        crypto::{ED25519PublicKey, ED25519Signature, Keypair},
11
+        near::TxExecutionError,
12
+    };
13
+}
14
+
15
+#[derive(Debug, thiserror::Error)]
16
+pub enum Error {
17
+    #[error("{0}")]
18
+    Type(types::Error),
19
+    #[error("Rpc request failed for {method} with {error}")]
20
+    Rpc {
21
+        error: rpc::Error,
22
+        method: &'static str,
23
+    },
24
+    #[error("Couldn't get a previous block hash")]
25
+    EmptyBlock,
26
+    #[error("Couldn't serialize function arguments {0}")]
27
+    ArgsSerialize(serde_json::Error),
28
+    #[error("Couldn't deserialize tx response {0}")]
29
+    DeserializeTxResp(serde_json::Error),
30
+    #[error("Borsh can't serialize transaction {0}")]
31
+    TransactionSerialize(std::io::Error),
32
+    #[error("Execution of transaction failed {0:?}")]
33
+    TransactionExec(TxExecutionError),
34
+    #[error("Transaction not started yet")]
35
+    TxNotStarted,
36
+    #[error("Rpc:")]
37
+    NoSigner,
38
+}
39
+
40
+impl From<types::Error> for Error {
41
+    fn from(err: types::Error) -> Self {
42
+        Self::Type(err)
43
+    }
44
+}
45
+
46
+pub fn add(left: usize, right: usize) -> usize {
47
+    left + right
48
+}
49
+
50
+#[cfg(test)]
51
+mod tests {
52
+    use super::*;
53
+
54
+    #[test]
55
+    fn it_works() {
56
+        let result = add(2, 2);
57
+        assert_eq!(result, 4);
58
+    }
59
+}

+ 127
- 0
near-client/src/rpc/client.rs 查看文件

@@ -0,0 +1,127 @@
1
+use reqwest::{
2
+    header::{HeaderMap, CONTENT_TYPE},
3
+    Client, ClientBuilder,
4
+};
5
+use serde::{Deserialize, Serialize};
6
+use serde_json::Value;
7
+
8
+use super::{Error, NearError};
9
+use std::borrow::Cow;
10
+use url::Url;
11
+
12
+type Result<T> = std::result::Result<T, Error>;
13
+
14
+pub struct RpcClient {
15
+    client: Client,
16
+    url: Url,
17
+}
18
+
19
+impl RpcClient {
20
+    pub fn new(url: Url) -> Result<Self> {
21
+        let mut headers = HeaderMap::new();
22
+        // It can't panic
23
+        headers.insert(CONTENT_TYPE, "application/json".parse().unwrap());
24
+        let client = ClientBuilder::new()
25
+            .default_headers(headers)
26
+            .build()
27
+            .map_err(Error::RpcClientCreate)?;
28
+
29
+        Ok(Self { client, url })
30
+    }
31
+
32
+    pub async fn request<T: serde::de::DeserializeOwned>(
33
+        &self,
34
+        method: &str,
35
+        params: Option<Value>,
36
+    ) -> Result<T> {
37
+        let resp = self
38
+            .client
39
+            .post(self.url.clone())
40
+            .json(
41
+                &serde_json::to_value(&Request::new(method, params))
42
+                    .map_err(Error::SerializeRpcRequest)?,
43
+            )
44
+            .send()
45
+            .await
46
+            .and_then(reqwest::Response::error_for_status)
47
+            .map_err(Error::RpcRequest)?;
48
+
49
+        match resp
50
+            .json::<Response<T>>()
51
+            .await
52
+            .map_err(Error::DeserializeRpcResponse)?
53
+        {
54
+            Response {
55
+                result: RpcResult::Ok(data),
56
+                ..
57
+            } => Ok(data),
58
+            Response {
59
+                result: RpcResult::Err(err),
60
+                ..
61
+            } => Err(err.into()),
62
+        }
63
+    }
64
+}
65
+
66
+#[derive(Debug, Serialize, Deserialize)]
67
+struct Request<'a> {
68
+    /// JSON-RPC version.
69
+    pub jsonrpc: &'static str,
70
+    /// Request ID
71
+    pub id: &'static str,
72
+    /// Name of the method to be invoked.
73
+    #[serde(borrow)]
74
+    pub method: Cow<'a, str>,
75
+    /// Parameter values of the request.
76
+    pub params: Option<Value>,
77
+}
78
+
79
+impl<'a> Request<'a> {
80
+    fn new(method: &'a str, params: Option<Value>) -> Self {
81
+        Self {
82
+            jsonrpc: "2.0",
83
+            id: "dontcare",
84
+            method: Cow::from(method),
85
+            params,
86
+        }
87
+    }
88
+}
89
+
90
+#[derive(Debug, Serialize, Deserialize)]
91
+struct Response<T> {
92
+    /// JSON-RPC version.
93
+    pub jsonrpc: String,
94
+    /// Result.
95
+    #[serde(flatten)]
96
+    pub result: RpcResult<T>,
97
+    /// Request ID
98
+    pub id: String,
99
+}
100
+
101
+/// Near result format
102
+#[derive(Debug, Serialize, Deserialize)]
103
+enum RpcResult<T> {
104
+    #[serde(rename = "result")]
105
+    Ok(T),
106
+    #[serde(rename = "error")]
107
+    Err(NearError),
108
+}
109
+
110
+#[cfg(test)]
111
+mod tests {
112
+
113
+    use super::*;
114
+
115
+    #[test]
116
+    fn test() {
117
+        let resp = Response {
118
+            jsonrpc: "some".to_owned(),
119
+            result: RpcResult::Ok("wef".to_owned()),
120
+            id: "dontcare".to_owned(),
121
+        };
122
+
123
+        let r = serde_json::to_string_pretty(&resp).unwrap();
124
+
125
+        println!("{}", r);
126
+    }
127
+}

+ 57
- 0
near-client/src/rpc/mod.rs 查看文件

@@ -0,0 +1,57 @@
1
+pub mod client;
2
+
3
+use std::fmt::Display;
4
+
5
+use serde::{Deserialize, Serialize};
6
+use serde_json::Value;
7
+
8
+#[derive(Debug, thiserror::Error)]
9
+pub enum Error {
10
+    #[error("Couldn't create a RpcClient: {0}")]
11
+    RpcClientCreate(reqwest::Error),
12
+    #[error("Rpc request failed with: {0}")]
13
+    RpcRequest(reqwest::Error),
14
+    #[error("Failed to serialize an RPC request: {0}")]
15
+    SerializeRpcRequest(serde_json::Error),
16
+    #[error("Failed to serialize an RPC request: {0}")]
17
+    DeserializeRpcResponse(reqwest::Error),
18
+    #[error("Near protocol error: {0}")]
19
+    NearProtocol(NearError),
20
+}
21
+
22
+impl From<NearError> for Error {
23
+    fn from(err: NearError) -> Self {
24
+        Self::NearProtocol(err)
25
+    }
26
+}
27
+
28
+#[derive(Debug, Serialize, Deserialize)]
29
+pub struct NearError {
30
+    name: String,
31
+    cause: Cause,
32
+    code: i32,
33
+    data: Value,
34
+    message: String,
35
+}
36
+
37
+impl Display for NearError {
38
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39
+        write!(
40
+            f,
41
+            "Name: {} Msg: {} Cause: {} descr: {}",
42
+            self.name, self.message, self.cause, self.data
43
+        )
44
+    }
45
+}
46
+
47
+#[derive(Debug, Serialize, Deserialize)]
48
+pub struct Cause {
49
+    info: Value,
50
+    name: String,
51
+}
52
+
53
+impl Display for Cause {
54
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55
+        write!(f, "{}", self.info)
56
+    }
57
+}

+ 325
- 0
near-client/src/types/crypto.rs 查看文件

@@ -0,0 +1,325 @@
1
+use borsh::{BorshDeserialize, BorshSerialize};
2
+use ed25519_dalek::{
3
+    ExpandedSecretKey, PublicKey, SecretKey, Signature, Verifier, PUBLIC_KEY_LENGTH,
4
+    SECRET_KEY_LENGTH, SIGNATURE_LENGTH,
5
+};
6
+use itertools::Itertools;
7
+use serde::{Deserialize, Serialize};
8
+use std::{
9
+    fmt::{Debug, Display},
10
+    hash::{Hash, Hasher},
11
+    io::{Error as IoError, ErrorKind},
12
+    str::FromStr,
13
+};
14
+
15
+use crate::types::Error;
16
+
17
+type Result<T> = std::result::Result<T, Error>;
18
+
19
+pub trait Key<T, const KEY_LENGTH: usize> {
20
+    fn from_string(key: &str) -> Result<T> {
21
+        let bs58_encoded = split_encoded_str(key)?;
22
+        let bytes = bs58::decode(bs58_encoded)
23
+            .into_vec()
24
+            .map_err(|err| Error::from_string::<T>(bs58_encoded.to_owned(), err.to_string()))?;
25
+        Self::from_bytes(&bytes)
26
+    }
27
+
28
+    fn to_string(&self) -> String {
29
+        format!("ed25519:{}", bs58::encode(self.to_bytes()).into_string())
30
+    }
31
+
32
+    fn from_bytes(bytes: &[u8]) -> Result<T>;
33
+    fn to_bytes(&self) -> [u8; KEY_LENGTH];
34
+}
35
+
36
+#[derive(Copy, Clone, Default, Eq, PartialEq)]
37
+pub struct ED25519PublicKey(PublicKey);
38
+
39
+impl ED25519PublicKey {
40
+    pub fn verify(&self, data: &[u8], signature: &ED25519Signature) -> Result<()> {
41
+        self.0
42
+            .verify(data, &signature.0)
43
+            .map_err(|_| Error::Verification(*signature))
44
+    }
45
+
46
+    #[inline]
47
+    pub fn as_bytes(&self) -> &[u8; PUBLIC_KEY_LENGTH] {
48
+        self.0.as_bytes()
49
+    }
50
+}
51
+
52
+impl Key<ED25519PublicKey, PUBLIC_KEY_LENGTH> for ED25519PublicKey {
53
+    fn from_bytes(bytes: &[u8]) -> Result<ED25519PublicKey> {
54
+        PublicKey::from_bytes(bytes)
55
+            .map(Self)
56
+            .map_err(|err| Error::from_bytes::<ED25519PublicKey>(bytes, err.to_string()))
57
+    }
58
+
59
+    #[inline]
60
+    fn to_bytes(&self) -> [u8; PUBLIC_KEY_LENGTH] {
61
+        self.0.to_bytes()
62
+    }
63
+}
64
+
65
+impl BorshDeserialize for ED25519PublicKey {
66
+    fn deserialize(buf: &mut &[u8]) -> std::io::Result<Self> {
67
+        if buf.len() != PUBLIC_KEY_LENGTH {
68
+            return Err(IoError::new(ErrorKind::InvalidData, format!("Couldn't deserialize a ED25519PublicKey, input buffer {} is greater or less than required", buf.len())));
69
+        }
70
+
71
+        // The first byte is a key type, let's skip it because currently is used ed25519 only
72
+        let temp_buf = std::mem::take(buf);
73
+        Ok(Self(PublicKey::from_bytes(&temp_buf[1..]).map_err(
74
+            |err| {
75
+                IoError::new(
76
+                    ErrorKind::InvalidData,
77
+                    format!("Couldn't deserialize a ED25519PublicKey because of: {err}"),
78
+                )
79
+            },
80
+        )?))
81
+    }
82
+}
83
+
84
+impl BorshSerialize for ED25519PublicKey {
85
+    fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
86
+        BorshSerialize::serialize(&0_u8, writer)?;
87
+        writer.write_all(self.0.as_bytes())
88
+    }
89
+}
90
+
91
+impl From<&ED25519SecretKey> for ED25519PublicKey {
92
+    fn from(sk: &ED25519SecretKey) -> Self {
93
+        Self(PublicKey::from(&sk.0))
94
+    }
95
+}
96
+
97
+// This `Hash` implementation is safe since it retains the property
98
+// `k1 == k2 ⇒ hash(k1) == hash(k2)`.
99
+#[allow(clippy::derive_hash_xor_eq)]
100
+impl Hash for ED25519PublicKey {
101
+    fn hash<H: Hasher>(&self, state: &mut H) {
102
+        state.write_u8(0u8);
103
+        state.write(self.0.as_bytes());
104
+    }
105
+}
106
+
107
+impl Debug for ED25519PublicKey {
108
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
109
+        write!(f, "{}", self)
110
+    }
111
+}
112
+
113
+impl Display for ED25519PublicKey {
114
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115
+        write!(f, "{}", Key::to_string(self))
116
+    }
117
+}
118
+
119
+pub struct ED25519SecretKey(SecretKey);
120
+
121
+impl ED25519SecretKey {
122
+    pub fn sign(&self, data: &[u8], public_key: &ED25519PublicKey) -> ED25519Signature {
123
+        let expanded_key = ExpandedSecretKey::from(&self.0);
124
+        ED25519Signature(expanded_key.sign(data, &public_key.0))
125
+    }
126
+
127
+    #[inline]
128
+    pub fn as_bytes(&self) -> &[u8; PUBLIC_KEY_LENGTH] {
129
+        self.0.as_bytes()
130
+    }
131
+
132
+    pub fn from_encoded_expanded(key: &str) -> Result<Self> {
133
+        let bs58_encoded = split_encoded_str(key)?;
134
+        let expanded_key_bytes = bs58::decode(bs58_encoded).into_vec().map_err(|err| {
135
+            Error::from_string::<ED25519SecretKey>(bs58_encoded.to_owned(), err.to_string())
136
+        })?;
137
+        Self::from_bytes(&expanded_key_bytes[..32])
138
+    }
139
+}
140
+
141
+impl Key<ED25519SecretKey, SECRET_KEY_LENGTH> for ED25519SecretKey {
142
+    fn from_bytes(bytes: &[u8]) -> Result<ED25519SecretKey> {
143
+        SecretKey::from_bytes(bytes)
144
+            .map(Self)
145
+            .map_err(|err| Error::from_bytes::<ED25519SecretKey>(bytes, err.to_string()))
146
+    }
147
+
148
+    #[inline]
149
+    fn to_bytes(&self) -> [u8; SECRET_KEY_LENGTH] {
150
+        self.0.to_bytes()
151
+    }
152
+}
153
+
154
+#[derive(Copy, Clone, Eq, PartialEq)]
155
+pub struct ED25519Signature(Signature);
156
+
157
+impl Key<ED25519Signature, SIGNATURE_LENGTH> for ED25519Signature {
158
+    fn from_bytes(bytes: &[u8]) -> Result<ED25519Signature> {
159
+        Signature::from_bytes(bytes)
160
+            .map(Self)
161
+            .map_err(|err| Error::from_bytes::<ED25519Signature>(bytes, err.to_string()))
162
+    }
163
+
164
+    fn to_bytes(&self) -> [u8; SIGNATURE_LENGTH] {
165
+        self.0.to_bytes()
166
+    }
167
+}
168
+
169
+impl BorshSerialize for ED25519Signature {
170
+    fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
171
+        BorshSerialize::serialize(&0_u8, writer)?;
172
+        writer.write_all(&self.to_bytes())
173
+    }
174
+}
175
+
176
+impl BorshDeserialize for ED25519Signature {
177
+    fn deserialize(buf: &mut &[u8]) -> std::io::Result<Self> {
178
+        if buf.len() != SIGNATURE_LENGTH + 1 {
179
+            return Err(IoError::new(ErrorKind::InvalidData, format!("Couldn't deserialize a ED25519Signature, input buffer {} is greater or less than required", buf.len())));
180
+        }
181
+
182
+        // The first byte is a key type, let's skip it because currently is used ed25519 only
183
+        let temp_buf = std::mem::take(buf);
184
+        Self::from_bytes(&temp_buf[1..]).map_err(|err| {
185
+            IoError::new(
186
+                ErrorKind::InvalidData,
187
+                format!("Couldn't deserialize a ED25519Signature because of: {err}"),
188
+            )
189
+        })
190
+    }
191
+}
192
+
193
+#[allow(clippy::derive_hash_xor_eq)]
194
+impl Hash for ED25519Signature {
195
+    fn hash<H: Hasher>(&self, state: &mut H) {
196
+        self.to_bytes().hash(state);
197
+    }
198
+}
199
+
200
+impl Debug for ED25519Signature {
201
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
202
+        write!(f, "{}", self)
203
+    }
204
+}
205
+
206
+impl Display for ED25519Signature {
207
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208
+        write!(f, "{}", Key::to_string(self))
209
+    }
210
+}
211
+
212
+#[derive(Serialize, Deserialize)]
213
+pub struct Keypair {
214
+    pub public_key: ED25519PublicKey,
215
+    pub secret_key: ED25519SecretKey,
216
+}
217
+
218
+impl Keypair {
219
+    pub fn new(secret_key: ED25519SecretKey) -> Self {
220
+        let public_key = ED25519PublicKey::from(&secret_key);
221
+        Self {
222
+            public_key,
223
+            secret_key,
224
+        }
225
+    }
226
+
227
+    pub fn from_encoded(encoded: &str) -> Result<Self> {
228
+        let secret_key = ED25519SecretKey::from_encoded_expanded(encoded)?;
229
+        let public_key = ED25519PublicKey::from(&secret_key);
230
+        Ok(Self {
231
+            public_key,
232
+            secret_key,
233
+        })
234
+    }
235
+
236
+    pub fn sign(&self, data: &[u8]) -> ED25519Signature {
237
+        self.secret_key.sign(data, &self.public_key)
238
+    }
239
+
240
+    pub fn verify(&self, data: &[u8], signature: &ED25519Signature) -> Result<()> {
241
+        self.public_key.verify(data, signature)
242
+    }
243
+}
244
+
245
+macro_rules! util_impl {
246
+    ($key_type: ty) => {
247
+        impl TryFrom<&[u8]> for $key_type {
248
+            type Error = Error;
249
+
250
+            fn try_from(data: &[u8]) -> Result<Self> {
251
+                Self::from_bytes(data)
252
+            }
253
+        }
254
+
255
+        impl From<$key_type> for String {
256
+            fn from(key: $key_type) -> Self {
257
+                Key::to_string(&key)
258
+            }
259
+        }
260
+
261
+        impl FromStr for $key_type {
262
+            type Err = Error;
263
+
264
+            fn from_str(s: &str) -> Result<Self> {
265
+                Self::from_string(s)
266
+            }
267
+        }
268
+    };
269
+}
270
+
271
+macro_rules! serde_impl {
272
+    ($key_type: ty) => {
273
+        impl serde::Serialize for $key_type {
274
+            fn serialize<S>(
275
+                &self,
276
+                serializer: S,
277
+            ) -> std::result::Result<<S as serde::Serializer>::Ok, <S as serde::Serializer>::Error>
278
+            where
279
+                S: serde::Serializer,
280
+            {
281
+                serializer.serialize_str(&Key::to_string(self))
282
+            }
283
+        }
284
+
285
+        impl<'de> serde::Deserialize<'de> for $key_type {
286
+            fn deserialize<D>(
287
+                deserializer: D,
288
+            ) -> std::result::Result<Self, <D as serde::Deserializer<'de>>::Error>
289
+            where
290
+                D: serde::Deserializer<'de>,
291
+            {
292
+                let s = <String as serde::Deserialize>::deserialize(deserializer)?;
293
+                s.parse().map_err(|err| {
294
+                    serde::de::Error::custom(format!("Deserialization failed: `{}`", err))
295
+                })
296
+            }
297
+        }
298
+    };
299
+}
300
+
301
+fn split_encoded_str(encoded: &str) -> Result<&str> {
302
+    match encoded.split(':').next_tuple() {
303
+        Some((key_type, bs58_encoded)) => {
304
+            if key_type != "ed25519" {
305
+                return Err(Error::UnknownKeyType(key_type.to_owned()));
306
+            }
307
+            Ok(bs58_encoded)
308
+        }
309
+        _ => Err(Error::UnknownKeyType(encoded.to_owned())),
310
+    }
311
+}
312
+
313
+serde_impl!(ED25519PublicKey);
314
+util_impl!(ED25519PublicKey);
315
+serde_impl!(ED25519SecretKey);
316
+util_impl!(ED25519SecretKey);
317
+serde_impl!(ED25519Signature);
318
+util_impl!(ED25519Signature);
319
+
320
+#[cfg(test)]
321
+mod tests {
322
+
323
+    #[test]
324
+    fn test() {}
325
+}

+ 50
- 0
near-client/src/types/mod.rs 查看文件

@@ -0,0 +1,50 @@
1
+pub mod crypto;
2
+pub mod near;
3
+
4
+#[derive(thiserror::Error, Debug)]
5
+pub enum Error {
6
+    #[error(
7
+        "Couldn't convert key from bytes `{key_string}` into `{key_name}`, because of: {cause}"
8
+    )]
9
+    ConvertFromBytes {
10
+        key_name: &'static str,
11
+        key_string: String,
12
+        cause: String,
13
+    },
14
+    #[error(
15
+        "Couldn't convert key from string `{key_string}` into `{key_name}`, because of: {cause}"
16
+    )]
17
+    ConvertFromString {
18
+        key_name: &'static str,
19
+        key_string: String,
20
+        cause: String,
21
+    },
22
+    #[error("The key format `{0}` seems different from ed25519 format")]
23
+    UnknownKeyType(String),
24
+    #[error("Signature `{0}` verification failed")]
25
+    Verification(crypto::ED25519Signature),
26
+}
27
+
28
+impl Error {
29
+    pub(crate) fn from_string<T>(key_string: String, cause: String) -> Self {
30
+        Self::ConvertFromString {
31
+            key_name: std::any::type_name::<T>()
32
+                .rsplit("::")
33
+                .next()
34
+                .unwrap_or_default(),
35
+            key_string,
36
+            cause,
37
+        }
38
+    }
39
+
40
+    pub(crate) fn from_bytes<T>(data: &[u8], cause: String) -> Self {
41
+        Self::ConvertFromBytes {
42
+            key_name: std::any::type_name::<T>()
43
+                .rsplit("::")
44
+                .next()
45
+                .unwrap_or_default(),
46
+            key_string: bs58::encode(data).into_string(),
47
+            cause,
48
+        }
49
+    }
50
+}

+ 363
- 0
near-client/src/types/near.rs 查看文件

@@ -0,0 +1,363 @@
1
+use crate::types::crypto::{ED25519PublicKey, ED25519Signature};
2
+
3
+use borsh::{BorshDeserialize, BorshSerialize};
4
+use near_account_id::AccountId;
5
+
6
+use near_primitives::{
7
+    account::AccessKey,
8
+    hash::{hash, CryptoHash},
9
+    logging,
10
+    serialize::{base64_format, u128_dec_format_compatible},
11
+    types::{Balance, Gas, Nonce},
12
+};
13
+use serde::{Deserialize, Serialize};
14
+use serde_json::Value;
15
+use std::{
16
+    borrow::Borrow,
17
+    fmt,
18
+    hash::{Hash, Hasher},
19
+};
20
+
21
+#[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
22
+pub struct Transaction {
23
+    /// An account on which behalf transaction is signed
24
+    pub signer_id: AccountId,
25
+    /// A public key of the access key which was used to sign an account.
26
+    /// Access key holds permissions for calling certain kinds of actions.
27
+    pub public_key: ED25519PublicKey,
28
+    /// Nonce is used to determine order of transaction in the pool.
29
+    /// It increments for a combination of `signer_id` and `public_key`
30
+    pub nonce: Nonce,
31
+    /// Receiver account for this transaction
32
+    pub receiver_id: AccountId,
33
+    /// The hash of the block in the blockchain on top of which the given transaction is valid
34
+    pub block_hash: CryptoHash,
35
+    /// A list of actions to be applied
36
+    pub actions: Vec<Action>,
37
+}
38
+
39
+impl Transaction {
40
+    /// Computes a hash of the transaction for signing and size of serialized transaction
41
+    pub fn get_hash_and_size(&self) -> (CryptoHash, u64) {
42
+        let bytes = self.try_to_vec().expect("Failed to deserialize");
43
+        (hash(&bytes), bytes.len() as u64)
44
+    }
45
+}
46
+
47
+#[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
48
+pub enum Action {
49
+    /// Create an (sub)account using a transaction `receiver_id` as an ID for
50
+    /// a new account ID must pass validation rules described here
51
+    /// <http://nomicon.io/Primitives/Account.html>.
52
+    CreateAccount(CreateAccountAction),
53
+    /// Sets a Wasm code to a receiver_id
54
+    DeployContract(DeployContractAction),
55
+    FunctionCall(FunctionCallAction),
56
+    Transfer(TransferAction),
57
+    Stake(StakeAction),
58
+    AddKey(AddKeyAction),
59
+    DeleteKey(DeleteKeyAction),
60
+    DeleteAccount(DeleteAccountAction),
61
+    #[cfg(feature = "protocol_feature_chunk_only_producers")]
62
+    StakeChunkOnly(StakeAction),
63
+}
64
+
65
+impl Action {
66
+    pub fn get_prepaid_gas(&self) -> Gas {
67
+        match self {
68
+            Action::FunctionCall(a) => a.gas,
69
+            _ => 0,
70
+        }
71
+    }
72
+    pub fn get_deposit_balance(&self) -> Balance {
73
+        match self {
74
+            Action::FunctionCall(a) => a.deposit,
75
+            Action::Transfer(a) => a.deposit,
76
+            _ => 0,
77
+        }
78
+    }
79
+}
80
+
81
+/// Create account action
82
+#[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
83
+pub struct CreateAccountAction {}
84
+
85
+impl From<CreateAccountAction> for Action {
86
+    fn from(create_account_action: CreateAccountAction) -> Self {
87
+        Self::CreateAccount(create_account_action)
88
+    }
89
+}
90
+
91
+/// Deploy contract action
92
+#[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, PartialEq, Eq, Clone)]
93
+pub struct DeployContractAction {
94
+    /// WebAssembly binary
95
+    #[serde(with = "base64_format")]
96
+    pub code: Vec<u8>,
97
+}
98
+
99
+impl From<DeployContractAction> for Action {
100
+    fn from(deploy_contract_action: DeployContractAction) -> Self {
101
+        Self::DeployContract(deploy_contract_action)
102
+    }
103
+}
104
+
105
+impl fmt::Debug for DeployContractAction {
106
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107
+        f.debug_struct("DeployContractAction")
108
+            .field(
109
+                "code",
110
+                &format_args!("{}", logging::pretty_utf8(&self.code)),
111
+            )
112
+            .finish()
113
+    }
114
+}
115
+
116
+#[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, PartialEq, Eq, Clone)]
117
+pub struct FunctionCallAction {
118
+    pub method_name: String,
119
+    #[serde(with = "base64_format")]
120
+    pub args: Vec<u8>,
121
+    pub gas: Gas,
122
+    #[serde(with = "u128_dec_format_compatible")]
123
+    pub deposit: Balance,
124
+}
125
+
126
+impl From<FunctionCallAction> for Action {
127
+    fn from(function_call_action: FunctionCallAction) -> Self {
128
+        Self::FunctionCall(function_call_action)
129
+    }
130
+}
131
+
132
+impl fmt::Debug for FunctionCallAction {
133
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134
+        f.debug_struct("FunctionCallAction")
135
+            .field("method_name", &format_args!("{}", &self.method_name))
136
+            .field(
137
+                "args",
138
+                &format_args!("{}", logging::pretty_utf8(&self.args)),
139
+            )
140
+            .field("gas", &format_args!("{}", &self.gas))
141
+            .field("deposit", &format_args!("{}", &self.deposit))
142
+            .finish()
143
+    }
144
+}
145
+
146
+#[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
147
+pub struct TransferAction {
148
+    #[serde(with = "u128_dec_format_compatible")]
149
+    pub deposit: Balance,
150
+}
151
+
152
+impl From<TransferAction> for Action {
153
+    fn from(transfer_action: TransferAction) -> Self {
154
+        Self::Transfer(transfer_action)
155
+    }
156
+}
157
+
158
+/// An action which stakes singer_id tokens and setup's validator public key
159
+#[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
160
+pub struct StakeAction {
161
+    /// Amount of tokens to stake.
162
+    #[serde(with = "u128_dec_format_compatible")]
163
+    pub stake: Balance,
164
+    /// Validator key which will be used to sign transactions on behalf of singer_id
165
+    pub public_key: ED25519PublicKey,
166
+}
167
+
168
+impl From<StakeAction> for Action {
169
+    fn from(stake_action: StakeAction) -> Self {
170
+        Self::Stake(stake_action)
171
+    }
172
+}
173
+
174
+#[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
175
+pub struct AddKeyAction {
176
+    /// A public key which will be associated with an access_key
177
+    pub public_key: ED25519PublicKey,
178
+    /// An access key with the permission
179
+    pub access_key: AccessKey,
180
+}
181
+
182
+impl From<AddKeyAction> for Action {
183
+    fn from(add_key_action: AddKeyAction) -> Self {
184
+        Self::AddKey(add_key_action)
185
+    }
186
+}
187
+
188
+#[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
189
+pub struct DeleteKeyAction {
190
+    /// A public key associated with the access_key to be deleted.
191
+    pub public_key: ED25519PublicKey,
192
+}
193
+
194
+impl From<DeleteKeyAction> for Action {
195
+    fn from(delete_key_action: DeleteKeyAction) -> Self {
196
+        Self::DeleteKey(delete_key_action)
197
+    }
198
+}
199
+
200
+#[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
201
+pub struct DeleteAccountAction {
202
+    pub beneficiary_id: AccountId,
203
+}
204
+
205
+impl From<DeleteAccountAction> for Action {
206
+    fn from(delete_account_action: DeleteAccountAction) -> Self {
207
+        Self::DeleteAccount(delete_account_action)
208
+    }
209
+}
210
+
211
+#[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, Eq, Debug, Clone)]
212
+#[borsh_init(init)]
213
+pub struct SignedTransaction {
214
+    pub transaction: Transaction,
215
+    pub signature: ED25519Signature,
216
+    #[borsh_skip]
217
+    hash: CryptoHash,
218
+    #[borsh_skip]
219
+    size: u64,
220
+}
221
+
222
+impl SignedTransaction {
223
+    pub fn new(signature: ED25519Signature, transaction: Transaction) -> Self {
224
+        let mut signed_tx = Self {
225
+            signature,
226
+            transaction,
227
+            hash: CryptoHash::default(),
228
+            size: u64::default(),
229
+        };
230
+        signed_tx.init();
231
+        signed_tx
232
+    }
233
+
234
+    pub fn init(&mut self) {
235
+        let (hash, size) = self.transaction.get_hash_and_size();
236
+        self.hash = hash;
237
+        self.size = size;
238
+    }
239
+
240
+    pub fn get_hash(&self) -> CryptoHash {
241
+        self.hash
242
+    }
243
+
244
+    pub fn get_size(&self) -> u64 {
245
+        self.size
246
+    }
247
+}
248
+
249
+impl Hash for SignedTransaction {
250
+    fn hash<H: Hasher>(&self, state: &mut H) {
251
+        self.hash.hash(state)
252
+    }
253
+}
254
+
255
+impl PartialEq for SignedTransaction {
256
+    fn eq(&self, other: &SignedTransaction) -> bool {
257
+        self.hash == other.hash && self.signature == other.signature
258
+    }
259
+}
260
+
261
+impl Borrow<CryptoHash> for SignedTransaction {
262
+    fn borrow(&self) -> &CryptoHash {
263
+        &self.hash
264
+    }
265
+}
266
+
267
+#[derive(Debug, Clone, Serialize, Deserialize)]
268
+pub struct SignedTransactionView {
269
+    pub signer_id: AccountId,
270
+    pub public_key: ED25519PublicKey,
271
+    pub nonce: Nonce,
272
+    pub receiver_id: AccountId,
273
+    pub actions: Vec<Action>,
274
+    pub signature: ED25519Signature,
275
+    pub hash: CryptoHash,
276
+}
277
+
278
+#[derive(Debug, Clone, Deserialize, Serialize)]
279
+pub enum TxExecutionError {
280
+    /// An error happened during Action execution
281
+    ActionError(Value),
282
+    /// An error happened during Transaction execution
283
+    InvalidTxError(Value),
284
+}
285
+
286
+#[derive(Debug, Clone, Serialize, Deserialize)]
287
+pub enum FinalExecutionStatus {
288
+    /// The execution has not yet started.
289
+    NotStarted,
290
+    /// The execution has started and still going.
291
+    Started,
292
+    /// The execution has failed with the given error.
293
+    Failure(TxExecutionError),
294
+    /// The execution has succeeded and returned some value or an empty vec encoded in base64.
295
+    SuccessValue(#[serde(with = "base64_format")] Vec<u8>),
296
+}
297
+
298
+#[derive(Debug, Clone, Serialize, Deserialize)]
299
+pub struct ExecutionOutcomeView {
300
+    /// Logs from this transaction or receipt.
301
+    pub logs: Vec<String>,
302
+    /// The amount of the gas burnt by the given transaction or receipt.
303
+    pub gas_burnt: Gas,
304
+}
305
+
306
+#[derive(Debug, Clone, Serialize, Deserialize)]
307
+pub struct ExecutionOutcomeWithIdView {
308
+    // pub proof: MerklePath,
309
+    pub block_hash: CryptoHash,
310
+    pub id: CryptoHash,
311
+    pub outcome: ExecutionOutcomeView,
312
+}
313
+
314
+#[derive(Debug, Clone, Serialize, Deserialize)]
315
+pub struct FinalExecutionOutcomeView {
316
+    /// Execution status. Contains the result in case of successful execution.
317
+    pub status: FinalExecutionStatus,
318
+    /// Signed Transaction
319
+    pub transaction: SignedTransactionView,
320
+    /// The execution outcome of the signed transaction.
321
+    pub transaction_outcome: ExecutionOutcomeWithIdView,
322
+}
323
+
324
+#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
325
+pub(crate) struct Chunks {
326
+    pub(crate) chunks: Vec<ShardChunkHeaderInnerV2>,
327
+}
328
+
329
+#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
330
+pub(crate) struct ShardChunkHeaderInnerV2 {
331
+    /// Previous block hash.
332
+    pub(crate) prev_block_hash: CryptoHash,
333
+    /// Root of the outcomes from execution transactions and results.
334
+    pub(crate) outcome_root: CryptoHash,
335
+    pub(crate) encoded_merkle_root: CryptoHash,
336
+    pub(crate) encoded_length: u64,
337
+    pub(crate) height_created: u64,
338
+    /// Gas used in this chunk.
339
+    pub(crate) gas_used: Gas,
340
+    /// Gas limit voted by validators.
341
+    pub(crate) gas_limit: Gas,
342
+    /// Total balance burnt in previous chunk
343
+    pub(crate) balance_burnt: String,
344
+    /// Outgoing receipts merkle root.
345
+    pub(crate) outgoing_receipts_root: CryptoHash,
346
+    /// Tx merkle root.
347
+    pub(crate) tx_root: CryptoHash,
348
+}
349
+
350
+#[derive(Debug, Clone, Serialize, Deserialize)]
351
+pub struct ViewResult {
352
+    pub result: Vec<u8>,
353
+    pub logs: Vec<String>,
354
+}
355
+
356
+#[derive(Debug, Clone, Serialize, Deserialize)]
357
+pub struct View {
358
+    pub request_type: String,
359
+    pub finality: String,
360
+    pub method_name: String,
361
+    #[serde(with = "base64_format")]
362
+    pub args_base64: Vec<u8>,
363
+}

+ 26
- 0
wasm-client/Cargo.toml 查看文件

@@ -0,0 +1,26 @@
1
+[package]
2
+name = "wasm-client"
3
+version = "0.1.0"
4
+edition = "2021"
5
+authors = ["silvestr@relayz.io"]
6
+description = """
7
+"""
8
+
9
+[lib]
10
+crate-type = ["cdylib", "rlib"]
11
+
12
+[dependencies]
13
+vodozemac = { version = "0.2.0", features = ["js"] }
14
+serde = { version = "1", default-features = false, features = ["derive"] }
15
+serde_json = { version = "1", default-features = false }
16
+wasm-bindgen = "0.2"
17
+js-sys = "0.3"
18
+wasm-bindgen-test = "0.3"
19
+wasm-bindgen-futures = "0.4.32"
20
+web-sys = { version = "0.3", features = ["console", "WebSocket", "WebSocketDict", "WebSocketElement"] }
21
+borsh = "0.9"
22
+near-account-id = "0.14.0"
23
+near-primitives = { version = "0.14.0", package = "near-primitives-core" }
24
+near-units = "0.2"
25
+near-client = { path = "../near-client" }
26
+url = "2.2"

+ 80
- 0
wasm-client/src/lib.rs 查看文件

@@ -0,0 +1,80 @@
1
+use std::str::FromStr;
2
+
3
+use js_sys::Function;
4
+use near_primitives::account;
5
+use serde::{Deserialize, Serialize};
6
+use serde_json::json;
7
+use wasm_bindgen::__rt::IntoJsResult;
8
+use wasm_bindgen::{prelude::*, JsCast};
9
+use wasm_bindgen_futures::*;
10
+use web_sys::console;
11
+
12
+use near_client::{
13
+    client::{NearClient, Signer},
14
+    types::crypto::{ED25519SecretKey, Key, Keypair},
15
+};
16
+
17
+macro_rules! console_log {
18
+    ($($t:tt)*) => (web_sys::console::log_1(&format!($($t)*).into()))
19
+}
20
+
21
+#[wasm_bindgen]
22
+pub async fn greet() -> Result<(), JsValue> {
23
+    // let socket = web_sys::WebSocket::new("ws://127.0.0.1:3000/ws")?;
24
+    // let cl_socket = socket.clone();
25
+    // let on_open = Closure::<dyn Fn()>::new(move || {
26
+    //     console_log!("Open A socket");
27
+    //     cl_socket.send_with_str("Init Message").unwrap();
28
+    // });
29
+    // socket.set_onopen(Some(on_open.as_ref().unchecked_ref()));
30
+    // on_open.forget();
31
+
32
+    Ok(())
33
+}
34
+
35
+#[wasm_bindgen]
36
+pub async fn send_transaction(
37
+    keypair: String,
38
+    nounce: near_primitives::types::Nonce,
39
+    account_id: String,
40
+) -> Result<(), JsValue> {
41
+    let signer = near_account_id::AccountId::from_str(&account_id)
42
+        .map_err(|err| JsValue::from_str(&format!("Error {err}")))?;
43
+    let contract_id = near_account_id::AccountId::from_str("contract1.demoacc.testnet")
44
+        .map_err(|err| JsValue::from_str(&format!("Error {err}")))?;
45
+
46
+    console_log!("SignAcc");
47
+    let mut signer_acc = Signer::new(&keypair, signer, nounce)
48
+        .map_err(|err| JsValue::from_str(&format!("Error {err}")))?;
49
+
50
+    console_log!("Keypair");
51
+    let keypair = Keypair::from_encoded(&keypair)
52
+        .map_err(|err| JsValue::from_str(&format!("Error {err}")))?;
53
+
54
+    console_log!("Near Client");
55
+    let near_client = NearClient::new(url::Url::from_str("https://rpc.testnet.near.org").unwrap())
56
+        .map_err(|err| JsValue::from_str(&format!("Error {err}")))?;
57
+
58
+    console_log!("Call");
59
+    let build = near_client
60
+        .call(&mut signer_acc, &contract_id, "add_key")
61
+        .gas(near_units::parse_gas!("300 T") as u64)
62
+        .args(json!({
63
+            "id": "Some",
64
+            "value": String::from(keypair.public_key)
65
+        }))
66
+        .build();
67
+
68
+    console_log!("Build");
69
+    let res = build
70
+        .commit_async()
71
+        .await
72
+        .map_err(|err| JsValue::from_str(&format!("Error {err}")))?;
73
+
74
+    console_log!("Response: {}", res);
75
+
76
+    Ok(())
77
+}
78
+
79
+#[cfg(test)]
80
+mod tests {}

正在加载...
取消
保存