您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

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