eosio_client_api/
wallet_types.rs1use serde::{Deserialize, Serialize};
8use crate::errors::{ErrorKind, Result};
10use crate::json_rpc::EOSRPC;
11use eosio_client_keys::{EOSPrivateKey, EOSPublicKey};
12use serde_json::Value;
13use libabieos_sys::vec_u8_to_hex;
14use crate::api_types::{ TransactionIn, TransactionInSigned};
15
16const WALLET_UNLOCKED_EXCEPTION: usize = 3_120_007;
17#[allow(dead_code)]
18pub const EOSIO_CHAIN_ID: &str = "cf057bbfb72640471fd910bcb67639c22df9f92470936cddc1ade0e2f2e7dc4f";
19
20#[derive(Debug, Serialize, Deserialize)]
21pub struct WalletList {
22 name: String,
23}
24
25pub struct Wallet {
26 keos: EOSRPC,
27 chain_id: Option<String>,
28}
29
30impl Wallet {
31 pub fn create(keos: EOSRPC) -> Wallet {
32 Wallet {
33 keos,
34 chain_id: None,
35 }
36 }
37 pub fn create_with_chain_id(keos: EOSRPC, chain_id: &str) -> Wallet {
38 Wallet {
39 keos,
40 chain_id: Some(String::from(chain_id)),
41 }
42 }
43 pub async fn list(&self) -> Result<Vec<String>> {
44 let value = serde_json::json!({});
45 let res = self
46 .keos
47 .non_blocking_req("/v1/wallet/list_wallets", value)
48 .await?;
49 let list: Vec<String> = serde_json::from_str(&res).unwrap();
50
51 Ok(list)
52 }
53 pub async fn keys(&self) -> Result<Vec<EOSPublicKey>> {
54 let value = serde_json::json!({});
55 let res = self
56 .keos
57 .non_blocking_req("/v1/wallet/get_public_keys", value)
58 .await?;
59 let list: Vec<String> = serde_json::from_str(&res).unwrap();
60 let keys = EOSPublicKey::from_eos_strings(&list)?;
61
62 Ok(keys)
63 }
64 pub async fn private_keys(
65 &self,
66 wallet: &str,
67 pass: &str,
68 ) -> Result<Vec<(EOSPublicKey, EOSPrivateKey)>> {
69 let value = serde_json::json!([wallet, pass]);
70 let res = self
71 .keos
72 .non_blocking_req("/v1/wallet/list_keys", value)
73 .await?;
74 let list: Vec<(String, String)> = serde_json::from_str(&res).unwrap();
75 let mut r: Vec<(EOSPublicKey, EOSPrivateKey)> = vec![];
76 for pair in list {
77 let public: EOSPublicKey = EOSPublicKey::from_eos_string(&pair.0)?;
78 let private: EOSPrivateKey = EOSPrivateKey::from_string(&pair.1)?;
79 r.push((public, private));
80 }
81
82 Ok(r)
83 }
84
85 pub async fn unlock(&self, wallet: &str, pass: &str) -> Result<bool> {
86 let value = serde_json::json!([wallet, pass]);
87 match self.keos.non_blocking_req("/v1/wallet/unlock", value).await {
88 Ok(res) => {
89 let resp: Value = serde_json::from_str(&res).unwrap();
90 if resp.is_object() {
91 Ok(true)
92 } else {
93 Err("Fail-Wallet Unlock unknown response".into())
94 }
95 }
96 Err(e) => match e.0 {
97 ErrorKind::InvalidResponseStatus(k) => {
98 if k.code == WALLET_UNLOCKED_EXCEPTION {
99 Ok(true)
100 } else {
101 eprintln!("{:#?}", k);
102 Err("Fail-Wallet Unlock".into())
103 }
104 },
105 ErrorKind::InvalidResponseErr(k) => {
106 eprintln!("{:#?}", k);
107 panic!("Wallet unlock Fail-Err");
108 }
109 _ => {
110 eprintln!("{:#?}", e);
111 panic!("Wallet unlock Fail");
112 }
113 },
114 }
115 }
116 pub async fn sign_transaction(
117 &self,
118 transaction: TransactionIn,
119 pubkey: Vec<EOSPublicKey>,
120 ) -> Result<TransactionInSigned> {
121 let mut pubkey_str: Vec<String> = vec![];
122 for k in pubkey {
123 pubkey_str.push(k.to_eos_string()?)
124 }
125 if self.chain_id.is_none() {
126 Err(ErrorKind::WalletMissingChainID.into())
127 } else {
128 let value =
129 serde_json::json![[transaction, pubkey_str, self.chain_id.as_ref().unwrap()]];
130 let res = self
131 .keos
132 .non_blocking_req("/v1/wallet/sign_transaction", value)
133 .await?;
134 let t: TransactionInSigned = serde_json::from_str(&res).unwrap();
135 Ok(t)
136 }
137 }
138
139 pub async fn sign_digest(&self, digest: &[u8], pubkey: &EOSPublicKey) -> Result<String> {
140 let digest_b = vec_u8_to_hex(digest)?;
141 let value = serde_json::json![[digest_b, pubkey.to_eos_string()?]];
142 let res = self
143 .keos
144 .non_blocking_req("/v1/wallet/sign_digest", value)
145 .await?;
146 let sig: String = serde_json::from_str(&res).unwrap();
147 Ok(sig)
148 }
149}
150#[allow(dead_code)]
151pub fn get_wallet_pass() -> Result<String> {
152 use std::fs;
153 let pass = String::from(fs::read_to_string(".env")?.trim());
154 Ok(pass)
155}
156
157#[cfg(test)]
158mod test {
159 use super::*;
160 use eosio_client_keys::hash::hash_sha256;
161 use eosio_client_keys::EOSSignature;
162
163 const KEOSD_HOST: &str = "http://127.0.0.1:3888";
164
165 #[tokio::test]
166 async fn wallet_list_test() -> Result<()> {
167 let keos = EOSRPC::non_blocking(String::from(KEOSD_HOST)).await?;
168 let _wallets = Wallet::create(keos).list().await?;
169 Ok(())
170 }
171
172 #[tokio::test]
173 async fn wallet_list_unlock() -> Result<()> {
174 let keos = EOSRPC::non_blocking(String::from(KEOSD_HOST)).await?;
175 let pass = get_wallet_pass()?;
176 let _wallets = Wallet::create(keos).unlock("default", &pass).await?;
177 Ok(())
178 }
179
180 #[tokio::test]
181 async fn wallet_list_keys() -> Result<()> {
182 let keos = EOSRPC::non_blocking(String::from(KEOSD_HOST)).await?;
183 let pass = get_wallet_pass()?;
184 let wallet = Wallet::create(keos);
185 let _wallets = wallet.unlock("default", &pass).await?;
186 let _keys = wallet.keys().await?;
187
188 Ok(())
189 }
190
191 #[tokio::test]
192 async fn wallet_list_private_keys() -> Result<()> {
193 let keos = EOSRPC::non_blocking(String::from(KEOSD_HOST)).await?;
194 let pass = get_wallet_pass()?;
195 let wallet = Wallet::create(keos);
196 let _res = wallet.unlock("default", &pass).await?;
197 let keys = wallet.private_keys("default", &pass).await?;
198 for k in keys {
199 assert_eq!(k.0.to_eos_string()?, k.1.to_public().to_eos_string()?);
200 }
201 Ok(())
202 }
203
204 #[tokio::test]
205 async fn wallet_sign_txn() -> Result<()> {
206 let keos = EOSRPC::non_blocking(String::from(KEOSD_HOST)).await?;
207 let pass = get_wallet_pass()?;
208 let wallet = Wallet::create_with_chain_id(keos, EOSIO_CHAIN_ID);
209 let _res = wallet.unlock("default", &pass).await?;
210 let t = TransactionIn::dummy();
211 let pubkey =
212 EOSPublicKey::from_eos_string("EOS7ctUUZhtCGHnxUnh4Rg5eethj3qNS5S9fijyLMKgRsBLh8eMBB")?;
213 let ti: TransactionInSigned = wallet.sign_transaction(t, vec![pubkey]).await?;
214 let sigs = ti.signatures;
215 assert_eq!(sigs.len(), 1);
216 for sig in sigs {
217 let eos_sig: EOSSignature = EOSSignature::from_string(&sig)?;
218 if !eos_sig.is_canonical() {
219 eprintln!("{:#?}", eos_sig.to_eos_string());
220 }
221 assert!(eos_sig.is_canonical());
222 }
223 Ok(())
224 }
225
226 #[tokio::test]
227 async fn wallet_sign_digest() -> Result<()> {
228 let keos = EOSRPC::non_blocking(String::from(KEOSD_HOST)).await?;
229 let pass = get_wallet_pass()?;
230 let wallet = Wallet::create(keos);
231 let _res = wallet.unlock("default", &pass).await?;
232 let pubkey =
233 EOSPublicKey::from_eos_string("EOS7ctUUZhtCGHnxUnh4Rg5eethj3qNS5S9fijyLMKgRsBLh8eMBB")?;
234 let phrase: Vec<u8> = "Greg! The Stop sign".as_bytes().to_vec();
235 let hash = hash_sha256(&phrase);
236 let sig = wallet.sign_digest(&hash, &pubkey).await?;
237 let eos_sig: EOSSignature = EOSSignature::from_string(&sig)?;
238 eos_sig.verify_hash(&hash, &pubkey)?;
239 assert!(eos_sig.is_canonical());
240
241 Ok(())
242 }
243}