You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

rpc.rs 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  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 contract_function_call_failed() {
  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 = download_contract().await;
  126. client
  127. .deploy_contract(&signer, &signer_account_id, wasm)
  128. .commit(Finality::None)
  129. .await
  130. .unwrap();
  131. assert!(client
  132. .function_call(&signer, &signer_account_id, "new_default_meta")
  133. .args(json!({
  134. "owner_id": &signer_account_id,
  135. "total_suppl": "100",
  136. }))
  137. .gas(near_units::parse_gas!("300 T") as u64)
  138. .commit(Finality::None)
  139. .await
  140. .is_err());
  141. client
  142. .function_call(&signer, &signer_account_id, "new_default_meta")
  143. .args(json!({
  144. "owner_id": &signer_account_id,
  145. "total_supply": "100",
  146. }))
  147. .gas(near_units::parse_gas!("300 T") as u64)
  148. .commit(Finality::None)
  149. .await
  150. .unwrap();
  151. }
  152. #[tokio::test]
  153. async fn multiple_tests() {
  154. let worker = workspaces::sandbox().await.unwrap();
  155. let client = near_client(&worker);
  156. let signer_account_id = AccountId::from_str("alice.test.near").unwrap();
  157. let signer = create_signer(&worker, &client, &signer_account_id).await;
  158. let wasm = clone_and_compile_wasm().await;
  159. init_contract(&client, &signer_account_id, &signer, wasm).await;
  160. fc_no_params(&client, &signer_account_id, &signer).await;
  161. fc_with_one_param_and_result(&client, &signer_account_id, &signer).await;
  162. fc_with_param_and_result(&client, &signer_account_id, &signer).await;
  163. view_no_params(&client, &signer_account_id).await;
  164. view_with_params(&client, &signer_account_id).await;
  165. }
  166. async fn init_contract(
  167. client: &NearClient,
  168. contract_id: &AccountId,
  169. signer: &Signer,
  170. wasm: Vec<u8>,
  171. ) {
  172. client
  173. .deploy_contract(signer, contract_id, wasm)
  174. .commit(Finality::None)
  175. .await
  176. .unwrap();
  177. }
  178. async fn view_no_params(client: &NearClient, contract_id: &AccountId) {
  179. client
  180. .view::<u64>(contract_id, Finality::None, "show_id", None)
  181. .await
  182. .unwrap();
  183. }
  184. async fn view_with_params(client: &NearClient, contract_id: &AccountId) {
  185. client
  186. .view::<String>(
  187. contract_id,
  188. Finality::None,
  189. "show_type",
  190. Some(json!({"is_message": true})),
  191. )
  192. .await
  193. .unwrap();
  194. }
  195. // fc = function call
  196. async fn fc_no_params(client: &NearClient, contract_id: &AccountId, signer: &Signer) {
  197. client
  198. .function_call(signer, contract_id, "increment")
  199. .gas(near_units::parse_gas!("300 T") as u64)
  200. .commit(Finality::None)
  201. .await
  202. .unwrap();
  203. }
  204. async fn fc_with_one_param_and_result(
  205. client: &NearClient,
  206. contract_id: &AccountId,
  207. signer: &Signer,
  208. ) {
  209. let expected_result = "change message";
  210. let message = client
  211. .function_call(signer, contract_id, "change_message")
  212. .args(json!({ "message": expected_result }))
  213. .gas(near_units::parse_gas!("300 T") as u64)
  214. .commit(Finality::Final)
  215. .await
  216. .unwrap()
  217. .output::<String>()
  218. .unwrap();
  219. assert_eq!(message, expected_result);
  220. }
  221. async fn fc_with_param_and_result(client: &NearClient, contract_id: &AccountId, signer: &Signer) {
  222. let expected_id = 666u64;
  223. let id = client
  224. .function_call(signer, contract_id, "change_id")
  225. .args(json!({ "id": expected_id }))
  226. .gas(near_units::parse_gas!("300 T") as u64)
  227. .commit(Finality::Final)
  228. .await
  229. .unwrap()
  230. .output::<u64>()
  231. .unwrap();
  232. assert_eq!(id, expected_id);
  233. }
  234. #[tokio::test]
  235. async fn async_transaction() {
  236. let worker = workspaces::sandbox().await.unwrap();
  237. let client = near_client(&worker);
  238. let signer_account_id = AccountId::from_str("alice.test.near").unwrap();
  239. let signer = create_signer(&worker, &client, &signer_account_id).await;
  240. let wasm = clone_and_compile_wasm().await;
  241. client
  242. .deploy_contract(&signer, &signer_account_id, wasm)
  243. .commit(Finality::None)
  244. .await
  245. .unwrap();
  246. let expected_result = "change message";
  247. let transaction_id = client
  248. .function_call(&signer, &signer_account_id, "change_message")
  249. .args(json!({ "message": expected_result }))
  250. .gas(near_units::parse_gas!("300 T") as u64)
  251. .commit_async(Finality::Final)
  252. .await
  253. .unwrap();
  254. let (tx, rx) = tokio::sync::oneshot::channel::<()>();
  255. tokio::spawn(async move {
  256. tokio::time::timeout(std::time::Duration::from_secs(3), rx)
  257. .await
  258. .expect("Wait async transaction timeout")
  259. });
  260. loop {
  261. let res = client.view_transaction(&transaction_id, &signer).await;
  262. if let Err(near_rpc::Error::ViewTransaction(_)) = &res {
  263. // try one more time
  264. continue;
  265. }
  266. // cancel timeout
  267. tx.send(()).unwrap();
  268. let msg = res.unwrap().output::<String>().unwrap();
  269. assert_eq!(msg, expected_result);
  270. break;
  271. }
  272. }
  273. #[tokio::test]
  274. async fn view_access_key_success() {
  275. let worker = workspaces::sandbox().await.unwrap();
  276. let client = near_client(&worker);
  277. let signer_account_id = AccountId::from_str("alice.test.near").unwrap();
  278. let signer = create_signer(&worker, &client, &signer_account_id).await;
  279. let new_acc = AccountId::from_str("one.alice.test.near").unwrap();
  280. let secret_key = Ed25519SecretKey::try_from_bytes(&random_bits()).unwrap();
  281. let pk = Ed25519PublicKey::from(&secret_key);
  282. let _ = client
  283. .create_account(&signer, &new_acc, pk, near_units::parse_near!("3 N"))
  284. .commit(Finality::None)
  285. .await
  286. .unwrap()
  287. .output::<serde_json::Value>();
  288. let access_key = client
  289. .view_access_key(&new_acc, &pk, Finality::None)
  290. .await
  291. .unwrap();
  292. assert!(matches!(
  293. access_key,
  294. ViewAccessKey {
  295. result: ViewAccessKeyResult::Ok { .. },
  296. ..
  297. }
  298. ));
  299. }
  300. #[tokio::test]
  301. async fn view_access_key_failure() {
  302. let worker = workspaces::sandbox().await.unwrap();
  303. let client = near_client(&worker);
  304. let new_acc = AccountId::from_str("one.alice.test.near").unwrap();
  305. let secret_key = Ed25519SecretKey::try_from_bytes(&random_bits()).unwrap();
  306. let pk = Ed25519PublicKey::from(&secret_key);
  307. let access_key = client
  308. .view_access_key(&new_acc, &pk, Finality::None)
  309. .await
  310. .unwrap();
  311. assert!(matches!(
  312. access_key,
  313. ViewAccessKey {
  314. result: ViewAccessKeyResult::Err { .. },
  315. ..
  316. }
  317. ));
  318. }
  319. #[tokio::test]
  320. async fn create_account() {
  321. let worker = workspaces::sandbox().await.unwrap();
  322. let client = near_client(&worker);
  323. let signer_account_id = AccountId::from_str("alice.test.near").unwrap();
  324. let signer = create_signer(&worker, &client, &signer_account_id).await;
  325. let new_acc = AccountId::from_str("one.alice.test.near").unwrap();
  326. let secret_key = Ed25519SecretKey::try_from_bytes(&random_bits()).unwrap();
  327. let pk = Ed25519PublicKey::from(&secret_key);
  328. let _ = client
  329. .create_account(&signer, &new_acc, pk, near_units::parse_near!("3 N"))
  330. .commit(Finality::Final)
  331. .await
  332. .unwrap()
  333. .output::<serde_json::Value>();
  334. let access_key = client
  335. .view_access_key(&new_acc, &pk, Finality::None)
  336. .await
  337. .unwrap();
  338. assert!(matches!(
  339. access_key,
  340. ViewAccessKey {
  341. result: ViewAccessKeyResult::Ok { .. },
  342. ..
  343. }
  344. ));
  345. }
  346. #[tokio::test]
  347. async fn delete_account() {
  348. let worker = workspaces::sandbox().await.unwrap();
  349. let client = near_client(&worker);
  350. let signer_account_id = AccountId::from_str("alice.test.near").unwrap();
  351. let signer = create_signer(&worker, &client, &signer_account_id).await;
  352. let new_acc = AccountId::from_str("one.alice.test.near").unwrap();
  353. let secret_key = Ed25519SecretKey::try_from_bytes(&random_bits()).unwrap();
  354. let pk = Ed25519PublicKey::from(&secret_key);
  355. client
  356. .create_account(&signer, &new_acc, pk, near_units::parse_near!("3 N"))
  357. .commit(Finality::Final)
  358. .await
  359. .unwrap();
  360. let access_key = client
  361. .view_access_key(&new_acc, &pk, Finality::None)
  362. .await
  363. .unwrap();
  364. let nonce = if let ViewAccessKey {
  365. result: ViewAccessKeyResult::Ok(AccessKeyView { nonce, .. }),
  366. ..
  367. } = access_key
  368. {
  369. nonce
  370. } else {
  371. panic!("Can't view access key for just created account")
  372. };
  373. let acc_signer = Signer::from_secret(secret_key, new_acc.clone(), nonce);
  374. client
  375. .delete_account(&acc_signer, &new_acc, &signer_account_id)
  376. .commit(Finality::Final)
  377. .await
  378. .unwrap();
  379. let access_key = client
  380. .view_access_key(&new_acc, &pk, Finality::None)
  381. .await
  382. .unwrap();
  383. assert!(matches!(
  384. access_key,
  385. ViewAccessKey {
  386. result: ViewAccessKeyResult::Err { .. },
  387. ..
  388. }
  389. ));
  390. }
  391. fn temp_dir() -> tempfile::TempDir {
  392. tempfile::Builder::new()
  393. .prefix("near-client-test-")
  394. .tempdir()
  395. .unwrap()
  396. }