Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

rpc.rs 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. use common_api::crypto::prelude::*;
  2. use git2::{Cred, RemoteCallbacks};
  3. use near_primitives_light::{types::Finality, views::AccessKeyView};
  4. use near_rpc::{
  5. client::{NearClient, Signer},
  6. utils::{ViewAccessKey, ViewAccessKeyResult},
  7. };
  8. use rand::{RngCore, SeedableRng};
  9. use rand_chacha::ChaChaRng;
  10. use reqwest::Url;
  11. use serde_json::json;
  12. use std::{fs::write, path::Path, str::FromStr};
  13. use workspaces::{network::Sandbox, types::SecretKey, AccountId, Worker};
  14. // auxiliary structs and methods
  15. fn near_client(worker: &Worker<Sandbox>) -> NearClient {
  16. let rpc_url = Url::parse(format!("http://localhost:{}", worker.rpc_port()).as_str()).unwrap();
  17. NearClient::new(rpc_url).unwrap()
  18. }
  19. async fn create_signer(
  20. worker: &Worker<Sandbox>,
  21. client: &NearClient,
  22. signer_acc_id: &AccountId,
  23. ) -> Signer {
  24. let sk = Ed25519SecretKey::try_from_bytes(&random_bits()).unwrap();
  25. let pk = Ed25519PublicKey::from(&sk);
  26. let keypair = Keypair::new(sk).to_string();
  27. let workspaces_sk = SecretKey::from_str(&keypair).unwrap();
  28. let _ = worker
  29. .create_tla(signer_acc_id.clone(), workspaces_sk)
  30. .await
  31. .unwrap();
  32. let view_access_key = client
  33. .view_access_key(signer_acc_id, &pk, Finality::None)
  34. .await
  35. .unwrap();
  36. match view_access_key.result {
  37. ViewAccessKeyResult::Ok(AccessKeyView { nonce, .. }) => {
  38. Signer::from_secret_str(&keypair, signer_acc_id.clone(), nonce).unwrap()
  39. }
  40. ViewAccessKeyResult::Err { error, .. } => panic!("{error}"),
  41. }
  42. }
  43. async fn download_contract() -> Vec<u8> {
  44. let target = "https://github.com/near-examples/FT/raw/master/res/fungible_token.wasm";
  45. let target_path = temp_dir().into_path();
  46. let fname = "contract.wasm";
  47. let full_dest = format!("{}/{}", target_path.to_string_lossy(), fname);
  48. let contract_bytes = reqwest::get(target).await.unwrap().bytes().await.unwrap();
  49. write(full_dest, &contract_bytes).unwrap();
  50. contract_bytes.to_vec()
  51. }
  52. async fn clone_and_compile_wasm() -> Vec<u8> {
  53. let tmp_path = temp_dir().into_path();
  54. let repo_path = format!("{}/near-smartcontracts", tmp_path.to_string_lossy());
  55. let target_path = format!("{}/test-contract", repo_path);
  56. let repo_url = "git@github.com:Relayz-io/near-smartcontracts.git";
  57. // Prepare callbacks.
  58. let mut callbacks = RemoteCallbacks::new();
  59. callbacks.credentials(|_, username_from_url, _| {
  60. let username = username_from_url
  61. .ok_or_else(|| git2::Error::from_str("Parsing the given URL is failed"))
  62. .unwrap();
  63. Cred::ssh_key_from_agent(username)
  64. });
  65. // Prepare fetch options.
  66. let mut fo = git2::FetchOptions::new();
  67. fo.remote_callbacks(callbacks);
  68. // Prepare builder.
  69. let mut builder = git2::build::RepoBuilder::new();
  70. builder.fetch_options(fo);
  71. builder.branch("main");
  72. // Clone the project.
  73. builder.clone(repo_url, Path::new(&repo_path)).unwrap();
  74. workspaces::compile_project(&target_path).await.unwrap()
  75. }
  76. fn random_bits() -> [u8; ED25519_SECRET_KEY_LENGTH] {
  77. let mut chacha = ChaChaRng::from_entropy();
  78. let mut secret_bytes = [0_u8; ED25519_SECRET_KEY_LENGTH];
  79. chacha.fill_bytes(&mut secret_bytes);
  80. secret_bytes
  81. }
  82. // tests themselves
  83. #[tokio::test]
  84. async fn contract_creation() {
  85. let worker = workspaces::sandbox().await.unwrap();
  86. let client = near_client(&worker);
  87. let signer_account_id = AccountId::from_str("alice.test.near").unwrap();
  88. let signer = create_signer(&worker, &client, &signer_account_id).await;
  89. let wasm = download_contract().await;
  90. client
  91. .deploy_contract(&signer, &signer_account_id, wasm)
  92. .commit(Finality::None)
  93. .await
  94. .unwrap();
  95. }
  96. #[tokio::test]
  97. async fn contract_function_call() {
  98. let worker = workspaces::sandbox().await.unwrap();
  99. let client = near_client(&worker);
  100. let signer_account_id = AccountId::from_str("alice.test.near").unwrap();
  101. let signer = create_signer(&worker, &client, &signer_account_id).await;
  102. let wasm = download_contract().await;
  103. client
  104. .deploy_contract(&signer, &signer_account_id, wasm)
  105. .commit(Finality::None)
  106. .await
  107. .unwrap();
  108. client
  109. .function_call(&signer, &signer_account_id, "new_default_meta")
  110. .args(json!({
  111. "owner_id": &signer_account_id,
  112. "total_supply": "100",
  113. }))
  114. .gas(near_units::parse_gas!("300 T") as u64)
  115. .commit(Finality::None)
  116. .await
  117. .unwrap();
  118. }
  119. #[tokio::test]
  120. async fn multiple_tests() {
  121. let worker = workspaces::sandbox().await.unwrap();
  122. let client = near_client(&worker);
  123. let signer_account_id = AccountId::from_str("alice.test.near").unwrap();
  124. let signer = create_signer(&worker, &client, &signer_account_id).await;
  125. let wasm = clone_and_compile_wasm().await;
  126. init_contract(&client, &signer_account_id, &signer, wasm).await;
  127. fc_no_params(&client, &signer_account_id, &signer).await;
  128. fc_with_one_param_and_result(&client, &signer_account_id, &signer).await;
  129. fc_with_param_and_result(&client, &signer_account_id, &signer).await;
  130. view_no_params(&client, &signer_account_id).await;
  131. view_with_params(&client, &signer_account_id).await;
  132. }
  133. async fn init_contract(
  134. client: &NearClient,
  135. contract_id: &AccountId,
  136. signer: &Signer,
  137. wasm: Vec<u8>,
  138. ) {
  139. client
  140. .deploy_contract(signer, contract_id, wasm)
  141. .commit(Finality::None)
  142. .await
  143. .unwrap();
  144. }
  145. async fn view_no_params(client: &NearClient, contract_id: &AccountId) {
  146. client
  147. .view::<u64>(contract_id, Finality::None, "show_id", None)
  148. .await
  149. .unwrap();
  150. }
  151. async fn view_with_params(client: &NearClient, contract_id: &AccountId) {
  152. client
  153. .view::<String>(
  154. contract_id,
  155. Finality::None,
  156. "show_type",
  157. Some(json!({"is_message": true})),
  158. )
  159. .await
  160. .unwrap();
  161. }
  162. // fc = function call
  163. async fn fc_no_params(client: &NearClient, contract_id: &AccountId, signer: &Signer) {
  164. client
  165. .function_call(signer, contract_id, "increment")
  166. .gas(near_units::parse_gas!("300 T") as u64)
  167. .commit(Finality::None)
  168. .await
  169. .unwrap();
  170. }
  171. async fn fc_with_one_param_and_result(
  172. client: &NearClient,
  173. contract_id: &AccountId,
  174. signer: &Signer,
  175. ) {
  176. let expected_result = "change message";
  177. let message = client
  178. .function_call(signer, contract_id, "change_message")
  179. .args(json!({ "message": expected_result }))
  180. .gas(near_units::parse_gas!("300 T") as u64)
  181. .commit(Finality::Final)
  182. .await
  183. .unwrap()
  184. .output::<String>()
  185. .unwrap();
  186. assert_eq!(message, expected_result);
  187. }
  188. async fn fc_with_param_and_result(client: &NearClient, contract_id: &AccountId, signer: &Signer) {
  189. let expected_id = 666u64;
  190. let id = client
  191. .function_call(signer, contract_id, "change_id")
  192. .args(json!({ "id": expected_id }))
  193. .gas(near_units::parse_gas!("300 T") as u64)
  194. .commit(Finality::Final)
  195. .await
  196. .unwrap()
  197. .output::<u64>()
  198. .unwrap();
  199. assert_eq!(id, expected_id);
  200. }
  201. #[tokio::test]
  202. async fn async_transaction() {
  203. let worker = workspaces::sandbox().await.unwrap();
  204. let client = near_client(&worker);
  205. let signer_account_id = AccountId::from_str("alice.test.near").unwrap();
  206. let signer = create_signer(&worker, &client, &signer_account_id).await;
  207. let wasm = clone_and_compile_wasm().await;
  208. client
  209. .deploy_contract(&signer, &signer_account_id, wasm)
  210. .commit(Finality::None)
  211. .await
  212. .unwrap();
  213. let expected_result = "change message";
  214. let transaction_id = client
  215. .function_call(&signer, &signer_account_id, "change_message")
  216. .args(json!({ "message": expected_result }))
  217. .gas(near_units::parse_gas!("300 T") as u64)
  218. .commit_async(Finality::Final)
  219. .await
  220. .unwrap();
  221. let (tx, rx) = tokio::sync::oneshot::channel::<()>();
  222. tokio::spawn(async move {
  223. tokio::time::timeout(std::time::Duration::from_secs(3), rx)
  224. .await
  225. .expect("Wait async transaction timeout")
  226. });
  227. loop {
  228. let res = client.view_transaction(&transaction_id, &signer).await;
  229. if let Err(near_rpc::Error::ViewTransaction(_)) = &res {
  230. // try one more time
  231. continue;
  232. }
  233. // cancel timeout
  234. tx.send(()).unwrap();
  235. let msg = res.unwrap().output::<String>().unwrap();
  236. assert_eq!(msg, expected_result);
  237. break;
  238. }
  239. }
  240. #[tokio::test]
  241. async fn view_access_key_success() {
  242. let worker = workspaces::sandbox().await.unwrap();
  243. let client = near_client(&worker);
  244. let signer_account_id = AccountId::from_str("alice.test.near").unwrap();
  245. let signer = create_signer(&worker, &client, &signer_account_id).await;
  246. let new_acc = AccountId::from_str("one.alice.test.near").unwrap();
  247. let secret_key = Ed25519SecretKey::try_from_bytes(&random_bits()).unwrap();
  248. let pk = Ed25519PublicKey::from(&secret_key);
  249. let _ = client
  250. .create_account(&signer, &new_acc, pk, near_units::parse_near!("3 N"))
  251. .commit(Finality::None)
  252. .await
  253. .unwrap()
  254. .output::<serde_json::Value>();
  255. let access_key = client
  256. .view_access_key(&new_acc, &pk, Finality::None)
  257. .await
  258. .unwrap();
  259. assert!(matches!(
  260. access_key,
  261. ViewAccessKey {
  262. result: ViewAccessKeyResult::Ok { .. },
  263. ..
  264. }
  265. ));
  266. }
  267. #[tokio::test]
  268. async fn view_access_key_failure() {
  269. let worker = workspaces::sandbox().await.unwrap();
  270. let client = near_client(&worker);
  271. let new_acc = AccountId::from_str("one.alice.test.near").unwrap();
  272. let secret_key = Ed25519SecretKey::try_from_bytes(&random_bits()).unwrap();
  273. let pk = Ed25519PublicKey::from(&secret_key);
  274. let access_key = client
  275. .view_access_key(&new_acc, &pk, Finality::None)
  276. .await
  277. .unwrap();
  278. assert!(matches!(
  279. access_key,
  280. ViewAccessKey {
  281. result: ViewAccessKeyResult::Err { .. },
  282. ..
  283. }
  284. ));
  285. }
  286. #[tokio::test]
  287. async fn create_account() {
  288. let worker = workspaces::sandbox().await.unwrap();
  289. let client = near_client(&worker);
  290. let signer_account_id = AccountId::from_str("alice.test.near").unwrap();
  291. let signer = create_signer(&worker, &client, &signer_account_id).await;
  292. let new_acc = AccountId::from_str("one.alice.test.near").unwrap();
  293. let secret_key = Ed25519SecretKey::try_from_bytes(&random_bits()).unwrap();
  294. let pk = Ed25519PublicKey::from(&secret_key);
  295. let _ = client
  296. .create_account(&signer, &new_acc, pk, near_units::parse_near!("3 N"))
  297. .commit(Finality::Final)
  298. .await
  299. .unwrap()
  300. .output::<serde_json::Value>();
  301. let access_key = client
  302. .view_access_key(&new_acc, &pk, Finality::None)
  303. .await
  304. .unwrap();
  305. assert!(matches!(
  306. access_key,
  307. ViewAccessKey {
  308. result: ViewAccessKeyResult::Ok { .. },
  309. ..
  310. }
  311. ));
  312. }
  313. #[tokio::test]
  314. async fn delete_account() {
  315. let worker = workspaces::sandbox().await.unwrap();
  316. let client = near_client(&worker);
  317. let signer_account_id = AccountId::from_str("alice.test.near").unwrap();
  318. let signer = create_signer(&worker, &client, &signer_account_id).await;
  319. let new_acc = AccountId::from_str("one.alice.test.near").unwrap();
  320. let secret_key = Ed25519SecretKey::try_from_bytes(&random_bits()).unwrap();
  321. let pk = Ed25519PublicKey::from(&secret_key);
  322. client
  323. .create_account(&signer, &new_acc, pk, near_units::parse_near!("3 N"))
  324. .commit(Finality::Final)
  325. .await
  326. .unwrap();
  327. let access_key = client
  328. .view_access_key(&new_acc, &pk, Finality::None)
  329. .await
  330. .unwrap();
  331. let nonce = if let ViewAccessKey {
  332. result: ViewAccessKeyResult::Ok(AccessKeyView { nonce, .. }),
  333. ..
  334. } = access_key
  335. {
  336. nonce
  337. } else {
  338. panic!("Can't view access key for just created account")
  339. };
  340. let acc_signer = Signer::from_secret(secret_key, new_acc.clone(), nonce);
  341. client
  342. .delete_account(&acc_signer, &new_acc, &signer_account_id)
  343. .commit(Finality::Final)
  344. .await
  345. .unwrap();
  346. let access_key = client
  347. .view_access_key(&new_acc, &pk, Finality::None)
  348. .await
  349. .unwrap();
  350. assert!(matches!(
  351. access_key,
  352. ViewAccessKey {
  353. result: ViewAccessKeyResult::Err { .. },
  354. ..
  355. }
  356. ));
  357. }
  358. fn temp_dir() -> tempfile::TempDir {
  359. tempfile::Builder::new()
  360. .prefix("near-client-test-")
  361. .tempdir()
  362. .unwrap()
  363. }