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 12KB

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